diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index c98e33194..000000000 --- a/.dockerignore +++ /dev/null @@ -1,2 +0,0 @@ -node_modules -.turbo diff --git a/.dockerignore b/.dockerignore new file mode 120000 index 000000000..3e4e48b0b --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +.gitignore \ No newline at end of file diff --git a/.env.example b/.env.example index 75dcf7683..bdacbe671 100644 --- a/.env.example +++ b/.env.example @@ -8,6 +8,8 @@ #GITHUB_CLIENT_ID= #GITHUB_CLIENT_SECRET= +#AUTH_OIDC_PROVIDER='{"issuer":"http://localhost:8080/realms/dev_realm","clientId":"dev_client","clientSecret":"your_generated_secret"}' + #DATABASE_URL=postgresql://postgres:postgres-mqf3nzx@localhost:5438/postgres #REDIS_URL=redis://default:redis-mqf3nzx@localhost:6380 #KAFKA_BOOTSTRAP_SERVERS=localhost:19092 diff --git a/.github/ISSUE_TEMPLATE/issue-template.md b/.github/ISSUE_TEMPLATE/issue-template.md new file mode 100644 index 000000000..28533efd4 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/issue-template.md @@ -0,0 +1,34 @@ +--- +name: Issue template +about: Template for creating issues, both bugs and feature requests +title: '' +labels: '' +assignees: vklimontovich + +--- +``` +┌─────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ IMPORTANT: GitHub is for discussing issues of self-hosting Jitsu. Please make sure that your │ +│ issue can be reproduced for self-hosting environment. If you're experiencing problem with your │ +│ Jitsu Cloud account, please contact support@jitsu.com │ +└─────────────────────────────────────────────────────────────────────────────────────────────────┘ + +Please delete this block before submitting the issue +``` + +## Summary + + + +## System configuration and versions + + + +## Artifacts (logs, etc) + + diff --git a/.github/workflows/close-stale-issues.yml b/.github/workflows/close-stale-issues.yml new file mode 100644 index 000000000..10a3ae081 --- /dev/null +++ b/.github/workflows/close-stale-issues.yml @@ -0,0 +1,23 @@ +name: Close inactive issues +on: + schedule: + - cron: "30 1 * * *" #once a day at 1:30am + +jobs: + close-issues: + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + steps: + - uses: actions/stale@v5 + with: + days-before-issue-stale: 60 + days-before-issue-close: 60 + stale-issue-label: "🕰️Stale" + exempt-issue-labels: "⏳Postpone" + stale-issue-message: "This issue is stale because it has been open for 60 days with no activity." + close-issue-message: "This issue was closed because it has been inactive for 60 days since being marked as stale." + days-before-pr-stale: -1 + days-before-pr-close: -1 + repo-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml new file mode 100644 index 000000000..e8456fdf3 --- /dev/null +++ b/.github/workflows/docker-build.yml @@ -0,0 +1,46 @@ +name: Jitsu 2.0 Build Docker Images +on: + push: + branches: + # todo - change with main + - feat/newjitsu/self-hosted-revamped + +jobs: + build: + name: Build Project + timeout-minutes: 60 + runs-on: ubuntu-latest + steps: + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + + - name: Install Node.js + uses: actions/setup-node@v3 + with: + node-version: 18 + + - name: Install pnpm + uses: pnpm/action-setup@v2 + with: + version: 8 + + - uses: actions/checkout@v3 + - name: Get pnpm store directory + id: pnpm-cache + shell: bash + run: | + echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT + + - uses: actions/cache@v3 + name: Setup pnpm cache + with: + path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + + - name: Install dependencies with pnpm + run: pnpm install --no-frozen-lockfile + + - name: Build Beta Images for AMD64 + run: pnpm build-scripts docker --platform linux/amd64 --tag beta --push-git-tag --push-docker --logs diff --git a/.github/workflows/test-and-build.yml b/.github/workflows/test-and-build.yml index fdb053e4d..e19ae12b8 100644 --- a/.github/workflows/test-and-build.yml +++ b/.github/workflows/test-and-build.yml @@ -1,29 +1,30 @@ -name: Build and Test +name: Jitsu 2.0 Test and Build on: push: branches: [newjitsu] pull_request: branches: [newjitsu] + workflow_dispatch: env: - #container jitsucom/node16builder:latest has playwright browsers binaries preinstalled on this path: + #container jitsucom/node18builder:latest has playwright browsers binaries preinstalled on this path: PLAYWRIGHT_BROWSERS_PATH: /root/.cache/ms-playwright jobs: - test: - name: Test and Build project - container: jitsucom/node16builder:latest + build: + name: Build Project + container: jitsucom/node22builder:latest timeout-minutes: 60 runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Get pnpm store directory id: pnpm-cache shell: bash run: | echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT - - uses: actions/cache@v3 + - uses: actions/cache@v4 name: Setup pnpm cache with: path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} @@ -34,18 +35,27 @@ jobs: - name: Install dependencies with pnpm run: pnpm install --no-frozen-lockfile - name: Check code format - run: pnpm format:check + run: pnpm format:check:all - name: Run linter run: pnpm lint - name: Build project run: pnpm build - # - name: Publish canary versions - # run: pnpm build - name: Run Tests run: pnpm test - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 if: always() with: path: libs/jitsu-js/playwright/artifacts name: artifacts/@jitsu/js retention-days: 30 + - name: Notify Slack + uses: rtCamp/action-slack-notify@v2 + # Do not send notification for PRs, feature branches, or forks + if: ${{ env.SLACK_WEBHOOK != '' && github.ref == 'refs/heads/newjitsu' }} + env: + SLACK_USERNAME: "GitHub Actions" + SLACK_WEBHOOK: ${{ secrets.SLACK_DEV_CHANNEL }} + SLACK_COLOR: ${{ job.status }} + MSG_MINIMAL: "true" + SLACK_ICON_EMOJI: ":github-mark:" + SLACK_MESSAGE: "${{ job.status }}: Jitsu 2.0 Build: ${{ github.event.head_commit.message }}" diff --git a/.gitignore b/.gitignore index cdf117c1a..11f42415d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ node_modules +**/node_modules .idea .env.local .turbo @@ -8,3 +9,15 @@ node_modules /libs/jitsu-react/dist/ /examples/nextjs-app/.next/ +/examples/hubspot-function/dist/ +/cli/jitsu-cli/compiled/ +/cli/jitsu-cli/dist/ +/types/protocols/dist/ +/libs/functions/dist/ +/docker/data/** +/cache/** +/cli/build-scripts/compiled/ +/cli/build-scripts/dist/ +/libs/jsondiffpatch/coverage/ +/libs/jsondiffpatch/dist/ +/devenv-* diff --git a/.prettierignore b/.prettierignore index df92e3fb0..dbd0d0a8c 100644 --- a/.prettierignore +++ b/.prettierignore @@ -6,6 +6,7 @@ # bundle stats *.mdx *.md + jitsu # configs -- will keep multi-line json arrays .eslintrc.json @@ -24,4 +25,7 @@ pnpm-lock.yaml /webapps/console/prisma/schema /libs/jitsu-js/__tests__/playwright/artifacts /libs/jitsu-js/compiled/ -libs/jitsu-js/dist/ \ No newline at end of file +libs/jitsu-js/dist/ +/cli/jitsu-cli/compiled/ +/cli/jitsu-cli/dist/ +/libs/functions/dist/ \ No newline at end of file diff --git a/.readme-assets/overview-screenshot.png b/.readme-assets/overview-screenshot.png new file mode 100644 index 000000000..409de2f82 Binary files /dev/null and b/.readme-assets/overview-screenshot.png differ diff --git a/.readme-assets/screenshot.png b/.readme-assets/screenshot.png deleted file mode 100644 index 2bda5d409..000000000 Binary files a/.readme-assets/screenshot.png and /dev/null differ diff --git a/.versions.json b/.versions.json new file mode 100644 index 000000000..a6a5c05be --- /dev/null +++ b/.versions.json @@ -0,0 +1,4 @@ +{ + "beta": "2.3.*", + "latest": "2.2.*" +} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9096896f8..59c1fb169 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,24 +1,37 @@ # Prerequisites -- `node: 16.x` -- `pnpm: >= 7.15.0` +- `node: 18.x` +- `npx` +- `pnpm: >= 8.2.0` +- `docker: >= 19.03.0` # Commands - `pnpm install` - Install dependencies - `pnpm build` - Build the project -- `pnpm format` - Apply prettier to the project - - `pnpm format:check` - Check if prettier needs to be applied +- `pnpm format` - Apply prettier to the project, only to changed files + - `pnpm format:check` - Check if prettier needs to be applied, check only changed files + - `pnpm format:check:all` - Check if prettier needs to be applied. Check all files + - `pnpm format:all` - Same as `pnpm format`, but check all files, regardless of changes - `pnpm lint` - Run linter - `pnpm test` - Run tests -- CI runs equivalent of `pnpm install && pnpm format:check && pnpm build && pnpm lint && pnpm test`. +- CI runs equivalent of `pnpm install && pnpm format:check:all && pnpm build && pnpm lint && pnpm test`. - `pnpm factory-reset` - if you have any problems -# Releasing +# Releasing NPM packages -We use [monorel](https://github.com/jitsu/monorel) to publish releases. At the moment only `@jitsu/jitsu-react`, and `@jitsu/js` are published to npm. To avoid confusion, -always release them together, even if only one of them has changes. +We use [monorel](https://github.com/jitsucom/monorel) to publish releases to npm. + +## Packages + +- `@jitsu/protocols` (./types/protocols) - Base types for JS and React SDKs and Functions library +- `@jitsu/jitsu-react` (./libs/jitsu-react) - React SDK +- `@jitsu/js` (./libs/jitsu-js) - JS SDK +- `@jitsu/functions-lib` (./libs/functions) - library for Jitsu Functions +- `@jitsu/jitsu-cli` (./cli/jitsu-cli) - CLI to create, debug and deploy Jitsu Functions + +To avoid confusion, always release all npm packages together, even if only one of them has changes. ## Common steps @@ -28,15 +41,50 @@ always release them together, even if only one of them has changes. ## Canary releases - - `pnpm exec monorel --filter '@jitsu/js' --filter '@jitsu/jitsu-react' --filter '@jitsu/protocols' --version '1.1.0-canary.{rev}.{time}' --npm-tag canary` - to **dry-run** publishing - - Same command, but with `--publish` - to **publish**. + - `pnpm release:canary` - to **dry-run** publishing + - Same command, but with `pnpm release:canary --publish` - to **publish**. -> **Note** -> Replace `1.1.0` in `--version '1.1.0-canary.{rev}'` to the version that makes sense ## Stable releases -- `pnpm exec monorel --filter '@jitsu/js' --filter '@jitsu/jitsu-react' --filter '@jitsu/protocols' --version '****' --npm-tag latest` - to **dry-run** publishing +- `pnpm release --version ` - to **dry-run** publishing - Same command, but with `--publish` - to **publish**. + +# Releasing Docker packages + +We use [build-scripts](https://github.com/jitsucom/jitsu/tree/newjitsu/cli/build-scripts) along with `all.Dockerfile` to publish releases to Docker. + +## Packages + +- `jitsucom/console` (./webapps/console) - UI for Jitsu +- `jitsucom/rotor` (./services/rotor) - Functions Server for Jitsu + +To avoid confusion, always release all packages together, even if only one of them has changes. + +## Common steps + +- Make sure that you are logged to your docker account `docker login` +- `pnpm install && pnpm format:check && pnpm build && pnpm lint && pnpm test` should succeed +- All changes should be committed (check with `git status`). It's ok to release canary from branches! + +## Beta releases + +- `./release.sh --dryRun` - to **dry-run** publishing. +- `./release.sh` - to actually **publish** beta. + +## Stable releases + +- `./release.sh --release latest --dryRun` - to **dry-run** publishing. +- `./release.sh --release latest ` - to actually **publish** latest image. + +## Bumping versions + +For initial release or to bump major/minor version pass `--version` argument to `./release.sh` script. + +- `./release.sh --version 2.5.0` +- `./release.sh --release latest --version 2.5.0` + + + diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..88c66852c --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Jitsu Labs, Inc + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index a5ac370fb..b9e56a36a 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,18 @@ # Jitsu 2.0 - -> Looking for Jitsu Classic? Switch to -> classic branch, and read about Jitsu Classic and Jitsu Next differences

- - +👉Looking for Jitsu Classic? Switch to +classic branch, and read about Jitsu Classic and Jitsu Next differences +

+

+ +

Learn more »

-Slack · Website · Docs · MIT License +Slack · Website · Docs · MIT License · Self-hosting

--- @@ -29,11 +30,11 @@ # What is Jitsu? -Jitsu is a tool for collecting event data from your websites, apps and stream them to your data warehouse or other servises. +Jitsu is a tool for collecting event data from your websites, apps and stream them to your data warehouse or other services. It is a self-hosted, open-source alternative to Segment.

- +

# Quick start @@ -64,7 +65,7 @@ comes with a [FREE ClickHouse instance](https://next.jitsu.com/features/clickhou ## 2. Configure Jitsu * Follow [Quick Start Guide](https://docs.jitsu.com/) -* Get yourself familiar with [Jitsu Concepts](https://docs.jitsu.com/concepts) +* Get yourself familiar with [Jitsu Concepts](https://docs.jitsu.com/core-concepts/) * Browse [Destination Catalog](https://next.jitsu.com/integrations/destinations) ## 3. Send events @@ -75,7 +76,7 @@ comes with a [FREE ClickHouse instance](https://next.jitsu.com/features/clickhou * [React](https://docs.jitsu.com/sending-data/react) (including Next.js) * [NPM Package](https://docs.jitsu.com/sending-data/npm). Yes, it's isomorphic and works in server-side Node.js too! * [HTTP API](https://docs.jitsu.com/sending-data/http) -* [Segment Compatible API](https://docs.jitsu.com/sending-data/segment) +* [Segment Proxy](https://docs.jitsu.com/sending-data/segment-proxy) # 🚚 Bulker diff --git a/all.Dockerfile b/all.Dockerfile new file mode 100644 index 000000000..581afd8e1 --- /dev/null +++ b/all.Dockerfile @@ -0,0 +1,105 @@ +# Run `docker login`, use jitsucom account +# Build & push it with +# docker buildx build --platform linux/amd64 . -f console.Dockerfile --push -t jitsucom/console:latest + +FROM node:24-bookworm as base + +WORKDIR /app +RUN apt-get update -y +RUN apt-get install nano curl cron bash netcat-traditional procps jq -y + +FROM base as builder + +RUN apt-get update -y +RUN apt-get install git openssl1.1 procps python3 make g++ -y +RUN npm -g install pnpm@^9.0.0 + +# Create app directory +WORKDIR /app +COPY pnpm-lock.yaml . +RUN --mount=type=cache,id=onetag_pnpm,target=/root/.local/share/pnpm/store/v3 pnpm fetch + +COPY . . +RUN rm .env* +RUN --mount=type=cache,id=onetag_pnpm,target=/root/.local/share/pnpm/store/v3 pnpm install -r --unsafe-perm + +ENV NEXTJS_STANDALONE_BUILD=1 +#Tubo cache is not working well ? +#RUN --mount=type=cache,id=onetag_turbo,target=/app/node_modules/.cache/turbo pnpm build +RUN pnpm build + +FROM base as console + +ARG JITSU_BUILD_VERSION=dev, +ARG JITSU_BUILD_DOCKER_TAG=dev, +ARG JITSU_BUILD_COMMIT_SHA=unknown, + + +WORKDIR /app +RUN npm -g install prisma@$(cat webapps/console/package.json | jq -r '.dependencies.prisma') +COPY --from=builder /app/docker-start-console.sh ./ +COPY --from=builder /app/webapps/console/prisma/schema.prisma ./ +COPY --from=builder /app/webapps/console/.next/standalone ./ +COPY --from=builder /app/webapps/console/.next/static ./webapps/console/.next/static +COPY --from=builder /app/webapps/console/public ./webapps/console/public + +COPY --from=builder /app/console.cron /etc/cron.d/console.cron +RUN chmod 0644 /etc/cron.d/console.cron +RUN crontab /etc/cron.d/console.cron + +EXPOSE 3000 + +HEALTHCHECK CMD curl --fail http://localhost:3000/api/healthcheck || exit 1 + +ENV NODE_ENV=production +ENV JITSU_VERSION_COMMIT_SHA=${JITSU_BUILD_COMMIT_SHA} +ENV JITSU_VERSION_DOCKER_TAG=${JITSU_BUILD_DOCKER_TAG} +ENV JITSU_VERSION_STRING=${JITSU_BUILD_VERSION} + +ENTRYPOINT ["sh", "-c", "/app/docker-start-console.sh"] + +FROM base as rotor + +ARG JITSU_BUILD_VERSION=dev, +ARG JITSU_BUILD_DOCKER_TAG=dev, +ARG JITSU_BUILD_COMMIT_SHA=unknown, + + +WORKDIR /app +RUN addgroup --system --gid 1001 runner +RUN adduser --system --uid 1001 runner +USER runner + +EXPOSE 3401 + +COPY --from=builder /app/services/rotor/dist . + +ENV NODE_ENV=production +ENV JITSU_VERSION_COMMIT_SHA=${JITSU_BUILD_COMMIT_SHA} +ENV JITSU_VERSION_DOCKER_TAG=${JITSU_BUILD_DOCKER_TAG} +ENV JITSU_VERSION_STRING=${JITSU_BUILD_VERSION} + +CMD ["--no-node-snapshot", "--max-old-space-size=2048", "main.js"] + +FROM base as profiles + +ARG JITSU_BUILD_VERSION=dev, +ARG JITSU_BUILD_DOCKER_TAG=dev, +ARG JITSU_BUILD_COMMIT_SHA=unknown, + + +WORKDIR /app +RUN addgroup --system --gid 1001 runner +RUN adduser --system --uid 1001 runner +USER runner + +EXPOSE 3401 + +COPY --from=builder /app/services/profiles/dist . + +ENV NODE_ENV=production +ENV JITSU_VERSION_COMMIT_SHA=${JITSU_BUILD_COMMIT_SHA} +ENV JITSU_VERSION_DOCKER_TAG=${JITSU_BUILD_DOCKER_TAG} +ENV JITSU_VERSION_STRING=${JITSU_BUILD_VERSION} + +CMD ["--no-node-snapshot", "--max-old-space-size=2048", "main.js"] diff --git a/builder.Dockerfile b/builder.Dockerfile index 2506cdd5c..2d5a7748b 100644 --- a/builder.Dockerfile +++ b/builder.Dockerfile @@ -4,17 +4,15 @@ # Run `docker login` # Build & push it with -# docker buildx build --platform linux/amd64 . -f builder.Dockerfile --push -t jitsucom/node16builder:latest +# docker buildx build --platform linux/amd64 . -f builder.Dockerfile --push -t jitsucom/node22builder:latest -FROM debian:bullseye-slim +FROM node:22-bookworm RUN apt-get update -# Telnet is useful for debugging, and we need curl for Node 16 -RUN apt-get install git curl telnet python3 g++ make -y -RUN curl -sL https://deb.nodesource.com/setup_16.x | bash - -#We need bash for pnpm setup -RUN apt-get install nodejs bash -y - +# Telnet is useful for debugging, and we need curl for Node +RUN apt-get install git curl telnet python3 ca-certificates gnupg g++ make -y RUN npm -g install pnpm -RUN npm install --global playwright@1.31.2 + +#Should be the same as playwrite version in ./libs/jitsu-js/package.json +RUN npm install --global playwright@1.39.0 RUN playwright install --with-deps diff --git a/cli/build-scripts/babel.config.cjs b/cli/build-scripts/babel.config.cjs new file mode 100644 index 000000000..e60440a86 --- /dev/null +++ b/cli/build-scripts/babel.config.cjs @@ -0,0 +1,4 @@ +module.exports = { + presets: ["@babel/preset-env", "@babel/preset-typescript"], + plugins: [], +}; diff --git a/cli/build-scripts/bin/jitsu-build-scripts b/cli/build-scripts/bin/jitsu-build-scripts new file mode 100755 index 000000000..864e6337f --- /dev/null +++ b/cli/build-scripts/bin/jitsu-build-scripts @@ -0,0 +1,3 @@ +#!/usr/bin/env node + +require("../dist/main.js"); \ No newline at end of file diff --git a/cli/build-scripts/package.json b/cli/build-scripts/package.json new file mode 100644 index 000000000..44db75a20 --- /dev/null +++ b/cli/build-scripts/package.json @@ -0,0 +1,37 @@ +{ + "name": "jitsu-build-scripts", + "version": "0.0.0", + "description": "", + "author": "Jitsu Dev Team ", + "publishConfig": { + "access": "public" + }, + "bin": "./bin/jitsu-build-scripts", + "license": "MIT", + "private": false, + "scripts": { + "clean": "rm -rf ./dist", + "compile": "tsc -p . ", + "build": "pnpm compile && webpack", + "exec": "ts-node src/index.ts" + }, + "dependencies": { + "boxen": "^7.1.1", + "colorette": "^2.0.20", + "semver": "^7.5.4", + "simple-git": "^3.22.0", + "string-width": "^7.0.0", + "tslib": "^2.6.3" + }, + "devDependencies": { + "commander": "^11.0.0", + "@types/node": "^18.15.3", + "@types/semver": "^7.5.6", + "@babel/preset-env": "^7.23.2", + "@babel/preset-typescript": "^7.23.2", + "babel-loader": "^9.1.3", + "webpack": "^5.99.5", + "webpack-cli": "^6.0.1", + "juava": "workspace:*" + } +} diff --git a/cli/build-scripts/src/box.ts b/cli/build-scripts/src/box.ts new file mode 100644 index 000000000..d840ddc17 --- /dev/null +++ b/cli/build-scripts/src/box.ts @@ -0,0 +1,60 @@ +import { color } from "./colors"; +const chars = { + topLeft: "┌", + topRight: "┐", + bottomRight: "┘", + bottomLeft: "└", + vertical: "│", + horizontal: "─", +}; + +function stringWidth(str: string): number { + let width = 0; + + for (const char of Array.from(str)) { + const codePoint = char.codePointAt(0); + + // Ignore control characters and other non-printable characters + if (codePoint && (codePoint <= 31 || codePoint === 127)) { + continue; + } + + // Basic check for surrogate pairs (common in emojis) + if (codePoint && codePoint > 0xffff) { + width += 2; // Assuming emoji width as 2 + } else { + width += 1; + } + } + + return width; +} +export function drawBox({ content }: { content: string | string[] }): string { + const contentLines = typeof content === "string" ? content.split("\n") : content; + const maxWidth = Math.max(...contentLines.map(stringWidth)); + + const resLines: string[] = []; + const px = 2; + const mx = 0; + resLines.push( + " ".repeat(mx) + color.gray(chars.topLeft + chars.horizontal.repeat(maxWidth + px * 2) + chars.topRight) + ); + + resLines.push( + ...contentLines.map( + line => + " ".repeat(mx) + + color.gray(chars.vertical) + + " ".repeat(px) + + line.padEnd(maxWidth, " ") + + " ".repeat(px) + + color.gray(chars.vertical) + ) + ); + + resLines.push( + " ".repeat(mx) + color.gray(chars.bottomLeft + chars.horizontal.repeat(maxWidth + px * 2) + chars.bottomRight) + ); + + return resLines.join("\n"); +} diff --git a/cli/build-scripts/src/colors.ts b/cli/build-scripts/src/colors.ts new file mode 100644 index 000000000..198c45e50 --- /dev/null +++ b/cli/build-scripts/src/colors.ts @@ -0,0 +1,3 @@ +import { createColors } from "colorette"; + +export const color = createColors(); diff --git a/cli/build-scripts/src/commands/docker.ts b/cli/build-scripts/src/commands/docker.ts new file mode 100644 index 000000000..b116fbcbc --- /dev/null +++ b/cli/build-scripts/src/commands/docker.ts @@ -0,0 +1,242 @@ +import * as path from "path"; +import simpleGit, { SimpleGit } from "simple-git"; +import * as fs from "fs"; +import { compare as semverCompare, parse as semverParse, SemVer } from "semver"; +import * as child_process from "child_process"; +import { color } from "../colors"; +import { drawBox } from "../box"; + +type ReleaseStream = "beta" | "latest"; + +type Target = "console" | "rotor" | "bulker" | "ingest" | "syncctl" | "sidecar"; + +const git = simpleGit(); + +export type DockerArgs = { + targets: Target[]; + version?: string; + release?: ReleaseStream; + push?: boolean; + platform?: "linux/amd64" | "linux/arm64" | "linux/amd64,linux/arm64"; + gitTagPrefix?: string; + dryRun?: boolean; + pushGitTag?: boolean; + logs?: string; +}; + +async function getLastCommitSha(git: SimpleGit): Promise { + return (await git.log({ n: 1 }))?.latest?.hash || "unknown"; +} + +export async function docker(dir: string | undefined, args: DockerArgs): Promise { + let version = args.version; + const projectRootDir = dir ? path.resolve(process.cwd(), dir) : process.cwd(); + const dockerTag: ReleaseStream = args.release || "beta"; + const tagPrefix = args.gitTagPrefix || "jitsu2"; + console.log( + drawBox({ + content: [ + `Hi, I'm Jitsu Docker Builder!`, + ``, + `I'm going to build docker images for \`${dockerTag}\` release stream`, + ], + }) + ); + console.log( + `📦 Project root directory: ${color.cyan(projectRootDir)}${ + dir ? ` (provided as ${color.cyan(dir)})` : ` (default)` + }}` + ); + + //git check that all changes are pulled from remote + await git.fetch(); + const status = await git.status(); + if (status.behind > 0) { + throw new Error(`You are ${status.behind} commits behind the remote. Please pull the changes first.`); + } + + version = await adjustVersion(version, dockerTag, tagPrefix); + console.info(`💁🏻‍ Adjusted version for ${dockerTag} release: ` + color.bold(color.cyan(version))); + const gitTag = `${tagPrefix}-v${version}`; + if ((await git.tags()).all.includes(gitTag)) { + throw new Error(`Tag ${gitTag} for next version ${version} already exists. Aborting`); + } + const logsDir = args.logs ? path.resolve(__dirname, args.logs) : undefined; + if (logsDir) { + fs.mkdirSync(logsDir, { recursive: true }); + } + + const targets = args.targets.flatMap(t => t.split(",")); + + for (const dockerTarget of targets) { + console.log( + `🚀 Building ${color.cyan(dockerTarget)} docker with tags: ${color.cyan( + `jitsucom/${dockerTarget}:${version}` + )} and ${color.cyan(`jitsucom/${dockerTarget}:${dockerTag}`)}...` + ); + const dockerImageName = `jitsucom/${dockerTarget}`; + const dockerArgs = [ + "buildx build", + `--target ${dockerTarget}`, + "--progress=plain", + `--platform ${args.platform || "linux/amd64"}`, + `-t ${dockerImageName}:${dockerTag}`, + `-t ${dockerImageName}:${version}`, + `--build-arg JITSU_BUILD_VERSION=${version}`, + `--build-arg JITSU_BUILD_DOCKER_TAG=${dockerTag}`, + `--build-arg JITSU_BUILD_COMMIT_SHA=${await getLastCommitSha(git)}`, + `-f all.Dockerfile`, + args.push ? "--push" : "--load", + ".", + ].filter(Boolean); + const qt = `${color.gray(color.bold("`"))}`; + console.log( + `🎻 Docker command\n\n\t${qt}${color.cyan( + `docker ${dockerArgs.filter(args => !args.startsWith("--progress")).join(" ")}` + )}${qt}\n\n\t` + ); + if (args.dryRun) { + console.log(`🏃🏻 Skipping actual build because of ${color.cyan("--dry-run")} flag`); + } else { + const logPath = logsDir + ? path.join(logsDir, `${dockerTarget}-docker-build-${new Date().toISOString()}.log`) + : undefined; + const stream = logsDir ? fs.createWriteStream(logPath!) : undefined; + if (stream) { + console.log(`📝 Writing logs to ${color.cyan(logPath!)}`); + stream.write(`Building ${dockerImageName}:${dockerTag}\n`); + stream.write(`Command:\n\tdocker ${dockerArgs.join(" ")}\n`); + stream.write("=".repeat(80)); + stream.write("\n\n"); + } + const exitCode = await runCommand("docker", { + args: [...dockerArgs], + cwd: projectRootDir, + outputHandler: (data, opts) => { + const dataStr = data.toString(); + dataStr.split("\n").forEach(line => { + if (!logsDir) { + process.stdout.write(`${color.green(dockerTarget)}: ${line}\n`); + } + if (stream) { + stream.write(`${line}\n`); + } + }); + }, + }); + if (stream) { + stream.write("=".repeat(80)); + stream.write("\n\n"); + stream.end(); + } + if (exitCode != 0) { + throw new Error(`Docker build failed with exit code ${exitCode}`); + } + } + } + if (!args.dryRun && args.push && args.pushGitTag) { + console.log(`Pushing git tag ${gitTag}...`); + await git.addTag(gitTag); + try { + await git.pushTags("origin", [gitTag]); + } catch (e: any) { + throw new Error(`Failed to push git tag ${gitTag}: ${e.message}`); + } + } +} + +function formatDate(date: Date): number { + const pad = number => number.toString().padStart(2, "0"); + + let year = date.getFullYear(); + let month = pad(date.getMonth() + 1); // getMonth() returns 0-11 + let day = pad(date.getDate()); + let hour = pad(date.getHours()); + let minute = pad(date.getMinutes()); + let second = pad(date.getSeconds()); + + return parseInt(`${year}${month}${day}${hour}${minute}${second}`); +} + +async function adjustVersion( + manualVersion: string | undefined, + str: ReleaseStream, + tagPrefix: string +): Promise { + let version: SemVer | undefined; + if (!manualVersion) { + const gitTags = await git.tags(); + const allSemvers = gitTags.all + .filter(tag => tag.startsWith(tagPrefix + "-") && tag.indexOf("beta") < 0) + .map(tag => tag.slice(tagPrefix.length + 1)) + .map(tag => (tag.startsWith("v") ? tag.slice(1) : tag)) + .map(t => semverParse(t)) + .filter(Boolean) as SemVer[]; + + version = allSemvers.sort((a, b) => semverCompare(a, b)).pop(); + + if (!version) { + throw new Error(`Couldn't guess version from git tags. Please provide --version param`); + } + } else { + const bs = semverParse(manualVersion); + if (!bs) { + throw new Error(`Cannot parse --version ${manualVersion} param as semver`); + } + version = bs; + } + + if (str === "beta") { + const gitHistory = await git.log(); + const latest = gitHistory.all.length; + const revision = gitHistory.latest!.hash.slice(0, 7); + return `${version.major}.${version.minor}.${latest}-${str}.${formatDate(new Date())}.${revision}`; + } else { + if (manualVersion) { + const nextVersion = `${version.major}.${version.minor}.${version.patch}`; + console.log(`Going to use ${nextVersion} as next version`); + return nextVersion; + } else { + const nextVersion = `${version.major}.${version.minor}.${version.patch + 1}`; + console.log(`Found latest stable release: ' + ${version.version}. Going to use ${nextVersion} as next version`); + return nextVersion; + } + } +} + +export function runCommand( + command: string, + opts: { + args?: string[]; + cwd?: string; + outputHandler?: (data: any, opts: { stream: "stderr" | "stdout" }) => void; + } = {} +): Promise { + return new Promise((resolve, reject) => { + const fullCommand = `${command}${opts.args && opts.args.length > 0 ? " " + opts.args?.join(" ") : ""}`; + const proc = child_process.exec( + fullCommand, + { env: process.env, cwd: opts.cwd ? path.resolve(__dirname, opts.cwd) : undefined }, + error => { + if (error) { + console.log( + `Command \`${fullCommand}\`\n\tfailed with exit code ${error.code}: ${error?.message} || unknown error` + ); + reject(error); + } else { + resolve(0); + } + } + ); + proc.stdout?.on("data", data => { + if (data && opts.outputHandler) { + opts.outputHandler(data, { stream: "stdout" }); + } + }); + proc.stderr?.on("data", data => { + if (data && opts.outputHandler) { + opts.outputHandler(data, { stream: "stderr" }); + } + }); + }); +} diff --git a/cli/build-scripts/src/index.ts b/cli/build-scripts/src/index.ts new file mode 100644 index 000000000..0fe875436 --- /dev/null +++ b/cli/build-scripts/src/index.ts @@ -0,0 +1,40 @@ +import { Command, Option, Argument } from "commander"; +import { docker } from "./commands/docker"; +import pkg from "../package.json"; + +const version = pkg.version; +const packageName = pkg.name; + +console.log("Jitsu build-scripts. Version: " + version + "\n"); + +const p = new Command(); + +p.name("build-scripts").description("CLI command to create, test and deploy extensions for Jitsu Next"); + +p.command("docker") + .description("Builds and pushes docker images for Jitsu Next services") + .addArgument(new Argument("[dir]", "directory of project to build. Must contain all.Dockerfile file")) + .addOption(new Option("-t, --targets ", "list of Dockerfile targets to build.")) + .option( + "-v, --version ", + "base version of release in semver format. Required for the first release or version bumps." + ) + .addOption(new Option("--release ", "docker release tag to use").default("beta").choices(["beta", "latest"])) + .option("--push", "whether to push images to docker hub or load it locally", false) + .addOption( + new Option("--platform ", "docker platform to build for") + .default("linux/amd64") + .choices(["linux/amd64", "linux/arm64", "linux/amd64,linux/arm64"]) + ) + .option("--gitTagPrefix ", "prefix that will be used for git tag before version", "jitsu2") + .option("--dryRun", "dry run. Just prints commands that will be executed and do not build anything", false) + .option("--pushGitTag", "push git tag to origin (only after docker push)", true) + .option("--logs ", "path to directory where logs will be stored") + .action(docker); + +//version +//p.version(packageName + " " + version, "-v, --version"); +//help +//p.helpOption("--help", "display help for command"); + +p.parse(); diff --git a/cli/build-scripts/tsconfig.json b/cli/build-scripts/tsconfig.json new file mode 100644 index 000000000..3b3ffd3c2 --- /dev/null +++ b/cli/build-scripts/tsconfig.json @@ -0,0 +1,28 @@ +{ + "compilerOptions": { + "outDir": "./compiled", + "rootDir": ".", + "noImplicitAny": false, + "allowSyntheticDefaultImports": true, + "importHelpers": true, + "removeComments": true, + "target": "ES2021", + "module": "commonjs", + "lib": [ + "dom","esnext" + ], + "allowJs": true, + "strict": true, + "esModuleInterop": true, + "moduleResolution": "node", + "resolveJsonModule": true, + "skipLibCheck": true + }, + "exclude": [ + "node_modules" + ], + "include": [ + "src/**/*" + ] +} + diff --git a/cli/build-scripts/webpack.config.js b/cli/build-scripts/webpack.config.js new file mode 100644 index 000000000..27b02df4f --- /dev/null +++ b/cli/build-scripts/webpack.config.js @@ -0,0 +1,43 @@ +const path = require("path"); +const webpack = require("webpack"); + +const config = { + entry: "./src/index.ts", + target: "node", + externals: { + "../package.json": "require('../package.json')", + }, + node: { + __dirname: false, + }, + devtool: "source-map", + output: { + path: path.resolve(__dirname, "dist"), + }, + plugins: [ + new webpack.IgnorePlugin({ resourceRegExp: /^fsevents$/ }), // Ignore MacOS-only module + ], + module: { + rules: [ + { + test: /\.(ts|tsx)$/i, + use: { + loader: "babel-loader", + }, + }, + { + test: /\.node$/, + loader: "node-loader", + }, + ], + }, + optimization: { + minimize: false, + }, + resolve: { + extensions: [".tsx", ".ts", ".jsx", ".js", ".node", "..."], + }, + mode: "production", +}; + +module.exports = () => config; diff --git a/cli/jitsu-cli/babel.config.cjs b/cli/jitsu-cli/babel.config.cjs new file mode 100644 index 000000000..e60440a86 --- /dev/null +++ b/cli/jitsu-cli/babel.config.cjs @@ -0,0 +1,4 @@ +module.exports = { + presets: ["@babel/preset-env", "@babel/preset-typescript"], + plugins: [], +}; diff --git a/cli/jitsu-cli/bin/jitsu-cli b/cli/jitsu-cli/bin/jitsu-cli new file mode 100755 index 000000000..864e6337f --- /dev/null +++ b/cli/jitsu-cli/bin/jitsu-cli @@ -0,0 +1,3 @@ +#!/usr/bin/env node + +require("../dist/main.js"); \ No newline at end of file diff --git a/cli/jitsu-cli/data/page.json b/cli/jitsu-cli/data/page.json new file mode 100644 index 000000000..5424407c5 --- /dev/null +++ b/cli/jitsu-cli/data/page.json @@ -0,0 +1,54 @@ +{ + "type": "page", + "properties": { + "title": "Example page event", + "url": "https://example.com/", + "path": "/", + "hash": "", + "search": "", + "currency": "USD", + "width": 1458, + "height": 1186 + }, + "userId": "user@example.com", + "anonymousId": "f6ps414GyUoRLws4r4NXD1kF", + "timestamp": "2023-10-23T08:56:09.043Z", + "sentAt": "2023-10-23T08:56:09.043Z", + "messageId": "y4eAqqw9PFErquqfT9IDZDRh", + "writeKey": "Bysczu8o75nkuoQtlpyJWE11", + "context": { + "library": { + "name": "jitsu-js", + "version": "1.0.0" + }, + "ip": "69.162.81.155", + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/111.0", + "locale": "en-US", + "screen": { + "width": 2304, + "height": 1296, + "innerWidth": 1458, + "innerHeight": 1186, + "density": 2 + }, + "traits": { + "email": "user@example.com" + }, + "page": { + "path": "/", + "referrer": "", + "referring_domain": "", + "host": "example.com", + "search": "", + "title": "Example page event", + "url": "https://example.com/", + "encoding": "UTF-8" + }, + "campaign": { + "name": "example", + "source": "g" + } + }, + "requestIp": "69.162.81.155", + "receivedAt": "2023-10-23T08:56:09.043Z" +} diff --git a/cli/jitsu-cli/package.json b/cli/jitsu-cli/package.json index aa1a145c6..31c21d422 100644 --- a/cli/jitsu-cli/package.json +++ b/cli/jitsu-cli/package.json @@ -6,17 +6,66 @@ "publishConfig": { "access": "public" }, + "bin": "./bin/jitsu-cli", "license": "MIT", "private": false, - "scripts": {}, + "scripts": { + "clean": "rm -rf ./dist", + "compile": "tsc -p . ", + "build": "pnpm compile && webpack", + "run": "pnpm build && node dist/main.js", + "login": "pnpm build && node dist/main.js login", + "init child": "pnpm build && node dist/main.js init", + "build child": "pnpm build && node dist/main.js build", + "run child": "pnpm build && node dist/main.js run -e ./data/page.json", + "test child": "pnpm build && node dist/main.js test", + "deploy child": "pnpm build && node dist/main.js deploy" + }, "dependencies": { - "chalk": "^5.0.1", - "minimist": "^1.2.6", - "tslib": "^2.4.0", - "juava": "workspace:*" + "figlet": "^1.6.0", + "jest-cli": "^29.7.0", + "tslib": "^2.6.3", + "typescript": "^5.6.3" }, "devDependencies": { + "@babel/preset-env": "^7.23.2", + "@babel/preset-typescript": "^7.23.2", + "@jitsu/functions-lib": "workspace:*", + "@jitsu/protocols": "workspace:*", + "@rollup/plugin-commonjs": "^28.0.2", + "@rollup/plugin-json": "^6.0.0", + "@rollup/plugin-node-resolve": "^16.0.0", + "@rollup/plugin-terser": "^0.4.3", + "@rollup/plugin-typescript": "^11.1.3", "@types/chalk": "^2.2.0", - "@types/minimist": "^1.2.2" + "@types/commander": "^2.12.2", + "@types/express": "^4.17.21", + "@types/inquirer": "^9.0.3", + "@types/jest": "^29.5.5", + "@types/lodash": "^4.14.198", + "@types/node": "^18.15.3", + "@types/webpack": "^5.28.5", + "@webpack-cli/generators": "^3.0.7", + "babel-loader": "^9.1.3", + "chalk": "^5.3.0", + "commander": "^11.0.0", + "cross-fetch": "^4.0.0", + "cuid": "^2.1.8", + "etag": "^1.8.1", + "inquirer": "^9.2.11", + "jest": "^29.7.0", + "json5": "^2.2.3", + "juava": "workspace:*", + "lodash": "^4.17.21", + "node-fetch": "^3.3.2", + "node-loader": "^2.0.0", + "prismjs": "^1.30.0", + "rollup": "^3.29.5", + "semver": "^7.5.4", + "ts-jest": "^29.1.1", + "ts-loader": "^9.5.1", + "ts-node": "^10.9.2", + "webpack": "^5.99.5", + "webpack-cli": "^6.0.1" } } diff --git a/cli/jitsu-cli/src/commands/build.ts b/cli/jitsu-cli/src/commands/build.ts new file mode 100644 index 000000000..4d10a6fb1 --- /dev/null +++ b/cli/jitsu-cli/src/commands/build.ts @@ -0,0 +1,184 @@ +import path from "path"; +import { mkdirSync, readdirSync, writeFileSync, existsSync, lstatSync } from "fs"; +import typescript from "@rollup/plugin-typescript"; +import resolve from "@rollup/plugin-node-resolve"; +import commonjs from "@rollup/plugin-commonjs"; +import rollupJson from "@rollup/plugin-json"; +import { ModuleFormat, rollup } from "rollup"; +import { exec } from "child_process"; +import { loadPackageJson } from "./shared"; +import { b, green, red } from "../lib/chalk-code-highlight"; +import { getFunctionFromFilePath } from "../lib/compiled-function"; +import * as ts from "typescript"; + +export async function build({ dir }: { dir?: string }) { + const { packageJson, projectDir } = await loadPackageJson(dir || process.cwd()); + + console.log(`Building ${b(packageJson.name)} project`); + const errors = checkTypescript(projectDir); + if (errors) { + console.error(`Found ${errors.length} errors in functions files. Exiting`); + process.exit(1); + } + + try { + await buildFiles(projectDir, "functions"); + await buildFiles(projectDir, "profiles"); + } catch (e: any) { + throw new Error( + `Some of the functions failed to compile. See details above. Last error: ${e.message || "unknown"}` + ); + } + + console.log(`${b("Build finished.")}`); +} + +const run = async cmd => { + const child = exec(cmd, err => { + if (err) { + console.error(err); + return; + } + }); + child.stdout?.pipe(process.stdout); + child.stderr?.pipe(process.stderr); + return new Promise(resolve => child.on("close", resolve)); +}; + +async function buildFiles(projectDir: string, dir: string = "") { + let lastError: any = undefined; + const srcDir = path.resolve(projectDir, "src", dir); + if (!existsSync(srcDir)) { + console.info(`${b(dir)} directory not found in ${b(path.resolve(projectDir, "src"))}`); + return; + } + const files = readdirSync(srcDir); + if (files.length === 0) { + console.warn(`No functions found in ${b(srcDir)} directory`); + return; + } + for (const file of files) { + if (lstatSync(path.resolve(srcDir, file)).isDirectory()) { + try { + await buildFiles(projectDir, path.join(dir, file)); + } catch (e: any) { + lastError = e; + } + continue; + } + try { + await buildFile(projectDir, dir, file); + } catch (e: any) { + console.error( + [ + `${red(`⚠`)} Function ${b(file)} failed to compile: ${red(e?.message)}. See details below`, + ...(e?.stack?.split("\n") || []).map(s => ` ${s}`), + ] + .filter(Boolean) + .join("\n") + ); + lastError = e; + } + } + if (lastError) { + throw lastError; + } +} + +async function buildFile(projectDir: string, dir: string, fileName: string) { + const funcFile = path.resolve(projectDir, "src", path.join(dir, fileName)); + process.chdir(projectDir); + + const rollupPlugins = [ + typescript(), + resolve({ preferBuiltins: false }), + commonjs(), + rollupJson(), + // terser(), + ]; + + const bundle = await rollup({ + input: [funcFile], + plugins: rollupPlugins, + external: ["@jitsu/functions-lib"], + logLevel: "silent", + }); + + let format: ModuleFormat = "es"; + let output = await bundle.generate({ + dir: projectDir, + format: format, + }); + + mkdirSync(path.resolve(projectDir, "dist/" + dir), { recursive: true }); + const compiledFunctionPath = `dist/${dir}/${fileName.replace(".ts", ".js")}`; + writeFileSync(path.resolve(projectDir, compiledFunctionPath), output.output[0].code); + //to verify that function is readable + const compiledFunction = await getFunctionFromFilePath(path.resolve(projectDir, compiledFunctionPath), "function"); + console.log( + [`${green(`✓`)} Function ${b(fileName)} compiled successfully`, ` slug = ${b(compiledFunction.meta.slug)}`] + .filter(Boolean) + .join("\n") + ); +} + +function checkTypescript(projectDir: string): string[] | void { + const tsconfigPath = path.resolve(projectDir, "tsconfig.json"); + if (!existsSync(tsconfigPath)) { + console.info(`No ${b("tsconfig.json")} file found in ${b(projectDir)}. Assuming JavaScript project`); + return; + } + console.log(`Checking TypeScript files in ${b(projectDir)}`); + let compilerOptions: ts.CompilerOptions = {}; + let filenames: string[] = []; + const tsconfig = ts.readConfigFile(tsconfigPath, ts.sys.readFile); + tsconfig.config.compilerOptions = { + ...tsconfig.config.compilerOptions, + typeRoots: [path.resolve(projectDir, "node_modules", "@types")], + checkJs: true, + allowJs: true, + noEmit: true, + esModuleInterop: typeof compilerOptions.esModuleInterop !== "undefined" ? compilerOptions.esModuleInterop : true, + moduleResolution: + typeof compilerOptions.moduleResolution !== "undefined" ? compilerOptions.moduleResolution : "node", + target: "esnext", + module: "esnext", + }; + const parsed = ts.parseJsonConfigFileContent(tsconfig.config, ts.sys, path.dirname(tsconfigPath)); + filenames = parsed.fileNames; + compilerOptions = parsed.options; + + //console.log(`Filenames ${JSON.stringify(filenames)}`); + + let program = ts.createProgram(filenames, compilerOptions); + let emitResult = program.emit(); + let allDiagnostics = ts.getPreEmitDiagnostics(program).concat(emitResult.diagnostics); + const errors: string[] = []; + allDiagnostics.forEach(diagnostic => { + let logF = console.log; + switch (diagnostic.category) { + case ts.DiagnosticCategory.Error: + logF = (...args) => { + console.error(...args); + errors.push(args.join(" ")); + }; + break; + case ts.DiagnosticCategory.Warning: + logF = console.warn; + break; + case ts.DiagnosticCategory.Message: + case ts.DiagnosticCategory.Suggestion: + logF = console.info; + } + if (diagnostic.file) { + let { line, character } = ts.getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start!); + let message = ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n"); + logF(`${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`); + } else { + logF(ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n")); + } + }); + if (errors.length > 0) { + return errors; + } +} diff --git a/cli/jitsu-cli/src/commands/deploy.ts b/cli/jitsu-cli/src/commands/deploy.ts new file mode 100644 index 000000000..5c45db3cc --- /dev/null +++ b/cli/jitsu-cli/src/commands/deploy.ts @@ -0,0 +1,261 @@ +import path from "path"; +import { homedir } from "os"; +import inquirer from "inquirer"; +import { existsSync, readdirSync, readFileSync } from "fs"; +import { loadPackageJson } from "./shared"; +import fetch from "node-fetch"; +import cuid from "cuid"; +import { b, green, red } from "../lib/chalk-code-highlight"; +import { getFunctionFromFilePath } from "../lib/compiled-function"; + +function readLoginFile() { + const configFile = `${homedir()}/.jitsu/jitsu-cli.json`; + if (!existsSync(configFile)) { + console.error(red("Please login first with `jitsu-cli login` command or provide --apikey option")); + process.exit(1); + } + return JSON.parse(readFileSync(configFile, { encoding: "utf-8" })); +} + +type Args = { + dir?: string; + workspace?: string; + name?: string[]; + apikey?: string; + host?: string; +}; + +type Workspace = { + id?: string; + name?: string[]; + slug?: string; +}; + +export async function deploy({ dir, workspace, name: names, ...params }: Args) { + const { packageJson, projectDir } = await loadPackageJson(dir || process.cwd()); + + const { host, apikey } = params.apikey + ? { apikey: params.apikey, host: params.host || "https://use.jitsu.com" } + : readLoginFile(); + + console.log( + `Deploying ${b(packageJson.name)} project.${ + names && names.length > 0 ? ` (selected functions: ${names.join(",")})` : "" + }` + ); + + const res = await fetch(`${host}/api/workspace`, { + method: "GET", + headers: { + Authorization: `Bearer ${apikey}`, + }, + }); + if (!res.ok) { + console.error(red(`Cannot get workspace list:\n${b(await res.text())}`)); + process.exit(1); + } + const workspaces = (await res.json()) as any[]; + + let workspaceId = workspace; + if (!workspace) { + if (workspaces.length === 0) { + console.error(`${red("No workspaces found")}`); + process.exit(1); + } else if (workspaces.length === 1) { + workspaceId = workspaces[0].id; + } else { + workspaceId = ( + await inquirer.prompt([ + { + type: "list", + name: "workspaceId", + message: `Select workspace:`, + choices: workspaces.map(w => ({ + name: `${w.name} (${w.id})`, + value: w.id, + })), + }, + ]) + ).workspaceId; + } + } + + const workspaceObj = workspaces.find(w => w.id === workspaceId); + const workspaceName = workspaceObj?.name; + if (!workspaceId || !workspaceName) { + console.error(red(`Workspace with id ${workspaceId} not found`)); + process.exit(1); + } + await deployFunctions({ ...params, host, apikey, name: names }, projectDir, packageJson, workspaceObj, "function"); + await deployFunctions({ ...params, host, apikey, name: names }, projectDir, packageJson, workspaceObj, "profile"); +} + +async function deployFunctions( + { host, apikey, name: names }: Args, + projectDir: string, + packageJson: any, + workspace: Workspace, + kind: "function" | "profile" +) { + const selected = names ? names.flatMap(n => n.split(",")).map(n => n.trim()) : undefined; + const dir = `dist/${kind}s`; + const functionsDir = path.resolve(projectDir, dir); + + const functionsFiles = readdirSync(functionsDir); + if (functionsFiles.length === 0) { + console.warn( + red(`Can't find function files in ${b(dir)} directory. Please make sure that you have built the project.`) + ); + process.exit(1); + } + const selectedFiles: string[] = []; + if (selected) { + const s = selected.map(n => (n.endsWith(".js") ? n : `${n.replace(".ts", "")}.js`)); + for (const file of s) { + if (functionsFiles.includes(file)) { + selectedFiles.push(file); + } else { + console.error( + red( + `Can't find function file ${b(file)} in ${b( + dir + )} directory. Please make sure that you have built the project.` + ) + ); + process.exit(1); + } + } + } else { + selectedFiles.push(...functionsFiles); + } + + let profileBuilders: any[] = []; + if (kind == "profile") { + const res = await fetch(`${host}/api/${workspace.id}/config/profile-builder`, { + method: "GET", + headers: { + Authorization: `Bearer ${apikey}`, + }, + }); + if (!res.ok) { + console.error(red(`Cannot get profile builders list:\n${b(await res.text())}`)); + process.exit(1); + } + profileBuilders = ((await res.json()) as any).profileBuilders as any[]; + } + + for (const file of selectedFiles) { + console.log( + `${b(`𝑓`)} Deploying function ${b(path.basename(file))} to workspace ${workspace.name} (${host}/${ + workspace.slug || workspace.id + })` + ); + await deployFunction( + { host, apikey }, + packageJson, + workspace, + kind, + path.resolve(functionsDir, file), + profileBuilders + ); + } +} + +async function deployFunction( + { host, apikey }: Args, + packageJson: any, + workspace: Workspace, + kind: "function" | "profile", + file: string, + profileBuilders: any[] = [] +) { + const code = readFileSync(file, "utf-8"); + + const wrapped = await getFunctionFromFilePath(file, kind, profileBuilders); + const meta = wrapped.meta; + if (meta) { + console.log(` meta: slug=${meta.slug}, name=${meta.name || "not set"}`); + } else { + console.log(`File ${b(path.basename(file))} doesn't have function meta information. ${red("Skipping")}`); + return; + } + let existingFunctionId: string | undefined; + if (meta.slug) { + const res = await fetch(`${host}/api/${workspace.id}/config/function`, { + headers: { + Authorization: `Bearer ${apikey}`, + }, + }); + if (!res.ok) { + console.error(red(`Cannot add function to workspace:\n${b(await res.text())}`)); + process.exit(1); + } else { + const existing = (await res.json()) as any; + existingFunctionId = existing.objects.find(f => f.slug === meta.slug || f.id === meta.id)?.id; + } + } + let functionPayload = {}; + if (kind === "profile") { + functionPayload = { + draft: code, + kind: "profile", + }; + } else { + functionPayload = { + code, + }; + } + if (!existingFunctionId) { + const id = cuid(); + const res = await fetch(`${host}/api/${workspace.id}/config/function`, { + method: "POST", + headers: { + Authorization: `Bearer ${apikey}`, + }, + body: JSON.stringify({ + id, + workspaceId: workspace.id, + type: "function", + origin: "jitsu-cli", + slug: meta.slug, + description: meta.description, + version: packageJson.version, + name: meta.name, + // we always add code to the initial function creation + code, + ...functionPayload, + }), + }); + if (!res.ok) { + console.error(red(`Cannot add function to workspace:\n${b(await res.text())}`)); + process.exit(1); + } else { + console.log(`Function ${b(meta.name)} was successfully added to workspace ${workspace.name} with id: ${b(id)}`); + } + } else { + const id = existingFunctionId; + const res = await fetch(`${host}/api/${workspace.id}/config/function/${id}`, { + method: "PUT", + headers: { + Authorization: `Bearer ${apikey}`, + }, + body: JSON.stringify({ + id: id, + workspaceId: workspace.id, + type: "function", + origin: "jitsu-cli", + slug: meta.slug, + description: meta.description, + version: packageJson.version, + name: meta.name, + ...functionPayload, + }), + }); + if (!res.ok) { + console.error(red(`⚠ Cannot deploy function ${b(meta.slug)}(${id}):\n${b(await res.text())}`)); + process.exit(1); + } else { + console.log(`${green(`✓`)} ${b(meta.name)} deployed successfully!`); + } + } +} diff --git a/cli/jitsu-cli/src/commands/init.ts b/cli/jitsu-cli/src/commands/init.ts new file mode 100644 index 000000000..34f1d5da7 --- /dev/null +++ b/cli/jitsu-cli/src/commands/init.ts @@ -0,0 +1,44 @@ +import inquirer from "inquirer"; +import path from "path"; +import { b } from "../lib/chalk-code-highlight"; +import * as fs from "fs"; +import { functionProjectTemplate } from "../templates/functions"; +import { write } from "../lib/template"; +import { jitsuCliVersion } from "../lib/version"; + +export async function init(dir?: string, opts?: { jitsuVersion?: string; allowNonEmptyDir?: boolean }) { + let projectName; + if (dir) { + dir = path.resolve(dir); + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir); + } else if (fs.readdirSync(dir).length > 0) { + const msg = `Directory ${b(dir)} is not empty, can't create project there`; + if (opts?.allowNonEmptyDir) { + console.warn(`Directory ${b(dir)} is not empty. Will create project there, files may be overwritten`); + } else { + console.error(msg); + process.exit(1); + } + } + projectName = path.basename(dir); + } else { + projectName = ( + await inquirer.prompt([ + { + type: "input", + name: "project", + message: `Enter project name. It will be used as a package name and directory name:`, + }, + ]) + ).project; + dir = path.resolve(projectName); + } + + write(dir, functionProjectTemplate, { + packageName: projectName, + jitsuVersion: opts?.jitsuVersion || jitsuCliVersion, + }); + + console.log(`Project ${b(projectName)} created!`); +} diff --git a/cli/jitsu-cli/src/commands/login.ts b/cli/jitsu-cli/src/commands/login.ts new file mode 100644 index 000000000..5cdb096f2 --- /dev/null +++ b/cli/jitsu-cli/src/commands/login.ts @@ -0,0 +1,100 @@ +import { decrypt, randomId } from "juava"; +import * as fs from "fs"; +import { mkdirSync, readFileSync, writeFileSync } from "fs"; +import { homedir } from "os"; +import readline from "readline"; +import { b, red } from "../lib/chalk-code-highlight"; +import inquirer from "inquirer"; + +const origin = "jitsu-cli"; +export async function logout({ force }: { force?: boolean }) { + const jitsuFile = `${homedir()}/.jitsu/jitsu-cli.json`; + if (fs.existsSync(jitsuFile)) { + if (force) { + fs.unlinkSync(jitsuFile); + } else { + const { confirm } = await inquirer.prompt([ + { + type: "confirm", + name: "confirm", + message: "Are you sure you want to logout?", + default: false, + }, + ]); + if (confirm) { + fs.unlinkSync(jitsuFile); + } else { + console.log("Logout cancelled"); + return; + } + } + console.log("You are logged out"); + } else { + console.log("You are not logged in"); + } +} + +export async function login({ host, apikey, force }: { host: string; apikey?: string; force?: boolean }) { + const jitsuDir = `${homedir()}/.jitsu`; + fs.mkdirSync(jitsuDir, { recursive: true }); + const jitsuFile = `${jitsuDir}/jitsu-cli.json`; + if (fs.existsSync(jitsuFile) && !force) { + const loginInfo = JSON.parse(readFileSync(jitsuFile, { encoding: "utf-8" })); + console.error( + red( + `Error: seems like you already logged into jitsu at ${loginInfo.host}. If you want to re-login again, use --force flag, or logout first with \`jitsu-cli logout\` command` + ) + ); + process.exit(1); + } + if (apikey) { + writeFileSync(jitsuFile, JSON.stringify({ host, apikey }, null, 2)); + console.info(`\nSuccess!`); + return; + } + let url = host; + if (!url.startsWith("http")) { + if (url.startsWith("localhost") || url.match(/^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/)) { + url = "http://" + url; + } else { + url = "https://" + url; + } + } + if (!url.endsWith("/")) { + url += "/"; + } + try { + const c = randomId(32); + console.log(`Please open this url in your browser and log in:\n\n${b(`${url}?origin=${origin}&c=${c}`)}`); + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }); + console.log("\nSuccessful authorization will provide you with a code."); + rl.question("Please paste it here: ", code => { + processCode(code, c, host); + rl.close(); + }); + } catch (e) { + console.error(e); + process.exit(1); + } +} + +function processCode(code: string, key: string, host: string) { + try { + const iv = `${origin}${code.substring(0, 16 - origin.length)}`; + const enc = code.substring(16 - origin.length); + const decoded = decrypt(key, iv, enc); + const { plaintext, id } = JSON.parse(decoded); + mkdirSync(`${homedir()}/.jitsu`, { recursive: true }); + writeFileSync( + `${homedir()}/.jitsu/jitsu-cli.json`, + JSON.stringify({ host, apikey: `${id}:${plaintext}` }, null, 2) + ); + console.info(`\nSuccess!`); + } catch (e) { + console.error(`\n${red("Incorrect code value")}`); + process.exit(1); + } +} diff --git a/cli/jitsu-cli/src/commands/shared.ts b/cli/jitsu-cli/src/commands/shared.ts new file mode 100644 index 000000000..47d44b38b --- /dev/null +++ b/cli/jitsu-cli/src/commands/shared.ts @@ -0,0 +1,38 @@ +import path from "path"; +import inquirer from "inquirer"; +import { existsSync, readFileSync } from "fs"; +import { b, red } from "../lib/chalk-code-highlight"; + +export async function loadPackageJson(projectDir: string): Promise<{ projectDir: string; packageJson: any }> { + let packageJson = loadPackageJson0(projectDir); + if (!packageJson) { + projectDir = ( + await inquirer.prompt([ + { + type: "input", + name: "dir", + message: `Enter path of project directory:`, + }, + ]) + ).dir; + packageJson = loadPackageJson0(projectDir); + if (!packageJson) { + process.exit(1); + } + } + return { projectDir, packageJson }; +} + +export function loadPackageJson0(projectDir: string): any { + const packageJsonPath = path.resolve(projectDir, "package.json"); + if (!existsSync(packageJsonPath)) { + console.error(red(`Can't find node.js project in: ${b(projectDir)}`)); + return undefined; + } + const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8")); + if (!packageJson.devDependencies?.["jitsu-cli"]) { + console.error(red(`directory ${b(projectDir)} doesn't contain jitsu-cli managed project`)); + return undefined; + } + return packageJson; +} diff --git a/cli/jitsu-cli/src/commands/test.ts b/cli/jitsu-cli/src/commands/test.ts new file mode 100644 index 000000000..a4fbe1321 --- /dev/null +++ b/cli/jitsu-cli/src/commands/test.ts @@ -0,0 +1,14 @@ +import { run as jest } from "jest-cli"; +import { loadPackageJson } from "./shared"; +import chalk from "chalk"; +import { b } from "../lib/chalk-code-highlight"; + +export async function test({ dir }: { dir?: string }) { + const { packageJson, projectDir } = await loadPackageJson(dir || process.cwd()); + + console.log(`Running tests for ${b(packageJson.name)}`); + + const jestArgs = ["--passWithNoTests", "--projects", projectDir, "--preset", "ts-jest"]; + + await jest(jestArgs); +} diff --git a/cli/jitsu-cli/src/commands/whoami.ts b/cli/jitsu-cli/src/commands/whoami.ts new file mode 100644 index 000000000..7c54729b5 --- /dev/null +++ b/cli/jitsu-cli/src/commands/whoami.ts @@ -0,0 +1,48 @@ +import { homedir } from "os"; +import * as fs from "fs"; +import { readFileSync } from "fs"; +import { b } from "../lib/chalk-code-highlight"; + +export async function whoami({ host, apikey, force }: { host: string; apikey?: string; force?: boolean }) { + if (!apikey) { + const jitsuFile = `${homedir()}/.jitsu/jitsu-cli.json`; + if (!fs.existsSync(jitsuFile)) { + console.log("You are not logged in. Log in with `jitsu-cli login` or provide --apikey option"); + return; + } + const loginInfo = JSON.parse(readFileSync(jitsuFile, { encoding: "utf-8" })); + if (loginInfo.host) { + host = loginInfo.host; + } + apikey = loginInfo.apikey; + } + if (!host) { + host = "https://use.jitsu.com"; + } + if (!host.endsWith("/")) { + host += "/"; + } + const res = await fetch(`${host}api/me`, { + method: "GET", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${apikey}`, + }, + }); + + if (!res.ok) { + console.error( + `Login is invalid (${res.status}). Please login once again with \`jitsu-cli login -f\`, or provide a valid --apikey option` + ); + process.exit(1); + } + + const me = await res.json(); + if (!me.auth) { + console.error( + `Login is invalid. Please login once again with \`jitsu-cli login -f\`, or provide a valid --apikey option` + ); + } + + console.log(`You are logged in as ${b(me.user.email)} at ${host}. Internal userId: ${b(me.user.internalId)}`); +} diff --git a/cli/jitsu-cli/src/index.ts b/cli/jitsu-cli/src/index.ts index 9716b9898..6c7e8a438 100644 --- a/cli/jitsu-cli/src/index.ts +++ b/cli/jitsu-cli/src/index.ts @@ -1,16 +1,83 @@ -import { main } from "./main"; -import minimist from "minimist"; -import chalk from "chalk"; -import { getErrorMessage } from "juava"; - -(async function (): Promise { - try { - const exitCode = await main(minimist(process.argv.slice(2))); - process.exit(exitCode || 0); - } catch (e: any) { - process.stderr.write(chalk.red("Error!") + " - " + getErrorMessage(e) + "\n"); - if (e?.stack) { - process.stderr.write(`\n${e?.stack}\n`); - } - } -})(); +import figlet from "figlet"; +import { Command } from "commander"; +import { login, logout } from "./commands/login"; +import { deploy } from "./commands/deploy"; +import { init } from "./commands/init"; +import { build } from "./commands/build"; +import { test } from "./commands/test"; + +import { jitsuCliVersion, jitsuCliPackageName } from "./lib/version"; +import { whoami } from "./commands/whoami"; + +console.log(figlet.textSync("Jitsu CLI", { horizontalLayout: "full" })); + +const p = new Command(); + +p.name(jitsuCliPackageName).description("CLI command to create, test and deploy extensions for Jitsu Next"); +p.command("init") + .description("Initialize a new Jitsu extension project") + .arguments("[dir]") + .option("-j, --jitsu-version ", "Jitsu version to use in package.json. (Optional)") + .option("--allow-non-empty-dir", "Allow to create project in non-empty directory. (Optional)") + .action(init); + +p.command("build") + .description("Build the extension") + .option("-d, --dir ", "the directory of project. (Optional). By default, current directory is used") + .action(build); + +p.command("test") + .description("Run test provided with the extension") + .option("-d, --dir ", "the directory of project. (Optional). By default, current directory is used") + .action(test); + +// p.command("run") +// .description("Check extensions on provided event, config and persistent storage state") +// .option("-d, --dir ", "the directory of project. (Optional). By default, current directory is used") +// .option( +// "-n, --name ", +// "name of function file to check (optional). Required if multiple functions are defined in project" +// ) +// .option("-t, --type ", "entity type to run", "function") +// .requiredOption("-e, --event ", "path to file with event json or event json as a string") +// .option("-p, --props ", "path to file with config json or config json as a string. (Optional)") +// .option("-s, --store ", "path to file with state json or state json as a string. (Optional)") +// .action(run); + +p.command("whoami") + .description("Check if current user is logged in. Shows user's info if logged in") + .option("-h, --host ", "Jitsu host or base url", "https://use.jitsu.com") + .option("-k, --apikey ", "Jitsu user's Api Key. (Optional). Disables interactive login.") + .action(whoami); + +p.command("login") + .description("Login to Jitsu and remember credentials in `~/.jitsu/jitsu-cli.json` file") + .option("-f, --force", "If user already logged in, replace existing session") + .option("-h, --host ", "Jitsu host or base url", "https://use.jitsu.com") + .option("-k, --apikey ", "Jitsu user's Api Key. (Optional). Disables interactive login.") + .action(login); +p.command("logout").description("Logout").option("-f, --force", "Do not ask for confirmation").action(logout); + +p.command("deploy") + .description("Deploy functions to Jitsu project") + .option("-d, --dir ", "the directory of project. (Optional). By default, current directory is used") + .option( + "-h, --host ", + "(Optional) Jitsu host or base url. Useful for CI, if it's not possible to run login beforehand", + "https://use.jitsu.com" + ) + .option("-k, --apikey ", "(Optional) Jitsu user's Api Key.") + .option( + "-w, --workspace ", + "Id of workspace where to deploy function (Optional). By default, interactive prompt is shown to select workspace" + ) + .option("-t, --type ", "entity type to deploy", "function") + .option("-n, --name ", "limit deploy to provided entities only. (Optional)") + .action(deploy); + +//version +p.version(jitsuCliPackageName + " " + jitsuCliVersion, "-v, --version"); +//help +p.helpOption("--help", "display help for command"); + +p.parse(); diff --git a/cli/jitsu-cli/src/lib/chalk-code-highlight.ts b/cli/jitsu-cli/src/lib/chalk-code-highlight.ts new file mode 100644 index 000000000..840c9cd11 --- /dev/null +++ b/cli/jitsu-cli/src/lib/chalk-code-highlight.ts @@ -0,0 +1,51 @@ +import Prism, { Grammar } from "prismjs"; +import chalk from "chalk"; + +export type ColorScheme = Record; + +export const defaultColorScheme = { + punctuation: "#999", + operator: "#9a6e3a", + string: "#9a6e3a", + keyword: "b#07a", + "function-variable": null, +}; + +export const b = chalk.bold; +export const red = chalk.red; +export const green = chalk.green; +export const yellow = chalk.yellow; + +function chalkString(expr: string, str: string): string { + if (expr.startsWith("b")) { + return b(chalkString(expr.substring(1), str)); + } else { + return chalk.hex(expr)(str); + } +} + +/** + * Highlights code with Prism and the applies Chalk to color + * output for terminal + * @param code + */ +export function chalkCode(code: string, lang: Grammar, colorScheme: ColorScheme = defaultColorScheme): string { + return Prism.tokenize(code, Prism.languages.javascript) + .map(element => { + if (typeof element === "string") { + return element; + } else { + let highlight = colorScheme[element.type]; + return highlight ? chalkString(highlight, element.content.toString()) : `${element.content}`; + } + }) + .join(""); +} + +chalkCode.typescript = (code: TemplateStringsArray | string, colorScheme: ColorScheme = defaultColorScheme) => { + return chalkCode(typeof code === "string" ? code : code.join("\n"), Prism.languages.typescript, colorScheme); +}; + +chalkCode.json = (code: TemplateStringsArray | string, colorScheme: ColorScheme = defaultColorScheme) => { + return chalkCode(typeof code === "string" ? code : code.join("\n"), Prism.languages.json, colorScheme); +}; diff --git a/cli/jitsu-cli/src/lib/compiled-function.ts b/cli/jitsu-cli/src/lib/compiled-function.ts new file mode 100644 index 000000000..db9fde830 --- /dev/null +++ b/cli/jitsu-cli/src/lib/compiled-function.ts @@ -0,0 +1,78 @@ +import { JitsuFunction } from "@jitsu/protocols/functions"; +import fs from "fs"; +import { rollup } from "rollup"; +import { assertDefined, assertTrue } from "juava"; + +export type CompiledFunction = { + func: JitsuFunction; + meta: { + slug: string; + id?: string; + name?: string; + description?: string; + }; +}; + +function getSlug(filePath: string) { + return filePath.split("/").pop()?.replace(".ts", ""); +} + +export async function getFunctionFromFilePath( + filePath: string, + kind: "function" | "profile", + profileBuilders: any[] = [] +): Promise { + if (!fs.existsSync(filePath)) { + throw new Error(`Cannot load function from file ${filePath}: file doesn't exist`); + } else if (!fs.statSync(filePath).isFile()) { + throw new Error(`Cannot load function from file ${filePath}: path is not a file`); + } + + const bundle = await rollup({ + input: [filePath], + external: ["@jitsu/functions-lib"], + logLevel: "silent", + }); + + const output = await bundle.generate({ + file: filePath, + format: "commonjs", + }); + + const exports: Record = {} as Record; + eval(output.output[0].code); + assertDefined( + exports.default, + `Function from ${filePath} doesn't have default export. Exported symbols: ${Object.keys(exports)}` + ); + assertTrue(typeof exports.default === "function", `Default export from ${filePath} is not a function`); + + let name = exports.config?.name || exports.config?.slug || getSlug(filePath); + let id = exports.config?.id; + if (kind === "profile") { + const profileBuilderId = exports.config?.profileBuilderId; + const profileBuilder = profileBuilders.find(pb => pb.id === profileBuilderId); + if (!profileBuilder) { + throw new Error( + `Cannot find profile builder with id ${profileBuilderId} for profile function ${filePath}. Please setup Profile Builder in UI first.` + ); + } + name = name || `${profileBuilder.name} function`; + id = id || profileBuilder.functions[0]?.functionId; + if (!id) { + throw new Error( + `Cannot find function id for profile function ${filePath}. Please setup Profile Builder in UI first.` + ); + } + } + + return { + func: exports.default, + meta: { + slug: exports.config?.slug || getSlug(filePath), + id: id, + name: name, + description: exports.config?.description, + }, + }; +} diff --git a/cli/jitsu-cli/src/lib/indent.ts b/cli/jitsu-cli/src/lib/indent.ts new file mode 100644 index 000000000..391fdfeec --- /dev/null +++ b/cli/jitsu-cli/src/lib/indent.ts @@ -0,0 +1,42 @@ +function getIndentSize(line): number { + let idx = 0; + for (; line.charAt(idx) === " " && idx < line.length; idx++) {} + return idx; +} + +/** + * Finds a common indentation in text and removes it + */ +export function removeIndentation(text: string, { trimLines = true } = {}): string { + let lines = text.split("\n"); + if (trimLines) { + let start = 0, + end = lines.length - 1; + for (; lines[start].trim().length == 0 && start <= end; start++) {} + for (; lines[end].trim().length == 0 && end >= start; end--) {} + lines = lines.slice(start, end + 1); + } + let commonIndent = Math.min(...lines.filter(ln => ln.trim().length > 0).map(getIndentSize)); + + return lines.map(ln => ln.substring(commonIndent)).join("\n"); +} + +export function align(text: string, { indent = 0, lnBefore = 0, lnAfter = 0 } = {}) { + const cleanText = removeIndentation(text, { trimLines: true }); + return [ + ...new Array(lnBefore).fill(""), + ...cleanText.split("\n").map(ln => " ".repeat(indent) + ln), + ...new Array(lnAfter).fill(""), + ].join("\n"); +} + +export function jsonify(obj: any) { + if (typeof obj === "string") { + try { + return JSON.parse(obj); + } catch (e) { + return obj; + } + } + return obj; +} diff --git a/cli/jitsu-cli/src/lib/template.ts b/cli/jitsu-cli/src/lib/template.ts new file mode 100644 index 000000000..f07ba8d00 --- /dev/null +++ b/cli/jitsu-cli/src/lib/template.ts @@ -0,0 +1,37 @@ +import path from "path"; +import * as fs from "fs"; +import { removeIndentation } from "./indent"; + +export type TemplateVars = Record; +export type TemplateFunction = (vars: T) => any; +export type FileTemplate = TemplateFunction | string | any; + +export type ProjectTemplate = (vars: T) => Record>; + +function toTemplateFunction(template: FileTemplate): TemplateFunction { + if (template === null || template === undefined) { + return () => undefined; + } else if (typeof template === "function") { + return template; + } else { + return () => template; + } +} + +export function write(dir: string, template: ProjectTemplate, vars: T) { + Object.entries(template(vars)).forEach(([fileName, template]) => { + let filePath = path.resolve(dir, fileName); + let fileDir = path.dirname(filePath); + if (!fs.existsSync(fileDir)) { + fs.mkdirSync(fileDir, { recursive: true }); + } + let content = toTemplateFunction(template)(vars); + if (typeof content === "object") { + content = JSON.stringify(content, null, 2); + } + if (content) { + let data = removeIndentation(content); + fs.writeFileSync(filePath, data); + } + }); +} diff --git a/cli/jitsu-cli/src/lib/version.ts b/cli/jitsu-cli/src/lib/version.ts new file mode 100644 index 000000000..c491d6bc3 --- /dev/null +++ b/cli/jitsu-cli/src/lib/version.ts @@ -0,0 +1,36 @@ +import pkg from "../../package.json"; +import chalk from "chalk"; +import semver from "semver/preload"; +import { b } from "./chalk-code-highlight"; +const fetch = require("cross-fetch"); + +export const jitsuCliVersion = pkg.version; +export const jitsuCliPackageName = pkg.name; +let newVersion = undefined; + +export function getUpgradeMessage(newVersion: string, oldVersion: string) { + return box( + `🚀 New version of Jitsu CLI is available: ${oldVersion} → ${chalk.green(newVersion)} \n Run ${b( + "npm install -g " + jitsuCliPackageName + )} or ${b("yarn global install " + jitsuCliPackageName)}` + ); +} +function padRight(str: string, minLen: number, symbol: string = " ") { + return str.length >= minLen ? str : str + symbol.repeat(minLen - str.length); +} +export function box(msg: string) { + let lines = msg.split("\n"); + return ["──".repeat(80), ...lines.map(ln => ` ${ln}`), "──".repeat(80)].join("\n"); +} + +export async function hasNewerVersion(): Promise { + try { + let json = (await ( + await fetch(`https://registry.npmjs.org/-/package/${jitsuCliPackageName}/dist-tags`) + ).json()) as any; + let latestVersion = json.latest; + return semver.gt(latestVersion, jitsuCliVersion) ? latestVersion : undefined; + } catch (e: any) { + console.debug(`Failed to fetch latest version of ${jitsuCliPackageName}: ${e?.message}`); + } +} diff --git a/cli/jitsu-cli/src/main.ts b/cli/jitsu-cli/src/main.ts deleted file mode 100644 index a23d2231a..000000000 --- a/cli/jitsu-cli/src/main.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { ParsedArgs } from "minimist"; - -export async function main(args): Promise {} diff --git a/cli/jitsu-cli/src/templates/functions.ts b/cli/jitsu-cli/src/templates/functions.ts new file mode 100644 index 000000000..3676c5c85 --- /dev/null +++ b/cli/jitsu-cli/src/templates/functions.ts @@ -0,0 +1,119 @@ +import { ProjectTemplate } from "../lib/template"; +import { jitsuCliVersion, jitsuCliPackageName } from "../lib/version"; +import { sanitize } from "juava"; + +export type TemplateVars = { + license?: "MIT" | "Other"; + packageName: string; + jitsuVersion?: string; +}; + +export const packageJsonTemplate = ({ packageName, license = "MIT", jitsuVersion = undefined }: TemplateVars) => ({ + name: `${packageName}`, + version: "0.0.1", + description: `Jitsu extension - ${packageName}`, + license: license, + keywords: ["jitsu", "jitsu-cli", "function", `jitsu-extension`], + scripts: { + clean: "rm -rf ./dist", + build: `${jitsuCliPackageName} build`, + test: `${jitsuCliPackageName} test`, + deploy: `${jitsuCliPackageName} deploy`, + }, + devDependencies: { + "jitsu-cli": `${jitsuVersion || "^" + jitsuCliVersion}`, + "@jitsu/protocols": `${jitsuVersion || "^" + jitsuCliVersion}`, + "@jitsu/functions-lib": `${jitsuVersion || "^" + jitsuCliVersion}`, + "@types/jest": "^29.5.5", + "ts-jest": "^29.1.1", + }, + dependencies: {}, +}); + +let functionTest = ({ packageName }: TemplateVars) => { + return ` +test("${sanitize(packageName)} test", () => { + //TODO: implement test +}); +`; +}; + +let functionCode = ({}: TemplateVars) => { + return ` +import { JitsuFunction } from "@jitsu/protocols/functions"; +import { RetryError } from "@jitsu/functions-lib"; +import { AnalyticsServerEvent } from "@jitsu/protocols/analytics"; + +export const config = { + slug: "hello.ts", //id (uniq per workspace) used to identify function in Jitsu + name: "Hello World Function", //human readable name of function + description: "" +}; + +const helloWorldFunction: JitsuFunction = async (event, { log, fetch, store, geo, ...meta }) => { + //output "Hello World!" to logs and return unchanged event + log.info("Hello World!"); + return event +}; + +export default helloWorldFunction; +`; +}; + +let profileTest = ({ packageName }: TemplateVars) => { + return ` +test("${sanitize(packageName)} test", () => { + //TODO: implement test +}); +`; +}; + +let profileCode = ({}: TemplateVars) => { + return ` +import { ProfileFunction } from "@jitsu/protocols/profile"; + +export const config = { + profileBuilderId: "", // Required: id of Profile Builder where this function will be used. Can be found in the Profile Builder Settings UI + slug: "profile-example.ts", //id (uniq per workspace) used to identify function in Jitsu + description: "" +}; + +const profileExample: ProfileFunction = async (events, user, context) => { + context.log.info("Profile func: " + user.id) + const profile = {} as any + for (const event of events) { + profile.lastMessageDate = Math.max(new Date(event.timestamp).getTime(),profile.lastMessageDate??0) + } + profile.anonId = user.anonymousId + return { + traits: profile + } +}; + +export default profileExample; +`; +}; + +export const functionProjectTemplate: ProjectTemplate = ({ packageName }: TemplateVars) => ({ + [`__tests__/profiles/profile-example.test.ts`]: profileTest, + [`__tests__/functions/hello.test.ts`]: functionTest, + [`src/profiles/profile-example.ts`]: profileCode, + [`src/functions/hello.ts`]: functionCode, + "package.json": packageJsonTemplate, + "tsconfig.json": { + compilerOptions: { + rootDir: "./src", + outDir: "./dist", + declaration: true, + esModuleInterop: true, + moduleResolution: "node", + importHelpers: false, + module: "esnext", + lib: ["esnext", "dom"], + noEmit: false, + target: "esnext", + }, + target: "esnext", + exclude: ["__tests__", "node_modules", "dist"], + }, +}); diff --git a/cli/jitsu-cli/tsconfig.json b/cli/jitsu-cli/tsconfig.json index e9878393e..3b3ffd3c2 100644 --- a/cli/jitsu-cli/tsconfig.json +++ b/cli/jitsu-cli/tsconfig.json @@ -1,14 +1,28 @@ { - "extends": "../../tsconfig.base.json", "compilerOptions": { - "sourceMap": true, + "outDir": "./compiled", "rootDir": ".", - "outDir": "./dist", - "declaration": true + "noImplicitAny": false, + "allowSyntheticDefaultImports": true, + "importHelpers": true, + "removeComments": true, + "target": "ES2021", + "module": "commonjs", + "lib": [ + "dom","esnext" + ], + "allowJs": true, + "strict": true, + "esModuleInterop": true, + "moduleResolution": "node", + "resolveJsonModule": true, + "skipLibCheck": true }, + "exclude": [ + "node_modules" + ], "include": [ - "./src", - "package.json" + "src/**/*" ] +} -} \ No newline at end of file diff --git a/cli/jitsu-cli/webpack.config.js b/cli/jitsu-cli/webpack.config.js new file mode 100644 index 000000000..f6ab839a1 --- /dev/null +++ b/cli/jitsu-cli/webpack.config.js @@ -0,0 +1,49 @@ +const path = require("path"); +const webpack = require("webpack"); + +const config = { + entry: "./src/index.ts", + target: "node", + externals: { + figlet: "require('figlet')", + "@swc/core": "require('@swc/core')", + "@swc/wasm": "require('@swc/wasm')", + typescript: "require('typescript')", + //"isolated-vm": "require('isolated-vm')", + "jest-cli": "require('jest-cli')", + "../../package.json": "require('../package.json')", + }, + node: { + __dirname: false, + }, + devtool: "source-map", + output: { + path: path.resolve(__dirname, "dist"), + }, + plugins: [ + new webpack.IgnorePlugin({ resourceRegExp: /^fsevents$/ }), // Ignore MacOS-only module + ], + module: { + rules: [ + { + test: /\.(ts|tsx)$/i, + use: { + loader: "babel-loader", + }, + }, + { + test: /\.node$/, + loader: "node-loader", + }, + ], + }, + optimization: { + minimize: false, + }, + resolve: { + extensions: [".tsx", ".ts", ".jsx", ".js", ".node", "..."], + }, + mode: "production", +}; + +module.exports = () => config; diff --git a/configurator/frontend/vercel.sh b/configurator/frontend/vercel.sh deleted file mode 100644 index 95715730b..000000000 --- a/configurator/frontend/vercel.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash - -# This script defines if vercel should deploy the commit, -# see https://vercel.com/support/articles/how-do-i-use-the-ignored-build-step-field-on-vercel - - -echo "❌ $VERCEL_GIT_COMMIT_REF branch doesn't contain jitsu legacy code, skipping deploy." -exit 0 \ No newline at end of file diff --git a/console.Dockerfile b/console.Dockerfile deleted file mode 100644 index 79ff47ef2..000000000 --- a/console.Dockerfile +++ /dev/null @@ -1,39 +0,0 @@ -# Run `docker login`, use jitsucom account -# Build & push it with -# docker buildx build --platform linux/amd64 . -f console.Dockerfile --push -t jitsucom/console:latest - - -FROM node:16-bullseye-slim as builder - -RUN apt-get update -y -RUN apt-get install nano curl git openssl1.1 procps python3 make g++ bash netcat -y - -# Create app directory -WORKDIR /app -RUN npm -g install pnpm -COPY pnpm-lock.yaml . -RUN --mount=type=cache,id=onetag_pnpm,target=/root/.local/share/pnpm/store/v3 pnpm fetch -COPY . . -RUN --mount=type=cache,id=onetag_pnpm,target=/root/.local/share/pnpm/store/v3 pnpm install -r --unsafe-perm -RUN --mount=type=cache,id=console_turborepo,target=/app/node_modules/.cache/turbo pnpm build -#RUN pnpm build - -#Remove env files to prevent accidental leaks of credentials -RUN rm .env* - - -#FROM node:16-bullseye-slim AS runner -#RUN apt-get install bash -#RUN npm -g install pnpm -#WORKDIR /app -#RUN addgroup --system --gid 1001 runner -#RUN adduser --system --uid 1001 runner -#USER runner -# -#COPY --from=builder /app/ . - -EXPOSE 3000 - -HEALTHCHECK CMD curl --fail http://localhost:3000/api/healthcheck || exit 1 - -ENTRYPOINT ["sh", "-c", "./docker-start-console.sh"] diff --git a/console.cron b/console.cron new file mode 100755 index 000000000..fa3ab30a2 --- /dev/null +++ b/console.cron @@ -0,0 +1 @@ +*/15 * * * * curl --silent --show-error http://localhost:3000/api/admin/events-log-trim diff --git a/consolebuild.sh b/consolebuild.sh new file mode 100755 index 000000000..44706b093 --- /dev/null +++ b/consolebuild.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +docker buildx build --platform linux/amd64 . -f all.Dockerfile --target console --push -t jitsucom/console:latest \ No newline at end of file diff --git a/devenv/docker-compose.yml b/devenv/docker-compose.yml index d34f77d79..1cae5c710 100644 --- a/devenv/docker-compose.yml +++ b/devenv/docker-compose.yml @@ -30,7 +30,7 @@ services: max-size: 10m max-file: "3" healthcheck: - test: ["CMD-SHELL", "pg_isready", "-d", "postgres"] + test: ["CMD-SHELL", "pg_isready", "-d", "postgres", "-U", "postgres"] interval: 1s timeout: 10s retries: 10 @@ -38,44 +38,28 @@ services: - "${PG_PORT:-5438}:5432" volumes: - ./data/postgres:/var/lib/postgresql/data - jitsu-dev-zookeeper: - tty: true - image: wurstmeister/zookeeper:latest - expose: - - 2181 jitsu-dev-kafka: - tty: true - image: wurstmeister/kafka:latest - depends_on: - - jitsu-dev-zookeeper + image: bitnami/kafka:3.4 # ports: # - "19092:19092" # - "19093:19093" environment: - TERM: "xterm-256color" - KAFKA_ZOOKEEPER_CONNECT: jitsu-dev-zookeeper:2181 - - KAFKA_LISTENERS: INTERNAL://0.0.0.0:19093,OUTSIDE://0.0.0.0:19092 - KAFKA_ADVERTISED_LISTENERS: INTERNAL://jitsu-dev-kafka:19093,OUTSIDE://localhost:19092 - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: INTERNAL:PLAINTEXT,OUTSIDE:PLAINTEXT - KAFKA_INTER_BROKER_LISTENER_NAME: INTERNAL + KAFKA_CFG_NODE_ID: 0 + KAFKA_CFG_PROCESS_ROLES: controller,broker + KAFKA_CFG_LISTENERS: PLAINTEXT://:19093,CONTROLLER://:9093,EXTERNAL://:19092 + KAFKA_CFG_ADVERTISED_LISTENERS: PLAINTEXT://jitsu-dev-kafka:19093,EXTERNAL://localhost:19092 + KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT,EXTERNAL:PLAINTEXT + KAFKA_CFG_CONTROLLER_QUORUM_VOTERS: 0@jitsu-dev-kafka:9093 + KAFKA_CFG_CONTROLLER_LISTENER_NAMES: CONTROLLER + KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE: true + ALLOW_PLAINTEXT_LISTENER: yes jitsu-dev-kafka-console: - tty: true - image: docker.redpanda.com/vectorized/console:master-173596f - links: - - "jitsu-dev-kafka:localhost" - restart: on-failure - entrypoint: /bin/sh - command: -c "echo \"$$CONSOLE_CONFIG_FILE\" > /tmp/config.yml; /app/console" - environment: - TERM: "xterm-256color" - CONFIG_FILEPATH: /tmp/config.yml - CONSOLE_CONFIG_FILE: | - kafka: - brokers: ["jitsu-dev-kafka:19093"] + image: docker.redpanda.com/redpandadata/console:latest ports: - "${KAFKA_CONSOLE_PORT:-3032}:8080" + environment: + KAFKA_BROKERS: "jitsu-dev-kafka:19093" depends_on: - jitsu-dev-kafka @@ -105,3 +89,11 @@ services: depends_on: - jitsu-dev-postgres - jitsu-dev-kafka + keycloak: + command: start-dev + image: "quay.io/keycloak/keycloak:26.0.4" + environment: + - KC_BOOTSTRAP_ADMIN_PASSWORD=admin + - KC_BOOTSTRAP_ADMIN_USERNAME=admin + ports: + - "8080:8080" diff --git a/docker-start-console.sh b/docker-start-console.sh index 29571c618..5dbdb20f1 100755 --- a/docker-start-console.sh +++ b/docker-start-console.sh @@ -1,9 +1,20 @@ #!/bin/bash cancel_healthcheck="0" +inited="0" +export CONSOLE_INIT_TOKEN=$RANDOM$RANDOM$RANDOM$RANDOM export my_pid=$$ - +init() { + if [ "$inited" = "0" ]; then + echo "Initializing console..." + inited="1" + curl --silent --show-error http://localhost:3000/api/admin/events-log-init?token=$CONSOLE_INIT_TOKEN + echo "" + echo "Starting cron..." + cron + fi +} wait_for_service() { url=$1 @@ -46,35 +57,52 @@ healthcheck() { http_code=$(curl -s $healthcheck_url -o healthcheck-result -w '%{http_code}') if [ "$http_code" = "200" ]; then echo "⚡️⚡️⚡️ HEALTHCHECK PASSED - $http_code from $healthcheck_url. Details:" - cat healthcheck-result + if [ -f healthcheck-result ]; then + cat healthcheck-result + fi echo "" + init else - echo "❌ ❌ ❌ HEALTHCHECK FAILED - $http_code from $healthcheck_url. Response:" - cat healthcheck-result - echo "" - kill -9 $pid + if [ "$http_code" = "000" ]; then + echo "❌ ❌ ❌ HEALTHCHECK FAILED $healthcheck_url is not available" + else + echo "❌ ❌ ❌ HEALTHCHECK FAILED - $http_code from $healthcheck_url. Response:" + if [ -f healthcheck-result ]; then + cat healthcheck-result + fi + echo "" + fi + kill -9 $$ + exit 1 fi fi } main() { cmd=$1 - export SIGNALS_LYFECICLE=1 + export SIGNALS_LIFECYCLE=1 if [ -z "$cmd" ]; then - if [ "$UPDATE_DB" = "1" ] || [ "$UPDATE_DB" = "yes" ] || [ "$UPDATE_DB" = "true" ]; then - echo "UPDATE_DB is set, updating database schema..." - npm run console:db-prepare + if [ "$FORCE_UPDATE_DB" = "1" ] || [ "$FORCE_UPDATE_DB" = "yes" ] || [ "$FORCE_UPDATE_DB" = "true" ]; then + echo "FORCE_UPDATE_DB is set, updating database schema..." + prisma db push --skip-generate --schema schema.prisma --accept-data-loss + elif [ "$UPDATE_DB" != "0" ] && [ "$UPDATE_DB" != "no" ] && [ "$UPDATE_DB" != "false" ]; then + echo "Updating database schema..." + prisma db push --skip-generate --schema schema.prisma fi echo "Starting the app" healthcheck $$ & - npm run console:start + + cd /app/webapps/console + HOSTNAME="::" node server.js + exit_code=$? + sleep 1000 cancel_healthcheck="1" - echo "App stopped, exiting..." + echo "App stopped with exit code ${exit_code}, exiting..." elif [ "$cmd" = "db-prepare" ]; then - npm run console:db-prepare + prisma db push --skip-generate --schema schema.prisma else echo "ERROR! Unknown command '$cmd'" fi diff --git a/docker/.env.example b/docker/.env.example index 06389e072..16336085d 100644 --- a/docker/.env.example +++ b/docker/.env.example @@ -1,16 +1,59 @@ -#Required -GITHUB_CLIENT_ID -GITHUB_CLIENT_SECRET +#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +# REQUIRED PARAMETERS, PLEASE READ INSTRUCTIONS THOROUGHLY +# See https://docs.jitsu.com/self-hosting/quick-start/ for more details +#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -#Secrets +# Password and login of an *initial* admin user of Jitsu UI. After initial login, you must change your password! +# You can keep those variable empty if you are configuring Jitsu UI via Github auth (see below). In this case first +# authorized user will be granted a full access to Jitsu UI. +SEED_USER_EMAIL=admin@jitsu.com +SEED_USER_PASSWORD=changeme -POSTGRES_PASSWORD= -BULKER_PASSWORD -REDIS_PASSWORD +# Domain name of your Jitsu server where events will be sent. E.g. https://jitsu.mycompany.com or http://localhost:8080 +JITSU_INGEST_PUBLIC_URL=http://localhost:8080 +# Public URL of your Jitsu UI. E.g. https://jitsu.mycompany.com or http://localhost:3000 +JITSU_PUBLIC_URL=http://localhost:3000 -#Optional -REDIS_PORT=6380 -PG_PORT=5438 -BULKER_PORT=3042 -JITSU_PORT=3000 \ No newline at end of file +#### Secrets ### +# It is highly recommended to generate random values for those variables. +# You can use `openssl rand -hex 32` to generate random values. + +CONSOLE_TOKEN=changeme +BULKER_TOKEN=changeme +SYNCCTL_TOKEN=changeme +POSTGRES_PASSWORD=changeme + +#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +# OPTIONAL PARAMETERS +#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +JITSU_UI_PORT=3000 +JITSU_INGEST_PORT=8080 + +# If you want to have more than one user for Jitsu UI, this could be done via delegating auth to github. +# You need to create a new Github OAuth app and set those variables accordingly. +GITHUB_CLIENT_ID= +GITHUB_CLIENT_SECRET= + +#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +# Connectors specific +#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +SYNCS_ENABLED=false + +# sync controller is running using host network to access 'minikube' w/o problems. +EXTERNAL_SYNCS_PORT=3043 + +#postgres host how it is reachable from k8s cluster +EXTERNAL_POSTGRES_HOST=host.minikube.internal +EXTERNAL_POSTGRES_PORT=5432 + +SYNCCTL_KUBERNETES_CLIENT_CONFIG= +# to use non-default k8s context +SYNCCTL_KUBERNETES_CONTEXT= +# For syncs scheduling +# google service account key +GOOGLE_SCHEDULER_KEY= +# for Google Ads connector +GOOGLE_ADS_DEVELOPER_TOKEN= diff --git a/docker/.gitignore b/docker/.gitignore index f10862a65..dabf27f1a 100644 --- a/docker/.gitignore +++ b/docker/.gitignore @@ -1 +1,2 @@ /.env +/data/syncctl/* diff --git a/docker/README.md b/docker/README.md new file mode 100644 index 000000000..24123a4f3 --- /dev/null +++ b/docker/README.md @@ -0,0 +1,84 @@ +# Quick Start + +This guide uses docker compose to run Jitsu locally. It's suitable for testing purposes only. + +## Requirements + +* [Docker Engine](https://docs.docker.com/engine/install/) >= 19.03.0 +* Kubernetes cluster (Optional. Required for connectors syncs) + +## Configuration + +1. You'll need to create a `.env` file in `docker` directory. You can copy `.env.example` to `.env`: + +```shell +# Copy .env.example to .env +cp .env.example .env +``` + +2. Set up all required variables in `.env` file. + +#### `SEED_USER_EMAIL`, `SEED_USER_PASSWORD` + +**Required** + +Initial user login and password. + +The very first user will be created with those credentials, and it will become an admin user. + +**Please change the password right after first login.** + +#### `JITSU_PUBLIC_URL` + +**Default value:** `http://localhost:${JITSU_UI_PORT}/` + +This is a URL where Jitsu UI will be available. + +When UI is deployed on remote server, set it with your remote server URL, e.g.: http://your-domain:3000. + +For production deployments, it is recommended to put it behind an HTTPs load balancer or reverse proxy. + +In this case, this value must be set to a public URL, such as: https://jitsu.my-company.com. + +#### `JITSU_INGEST_PUBLIC_URL` + +**Default value:** `http://localhost:${JITSU_INGEST_PORT}/` + +This is a URL where Jitsu Ingest will be available. + +When Jitsu is deployed on remote server, set it with your remote server URL, e.g.: http://your-domain:8080. + +For production deployments, it is recommended to put it behind an HTTPs load balancer or reverse proxy. + +In this case, this value must be set to a public URL, such as: https://jitsu.my-company.com. + +#### `CONSOLE_TOKEN`, `POSTGRES_PASSWORD`, `BULKER_TOKEN`, `SYNCCTL_TOKEN` + +Those secrets are used mostly for internal communication between Jitsu components. + +Please make sure to generate random values for those variables. + + +## Run Jitsu + +In `jitsu/docker` directory run: +```shell +docker-compose up +``` + +Open Jitsu UI url (e.g.: `http://localhost:3000/`) in your browser, login with any GitHub account and follow the instructions. + +## Sending events + +Create a new site in Jitsu UI and click `Setup instructions` button in the site context menu. + +See [HTTP API](https://docs.jitsu.com/sending-data/) guide for details. + + +## Further steps + +Those steps are optional, but they might make sense for you: + +* Set up **Connectors Syncs**. See SYNCS_README.md for details +* Set `DISABLE_SIGNUP` in `.env` to `true` if you don't want to allow users to sign up +* See reference for other variables in [Production Deployment](https://docs.jitsu.com/self-hosting/configuration) guide. \ No newline at end of file diff --git a/docker/SYNCS_README.md b/docker/SYNCS_README.md new file mode 100644 index 000000000..2da819c45 --- /dev/null +++ b/docker/SYNCS_README.md @@ -0,0 +1,50 @@ +# Syncs in Docker Compose + +Syncs rely on kubernetes cluster to run jobs. + +This doc is just a proof-of-concept: +In the presence of kubernetes cluster, it is not recommended to run Jitsu in Docker Compose. Instead, running all Jitsu components in kubernetes cluster is preferable. + +# How to set up + +Jitsu runs sync jobs in kubernetes cluster. So we need to connect `syncctl` service to kubernetes cluster using following env variables in `.env` file: + +* `SYNCS_ENABLED` - set to `true` to enable syncs on UI side +* `SYNCCTL_KUBERNETES_CLIENT_CONFIG` - path to kubernetes config file or kubernetes config in yaml format +* `EXTERNAL_POSTGRES_HOST`- host of `postgres` service as it is reachable from kubernetes cluster + +# How to run + +## Minikube example + +**Pre-requisites** + +* `minikube` [installed](https://minikube.sigs.k8s.io/docs/start/) +* `kubectl` [installed](https://kubernetes.io/docs/tasks/tools/) + +**Start minikube** + +```shell +minikube start +``` + +**Set kubernetes config** + +```shell +kubectl config view --raw=true --minify=true --flatten=true +``` +Put the modified config payload to `SYNCCTL_KUBERNETES_CLIENT_CONFIG` env variable in `.env` file (keep indentation intact: use line break after opening quotation marks symbol) + +**Adjust `.env` config** + +* Set `EXTERNAL_POSTGRES_HOST` to: `host.minikube.internal` as it is how these services will be reachable from minikube +* Set `SYNCS_ENABLED` to `true` + +**Start Jitsu** + +```shell +docker compose up +``` + +Keep in mind that minikube choose random port each time it starts. +So you need to update server port in config each time you restart minikube. diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 3a6d619fb..363f553af 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -1,14 +1,53 @@ version: "3.8" services: - redis: + kafka: + tty: true + image: bitnamilegacy/kafka:3.6.0 + environment: + TERM: "xterm-256color" + KAFKA_CFG_NODE_ID: 0 + KAFKA_CFG_PROCESS_ROLES: controller,broker + KAFKA_CFG_LISTENERS: PLAINTEXT://:9092,CONTROLLER://:9093 + KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT + KAFKA_CFG_CONTROLLER_QUORUM_VOTERS: 0@kafka:9093 + KAFKA_CFG_CONTROLLER_LISTENER_NAMES: CONTROLLER + healthcheck: + test: ["CMD-SHELL", "kafka-topics.sh --bootstrap-server 127.0.0.1:9092 --list"] + interval: 5s + timeout: 10s + retries: 30 + # volumes: + # - ./data/kafka:/bitnami/kafka + + # + # redis: + # tty: true + # image: redis:6.2-alpine + # restart: "unless-stopped" + # command: "redis-server --save 20 1 --loglevel warning --requirepass ${REDIS_PASSWORD:-default}" + # # volumes: + # # - ./data/redis:/var/lib/redis + + clickhouse: tty: true - image: redis:6.2-alpine + image: clickhouse/clickhouse-server:24.1 restart: "unless-stopped" - ports: - - "${REDIS_PORT:-6378}:6379" - command: "redis-server --save 20 1 --loglevel warning --requirepass ${REDIS_PASSWORD:-redis}" - volumes: - - ./data/redis:/var/lib/redis + environment: + - CLICKHOUSE_DB=newjitsu_metrics + - CLICKHOUSE_USER=default + - CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT=1 + - CLICKHOUSE_PASSWORD=${CLICKHOUSE_PASSWORD:-default} + logging: + options: + max-size: 10m + max-file: "3" + + mongo: + image: mongo + restart: "unless-stopped" + environment: + MONGO_INITDB_ROOT_USERNAME: default + MONGO_INITDB_ROOT_PASSWORD: ${MONGO_PASSWORD:-default} postgres: tty: true @@ -17,7 +56,7 @@ services: user: postgres environment: - POSTGRES_USER=postgres - - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-postgres} + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-default} logging: options: max-size: 10m @@ -28,96 +67,171 @@ services: timeout: 10s retries: 10 ports: - - "${PG_PORT:-5437}:5432" + - "${EXTERNAL_POSTGRES_PORT:-5432}:5432" + ## permissions problem is not that trivial to solve https://forums.docker.com/t/data-directory-var-lib-postgresql-data-pgdata-has-wrong-ownership/17963/42 # volumes: # - ./data/postgres:/var/lib/postgresql/data - zookeeper: - tty: true - image: wurstmeister/zookeeper:latest - expose: - - 2181 - kafka: + + console: tty: true - image: wurstmeister/kafka:latest - depends_on: - - zookeeper - # ports: - # - "19092:19092" - # - "19093:19093" + image: jitsucom/console:${DOCKER_TAG:-latest} + restart: "unless-stopped" + platform: linux/amd64 environment: - TERM: "xterm-256color" - KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 - KAFKA_LISTENERS: INTERNAL://0.0.0.0:19093,OUTSIDE://0.0.0.0:19092 - KAFKA_ADVERTISED_LISTENERS: INTERNAL://kafka:19093,OUTSIDE://localhost:19092 - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: INTERNAL:PLAINTEXT,OUTSIDE:PLAINTEXT - KAFKA_INTER_BROKER_LISTENER_NAME: INTERNAL + ROTOR_URL: "http://rotor:3401" + ROTOR_AUTH_KEY: ${BULKER_TOKEN:-default} + BULKER_URL: "http://bulker:3042" + CONSOLE_RAW_AUTH_TOKENS: ${CONSOLE_TOKEN:-default} + BULKER_AUTH_KEY: ${BULKER_TOKEN:-default} + MIT_COMPLIANT: ${MIT_COMPLIANT:-false} + DATABASE_URL: "postgresql://postgres:${POSTGRES_PASSWORD:-default}@postgres:5432/postgres?schema=newjitsu" + SEED_USER_EMAIL: ${SEED_USER_EMAIL:-} + SEED_USER_PASSWORD: ${SEED_USER_PASSWORD:-} + ENABLE_CREDENTIALS_LOGIN: ${ENABLE_CREDENTIALS_LOGIN:-true} + GITHUB_CLIENT_ID: ${GITHUB_CLIENT_ID} + GITHUB_CLIENT_SECRET: ${GITHUB_CLIENT_SECRET} + SYNCS_ENABLED: ${SYNCS_ENABLED:-false} + SYNCCTL_URL: "http://syncctl:${EXTERNAL_SYNCS_PORT:-3043}" + SYNCCTL_AUTH_KEY: ${SYNCCTL_TOKEN:-default} + GOOGLE_SCHEDULER_KEY: ${GOOGLE_SCHEDULER_KEY} + JITSU_INGEST_PUBLIC_URL: "${JITSU_INGEST_PUBLIC_URL:-http://localhost:${JITSU_INGEST_PORT:-8080}/}" + JITSU_PUBLIC_URL: "${JITSU_PUBLIC_URL:-${NEXTAUTH_URL:-http://localhost:${JITSU_UI_PORT:-3000}/}}" + NEXTAUTH_URL: "${JITSU_PUBLIC_URL:-${NEXTAUTH_URL:-http://localhost:${JITSU_UI_PORT:-3000}/}}" + CLICKHOUSE_HOST: "clickhouse:8123" + CLICKHOUSE_PASSWORD: "${CLICKHOUSE_PASSWORD:-default}" + CLICKHOUSE_DATABASE: "newjitsu_metrics" + MONGODB_URL: "mongodb://default:${MONGO_PASSWORD:-default}@mongo/" + MONGODB_NETWORK_COMPRESSION: "none" + FORCE_UPDATE_DB: "true" healthcheck: - test: ["CMD-SHELL", "kafka-topics.sh --bootstrap-server 127.0.0.1:19092 --describe"] - interval: 5s - timeout: 2s - retries: 15 + test: ["CMD", "curl", "-f", "http://console:3000/api/healthcheck"] + interval: 2s + timeout: 10s + retries: 30 + extra_hosts: + - "syncctl:host-gateway" + depends_on: + clickhouse: + condition: service_started + postgres: + condition: service_healthy + ports: + - "${JITSU_UI_PORT:-3000}:3000" + + sync-catalog-init: + tty: true + image: curlimages/curl + restart: "on-failure" + environment: + CONSOLE_TOKEN: ${CONSOLE_TOKEN:-default} + command: "curl --silent --output nul --show-error -H 'Authorization: Bearer service-admin-account:${CONSOLE_TOKEN:-default}' http://console:3000/api/admin/catalog-refresh?initial=true" + depends_on: + console: + condition: service_healthy bulker: tty: true - image: jitsucom/bulker:latest + image: jitsucom/bulker:${DOCKER_TAG:-latest} + platform: linux/amd64 restart: "unless-stopped" - ports: - - "${BULKER_PORT:-3045}:3042" environment: - BULKER_HTTP_PORT: "3042" TERM: "xterm-256color" - BULKER_KAFKA_BOOTSTRAP_SERVERS: "kafka:19093" - BULKER_AUTH_TOKENS: "${BULKER_PASSWORD:-4ba41958f341469993fd8ea1c0c932f0}" - BULKER_CONFIG_SOURCE: "redis" - REDIS_URL: "redis://default:${REDIS_PASSWORD:-redis}@redis:6379" + BULKER_KAFKA_BOOTSTRAP_SERVERS: "kafka:9092" + BULKER_RAW_AUTH_TOKENS: ${BULKER_TOKEN:-default} + BULKER_CONFIG_SOURCE: "http://console:3000/api/admin/export/bulker-connections" + BULKER_CONFIG_SOURCE_HTTP_AUTH_TOKEN: "service-admin-account:${CONSOLE_TOKEN:-default}" + BULKER_CACHE_DIR: "/tmp/cache" + BULKER_INTERNAL_TASK_LOG: '{"id":"task_log","metricsKeyPrefix":"syncs","usesBulker":true,"type":"postgres","options":{"mode":"stream"},"credentials":{"host":"postgres","port":5432,"sslMode":"disable","database":"postgres","password":"${POSTGRES_PASSWORD:-default}","username":"postgres","defaultSchema":"newjitsu"}}' + BULKER_CLICKHOUSE_HOST: "clickhouse:8123" + BULKER_CLICKHOUSE_PASSWORD: "${CLICKHOUSE_PASSWORD:-default}" + BULKER_CLICKHOUSE_DATABASE: "newjitsu_metrics" healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:3042/ready"] + test: ["CMD", "curl", "-f", "http://bulker:3042/health"] interval: 2s timeout: 10s retries: 15 depends_on: - redis: - condition: service_started + console: + condition: service_healthy kafka: condition: service_healthy + rotor: tty: true - image: jitsucom/rotor:latest + image: jitsucom/rotor:${DOCKER_TAG:-latest} + platform: linux/amd64 restart: "unless-stopped" environment: - DISABLE_SERVICE_prisma: "true" - DISABLE_SERVICE_pg: "true" + ROTOR_RAW_AUTH_TOKENS: ${BULKER_TOKEN:-default} BULKER_URL: "http://bulker:3042" - BULKER_AUTH_KEY: "${BULKER_PASSWORD:-4ba41958f341469993fd8ea1c0c932f0}" - KAFKA_BOOTSTRAP_SERVERS: "kafka:19093" - REDIS_URL: "redis://default:${REDIS_PASSWORD:-redis}@redis:6379" + BULKER_AUTH_KEY: ${BULKER_TOKEN:-default} + KAFKA_BOOTSTRAP_SERVERS: "kafka:9092" + # REDIS_URL: "redis://default:${REDIS_PASSWORD:-default}@redis:6379" + REPOSITORY_BASE_URL: "http://console:3000/api/admin/export/" + REPOSITORY_AUTH_TOKEN: "service-admin-account:${CONSOLE_TOKEN:-default}" + REPOSITORY_CACHE_DIR: "/tmp/cache" + MONGODB_URL: "mongodb://default:${MONGO_PASSWORD:-default}@mongo/" + CLICKHOUSE_HOST: "clickhouse:8123" + CLICKHOUSE_PASSWORD: "${CLICKHOUSE_PASSWORD:-default}" + CLICKHOUSE_DATABASE: "newjitsu_metrics" + healthcheck: + test: ["CMD", "curl", "-f", "http://rotor:3401/health"] + interval: 5s + timeout: 10s + retries: 15 depends_on: + console: + condition: service_healthy bulker: - condition: service_started - kafka: condition: service_healthy - redis: - condition: service_started - console: + + ingest: tty: true - image: jitsucom/console:latest + image: jitsucom/ingest:${DOCKER_TAG:-latest} + platform: linux/amd64 restart: "unless-stopped" environment: - ROTOR_URL: "http://rotor:3401" - BULKER_URL: "http://bulker:3042" - BULKER_AUTH_KEY: "${BULKER_PASSWORD:-4ba41958f341469993fd8ea1c0c932f0}" - DATABASE_URL: "postgresql://postgres:${POSTGRES_PASSWORD:-postgres}@postgres:5432/postgres" - REDIS_URL: "redis://default:${REDIS_PASSWORD:-redis}@redis:6379" - TEST_CREDENTIALS: ${TEST_CREDENTIALS} - TEST_CREDENTIALS_SHOW_LOGIN: "true" - GITHUB_CLIENT_ID: ${GITHUB_CLIENT_ID} - GITHUB_CLIENT_SECRET: ${GITHUB_CLIENT_SECRET} - NEXTAUTH_URL: "http://localhost:${JITSU_PORT:-3216}/" - UPDATE_DB: "true" + TERM: "xterm-256color" + INGEST_PUBLIC_URL: "${JITSU_INGEST_PUBLIC_URL:-http://localhost:${JITSU_INGEST_PORT:-8080}/}" + INGEST_KAFKA_BOOTSTRAP_SERVERS: "kafka:9092" + INGEST_RAW_AUTH_TOKENS: ${BULKER_TOKEN:-default} + INGEST_REPOSITORY_URL: "http://console:3000/api/admin/export/streams-with-destinations" + INGEST_SCRIPT_ORIGIN: "http://console:3000/api/s/javascript-library" + INGEST_REPOSITORY_AUTH_TOKEN: "service-admin-account:${CONSOLE_TOKEN:-default}" + INGEST_CACHE_DIR: "/tmp/cache" + INGEST_ROTOR_URL: "http://rotor:3401" + INGEST_ROTOR_AUTH_KEY: ${BULKER_TOKEN:-default} + INGEST_CLICKHOUSE_HOST: "clickhouse:8123" + INGEST_CLICKHOUSE_PASSWORD: "${CLICKHOUSE_PASSWORD:-default}" + INGEST_CLICKHOUSE_DATABASE: "newjitsu_metrics" + healthcheck: + test: ["CMD", "curl", "-f", "http://ingest:3049/health"] + interval: 2s + timeout: 10s + retries: 15 depends_on: - redis: - condition: service_started - bulker: + console: + condition: service_healthy + rotor: condition: service_started ports: - - "${JITSU_PORT:-3216}:3000" + - "${JITSU_INGEST_PORT:-8080}:3049" + + syncctl: + tty: true + image: jitsucom/syncctl:${DOCKER_TAG:-latest} + platform: linux/amd64 + restart: "on-failure" + environment: + TERM: "xterm-256color" + HTTP_PORT: ${EXTERNAL_SYNCS_PORT:-3043} + SYNCCTL_SYNCS_ENABLED: ${SYNCS_ENABLED:-false} + SYNCCTL_RAW_AUTH_TOKENS: ${SYNCCTL_TOKEN:-default} + SYNCCTL_DATABASE_URL: "postgresql://postgres:${POSTGRES_PASSWORD:-default}@127.0.0.1:${EXTERNAL_POSTGRES_PORT:-5432}/postgres?search_path=newjitsu" + SYNCCTL_SIDECAR_DATABASE_URL: "postgresql://postgres:${POSTGRES_PASSWORD:-default}@${EXTERNAL_POSTGRES_HOST}:${EXTERNAL_POSTGRES_PORT:-5432}/postgres?search_path=newjitsu" + SYNCCTL_KUBERNETES_CLIENT_CONFIG: "${SYNCCTL_KUBERNETES_CLIENT_CONFIG:-local}" + SYNCCTL_KUBERNETES_CONTEXT: "${SYNCCTL_KUBERNETES_CONTEXT}" + network_mode: "host" + depends_on: + bulker: + condition: service_healthy diff --git a/e2e/package.json b/e2e/package.json index 58c6d66cf..181b1fd8f 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -16,13 +16,13 @@ "@jitsu-internal/console": "workspace:*", "@types/jest": "^29.1.1", "find-free-port": "^2.0.0", - "ioredis": "^5.2.3", + "ioredis": "^5.3.2", "jest": "^29.3.1", "json5": "^2.1.0", "juava": "workspace:*", "node-postgres": "^0.6.2", "testcontainers": "^9.0.0", "ts-jest": "29.0.5", - "tslib": "^2.4.0" + "tslib": "^2.6.3" } } diff --git a/examples/react-app/package.json b/examples/react-app/package.json index ef950336d..96231d951 100644 --- a/examples/react-app/package.json +++ b/examples/react-app/package.json @@ -4,12 +4,12 @@ "private": true, "dependencies": { "@jitsu/jitsu-react": "workspace:*", - "@types/node": "^16.18.3", + "@types/node": "^18.15.3", "lodash": "^4.17.21", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "react-router-dom": "^6.4.3", - "react-scripts": "^5.1.0-next.14", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-router": "^6.25.1", + "react-router-dom": "^6.25.1", "web-vitals": "^2.1.4" }, "scripts": { @@ -29,10 +29,11 @@ ] }, "devDependencies": { - "typescript": "^4.9.3", - "@types/react-dom": "18.0.11", - "@types/react": "18.0.28", + "react-scripts": "^5.0.1", + "typescript": "^5.6.3", + "@types/react-dom": "^18.3.0", + "@types/react": "^18.3.3", "@types/lodash": "^4.14.185", - "tailwindcss": "^3.0.24" + "tailwindcss": "^3.4.14" } } diff --git a/examples/react-app/src/Page.tsx b/examples/react-app/src/Page.tsx index 510bd7a57..474b9082d 100644 --- a/examples/react-app/src/Page.tsx +++ b/examples/react-app/src/Page.tsx @@ -4,7 +4,7 @@ import { Link, Outlet, Route, Routes, useLocation } from "react-router-dom"; import { useJitsu } from "@jitsu/jitsu-react"; import { useUser } from "./ConfigurationProvider"; import "./Page.css"; -import { omit } from "lodash"; +import omit from "lodash/omit"; export default function Page() { const user = useUser(); diff --git a/libs/core-functions/__tests__/datalayout-segment.test.ts b/libs/core-functions/__tests__/datalayout-segment.test.ts new file mode 100644 index 000000000..6e8687164 --- /dev/null +++ b/libs/core-functions/__tests__/datalayout-segment.test.ts @@ -0,0 +1,71 @@ +import { BulkerDestinationConfig, MappedEvent, segmentLayout } from "../src/functions/bulker-destination"; + +import { + group, + groupExpected, + groupExpectedSingleTable, + identify, + identifyExpected, + identifyExpectedSingleTable, + page, + pageExpected, + pageExpectedSingleTable, + track, + trackExpected, + trackExpectedSingleTable, +} from "./lib/datalayout-test-data"; +import { FullContext } from "@jitsu/protocols/functions"; + +test("segment event", () => { + const ctx = { props: {} } as FullContext; + const pageResult = segmentLayout(page, false, ctx); + expect(Array.isArray(pageResult)).toBe(false); + const pageEvent = (pageResult as MappedEvent).event; + console.log(JSON.stringify(pageEvent, null, 2)); + expect(pageEvent).toStrictEqual(pageExpected); + + const identifyResult = segmentLayout(identify, false, ctx); + expect(Array.isArray(identifyResult)).toBe(false); + const identifyEvent = (identifyResult as MappedEvent).event; + console.log(JSON.stringify(identifyEvent, null, 2)); + expect(identifyEvent).toStrictEqual(identifyExpected); + + const trackResult = segmentLayout(track, false, ctx); + expect(Array.isArray(trackResult)).toBe(true); + const trackEvents = (trackResult as MappedEvent[]).map(t => t.event); + console.log(JSON.stringify(trackEvents, null, 2)); + expect(trackEvents).toStrictEqual(trackExpected); + + const groupResult = segmentLayout(group, false, ctx); + expect(Array.isArray(groupResult)).toBe(false); + const groupEvents = (groupResult as MappedEvent).event; + console.log(JSON.stringify(groupEvents, null, 2)); + expect(groupEvents).toStrictEqual(groupExpected); +}); + +test("segment event single table", () => { + const ctx = { props: {} } as FullContext; + const pageResult = segmentLayout(page, true, ctx); + expect(Array.isArray(pageResult)).toBe(false); + const pageEvent = (pageResult as MappedEvent).event; + console.log(JSON.stringify(pageEvent, null, 2)); + expect(pageEvent).toStrictEqual(pageExpectedSingleTable); + + const identifyResult = segmentLayout(identify, true, ctx); + expect(Array.isArray(identifyResult)).toBe(false); + const identifyEvent = (identifyResult as MappedEvent).event; + console.log(JSON.stringify(identifyEvent, null, 2)); + expect(identifyEvent).toStrictEqual(identifyExpectedSingleTable); + + const trackResult = segmentLayout(track, true, ctx); + expect(Array.isArray(trackResult)).toBe(false); + const trackEvent = (trackResult as MappedEvent).event; + console.log(JSON.stringify(trackEvent, null, 2)); + expect(trackEvent).toStrictEqual(trackExpectedSingleTable); + + const groupResult = segmentLayout(group, true, ctx); + expect(Array.isArray(groupResult)).toBe(false); + const groupEvents = (groupResult as MappedEvent).event; + console.log(JSON.stringify(groupEvents, null, 2)); + expect(groupEvents).toStrictEqual(groupExpectedSingleTable); +}); diff --git a/libs/core-functions/__tests__/facebook-conversions.test.ts b/libs/core-functions/__tests__/facebook-conversions.test.ts new file mode 100644 index 000000000..ed55330c3 --- /dev/null +++ b/libs/core-functions/__tests__/facebook-conversions.test.ts @@ -0,0 +1,30 @@ +//Check that the hashing is consistent with FB examples +//https://developers.facebook.com/docs/marketing-api/conversions-api/parameters/customer-information-parameters#email +import FacebookConversionsApi, { facebookHash } from "../src/functions/facebook-conversions"; +import { eventsSequence } from "./lib/test-data"; +import { testJitsuFunction, TestOptions } from "./lib/testing-lib"; +import { FacebookConversionApiCredentials } from "../src/meta"; + +test("hashConsistency", () => { + expect(facebookHash("john_smith@gmail.com")).toBe("62a14e44f765419d10fea99367361a727c12365e2520f32218d505ed9aa0f62f"); + expect(facebookHash("16505551212")).toBe("e323ec626319ca94ee8bff2e4c87cf613be6ea19919ed1364124e16807ab3176"); +}); + +test("test", async () => { + if (!process.env.TEST_FACEBOOK_CONVERSION_API_DESTINATION) { + console.log("Skipping facebook destination integration test - TEST_FACEBOOK_CONVERSION_API_DESTINATION is not set"); + return; + } + const events = eventsSequence().map(e => { + e.context.clientIds = { + fbp: "fb.1.1699639746924.1914402145", + }; + return e; + }); + const opts: TestOptions = { + func: FacebookConversionsApi, + configEnvVar: "TEST_FACEBOOK_CONVERSION_API_DESTINATION", + events: events, + }; + await testJitsuFunction(opts); +}); diff --git a/libs/core-functions/__tests__/hubspot-destination.test.ts b/libs/core-functions/__tests__/hubspot-destination.test.ts new file mode 100644 index 000000000..1bc90a3ba --- /dev/null +++ b/libs/core-functions/__tests__/hubspot-destination.test.ts @@ -0,0 +1,32 @@ +import { testJitsuFunction, TestOptions } from "./lib/testing-lib"; +import { eventsSequence } from "./lib/test-data"; +import { HubspotCredentials } from "../src/meta"; +import { HubspotDestination } from "../src/functions/hubspot-destination"; +import { AnalyticsServerEvent } from "@jitsu/protocols/analytics"; +import { undefined } from "zod"; + +test("hubspot-integration-test", async () => { + if (!process.env.TEST_HUBSPOT_DESTINATIONS) { + console.log("Skipping mixpanel destination integration test - TEST_HUBSPOT_DESTINATIONS is not set"); + return; + } + const groupEvent: AnalyticsServerEvent = { + context: undefined, + messageId: "group1", + type: "group", + traits: { + name: "Company 1", + }, + }; + const events = [...eventsSequence(), groupEvent]; + const opts: TestOptions = { + func: HubspotDestination, + configEnvVar: "TEST_HUBSPOT_DESTINATIONS", + events: events, + }; + await testJitsuFunction(opts); +}); + +// test("mixpanel-destination-unit", () => {љ +// //implement later, when testing library is ready to mock fetch +// }); diff --git a/libs/core-functions/__tests__/intercom-destination.test.ts b/libs/core-functions/__tests__/intercom-destination.test.ts new file mode 100644 index 000000000..1bf10375f --- /dev/null +++ b/libs/core-functions/__tests__/intercom-destination.test.ts @@ -0,0 +1,136 @@ +import { testJitsuFunction, TestOptions } from "./lib/testing-lib"; +import { IntercomDestinationCredentials } from "../src/meta"; +import IntercomDestination from "../src/functions/intercom-destination"; +import { setGlobalLogLevel, setServerLogColoring } from "juava"; +import { JitsuFunction } from "@jitsu/protocols/functions"; +import { AnalyticsServerEvent } from "@jitsu/protocols/analytics"; + +setServerLogColoring(true); +setGlobalLogLevel("debug"); + +test("test", async () => { + if (!process.env.TEST_INTERCOM_DESTINATION_CONFIG) { + console.log("TEST_INTERCOM_DESTINATION_CONFIG is not set, skipping test"); + return; + } + const email = "dwight.schrute@dunder-mifflin.com"; + const userId = "user-id-ds"; + + const workspaceId = "workspace-id-dm"; + const workspaceName = "Dunder Mifflin"; + const workspaceSlug = "dunder-mifflin"; + const name = "Dwight Schrute"; + + let opts: TestOptions; + opts = { + func: IntercomDestination, + configEnvVar: "TEST_INTERCOM_DESTINATION_CONFIG", + events: [ + { + type: "identify", + userId: userId, + traits: { email, name }, + timestamp: "2023-11-28T20:37:14.693Z", + sentAt: "2023-11-28T20:37:14.693Z", + messageId: "7qfgopt6mo22xk2tqs0tb", + groupId: workspaceId, + context: {}, + receivedAt: "2023-11-28T20:37:14.799Z", + }, + { + type: "track", + event: "user_created", + properties: {}, + userId: userId, + timestamp: "2023-11-29T16:55:50.255Z", + sentAt: "2023-11-29T16:55:50.255Z", + messageId: "22ccyzg8enx2duj3bcit8h", + writeKey: "FDiExsmGYePJa651vnN7LyhMQYq972s1:***", + context: { + traits: { email, name, externalId: `ext-${userId}` }, + page: {}, + clientIds: {}, + campaign: {}, + }, + }, + { + type: "group", + groupId: workspaceId, + traits: { workspaceSlug, workspaceName, workspaceId, name: workspaceName }, + timestamp: "2023-11-28T20:37:14.673Z", + sentAt: "2023-11-28T20:37:14.673Z", + messageId: "1xdx6pryjnuqgi4jz362j", + context: {}, + receivedAt: "2023-11-28T20:37:14.798Z", + }, + { + type: "track", + event: "workspace_created", + properties: {}, + userId, + timestamp: "2023-11-29T19:02:36.535Z", + sentAt: "2023-11-29T19:02:36.535Z", + messageId: "223drh0xi901pujmvl2kds", + writeKey: "FDiExsmGYePJa651vnN7LyhMQYq972s1:***", + groupId: "clpk4wd6n0000l90fmzoahn63", + context: { + traits: { + workspaceName, + workspaceId, + email, + name, + externalId: `ext-${userId}`, + }, + page: {}, + clientIds: {}, + campaign: {}, + }, + receivedAt: "2023-11-29T19:02:36.643Z", + }, + { + type: "page", + userId, + groupId: workspaceId, + timestamp: "2023-11-29T19:02:36.152Z", + messageId: "1m6c2acu28b1bt4eak2qk1", + context: { + traits: { + email, + name, + externalId: `ext-${userId}`, + }, + page: { + title: "Jitsu", + url: "https://use.jitsu.com/", + path: `/${workspaceSlug}`, + }, + }, + }, + { + type: "track", + event: "workspace_access", + properties: { workspaceSlug, workspaceName, workspaceId }, + userId, + anonymousId: "7c3f1bac-2e04-4ebf-8d34-95d56fb126ac", + timestamp: "2023-11-28T20:36:42.201Z", + sentAt: "2023-11-28T20:36:42.201Z", + messageId: "24zebq8rj3414ubowikeup", + groupId: workspaceId, + context: { + library: { + name: "@jitsu/js", + version: "0.0.0", + }, + traits: { workspaceSlug, workspaceName, workspaceId, email, name }, + page: {}, + clientIds: {}, + campaign: {}, + userAgent: "undici", + locale: "*", + }, + receivedAt: "2023-11-28T20:36:42.255Z", + }, + ], + }; + await testJitsuFunction(opts); +}); diff --git a/libs/core-functions/__tests__/jitsu-legacy.test.ts b/libs/core-functions/__tests__/jitsu-legacy.test.ts deleted file mode 100644 index 88ddbc757..000000000 --- a/libs/core-functions/__tests__/jitsu-legacy.test.ts +++ /dev/null @@ -1,233 +0,0 @@ -import { jitsuLegacy, MappedEvent, segmentLayout } from "../src/functions/bulker-destination"; -import { AnalyticsServerEvent } from "@jitsu/protocols/analytics"; -import type { Event as JitsuLegacyEvent } from "@jitsu/sdk-js"; - -const identify: AnalyticsServerEvent = { - writeKey: "writeKey", - messageId: "a6c09b16-c2bc-4193-990f-5e2b694ae610", - anonymousId: "6638caf0-d2c2-4bc0-aecf-8b290b559a37", - context: { - ip: "141.136.89.181", - campaign: { - medium: "medium", - name: "campaign", - source: "source", - }, - library: { - name: "jitsu-js", - version: "1.0.0", - }, - locale: "en-US", - page: { - host: "localhost:3088", - path: "/basic.html", - referrer: "https://referrer.com", - referring_domain: "referrer.com", - search: "?utm_source=source&utm_medium=medium&utm_campaign=campaign", - title: "Tracking page", - url: "https://localhost:3088/basic.html?utm_source=source&utm_medium=medium&utm_campaign=campaign", - }, - screen: { - density: 1, - height: 720, - innerHeight: 720, - innerWidth: 1280, - width: 1280, - }, - userAgent: - "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/107.0.5304.18 Safari/537.36", - }, - sentAt: "2022-11-14T08:56:34.387Z", - timestamp: "2022-11-14T08:56:34.387Z", - traits: { - caseName: "basic-identify", - email: "john.doe2@gmail.com", - }, - type: "identify", - userId: "userId2", -}; - -const legacyIdentifyExpected: Omit & { local_tz_offset?: number } = { - anon_ip: "141.136.89.0", - doc_path: "/basic.html", - doc_search: "?utm_source=source&utm_medium=medium&utm_campaign=campaign", - source_ip: "141.136.89.181", - api_key: "writeKey", - - click_id: {}, - doc_host: "localhost", - event_id: "a6c09b16-c2bc-4193-990f-5e2b694ae610", - event_type: "identify", - page_title: "Tracking page", - referer: "https://referrer.com", - src: "jitsu", - url: "https://localhost:3088/basic.html?utm_source=source&utm_medium=medium&utm_campaign=campaign", - user: { - id: "userId2", - email: "john.doe2@gmail.com", - case_name: "basic-identify", - }, - user_agent: - "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/107.0.5304.18 Safari/537.36", - user_language: "en-US", - utc_time: "2022-11-14T08:56:34.387Z", - utm: { - medium: "medium", - name: "campaign", - source: "source", - }, - vp_size: "", -}; - -const page: AnalyticsServerEvent = { - messageId: "d0c6abf6-97f7-487a-a197-8f236c728fa8", - anonymousId: "6638caf0-d2c2-4bc0-aecf-8b290b559a37", - context: { - campaign: { - medium: "medium", - name: "campaign", - source: "source", - }, - library: { - name: "jitsu-js", - version: "1.0.0", - }, - locale: "en-US", - page: { - host: "localhost:3088", - path: "/basic.html", - referrer: "https://referrer.com", - referring_domain: "", - search: "?utm_source=source&utm_medium=medium&utm_campaign=campaign", - title: "Tracking page", - url: "https://localhost:3088/basic.html?utm_source=source&utm_medium=medium&utm_campaign=campaign", - }, - screen: { - density: 1, - height: 720, - innerHeight: 720, - innerWidth: 1280, - width: 1280, - }, - traits: { - caseName: "identify-without-user-id", - email: "john.doe3@gmail.com", - }, - userAgent: - "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/107.0.5304.18 Safari/537.36", - }, - properties: { - caseName: "page-with-name", - hash: "", - height: 720, - name: "test-page", - path: "/basic.html", - search: "?utm_source=source&utm_medium=medium&utm_campaign=campaign", - title: "Tracking page", - url: "https://localhost:3088/basic.html?utm_source=source&utm_medium=medium&utm_campaign=campaign", - width: 1280, - }, - sentAt: "2022-11-14T08:56:34.395Z", - timestamp: "2022-11-14T08:56:34.395Z", - type: "page", - userId: "userId2", -}; - -const legacyPageExpected = { - api_key: "", - click_id: {}, - doc_host: "localhost", - doc_path: "/basic.html", - doc_search: "?utm_source=source&utm_medium=medium&utm_campaign=campaign", - event_id: "d0c6abf6-97f7-487a-a197-8f236c728fa8", - event_type: "page", - page_title: "Tracking page", - referer: "https://referrer.com", - src: "jitsu", - url: "https://localhost:3088/basic.html?utm_source=source&utm_medium=medium&utm_campaign=campaign", - user: { - id: "userId2", - email: "john.doe3@gmail.com", - case_name: "identify-without-user-id", - }, - user_agent: - "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/107.0.5304.18 Safari/537.36", - user_language: "en-US", - utc_time: "2022-11-14T08:56:34.395Z", - utm: { - medium: "medium", - name: "campaign", - source: "source", - }, - vp_size: "", -}; - -test("legacy event", () => { - const identifyLegacyResult = jitsuLegacy(identify).event; - const pageLegacyResult = jitsuLegacy(page).event; - // console.log(JSON.stringify(identifyLegacyResult, null, 2)); - // expect(identifyLegacyResult).toStrictEqual(legacyIdentifyExpected); - - console.log(JSON.stringify(pageLegacyResult, null, 2)); - expect(pageLegacyResult).toStrictEqual(legacyPageExpected); -}); - -const pageSegmentExpected = { - message_id: "d0c6abf6-97f7-487a-a197-8f236c728fa8", - anonymous_id: "6638caf0-d2c2-4bc0-aecf-8b290b559a37", - context: { - campaign: { - medium: "medium", - name: "campaign", - source: "source", - }, - library: { - name: "jitsu-js", - version: "1.0.0", - }, - locale: "en-US", - page: { - host: "localhost:3088", - path: "/basic.html", - referrer: "https://referrer.com", - referring_domain: "", - search: "?utm_source=source&utm_medium=medium&utm_campaign=campaign", - title: "Tracking page", - url: "https://localhost:3088/basic.html?utm_source=source&utm_medium=medium&utm_campaign=campaign", - }, - screen: { - density: 1, - height: 720, - inner_height: 720, - inner_width: 1280, - width: 1280, - }, - traits: { - case_name: "identify-without-user-id", - email: "john.doe3@gmail.com", - }, - user_agent: - "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/107.0.5304.18 Safari/537.36", - }, - sent_at: "2022-11-14T08:56:34.395Z", - timestamp: "2022-11-14T08:56:34.395Z", - type: "page", - user_id: "userId2", - case_name: "page-with-name", - hash: "", - height: 720, - name: "test-page", - path: "/basic.html", - search: "?utm_source=source&utm_medium=medium&utm_campaign=campaign", - title: "Tracking page", - url: "https://localhost:3088/basic.html?utm_source=source&utm_medium=medium&utm_campaign=campaign", - width: 1280, -}; - -test("segment event", () => { - const pageSegmentResult = segmentLayout(page, false); - expect(Array.isArray(pageSegmentResult)).toBe(false); - const pageSegment = (pageSegmentResult as MappedEvent).event; - console.log(JSON.stringify(pageSegment, null, 2)); - expect(pageSegment).toStrictEqual(pageSegmentExpected); -}); diff --git a/libs/core-functions/__tests__/lib/datalayout-test-data.ts b/libs/core-functions/__tests__/lib/datalayout-test-data.ts new file mode 100644 index 000000000..98e1bdd87 --- /dev/null +++ b/libs/core-functions/__tests__/lib/datalayout-test-data.ts @@ -0,0 +1,587 @@ +import { AnalyticsServerEvent } from "@jitsu/protocols/analytics"; + +export const page: AnalyticsServerEvent = { + messageId: "d0c6abf6-97f7-487a-a197-8f236c728fa8", + anonymousId: "6638caf0-d2c2-4bc0-aecf-8b290b559a37", + context: { + groupId: "cl9y5kgth0002ccfn3vtqz64g", + campaign: { + medium: "medium", + name: "campaign", + source: "source", + }, + library: { + name: "jitsu-js", + version: "1.0.0", + }, + locale: "en-US", + page: { + host: "localhost:3088", + path: "/basic.html", + referrer: "https://referrer.com", + referring_domain: "", + search: "?utm_source=source&utm_medium=medium&utm_campaign=campaign", + title: "Tracking page", + url: "https://localhost:3088/basic.html?utm_source=source&utm_medium=medium&utm_campaign=campaign", + }, + screen: { + density: 1, + height: 720, + innerHeight: 720, + innerWidth: 1280, + width: 1280, + }, + traits: { + caseName: "identify-without-user-id", + CaseLastName: "Doe", + User_Name: "jj", + email: "john.doe3@gmail.com", + }, + userAgent: + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/107.0.5304.18 Safari/537.36", + }, + properties: { + caseName: "page-with-name", + hash: "", + height: 720, + name: "test-page", + path: "/basic.html", + search: "?utm_source=source&utm_medium=medium&utm_campaign=campaign", + title: "Tracking page", + url: "https://localhost:3088/basic.html?utm_source=source&utm_medium=medium&utm_campaign=campaign", + width: 1280, + }, + sentAt: "2022-11-14T08:56:34.395Z", + timestamp: "2022-11-14T08:56:34.395Z", + type: "page", + userId: "userId2", +}; + +export const pageExpected = { + message_id: "d0c6abf6-97f7-487a-a197-8f236c728fa8", + anonymous_id: "6638caf0-d2c2-4bc0-aecf-8b290b559a37", + context: { + group_id: "cl9y5kgth0002ccfn3vtqz64g", + campaign: { + medium: "medium", + name: "campaign", + source: "source", + }, + library: { + name: "jitsu-js", + version: "1.0.0", + }, + locale: "en-US", + page: { + host: "localhost:3088", + path: "/basic.html", + referrer: "https://referrer.com", + referring_domain: "", + search: "?utm_source=source&utm_medium=medium&utm_campaign=campaign", + title: "Tracking page", + url: "https://localhost:3088/basic.html?utm_source=source&utm_medium=medium&utm_campaign=campaign", + }, + screen: { + density: 1, + height: 720, + inner_height: 720, + inner_width: 1280, + width: 1280, + }, + traits: { + case_name: "identify-without-user-id", + email: "john.doe3@gmail.com", + case_last_name: "Doe", + user_name: "jj", + }, + user_agent: + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/107.0.5304.18 Safari/537.36", + }, + sent_at: "2022-11-14T08:56:34.395Z", + timestamp: "2022-11-14T08:56:34.395Z", + type: "page", + user_id: "userId2", + case_name: "page-with-name", + hash: "", + height: 720, + name: "test-page", + path: "/basic.html", + search: "?utm_source=source&utm_medium=medium&utm_campaign=campaign", + title: "Tracking page", + url: "https://localhost:3088/basic.html?utm_source=source&utm_medium=medium&utm_campaign=campaign", + width: 1280, +}; + +export const pageExpectedSingleTable = { + message_id: "d0c6abf6-97f7-487a-a197-8f236c728fa8", + anonymous_id: "6638caf0-d2c2-4bc0-aecf-8b290b559a37", + context: { + group_id: "cl9y5kgth0002ccfn3vtqz64g", + campaign: { + medium: "medium", + name: "campaign", + source: "source", + }, + library: { + name: "jitsu-js", + version: "1.0.0", + }, + locale: "en-US", + page: { + host: "localhost:3088", + path: "/basic.html", + referrer: "https://referrer.com", + referring_domain: "", + search: "?utm_source=source&utm_medium=medium&utm_campaign=campaign", + title: "Tracking page", + url: "https://localhost:3088/basic.html?utm_source=source&utm_medium=medium&utm_campaign=campaign", + }, + screen: { + density: 1, + height: 720, + inner_height: 720, + inner_width: 1280, + width: 1280, + }, + traits: { + case_name: "identify-without-user-id", + email: "john.doe3@gmail.com", + case_last_name: "Doe", + user_name: "jj", + }, + user_agent: + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/107.0.5304.18 Safari/537.36", + }, + sent_at: "2022-11-14T08:56:34.395Z", + timestamp: "2022-11-14T08:56:34.395Z", + type: "page", + user_id: "userId2", + case_name: "page-with-name", + hash: "", + height: 720, + name: "test-page", + path: "/basic.html", + search: "?utm_source=source&utm_medium=medium&utm_campaign=campaign", + title: "Tracking page", + url: "https://localhost:3088/basic.html?utm_source=source&utm_medium=medium&utm_campaign=campaign", + width: 1280, +}; + +export const identify: AnalyticsServerEvent = { + writeKey: "writeKey", + messageId: "a6c09b16-c2bc-4193-990f-5e2b694ae610", + anonymousId: "6638caf0-d2c2-4bc0-aecf-8b290b559a37", + context: { + groupId: "cl9y5kgth0002ccfn3vtqz64g", + ip: "141.136.89.181", + campaign: { + medium: "medium", + name: "campaign", + source: "source", + }, + library: { + name: "jitsu-js", + version: "1.0.0", + }, + locale: "en-US", + page: { + host: "localhost:3088", + path: "/basic.html", + referrer: "https://referrer.com", + referring_domain: "referrer.com", + search: "?utm_source=source&utm_medium=medium&utm_campaign=campaign", + title: "Tracking page", + url: "https://localhost:3088/basic.html?utm_source=source&utm_medium=medium&utm_campaign=campaign", + }, + screen: { + density: 1, + height: 720, + innerHeight: 720, + innerWidth: 1280, + width: 1280, + }, + userAgent: + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/107.0.5304.18 Safari/537.36", + }, + sentAt: "2022-11-14T08:56:34.387Z", + timestamp: "2022-11-14T08:56:34.387Z", + traits: { + caseName: "basic-identify", + CaseLastName: "Doe", + User_Name: "jj", + email: "john.doe2@gmail.com", + }, + type: "identify", + userId: "userId2", +}; + +export const identifyExpected = { + write_key: "writeKey", + message_id: "a6c09b16-c2bc-4193-990f-5e2b694ae610", + anonymous_id: "6638caf0-d2c2-4bc0-aecf-8b290b559a37", + context: { + ip: "141.136.89.181", + group_id: "cl9y5kgth0002ccfn3vtqz64g", + campaign: { + medium: "medium", + name: "campaign", + source: "source", + }, + library: { + name: "jitsu-js", + version: "1.0.0", + }, + locale: "en-US", + page: { + host: "localhost:3088", + path: "/basic.html", + referrer: "https://referrer.com", + referring_domain: "referrer.com", + search: "?utm_source=source&utm_medium=medium&utm_campaign=campaign", + title: "Tracking page", + url: "https://localhost:3088/basic.html?utm_source=source&utm_medium=medium&utm_campaign=campaign", + }, + screen: { + density: 1, + height: 720, + inner_height: 720, + inner_width: 1280, + width: 1280, + }, + user_agent: + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/107.0.5304.18 Safari/537.36", + }, + sent_at: "2022-11-14T08:56:34.387Z", + timestamp: "2022-11-14T08:56:34.387Z", + case_name: "basic-identify", + case_last_name: "Doe", + user_name: "jj", + email: "john.doe2@gmail.com", + user_id: "userId2", +}; + +export const identifyExpectedSingleTable = { + write_key: "writeKey", + message_id: "a6c09b16-c2bc-4193-990f-5e2b694ae610", + anonymous_id: "6638caf0-d2c2-4bc0-aecf-8b290b559a37", + context: { + ip: "141.136.89.181", + group_id: "cl9y5kgth0002ccfn3vtqz64g", + campaign: { + medium: "medium", + name: "campaign", + source: "source", + }, + library: { + name: "jitsu-js", + version: "1.0.0", + }, + locale: "en-US", + page: { + host: "localhost:3088", + path: "/basic.html", + referrer: "https://referrer.com", + referring_domain: "referrer.com", + search: "?utm_source=source&utm_medium=medium&utm_campaign=campaign", + title: "Tracking page", + url: "https://localhost:3088/basic.html?utm_source=source&utm_medium=medium&utm_campaign=campaign", + }, + screen: { + density: 1, + height: 720, + inner_height: 720, + inner_width: 1280, + width: 1280, + }, + traits: { + case_name: "basic-identify", + case_last_name: "Doe", + user_name: "jj", + email: "john.doe2@gmail.com", + }, + user_agent: + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/107.0.5304.18 Safari/537.36", + }, + sent_at: "2022-11-14T08:56:34.387Z", + timestamp: "2022-11-14T08:56:34.387Z", + type: "identify", + user_id: "userId2", +}; + +export const track: AnalyticsServerEvent = { + type: "track", + event: "testEvent", + properties: { + testProp: "test event properties", + nestedObj: { + nestedProp: "sad", + }, + }, + userId: "user@example.com", + anonymousId: "6638caf0-d2c2-4bc0-aecf-8b290b559a37", + timestamp: "2022-11-14T08:56:34.395Z", + sentAt: "2022-11-14T08:56:34.395Z", + messageId: "d0c6abf6-97f7-487a-a197-8f236c728fa8", + context: { + groupId: "cl9y5kgth0002ccfn3vtqz64g", + library: { + name: "jitsu-js", + version: "1.0.0", + }, + ip: "127.0.0.1", + userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/111.0", + locale: "en-US", + screen: { + width: 2304, + height: 1296, + innerWidth: 1458, + innerHeight: 1186, + density: 2, + }, + traits: { + email: "user@example.com", + }, + page: { + path: "/", + referrer: "", + referring_domain: "", + host: "example.com", + search: "", + title: "Example page event", + url: "https://example.com/", + encoding: "UTF-8", + }, + campaign: { + name: "example", + source: "g", + }, + }, + requestIp: "127.0.0.1", + receivedAt: "2022-11-14T08:56:34.395Z", +}; + +export const trackExpected = [ + { + event: "testEvent", + user_id: "user@example.com", + anonymous_id: "6638caf0-d2c2-4bc0-aecf-8b290b559a37", + timestamp: "2022-11-14T08:56:34.395Z", + sent_at: "2022-11-14T08:56:34.395Z", + message_id: "d0c6abf6-97f7-487a-a197-8f236c728fa8", + context: { + group_id: "cl9y5kgth0002ccfn3vtqz64g", + library: { + name: "jitsu-js", + version: "1.0.0", + }, + ip: "127.0.0.1", + user_agent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/111.0", + locale: "en-US", + screen: { + width: 2304, + height: 1296, + inner_width: 1458, + inner_height: 1186, + density: 2, + }, + traits: { + email: "user@example.com", + }, + page: { + path: "/", + referrer: "", + referring_domain: "", + host: "example.com", + search: "", + title: "Example page event", + url: "https://example.com/", + encoding: "UTF-8", + }, + campaign: { + name: "example", + source: "g", + }, + }, + request_ip: "127.0.0.1", + received_at: "2022-11-14T08:56:34.395Z", + }, + { + event: "testEvent", + test_prop: "test event properties", + nested_obj: { + nested_prop: "sad", + }, + user_id: "user@example.com", + anonymous_id: "6638caf0-d2c2-4bc0-aecf-8b290b559a37", + timestamp: "2022-11-14T08:56:34.395Z", + sent_at: "2022-11-14T08:56:34.395Z", + message_id: "d0c6abf6-97f7-487a-a197-8f236c728fa8", + context: { + group_id: "cl9y5kgth0002ccfn3vtqz64g", + library: { + name: "jitsu-js", + version: "1.0.0", + }, + ip: "127.0.0.1", + user_agent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/111.0", + locale: "en-US", + screen: { + width: 2304, + height: 1296, + inner_width: 1458, + inner_height: 1186, + density: 2, + }, + traits: { + email: "user@example.com", + }, + page: { + path: "/", + referrer: "", + referring_domain: "", + host: "example.com", + search: "", + title: "Example page event", + url: "https://example.com/", + encoding: "UTF-8", + }, + campaign: { + name: "example", + source: "g", + }, + }, + request_ip: "127.0.0.1", + received_at: "2022-11-14T08:56:34.395Z", + }, +]; + +export const trackExpectedSingleTable = { + event: "testEvent", + test_prop: "test event properties", + nested_obj: { + nested_prop: "sad", + }, + user_id: "user@example.com", + anonymous_id: "6638caf0-d2c2-4bc0-aecf-8b290b559a37", + timestamp: "2022-11-14T08:56:34.395Z", + sent_at: "2022-11-14T08:56:34.395Z", + message_id: "d0c6abf6-97f7-487a-a197-8f236c728fa8", + context: { + group_id: "cl9y5kgth0002ccfn3vtqz64g", + library: { + name: "jitsu-js", + version: "1.0.0", + }, + ip: "127.0.0.1", + user_agent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/111.0", + locale: "en-US", + screen: { + width: 2304, + height: 1296, + inner_width: 1458, + inner_height: 1186, + density: 2, + }, + traits: { + email: "user@example.com", + }, + page: { + path: "/", + referrer: "", + referring_domain: "", + host: "example.com", + search: "", + title: "Example page event", + url: "https://example.com/", + encoding: "UTF-8", + }, + campaign: { + name: "example", + source: "g", + }, + }, + type: "track", + request_ip: "127.0.0.1", + received_at: "2022-11-14T08:56:34.395Z", +}; + +export const group: AnalyticsServerEvent = { + anonymousId: "fab18d01-fb6a-4845-b9ca-300b9db35527", + context: { + campaign: {}, + clientIds: {}, + library: { + env: "node", + name: "@jitsu/js", + version: "2.0.0", + }, + page: {}, + }, + groupId: "cl9y5kgth0002ccfn3vtqz64g", + messageId: "17dnlr6evs61jakjuz1xt6", + receivedAt: "2024-04-08T10:39:20.766Z", + requestIp: "127.0.0.1", + sentAt: "2024-04-08T10:39:20.764Z", + timestamp: "2024-04-08T10:39:20.764Z", + traits: { + name: "Ildar Nurislamov's workspace", + workspaceId: "cl9y5kgth0002ccfn3vtqz64g", + workspaceName: "Ildar Nurislamov's workspace", + workspaceSlug: "ildar", + }, + type: "group", + writeKey: "n6Cp3jwTGDFUZfD568wgve0mdCmPaT77:***", +}; + +export const groupExpected = { + anonymous_id: "fab18d01-fb6a-4845-b9ca-300b9db35527", + context: { + campaign: {}, + client_ids: {}, + library: { + env: "node", + name: "@jitsu/js", + version: "2.0.0", + }, + page: {}, + }, + group_id: "cl9y5kgth0002ccfn3vtqz64g", + message_id: "17dnlr6evs61jakjuz1xt6", + received_at: "2024-04-08T10:39:20.766Z", + request_ip: "127.0.0.1", + sent_at: "2024-04-08T10:39:20.764Z", + timestamp: "2024-04-08T10:39:20.764Z", + name: "Ildar Nurislamov's workspace", + workspace_id: "cl9y5kgth0002ccfn3vtqz64g", + workspace_name: "Ildar Nurislamov's workspace", + workspace_slug: "ildar", + write_key: "n6Cp3jwTGDFUZfD568wgve0mdCmPaT77:***", +}; + +export const groupExpectedSingleTable = { + anonymous_id: "fab18d01-fb6a-4845-b9ca-300b9db35527", + context: { + campaign: {}, + client_ids: {}, + group: { + name: "Ildar Nurislamov's workspace", + workspace_id: "cl9y5kgth0002ccfn3vtqz64g", + workspace_name: "Ildar Nurislamov's workspace", + workspace_slug: "ildar", + }, + group_id: "cl9y5kgth0002ccfn3vtqz64g", + library: { + env: "node", + name: "@jitsu/js", + version: "2.0.0", + }, + page: {}, + }, + message_id: "17dnlr6evs61jakjuz1xt6", + received_at: "2024-04-08T10:39:20.766Z", + request_ip: "127.0.0.1", + type: "group", + sent_at: "2024-04-08T10:39:20.764Z", + timestamp: "2024-04-08T10:39:20.764Z", + write_key: "n6Cp3jwTGDFUZfD568wgve0mdCmPaT77:***", +}; diff --git a/libs/core-functions/__tests__/lib/mem-store.ts b/libs/core-functions/__tests__/lib/mem-store.ts index 7cf3d2bf0..d250c184a 100644 --- a/libs/core-functions/__tests__/lib/mem-store.ts +++ b/libs/core-functions/__tests__/lib/mem-store.ts @@ -1,7 +1,7 @@ -import { AnonymousEventsStore, SetOpts, Store } from "@jitsu/protocols/functions"; +import { AnonymousEventsStore, SetOpts, Store, TTLStore } from "@jitsu/protocols/functions"; import { AnalyticsServerEvent } from "@jitsu/protocols/analytics"; -export function createStore(): Store { +export function createStore(): TTLStore { return { del(key: string): Promise { throw new Error("Method not implemented."); @@ -9,16 +9,21 @@ export function createStore(): Store { get(key: string): Promise { throw new Error("Method not implemented."); }, + getWithTTL(key: string): Promise<{ value: any; ttl: number } | undefined> { + throw new Error("Method not implemented."); + }, set(key: string, value: any, opts?: SetOpts): Promise { throw new Error("Method not implemented."); }, + ttl(key: string): Promise { + throw new Error("Method not implemented."); + }, }; } -type EventsByAnonId = Record; -const eventsStore: Record = {}; +export type EventsByAnonId = Record; -export function createAnonymousEventsStore(): AnonymousEventsStore { +export function createAnonymousEventsStore(eventsStore: Record): AnonymousEventsStore { return { async addEvent(collectionName: string, anonymousId: string, event: AnalyticsServerEvent, ttlDays: number) { let collection = eventsStore[collectionName]; diff --git a/libs/core-functions/__tests__/lib/test-data.ts b/libs/core-functions/__tests__/lib/test-data.ts index aaeddf138..330828540 100644 --- a/libs/core-functions/__tests__/lib/test-data.ts +++ b/libs/core-functions/__tests__/lib/test-data.ts @@ -54,7 +54,7 @@ function event( messageId: randomId(), originalTimestamp: new Date().toISOString(), receivedAt: new Date().toISOString(), - request_ip: "99.177.205.92", + requestIp: "99.177.205.92", sentAt: new Date().toISOString(), timestamp: new Date().toISOString(), }; @@ -65,6 +65,7 @@ function identify(opts: { url: string; userId?: string; traits?: Record; + groupId?: string; }): AnalyticsServerEvent { return { type: "identify", @@ -76,21 +77,46 @@ function identify(opts: { messageId: randomId(), originalTimestamp: new Date().toISOString(), receivedAt: new Date().toISOString(), - request_ip: "99.177.205.92", + requestIp: "99.177.205.92", sentAt: new Date().toISOString(), timestamp: new Date().toISOString(), + groupId: opts.groupId, }; } export function eventsSequence() { const traits = { email: "john.doe.1@gmail.com", name: "John Doe" }; - const events = [ - identify({ anonymousId: "anonymousId1", url: "https://jitsu.com?test=1" }), - event("page", { anonymousId: "anonymousId1", url: "https://jitsu.com?test=1" }), - event("page", { anonymousId: "anonymousId1", url: "https://jitsu.com/signup" }), - identify({ anonymousId: "anonymousId1", url: "https://jitsu.com?test=1", userId: "userId", traits }), - event("page", { anonymousId: "anonymousId1", userId: "userId1", url: "https://app.jitsu.com", traits }), - event("track/signup", { anonymousId: "anonymousId", userId: "userId1", url: "https://app.jitsu.com", traits }), + return [ + identify({ + anonymousId: "anonymousId1", + url: "https://jitsu.com?test=1", + groupId: "group1", + }), + event("page", { + anonymousId: "anonymousId1", + url: "https://jitsu.com?test=1", + }), + event("page", { + anonymousId: "anonymousId1", + url: "https://jitsu.com/signup", + }), + identify({ + anonymousId: "anonymousId1", + url: "https://jitsu.com?test=1", + userId: "userId", + traits, + }), + event("page", { + anonymousId: "anonymousId1", + userId: "userId1", + url: "https://app.jitsu.com", + traits, + }), + event("track/signup", { + anonymousId: "anonymousId", + userId: "userId1", + url: "https://app.jitsu.com", + traits, + }), ]; - return events; } diff --git a/libs/core-functions/__tests__/lib/testing-lib.ts b/libs/core-functions/__tests__/lib/testing-lib.ts index 79a5309aa..284a85efd 100644 --- a/libs/core-functions/__tests__/lib/testing-lib.ts +++ b/libs/core-functions/__tests__/lib/testing-lib.ts @@ -1,9 +1,10 @@ import { AnalyticsInterface, AnalyticsServerEvent } from "@jitsu/protocols/analytics"; -import { requireDefined } from "juava"; -import { AnyEvent, EventContext, FuncReturn, JitsuFunction, SystemContext } from "@jitsu/protocols/functions"; +import { getLog, logFormat, requireDefined } from "juava"; import nodeFetch from "node-fetch-commonjs"; +import { AnyEvent, EventContext, FuncReturn, JitsuFunction } from "@jitsu/protocols/functions"; import { createStore } from "./mem-store"; import * as JSON5 from "json5"; +import { FunctionChainContext, FunctionContext, InternalFetchType, wrapperFunction } from "../../src/functions/lib"; export type Or = | ({ [P in keyof T1]: T1[P] } & { [P in keyof T2]?: never }) @@ -12,13 +13,28 @@ export type Or = export type TestOptions = { mockFetch?: boolean; func: JitsuFunction; - ctx?: SystemContext & EventContext; + chainCtx?: FunctionChainContext; + ctx?: EventContext; } & Or<{ config: T }, { configEnvVar: string }> & Or<{ generateEvents: (jitsu: AnalyticsInterface) => void }, { events: AnalyticsServerEvent[] }>; -function prefixLogMessage(level: string, msg: any) { +export function prefixLogMessage(level: string, msg: any) { return `[${level}] ${msg}`; } +const testLogger = getLog("function-tester"); + +function toDate(timestamp?: string | number | Date): Date { + if (!timestamp) { + return new Date(); + } + if (typeof timestamp === "string") { + return new Date(timestamp); + } else if (typeof timestamp === "number") { + return new Date(timestamp); + } else { + return timestamp; + } +} export async function testJitsuFunction(opts: TestOptions): Promise { const config = @@ -29,36 +45,37 @@ export async function testJitsuFunction(opts: TestOptions): Promise< throw new Error("generateEvents() is not supported yet"); } if (opts.mockFetch) { - throw new Error("generateEvents() is not supported yet"); + throw new Error("mockFetch() is not supported yet"); } const events: AnalyticsServerEvent[] = opts.events; - const func = opts.func; - const fetch = nodeFetch; const log = { - info: (msg: any, ...args: any[]) => console.log(prefixLogMessage("INFO", msg), args), - error: (msg: any, ...args: any[]) => console.error(prefixLogMessage("ERROR", msg), args), - debug: (msg: any, ...args: any[]) => console.debug(prefixLogMessage("DEBUG", msg), args), - warn: (msg: any, ...args: any[]) => console.warn(prefixLogMessage("WARN", msg), args), + info: (ctx: FunctionContext, msg: any, ...args: any[]) => testLogger.atInfo().log(msg, ...args), + error: (ctx: FunctionContext, msg: any, ...args: any[]) => testLogger.atError().log(msg, ...args), + debug: (ctx: FunctionContext, msg: any, ...args: any[]) => testLogger.atDebug().log(msg, ...args), + warn: (ctx: FunctionContext, msg: any, ...args: any[]) => testLogger.atWarn().log(msg, ...args), }; + const func = wrapperFunction( + { log, fetch: nodeFetch as unknown as InternalFetchType, store: createStore(), ...opts.chainCtx }, + { function: { id: "test", type: "test" }, props: config }, + opts.func + ); - let res: AnyEvent[] = null; + let res: AnyEvent[] = []; for (const event of events) { try { + testLogger + .atInfo() + .log( + `📌Testing ${logFormat.bold(event.event || event.type)} message of ${toDate(event.timestamp).toISOString()}` + ); const r = await func(event, { - props: config, - fetch, - log, headers: {}, - store: createStore(), ...opts.ctx, }); if (r) { if (r === "drop") { break; } - if (res == null) { - res = []; - } if (Array.isArray(r)) { res.push(...r); } else { diff --git a/libs/core-functions/__tests__/strings.test.ts b/libs/core-functions/__tests__/strings.test.ts new file mode 100644 index 000000000..0513c4ba0 --- /dev/null +++ b/libs/core-functions/__tests__/strings.test.ts @@ -0,0 +1,81 @@ +import { idToSnakeCaseRegex } from "../src/functions/lib/strings"; +import { idToSnakeCaseFast } from "@jitsu/functions-lib"; + +const data: Record = { + // must be not touched + plain: "plain", + plain_: "plain_", + _plain: "_plain", + _plain_string: "_plain_string", + _plain__string: "_plain__string", + + // simple + camelCase: "camel_case", + camelCaseA: "camel_case_a", + cCase: "c_case", + + // node adding '_' before first + Camel: "camel", + CamelCase: "camel_case", + CCamel: "c_camel", + CCamelCase: "c_camel_case", + + // abbreviations. not fixed yet + camelUSCase: "camel_u_s_case", + camelCaseEU: "camel_case_e_u", + + // not adding extra '_' if already exists + _CamelCase: "_camel_case", + __CamelCase: "__camel_case", + Camel_Case: "camel_case", + Camel__Case: "camel__case", + + // but not collapsing existing ones + _camelCase: "_camel_case", + camelCase_: "camel_case_", + camelCase__: "camel_case__", + __camelCase: "__camel_case", +}; + +const dataExtra: Record = { + " CamelCase": "_camel_case", + " CamelCase": "__camel_case", + "Camel Case": "camel_case", + "Camel case": "camel_case", + "Camel Case": "camel__case", + "Camel Case ": "camel_case_", + "Camel Case ": "camel_case__", + "Camel _ Case": "camel___case", + "Camel_ _Case": "camel___case", + "_ CamelCase _": "__camel_case__", + " _CamelCase_ ": "__camel_case__", + + "Camel-Case": "camel-case", + "Camel-case": "camel-case", + "$camel-case": "$camel-case", + "$Camel-Case": "$camel-case", + // bulker will replace SQL unsupported characters with _ + "$camel##case": "$camel##case", + "$Camel##Case#": "$camel##case#", +}; + +test("test idToSnakeCaseFast", async () => { + for (const [value, expected] of Object.entries(data)) { + const res = idToSnakeCaseFast(value); + expect(res).toEqual(expected); + } +}); + +test("test idToSnakeCaseFast with spaces", async () => { + for (const [value, expected] of Object.entries(dataExtra)) { + const res = idToSnakeCaseFast(value); + expect(res).toEqual(expected); + } +}); + +test("test idToSnakeCaseRegex", async () => { + for (const [value, expected] of Object.entries(data)) { + const res = idToSnakeCaseRegex(value); + expect(res).toEqual(expected); + } +}); diff --git a/libs/core-functions/__tests__/udf.test.ts b/libs/core-functions/__tests__/udf.test.ts index a49ed984d..e410c694c 100644 --- a/libs/core-functions/__tests__/udf.test.ts +++ b/libs/core-functions/__tests__/udf.test.ts @@ -1,7 +1,98 @@ -import { NodeVM } from "vm2"; -import * as swc from "@swc/core"; +import { Isolate } from "isolated-vm"; +import { UDFTestRun, UDFWrapper } from "../src"; +import express from "express"; -const moduleCode = ` +test("UDFWrapper", async () => { + let server: any = undefined; + let wrapper: any = undefined; + try { + const app = express(); + app.get("/", (req, res) => { + return res.send("FETCH RESPONSE"); + }); + var promiseResolve; + let prom = new Promise((resolve, reject) => { + promiseResolve = resolve; + }); + server = app.listen(0, async () => { + const addr = server.address() as any; + promiseResolve(addr.port); + }); + const port = await prom; + + const udfCode = ` +export const config = { + slug: "udf", + name: "UDF Wrapper test", + description: "Description of UDF Wrapper test" +}; +const udf = async (event, { log, fetch, props, store, geo, ...meta }) => { + console.log("udf") + log.error("just for the test error. ignore it") + event.test = "test123" + const url = \`http://localhost:${port}/\`; + console.log("url", url) + const result = await fetch(url).then(r => r.text()); + console.log("result", result) + event.prop1 = props.prop1 + event.store1 = await store.get("store1") + event.fetch_result = result + store.set("test", result) + store.set("test2", result) + await store.del("test2", result) + console.log("done") + return event; +}; + +export default udf; +`; + + const res = await UDFTestRun({ + functionId: "udftest", + functionName: "UDF Wrapper test", + code: udfCode, + event: { + messageId: "test", + type: "page", + context: {}, + }, + variables: { + prop1: "test_prop1", + }, + store: { + store1: "test_store1", + }, + workspaceId: "test", + }); + console.log("res:" + JSON.stringify(res.result, null, 2)); + console.log("log:" + JSON.stringify(res.logs, null, 2)); + console.log("store:" + JSON.stringify(res.store, null, 2)); + console.log("error:" + JSON.stringify(res.error, null, 2)); + console.log("dropped:" + JSON.stringify(res.dropped, null, 2)); + + expect(res.error).toBeUndefined(); + + expect(res.result).toEqual({ + messageId: "test", + type: "page", + context: {}, + test: "test123", + prop1: "test_prop1", + store1: "test_store1", + fetch_result: "FETCH RESPONSE", + }); + expect(res.store).toEqual({ + store1: "test_store1", + test: "FETCH RESPONSE", + }); + } finally { + server?.close(); + wrapper?.close(); + } +}); + +test("isolate", async () => { + const moduleCode = ` let adder=0; export const myConst = 123; @@ -12,64 +103,34 @@ function inc() { } export default inc; `; + const isolate = new Isolate({ memoryLimit: 10 }); + const context = await isolate.createContext(); + const jail = context.global; + + // This make the global object available in the context as 'global'. We use 'derefInto()' here + // because otherwise 'global' would actually be a Reference{} object in the new isolate. + jail.setSync("global", jail.derefInto()); + const module = isolate.compileModuleSync(moduleCode, { filename: "udf.js" }); + module.instantiateSync(context, (specifier: string) => { + throw new Error(`import not allowed: ${specifier}`); + }); + module.evaluateSync(); + const exported = module.namespace; -test("vm2", async () => { - const vm = new NodeVM({ - timeout: 1000, - allowAsync: true, - require: false, + let ref = exported.getSync("default", { + reference: true, }); - const transpiled = swc.transformSync(moduleCode, { - filename: `index.js`, - module: { type: "commonjs" }, - }).code; - console.log("transpiled", transpiled); - // const transpiled = transformSync(moduleCode, { - // presets: ["@babel/preset-env"], - // filename: `index.js`, - // plugins: [plugin], - // }).code; - const r = vm.run(` -${transpiled} -return module.exports; - `); - console.log("exports", r); - const inc = r.default; - expect(inc()).toEqual(1); - expect(inc()).toEqual(2); - expect(inc()).toEqual(3); + const inc = async () => + await ref.applySync(undefined, [], { arguments: { reference: true }, result: { promise: true } }); + const myConst = exported.getSync("myConst"); + + console.log("udf", exported); + console.log("myConst", myConst); + console.log("inc", inc); + + expect(await inc()).toEqual(1); + expect(await inc()).toEqual(2); + expect(await inc()).toEqual(3); return []; }); -// -// test("isolate", async () => { -// const isolate = new ivm.Isolate({ memoryLimit: 10 }); -// const context = await isolate.createContext(); -// const jail = context.global; -// -// // This make the global object available in the context as 'global'. We use 'derefInto()' here -// // because otherwise 'global' would actually be a Reference{} object in the new isolate. -// jail.setSync("global", jail.derefInto()); -// const module = isolate.compileModuleSync("let exports = {}\n" + moduleCode, { filename: "udf.js" }); -// module.instantiateSync(context, (specifier: string) => { -// throw new Error(`import not allowed: ${specifier}`); -// }); -// module.evaluateSync(); -// const exported = module.namespace; -// -// let ref = exported.getSync("default", { -// reference: true, -// }); -// let add = ref.deref(); -// const myConst = exported.getSync("myConst2"); -// -// console.log("udf", exported); -// console.log("myConst", myConst); -// console.log("add", add); -// -// console.log("add", add(1, 2)); -// console.log("add", add(1, 2)); -// console.log("add", add(1, 2)); -// -// return []; -// }); diff --git a/libs/core-functions/__tests__/user-recognition.test.ts b/libs/core-functions/__tests__/user-recognition.test.ts index d7573eef7..97912d7e8 100644 --- a/libs/core-functions/__tests__/user-recognition.test.ts +++ b/libs/core-functions/__tests__/user-recognition.test.ts @@ -1,7 +1,10 @@ import { AnalyticsServerEvent } from "@jitsu/protocols/analytics"; -import UserRecognitionFunction, { UserRecognitionConfig } from "../src/functions/user-recognition"; -import { testJitsuFunction, TestOptions } from "./lib/testing-lib"; -import { createAnonymousEventsStore } from "./lib/mem-store"; +import UserRecognitionFunction from "../src/functions/user-recognition"; +import { prefixLogMessage, testJitsuFunction, TestOptions } from "./lib/testing-lib"; +import { createAnonymousEventsStore, createStore, EventsByAnonId } from "./lib/mem-store"; +import nodeFetch from "node-fetch-commonjs"; +import { FunctionContext } from "../src"; +import { InternalFetchType } from "../src/functions/lib"; const anonymousEvents: AnalyticsServerEvent[] = [ { @@ -26,20 +29,43 @@ const anonymousEvents: AnalyticsServerEvent[] = [ const identifiedEvent: AnalyticsServerEvent = { messageId: "4", - type: "page", + type: "identify", anonymousId: "anon1", userId: "user1", - context: { - traits: { - email: "test@example.com", - name: "Test User", - }, + traits: { + email: "test@example.com", + name: "Test User", + }, + context: {}, +}; + +const identifiedEventEmailOnly: AnalyticsServerEvent = { + messageId: "4", + type: "identify", + anonymousId: "anon1", + userId: "", + traits: { + email: "test@example.com", + name: "Test User", }, + context: {}, }; const expectedEvents: AnalyticsServerEvent[] = [ { messageId: "4", + type: "identify", + anonymousId: "anon1", + userId: "user1", + traits: { + email: "test@example.com", + name: "Test User", + }, + context: {}, + }, + { + messageId: "1", + _JITSU_UR_MESSAGE_ID: "4", type: "page", anonymousId: "anon1", userId: "user1", @@ -51,7 +77,8 @@ const expectedEvents: AnalyticsServerEvent[] = [ }, }, { - messageId: "1", + messageId: "2", + _JITSU_UR_MESSAGE_ID: "4", type: "page", anonymousId: "anon1", userId: "user1", @@ -63,7 +90,8 @@ const expectedEvents: AnalyticsServerEvent[] = [ }, }, { - messageId: "2", + messageId: "3", + _JITSU_UR_MESSAGE_ID: "4", type: "page", anonymousId: "anon1", userId: "user1", @@ -74,11 +102,52 @@ const expectedEvents: AnalyticsServerEvent[] = [ }, }, }, +]; + +const expectedEventsEmailOnly: AnalyticsServerEvent[] = [ + { + messageId: "4", + type: "identify", + anonymousId: "anon1", + userId: "", + traits: { + email: "test@example.com", + name: "Test User", + }, + context: {}, + }, + { + messageId: "1", + _JITSU_UR_MESSAGE_ID: "4", + type: "page", + anonymousId: "anon1", + userId: "", + context: { + traits: { + email: "test@example.com", + name: "Test User", + }, + }, + }, + { + messageId: "2", + _JITSU_UR_MESSAGE_ID: "4", + type: "page", + anonymousId: "anon1", + userId: "", + context: { + traits: { + email: "test@example.com", + name: "Test User", + }, + }, + }, { messageId: "3", + _JITSU_UR_MESSAGE_ID: "4", type: "page", anonymousId: "anon1", - userId: "user1", + userId: "", context: { traits: { email: "test@example.com", @@ -89,8 +158,21 @@ const expectedEvents: AnalyticsServerEvent[] = [ ]; test("user-recognition-test", async () => { + const store = createStore(); + const eventsStore: Record = {}; const options: TestOptions = { func: UserRecognitionFunction, + chainCtx: { + fetch: nodeFetch as unknown as InternalFetchType, + store: store, + log: { + info: (ctx: FunctionContext, msg: any, ...args: any[]) => console.log(prefixLogMessage("INFO", msg), args), + error: (ctx: FunctionContext, msg: any, ...args: any[]) => console.error(prefixLogMessage("ERROR", msg), args), + debug: (ctx: FunctionContext, msg: any, ...args: any[]) => console.debug(prefixLogMessage("DEBUG", msg), args), + warn: (ctx: FunctionContext, msg: any, ...args: any[]) => console.warn(prefixLogMessage("WARN", msg), args), + }, + anonymousEventsStore: createAnonymousEventsStore(eventsStore), + }, ctx: { headers: {}, connection: { @@ -100,15 +182,30 @@ test("user-recognition-test", async () => { deduplicate: true, }, }, - $system: { - anonymousEventsStore: createAnonymousEventsStore(), + destination: { + id: "test", + type: "test", + updatedAt: new Date(), + hash: "123", + }, + source: { + id: "test", + type: "browser", + }, + workspace: { + id: "test", }, + receivedAt: new Date(), }, - config: {} as UserRecognitionConfig, + config: {}, events: [], }; - let res = await testJitsuFunction({ ...options, events: anonymousEvents }); - expect(res).toEqual(anonymousEvents); + const copy = JSON.parse(JSON.stringify(anonymousEvents)); + let res = await testJitsuFunction({ ...options, events: copy }); + expect(res).toEqual([]); + + res = await testJitsuFunction({ ...options, events: [identifiedEventEmailOnly] }); + expect(res).toEqual([]); res = await testJitsuFunction({ ...options, events: [identifiedEvent] }); expect(res).toEqual(expectedEvents); @@ -116,5 +213,61 @@ test("user-recognition-test", async () => { const fifthEvent = { ...identifiedEvent, messageId: "5" }; //no more anonymous events res = await testJitsuFunction({ ...options, events: [fifthEvent] }); - expect(res).toEqual([fifthEvent]); + expect(res).toEqual([]); +}); + +test("user-recognition-test-email-only", async () => { + const store = createStore(); + const eventsStore: Record = {}; + + const options: TestOptions = { + func: UserRecognitionFunction, + chainCtx: { + fetch: nodeFetch as unknown as InternalFetchType, + store: store, + log: { + info: (ctx: FunctionContext, msg: any, ...args: any[]) => console.log(prefixLogMessage("INFO", msg), args), + error: (ctx: FunctionContext, msg: any, ...args: any[]) => console.error(prefixLogMessage("ERROR", msg), args), + debug: (ctx: FunctionContext, msg: any, ...args: any[]) => console.debug(prefixLogMessage("DEBUG", msg), args), + warn: (ctx: FunctionContext, msg: any, ...args: any[]) => console.warn(prefixLogMessage("WARN", msg), args), + }, + anonymousEventsStore: createAnonymousEventsStore(eventsStore), + }, + ctx: { + headers: {}, + connection: { + id: "test", + options: { + primaryKey: "messageId", + deduplicate: true, + }, + }, + destination: { + id: "test", + type: "test", + updatedAt: new Date(), + hash: "123", + }, + source: { + id: "test", + type: "browser", + }, + workspace: { + id: "test", + }, + receivedAt: new Date(), + }, + config: {}, + events: [], + }; + const copy = JSON.parse(JSON.stringify(anonymousEvents)); + let res = await testJitsuFunction({ ...options, events: copy }); + expect(res).toEqual([]); + + res = await testJitsuFunction({ ...options, events: [identifiedEventEmailOnly] }); + expect(res).toEqual([]); + + options.ctx.connection.options.functionsEnv = { IDENTIFYING_TRAITS: "email" }; + res = await testJitsuFunction({ ...options, events: [identifiedEventEmailOnly] }); + expect(res).toEqual(expectedEventsEmailOnly); }); diff --git a/libs/core-functions/jest.config.js b/libs/core-functions/jest.config.js index 6ef2c4778..de6e6d30e 100644 --- a/libs/core-functions/jest.config.js +++ b/libs/core-functions/jest.config.js @@ -5,5 +5,5 @@ module.exports = { testMatch: ["**/__tests__/**/*.test.ts"], testEnvironment: "node", runner: "jest-runner", - testMatch: ["**/__tests__/**/*.test.ts"], + "setupFiles": ["./jest.setup.js"] }; diff --git a/libs/core-functions/jest.setup.js b/libs/core-functions/jest.setup.js new file mode 100644 index 000000000..847bac649 --- /dev/null +++ b/libs/core-functions/jest.setup.js @@ -0,0 +1,8 @@ + +global.console = { + log: message => process.stdout.write(message + '\n'), + error: console.error, + warn: console.warn, + info: console.info, + debug: console.debug, +}; diff --git a/libs/core-functions/package.json b/libs/core-functions/package.json index 9e6014006..276f9da32 100644 --- a/libs/core-functions/package.json +++ b/libs/core-functions/package.json @@ -20,26 +20,39 @@ "test": "tsc -p . && jest --verbose" }, "devDependencies": { + "@jitsu/functions-lib": "workspace:*", "@jitsu/protocols": "workspace:*", - "@jitsu/sdk-js": "^3.1.3", - "@types/jest": "^29.1.1", + "@jitsu/sdk-js": "^3.1.5", + "@types/jest": "^29.5.12", "@types/lodash": "^4.14.185", "@types/node": "^18.15.3", - "jest": "^29.5.0", + "express": "^4.21.2", + "jest": "^29.7.0", "json5": "^2.1.0", - "node-fetch-commonjs": "^3.2.4", - "ts-jest": "29.0.5" + "node-fetch-commonjs": "^3.3.2", + "ts-jest": "^29.2.3" }, "dependencies": { - "@swc/core": "^1.3.40", - "@swc/wasm": "^1.3.40", - "vm2": "^3.9.14", + "@amplitude/ua-parser-js": "^0.7.33", + "@hubspot/api-client": "^11.1.0", + "@mongodb-js/zstd": "^2.0.1", + "undici": "^7.11.0", + "ip": "^2.0.1", + "agentkeepalive": "4.3.0", + "dayjs": "^1.11.10", + "google-ads-api": "^17.1.0-rest-beta", + "ioredis": "^5.3.2", + "isolated-vm": "6.0.0", "juava": "workspace:*", "lodash": "^4.17.21", - "mongodb": "^5.1.0", - "posthog-node": "^2.4.0", - "tslib": "^2.4.0", - "zod": "3.21.4", - "agentkeepalive": "4.3.0" + "mongodb": "^6.16.0", + "@clickhouse/client": "^1.10.1", + "node-cache": "^5.1.2", + "parse-duration": "^1.1.2", + "axios": "1.8.2", + "posthog-node": "^4.2.1", + "tslib": "^2.6.3", + "zod": "^3.23.8", + "node-sql-parser": "^5.3.8" } } diff --git a/libs/core-functions/src/context.ts b/libs/core-functions/src/context.ts deleted file mode 100644 index deedbc665..000000000 --- a/libs/core-functions/src/context.ts +++ /dev/null @@ -1,140 +0,0 @@ -import { EventContext, EventsStore, FetchOpts, FullContext, Store, SystemContext } from "@jitsu/protocols/functions"; -import nodeFetch from "node-fetch-commonjs"; -import { getErrorMessage, getLog } from "juava"; -import { httpAgent, httpsAgent } from "./functions/lib/http-agent"; - -const log = getLog("functions-context"); - -export function createFullContext( - functionId: string, - eventsStore: EventsStore, - store: Store, - eventContext: EventContext, - systemContext: SystemContext | {} = {}, - props: Record = {} -): FullContext { - const ar = functionId.split("."); - const id = ar.pop(); - const type = ar.join("."); - return { - props: props, - store: store, - async fetch(url: string, init?: FetchOpts, logToRedis: boolean = true): Promise { - const baseInfo = { - functionId: id, - functionType: type, - type: "http-request", - url: url, - method: init?.method || "GET", - body: init?.body, - headers: init?.headers ? hideSensitiveHeaders(init.headers) : undefined, - }; - if (!init?.agent) { - init = { ...init, agent: (url.startsWith("https://") ? httpsAgent : httpAgent)() }; - } - let fetchResult: any = undefined; - try { - fetchResult = await nodeFetch(url, init as any); - } catch (err) { - if (logToRedis) { - eventsStore.log(true, { ...baseInfo, error: getErrorMessage(err) }); - } - throw err; - } - //clone response to be able to read it twice - const cloned = fetchResult.clone(); - if (logToRedis) { - eventsStore.log(!fetchResult.ok, { - ...baseInfo, - status: fetchResult.status, - statusText: fetchResult.statusText, - response: await tryJson(cloned), - }); - } - - return fetchResult; - }, - log: { - debug: (message, ...args: any[]) => { - log.atDebug().log(`[CON:${eventContext.connection?.id}]: [f:${id}]: ${message}`, ...args); - eventsStore.log(false, { - type: "log-debug", - functionId: id, - functionType: type, - message: { - text: message, - args, - }, - }); - }, - warn: (message, ...args: any[]) => { - log.atWarn().log(`[CON:${eventContext.connection?.id}]: [f:${id}]: ${message}`, ...args); - eventsStore.log(false, { - type: "log-warn", - functionId: id, - functionType: type, - message: { - text: message, - args, - }, - }); - }, - error: (message, ...args: any[]) => { - eventsStore.log(true, { - type: "log-error", - functionId: id, - functionType: type, - message: { - text: message, - args: args.map(a => `${a}`), - }, - }); - const l = log.atError(); - if (args.length > 0) { - const last = args[args.length - 1]; - if (last.stack) { - l.withCause(last); - args = args.slice(0, args.length - 1); - } - } - l.log(`[CON:${eventContext.connection?.id}]: [f:${id}]: ${message}`, ...args); - }, - info: (message, ...args: any[]) => { - log.atInfo().log(`[CON:${eventContext.connection?.id}]: [f:${id}]: ${message}`, ...args); - eventsStore.log(false, { - type: "log-info", - functionId: id, - functionType: type, - message: { - text: message, - args, - }, - }); - }, - }, - ...eventContext, - ...systemContext, - }; -} - -function hideSensitiveHeaders(headers: Record): Record { - const result: Record = {}; - for (const [k, v] of Object.entries(headers)) { - result[k] = k.toLowerCase().includes("authorization") || k.toLowerCase().includes("token") ? "*****" : v; - } - return result; -} - -async function tryJson(fetchResult: Response): Promise { - const text = await fetchResult.text(); - const maxLen = 1000; - try { - return JSON.parse(text); - } catch (err) { - if (text.length < maxLen) { - return text; - } else { - return `${text.substring(0, maxLen)} ... [truncated, length: ${text.length}]`; - } - } -} diff --git a/libs/core-functions/src/functions/amplitude-destination.ts b/libs/core-functions/src/functions/amplitude-destination.ts new file mode 100644 index 000000000..964837a39 --- /dev/null +++ b/libs/core-functions/src/functions/amplitude-destination.ts @@ -0,0 +1,173 @@ +import { JitsuFunction } from "@jitsu/protocols/functions"; +import { RetryError } from "@jitsu/functions-lib"; +import { AnalyticsServerEvent } from "@jitsu/protocols/analytics"; +import { randomUUID } from "crypto"; +import { AmplitudeDestinationConfig } from "../meta"; +import { eventTimeSafeMs, getPageOrScreenProps } from "./lib"; + +const AmplitudeDestination: JitsuFunction = async ( + event, + { props, store, fetch, log, geo, ua, ...ctx } +) => { + try { + const deviceId = event.anonymousId; + let sessionId: number | undefined = undefined; + const optionsPart = props.minIdLength + ? { + options: { + min_id_length: props.minIdLength, + }, + } + : {}; + if (deviceId) { + const ttlStore = store; + const ttlSec = 60 * (props.sessionWindow ?? 30); + const sessionKey = `${ctx.source.id}_${deviceId}_sess`; + const savedSessionValue = await ttlStore.getWithTTL(sessionKey); + if (savedSessionValue) { + sessionId = savedSessionValue.value; + const ttl = savedSessionValue.ttl; + log.debug(`Amplitude session found: ${sessionId} for deviceId: ${deviceId} ttl: ${ttl}`); + if (ttl < ttlSec - 60) { + // refresh ttl not often than once per minute + await ttlStore.set(sessionKey, sessionId, { ttl: ttlSec }); + } + } else { + sessionId = new Date().getTime(); + log.debug(`Amplitude session not found for deviceId: ${deviceId} new session: ${sessionId}`); + await ttlStore.set(sessionKey, sessionId, { ttl: ttlSec }); + } + } + const groupType = props.groupType || "group"; + const endpoint = + props.dataResidency === "EU" ? "https://api.eu.amplitude.com/2/httpapi" : "https://api2.amplitude.com/2/httpapi"; + let payload: any = undefined; + if (event.amplitudeEvent && typeof event.amplitudeEvent === "object") { + payload = { + api_key: props.key, + events: Array.isArray(event.amplitudeEvent) ? event.amplitudeEvent : [event.amplitudeEvent], + }; + } else if (event.type === "identify" && event.userId) { + payload = { + api_key: props.key, + events: [ + { + time: eventTimeSafeMs(event), + insert_id: event.messageId || randomUUID(), + user_id: event.userId, + event_type: "$identify", + user_properties: { + $set: { + ...event.traits, + }, + }, + }, + ], + ...optionsPart, + }; + } else if (event.type === "group" && props.enableGroupAnalytics && event.userId) { + payload = { + api_key: props.key, + events: [ + { + time: eventTimeSafeMs(event), + insert_id: event.messageId || randomUUID(), + user_id: event.userId, + event_type: "$groupidentify", + group_properties: { + $set: { + ...event.traits, + }, + }, + groups: { + [groupType]: event.groupId, + }, + }, + ], + ...optionsPart, + }; + } else if ( + (event.type === "page" || event.type === "track" || event.type === "screen") && + (event.userId || props.enableAnonymousUserProfiles) + ) { + const app = event.context?.app || ({} as any); + const os = event.context?.os || ({} as any); + const device = event.context?.device || ({} as any); + + let geoObj: any = {}; + if (geo) { + geoObj = { + country: geo.country?.code, + region: geo.region?.code, + city: geo.city?.name, + dma: geo.location?.usaData ? geo.location.usaData.metroCode : undefined, + location_lat: geo.location?.latitude, + location_lng: geo.location?.longitude, + }; + } + let groups = {}; + if (event.context?.groupId && props.enableGroupAnalytics) { + groups = { [groupType]: event.context?.groupId }; + } + let eventType: string = event.type; + switch (event.type) { + case "page": + eventType = "pageview"; + break; + case "track": + eventType = event.event || "Unknown Event"; + break; + } + payload = { + api_key: props.key, + events: [ + { + time: eventTimeSafeMs(event), + insert_id: event.messageId || randomUUID(), + event_type: eventType, + session_id: sessionId || -1, + event_properties: { ...getPageOrScreenProps(event), ...event.properties }, + groups, + user_properties: event.context?.traits, + user_id: event.userId, + + app_version: app.version, + platform: os.name || ua?.device?.type, + + device_id: deviceId ?? undefined, + os_name: os.name || ua?.os?.name, + os_version: os.version || ua?.os?.version, + device_model: device.model || ua?.device?.model, + device_manufacturer: device.manufacturer || ua?.device?.vendor, + device_brand: device.manufacturer || ua?.device?.vendor, + + language: event.context?.locale, + ip: event.context?.ip, + user_agent: event.context?.userAgent, + ...geoObj, + }, + ], + ...optionsPart, + }; + } + if (payload) { + const res = await fetch(endpoint, { + headers: { + "Content-Type": "application/json", + Accept: "*/*", + }, + method: "POST", + body: JSON.stringify(payload), + }); + if (res.status === 200) { + log.debug(`Amplitude ${event.type} OK: ${res.status} message: ${await res.text()}`); + } else { + throw new Error(`Amplitude ${event.type} Error: ${res.status} message: ${await res.text()}`); + } + } + } catch (e: any) { + throw new RetryError(e.message); + } +}; + +export default AmplitudeDestination; diff --git a/libs/core-functions/src/functions/braze-destination.ts b/libs/core-functions/src/functions/braze-destination.ts new file mode 100644 index 000000000..6939ff84c --- /dev/null +++ b/libs/core-functions/src/functions/braze-destination.ts @@ -0,0 +1,281 @@ +import { FullContext, JitsuFunction } from "@jitsu/protocols/functions"; +import { RetryError } from "@jitsu/functions-lib"; +import type { AnalyticsServerEvent } from "@jitsu/protocols/analytics"; +import { BrazeCredentials } from "../meta"; +import { eventTimeSafeMs } from "./lib"; +import omit from "lodash/omit"; +import { pick } from "lodash"; + +const endpoints = { + "US-01 : dashboard-01.braze.com": "https://rest.iad-01.braze.com", + "US-02 : dashboard-02.braze.com": "https://rest.iad-02.braze.com", + "US-03 : dashboard-03.braze.com": "https://rest.iad-03.braze.com", + "US-04 : dashboard-04.braze.com": "https://rest.iad-04.braze.com", + "US-05 : dashboard-05.braze.com": "https://rest.iad-05.braze.com", + "US-06 : dashboard-06.braze.com": "https://rest.iad-06.braze.com", + "US-07 : dashboard-07.braze.com": "https://rest.iad-07.braze.com", + "US-08 : dashboard-08.braze.com": "https://rest.iad-08.braze.com", + "US-09 : dashboard-09.braze.com": "https://rest.iad-09.braze.com", + "EU-01 : dashboard-01.braze.eu": "https://rest.fra-01.braze.eu", + "EU-02 : dashboard-02.braze.eu": "https://rest.fra-02.braze.eu", +}; +export type HttpRequest = { + method?: string; + url: string; + payload?: any; + headers?: Record; +}; + +function toBrazeGender(gender: string | null | undefined): string | null | undefined { + if (!gender) { + return gender; + } + + const genders: Record = { + M: ["man", "male", "m"], + F: ["woman", "female", "w", "f"], + O: ["other", "o"], + N: ["not applicable", "n"], + P: ["prefer not to say", "p"], + }; + + const brazeGender = Object.keys(genders).find(key => genders[key].includes(gender.toLowerCase())); + return brazeGender || gender; +} + +function getAnonymousIdAlias(event: AnalyticsServerEvent, ctx: FullContext) { + if (ctx.props.useJitsuAnonymousIdAlias && event.anonymousId) { + return { + alias_name: event.anonymousId, + alias_label: "anonymous_id", + }; + } +} + +function getIdPart(event: AnalyticsServerEvent, ctx: FullContext) { + let idPart = {} as any; + const traits = event.traits || event.context?.traits || {}; + const user_alias = traits.user_alias || event.properties?.user_alias || getAnonymousIdAlias(event, ctx); + const braze_id = traits.braze_id || event.properties?.braze_id; + if (event.userId) { + idPart.external_id = event.userId; + } else if (user_alias) { + idPart.user_alias = user_alias; + } else if (braze_id) { + idPart.braze_id = braze_id; + } + if (traits.email) { + idPart.email = traits.email; + } + if (traits.phone) { + idPart.phone = traits.phone; + } + if (Object.keys(idPart).length === 0) { + throw new Error('One of "external_id", "user_alias", "braze_id", "email" or "phone" is required.'); + } + return idPart; +} + +function trackEvent(event: AnalyticsServerEvent, ctx: FullContext): any { + const reservedKeys = ["currency", "price", "quantity", "time", "event_name"]; + return { + events: [ + { + ...getIdPart(event, ctx), + app_id: ctx.props.appId, + name: event.event, + time: new Date(eventTimeSafeMs(event)).toISOString(), + properties: omit(event.properties, reservedKeys), + _update_existing_only: false, + }, + ], + }; +} + +function trackPurchase(event: AnalyticsServerEvent, ctx: FullContext): any { + const products = event.properties?.products as any[]; + if (!products || !products.length) { + return; + } + const reservedKeys = ["product_id", "currency", "price", "quantity", "time", "event_name"]; + const event_properties = omit(event.properties, ["products"]); + const base = { + ...getIdPart(event, ctx), + app_id: ctx.props.appId, + time: new Date(eventTimeSafeMs(event)).toISOString(), + _update_existing_only: false, + }; + return { + purchases: products.map(product => ({ + ...base, + product_id: product.product_id, + currency: product.currency ?? "USD", + price: product.price, + quantity: product.quantity, + properties: { + ...omit(product, reservedKeys), + ...omit(event_properties, reservedKeys), + }, + })), + }; +} + +function updateUserProfile(event: AnalyticsServerEvent, ctx: FullContext): any { + const geo = ctx.geo || ({} as any); + const traits = event.traits || ({} as any); + const knownProps = [ + "country", + "current_location", + "date_of_first_session", + "date_of_last_session", + "dob", + "email", + "email_subscribe", + "email_open_tracking_disabled", + "email_click_tracking_disabled", + "facebook", + "first_name", + "home_city", + "image_url", + "language", + "last_name", + "marked_email_as_spam_at", + "phone", + "push_subscribe", + "push_tokens", + "time_zone", + "twitter", + "subscription_groups", + ]; + return { + attributes: [ + { + ...getIdPart(event, ctx), + country: ctx.geo?.country?.name, + current_location: + geo.location?.latitude && geo.location?.longitude + ? { + latitude: geo.location?.latitude, + longitude: geo.location?.longitude, + } + : undefined, + first_name: traits.firstName, + last_name: traits.lastName, + home_city: traits.address?.city, + image_url: traits.avatar, + time_zone: geo.location?.timezone, + gender: toBrazeGender(traits.gender), + ...omit(traits, ["firstName", "lastName", "avatar", "gender", "user_alias", "braze_id"]), + _update_existing_only: false, + }, + ], + }; +} + +function identifyUser(event: AnalyticsServerEvent, ctx: FullContext): any { + const external_id = event.userId; + const user_alias = event.traits?.user_alias || getAnonymousIdAlias(event, ctx); + if (!external_id || !user_alias) { + return; + } + return { + aliases_to_identify: [ + { + external_id, + user_alias, + }, + ], + merge_behavior: "merge", + }; +} + +const BrazeDestination: JitsuFunction = async (event, ctx) => { + const endpoint = endpoints[ctx.props.endpoint]; + if (!endpoint) { + throw new Error(`Unknown endpoint ${ctx.props.endpoint}`); + } + try { + let httpRequests: HttpRequest[] = []; + const headers = { + "Content-type": "application/json", + Authorization: `Bearer ${ctx.props.apiKey}`, + }; + const url = `${endpoint}/users/track`; + try { + if (event.type === "identify") { + httpRequests.push({ + url, + payload: updateUserProfile(event, ctx), + headers, + }); + const identify = identifyUser(event, ctx); + if (identify) { + httpRequests.push({ + url: `${endpoint}/users/identify`, + payload: identify, + headers, + }); + } + } else if (event.type === "track" && event.event != "Order Completed") { + httpRequests.push({ + url, + payload: trackEvent(event, ctx), + headers, + }); + } else if (event.type === "track" && event.event === "Order Completed") { + httpRequests.push({ + url, + payload: trackPurchase(event, ctx), + headers, + }); + } else if ((event.type === "page" || event.type === "screen") && ctx.props.sendPageEvents) { + const track = { ...event }; + track.event = event.type; + const props = { ...event.properties }; + if (event.name) { + props[`${event.type}_name`] = event.name; + } + if (event.category) { + props[`${event.type}_category`] = event.category; + } + track.properties = props; + httpRequests.push({ + url, + payload: trackEvent(track, ctx), + headers, + }); + } + } catch (e: any) { + ctx.log.error(e); + return false; + } + + for (const httpRequest of httpRequests) { + if (httpRequest.payload) { + const method = httpRequest.method || "POST"; + const result = await ctx.fetch(httpRequest.url, { + method, + headers: httpRequest.headers, + ...(httpRequest.payload ? { body: JSON.stringify(httpRequest.payload) } : {}), + }); + if (result.status !== 200 && result.status !== 201) { + throw new Error( + `Braze ${method} ${httpRequest.url}:${ + httpRequest.payload ? `${JSON.stringify(httpRequest.payload)} --> ` : "" + }${result.status} ${await result.text()}` + ); + } else { + ctx.log.debug(`Braze ${method} ${httpRequest.url}: ${result.status} ${await result.text()}`); + } + } + } + } catch (e: any) { + throw new RetryError(e.message); + } +}; + +BrazeDestination.displayName = "braze-destination"; + +BrazeDestination.description = "This functions covers jitsu events and sends them to Braze"; + +export default BrazeDestination; diff --git a/libs/core-functions/src/functions/bulker-destination.ts b/libs/core-functions/src/functions/bulker-destination.ts index 195122802..eb6171d4b 100644 --- a/libs/core-functions/src/functions/bulker-destination.ts +++ b/libs/core-functions/src/functions/bulker-destination.ts @@ -1,158 +1,172 @@ -import { JitsuFunction } from "@jitsu/protocols/functions"; +import { FullContext, JitsuFunction, UserAgent } from "@jitsu/protocols/functions"; +import { + HTTPError, + RetryError, + transferAsSnakeCase, + transferValueAsSnakeCase, + transfer, + transferValue, + TableNameParameter, + toJitsuClassic, +} from "@jitsu/functions-lib"; import { AnalyticsServerEvent, DataLayoutType } from "@jitsu/protocols/analytics"; -import type { Event as JitsuLegacyEvent } from "@jitsu/sdk-js"; -import { omit } from "lodash"; +import { request, Agent } from "undici"; +import omit from "lodash/omit"; +import { bulkerPartitionParam, MetricsMeta } from "./lib"; +import { UserRecognitionParameter } from "./user-recognition"; +import { parseNumber } from "juava"; + +const JitsuInternalProperties = [TableNameParameter, UserRecognitionParameter]; + +const concurrency = parseNumber(process.env.CONCURRENCY, 10); +const fetchTimeoutMs = parseNumber(process.env.FETCH_TIMEOUT_MS, 2000); + +export const undiciAgent = new Agent({ + connections: concurrency, // Limit concurrent kept-alive connections to not run out of resources + maxRequestsPerClient: 3000, + headersTimeout: fetchTimeoutMs, + connectTimeout: fetchTimeoutMs, + bodyTimeout: fetchTimeoutMs, +}); -const TableNameParameter = "JITSU_TABLE_NAME"; export type MappedEvent = { event: any; table: string; }; -export type DataLayoutImpl = (event: AnalyticsServerEvent) => MappedEvent[] | MappedEvent; - -function anonymizeIp(ip: string | undefined) { - if (!ip) { - return; - } - const parts = ip.split("."); - if (parts.length === 4) { - return `${parts[0]}.${parts[1]}.${parts[2]}.0`; - } -} - -function idToSnakeCase(id: string) { - return id.replace(/([A-Z])/g, "_$1").toLowerCase(); -} - -function toSnakeCase(param: any): any { - if (Array.isArray(param)) { - return param.map(toSnakeCase); - } else if (typeof param === "object" && param !== null) { - return Object.fromEntries(Object.entries(param).map(([key, value]) => [idToSnakeCase(key), toSnakeCase(value)])); - } else { - return param; - } -} - -export function removeUndefined(param: any): any { - if (Array.isArray(param)) { - return param.map(removeUndefined); - } else if (typeof param === "object" && param !== null) { - return Object.fromEntries( - Object.entries(param) - .filter(([, value]) => value !== undefined) - .map(([key, value]) => [key, removeUndefined(value)]) - ); - } else { - return param; - } -} - -export function jitsuLegacy(event: AnalyticsServerEvent): MappedEvent { - let url: URL | undefined = undefined; - const urlStr = event.context.page?.url || event.properties?.url; - try { - if (urlStr) { - url = new URL(urlStr as string); - } - } catch (e) {} +export type DataLayoutImpl = ( + event: AnalyticsServerEvent, + ctx: FullContext +) => MappedEvent[] | MappedEvent; - const flat = removeUndefined( - toSnakeCase({ - anon_ip: event.context?.ip ? anonymizeIp(event.context?.ip) : undefined, - api_key: event.writeKey || "", - click_id: {}, - doc_encoding: event.context?.page?.encoding || event.properties?.encoding, - doc_host: url?.hostname, - doc_path: url?.pathname, - doc_search: url?.search, - event_id: event.messageId, - event_type: event.type, - local_tz_offset: event.context?.page?.timezoneOffset || event.properties?.timezoneOffset, - page_title: event.context?.page?.title, - referer: event.context?.page?.referrer, - screen_resolution: event.context?.page?.screenResolution, - source_ip: event.context?.ip, - src: "jitsu", - url: (urlStr || "") as string, - user: { - id: event.userId, - email: (event.context?.traits?.email || event.traits?.email || undefined) as string | undefined, - name: (event.context?.traits?.name || event.traits?.name || undefined) as string | undefined, - ...omit( - { - ...(event.context?.traits || {}), - ...(event.traits || {}), - }, - ["email", "name"] - ), - }, - user_agent: event.context.userAgent, - user_language: event.context?.locale, - utc_time: event.timestamp, - utm: event.context?.campaign, - vp_size: "", - }) - ); - return { event: flat, table: event[TableNameParameter] ?? "events" }; +export function jitsuLegacy(event: AnalyticsServerEvent, ctx: FullContext): MappedEvent { + const flat = toJitsuClassic(event, ctx); + return { event: omit(flat, JitsuInternalProperties), table: event[TableNameParameter] ?? "events" }; } -export function segmentLayout(event: AnalyticsServerEvent, singleTable: boolean): MappedEvent[] | MappedEvent { +export function segmentLayout( + event: AnalyticsServerEvent, + singleTable: boolean, + ctx: FullContext +): MappedEvent[] | MappedEvent { let transformed: any; //track without properties for segment multi-table layout, because full track event is stored in the table with event name let baseTrackFlat: any; + const keepOriginalNames = !!ctx.props.keepOriginalNames; + const transferFunc = keepOriginalNames ? transfer : transferAsSnakeCase; + const transferValueFunc = keepOriginalNames ? transferValue : transferValueAsSnakeCase; switch (event.type) { case "identify": - transformed = { - ...(event.context ? { context: omit(event.context, "traits") } : {}), - ...event.properties, - ...event.context?.traits, - ...event.traits, - ...omit(event, ["context", "properties", "traits", "type", TableNameParameter]), - }; + if (singleTable) { + transformed = { + context: { + traits: {}, + }, + }; + transferFunc(transformed.context, event.context, ["groupId", "traits"]); + transferFunc(transformed.context.traits, event.context?.traits, ["groupId"]); + transferFunc(transformed.context.traits, event.traits, ["groupId"]); + transferValueFunc( + transformed.context, + "group_id", + event.context?.groupId || event.traits?.groupId || event.context?.traits?.groupId + ); + transferFunc(transformed, event.properties); + transferFunc(transformed, event, ["context", "properties", "traits", "type", ...JitsuInternalProperties]); + } else { + transformed = { + context: {}, + }; + transferFunc(transformed.context, event.context, ["traits"]); + transferFunc(transformed, event.properties); + transferFunc(transformed, event.context?.traits); + transferFunc(transformed, event.traits); + transferFunc(transformed, event, ["context", "properties", "traits", "type", ...JitsuInternalProperties]); + } break; case "group": - transformed = { - ...(event.context ? { context: omit(event.context, "traits") } : {}), - ...event.properties, - ...event.traits, - ...omit(event, ["context", "properties", "traits", "type", TableNameParameter]), - }; + if (singleTable) { + transformed = { + context: { + group: {}, + }, + }; + transferFunc(transformed.context, event.context); + transferFunc(transformed.context.group, event.traits); + transferValueFunc(transformed.context, "group_id", event.groupId); + transferFunc(transformed, event.properties); + transferFunc(transformed, event, [ + "context", + "properties", + "traits", + "type", + "groupId", + ...JitsuInternalProperties, + ]); + } else { + transformed = { + context: {}, + }; + transferFunc(transformed.context, event.context, ["traits"]); + transferFunc(transformed, event.properties); + transferFunc(transformed, event.traits); + transferFunc(transformed, event, ["context", "properties", "traits", "type", ...JitsuInternalProperties]); + } break; case "track": - if (!singleTable) { - baseTrackFlat = toSnakeCase({ - ...omit(event, ["properties", "type", TableNameParameter]), - }); + if (singleTable) { + transformed = { + context: { + traits: {}, + }, + }; + transferFunc(transformed.context, event.context, ["groupId", "traits"]); + transferFunc(transformed.context.traits, event.context?.traits, ["groupId"]); + transferFunc(transformed.context.traits, event.properties?.traits, ["groupId"]); + transferValueFunc(transformed.context, "group_id", event.context?.groupId || event.context?.traits?.groupId); + transferFunc(transformed, event.properties, ["traits"]); + transferFunc(transformed, event, ["context", "properties", "type", ...JitsuInternalProperties]); + } else { + baseTrackFlat = {}; + transferFunc(baseTrackFlat, event, ["properties", "type", ...JitsuInternalProperties]); + transformed = {}; + transferFunc(transformed, event.properties); + transferFunc(transformed, event, ["properties", "type", ...JitsuInternalProperties]); } - transformed = { - ...(event.properties || {}), - ...omit(event, ["properties", "type", TableNameParameter]), - }; break; default: - transformed = { - ...(event.properties || {}), - ...omit(event, ["properties", TableNameParameter]), - }; + if (singleTable) { + transformed = { + context: { + traits: {}, + }, + }; + transferFunc(transformed.context, event.context, ["groupId", "traits"]); + transferFunc(transformed.context.traits, event.context?.traits, ["groupId"]); + transferValueFunc(transformed.context, "group_id", event.context?.groupId || event.context?.traits?.groupId); + transferFunc(transformed, event.properties); + transferFunc(transformed, event, ["context", "properties", ...JitsuInternalProperties]); + } else { + transformed = {}; + transferFunc(transformed, event.properties); + transferFunc(transformed, event, ["properties", ...JitsuInternalProperties]); + } } - const flat: Record = toSnakeCase(transformed); if (event[TableNameParameter]) { - flat.type = event.type; - return { event: flat, table: event[TableNameParameter] }; + transformed.type = event.type; + return { event: transformed, table: event[TableNameParameter] }; } if (singleTable) { - flat.type = event.type; - return { event: flat, table: "events" }; + transformed.type = event.type; + return { event: transformed, table: "events" }; } else { if (event.type === "track" && event.event) { return [ { event: baseTrackFlat, table: "tracks" }, - { event: flat, table: event.event }, + { event: transformed, table: event.event }, ]; } else { - return { event: flat, table: plural(event.type) }; + return { event: transformed, table: plural(event.type) }; } } } @@ -173,9 +187,10 @@ export function plural(s: string) { } export const dataLayouts: Record> = { - segment: event => segmentLayout(event, false), - "segment-single-table": event => segmentLayout(event, true), - "jitsu-legacy": event => jitsuLegacy(event), + segment: (event, ctx) => segmentLayout(event, false, ctx), + "segment-single-table": (event, ctx) => segmentLayout(event, true, ctx), + "jitsu-legacy": jitsuLegacy, + passthrough: event => ({ event: omit(event, JitsuInternalProperties), table: event[TableNameParameter] ?? "events" }), }; export type BulkerDestinationConfig = { @@ -183,24 +198,70 @@ export type BulkerDestinationConfig = { destinationId: string; authToken: string; dataLayout?: DataLayoutType; + keepOriginalNames?: boolean; + streamOptions?: any; }; const BulkerDestination: JitsuFunction = async (event, ctx) => { - const { bulkerEndpoint, destinationId, authToken, dataLayout = "segment-single-table" } = ctx.props; - const events = dataLayouts[dataLayout](event); - - for (const { event, table } of Array.isArray(events) ? events : [events]) { - await ctx.fetch( - `${bulkerEndpoint}/post/${destinationId}?tableName=${table}`, - { - method: "POST", - headers: { Authorization: `Bearer ${authToken}` }, - body: JSON.stringify(event), - }, - false - ); + const { bulkerEndpoint, destinationId, authToken, dataLayout = "segment-single-table", streamOptions } = ctx.props; + try { + const metricsMeta: Omit = { + workspaceId: ctx.workspace.id, + streamId: ctx.source.id, + destinationId: ctx.destination.id, + connectionId: ctx.connection.id, + functionId: "builtin.destination.bulker", + }; + let adjustedEvent = event; + const clientIds = event.context?.clientIds; + const ga4 = clientIds?.ga4; + if (ga4) { + if (ga4.sessionIds) { + ga4.sessionIds = JSON.stringify(ga4.sessionIds); + } else { + const oldSessions = ga4["sessions"]; + if (oldSessions) { + ga4.sessionIds = JSON.stringify(oldSessions); + delete ga4["sessions"]; + } + } + } + const events = dataLayouts[dataLayout](adjustedEvent, ctx); + for (const { event, table } of Array.isArray(events) ? events : [events]) { + const payload = JSON.stringify(event); + if (payload.length > 1000000) { + throw new Error( + `Max allowed size is 1 000 000 bytes. Event size is: ${payload.length} bytes: \n${payload.substring( + 0, + 256 + )}...` + ); + } + const headers = { Authorization: `Bearer ${authToken}`, metricsMeta: JSON.stringify(metricsMeta) }; + if (streamOptions && Object.keys(streamOptions).length > 0) { + headers["streamOptions"] = JSON.stringify(streamOptions); + } + const res = await request( + `${bulkerEndpoint}/post/${destinationId}?tableName=${table}${bulkerPartitionParam(ctx, event)}`, + { + method: "POST", + headers, + body: payload, + bodyTimeout: fetchTimeoutMs, + headersTimeout: fetchTimeoutMs, + dispatcher: undiciAgent, + } + ); + if (res.statusCode != 200) { + throw new HTTPError(`HTTP Error: ${res.statusCode}`, res.statusCode, await res.body.text()); + } else { + ctx.log.debug(`HTTP Status: ${res.statusCode} Response: ${await res.body.text()}`); + } + } + return event; + } catch (e: any) { + throw new RetryError(e); } - return event; }; BulkerDestination.displayName = "Bulker Destination"; diff --git a/libs/core-functions/src/functions/facebook-conversions.ts b/libs/core-functions/src/functions/facebook-conversions.ts new file mode 100644 index 000000000..4b910632a --- /dev/null +++ b/libs/core-functions/src/functions/facebook-conversions.ts @@ -0,0 +1,161 @@ +import { JitsuFunction } from "@jitsu/protocols/functions"; +import { AnalyticsServerEvent, ID } from "@jitsu/protocols/analytics"; +import { FacebookConversionApiCredentials } from "../meta"; + +import crypto from "crypto"; +import omit from "lodash/omit"; +import { RetryError } from "@jitsu/functions-lib"; +import { createFilter, eventTimeSafeMs } from "./lib"; +import { deepMerge } from "juava"; + +export function facebookHash(input: string | undefined) { + if (!input) { + return undefined; + } + return crypto.createHash("sha256").update(input.trim().toLowerCase()).digest("hex"); +} + +function reduceArray(strings: ID[]): ID[] | ID { + return strings.length === 1 ? strings[0] : strings; +} + +function sanitizePhone(ph: string) { + let sanitizedPhone = ph.replace(/[^\d]/g, ""); + sanitizedPhone = sanitizedPhone.replace(/^0+/, ""); + return sanitizedPhone; +} + +function tryParse(responseText: string) { + try { + return JSON.parse(responseText); + } catch (e) { + return responseText; + } +} + +function toPrettyString(responseJson: any) { + return typeof responseJson === "string" ? responseJson : JSON.stringify(responseJson, null, 2); +} + +/** + * See https://developers.facebook.com/docs/marketing-api/conversions-api/using-the-api + * and https://developers.facebook.com/docs/marketing-api/conversions-api/parameters + */ +const FacebookConversionsApi: JitsuFunction = async ( + event, + ctx +) => { + if (["track", "page", "screen"].includes(event.type)) { + const actionSource = ctx.props?.actionSource || "website"; + const analyticsContext = event.context || ({} as any); + const device = analyticsContext.device || ({} as any); + const app = analyticsContext.app || ({} as any); + const screen = analyticsContext.screen || ({} as any); + const os = (analyticsContext.os?.name ?? "").toLowerCase(); + const filter = createFilter(ctx.props.events || ""); + if (!filter(event.type, event.event)) return; + const geo = ctx.geo; + let geoUserData = {}; + if (geo) { + geoUserData = { + ct: facebookHash(geo.city?.name), + st: facebookHash(geo.region?.code), + country: facebookHash(geo.country?.code), + zp: facebookHash(geo.postalCode?.code), + }; + } + + const baseProps = { + event_name: event.type === "track" ? event.event : event.type, + event_time: Math.floor(eventTimeSafeMs(event) / 1000), + event_id: event.messageId, + action_source: actionSource, + event_source_url: event.context?.page?.url, + user_data: { + em: event.context.traits?.email ? facebookHash(event.context.traits.email as string) : undefined, + ph: + ctx.props?.phoneFieldName && event.context.traits?.[ctx.props.phoneFieldName] + ? facebookHash(sanitizePhone(String(event.context.traits[ctx.props.phoneFieldName]))) + : undefined, + external_id: reduceArray([event.userId, event.anonymousId].filter(e => !!e)), + client_ip_address: event.context.ip, + client_user_agent: event.context.userAgent, + fbc: event.context.clientIds?.fbc, + fbp: event.context.clientIds?.fbp, + ...geoUserData, + }, + app_data: + actionSource === "app" + ? { + advertiser_tracking_enabled: 0, + application_tracking_enabled: 0, + extinfo: [ + os === "ios" || os === "macos" ? "i2" : "a2", + app.namespace ?? "", + app.version ?? "", + app.version ?? "", + analyticsContext.os?.version || ctx.ua?.os?.version || "1.0", + device.model ?? "", + analyticsContext.locale ?? "", + "", + "", + screen.width ? screen.width.toString() : "", + screen.height ? screen.height.toString() : "", + screen.density ? screen.density.toString() : "", + "", + "", + "", + analyticsContext.timezone ?? "", + ], + } + : undefined, + }; + + const fbEvent = + typeof event.facebookEvent === "object" && !Array.isArray(event.facebookEvent) + ? deepMerge(baseProps, event.facebookEvent) + : { + ...baseProps, + custom_data: omit(event.properties, [ + "path", + "referrer", + "host", + "referring_domain", + "search", + "title", + "url", + "hash", + "height", + "width", + ]), + }; + + const baseUrl = `https://graph.facebook.com/v23.0/${ctx.props.pixelId}/events?access_token=`; + const payload = { data: [fbEvent] }; + const fetchResult = await ctx.fetch(`${baseUrl}${ctx.props.accessToken}`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(payload), + }); + const responseText = await fetchResult.text(); + const responseJson = tryParse(responseText); + ctx.log.debug( + `Facebook API - ${baseUrl}**** ---------> ${fetchResult.status} ${fetchResult.statusText}:\n${toPrettyString( + responseJson + )}` + ); + if (!fetchResult.ok) { + throw new RetryError( + `Facebook API error. Called: ${baseUrl}****, got ${fetchResult.status} ${fetchResult.statusText} - ${responseText}` + ); + } + } +}; + +FacebookConversionsApi.displayName = "facebook-conversion-api"; + +FacebookConversionsApi.description = "Send events to facebook conversion API"; + +export default FacebookConversionsApi; diff --git a/libs/core-functions/src/functions/ga4-destination.ts b/libs/core-functions/src/functions/ga4-destination.ts index c9259fcad..d0b2c0c02 100644 --- a/libs/core-functions/src/functions/ga4-destination.ts +++ b/libs/core-functions/src/functions/ga4-destination.ts @@ -1,6 +1,8 @@ import { JitsuFunction } from "@jitsu/protocols/functions"; +import { RetryError } from "@jitsu/functions-lib"; import type { AnalyticsServerEvent } from "@jitsu/protocols/analytics"; import { Ga4Credentials } from "../meta"; +import { createFilter, eventTimeSafeMs } from "./lib"; const ReservedUserProperties = [ "first_open_time", @@ -20,7 +22,9 @@ function removeProperties(properties: Record, toRemove: string[]): } type Ga4Request = { - client_id: string; + app_instance_id?: string; + client_id?: string; + user_id?: string; timestamp_micros: number; user_properties?: Record; events: Ga4Event[]; @@ -116,46 +120,74 @@ function getUserProperties(event: AnalyticsServerEvent): Record { // if (prop.value == undefined) delete userProperties[key]; // } - // for (const [key, value] of Object.entries(event.context?.traits || {})) { - // if ( - // !ReservedUserProperties.includes(key) && - // !key.startsWith("google_") && - // !key.startsWith("ga_") && - // !key.startsWith("firebase_") - // ) { - // userProperties[key] = { value }; - // } - // } + for (const [key, value] of Object.entries((event.type == "identify" ? event.traits : event.context?.traits) || {})) { + if ( + !ReservedUserProperties.includes(key) && + !key.startsWith("google_") && + !key.startsWith("ga_") && + !key.startsWith("firebase_") + ) { + userProperties[key] = { value }; + } + } return userProperties; } -function getClientId(event: AnalyticsServerEvent): string { - return event.userId || event.anonymousId || ""; +function getClientId(event: AnalyticsServerEvent): string | undefined { + return event.context?.clientIds?.ga4?.clientId || event.anonymousId || undefined; +} + +function getFirebaseAppInstanceId(event: AnalyticsServerEvent): string | undefined { + return event.context?.clientIds?.firebase?.appInstanceId; +} + +function getSessionId(event: AnalyticsServerEvent, measurementId: string): string | undefined { + return event.context?.clientIds?.ga4?.sessionIds?.[measurementId.replace("G-", "")]; } function pageViewEvent(event: AnalyticsServerEvent): Ga4Event { - const customProperties = { + const pageProperties = { ...(event.context?.page || {}), - ...(event.traits || {}), - ...(event.context?.traits || {}), ...(event.properties || {}), - userAgent: event.context?.userAgent, }; return { name: "page_view", params: { - page_location: customProperties.url || "", - page_referrer: customProperties.referrer || "", - page_title: customProperties.title || "", + page_location: pageProperties.url || "", + page_referrer: pageProperties.referrer || "", + page_title: pageProperties.title || "", engagement_time_msec: 1, }, }; } +function adjustName(name: string): string { + name.replace(/[^a-zA-Z0-9_]/g, "_"); + return name.substring(0, 40); +} + +// `0`is falsy so we need to explicitly check for it +function resolvePossibleValue(...possibleValues: any[]): any { + for (const value of possibleValues) { + if (typeof value !== "undefined" && value !== null) { + return value; + } + } + + return possibleValues.at(-1); +} + +// typically values are resolved in the following order, however there are +// a few exceptions. +function resolveStandardValue(evp: any) { + return resolvePossibleValue(evp?.value, evp?.total, evp?.revenue); +} + function trackEvent(event: AnalyticsServerEvent): Ga4Event { const evp = event.properties || {}; let params: Record = {}; let name; + const eventName = event.event || event.name || event.type; switch (event.name) { case "Promotion Clicked": name = "select_promotion"; @@ -175,7 +207,7 @@ function trackEvent(event: AnalyticsServerEvent): Ga4Event { case "Checkout Started": name = "begin_checkout"; params.currency = evp.currency; - params.value = evp.value || evp.total || evp.revenue; + params.value = resolveStandardValue(evp); params.coupon = evp.coupon; params.items = getItems(event); break; @@ -183,7 +215,7 @@ function trackEvent(event: AnalyticsServerEvent): Ga4Event { name = "refund"; params.currency = evp.currency; params.transaction_id = evp.order_id; - params.value = evp.total || evp.value || evp.revenue; + params.value = resolvePossibleValue(evp.total, evp.value, evp.revenue); params.coupon = evp.coupon; params.shipping = evp.shipping; params.affiliation = evp.affiliation; @@ -193,13 +225,13 @@ function trackEvent(event: AnalyticsServerEvent): Ga4Event { case "Product Added": name = "add_to_cart"; params.currency = evp.currency; - params.value = evp.value || evp.total || evp.revenue; + params.value = resolveStandardValue(evp); params.items = getItems(event); break; case "Payment Info Entered": name = "add_payment_info"; params.currency = evp.currency; - params.value = evp.value || evp.total || evp.revenue; + params.value = resolveStandardValue(evp); params.coupon = evp.coupon; params.payment_type = evp.payment_method; params.items = getItems(event); @@ -207,13 +239,13 @@ function trackEvent(event: AnalyticsServerEvent): Ga4Event { case "Product Added to Wishlist": name = "add_to_wishlist"; params.currency = evp.currency; - params.value = evp.value || evp.total || evp.revenue; + params.value = resolveStandardValue(evp); params.items = getItems(event); break; case "Product Viewed": name = "view_item"; params.currency = evp.currency; - params.value = evp.value || evp.total || evp.revenue; + params.value = resolveStandardValue(evp); params.items = getItems(event); break; case "Signed Up": @@ -224,7 +256,7 @@ function trackEvent(event: AnalyticsServerEvent): Ga4Event { name = "purchase"; params.currency = evp.currency; params.transaction_id = evp.order_id; - params.value = evp.total || evp.value || evp.revenue; + params.value = resolvePossibleValue(evp.total, evp.value, evp.revenue); params.coupon = evp.coupon; params.shipping = evp.shipping; params.affiliation = evp.affiliation; @@ -243,7 +275,7 @@ function trackEvent(event: AnalyticsServerEvent): Ga4Event { case "Cart Viewed": name = "view_cart"; params.currency = evp.currency; - params.value = evp.value || evp.total || evp.revenue; + params.value = resolveStandardValue(evp); params.items = getItems(event); break; case "Signed In": @@ -253,7 +285,7 @@ function trackEvent(event: AnalyticsServerEvent): Ga4Event { case "Product Removed": name = "remove_from_cart"; params.currency = evp.currency; - params.value = evp.value || evp.total || evp.revenue; + params.value = resolveStandardValue(evp); params.items = getItems(event); break; case "Products Searched": @@ -267,11 +299,11 @@ function trackEvent(event: AnalyticsServerEvent): Ga4Event { params.items = getItems(event); break; default: - name = event.event || event.name; + name = adjustName(eventName); params = { ...evp }; params = removeProperties(params, StandardProperties); params.currency = evp.currency; - params.value = evp.value || evp.total || evp.revenue; + params.value = resolveStandardValue(evp); break; } params.engagement_time_msec = 1; @@ -282,13 +314,39 @@ function trackEvent(event: AnalyticsServerEvent): Ga4Event { } const Ga4Destination: JitsuFunction = async (event, ctx) => { + if (typeof ctx.props.events !== "undefined") { + const filter = createFilter(ctx.props.events || ""); + if (!filter(event.type, event.event)) { + return; + } + } let gaRequest: Ga4Request | undefined = undefined; try { const clientId = getClientId(event); - if (!clientId) { - ctx.log.info(`Ga4: no client_id found for event ID: ${event.messageId}`); - return; + const sessionId = getSessionId(event, ctx.props.measurementId); + const firebaseAppInstanceId = getFirebaseAppInstanceId(event); + const measurementId = ctx.props.measurementId || ""; + let query = `api_secret=${ctx.props.apiSecret}`; + let idPart = {} as any; + if (measurementId.match(/^\d:\d+:\w+:\w+$/)) { + if (!firebaseAppInstanceId) { + ctx.log.info(`Ga4: no app instance id found for event ID: ${event.messageId}`); + return; + } + idPart.app_instance_id = firebaseAppInstanceId; + query += `&firebase_app_id=${measurementId}`; + } else { + if (!clientId) { + ctx.log.info(`Ga4: no client_id found for event ID: ${event.messageId}`); + return; + } + if (!measurementId.match(/^G-\w+$/)) { + ctx.log.warn(`Ga4: measurement_id is not in the correct format: ${measurementId}`); + } + idPart.client_id = clientId; + query += `&measurement_id=${measurementId}`; } + const userProperties = getUserProperties(event); const events: Ga4Event[] = []; @@ -306,15 +364,17 @@ const Ga4Destination: JitsuFunction = asyn ctx.log.info(`Ga4: no GA4 event is mapped for event type: ${event.type} ID: ${event.messageId}`); return; } - const debug = ""; - //const debug = ctx.props.validationMode ? "/debug" : ""; - const url = `https://www.google-analytics.com${debug}/mp/collect?measurement_id=${ctx.props.measurementId}&api_secret=${ctx.props.apiSecret}`; + + const baseUrl = ctx.props.url ?? "https://www.google-analytics.com/mp/collect"; + + const url = `${baseUrl}?${query}`; gaRequest = { - client_id: clientId, - timestamp_micros: new Date(event.timestamp as string).getTime() * 1000, + ...idPart, + user_id: event.userId, + timestamp_micros: eventTimeSafeMs(event) * 1000, user_properties: userProperties, - events: events, + events: sessionId ? events.map(e => ({ ...e, params: { ...e.params, session_id: sessionId } })) : events, }; const result = await ctx.fetch(url, { @@ -326,12 +386,12 @@ const Ga4Destination: JitsuFunction = asyn // ctx.log.info(`Ga4:${JSON.stringify(gaRequest)} --> ${result.status}: ${await result.text()}`); // } else if (result.status !== 200 && result.status !== 204) { - ctx.log.error(`Ga4:${JSON.stringify(gaRequest)} --> ${result.status} ${await result.text()}`); + throw new Error(`Ga4:${JSON.stringify(gaRequest)} --> ${result.status} ${await result.text()}`); } else { - ctx.log.debug(`Ga4:${JSON.stringify(gaRequest)} --> ${result.status}`); + ctx.log.debug(`Ga4: ${result.status} ${await result.text()}`); } } catch (e: any) { - throw new Error(`Failed to send request to Ga4: ${JSON.stringify(gaRequest)}: ${e?.message}`); + throw new RetryError(`Failed to send request to Ga4: ${JSON.stringify(gaRequest)}: ${e?.message}`); } }; diff --git a/libs/core-functions/src/functions/hubspot-destination.ts b/libs/core-functions/src/functions/hubspot-destination.ts new file mode 100644 index 000000000..7cf553c65 --- /dev/null +++ b/libs/core-functions/src/functions/hubspot-destination.ts @@ -0,0 +1,308 @@ +import type { FullContext, FunctionLogger, JitsuFunction } from "@jitsu/protocols/functions"; +import { AnalyticsServerEvent } from "@jitsu/protocols/analytics"; +import { HubspotCredentials } from "../meta"; +import { Client } from "@hubspot/api-client"; +import { + PropertyCreateFieldTypeEnum, + PropertyCreateTypeEnum, +} from "@hubspot/api-client/lib/codegen/crm/properties/models/PropertyCreate"; +import { FilterOperatorEnum } from "@hubspot/api-client/lib/codegen/crm/contacts/models/Filter"; +import { FilterGroup } from "@hubspot/api-client/lib/codegen/crm/contacts/models/FilterGroup"; +import omit from "lodash/omit"; +import assert from "node:assert"; +import { BehavioralEventHttpCompletionRequest } from "@hubspot/api-client/lib/codegen/events/send/models/BehavioralEventHttpCompletionRequest"; +import { idToSnakeCaseFast } from "@jitsu/functions-lib"; + +const JITSU_USER_ID_PROPERTY = "jitsu_user_id"; + +const JITSU_GROUP_ID_PROPERTY = "jitsu_group_id"; + +function splitName(name?: string): [string | undefined, string | undefined] { + if (!name) { + return [undefined, undefined]; + } + const [firstName, ...rest] = name.split(" "); + return [firstName, rest.join(" ") || undefined]; +} + +type PropertyOptions = { group?: string; objectType?: string }; + +class HubspotHelper { + private client: Client; + private log: FunctionLogger; + + constructor(client: Client, ctx: FullContext) { + this.client = client; + this.log = ctx.log; + } + + async checkIfPropertyExists(propertyName: string, opts: PropertyOptions = {}): Promise { + const properties = await this.client.crm.properties.coreApi.getAll(opts.objectType || "contacts"); + return properties.results.some(property => property.name === propertyName); + } + + async createProperty(propertyName: string, opts: PropertyOptions = {}): Promise { + await this.client.crm.properties.coreApi.create(opts.objectType || "contacts", { + name: propertyName, + label: propertyName, + type: PropertyCreateTypeEnum.String, + fieldType: PropertyCreateFieldTypeEnum.Text, + groupName: opts.group || "contactinformation", + description: `Custom property, created by Jitsu Integration`, + }); + this.log.info(`Property '${propertyName}' created successfully.`); + } + + async ensurePropertyExists(propertyName: string, opts: PropertyOptions = {}): Promise { + const exists = await this.checkIfPropertyExists(propertyName, opts); + if (!exists) { + this.log.info(`Property '${propertyName}' does not exist. Creating...`); + await this.createProperty(propertyName, opts); + } else { + //this.log.debug(`Property '${propertyName}' already exists`); + } + } + + async removeUnknownProperties( + customProps: Record, + opts: PropertyOptions = {} + ): Promise> { + const remoteProps = await this.client.crm.properties.coreApi.getAll(opts.objectType || "contacts"); + return Object.fromEntries( + Object.entries(customProps) + .map(([k, v]) => [idToSnakeCaseFast(k), v]) + .filter(([key]) => remoteProps.results.some(rp => rp.name === key)) + ); + } + + async ensurePropertiesExist( + customProps: Record, + opts: PropertyOptions = {} + ): Promise> { + const remoteProps = await this.client.crm.properties.coreApi.getAll(opts.objectType || "contacts"); + customProps = Object.fromEntries(Object.entries(customProps).map(([k, v]) => [idToSnakeCaseFast(k), v])); + const notExistis = Object.keys(customProps).filter(p => !remoteProps.results.some(rp => rp.name === p)); + for (const propertyName of notExistis) { + this.log.info(`Property '${propertyName}' does not exist. Creating...`); + await this.createProperty(propertyName, opts); + } + return customProps; + } + + async upsertHubspotCompany( + cred: HubspotCredentials, + c: { + companyId: string; + name: string; + customProps?: Record; + doNotUpdate?: boolean; + } + ): Promise { + const filterGroup: FilterGroup = { + filters: [ + { + propertyName: JITSU_GROUP_ID_PROPERTY, + operator: FilterOperatorEnum.Eq, + value: c.companyId, + }, + ], + }; + const searchResults = await this.client.crm.companies.searchApi.doSearch({ + filterGroups: [filterGroup], + limit: 10, + after: "0", + properties: [], + sorts: [], + }); + let customProps = c.customProps; + if (customProps && Object.keys(customProps).length > 0) { + if (cred.autoCreateCustomProperties) { + customProps = await this.ensurePropertiesExist(customProps, { + objectType: "company", + group: "companyinformation", + }); + } else { + customProps = await this.removeUnknownProperties(customProps, { + objectType: "company", + group: "companyinformation", + }); + } + } + const companyProperties = { + properties: { + name: c.name, + [JITSU_GROUP_ID_PROPERTY]: c.companyId, + ...customProps, + }, + }; + + if (searchResults.results.length > 0) { + // Contact exists, update it + const companyId = searchResults.results[0].id; + if (!c.doNotUpdate) { + await this.client.crm.companies.basicApi.update(companyId, companyProperties); + } + return companyId; + } else { + // Contact does not exist, create it + const { id: companyId } = await this.client.crm.companies.basicApi.create({ + ...companyProperties, + associations: [], + }); + return companyId; + } + } + + async upsertHubspotContact( + cred: HubspotCredentials, + u: { + userId: string; + name?: string; + email: string; + customProps?: Record; + } + ): Promise { + const existingContactId = await this.getContactByJitsuId(u.userId, u.email); + const [fistName, lastName] = splitName(u.name); + let customProps = u.customProps; + if (customProps && Object.keys(customProps).length > 0) { + if (cred.autoCreateCustomProperties) { + customProps = await this.ensurePropertiesExist(customProps, { objectType: "contacts" }); + } else { + customProps = await this.removeUnknownProperties(customProps, { objectType: "contacts" }); + } + } + const contactProperties = { + properties: { + email: u.email, + firstname: fistName, + lastname: lastName, + [JITSU_USER_ID_PROPERTY]: u.userId, + ...customProps, + } as { [key: string]: string }, + }; + + if (existingContactId) { + // Contact exists, update it + await this.client.crm.contacts.basicApi.update(existingContactId, contactProperties); + return existingContactId; + } else { + // Contact does not exist, create it + const { id: contactId } = await this.client.crm.contacts.basicApi.create({ + ...contactProperties, + associations: [], + }); + return contactId; + } + } + + async associateContactWithCompany(contactId: string, companyId: string): Promise { + await this.client.crm.associations.batchApi.create("contacts", "companies", { + inputs: [ + { + _from: { + id: contactId, + }, + to: { + id: companyId, + }, + // The type of association, 2 is the ID for Contact to Company association + type: "2", + }, + ], + }); + } + + async searchContactByField(fieldName: string, fieldValue: string): Promise { + const filterGroup: FilterGroup = { + filters: [ + { + propertyName: fieldName, + operator: FilterOperatorEnum.Eq, + value: fieldValue, + }, + ], + }; + const searchResults = await this.client.crm.contacts.searchApi.doSearch({ + filterGroups: [filterGroup], + limit: 10, + after: "0", + properties: [], + sorts: [], + }); + + return searchResults?.results?.[0]?.id; + } + + public async getContactByJitsuId(jitsuUserId: string, email?: string): Promise { + return ( + (await this.searchContactByField(JITSU_USER_ID_PROPERTY, jitsuUserId)) || + (email ? await this.searchContactByField("email", email) : undefined) + ); + } +} + +const HubspotDestination: JitsuFunction = async (event, ctx) => { + assert(ctx.props.accessToken); + const hubspotClient = new Client({ accessToken: ctx.props.accessToken }); + const helper = new HubspotHelper(hubspotClient, ctx); + await helper.ensurePropertyExists(JITSU_USER_ID_PROPERTY); + await helper.ensurePropertyExists(JITSU_GROUP_ID_PROPERTY, { + objectType: "company", + group: "companyinformation", + }); + hubspotClient.init(); + let contactId: string | undefined = undefined; + let companyId: string | undefined = undefined; + if (event.type === "identify" && event.userId && event.traits?.email) { + contactId = await helper.upsertHubspotContact(ctx.props, { + userId: event.userId, + name: event.traits.name as string | undefined, + email: event.traits.email as string, + customProps: omit(event.traits, "name", "email"), + }); + if (event.groupId) { + companyId = await helper.upsertHubspotCompany(ctx.props, { + companyId: event.groupId, + name: `Company ${event.groupId}`, + doNotUpdate: true, + }); + } + } + if (event.type === "group" && event.groupId) { + const groupName = event.type === "group" ? event.traits?.name : undefined; + + await helper.upsertHubspotCompany(ctx.props, { + companyId: event.groupId, + name: (groupName || `Company ${event.groupId}`) as string, + customProps: omit(event.traits, "name"), + }); + if (event.userId) { + contactId = await helper.getContactByJitsuId(event.userId); + } + } + if (contactId && companyId) { + await helper.associateContactWithCompany(contactId, companyId); + } + const email = (event.traits?.email || event.properties?.email || undefined) as string | undefined; + + if (email && ctx.props.sendPageViewEvents) { + const url = event.context?.page?.url || event.properties?.url; + const properties: Record = {}; + if (url) { + properties.url = url.toString(); + } + const hubspotEvent: BehavioralEventHttpCompletionRequest = { + email: email, + eventName: event.type === "track" ? event.event ?? "track" : event.type, + objectId: contactId, + occurredAt: event.timestamp ? new Date(event.timestamp) : new Date(), + properties: properties, + uuid: event.messageId, + }; + await hubspotClient.events.send.behavioralEventsTrackingApi.send(hubspotEvent); + } +}; + +export default HubspotDestination; +export { HubspotDestination }; diff --git a/libs/core-functions/src/functions/intercom-destination.ts b/libs/core-functions/src/functions/intercom-destination.ts new file mode 100644 index 000000000..93c9f32e8 --- /dev/null +++ b/libs/core-functions/src/functions/intercom-destination.ts @@ -0,0 +1,405 @@ +import { FullContext, JitsuFunction } from "@jitsu/protocols/functions"; +import { AnalyticsServerEvent } from "@jitsu/protocols/analytics"; +import { IntercomDestinationCredentials } from "../meta"; +import { JsonFetcher, jsonFetcher } from "./lib/json-fetch"; +import { isEqual, pick } from "lodash"; +import { requireDefined } from "juava"; +import { RetryError } from "@jitsu/functions-lib"; +import { getPageOrScreenProps } from "./lib"; + +type ExtendedCtx = FullContext & { + jsonFetch: JsonFetcher; +}; + +const alwaysUpdate = true; + +function nullsToUndefined(obj: any) { + return Object.entries(obj).reduce((acc, [key, value]) => ({ ...acc, [key]: value === null ? undefined : value }), {}); +} + +export type IntercomCompany = { + id: string; + // company_id?: string; + [key: string]: any; +}; + +export type IntercomContact = { + id: string; + custom_attributes?: Record; + // company_id?: string; +}; + +async function getContactByOurUserId( + userId: string, + { jsonFetch, props, log }: ExtendedCtx +): Promise { + const requestBody = { + query: { + field: "external_id", + operator: "=", + value: userId, + }, + }; + //log.info(`Intercom: searching for contact by external_id ${userId}, ${JSON.stringify(requestBody, null, 2)})}`); + const response = await jsonFetch(`https://api.intercom.io/contacts/search`, { + headers: { + Authorization: `Bearer ${props.accessToken}`, + Accept: "application/json", + "Content-Type": "application/json", + }, + body: requestBody, + }); + if (response.data.length === 0) { + return undefined; + } + return response.data[0]; +} + +async function getCompanyByGroupId( + groupId: string, + { jsonFetch, props, fetch }: ExtendedCtx +): Promise { + const response = await fetch(`https://api.intercom.io/companies?company_id=${groupId}`, { + headers: { + Authorization: `Bearer ${props.accessToken}`, + Accept: "application/json", + "Content-Type": "application/json", + }, + }); + if (response.status === 404) { + return undefined; + } else if (response.ok) { + return await response.json(); + } else { + throw new Error( + `Intercom: failed to get company by groupId=${groupId}, status ${response.status} ${response.statusText}` + ); + } +} + +async function createOrUpdateCompany(event: AnalyticsServerEvent, ctx: ExtendedCtx) { + const { jsonFetch, log, props } = ctx; + const existingCompany = await getCompanyByGroupId(requireDefined(event.groupId, `Group event has no groupId`), ctx); + //log.debug(`Intercom: search for company ${event.groupId} returned ${JSON.stringify(existingCompany, null, 2)}`); + + const newCompany = { + company_id: event.groupId, + name: event.traits?.name || undefined, + custom_attributes: {}, // omit(event.traits || {}, "name"), + }; + + if (!existingCompany) { + log.debug(`Company ${event.groupId} not found, creating a new one:\n${JSON.stringify(newCompany, null, 2)}`); + const createCompanyResponse = await jsonFetch(`https://api.intercom.io/companies`, { + headers: { + Authorization: `Bearer ${props.accessToken}`, + Accept: "application/json", + "Content-Type": "application/json", + }, + body: newCompany, + }); + return createCompanyResponse.id; + } else { + const forComparison = { + ...nullsToUndefined(pick(existingCompany, "name")), + custom_attributes: {}, // existingCompany.custom_attributes || {}, + }; + if (isEqual(forComparison, newCompany) && !alwaysUpdate) { + log.debug(`Company ${event.groupId} already exists and is up to date, skipping`); + return; + } else { + log.debug(`Company ${event.groupId} needs to be updated, updating ${JSON.stringify(existingCompany, null, 2)}`); + await jsonFetch(`https://api.intercom.io/companies/${existingCompany.id}`, { + method: "PUT", + headers: { + Authorization: `Bearer ${props.accessToken}`, + Accept: "application/json", + "Content-Type": "application/json", + }, + body: newCompany, + }); + } + return existingCompany.id; + } +} + +function toDate(timestamp?: string | number | Date): Date { + if (!timestamp) { + return new Date(); + } + if (typeof timestamp === "string") { + return new Date(timestamp); + } else if (typeof timestamp === "number") { + return new Date(timestamp); + } else { + return timestamp; + } +} + +async function getContactByExternalIdOrEmail( + { email, externalId }: { email: string; externalId?: string }, + { jsonFetch, props, log }: ExtendedCtx +): Promise { + const result = await jsonFetch(`https://api.intercom.io/contacts/search`, { + headers: { + Authorization: `Bearer ${props.accessToken}`, + Accept: "application/json", + "Content-Type": "application/json", + }, + body: { + query: { + operator: "OR", + value: [ + { + field: "email", + operator: "=", + value: email, + }, + ...(externalId + ? [ + { + field: "external_id", + operator: "=", + value: externalId, + }, + ] + : []), + ], + }, + }, + }); + log.debug( + `Intercom: search for contact by email=${email} and external_id=${externalId} returned ${result.data.length} contacts` + ); + if (result.data.length > 1) { + log.warn( + `Intercom: search for contact by email=${email} and external_id=${externalId} returned more than 1 (=${result.data.length}) contacts` + ); + } + return result.data?.[0]; +} + +function extractContactIdFromErrorMessage(body: any): string | undefined { + const message = body?.errors?.[0]?.message; + if (!message) { + return; + } + const idPattern = /id=([a-zA-Z0-9]+)/; + const match = message.match(idPattern); + + if (match && match[1]) { + return match[1]; + } +} + +async function createOrUpdateContact(event: AnalyticsServerEvent, ctx: ExtendedCtx): Promise { + const { jsonFetch, log, props } = ctx; + if (!event.traits?.email) { + log.warn( + `Intercom: ${event.type} with userId=${event.userId} doesn't have email, skipping. Intercom requires email to create a contact` + ); + return; + } + const email = event.traits.email as string; + + const existingContact = await getContactByExternalIdOrEmail({ email, externalId: event.userId }, ctx); + + const contactData = { + role: "user", + external_id: event.userId || undefined, + email, + last_seen_at: toDate(event.timestamp).toISOString(), + name: + event.traits?.name || + (event.traits?.firstName && event.traits?.lastName + ? `${event.traits.firstName} ${event.traits.lastName}` + : undefined), + phone: event.traits?.phone, + custom_attributes: {}, // omit(event.traits || {}, "name", "firstName", "lastName", "phone", "email"), + }; + if (!existingContact) { + log.debug( + `Contact with email=${email} and userId=${event.userId} not found, creating a new one:\n${JSON.stringify( + contactData, + null, + 2 + )}` + ); + const createContactResponse = await fetch(`https://api.intercom.io/contacts`, { + method: "POST", + headers: { + Authorization: `Bearer ${props.accessToken}`, + Accept: "application/json", + "Content-Type": "application/json", + }, + body: JSON.stringify(contactData), + }); + if (!createContactResponse.ok) { + if (createContactResponse.status === 409) { + const body = await createContactResponse.json(); + log.warn( + `Intercom: contact with email=${email} and userId=${ + event.userId + } already exists, skipping. Request: ${email}, ${JSON.stringify( + contactData, + null, + 2 + )}, response: ${JSON.stringify(body, null, 2)}` + ); + return extractContactIdFromErrorMessage(body); + } else { + const body = await createContactResponse.json(); + const errorMessage = `Intercom: attempt to create a contact email=${email} and userId=${ + event.userId + } failed with ${createContactResponse.status} ${ + createContactResponse.statusText + }. Request: ${email}, ${JSON.stringify(contactData, null, 2)}, response: ${JSON.stringify(body, null, 2)}`; + //log.warn(errorMessage); + throw new RetryError(errorMessage, { drop: false }); + } + } + const newContact = await createContactResponse.json(); + const newContactId = newContact.id; + log.debug( + `Contact with email=${email} and userId=${event.userId} created, id=${newContactId}: ${JSON.stringify( + newContact, + null, + 2 + )}` + ); + return newContactId; + } else { + const contact = existingContact; + const forComparison = { + ...nullsToUndefined(pick(contact, "name", "email", "phone", "role", "external_id")), + custom_attributes: contact.custom_attributes || {}, + } as any; + if (isEqual(forComparison, contactData) && !alwaysUpdate) { + log.debug(`Contact with email=${email} and userId=${event.userId} already exists and is up to date, skipping`); + return; + } else { + log.debug( + `Contact with email=${email} and userId=${event.userId} needs to be updated, updating ${JSON.stringify( + contactData, + null, + 2 + )}` + ); + await jsonFetch(`https://api.intercom.io/contacts/${contact.id}`, { + method: "PUT", + headers: { + Authorization: `Bearer ${props.accessToken}`, + Accept: "application/json", + "Content-Type": "application/json", + }, + body: contactData, + //body: forComparison.external_id ? omit(contactData, "external_id") : contactData, + }); + } + return contact.id; + } +} + +const IntercomDestination: JitsuFunction = async (event, ctx) => { + const jsonFetch = jsonFetcher(ctx.fetch, { log: ctx.log, debug: true }); + let intercomContactId: string | undefined; + let intercomCompanyId: string | undefined; + if (event.type === "identify") { + intercomContactId = await createOrUpdateContact(event, { ...ctx, jsonFetch }); + } else if (event.type === "group") { + intercomCompanyId = await createOrUpdateCompany(event, { ...ctx, jsonFetch }); + } + if ((event.type === "group" || event.type === "identify") && event.groupId && event.userId) { + if (!intercomCompanyId) { + intercomCompanyId = (await getCompanyByGroupId(event.groupId, { ...ctx, jsonFetch }))?.id; + if (!intercomCompanyId) { + ctx.log.warn( + `Intercom company ${event.groupId} not found. It's coming from ${event.type} event. Following .group() call might fix it` + ); + return; + } + } + if (!intercomContactId) { + intercomContactId = (await getContactByOurUserId(event.userId, { ...ctx, jsonFetch }))?.id; + if (!intercomContactId) { + ctx.log.info(`Intercom contact ${event.userId} not found`); + return; + } + } + await jsonFetch(`https://api.intercom.io/contacts/${intercomContactId}/companies`, { + method: "POST", + headers: { + Authorization: `Bearer ${ctx.props.accessToken}`, + Accept: "application/json", + "Content-Type": "application/json", + }, + body: { + id: intercomCompanyId, + }, + }); + } + + if (event.type !== "identify" && event.type !== "group") { + const email = event.context?.traits?.email || event.traits?.email; + const userId = event.userId; + if (!email && !userId) { + //doesn't make sense to send event without email or userId, Intercom won't know how to link it to a user + } + const intercomEvent = { + type: "event", + event_name: event.type === "track" ? event.event : event.type === "page" ? "page-view" : event.type, + created_at: Math.round(toDate(event.timestamp).getTime() / 1000), + user_id: userId || undefined, + email: email || undefined, + metadata: { + ...getPageOrScreenProps(event), + ...event.properties, + url: event.context?.page?.url || undefined, + eventName: event.name, + ip: event.context?.ip, + libraryName: event.context?.library?.name, + libraryVersion: event.context?.library?.version, + timezone: event.context?.timezone, + osName: event.context?.os?.name, + osVersion: event.context?.os?.version, + networkCellular: event.context?.network?.cellular, + networkWifi: event.context?.network?.wifi, + instanceId: event.context?.instanceId, + appBuild: event.context?.app?.build, + appVersion: event.context?.app?.version, + appNamespace: event.context?.app?.namespace, + appName: event.context?.app?.name, + }, + }; + await jsonFetch(`https://api.intercom.io/events`, { + headers: { + Authorization: `Bearer ${ctx.props.accessToken}`, + Accept: "application/json", + "Content-Type": "application/json", + }, + body: intercomEvent, + }); + if (ctx.props.updateLastSeenOnEveryEvent && (email || userId)) { + const contact = await getContactByExternalIdOrEmail( + { email: email as string, externalId: userId }, + { ...ctx, jsonFetch } + ); + if (contact) { + await jsonFetch(`https://api.intercom.io/contacts/${contact.id}`, { + method: "PUT", + headers: { + Authorization: `Bearer ${ctx.props.accessToken}`, + Accept: "application/json", + "Content-Type": "application/json", + }, + body: { + last_seen_at: Math.round(toDate(event.timestamp).getTime() / 1000), + }, + }); + } + } + } +}; + +export default IntercomDestination; +export { IntercomDestination }; diff --git a/libs/core-functions/src/functions/june-destination.ts b/libs/core-functions/src/functions/june-destination.ts index 7e6da3590..99554c0e4 100644 --- a/libs/core-functions/src/functions/june-destination.ts +++ b/libs/core-functions/src/functions/june-destination.ts @@ -1,4 +1,5 @@ import { JitsuFunction } from "@jitsu/protocols/functions"; +import { RetryError } from "@jitsu/functions-lib"; import type { AnalyticsServerEvent } from "@jitsu/protocols/analytics"; import { JuneCredentials } from "../meta"; @@ -31,7 +32,7 @@ function trackEvent(event: AnalyticsServerEvent): any { referrer: event.context?.referrer, referring_domain: event.context?.referring_domain, ...prefix(event.context?.campaign || {}, "campaign_"), - ip: event.request_ip, + ip: event.context?.ip, userAgent: event.context?.userAgent, locale: event.context?.locale, ...prefix(event.context?.screen || {}, "screen_"), @@ -65,57 +66,53 @@ function groupEvent(event: AnalyticsServerEvent): any { } const JuneDestination: JitsuFunction = async (event, ctx) => { - ctx.log.debug(`June destination (props=${JSON.stringify(ctx.props)}) received event ${JSON.stringify(event)}`); - let httpRequest: HttpRequest | undefined = undefined; - const headers = { - "Content-type": "application/json", - Authorization: `Basic ${ctx.props.apiKey}`, - }; - if (event.type === "identify" && event.userId) { - httpRequest = { - url: `https://api.june.so/sdk/identify`, - payload: identifyEvent(event), - headers, - }; - } else if (event.type === "group") { - httpRequest = { - url: `https://api.june.so/sdk/group`, - payload: groupEvent(event), - headers, + try { + let httpRequest: HttpRequest | undefined = undefined; + const headers = { + "Content-type": "application/json", + Authorization: `Basic ${ctx.props.apiKey}`, }; - } else if (event.type === "track" || event.type === "page") { - if (event.userId || ctx.props.enableAnonymousUserProfiles) { + if (event.type === "identify" && event.userId) { httpRequest = { - url: `https://api.june.so/sdk/track`, - payload: trackEvent(event), + url: `https://api.june.so/sdk/identify`, + payload: identifyEvent(event), headers, }; + } else if (event.type === "group") { + httpRequest = { + url: `https://api.june.so/sdk/group`, + payload: groupEvent(event), + headers, + }; + } else if (event.type === "track" || event.type === "page") { + if (event.userId || ctx.props.enableAnonymousUserProfiles) { + httpRequest = { + url: `https://api.june.so/sdk/track`, + payload: trackEvent(event), + headers, + }; + } } - } - if (httpRequest) { - const method = httpRequest.method || "POST"; - try { + if (httpRequest) { + const method = httpRequest.method || "POST"; const result = await ctx.fetch(httpRequest.url, { method, headers: httpRequest.headers, ...(httpRequest.payload ? { body: JSON.stringify(httpRequest.payload) } : {}), }); - const logMessage = `June.so ${method} ${httpRequest.url}:${ - httpRequest.payload ? `${JSON.stringify(httpRequest.payload)} --> ` : "" - }${result.status} ${await result.text()}`; if (result.status !== 200) { - ctx.log.error(logMessage); + throw new Error( + `June.so ${method} ${httpRequest.url}:${ + httpRequest.payload ? `${JSON.stringify(httpRequest.payload)} --> ` : "" + }${result.status} ${await result.text()}` + ); } else { - ctx.log.debug(logMessage); + ctx.log.debug(`June.so ${method} ${httpRequest.url}: ${result.status} ${await result.text()}`); } - } catch (e: any) { - throw new Error( - `Failed to send event to MixPanel: ${method} ${httpRequest.url} ${JSON.stringify(httpRequest.payload)}: ${ - e?.message - }` - ); } + } catch (e: any) { + throw new RetryError(e.message); } }; diff --git a/libs/core-functions/src/functions/lib/browser.ts b/libs/core-functions/src/functions/lib/browser.ts index 8bed7c743..4bbe72491 100644 --- a/libs/core-functions/src/functions/lib/browser.ts +++ b/libs/core-functions/src/functions/lib/browser.ts @@ -1,6 +1,6 @@ type ParsedUserAgent = { name: string; deviceType: string; os: string; browserVersion: number | undefined }; -export function parseUserAgent(userAgent: string, vendor: string | undefined): ParsedUserAgent { +export function parseUserAgentLegacy(userAgent: string, vendor: string | undefined): ParsedUserAgent { return { name: browser(userAgent, vendor), deviceType: device(userAgent), diff --git a/libs/core-functions/src/functions/lib/clickhouse-logger.ts b/libs/core-functions/src/functions/lib/clickhouse-logger.ts new file mode 100644 index 000000000..484443b32 --- /dev/null +++ b/libs/core-functions/src/functions/lib/clickhouse-logger.ts @@ -0,0 +1,86 @@ +import { getLog, isTruish, LogLevel, requireDefined } from "juava"; + +const log = getLog("clickhouseLogger"); + +import { createClient } from "@clickhouse/client"; +import { EventsStore } from "./index"; +import { Readable } from "stream"; + +type LogEntry = [number, string, string, LogLevel, any]; + +function clickhouseHost() { + if (process.env.CLICKHOUSE_URL) { + return process.env.CLICKHOUSE_URL; + } + return `${isTruish(process.env.CLICKHOUSE_SSL) ? "https://" : "http://"}${requireDefined( + process.env.CLICKHOUSE_HOST, + "env CLICKHOUSE_HOST is not defined" + )}`; +} + +export function createClickhouseLogger(): EventsStore { + const buffer: LogEntry[] = []; + const metricsSchema = process.env.CLICKHOUSE_METRICS_SCHEMA || process.env.CLICKHOUSE_DATABASE || "newjitsu_metrics"; + + const clickhouse = createClient({ + url: clickhouseHost(), + username: process.env.CLICKHOUSE_USERNAME || "default", + password: requireDefined(process.env.CLICKHOUSE_PASSWORD, `env CLICKHOUSE_PASSWORD is not defined`), + clickhouse_settings: { + async_insert: 1, + wait_for_async_insert: 0, + async_insert_busy_timeout_ms: 10000, + date_time_input_format: "best_effort", + }, + }); + + const flush = async () => { + if (buffer.length === 0) { + return; + } + const copy = buffer.slice(); + buffer.length = 0; + const eventsStream = new Readable({ objectMode: true }); + const res = clickhouse.insert({ + table: metricsSchema + ".events_log", + format: "JSONCompactEachRow", + values: eventsStream, + }); + const asyncWrite = async () => { + for (let i = 0; i < copy.length; i++) { + eventsStream.push(copy[i]); + } + eventsStream.push(null); + return res; + }; + return asyncWrite() + .then(res => { + if (res.executed) { + log.atDebug().log(`Inserted ${copy.length} records.`); + } else { + log.atError().log(`Failed to insert ${copy.length} records: ${JSON.stringify(res)}`); + } + }) + .catch(e => { + log.atError().withCause(e).log(`Failed to insert ${copy.length} records`); + }); + }; + + const interval = setInterval(async () => { + if (Object.keys(buffer).length === 0) { + return; + } + await flush(); + }, 5000); + + return { + log: (connectionId: string, level: LogLevel, message) => { + const logEntry: LogEntry = [Date.now(), connectionId, "function", level, message]; + buffer.push(logEntry); + }, + close: () => { + clearInterval(interval); + clickhouse.close(); + }, + }; +} diff --git a/libs/core-functions/src/functions/lib/crypto-code.ts b/libs/core-functions/src/functions/lib/crypto-code.ts new file mode 100644 index 000000000..472518d60 --- /dev/null +++ b/libs/core-functions/src/functions/lib/crypto-code.ts @@ -0,0 +1,30 @@ +export const cryptoCode = `const randomUUID = (options) => { + return _jitsu_crypto.getSync("randomUUID", {accessors: true, reference: true}).applySync(undefined, [options], { + arguments: { copy: true }, + result: { copy: true } + }); +} + +const randomBytes = (size) => { + return _jitsu_crypto.getSync("randomBytes", {accessors: true, reference: true}).applySync(undefined, [size], { + arguments: { copy: true }, + result: { copy: true } + }); +} + +const randomInt = (min, max) => { + return _jitsu_crypto.getSync("randomInt", {accessors: true, reference: true}).applySync(undefined, [min, max], { + arguments: { copy: true }, + result: { copy: true } + }); +} + +const hash = (algorithm, input , encoding) => { + return _jitsu_crypto.getSync("hash", {accessors: true, reference: true}).applySync(undefined, [algorithm, input, encoding], { + arguments: { copy: true }, + result: { copy: true } + }); +} + +export {hash, randomUUID, randomBytes, randomInt }; +`; diff --git a/libs/core-functions/src/functions/lib/crypto-code.txtjs b/libs/core-functions/src/functions/lib/crypto-code.txtjs new file mode 100644 index 000000000..0d666a9d3 --- /dev/null +++ b/libs/core-functions/src/functions/lib/crypto-code.txtjs @@ -0,0 +1,29 @@ +const randomUUID = (options) => { + return _jitsu_crypto.getSync("randomUUID", {accessors: true, reference: true}).applySync(undefined, [options], { + arguments: { copy: true }, + result: { copy: true } + }); +} + +const randomBytes = (size) => { + return _jitsu_crypto.getSync("randomBytes", {accessors: true, reference: true}).applySync(undefined, [size], { + arguments: { copy: true }, + result: { copy: true } + }); +} + +const randomInt = (min, max) => { + return _jitsu_crypto.getSync("randomInt", {accessors: true, reference: true}).applySync(undefined, [min, max], { + arguments: { copy: true }, + result: { copy: true } + }); +} + +const hash = (algorithm, input , encoding) => { + return _jitsu_crypto.getSync("hash", {accessors: true, reference: true}).applySync(undefined, [algorithm, input, encoding], { + arguments: { copy: true }, + result: { copy: true } + }); +} + +export {hash, randomUUID, randomBytes, randomInt }; diff --git a/libs/core-functions/src/functions/lib/http-agent.ts b/libs/core-functions/src/functions/lib/http-agent.ts index c67ef7c4b..0b79a2158 100644 --- a/libs/core-functions/src/functions/lib/http-agent.ts +++ b/libs/core-functions/src/functions/lib/http-agent.ts @@ -1,8 +1,8 @@ import { getSingleton } from "juava"; import Agent, { HttpsAgent } from "agentkeepalive"; -export const httpAgent = getSingleton("http-agent", createHTTPAgent); -export const httpsAgent = getSingleton("https-agent", createHTTPSAgent); +export const httpAgent = getSingleton("http-agent", createHTTPAgent, { silent: true }); +export const httpsAgent = getSingleton("https-agent", createHTTPSAgent, { silent: true }); async function createHTTPAgent(): Promise { const agent = new Agent({ timeout: 300000, freeSocketTimeout: 30000, maxSockets: 1024 }); diff --git a/libs/core-functions/src/functions/lib/index.ts b/libs/core-functions/src/functions/lib/index.ts index 2eefa4f18..8da0c9c42 100644 --- a/libs/core-functions/src/functions/lib/index.ts +++ b/libs/core-functions/src/functions/lib/index.ts @@ -5,6 +5,202 @@ import { ProcessingContext, ServerContextReservedProps, } from "@jitsu/protocols/analytics"; +import { + AnonymousEventsStore, + AnyEvent, + AnyProps, + EventContext, + FetchOpts, + FetchResponse, + FullContext, + FuncReturn, + FunctionLogger, + JitsuFunction, + FunctionMetrics, + TTLStore, +} from "@jitsu/protocols/functions"; +import { + getErrorMessage, + getLog, + getThrottle, + int32Hash, + isTruish, + LogLevel, + LogMessageBuilder, + newError, + noThrottle, + stopwatch, +} from "juava"; +import * as dns from "node:dns"; +import ip from "ip"; +import NodeCache from "node-cache"; + +const log = getLog("functions-context"); + +const fetchForbidLocal = isTruish(process.env.FETCH_FORBID_LOCAL); +const fetchLocalWhitelist = process.env.FETCH_LOCAL_WHITELIST + ? process.env.FETCH_LOCAL_WHITELIST.split(",").map(h => h.trim().toLowerCase()) + : []; +const publicHostnamesCache = new NodeCache({ stdTTL: 300, checkperiod: 60, useClones: false }); + +/** + * Store for incoming events, destination results and function log messages + */ +export interface EventsStore { + log(connectionId: string, level: LogLevel, msg: Record): void; + close(): void; +} + +export const DummyEventsStore: EventsStore = { + log(connectionId: string, level: LogLevel, msg: Record): void {}, + close(): void {}, +}; + +export function MultiEventsStore(stores: EventsStore[]): EventsStore { + return { + log(connectionId: string, level: LogLevel, msg: Record): void { + for (const store of stores) { + store.log(connectionId, level, msg); + } + }, + close(): void { + for (const store of stores) { + store.close(); + } + }, + }; +} + +export type MetricsMeta = { + workspaceId: string; + messageId: string; + streamId: string; + destinationId: string; + connectionId: string; + functionId?: string; + retries?: number; +}; + +export type FuncChainResult = { + connectionId?: string; + events: AnyEvent[]; + execLog: FunctionExecLog; +}; + +export type FunctionExecRes = { + receivedAt?: Date; + eventIndex: number; + event?: any; + metricsMeta?: MetricsMeta; + functionId: string; + error?: any; + dropped?: boolean; + ms: number; +}; + +export type FunctionExecLog = FunctionExecRes[]; + +export interface StoreMetrics { + storeStatus: (namespace: string, operation: string, status: string) => void; + warehouseStatus: (id: string, table: string, status: string, timeMs: number) => void; +} + +export interface RotorMetrics extends StoreMetrics { + logMetrics: (execLog: FunctionExecLog) => void; + close: () => void; +} + +export type FetchType = ( + url: string, + opts?: FetchOpts, + extra?: { log?: boolean; event?: AnyEvent } +) => Promise; + +export type InternalFetchType = ( + url: string, + opts?: FetchOpts, + extra?: { log?: boolean; ctx?: FunctionContext; event?: AnyEvent } +) => Promise; + +export type FunctionChainContext = { + log: { + info: (ctx: FunctionContext, message: string, ...args: any[]) => void | Promise; + warn: (ctx: FunctionContext, message: string, ...args: any[]) => void | Promise; + debug: (ctx: FunctionContext, message: string, ...args: any[]) => void | Promise; + error: (ctx: FunctionContext, message: string, ...args: any[]) => void | Promise; + }; + fetch: InternalFetchType; + store: TTLStore; + query?: (conId: string, query: string, params?: any) => Promise; + anonymousEventsStore?: AnonymousEventsStore; + metrics?: FunctionMetrics; + connectionOptions?: any; +}; + +export function wrapperFunction( + chainCtx: FunctionChainContext, + funcCtx: FunctionContext

, + jitsuFunction: JitsuFunction +): JitsuFunctionWrapper { + const log = createFunctionLogger(chainCtx, funcCtx); + const fetchWithLog = createFetchWrapper(chainCtx, funcCtx); + const props = funcCtx.props; + const store = chainCtx.store; + + return async (event: E, ctx: EventContext) => { + let ftch = chainCtx.fetch; + const fetchLogEnabled = + chainCtx.connectionOptions?.fetchLogLevel !== "debug" || + (funcCtx.function.debugTill && funcCtx.function.debugTill > new Date()); + if (fetchLogEnabled) { + ftch = async (url, opts, extra) => { + return fetchWithLog(url, opts, { ...extra, event }); + }; + } + const fullContext: FullContext

= { + ...ctx, + log, + fetch: ftch, + getWarehouse: () => { + throw newError("Warehouse API is not available in builtin functions"); + }, + store, + props, + }; + fullContext["anonymousEventsStore"] = chainCtx.anonymousEventsStore; + fullContext["connectionOptions"] = chainCtx.connectionOptions; + return jitsuFunction(event, fullContext); + }; +} + +function createFunctionLogger(chainCtx: FunctionChainContext, funcCtx: FunctionContext): FunctionLogger { + return { + info: (message: string, ...args: any) => chainCtx.log.info(funcCtx, message, ...args), + warn: (message: string, ...args: any) => chainCtx.log.warn(funcCtx, message, ...args), + debug: (message: string, ...args: any) => chainCtx.log.debug(funcCtx, message, ...args), + error: (message: string, ...args: any) => chainCtx.log.error(funcCtx, message, ...args), + }; +} + +function createFetchWrapper(chainCtx: FunctionChainContext, funcCtx: FunctionContext): FetchType { + return (url: string, opts?: FetchOpts, debug?: { event?: AnyEvent; log?: boolean }) => { + return chainCtx.fetch(url, opts, { log: debug?.log, ctx: funcCtx, event: debug?.event }); + }; +} + +export type FunctionContext

= { + function: { + id: string; + type: string; + debugTill?: Date; + }; + props: P; +}; + +export type JitsuFunctionWrapper = ( + event: E, + ctx: EventContext +) => Promise | FuncReturn; type KnownEventKeys = keyof Required; //to make sure we @@ -21,7 +217,7 @@ const knownProps: Record, true> = { url: true, }; +// returns page name or screen name as part of properties object +export function getPageOrScreenProps(event: AnalyticsServerEvent) { + if ((event.type === "page" || event.type === "screen") && event.name) { + return { + [event.type + "_name"]: event.name, + }; + } + return {}; +} + export function getEventCustomProperties( event: AnalyticsServerEvent, opts?: { exclude?: (obj: Record) => void } @@ -65,3 +271,260 @@ export function getEventCustomProperties( export function getTraits(event: AnalyticsServerEvent) { return { ...(event.traits || {}), ...(event.context?.traits || {}) }; } + +export function createFilter(filter: string): (eventType: string, eventName?: string) => boolean { + if (filter === "*") { + return () => true; + } else if (filter === "") { + return eventType => eventType !== "page" && eventType !== "screen"; + } else { + const events = filter.split(",").map(e => e.trim()); + return (eventType: string, eventName?: string) => { + return events.includes(eventType) || (!!eventName && events.includes(eventName)); + }; + } +} + +export function eventTimeSafeMs(event: AnalyticsServerEvent) { + const now = Date.now(); + const ts = event.timestamp ? new Date(event.timestamp as string).getTime() : NaN; + const receivedAt = event.receivedAt ? new Date(event.receivedAt as string).getTime() : NaN; + return Math.min(!isNaN(ts) ? ts : now, !isNaN(receivedAt) ? receivedAt : now, now); +} + +export const makeLog = (connectionId: string, eventsStore: EventsStore, repeatToLog?: boolean) => { + const logFunc = (lb: () => LogMessageBuilder, callback: (l: LogMessageBuilder) => void) => { + if (repeatToLog) { + callback(lb()); + } else { + log.inDebug(callback); + } + }; + return { + debug: (ctx: FunctionContext, message: any, ...args: any[]) => { + if (ctx.function.debugTill && ctx.function.debugTill > new Date()) { + const fid = ctx.function.id; + logFunc(log.atDebug, l => l.log(`[CON:${connectionId}]: [f:${fid}][DEBUG]: ${message}`, ...args)); + eventsStore.log(connectionId, "debug", { + type: "log-debug", + functionId: fid, + functionType: ctx.function.type, + message: { + text: message, + args, + }, + }); + } + }, + warn: (ctx: FunctionContext, message: any, ...args: any[]) => { + const fid = ctx.function.id; + logFunc(log.atWarn, l => l.log(`[CON:${connectionId}]: [f:${fid}][WARN]: ${message}`, ...args)); + eventsStore.log(connectionId, "warn", { + type: "log-warn", + functionId: fid, + functionType: ctx.function.type, + message: { + text: message, + args, + }, + }); + }, + error: (ctx: FunctionContext, message: any, ...args: any[]) => { + const fid = ctx.function.id; + eventsStore.log(connectionId, "error", { + type: "log-error", + functionId: fid, + functionType: ctx.function.type, + message: { + text: message, + args, + }, + }); + logFunc(log.atError, l => { + if (args?.length > 0) { + const last = args[args.length - 1]; + if (last.stack) { + l.withCause(last); + args = args.slice(0, args.length - 1); + } + } + l.log(`[CON:${connectionId}]: [f:${fid}][ERROR]: ${message}`, ...args); + }); + }, + info: (ctx: FunctionContext, message: any, ...args: any[]) => { + const fid = ctx.function.id; + logFunc(log.atInfo, l => l.log(`[CON:${connectionId}]: [f:${fid}][INFO]: ${message}`, ...args)); + eventsStore.log(connectionId, "info", { + type: "log-info", + functionId: fid, + functionType: ctx.function.type, + message: { + text: message, + args, + }, + }); + }, + }; +}; + +export const makeFetch = ( + connectionId: string, + eventsStore: EventsStore, + logLevel: "info" | "debug", + fetchTimeoutMs: number = 2000 +) => { + const throttle = connectionId === "clke5lrfm0000ii0gahryc37d-wbyo-5jyq-KIMXwt" ? getThrottle(5000) : noThrottle(); + + async function isPublic(hostname: string) { + const cached = publicHostnamesCache.get(hostname); + if (typeof cached !== "undefined") { + return cached as boolean; + } + const addresses = await dns.promises.lookup(hostname, { all: true }); + if (addresses.length === 0) { + publicHostnamesCache.set(hostname, false); + return false; + } + for (const addr of addresses) { + if (ip.isPrivate(addr.address)) { + publicHostnamesCache.set(hostname, false); + return false; + } + } + publicHostnamesCache.set(hostname, true); + return true; + } + + return async ( + url: string, + init?: FetchOpts, + extra?: { log?: boolean; ctx?: FunctionContext; event?: AnyEvent } + ): Promise => { + //capture execution time + const sw = stopwatch(); + const ctx = extra?.ctx?.function; + const id = ctx?.id || "unknown"; + const type = ctx?.type || "unknown"; + const logEnabled = logLevel === "info" || (ctx?.debugTill && ctx?.debugTill > new Date()); + const logToRedis = typeof extra?.log === "boolean" ? extra.log : true; + const baseInfo = + logEnabled && logToRedis + ? { + functionId: id, + functionType: type, + type: "http-request", + url: url, + method: init?.method || "GET", + body: init?.body, + headers: init?.headers ? hideSensitiveHeaders(init.headers) : undefined, + event: extra?.event || {}, + } + : undefined; + + let fetchResult: any = undefined; + try { + const host = new URL(url).hostname; //this will throw if url is invalid + if (fetchForbidLocal && !fetchLocalWhitelist.includes(host.toLowerCase()) && !(await isPublic(host))) { + throw newError(`fetch failed`); + } + const throttleValue = throttle.throttle(); + if (throttleValue > 0 && Math.random() < throttleValue) { + const e = new Error(`Fetch request throttled because of timeout rate: ${throttleValue}`); + e.name = "ThrottleError"; + throw e; + } + const internalInit: RequestInit = { + ...init, + keepalive: true, + signal: AbortSignal.timeout(fetchTimeoutMs), + }; + fetchResult = await fetch(url, internalInit); + throttle.success(); + } catch (err: any) { + if (err.name === "TimeoutError") { + throttle.fail(); + err = newError(`Fetch request exceeded timeout ${fetchTimeoutMs}ms and was aborted`, err); + } else if (err.name !== "ThrottleError") { + // we throttle only timeout errors. all other cases considered as success here + throttle.success(); + } + if (logEnabled) { + const elapsedMs = sw.elapsedMs(); + if (logToRedis) { + eventsStore.log(connectionId, logLevel, { ...baseInfo, error: getErrorMessage(err), elapsedMs: elapsedMs }); + } + log.inDebug(l => + l.log( + `[CON:${connectionId}]: [f:${id}][ERROR][FETCH]: ${url} Error: ${getErrorMessage( + err + )} ElapsedMs: ${elapsedMs}` + ) + ); + } + throw err; + } + + if (logEnabled) { + const elapsedMs = sw.elapsedMs(); + //clone response to be able to read it twice + const cloned = fetchResult.clone(); + const respText = await trimResponse(cloned); + if (logToRedis) { + eventsStore.log(connectionId, logLevel, { + ...baseInfo, + status: fetchResult.status, + statusText: fetchResult.statusText, + elapsedMs: elapsedMs, + response: tryJson(respText), + }); + } + if (fetchResult.status >= 300) { + log.inDebug(l => + l.log( + `[CON:${connectionId}]: [f:${id}][ERROR][FETCH]: ${url} Status: ${fetchResult.status} Response: ${respText} ElapsedMs: ${elapsedMs}` + ) + ); + } + } + + return fetchResult; + }; +}; + +function hideSensitiveHeaders(headers: Record): Record { + const result: Record = {}; + for (const [k, v] of Object.entries(headers)) { + result[k] = k.toLowerCase().includes("authorization") || k.toLowerCase().includes("token") ? "*****" : v; + } + return result; +} + +async function trimResponse(fetchResult: Response, maxLen: number = 1000): Promise { + const text = await fetchResult.text(); + if (text.length > maxLen) { + return `${text.substring(0, maxLen)} ... [truncated, length: ${text.length}]`; + } + return text; +} + +function tryJson(text: string, maxLen: number = 1000): any { + try { + return JSON.parse(text); + } catch (err) { + return text; + } +} + +export function bulkerPartitionParam(ctx: FullContext, event: AnalyticsServerEvent): string { + let partitionParam = ""; + if (ctx["connectionOptions"]?.multithreading) { + const threadsCount = ctx["connectionOptions"]?.threadsCount || 2; + const thread = event.messageId + ? int32Hash(event.messageId) % threadsCount + : Math.floor(Math.random() * threadsCount); + if (thread > 0) { + partitionParam = `&partition=${thread}`; + } + } + return partitionParam; +} diff --git a/libs/core-functions/src/functions/lib/json-fetch.ts b/libs/core-functions/src/functions/lib/json-fetch.ts new file mode 100644 index 000000000..41f7bb336 --- /dev/null +++ b/libs/core-functions/src/functions/lib/json-fetch.ts @@ -0,0 +1,86 @@ +import { AnyEvent, FunctionLogger, FetchOpts, FetchResponse } from "@jitsu/protocols/functions"; + +export type JsonFetchOpts = Omit & { + body?: any; +}; + +export type JsonFetcher = (url: string, options?: JsonFetchOpts, debug?: { event?: AnyEvent }) => Promise; + +const maxResponseLen = 300; + +function prettifyJson(bodyStr: any) { + if (!bodyStr) { + return bodyStr + ""; + } else if (typeof bodyStr === "string") { + try { + return JSON.stringify(JSON.parse(bodyStr), null, 2); + } catch (e) { + return bodyStr + ""; + } + } else { + try { + return JSON.stringify(bodyStr, null, 2); + } catch (e) { + return bodyStr + ""; + } + } +} + +export class JsonFetchError extends Error { + public readonly responseStatus: number; + constructor(responseStatus: number, errorMessage: any) { + super(errorMessage); + this.responseStatus = responseStatus; + } +} + +export function jsonFetcher( + fetch: (url: string, opts?: FetchOpts, debug?: { event?: AnyEvent }) => Promise, + { log, debug }: { log: FunctionLogger; debug?: boolean } = { log: console } +): JsonFetcher { + return async (url: string, options?: JsonFetchOpts) => { + const method = options?.method || (options?.body ? "POST" : "GET"); + const bodyStr = + typeof options?.body === "string" ? options?.body : options?.body ? JSON.stringify(options?.body) : undefined; + const response = await fetch(url, { + ...(options || {}), + headers: { + ...(options?.headers || {}), + "Content-Type": "application/json", + Accept: "application/json", + }, + method, + body: bodyStr, + }); + let responseText = await response.text(); + if (debug) { + const message = `${method} ${url} → ${response.ok ? "🟢" : "🔴"}${response.status} ${ + response.statusText + }.\n📩Response body${responseText ? `: \n${prettifyJson(responseText)}` : " is empty"}`; + if (response.ok) { + log.debug(message); + } else { + log.warn(message); + } + } + if (!response.ok) { + if (responseText.length > maxResponseLen) { + responseText = + responseText.substring(0, maxResponseLen) + "...(truncated, total length: " + responseText.length + ")"; + } + throw new JsonFetchError( + response.status, + `${method} ${url} failed with status ${response.status} ${response.statusText}: ${responseText}` + ); + } + if (responseText === "") { + return undefined; + } else { + try { + return JSON.parse(responseText); + } catch (e) { + throw new Error(`Request error: ${method} ${url} returned unparseable JSON: ${responseText}`); + } + } + }; +} diff --git a/libs/core-functions/src/functions/lib/mongodb.ts b/libs/core-functions/src/functions/lib/mongodb.ts index 3dff82622..04d289c00 100644 --- a/libs/core-functions/src/functions/lib/mongodb.ts +++ b/libs/core-functions/src/functions/lib/mongodb.ts @@ -1,5 +1,5 @@ -import { getLog, getSingleton, requireDefined } from "juava"; -import { MongoClient, ObjectId } from "mongodb"; +import { getLog, getSingleton, parseDate, parseNumber, requireDefined } from "juava"; +import { MongoClient, MongoClientOptions, ObjectId } from "mongodb"; import { AnalyticsServerEvent } from "@jitsu/protocols/analytics"; import { AnonymousEventsStore } from "@jitsu/protocols/functions"; @@ -7,20 +7,38 @@ const AnonymousEventsStoreIdField = "_jitsu_anonymous_id_"; const log = getLog("mongodb"); -export const mongodb = getSingleton("mongodb", createClient); +export const mongodb = getSingleton("mongodb", createClient, { + errorTtlSec: 5, + cleanupFunc: async (client: MongoClient) => { + await client.close(); + }, + optional: true, +}); async function createClient() { + const mongoTimeout = parseNumber(process.env.MONGODB_TIMEOUT_MS, 1000); const mongodbURL = requireDefined(process.env.MONGODB_URL, "env MONGODB_URL is not defined"); log.atInfo().log(`Connecting to MongoDB server...`); // Create a new MongoClient - const client = new MongoClient(mongodbURL); - // Connect the client to the server (optional starting in v4.7) - await client.connect(); - // Establish and verify connection - await client.db().command({ ping: 1 }); - return client; + const client = new MongoClient(mongodbURL, { + compressors: process.env.MONGODB_NETWORK_COMPRESSION ? process.env.MONGODB_NETWORK_COMPRESSION : ["zstd"], + serverSelectionTimeoutMS: 60000, + maxPoolSize: 32, + connectTimeoutMS: 60000, + socketTimeoutMS: mongoTimeout, + }); + try { + // Connect the client to the server (optional starting in v4.7) + await client.connect(); + // Establish and verify connection + await client.db().admin().ping(); + return client; + } catch (e) { + client.close(true); + throw e; + } } const MongoCreatedCollections = new Set(); @@ -28,12 +46,15 @@ const MongoCreatedCollections = new Set(); export function mongoAnonymousEventsStore(): AnonymousEventsStore { return { async addEvent(collectionName: string, anonymousId: string, event: AnalyticsServerEvent, ttlDays: number) { - const mongo = await mongodb.waitInit(); - await ensureMongoCollection(mongo, collectionName, ttlDays); + const mongo = mongodb(); + await ensureMongoCollection(mongo, collectionName, ttlDays, [AnonymousEventsStoreIdField]); const res = await mongo .db() .collection(collectionName) - .insertOne({ ...event, [AnonymousEventsStoreIdField]: anonymousId }); + .insertOne( + { ...event, timestamp: parseDate(event.timestamp, new Date()), [AnonymousEventsStoreIdField]: anonymousId }, + { writeConcern: { w: 1, journal: false } } + ); if (res.acknowledged) { return; } else { @@ -42,7 +63,7 @@ export function mongoAnonymousEventsStore(): AnonymousEventsStore { }, async evictEvents(collectionName: string, anonymousId: string) { - const mongo = await mongodb.waitInit(); + const mongo = mongodb(); // to ensure query consistency between find and delete query - limit them to the same time window const maxObjectId = new ObjectId(Math.floor(new Date().getTime() / 1000).toString(16) + "0000000000000000"); // load anonymous events from user_recognition collection @@ -68,28 +89,43 @@ export function mongoAnonymousEventsStore(): AnonymousEventsStore { }; } -async function ensureMongoCollection(mongo: MongoClient, collectionName: string, ttlDays: number) { +async function ensureMongoCollection( + mongo: MongoClient, + collectionName: string, + ttlDays: number, + indexFields: string[] = [] +) { if (MongoCreatedCollections.has(collectionName)) { return; } try { const db = mongo.db(); - const collStatus = await db.collection(collectionName).stats(); - if (collStatus.wiredTiger) { + const collStatus = await db + .collection(collectionName) + .aggregate([{ $collStats: { count: {} } }]) + .next() + .catch(e => {}); + if (collStatus) { //collection already exists MongoCreatedCollections.add(collectionName); return; } const collection = await db.createCollection(collectionName, { - expireAfterSeconds: 60 * 60 * 24 * ttlDays, clusteredIndex: { key: { _id: 1 }, unique: true, }, - writeConcern: { w: 1, j: false }, + writeConcern: { w: 1, journal: false }, storageEngine: { wiredTiger: { configString: "block_compressor=zstd" } }, }); - await collection.createIndex({ [AnonymousEventsStoreIdField]: 1 }); + await collection.createIndex({ timestamp: 1 }, { expireAfterSeconds: 60 * 60 * 24 * ttlDays }); + if (indexFields.length > 0) { + const index = {}; + indexFields.forEach(field => { + index[field] = 1; + }); + await collection.createIndex(index); + } MongoCreatedCollections.add(collectionName); } catch (err) { throw new Error(`Failed to create collection ${collectionName}: ${err}`); diff --git a/libs/core-functions/src/functions/lib/nango-config.ts b/libs/core-functions/src/functions/lib/nango-config.ts new file mode 100644 index 000000000..f1574eb24 --- /dev/null +++ b/libs/core-functions/src/functions/lib/nango-config.ts @@ -0,0 +1,29 @@ +import { requireDefined } from "juava"; + +export type NangoParams = { + callback: string; + secretKey: string; + publicKey: string; + nangoAppHost: string; + nangoApiHost: string; +}; + +export type NangoConfig = + | ({ enabled: false } & { [k in keyof NangoParams]?: never }) + | ({ enabled: true } & NangoParams); + +function getNangoConfig(): NangoConfig { + if (!process.env.NANGO_APP_HOST) { + return { enabled: false }; + } + return { + enabled: true, + nangoAppHost: process.env.NANGO_APP_HOST, + nangoApiHost: requireDefined(process.env.NANGO_API_HOST, `env NANGO_API_HOST is required`), + secretKey: requireDefined(process.env.NANGO_SECRET_KEY, `env NANGO_SECRET_KEY is required`), + publicKey: requireDefined(process.env.NANGO_PUBLIC_KEY, `env NANGO_SECRET_KEY is required`), + callback: process.env.NANGO_CALLBACK || `${process.env.NANGO_HOST}/oauth/callback`, + }; +} + +export const nangoConfig: NangoConfig = getNangoConfig(); diff --git a/libs/core-functions/src/functions/lib/oauth.ts b/libs/core-functions/src/functions/lib/oauth.ts new file mode 100644 index 000000000..eb2779451 --- /dev/null +++ b/libs/core-functions/src/functions/lib/oauth.ts @@ -0,0 +1,17 @@ +import { rpc } from "juava"; +import { nangoConfig } from "./nango-config"; + +export const getOauthCreds = async (integrationId: string, connectionId: string): Promise => { + if (nangoConfig.enabled) { + const nangoConnectionObject = await rpc( + `${nangoConfig.nangoApiHost}/connection/${connectionId}?provider_config_key=${integrationId}`, + { headers: { Authorization: `Bearer ${nangoConfig.secretKey}` } } + ); + + // getLog().atInfo().log("Configuration object", JSON.stringify(nangoConnectionObject, null, 2)); + + return nangoConnectionObject; + } else { + throw new Error("Nango is not enabled, cannot get OAuth credentials"); + } +}; diff --git a/libs/core-functions/src/functions/lib/profiles-udf-wrapper-code.ts b/libs/core-functions/src/functions/lib/profiles-udf-wrapper-code.ts new file mode 100644 index 000000000..54f4f9232 --- /dev/null +++ b/libs/core-functions/src/functions/lib/profiles-udf-wrapper-code.ts @@ -0,0 +1,225 @@ +import { DropRetryErrorName, RetryErrorName } from "@jitsu/functions-lib"; + +export const functionsLibCode = `const DropRetryErrorName = "Drop & RetryError"; +const RetryErrorName = "RetryError"; +class RetryError extends Error { + constructor(message, options) { + super(message); + this.name = options?.drop ? "${DropRetryErrorName}" : "${RetryErrorName}"; + } +} + +export { DropRetryErrorName, RetryError, RetryErrorName };`; + +export const chainWrapperCode = `//** @UDF_FUNCTIONS_IMPORT **// + +function isDropResult(result) { + return result === "drop" || (Array.isArray(result) && result.length === 0) || result === null || result === false; +} + +async function runChain( + chain, + events, + user, + ctx +) { + const f = chain[0]; + try { + const result = await f.f(events, user, ctx); + if (isDropResult(result)) { + return undefined + } + return result + } catch (err) { + throw err; + } +} + +const wrappedFunctionChain = async function (eventsProvider, userProvider, ctx) { + let chain = []; + //** @UDF_FUNCTIONS_CHAIN **// + const iterator = { + [Symbol.iterator]() { + return { + next() { + const s = eventsProvider.applySyncPromise(undefined, [], { + arguments: {copy: true} + }) + if (typeof s === "undefined") { + return {done: true}; + } else { + return {done: false, value: JSON.parse(s) }; + } + }, + }; + }, + get length() { + throw new Error("The 'events' object doesn't have the \`length\` property, however you can iterate through it with \`for (const item of events)\` syntax"); + }, + filter() { + throw new Error("The 'events' object doesn't have the \`filter\` method, however you can iterate through it with \`for (const item of events)\` syntax"); + }, + map() { + throw new Error("The 'events' object doesn't have the \`map\` method, however you can iterate through it with \`for (const item of events)\` syntax"); + }, + find() { + throw new Error("The 'events' object doesn't have the \`find\` method, however you can iterate through it with \`for (const item of events)\` syntax"); + }, + some() { + throw new Error("The 'events' object doesn't have the \`some\` method, however you can iterate through it with \`for (const item of events)\` syntax"); + }, + reduce() { + throw new Error("The 'events' object doesn't have the \`reduce\` method, however you can iterate through it with \`for (const item of events)\` syntax"); + }, + sort() { + throw new Error("The 'events' object doesn't have the \`sort\` method, however you can iterate through it with \`for (const item of events)\` syntax"); + }, + }; + let lazyUser; + function lazyLoad() { + if (!lazyUser) { + lazyUser = JSON.parse(userProvider.applySyncPromise(undefined, [], { + arguments: {copy: true} + })); + } + return lazyUser; + } + + const user = { + get anonymousId() { + return lazyLoad().anonymousId; + }, + get userId() { + return lazyLoad().userId; + }, + get profileId() { + return lazyLoad().profileId; + }, + get traits() { + return lazyLoad().traits; + }, + }; + + return runChain(chain, iterator, user, ctx); +}; + +const wrappedUserFunction = (id, f, funcCtx) => { + + const log = { + info: (...args) => { + _jitsu_log.info.apply(undefined, [funcCtx, ...args], {arguments: {copy: true}}); + }, + error: (...args) => { + _jitsu_log.error.apply(undefined, [funcCtx, ...args], {arguments: {copy: true}}); + }, + warn: (...args) => { + _jitsu_log.warn.apply(undefined, [funcCtx, ...args], {arguments: {copy: true}}); + }, + debug: (...args) => { + _jitsu_log.debug.apply(undefined, [funcCtx, ...args], {arguments: {copy: true}}); + }, + } + + const store = { + set: async (key, value, opts) => { + await _jitsu_store.set.apply(undefined, [key, value, opts], { + arguments: {copy: true}, + result: {promise: true} + }); + }, + del: async key => { + await _jitsu_store.del.apply(undefined, [key], { + arguments: {copy: true}, + result: {promise: true} + }); + }, + get: async key => { + const res = await _jitsu_store.get.apply(undefined, [key], { + arguments: {copy: true}, + result: {promise: true} + }); + return res ? JSON.parse(res) : undefined; + }, + ttl: async key => { + return await _jitsu_store.ttl.apply(undefined, [key], { + arguments: {copy: true}, + result: {promise: true} + }); + }, + } + + const getWarehouse = (warehouseId) => { + return { + query: async (query, opts) => { + return await _jitsu_query.apply(undefined, [warehouseId, query, opts], { + arguments: {copy: true}, + result: {promise: true, copy: true} + }); + }, + }; + } + + const fetch = async (url, opts, extras) => { + let res + if (extras) { + res = await _jitsu_fetch.apply(undefined, [url, opts, {ctx: funcCtx, event: extras.event}], { + arguments: {copy: true}, + result: {promise: true} + }); + } else { + res = await _jitsu_fetch.apply(undefined, [url, opts], { + arguments: {copy: true}, + result: {promise: true} + }); + } + const r = JSON.parse(res); + + return { + ...r, + json: async () => { + return JSON.parse(r.body); + }, + text: async () => { + return r.body; + }, + arrayBuffer: async () => { + throw new Error("Method 'arrayBuffer' is not implemented"); + }, + blob: async () => { + throw new Error("Method 'blob' is not implemented"); + }, + formData: async () => { + throw new Error("Method 'formData' is not implemented"); + }, + clone: async () => { + throw new Error("Method 'clone' is not implemented"); + }, + }; + } + + return async function (events, user, c) { + const fetchLogEnabled = _jitsu_fetch_log_level !== "debug" || (funcCtx.function.debugTill && funcCtx.function.debugTill > new Date()); + let ftch = fetch + if (fetchLogEnabled) { + ftch = async(url, opts) => { + return fetch(url, opts, {event}); + } + } + const ctx = { + ...c, + props: funcCtx.props, + log, + getWarehouse, + store, + fetch: ftch, + profileBuilder: { + id: _jitsu_pbId, + version: _jitsu_pbVersion, + } + }; + return await f(events, user, ctx); + } +}; + +export {wrappedFunctionChain}; +`; diff --git a/libs/core-functions/src/functions/lib/profiles-udf-wrapper-code.txtjs b/libs/core-functions/src/functions/lib/profiles-udf-wrapper-code.txtjs new file mode 100644 index 000000000..2efcea4b2 --- /dev/null +++ b/libs/core-functions/src/functions/lib/profiles-udf-wrapper-code.txtjs @@ -0,0 +1,211 @@ +//** @UDF_FUNCTIONS_IMPORT **// + +function isDropResult(result) { + return result === "drop" || (Array.isArray(result) && result.length === 0) || result === null || result === false; +} + +async function runChain( + chain, + events, + user, + ctx +) { + const f = chain[0]; + try { + const result = await f.f(events, user, ctx); + if (isDropResult(result)) { + return undefined + } + return result + } catch (err) { + throw err; + } +} + +const wrappedFunctionChain = async function (eventsProvider, userProvider, ctx) { + let chain = []; + //** @UDF_FUNCTIONS_CHAIN **// + const iterator = { + [Symbol.iterator]() { + return { + next() { + const s = eventsProvider.applySyncPromise(undefined, [], { + arguments: {copy: true} + }) + if (typeof s === "undefined") { + return {done: true}; + } else { + return {done: false, value: JSON.parse(s) }; + } + }, + }; + }, + get length() { + throw new Error("The 'events' object doesn't have the `length` property, however you can iterate through it with `for (const item of events)` syntax"); + }, + filter() { + throw new Error("The 'events' object doesn't have the `filter` method, however you can iterate through it with `for (const item of events)` syntax"); + }, + map() { + throw new Error("The 'events' object doesn't have the `map` method, however you can iterate through it with `for (const item of events)` syntax"); + }, + find() { + throw new Error("The 'events' object doesn't have the `find` method, however you can iterate through it with `for (const item of events)` syntax"); + }, + some() { + throw new Error("The 'events' object doesn't have the `some` method, however you can iterate through it with `for (const item of events)` syntax"); + }, + reduce() { + throw new Error("The 'events' object doesn't have the `reduce` method, however you can iterate through it with `for (const item of events)` syntax"); + }, + sort() { + throw new Error("The 'events' object doesn't have the `sort` method, however you can iterate through it with `for (const item of events)` syntax"); + }, + }; + let lazyUser; + function lazyLoad() { + if (!lazyUser) { + lazyUser = JSON.parse(userProvider.applySyncPromise(undefined, [], { + arguments: {copy: true} + })); + } + return lazyUser; + } + + const user = { + get anonymousId() { + return lazyLoad().anonymousId; + }, + get userId() { + return lazyLoad().userId; + }, + get profileId() { + return lazyLoad().profileId; + }, + get traits() { + return lazyLoad().traits; + }, + }; + + return runChain(chain, iterator, user, ctx); +}; + +const wrappedUserFunction = (id, f, funcCtx) => { + + const log = { + info: (...args) => { + _jitsu_log.info.apply(undefined, [funcCtx, ...args], {arguments: {copy: true}}); + }, + error: (...args) => { + _jitsu_log.error.apply(undefined, [funcCtx, ...args], {arguments: {copy: true}}); + }, + warn: (...args) => { + _jitsu_log.warn.apply(undefined, [funcCtx, ...args], {arguments: {copy: true}}); + }, + debug: (...args) => { + _jitsu_log.debug.apply(undefined, [funcCtx, ...args], {arguments: {copy: true}}); + }, + } + + const store = { + set: async (key, value, opts) => { + await _jitsu_store.set.apply(undefined, [key, value, opts], { + arguments: {copy: true}, + result: {promise: true} + }); + }, + del: async key => { + await _jitsu_store.del.apply(undefined, [key], { + arguments: {copy: true}, + result: {promise: true} + }); + }, + get: async key => { + const res = await _jitsu_store.get.apply(undefined, [key], { + arguments: {copy: true}, + result: {promise: true} + }); + return res ? JSON.parse(res) : undefined; + }, + ttl: async key => { + return await _jitsu_store.ttl.apply(undefined, [key], { + arguments: {copy: true}, + result: {promise: true} + }); + }, + } + + const getWarehouse = (warehouseId) => { + return { + query: async (query, opts) => { + return await _jitsu_query.apply(undefined, [warehouseId, query, opts], { + arguments: {copy: true}, + result: {promise: true, copy: true} + }); + }, + }; + } + + const fetch = async (url, opts, extras) => { + let res + if (extras) { + res = await _jitsu_fetch.apply(undefined, [url, opts, {ctx: funcCtx, event: extras.event}], { + arguments: {copy: true}, + result: {promise: true} + }); + } else { + res = await _jitsu_fetch.apply(undefined, [url, opts], { + arguments: {copy: true}, + result: {promise: true} + }); + } + const r = JSON.parse(res); + + return { + ...r, + json: async () => { + return JSON.parse(r.body); + }, + text: async () => { + return r.body; + }, + arrayBuffer: async () => { + throw new Error("Method 'arrayBuffer' is not implemented"); + }, + blob: async () => { + throw new Error("Method 'blob' is not implemented"); + }, + formData: async () => { + throw new Error("Method 'formData' is not implemented"); + }, + clone: async () => { + throw new Error("Method 'clone' is not implemented"); + }, + }; + } + + return async function (events, user, c) { + const fetchLogEnabled = _jitsu_fetch_log_level !== "debug" || (funcCtx.function.debugTill && funcCtx.function.debugTill > new Date()); + let ftch = fetch + if (fetchLogEnabled) { + ftch = async(url, opts) => { + return fetch(url, opts, {event}); + } + } + const ctx = { + ...c, + props: funcCtx.props, + log, + getWarehouse, + store, + fetch: ftch, + profileBuilder: { + id: _jitsu_pbId, + version: _jitsu_pbVersion, + } + }; + return await f(events, user, ctx); + } +}; + +export {wrappedFunctionChain}; diff --git a/libs/core-functions/src/functions/lib/profiles-udf-wrapper.ts b/libs/core-functions/src/functions/lib/profiles-udf-wrapper.ts new file mode 100644 index 000000000..658ae1d0c --- /dev/null +++ b/libs/core-functions/src/functions/lib/profiles-udf-wrapper.ts @@ -0,0 +1,540 @@ +import { getLog, LogLevel, parseNumber, sanitize, stopwatch } from "juava"; +import { Isolate, ExternalCopy, Reference, Module, Context } from "isolated-vm"; +import { FetchOpts, Store, TTLStore } from "@jitsu/protocols/functions"; +import { AnalyticsServerEvent } from "@jitsu/protocols/analytics"; + +import { EventsStore, FunctionChainContext, FunctionContext, makeFetch, makeLog } from "./index"; +import { cryptoCode } from "./crypto-code"; +import { RetryError } from "@jitsu/functions-lib"; +import { clearTimeout } from "node:timers"; +import * as crypto from "node:crypto"; +import { ProfileResult } from "@jitsu/protocols/profile"; +import { chainWrapperCode, functionsLibCode } from "./profiles-udf-wrapper-code"; +import { logType } from "./udf_wrapper"; +import { createMemoryStore, memoryStoreDump } from "./store"; +import { warehouseQuery } from "./warehouse-store"; +import { EntityStore } from "../../lib/entity-store"; +import { EnrichedConnectionConfig } from "../../lib/config-types"; + +const log = getLog("udf-wrapper"); + +export type ProfileUser = { + profileId: string; + userId: string; + anonymousId: string; + traits: Record; +}; + +export type ProfileUserProvider = () => Promise; +export type EventsProvider = () => Promise; + +export type Profile = { + profile_id: string; + destination_id?: string; + table_name?: string; + traits: Record; + version?: number; + updated_at: Date; +}; + +export type ProfileFunctionWrapper = ( + eventsProvider: EventsProvider, + userProvider: ProfileUserProvider, + context: FunctionContext +) => Promise; + +type UDFWrapperResult = { + userFunction: ProfileFunctionWrapper; + isDisposed: () => boolean; + close: () => void; +}; + +type UDFFunction = { + id: string; + name: string; + code: string; +}; + +export const ProfileUDFWrapper = ( + id: string, + version: number, + fullId: string, + chainCtx: FunctionChainContext, + funcCtx: FunctionContext, + functions: UDFFunction[] +): UDFWrapperResult => { + log.atDebug().log(`[CON:${fullId}] Compiling ${functions.length} UDF functions`); + const sw = stopwatch(); + let isolate: Isolate; + let context: Context; + let refs: Reference[] = []; + try { + isolate = new Isolate({ memoryLimit: 512 }); + context = isolate.createContextSync(); + const jail = context.global; + + // This make the global object available in the context as 'global'. We use 'derefInto()' here + // because otherwise 'global' would actually be a Reference{} object in the new isolate. + jail.setSync("global", jail.derefInto()); + jail.setSync( + "process", + new ExternalCopy({ env: funcCtx.props || {} }).copyInto({ release: true, transferIn: true }) + ); + + jail.setSync("_jitsu_pbId", id); + jail.setSync("_jitsu_pbVersion", version); + jail.setSync("_jitsu_funcCtx", new ExternalCopy(funcCtx).copyInto({ release: true, transferIn: true })); + jail.setSync( + "_jitsu_log", + new ExternalCopy({ + info: makeReference(refs, chainCtx.log.info), + warn: makeReference(refs, chainCtx.log.warn), + debug: makeReference(refs, chainCtx.log.debug), + error: makeReference(refs, chainCtx.log.error), + }).copyInto({ release: true, transferIn: true }) + ); + jail.setSync("_jitsu_fetch_log_level", chainCtx.connectionOptions?.fetchLogLevel || "info"); + jail.setSync( + "_jitsu_crypto", + makeReference(refs, { + hash: crypto["hash"], + randomUUID: crypto.randomUUID, + randomBytes: crypto.randomBytes, + randomInt: crypto.randomInt, + }) + ); + jail.setSync("require", () => { + throw new Error("'require' is not supported. Please use 'import' instead"); + }); + jail.setSync("_jitsu_query", makeReference(refs, chainCtx.query)); + jail.setSync( + "_jitsu_fetch", + makeReference(refs, async (url: string, opts?: FetchOpts, extra?: any) => { + const res = await chainCtx.fetch(url, opts, extra); + const headers: any = {}; + res.headers.forEach((v, k) => { + headers[k] = v; + }); + const text = await res.text(); + const j = { + status: res.status, + statusText: res.statusText, + type: res.type, + redirected: res.redirected, + body: text, + bodyUsed: true, + url: res.url, + ok: res.ok, + headers: headers, + }; + return JSON.stringify(j); + }) + ); + jail.setSync( + "_jitsu_store", + new ExternalCopy({ + get: makeReference(refs, async (key: string) => { + const res = await chainCtx.store.get(key); + return JSON.stringify(res); + }), + set: makeReference(refs, chainCtx.store.set), + del: makeReference(refs, chainCtx.store.del), + ttl: makeReference(refs, async (key: string) => { + return await chainCtx.store.ttl(key); + }), + }).copyInto({ release: true, transferIn: true }) + ); + + const functionsLib = isolate.compileModuleSync(functionsLibCode, { + filename: "functions-lib.js", + }); + functionsLib.instantiateSync(context, (specifier: string): Module => { + throw new Error(`import is not allowed: ${specifier}`); + }); + const cryptoLib = isolate.compileModuleSync(cryptoCode, { + filename: "crypto.js", + }); + cryptoLib.instantiateSync(context, (specifier: string): Module => { + throw new Error(`import is not allowed: ${specifier}`); + }); + const udfModules: Record = {}; + for (let i = 0; i < functions.length; i++) { + const sw = stopwatch(); + const f = functions[i]; + log.atDebug().log(`[CON:${fullId}]: [f:${f.id}] Compiling UDF function '${f.name}'`); + const moduleName = "f_" + sanitize(f.name, "_") + "_" + f.id; + const udf = isolate.compileModuleSync(f.code, { filename: moduleName + ".js" }); + udf.instantiateSync(context, (specifier: string) => { + if (specifier === "@jitsu/functions-lib") { + return functionsLib; + } else if (specifier === "crypto") { + return cryptoLib; + } + throw new Error(`import is not allowed: ${specifier}`); + }); + udfModules[moduleName] = udf; + log.atDebug().log(`[CON:${fullId}] [f:${f.id}] UDF function '${f.name}' compiled in ${sw.elapsedPretty()}`); + } + + let code = chainWrapperCode.replace( + "//** @UDF_FUNCTIONS_IMPORT **//", + Object.keys(udfModules) + .map(m => `import * as ${m} from "${m}";\n`) + .join("") + ); + code = code.replace( + "//** @UDF_FUNCTIONS_CHAIN **//", + "chain = [" + + Object.keys(udfModules) + .map(m => { + const id = m.split("_").pop(); + return `{id: "${id}", meta: ${m}.config, f: wrappedUserFunction("${id}", ${m}.default, { props: _jitsu_funcCtx.props, function:{ ..._jitsu_funcCtx.function, id: "${id}"}})}`; + }) + .join(",") + + "];" + ); + + const wrapper = isolate.compileModuleSync(code, { + filename: "jitsu-wrapper.js", + }); + wrapper.instantiateSync(context, (specifier: string) => { + const udf = udfModules[specifier]; + if (udf) { + //log.atInfo().log(`[${connectionId}] UDF function '${specifier}' is imported`); + return udf; + } + if (specifier === "@jitsu/functions-lib") { + return functionsLib; + } + throw new Error(`import is not allowed: ${specifier}`); + }); + wrapper.evaluateSync(); + const wrapperFunc = wrap(fullId, isolate, context, wrapper, refs); + log.atInfo().log(`[CON:${fullId}] ${functions.length} UDF functions compiled in: ${sw.elapsedPretty()}`); + return wrapperFunc; + } catch (e) { + return { + userFunction: (): Promise => { + throw new Error(`Cannot compile function: ${e}`); + }, + isDisposed: () => { + return false; + }, + close: () => { + try { + if (isolate) { + for (const r of refs) { + r.release(); + } + context.release(); + if (!isolate.isDisposed) { + isolate.dispose(); + } + log.atDebug().log(`[${fullId}] isolate closed`); + } + } catch (e) { + log.atError().log(`[${fullId}] Error while closing isolate: ${e}`); + } + }, + }; + } +}; + +function wrap(connectionId: string, isolate: Isolate, context: Context, wrapper: Module, refs: Reference[]) { + const exported = wrapper.namespace; + + const ref = exported.getSync("wrappedFunctionChain", { + reference: true, + }); + if (!ref || ref.typeof !== "function") { + throw new Error("Function not found. Please export wrappedFunctionChain function."); + } + const userFunction: ProfileFunctionWrapper = async ( + eventsProvider, + userProvider, + ctx + ): Promise => { + if (isolate.isDisposed) { + throw new RetryError("Isolate is disposed", { drop: true }); + } + const ctxCopy = new ExternalCopy(ctx); + + const udfTimeoutMs = parseNumber(process.env.UDF_TIMEOUT_MS, 60000); + let isTimeout = false; + const timer = setTimeout(() => { + isTimeout = true; + isolate.dispose(); + }, udfTimeoutMs); + const eventsProviderRef = new Reference(async () => { + const ev = await eventsProvider(); + if (typeof ev !== "undefined") { + return JSON.stringify(ev); + } else { + return undefined; + } + }); + const userProviderRef = new Reference(async () => { + return JSON.stringify(await userProvider()); + }); + + try { + const res = await ref.apply( + undefined, + [eventsProviderRef, userProviderRef, ctxCopy.copyInto({ release: true, transferIn: true })], + { + result: { promise: true, copy: true }, + } + ); + switch (typeof res) { + case "undefined": + case "string": + case "number": + case "boolean": + return undefined; + default: + return res as any; + } + } catch (e: any) { + if (isolate.isDisposed) { + if (isTimeout) { + throw new RetryError( + `[${connectionId}] Function execution took longer than ${udfTimeoutMs}ms. Isolate is disposed`, + { + drop: true, + } + ); + } else { + throw new RetryError( + `[${connectionId}] Function execution stopped probably due to high memory usage. Isolate is disposed.`, + { + drop: true, + } + ); + } + } + const m = e.message; + if (m.startsWith("{")) { + throw JSON.parse(m); + } + //log.atInfo().log(`ERROR name: ${e.name} message: ${e.message} json: ${e.stack}`); + throw e; + } finally { + eventsProviderRef.release(); + userProviderRef.release(); + clearTimeout(timer); + } + }; + return { + userFunction, + isDisposed: () => { + if (isolate) { + return isolate.isDisposed; + } + return true; + }, + close: () => { + try { + if (isolate) { + for (const r of refs) { + r.release(); + } + context.release(); + if (!isolate.isDisposed) { + isolate.dispose(); + } + log.atDebug().log(`[${connectionId}] isolate closed.`); + } + } catch (e) { + log.atError().log(`[${connectionId}] Error while closing isolate: ${e}`); + } + }, + }; +} + +function makeReference(refs: Reference[], obj: any): Reference { + const ref = new Reference(obj); + refs.push(ref); + return ref; +} + +export async function mergeUserTraits(events: AnalyticsServerEvent[], userId?: string): Promise { + const user = { traits: {}, profileId: events[0]?._profile_id, userId: userId || events[0]?.userId } as ProfileUser; + for await (const e of events) { + if (e.type === "identify") { + if (e.anonymousId) { + user.anonymousId = e.anonymousId; + } + if (e.traits) { + Object.assign(user.traits, e.traits); + } + } + } + return user; +} + +export type ProfileUDFTestRequest = { + id: string; + name: string; + version: number; + code: string | UDFWrapperResult; + events: AnalyticsServerEvent[]; + settings: { + variables: any; + destinationId: string; + tableName?: string; + [key: string]: any; + }; + store: Store | any; + workspaceId: string; + userAgent?: string; +}; + +export type ProfileUDFTestResponse = { + error?: { + message: string; + stack?: string; + name: string; + retryPolicy?: any; + }; + result: Profile; + store: any; + logs: logType[]; +}; + +export async function ProfileUDFTestRun( + { id, name, version, code, store, events, settings, userAgent, workspaceId }: ProfileUDFTestRequest, + connStore?: EntityStore +): Promise { + const logs: logType[] = []; + const { variables, tableName, destinationId } = settings; + let wrapper: UDFWrapperResult | undefined = undefined; + let realStore = false; + const user = await mergeUserTraits(events); + const userProvider = async () => user; + const iter = events[Symbol.iterator](); + const eventsProvider = async () => { + const iv = iter.next(); + if (!iv.done) { + return iv.value; + } else { + return undefined; + } + }; + try { + let storeImpl: TTLStore; + if ( + typeof store?.set === "function" && + typeof store?.get === "function" && + typeof store?.del === "function" && + typeof store.ttl === "function" + ) { + storeImpl = store; + realStore = true; + } else { + store = store || {}; + storeImpl = createMemoryStore(store); + } + + const eventsStore: EventsStore = { + log(connectionId: string, level: LogLevel, msg: Record) { + switch (msg.type) { + case "log-info": + case "log-warn": + case "log-debug": + case "log-error": + logs.push({ + message: + msg.message?.text + + (Array.isArray(msg.message?.args) && msg.message.args.length > 0 + ? `, ${msg.message?.args.join(",")}` + : ""), + level: msg.type.replace("log-", ""), + timestamp: new Date(), + type: "log", + }); + break; + case "http-request": + let statusText; + if (msg.error) { + statusText = `${msg.error}`; + } else { + statusText = `${msg.statusText ?? ""}${msg.status ? `(${msg.status})` : ""}`; + } + logs.push({ + message: `${msg.method} ${msg.url} :: ${statusText}`, + level: msg.error ? "error" : "debug", + timestamp: new Date(), + type: "http", + data: { + body: msg.body, + headers: msg.headers, + response: msg.response, + }, + }); + } + }, + close() {}, + }; + const chainCtx: FunctionChainContext = { + store: storeImpl, + query: async (conId: string, query: string, params: any) => { + if (connStore) { + return warehouseQuery(workspaceId, connStore, conId, query, params); + } else { + throw new Error("Connection store is not provided"); + } + }, + fetch: makeFetch("functionsDebugger", eventsStore, "info"), + log: makeLog("functionsDebugger", eventsStore), + }; + const d = new Date(); + d.setDate(d.getDate() + 1); + const funcCtx: FunctionContext = { + function: { + type: "profile", + id: id, + debugTill: d, + }, + props: variables, + }; + if (typeof code === "string") { + wrapper = ProfileUDFWrapper(id, version, id, chainCtx, funcCtx, [{ id, name, code }]); + } else { + wrapper = code; + } + const result = await wrapper?.userFunction(eventsProvider, userProvider, funcCtx); + const profile = { + profile_id: result?.profileId || result?.["profile_id"] || user.profileId || user.userId, + destination_id: result?.destinationId || result?.["destination_id"] || destinationId, + table_name: result?.tableName || result?.["table_name"] || tableName || "profiles", + traits: { ...user.traits, ...result?.traits }, + version: version, + updated_at: new Date(), + }; + return { + result: profile, + store: !realStore ? memoryStoreDump(store) : {}, + logs, + }; + } catch (e: any) { + return { + error: { + message: e.message, + stack: e.stack, + name: e.name, + retryPolicy: e.retryPolicy, + }, + result: { + profile_id: user.profileId || user.userId, + destination_id: destinationId, + table_name: tableName, + traits: {}, + updated_at: new Date(), + }, + store: !realStore && store ? memoryStoreDump(store) : {}, + logs, + }; + } finally { + wrapper?.close(); + } +} diff --git a/libs/core-functions/src/functions/lib/store.ts b/libs/core-functions/src/functions/lib/store.ts new file mode 100644 index 000000000..9570c021c --- /dev/null +++ b/libs/core-functions/src/functions/lib/store.ts @@ -0,0 +1,372 @@ +import { SetOpts, TTLStore } from "@jitsu/protocols/functions"; +import type { Redis } from "ioredis"; +import parse from "parse-duration"; +import { MongoClient, ReadPreference, Collection } from "mongodb"; +import { RetryError } from "@jitsu/functions-lib"; +import { getLog, Singleton } from "juava"; +import { StoreMetrics } from "./index"; + +export const defaultTTL = 60 * 60 * 24 * 31; // 31 days +export const maxAllowedTTL = 2147483647; // max allowed value for ttl in redis (68years) + +const log = getLog("store"); + +function getTtlSec(opts?: SetOpts): number { + let seconds = defaultTTL; + if (typeof opts === "number") { + seconds = Math.ceil(opts); + } else if (typeof opts === "string") { + if (opts.toLowerCase() === "inf") { + seconds = -1; + } else { + try { + seconds = Math.ceil(parse(opts, "s") || defaultTTL); + } catch (e) {} + } + } else if (typeof opts === "object") { + return getTtlSec(opts.ttl); + } + return Math.min(seconds, maxAllowedTTL); +} + +function success(namespace: string, operation: "get" | "set" | "del" | "ttl", metrics?: StoreMetrics) { + if (metrics) { + metrics.storeStatus(namespace, operation, "success"); + } +} + +function storeErr( + namespace: string, + operation: "get" | "set" | "del" | "ttl", + err: any, + text: string, + metrics?: StoreMetrics +) { + log.atError().log(`${text}: ${err.message}`); + if (metrics) { + metrics.storeStatus(namespace, operation, "error"); + } + if ((err.message ?? "").includes("timed out")) { + return new RetryError(text + ": Timed out."); + } + return new RetryError(text + ": " + err.message); +} + +export const createRedisStore = (namespace: string, redisClient: Redis, metrics?: StoreMetrics): TTLStore => ({ + get: async (key: string) => { + try { + const res = await redisClient.get(`store:${namespace}:${key}`); + success(namespace, "get", metrics); + return res ? JSON.parse(res) : undefined; + } catch (err: any) { + throw storeErr(namespace, "get", err, `Error getting key ${key} from redis store ${namespace}`, metrics); + } + }, + getWithTTL: async (key: string) => { + try { + const res = await redisClient.get(`store:${namespace}:${key}`); + if (!res) { + return undefined; + } + const ttl = await redisClient.ttl(`store:${namespace}:${key}`); + success(namespace, "get", metrics); + return { value: JSON.parse(res), ttl }; + } catch (err: any) { + throw storeErr(namespace, "get", err, `Error getting key ${key} from redis store ${namespace}`, metrics); + } + }, + set: async (key: string, obj: any, opts?: SetOpts) => { + try { + const ttl = getTtlSec(opts); + if (ttl >= 0) { + await redisClient.set(`store:${namespace}:${key}`, JSON.stringify(obj), "EX", ttl); + } else { + await redisClient.set(`store:${namespace}:${key}`, JSON.stringify(obj)); + } + success(namespace, "set", metrics); + } catch (err: any) { + throw storeErr(namespace, "set", err, `Error setting key ${key} from redis store ${namespace}`, metrics); + } + }, + del: async (key: string) => { + try { + await redisClient.del(`store:${namespace}:${key}`); + success(namespace, "del", metrics); + } catch (err: any) { + throw storeErr(namespace, "del", err, `Error deleting key ${key} from redis store ${namespace}`, metrics); + } + }, + ttl: async (key: string) => { + try { + const res = await redisClient.ttl(`store:${namespace}:${key}`); + success(namespace, "ttl", metrics); + return res; + } catch (err: any) { + throw storeErr(namespace, "ttl", err, `Error getting key ${key} from redis store ${namespace}`, metrics); + } + }, +}); + +interface StoreValue { + _id: string; + value: any; + expireAt: Date; +} + +const MongoCreatedCollections: Record> = {}; + +export const createMongoStore = ( + namespace: string, + mongo: Singleton, + useLocalCache: boolean, + fast: boolean, + metrics?: StoreMetrics +): TTLStore => { + const localCache: Record = {}; + const readOptions = fast ? { readPreference: ReadPreference.NEAREST } : {}; + const writeOptions = fast ? { writeConcern: { w: 1, journal: false } } : {}; + + const dbName = `persistent_store`; + + function getFromLocalCache(key: string): StoreValue | undefined { + if (!useLocalCache) { + return undefined; + } + return localCache[key]; + } + + async function ensureCollection(): Promise> { + let collection = MongoCreatedCollections[namespace]; + if (collection) { + return collection; + } + try { + const db = mongo().db(dbName); + + const col = db.collection(namespace); + const collStatus = await col + .aggregate([{ $collStats: { count: {} } }]) + .next() + .catch(e => {}); + if (collStatus) { + //collection already exists + MongoCreatedCollections[namespace] = col; + return col; + } + collection = await db.createCollection(namespace, { + clusteredIndex: { + key: { _id: 1 }, + unique: true, + }, + storageEngine: { wiredTiger: { configString: "block_compressor=zstd" } }, + }); + await collection.createIndex({ expireAt: 1 }, { expireAfterSeconds: 0 }); + MongoCreatedCollections[namespace] = collection; + return collection; + } catch (err) { + throw new Error(`Failed to create collection ${namespace}: ${err}`); + } + } + + return { + get: async (key: string) => { + try { + const res = + getFromLocalCache(key) || (await ensureCollection().then(c => c.findOne({ _id: key }, readOptions))); + success(namespace, "get", metrics); + return res ? res.value : undefined; + } catch (err: any) { + throw storeErr(namespace, "get", err, `Error getting key ${key} from mongo store ${namespace}`, metrics); + } + }, + getWithTTL: async (key: string) => { + try { + const res = + getFromLocalCache(key) || (await ensureCollection().then(c => c.findOne({ _id: key }, readOptions))); + if (!res) { + return undefined; + } + const ttl = res.expireAt ? Math.max(Math.floor((res.expireAt.getTime() - new Date().getTime()) / 1000), 0) : -1; + success(namespace, "get", metrics); + return { value: res.value, ttl }; + } catch (err: any) { + throw storeErr(namespace, "get", err, `Error getting key ${key} from mongo store ${namespace}`, metrics); + } + }, + set: async (key: string, obj: any, opts?: SetOpts) => { + try { + const colObj: any = { value: obj }; + const ttl = getTtlSec(opts); + if (ttl >= 0) { + const expireAt = new Date(); + expireAt.setSeconds(expireAt.getSeconds() + ttl); + colObj.expireAt = expireAt; + } + + await ensureCollection() + .then(c => + c.replaceOne({ _id: key }, colObj, { + upsert: true, + ...writeOptions, + }) + ) + .then(() => { + if (useLocalCache) { + localCache[key] = colObj; + } + }) + .then(() => { + success(namespace, "set", metrics); + }); + } catch (err: any) { + throw storeErr(namespace, "set", err, `Error setting key ${key} in mongo store ${namespace}`, metrics); + } + }, + del: async (key: string) => { + try { + await ensureCollection() + .then(c => c.deleteOne({ _id: key }, writeOptions)) + .then(() => { + if (useLocalCache) { + delete localCache[key]; + } + }); + success(namespace, "del", metrics); + } catch (err: any) { + throw storeErr(namespace, "del", err, `Error deleting key ${key} from mongo store ${namespace}`, metrics); + } + }, + ttl: async (key: string) => { + try { + const res = + getFromLocalCache(key) || (await ensureCollection().then(c => c.findOne({ _id: key }, readOptions))); + success(namespace, "ttl", metrics); + return res + ? res.expireAt + ? Math.max(Math.floor((res.expireAt.getTime() - new Date().getTime()) / 1000), 0) + : -1 + : -2; + } catch (err: any) { + throw storeErr(namespace, "ttl", err, `Error getting key ${key} from mongo store ${namespace}`, metrics); + } + }, + }; +}; + +export const createMultiStore = (newStore: TTLStore, oldStore: TTLStore): TTLStore => { + return { + get: async (key: string) => { + const res = await newStore.get(key); + if (res) { + return res; + } + return await oldStore.get(key); + }, + set: async (key: string, obj: any, opts?: SetOpts) => { + await newStore.set(key, obj, opts); + }, + del: async (key: string) => { + await newStore.del(key); + await oldStore.del(key); + }, + ttl: async (key: string) => { + const res = await newStore.ttl(key); + if (res >= -1) { + return res; + } + return await oldStore.ttl(key); + }, + getWithTTL: async (key: string) => { + const res = await newStore.getWithTTL(key); + if (res) { + return res; + } + return await oldStore.getWithTTL(key); + }, + }; +}; + +export const createDummyStore = (): TTLStore => ({ + get: async (key: string) => { + return undefined; + }, + set: async (key: string, obj: any, opts) => {}, + del: async (key: string) => {}, + ttl: async (key: string) => { + return -2; + }, + getWithTTL: async (key: string) => { + return undefined; + }, +}); + +export const createMemoryStore = (store: any): TTLStore => ({ + get: async (key: string) => { + const val = store[key]; + if (val?.expireAt) { + if (val.expireAt < new Date().getTime()) { + delete store[key]; + return undefined; + } + return val.obj; + } + return val; + }, + set: async (key: string, obj: any, opts) => { + store[key] = { + obj, + expireAt: new Date().getTime() + getTtlSec(opts) * 1000, + }; + }, + del: async (key: string) => { + delete store[key]; + }, + ttl: async (key: string) => { + const val = store[key]; + if (!val) { + return -2; + } + const diff = (val.expireAt - new Date().getTime()) / 1000; + if (diff < 0) { + delete store[key]; + return -2; + } + return Math.floor(diff); + }, + getWithTTL: async (key: string) => { + const val = store[key]; + if (!val) { + return undefined; + } + const diff = (val.expireAt - new Date().getTime()) / 1000; + if (diff < 0) { + delete store[key]; + return undefined; + } + return { + value: val.obj, + ttl: Math.floor(diff), + }; + }, +}); + +export const memoryStoreDump = (store: any): any => { + const dt = new Date().getTime(); + return Object.entries(store as Record) + .map(([k, v]) => { + if (v?.expireAt) { + if (v.expireAt < dt) { + return null; + } + return [k, v.obj]; + } + return [k, v]; + }) + .filter(v => v !== null) + .reduce((prev, cur) => { + if (cur) { + prev[cur[0]] = cur[1]; + } + return prev; + }, {}); +}; diff --git a/libs/core-functions/src/functions/lib/strings.ts b/libs/core-functions/src/functions/lib/strings.ts new file mode 100644 index 000000000..b681d5bb3 --- /dev/null +++ b/libs/core-functions/src/functions/lib/strings.ts @@ -0,0 +1,3 @@ +export function idToSnakeCaseRegex(id: string) { + return id.replace(/((?<=[a-zA-Z0-9])[A-Z])/g, "_$1").toLowerCase(); +} diff --git a/libs/core-functions/src/functions/lib/ua.ts b/libs/core-functions/src/functions/lib/ua.ts new file mode 100644 index 000000000..d4f274b75 --- /dev/null +++ b/libs/core-functions/src/functions/lib/ua.ts @@ -0,0 +1,30 @@ +import { UserAgent } from "@jitsu/protocols/functions"; +import omit from "lodash/omit"; +import uaParser from "@amplitude/ua-parser-js"; +import NodeCache from "node-cache"; + +const BotUAKeywords = ["bot", "spider", "headless", "crawler", "uptimia"]; +const uaCacheTTL = 60 * 10; // 10 min; +const uaCache = new NodeCache({ stdTTL: uaCacheTTL, checkperiod: 60, maxKeys: 1000, useClones: false }); + +export function parseUserAgent(userAgent?: string): UserAgent { + if (!userAgent) { + return {} as UserAgent; + } + const cached = uaCache.get(userAgent); + if (cached) { + uaCache.ttl(userAgent, uaCacheTTL); + return cached as UserAgent; + } + const uas = userAgent || ""; + const ua = omit(uaParser(uas), "ua") as UserAgent; + const lower = uas.toLowerCase(); + ua.bot = BotUAKeywords.some(keyword => lower.includes(keyword)); + if (ua.device) { + ua.device.type = ua.device.type || "desktop"; + } + try { + uaCache.set(userAgent, ua); + } catch (e) {} + return ua; +} diff --git a/libs/core-functions/src/functions/lib/udf-wrapper-code.ts b/libs/core-functions/src/functions/lib/udf-wrapper-code.ts new file mode 100644 index 000000000..5da670997 --- /dev/null +++ b/libs/core-functions/src/functions/lib/udf-wrapper-code.ts @@ -0,0 +1,605 @@ +import { DropRetryErrorName, RetryErrorName } from "@jitsu/functions-lib"; + +export const functionsLibCode = `const DropRetryErrorName = "Drop & RetryError"; +const RetryErrorName = "RetryError"; +const TableNameParameter = "JITSU_TABLE_NAME"; +class RetryError extends Error { + constructor(message, options) { + super(message); + this.name = options?.drop ? "${DropRetryErrorName}" : "${RetryErrorName}"; + } +} + +function removeUndefined(param) { + if (Array.isArray(param)) { + return param.map(removeUndefined); + } else if (typeof param === "object" && param !== null) { + for (const [key, value] of Object.entries(param)) { + switch (typeof value) { + case "undefined": + delete param[key]; + break; + case "object": + if (value !== null) { + removeUndefined(value); + } + break; + } + } + } + return param; +} + +function transfer(target, source, omit) { + if (typeof source !== "object") { + return; + } + for (const [k, v] of Object.entries(source)) { + if (!omit || !omit.includes(k)) { + target[k] = v; + } + } +} + +function anonymizeIp(ip) { + if (!ip) { + return; + } + const parts = ip.split("."); + if (parts.length === 4) { + return \`\${parts[0]}.\${parts[1]}.\${parts[2]}.0\`; + } +} + +function toJitsuClassic(event, ctx) { + let url = undefined; + const analyticsContext = event.context || {}; + const urlStr = analyticsContext.page?.url || event.properties?.url; + const click_id = {}; + transfer(click_id, analyticsContext.clientIds, ["ga4", "fbp", "fbc"]); + let ids = {}; + if (Object.keys(analyticsContext.clientIds || {}).length > 0) { + ids = removeUndefined({ + ga: analyticsContext.clientIds.ga4?.clientId, + fbp: analyticsContext.clientIds.fbp, + fbc: analyticsContext.clientIds.fbc, + }); + } + const geo = analyticsContext.geo || {}; + const ua = ctx?.ua || {}; + const user = removeUndefined({ + id: event.userId, + anonymous_id: event.anonymousId, + email: analyticsContext.traits?.email || event.traits?.email || undefined, + name: analyticsContext.traits?.name || event.traits?.name || undefined, + }); + transfer(user, analyticsContext.traits, ["email", "name"]); + transfer(user, event.traits, ["email", "name"]); + const classic = { + [TableNameParameter]: event[TableNameParameter], + anon_ip: analyticsContext.ip ? anonymizeIp(analyticsContext.ip) : undefined, + api_key: event.writeKey || "", + click_id: Object.keys(click_id).length > 0 ? click_id : undefined, + doc_encoding: analyticsContext.page?.encoding ?? event.properties?.encoding, + doc_host: analyticsContext.page?.host ?? event.properties?.host, + doc_path: analyticsContext.page?.path ?? event.properties?.path, + doc_search: analyticsContext.page?.search ?? event.properties?.search, + eventn_ctx_event_id: event.messageId, + event_type: event.event || event.type, + local_tz_offset: analyticsContext.page?.timezoneOffset ?? event.properties?.timezoneOffset, + page_title: analyticsContext.page?.title, + referer: analyticsContext.page?.referrer, + screen_resolution: + Object.keys(analyticsContext.screen || {}).length > 0 + ? Math.max(analyticsContext.screen.width || 0) + "x" + Math.max(analyticsContext.screen.height || 0) + : undefined, + source_ip: analyticsContext.ip, + src: event.properties?.src || "jitsu", + url: urlStr, + user: Object.keys(user).length > 0 ? user : undefined, + location: + Object.keys(geo).length > 0 + ? { + city: geo.city?.name, + continent: geo.continent?.code, + country: geo.country?.code, + country_name: geo.country?.name, + latitude: geo.location?.latitude, + longitude: geo.location?.longitude, + region: geo.region?.code, + zip: geo.postalCode?.code, + timezone: geo.location?.timezone, + autonomous_system_number: geo.provider?.as?.num, + autonomous_system_organization: geo.provider?.as?.name, + isp: geo.provider?.isp, + domain: geo.provider?.domain, + } + : undefined, + ids: Object.keys(ids).length > 0 ? ids : undefined, + parsed_ua: + event.parsed_ua || + (Object.keys(ua).length > 0 + ? { + os_family: ua.os?.name, + os_version: ua.os?.version, + ua_family: ua.browser?.name, + ua_version: ua.browser?.version, + device_brand: ua.device?.vendor, + device_type: ua.device?.type, + device_model: ua.device?.model, + bot: ua.bot, + } + : undefined), + user_agent: analyticsContext.userAgent, + user_language: analyticsContext.locale, + utc_time: event.timestamp, + _timestamp: event.receivedAt, + utm: analyticsContext.campaign, + vp_size: + Object.keys(analyticsContext.screen || {}).length > 0 + ? Math.max(analyticsContext.screen.innerWidth || 0) + "x" + Math.max(analyticsContext.screen.innerHeight || 0) + : undefined, + }; + if (event.type === "track") { + transfer(classic, event.properties); + } else { + transfer(classic, event.properties, [ + "url", + "title", + "referrer", + "search", + "host", + "path", + "width", + "height", + ]); + } + + return removeUndefined(classic); +} + +function fromJitsuClassic(event) { + let type = "track"; + let eventName = undefined; + switch ((event.event_type ?? "").toLowerCase()) { + case "pageview": + case "page_view": + case "page": + type = "page"; + eventName = event.event_type; + break; + case "identify": + type = "identify"; + break; + case "screen": + type = "screen"; + break; + case "group": + type = "group"; + break; + case "alias": + type = "alias"; + break; + default: + type = "track"; + eventName = event.event_type; + break; + } + const clientIds = + Object.keys(event.ids || event.click_id || {}).length > 0 + ? { + ga4: event.ids?.ga + ? { + clientId: event.ids.ga, + } + : undefined, + fbp: event.ids?.fbp, + fbc: event.ids?.fbc, + ...event.click_id, + } + : undefined; + const loc = event.location || {}; + const geo = + Object.keys(loc).length > 0 + ? { + city: { + name: loc.city, + }, + continent: { + code: loc.continent, + }, + country: { + code: loc.country, + name: loc.country_name, + }, + location: { + latitude: loc.latitude, + longitude: loc.longitude, + timezone: loc.timezone, + }, + region: { + code: loc.region, + }, + postalCode: { + code: loc.zip, + }, + provider: { + as: { + num: loc.autonomous_system_number, + name: loc.autonomous_system_organization, + }, + isp: loc.isp, + domain: loc.domain, + }, + } + : undefined; + const traits = {}; + transfer(traits, event.user, ["id", "anonymous_id"]); + const properties = {}; + transfer(properties, event, [ + TableNameParameter, + "anon_ip", + "api_key", + "click_id", + "doc_encoding", + "doc_host", + "doc_path", + "doc_search", + "eventn_ctx_event_id", + "event_type", + "local_tz_offset", + "page_title", + "referer", + "screen_resolution", + "source_ip", + "url", + "user", + "location", + "parsed_ua", + "user_agent", + "user_language", + "utc_time", + "_timestamp", + "utm", + "vp_size", + ]); + if (type === "page") { + properties.url = event.url; + properties.title = event.page_title; + properties.referrer = event.referer; + properties.search = event.doc_search; + properties.host = event.doc_host; + properties.path = event.doc_path; + properties.width = parseInt(event.vp_size?.split("x")[0]); + properties.height = parseInt(event.vp_size?.split("x")[1]); + } + const screen = {}; + const sr = event.screen_resolution?.split("x"); + if (sr?.length === 2) { + screen.width = parseInt(sr[0]); + screen.height = parseInt(sr[1]); + } + const vs = event.vp_size?.split("x"); + if (vs?.length === 2) { + screen.innerWidth = parseInt(vs[0]); + screen.innerHeight = parseInt(vs[1]); + } + + return removeUndefined({ + [TableNameParameter]: event[TableNameParameter], + messageId: event.eventn_ctx_event_id, + userId: event.user?.id, + anonymousId: event.user?.anonymous_id, + timestamp: event.utc_time, + receivedAt: event._timestamp, + writeKey: event.api_key, + type, + event: eventName, + context: { + ip: event.source_ip, + locale: event.user_language, + userAgent: event.user_agent, + page: { + url: event.url, + title: event.page_title, + referrer: event.referer, + search: event.doc_search, + host: event.doc_host, + path: event.doc_path, + encoding: event.doc_encoding, + timezoneOffset: event.local_tz_offset, + }, + screen: Object.keys(screen).length > 0 ? screen : undefined, + clientIds, + campaign: event.utm, + traits, + geo, + }, + properties, + traits: type === "identify" || type === "group" ? traits : undefined, + }); +} + +export { DropRetryErrorName, RetryError, RetryErrorName, TableNameParameter, fromJitsuClassic, toJitsuClassic };`; + +export const chainWrapperCode = `//** @UDF_FUNCTIONS_IMPORT **// +import { + TableNameParameter, toJitsuClassic, fromJitsuClassic, DropRetryErrorName, + RetryError, + RetryErrorName, +} from "@jitsu/functions-lib"; + +global.RetryError = RetryError; +global.TableNameParameter = TableNameParameter; +global.toJitsuClassic = toJitsuClassic; +global.fromJitsuClassic = fromJitsuClassic; + +export function checkError(chainRes) { + let errObj = undefined; + for (const el of chainRes.execLog) { + const error = el.error; + if (error) { + if (!errObj && (error.name === DropRetryErrorName || error.name === RetryErrorName)) { + errObj = { + name: error.name, + message: error.message, + stack: error.stack, + retryPolicy: error.retryPolicy, + event: chainRes.events, + functionId: error.functionId || el.functionId + } + } else { + _jitsu_log.error.apply(undefined, [{ + function: { + ..._jitsu_funcCtx.function, + id: error.functionId || el.functionId + } + }, \`Function execution failed\`, error.name, error.message], {arguments: {copy: true}}); + } + } + } + if (errObj) { + throw new Error(JSON.stringify(errObj)); + } +} + +function deepCopy(o) { + if (typeof o !== "object") { + return o + } + if (!o) { + return o + } + + // https://jsperf.com/deep-copy-vs-json-stringify-json-parse/25 + if (Array.isArray(o)) { + const newO = [] + for (let i = 0; i < o.length; i += 1) { + const v = o[i] + newO[i] = !v || typeof v !== "object" ? v : deepCopy(v) + } + return newO + } + + const newO = {} + for (const [k, v] of Object.entries(o)) { + newO[k] = !v || typeof v !== "object" ? v : deepCopy(v) + } + return newO +} + +function isDropResult(result) { + return result === "drop" || (Array.isArray(result) && result.length === 0) || result === null || result === false; +} + +async function runSingle( + f, + event, + ctx +) { + let execLog = []; + let events = []; + let result = undefined; + try { + result = await f.f(event, ctx); + } catch (err) { + if (err.name === DropRetryErrorName) { + result = "drop"; + } + if (f.meta?.retryPolicy) { + err.retryPolicy = f.meta.retryPolicy; + } + execLog = [{ + functionId: f.id, + error: err, + }]; + } + if (!isDropResult(result)) { + events = result; + } + return {events, execLog}; +} + +async function runChain( + chain, + event, + ctx +) { + const execLog = []; + const fastFunctions = !!ctx.connection?.options?.fastFunctions + let events = [event]; + for (let k = 0; k < chain.length; k++) { + const f = chain[k]; + + const newEvents = []; + for (let i = 0; i < events.length; i++) { + const event = events[i]; + let result = undefined; + try { + result = await f.f(fastFunctions ? event : deepCopy(event), ctx); + + if (k < chain.length - 1 && Array.isArray(result) && result.length > 1) { + const l = result.length; + result = undefined; + throw new Error("Got " + l + " events as result of function #" + (k + 1) + " of " + chain.length + ". Only the last function in a chain is allowed to multiply events."); + } + } catch (err) { + if (err.name === DropRetryErrorName) { + result = "drop"; + } + if (f.meta?.retryPolicy) { + err.retryPolicy = f.meta.retryPolicy; + } + execLog.push({ + functionId: f.id, + error: err, + }); + } + if (!isDropResult(result)) { + if (result) { + if (Array.isArray(result)) { + newEvents.push(...result); + } else { + newEvents.push(result); + } + } else { + newEvents.push(event); + } + } + } + events = newEvents; + if (events.length === 0) { + break; + } + } + return {events, execLog}; +} + +const wrappedFunctionChain = async function (event, ctx) { + let chain = []; + //** @UDF_FUNCTIONS_CHAIN **// + + const chainRes = chain.length === 1 ? await runSingle(chain[0], event, ctx) : await runChain(chain, event, ctx); + checkError(chainRes); + if (Array.isArray(chainRes.events) && chainRes.events.length === 1) { + return chainRes.events[0]; + } + return chainRes.events; +}; + +const wrappedUserFunction = (id, f, funcCtx) => { + + const log = { + info: (...args) => { + _jitsu_log.info.apply(undefined, [funcCtx, ...args], {arguments: {copy: true}}); + }, + error: (...args) => { + _jitsu_log.error.apply(undefined, [funcCtx, ...args], {arguments: {copy: true}}); + }, + warn: (...args) => { + _jitsu_log.warn.apply(undefined, [funcCtx, ...args], {arguments: {copy: true}}); + }, + debug: (...args) => { + _jitsu_log.debug.apply(undefined, [funcCtx, ...args], {arguments: {copy: true}}); + }, + } + + const store = { + set: async (key, value, opts) => { + await _jitsu_store.set.apply(undefined, [key, value, opts], { + arguments: {copy: true}, + result: {promise: true} + }); + }, + del: async key => { + await _jitsu_store.del.apply(undefined, [key], { + arguments: {copy: true}, + result: {promise: true} + }); + }, + get: async key => { + const res = await _jitsu_store.get.apply(undefined, [key], { + arguments: {copy: true}, + result: {promise: true} + }); + return res ? JSON.parse(res) : undefined; + }, + ttl: async key => { + return await _jitsu_store.ttl.apply(undefined, [key], { + arguments: {copy: true}, + result: {promise: true} + }); + }, + } + + const getWarehouse = (warehouseId) => { + return { + query: async (query, opts) => { + return await _jitsu_query.apply(undefined, [warehouseId, query, opts], { + arguments: {copy: true}, + result: {promise: true, copy: true} + }); + }, + }; + } + + const fetch = async (url, opts, extras) => { + let res + if (extras) { + res = await _jitsu_fetch.apply(undefined, [url, opts, {ctx: funcCtx, event: extras.event}], { + arguments: {copy: true}, + result: {promise: true} + }); + } else { + res = await _jitsu_fetch.apply(undefined, [url, opts], { + arguments: {copy: true}, + result: {promise: true} + }); + } + const r = JSON.parse(res); + + return { + ...r, + json: async () => { + return JSON.parse(r.body); + }, + text: async () => { + return r.body; + }, + arrayBuffer: async () => { + throw new Error("Method 'arrayBuffer' is not implemented"); + }, + blob: async () => { + throw new Error("Method 'blob' is not implemented"); + }, + formData: async () => { + throw new Error("Method 'formData' is not implemented"); + }, + clone: async () => { + throw new Error("Method 'clone' is not implemented"); + }, + }; + } + + return async function (event, c) { + const fetchLogEnabled = _jitsu_fetch_log_level !== "debug" || (funcCtx.function.debugTill && funcCtx.function.debugTill > new Date()); + let ftch = fetch + if (fetchLogEnabled) { + ftch = async(url, opts) => { + return fetch(url, opts, {event}); + } + } + const ctx = { + ...c, + props: funcCtx.props, + log, + getWarehouse, + store, + fetch: ftch, + }; + return await f(event, ctx); + } +}; + +export {wrappedFunctionChain}; +`; diff --git a/libs/core-functions/src/functions/lib/udf-wrapper-code.txtjs b/libs/core-functions/src/functions/lib/udf-wrapper-code.txtjs new file mode 100644 index 000000000..84ec6e414 --- /dev/null +++ b/libs/core-functions/src/functions/lib/udf-wrapper-code.txtjs @@ -0,0 +1,280 @@ +//** @UDF_FUNCTIONS_IMPORT **// +import { + TableNameParameter, toJitsuClassic, fromJitsuClassic, DropRetryErrorName, + RetryError, + RetryErrorName, +} from "@jitsu/functions-lib"; + +global.RetryError = RetryError; +global.TableNameParameter = TableNameParameter; +global.toJitsuClassic = toJitsuClassic; +global.fromJitsuClassic = fromJitsuClassic; + +export function checkError(chainRes) { + let errObj = undefined; + for (const el of chainRes.execLog) { + const error = el.error; + if (error) { + if (!errObj && (error.name === DropRetryErrorName || error.name === RetryErrorName)) { + errObj = { + name: error.name, + message: error.message, + stack: error.stack, + retryPolicy: error.retryPolicy, + event: chainRes.events, + functionId: error.functionId || el.functionId + } + } else { + _jitsu_log.error.apply(undefined, [{ + function: { + ..._jitsu_funcCtx.function, + id: error.functionId || el.functionId + } + }, `Function execution failed`, error.name, error.message], {arguments: {copy: true}}); + } + } + } + if (errObj) { + throw new Error(JSON.stringify(errObj)); + } +} + +function deepCopy(o) { + if (typeof o !== "object") { + return o + } + if (!o) { + return o + } + + // https://jsperf.com/deep-copy-vs-json-stringify-json-parse/25 + if (Array.isArray(o)) { + const newO = [] + for (let i = 0; i < o.length; i += 1) { + const v = o[i] + newO[i] = !v || typeof v !== "object" ? v : deepCopy(v) + } + return newO + } + + const newO = {} + for (const [k, v] of Object.entries(o)) { + newO[k] = !v || typeof v !== "object" ? v : deepCopy(v) + } + return newO +} + +function isDropResult(result) { + return result === "drop" || (Array.isArray(result) && result.length === 0) || result === null || result === false; +} + +async function runSingle( + f, + event, + ctx +) { + let execLog = []; + let events = []; + let result = undefined; + try { + result = await f.f(event, ctx); + } catch (err) { + if (err.name === DropRetryErrorName) { + result = "drop"; + } + if (f.meta?.retryPolicy) { + err.retryPolicy = f.meta.retryPolicy; + } + execLog = [{ + functionId: f.id, + error: err, + }]; + } + if (!isDropResult(result)) { + events = result; + } + return {events, execLog}; +} + +async function runChain( + chain, + event, + ctx +) { + const execLog = []; + const fastFunctions = !!ctx.connection?.options?.fastFunctions + let events = [event]; + for (let k = 0; k < chain.length; k++) { + const f = chain[k]; + + const newEvents = []; + for (let i = 0; i < events.length; i++) { + const event = events[i]; + let result = undefined; + try { + result = await f.f(fastFunctions ? event : deepCopy(event), ctx); + + if (k < chain.length - 1 && Array.isArray(result) && result.length > 1) { + const l = result.length; + result = undefined; + throw new Error("Got " + l + " events as result of function #" + (k + 1) + " of " + chain.length + ". Only the last function in a chain is allowed to multiply events."); + } + } catch (err) { + if (err.name === DropRetryErrorName) { + result = "drop"; + } + if (f.meta?.retryPolicy) { + err.retryPolicy = f.meta.retryPolicy; + } + execLog.push({ + functionId: f.id, + error: err, + }); + } + if (!isDropResult(result)) { + if (result) { + if (Array.isArray(result)) { + newEvents.push(...result); + } else { + newEvents.push(result); + } + } else { + newEvents.push(event); + } + } + } + events = newEvents; + if (events.length === 0) { + break; + } + } + return {events, execLog}; +} + +const wrappedFunctionChain = async function (event, ctx) { + let chain = []; + //** @UDF_FUNCTIONS_CHAIN **// + + const chainRes = chain.length === 1 ? await runSingle(chain[0], event, ctx) : await runChain(chain, event, ctx); + checkError(chainRes); + if (Array.isArray(chainRes.events) && chainRes.events.length === 1) { + return chainRes.events[0]; + } + return chainRes.events; +}; + +const wrappedUserFunction = (id, f, funcCtx) => { + + const log = { + info: (...args) => { + _jitsu_log.info.apply(undefined, [funcCtx, ...args], {arguments: {copy: true}}); + }, + error: (...args) => { + _jitsu_log.error.apply(undefined, [funcCtx, ...args], {arguments: {copy: true}}); + }, + warn: (...args) => { + _jitsu_log.warn.apply(undefined, [funcCtx, ...args], {arguments: {copy: true}}); + }, + debug: (...args) => { + _jitsu_log.debug.apply(undefined, [funcCtx, ...args], {arguments: {copy: true}}); + }, + } + + const store = { + set: async (key, value, opts) => { + await _jitsu_store.set.apply(undefined, [key, value, opts], { + arguments: {copy: true}, + result: {promise: true} + }); + }, + del: async key => { + await _jitsu_store.del.apply(undefined, [key], { + arguments: {copy: true}, + result: {promise: true} + }); + }, + get: async key => { + const res = await _jitsu_store.get.apply(undefined, [key], { + arguments: {copy: true}, + result: {promise: true} + }); + return res ? JSON.parse(res) : undefined; + }, + ttl: async key => { + return await _jitsu_store.ttl.apply(undefined, [key], { + arguments: {copy: true}, + result: {promise: true} + }); + }, + } + + const getWarehouse = (warehouseId) => { + return { + query: async (query, opts) => { + return await _jitsu_query.apply(undefined, [warehouseId, query, opts], { + arguments: {copy: true}, + result: {promise: true, copy: true} + }); + }, + }; + } + + const fetch = async (url, opts, extras) => { + let res + if (extras) { + res = await _jitsu_fetch.apply(undefined, [url, opts, {ctx: funcCtx, event: extras.event}], { + arguments: {copy: true}, + result: {promise: true} + }); + } else { + res = await _jitsu_fetch.apply(undefined, [url, opts], { + arguments: {copy: true}, + result: {promise: true} + }); + } + const r = JSON.parse(res); + + return { + ...r, + json: async () => { + return JSON.parse(r.body); + }, + text: async () => { + return r.body; + }, + arrayBuffer: async () => { + throw new Error("Method 'arrayBuffer' is not implemented"); + }, + blob: async () => { + throw new Error("Method 'blob' is not implemented"); + }, + formData: async () => { + throw new Error("Method 'formData' is not implemented"); + }, + clone: async () => { + throw new Error("Method 'clone' is not implemented"); + }, + }; + } + + return async function (event, c) { + const fetchLogEnabled = _jitsu_fetch_log_level !== "debug" || (funcCtx.function.debugTill && funcCtx.function.debugTill > new Date()); + let ftch = fetch + if (fetchLogEnabled) { + ftch = async(url, opts) => { + return fetch(url, opts, {event}); + } + } + const ctx = { + ...c, + props: funcCtx.props, + log, + getWarehouse, + store, + fetch: ftch, + }; + return await f(event, ctx); + } +}; + +export {wrappedFunctionChain}; diff --git a/libs/core-functions/src/functions/lib/udf_wrapper.ts b/libs/core-functions/src/functions/lib/udf_wrapper.ts new file mode 100644 index 000000000..0e4d96a84 --- /dev/null +++ b/libs/core-functions/src/functions/lib/udf_wrapper.ts @@ -0,0 +1,524 @@ +import { getLog, LogLevel, parseNumber, sanitize, stopwatch } from "juava"; +import { Context, ExternalCopy, Isolate, Module, Reference } from "isolated-vm"; +import { EventContext, FetchOpts, FuncReturn, Store, TTLStore } from "@jitsu/protocols/functions"; +import { AnalyticsServerEvent } from "@jitsu/protocols/analytics"; + +import { + createMemoryStore, + EnrichedConnectionConfig, + EntityStore, + EventsStore, + FunctionChainContext, + FunctionContext, + isDropResult, + makeFetch, + makeLog, + memoryStoreDump, + warehouseQuery, +} from "../../index"; +import { chainWrapperCode, functionsLibCode } from "./udf-wrapper-code"; +import { parseUserAgent } from "./ua"; +import { RetryError } from "@jitsu/functions-lib"; +import { JitsuFunctionWrapper } from "./index"; +import { clearTimeout } from "node:timers"; +import * as crypto from "node:crypto"; +import { cryptoCode } from "./crypto-code"; + +const log = getLog("udf-wrapper"); + +export type logType = { + message: string; + level: string; + timestamp: Date; + type: string; + data?: any; +}; + +export type UDFWrapperResult = { + userFunction: JitsuFunctionWrapper; + isDisposed: () => boolean; + close: () => void; +}; + +export type UDFFunction = { + id: string; + name: string; + code: string; +}; + +export const UDFWrapper = ( + connectionId: string, + chainCtx: FunctionChainContext, + funcCtx: FunctionContext, + functions: UDFFunction[] +): UDFWrapperResult => { + log.atDebug().log(`[CON:${connectionId}] Compiling ${functions.length} UDF functions`); + const sw = stopwatch(); + let isolate: Isolate; + let context: Context; + let refs: Reference[] = []; + try { + isolate = new Isolate({ memoryLimit: 128 }); + context = isolate.createContextSync(); + const jail = context.global; + + // This make the global object available in the context as 'global'. We use 'derefInto()' here + // because otherwise 'global' would actually be a Reference{} object in the new isolate. + jail.setSync("global", jail.derefInto()); + jail.setSync( + "process", + new ExternalCopy({ env: funcCtx.props || {} }).copyInto({ release: true, transferIn: true }) + ); + + jail.setSync("_jitsu_funcCtx", new ExternalCopy(funcCtx).copyInto({ release: true, transferIn: true })); + jail.setSync( + "_jitsu_log", + new ExternalCopy({ + info: makeReference(refs, chainCtx.log.info), + warn: makeReference(refs, chainCtx.log.warn), + debug: makeReference(refs, chainCtx.log.debug), + error: makeReference(refs, chainCtx.log.error), + }).copyInto({ release: true, transferIn: true }) + ); + jail.setSync("_jitsu_fetch_log_level", chainCtx.connectionOptions?.fetchLogLevel || "info"); + jail.setSync( + "_jitsu_crypto", + makeReference(refs, { + hash: crypto["hash"], + randomUUID: crypto.randomUUID, + randomBytes: crypto.randomBytes, + randomInt: crypto.randomInt, + }) + ); + jail.setSync("require", () => { + throw new Error("'require' is not supported. Please use 'import' instead"); + }); + jail.setSync("_jitsu_query", makeReference(refs, chainCtx.query)); + jail.setSync( + "_jitsu_fetch", + makeReference(refs, async (url: string, opts?: FetchOpts, extra?: any) => { + const res = await chainCtx.fetch(url, opts, extra); + const headers: any = {}; + res.headers.forEach((v, k) => { + headers[k] = v; + }); + const text = await res.text(); + const j = { + status: res.status, + statusText: res.statusText, + type: res.type, + redirected: res.redirected, + body: text, + bodyUsed: true, + url: res.url, + ok: res.ok, + headers: headers, + }; + return JSON.stringify(j); + }) + ); + jail.setSync( + "_jitsu_store", + new ExternalCopy({ + get: makeReference(refs, async (key: string) => { + const res = await chainCtx.store.get(key); + return JSON.stringify(res); + }), + set: makeReference(refs, chainCtx.store.set), + del: makeReference(refs, chainCtx.store.del), + ttl: makeReference(refs, async (key: string) => { + return await chainCtx.store.ttl(key); + }), + }).copyInto({ release: true, transferIn: true }) + ); + + const functionsLib = isolate.compileModuleSync(functionsLibCode, { + filename: "functions-lib.js", + }); + functionsLib.instantiateSync(context, (specifier: string): Module => { + throw new Error(`import is not allowed: ${specifier}`); + }); + const cryptoLib = isolate.compileModuleSync(cryptoCode, { + filename: "crypto.js", + }); + cryptoLib.instantiateSync(context, (specifier: string): Module => { + throw new Error(`import is not allowed: ${specifier}`); + }); + const udfModules: Record = {}; + for (let i = 0; i < functions.length; i++) { + const sw = stopwatch(); + const f = functions[i]; + log.atDebug().log(`[CON:${connectionId}]: [f:${f.id}] Compiling UDF function '${f.name}'`); + const moduleName = "f_" + sanitize(f.name, "_") + "_" + f.id; + const udf = isolate.compileModuleSync(f.code, { filename: moduleName + ".js" }); + udf.instantiateSync(context, (specifier: string) => { + if (specifier === "@jitsu/functions-lib") { + return functionsLib; + } else if (specifier === "crypto") { + return cryptoLib; + } + throw new Error(`import is not allowed: ${specifier}`); + }); + udfModules[moduleName] = udf; + log.atDebug().log(`[CON:${connectionId}] [f:${f.id}] UDF function '${f.name}' compiled in ${sw.elapsedPretty()}`); + } + + let code = chainWrapperCode.replace( + "//** @UDF_FUNCTIONS_IMPORT **//", + Object.keys(udfModules) + .map(m => `import * as ${m} from "${m}";\n`) + .join("") + ); + code = code.replace( + "//** @UDF_FUNCTIONS_CHAIN **//", + "chain = [" + + Object.keys(udfModules) + .map(m => { + const id = m.split("_").pop(); + return `{id: "${id}", meta: ${m}.config, f: wrappedUserFunction("${id}", ${m}.default, { props: _jitsu_funcCtx.props, function:{ ..._jitsu_funcCtx.function, id: "${id}"}})}`; + }) + .join(",") + + "];" + ); + const wrapper = isolate.compileModuleSync(code, { + filename: "jitsu-wrapper.js", + }); + wrapper.instantiateSync(context, (specifier: string) => { + const udf = udfModules[specifier]; + if (udf) { + //log.atInfo().log(`[${connectionId}] UDF function '${specifier}' is imported`); + return udf; + } + if (specifier === "@jitsu/functions-lib") { + return functionsLib; + } + throw new Error(`import is not allowed: ${specifier}`); + }); + wrapper.evaluateSync(); + const wrapperFunc = wrap(connectionId, isolate, context, wrapper, refs); + log.atInfo().log(`[CON:${connectionId}] ${functions.length} UDF functions compiled in: ${sw.elapsedPretty()}`); + return wrapperFunc; + } catch (e) { + return { + userFunction: (): FuncReturn => { + throw new Error(`Cannot compile function: ${e}`); + }, + isDisposed: () => { + return false; + }, + close: () => { + try { + if (isolate) { + for (const r of refs) { + r.release(); + } + context.release(); + if (!isolate.isDisposed) { + isolate.dispose(); + } + log.atDebug().log(`[${connectionId}] isolate closed`); + } + } catch (e) { + log.atError().log(`[${connectionId}] Error while closing isolate: ${e}`); + } + }, + }; + } +}; + +function wrap(connectionId: string, isolate: Isolate, context: Context, wrapper: Module, refs: Reference[]) { + const exported = wrapper.namespace; + + const ref = exported.getSync("wrappedFunctionChain", { + reference: true, + }); + if (!ref || ref.typeof !== "function") { + throw new Error("Function not found. Please export wrappedFunctionChain function."); + } + const userFunction: JitsuFunctionWrapper = async (event, ctx) => { + if (isolate.isDisposed) { + throw new RetryError("Isolate is disposed", { drop: true }); + } + const eventCopy = new ExternalCopy(event); + const ctxCopy = new ExternalCopy(ctx); + const udfTimeoutMs = parseNumber(process.env.UDF_TIMEOUT_MS, 5000); + let isTimeout = false; + const timer = setTimeout(() => { + isTimeout = true; + isolate.dispose(); + }, udfTimeoutMs); + try { + const res = await ref.apply( + undefined, + [ + eventCopy.copyInto({ release: true, transferIn: true }), + ctxCopy.copyInto({ release: true, transferIn: true }), + ], + { + result: { promise: true, copy: true }, + } + ); + switch (typeof res) { + case "undefined": + return undefined; + case "string": + case "number": + case "boolean": + return res; + default: + return res; + } + } catch (e: any) { + //console.error(e); + if (isolate.isDisposed) { + if (isTimeout) { + throw new RetryError( + `[${connectionId}] Function execution took longer than ${udfTimeoutMs}ms. Isolate is disposed`, + { + drop: true, + } + ); + } else { + throw new RetryError( + `[${connectionId}] Function execution stopped probably due to high memory usage. Isolate is disposed.`, + { + drop: true, + } + ); + } + } + const m = e.message; + if (m.startsWith("{")) { + throw JSON.parse(m); + } + //log.atInfo().log(`ERROR name: ${e.name} message: ${e.message} json: ${e.stack}`); + throw e; + } finally { + clearTimeout(timer); + } + }; + return { + userFunction, + isDisposed: () => { + if (isolate) { + return isolate.isDisposed; + } + return true; + }, + close: () => { + try { + if (isolate) { + for (const r of refs) { + r.release(); + } + context.release(); + if (!isolate.isDisposed) { + isolate.dispose(); + } + log.atDebug().log(`[${connectionId}] isolate closed.`); + } + } catch (e) { + log.atError().log(`[${connectionId}] Error while closing isolate: ${e}`); + } + }, + }; +} + +function makeReference(refs: Reference[], obj: any): Reference { + const ref = new Reference(obj); + refs.push(ref); + return ref; +} + +export type UDFTestRequest = { + functionId: string; + functionName: string; + code: string | UDFWrapperResult; + event: AnalyticsServerEvent; + variables: any; + store: Store | any; + workspaceId: string; + userAgent?: string; +}; + +export type UDFTestResponse = { + error?: { + message: string; + stack?: string; + name: string; + retryPolicy?: any; + }; + dropped?: boolean; + result: FuncReturn; + store: any; + logs: logType[]; +}; + +export async function UDFTestRun( + { functionId: id, functionName: name, code, store, event, variables, userAgent, workspaceId }: UDFTestRequest, + connStore?: EntityStore +): Promise { + const logs: logType[] = []; + let wrapper: UDFWrapperResult | undefined = undefined; + let realStore = false; + try { + const eventContext: EventContext = { + receivedAt: new Date(), + geo: { + country: { + code: "US", + name: "United States", + isEU: false, + }, + city: { + name: "New York", + }, + region: { + code: "NY", + name: "New York", + }, + location: { + latitude: 40.6808, + longitude: -73.9701, + }, + postalCode: { + code: "11238", + }, + }, + ua: parseUserAgent( + event.context?.userAgent || + userAgent || + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36" + ), + headers: {}, + source: { + id: "functionsDebugger-streamId", + name: "Functions Debugger Stream", + type: "browser", + }, + destination: { + id: "functionsDebugger-destinationId", + type: "clickhouse", + updatedAt: new Date(), + hash: "hash", + }, + connection: { + id: "functionsDebugger", + }, + allConnections: [ + { + id: "functionsDebugger", + destinationId: "functionsDebugger-destinationId", + destinationName: "Functions Debugger Destination", + type: "clickhouse", + mode: "batch", + }, + ], + workspace: { + id: workspaceId, + }, + }; + let storeImpl: TTLStore; + if ( + typeof store?.set === "function" && + typeof store?.get === "function" && + typeof store?.del === "function" && + typeof store.ttl === "function" + ) { + storeImpl = store; + realStore = true; + } else { + store = store || {}; + storeImpl = createMemoryStore(store); + } + + const eventsStore: EventsStore = { + log(connectionId: string, level: LogLevel, msg: Record) { + switch (msg.type) { + case "log-info": + case "log-warn": + case "log-debug": + case "log-error": + logs.push({ + message: + msg.message?.text + + (Array.isArray(msg.message?.args) && msg.message.args.length > 0 + ? `, ${msg.message?.args.join(",")}` + : ""), + level: msg.type.replace("log-", ""), + timestamp: new Date(), + type: "log", + }); + break; + case "http-request": + let statusText; + if (msg.error) { + statusText = `${msg.error}`; + } else { + statusText = `${msg.statusText ?? ""}${msg.status ? `(${msg.status})` : ""}`; + } + logs.push({ + message: `${msg.method} ${msg.url} :: ${statusText}`, + level: msg.error ? "error" : "debug", + timestamp: new Date(), + type: "http", + data: { + body: msg.body, + headers: msg.headers, + response: msg.response, + }, + }); + } + }, + close() {}, + }; + const chainCtx: FunctionChainContext = { + store: storeImpl, + query: async (conId: string, query: string, params: any) => { + if (connStore) { + return warehouseQuery(workspaceId, connStore, conId, query, params); + } else { + throw new Error("Connection store is not provided"); + } + }, + fetch: makeFetch("functionsDebugger", eventsStore, "info"), + log: makeLog("functionsDebugger", eventsStore), + }; + const d = new Date(); + d.setDate(d.getDate() + 1); + const funcCtx: FunctionContext = { + function: { + type: "udf", + id, + debugTill: d, + }, + props: variables, + }; + if (typeof code === "string") { + wrapper = UDFWrapper(id, chainCtx, funcCtx, [{ id, name, code }]); + } else { + wrapper = code; + } + const result = await wrapper?.userFunction(event, eventContext); + return { + dropped: isDropResult(result), + result: typeof result === "undefined" ? event : result, + store: !realStore ? memoryStoreDump(store) : {}, + logs, + }; + } catch (e: any) { + return { + error: { + message: e.message, + stack: e.stack, + name: e.name, + retryPolicy: e.retryPolicy, + }, + result: {}, + store: !realStore && store ? memoryStoreDump(store) : {}, + logs, + }; + } finally { + wrapper?.close(); + } +} diff --git a/libs/core-functions/src/functions/lib/warehouse-store.ts b/libs/core-functions/src/functions/lib/warehouse-store.ts new file mode 100644 index 000000000..ce376ef5c --- /dev/null +++ b/libs/core-functions/src/functions/lib/warehouse-store.ts @@ -0,0 +1,158 @@ +import { createClient } from "@clickhouse/client"; +import { getLog, getSingleton, newError, parseNumber, Singleton } from "juava"; +import { EntityStore } from "../../lib/entity-store"; +import { EnrichedConnectionConfig } from "../../lib/config-types"; +import { StoreMetrics } from "./index"; +import { Parser } from "node-sql-parser"; +const parser = new Parser(); + +const log = getLog("warehouseStore"); + +const warehouses: Record> = {}; + +const warehouseTimeoutMs = parseNumber(process.env.WAREHOUSE_TIMEOUT_MS, 1000); + +interface WarehouseStore { + query: (query: string, params?: Record) => Promise; + close?: () => void; +} + +export async function warehouseQuery( + workspaceId: string, + connStore: EntityStore, + conId: string, + query: string, + params: Record, + storeMetrics?: StoreMetrics +) { + const con = connStore.getObject(conId); + if (!con || con.workspaceId !== workspaceId) { + throw newError(`Warehouse with id ${conId} not found`); + } + if (con.type !== "clickhouse") { + throw newError(`Only Clickhouse warehouse is currently supported`); + } + let singleTon = warehouses[`${con.id}-${con.credentialsHash}`]; + if (!singleTon) { + singleTon = getSingleton( + `warehouse-${con.id}-${con.credentialsHash}`, + () => { + log.atInfo().log(`Connecting to ClickHouse warehouse of con: ${con.id}`); + const cl = getClickhouseWarehouse(workspaceId, conId, con.credentials, storeMetrics); + log.atInfo().log(`Connected successfully ClickHouse warehouse of con: ${con.id}`); + return cl; + }, + { + optional: true, + ttlSec: 60 * 60, + cleanupFunc: async client => { + log.atInfo().log(`Closing ClickHouse warehouse of con: ${con.id}`); + client.close?.(); + }, + } + ); + warehouses[`${con.id}-${con.credentialsHash}`] = singleTon; + } + const wh = await singleTon.waitInit(); + return await wh.query(query, params); +} + +const getClickhouseWarehouse = ( + workspaceId: string, + conId: string, + cred: any, + storeMetrics?: StoreMetrics +): WarehouseStore => { + const client = getClickhouseClient(cred); + return { + query: async (query: string, query_params?: Record) => { + let status = "success"; + let table = "_unknown_"; + const start = Date.now(); + try { + const splits = (parser.tableList(query)[0] || "_unknown_").split("::"); + table = splits[splits.length - 1]; + } catch (e) {} + try { + //replace named parameters in query (like :paramName or @param_name) with clickhouse positional parameters (like {paramName: Int32}) + query = query.replace(/[:@](\w+)/g, (match, paramName) => { + let t = "String"; + const param = query_params?.[paramName]; + switch (typeof param) { + case "number": + if (Number.isInteger(param)) { + t = "Int64"; + } else { + t = "Float64"; + } + break; + case "boolean": + t = "UInt8"; + break; + case "undefined": + throw newError(`Parameter ${paramName} is not provided`); + default: + if (param == null) { + t = "Nullable(String)"; + } else if (Array.isArray(param)) { + query_params![paramName] = JSON.stringify(param); + } + } + return `{${paramName}: ${t}}`; + }); + const res = await client.query({ + query, + query_params, + abort_signal: AbortSignal.timeout(warehouseTimeoutMs), + format: "JSONEachRow", + }); + return res.json(); + } catch (e: any) { + if (e.message === "The user aborted a request.") { + status = "timeout"; + e = new Error(`Query execution exceeded ${warehouseTimeoutMs}ms timeout. Aborted.`); + } else { + status = "error"; + } + throw e; + } finally { + const ms = Date.now() - start; + if (storeMetrics) { + storeMetrics.warehouseStatus(conId, table, status, ms); + } + log + .atInfo() + .log( + `[${conId}] query: ${query} params: ${JSON.stringify( + query_params + )} table: ${table} status: ${status} ms: ${ms}` + ); + } + }, + close: async () => { + await client.close(); + }, + }; +}; + +const getClickhouseClient = (cred: any) => { + let [host, port] = cred.hosts[0].split(":"); + switch (cred.protocol) { + case "http": + port = port || "8123"; + break; + case "https": + port = port || "8443"; + break; + default: + port = "8443"; + } + const url = `https://${host}:${port}/`; + log.atDebug().log(`Connecting to ${url} with ${cred.username}`); + return createClient({ + url: url, + database: cred.database, + username: cred.username, + password: cred.password, + }); +}; diff --git a/libs/core-functions/src/functions/mixpanel-destination.ts b/libs/core-functions/src/functions/mixpanel-destination.ts index 8b1c93fc9..67e684380 100644 --- a/libs/core-functions/src/functions/mixpanel-destination.ts +++ b/libs/core-functions/src/functions/mixpanel-destination.ts @@ -1,18 +1,78 @@ -import { JitsuFunction } from "@jitsu/protocols/functions"; -import type { AnalyticsServerEvent } from "@jitsu/protocols/analytics"; -import { randomId } from "juava"; +import { FullContext, JitsuFunction } from "@jitsu/protocols/functions"; +import { HTTPError, RetryError } from "@jitsu/functions-lib"; +import type { AnalyticsServerEvent, Geo } from "@jitsu/protocols/analytics"; +import { hash } from "juava"; import { MixpanelCredentials } from "../meta"; +import { bulkerPartitionParam, eventTimeSafeMs, getPageOrScreenProps, MetricsMeta } from "./lib"; +import { randomUUID } from "crypto"; +import zlib from "zlib"; -export type HttpRequest = { - id: string; +const bulkerBase = process.env.BULKER_URL; +const bulkerAuthKey = process.env.BULKER_AUTH_KEY; + +//See https://help.mixpanel.com/hc/en-us/articles/115004708186-Profile-Properties +export const specialProperties = [ + "avatar", + "email", + "phone", + "name", + "first_name", + "last_name", + "timezone", + "unsubscribed", +]; + +const invalidDistinctIds = new Set([ + "00000000-0000-0000-0000-000000000000", + "anon", + "anonymous", + "nil", + "none", + "null", + "n/a", + "na", + "undefined", + "unknown", + "", + "0", + "-1", + "true", + "false", + "[]", + "{}", +]); + +const CLICK_IDS = ["dclid", "fbclid", "gclid", "ko_click_id", "li_fat_id", "msclkid", "ttclid", "twclid", "wbraid"]; + +export type MixpanelRequest = { method?: string; url: string; payload?: any; headers?: Record; + eventType: string; + insertId?: string; + skipLogs?: boolean; }; -function prefix

(param: Record, prefix: P): Record { - return Object.entries(param).reduce((acc, [key, value]) => ({ ...acc, [`${prefix}${key}`]: value }), {}); +// Map and extracts campaign parameters from context.campaign into object with utm properties +function utmFromCampaign(param: Record, prefix: string = ""): Record { + return Object.entries(param).reduce( + (acc, [key, value]) => ({ + ...acc, + [`${prefix}utm_${key === "name" ? "campaign" : key}`]: value, + }), + {} + ); +} + +// Extracts utm parameters from properties and returns them as a new object +function extractUtmParams(properties: Record, prefix: string = ""): Record { + return Object.entries(properties).reduce((acc, [key, value]) => { + if (key.startsWith("utm_")) { + acc[`${prefix}${key}`] = value; + } + return acc; + }, {}); } function evict(obj: Record, key: string) { @@ -21,109 +81,294 @@ function evict(obj: Record, key: string) { return val; } +function getQueryParam(url: string, param: string) { + param = param.replace(/[[]/, "\\[").replace(/[\]]/, "\\]"); + const regexS = "[\\?&]" + param + "=([^&#]*)", + regex = new RegExp(regexS), + results = regex.exec(url); + if (results === null || (results && typeof results[1] !== "string" && results[1]["length"])) { + return ""; + } else { + let result = results[1]; + try { + result = decodeURIComponent(result); + } catch (err) {} + return result.replace(/\+/g, " "); + } +} + +function geoParams(geo?: Geo) { + if (!geo) { + return {}; + } + const params: any = {}; + if (geo.country?.code) { + params.mp_country_code = geo.country.code; + } + if (geo.region?.code) { + params.$region = geo.region.code; + } + if (geo.city?.name) { + params.$city = geo.city.name; + } + if (geo.location?.latitude && geo.location?.longitude) { + params.$geo_source = "reverse_geocoding"; + params.$latitude = geo.location.latitude; + params.$longitude = geo.location.longitude; + } + return params; +} + +function clickParams(url: string) { + if (!url) { + return {}; + } + const params: any = {}; + CLICK_IDS.forEach(idkey => { + const id = getQueryParam(url, idkey); + if (id.length) { + params[idkey] = id; + } + }); + + return params; +} + function trackEvent( + ctx: FullContext, + deviceId: string, distinctId: string, eventType: string, - event: AnalyticsServerEvent, - opts: MixpanelCredentials -): HttpRequest { - const traits = { ...(event.traits || event.context?.traits || {}) }; - const groupId = event.context?.groupId || traits.groupId; + event: AnalyticsServerEvent +): MixpanelRequest { + const opts = ctx.props; + const analyticsContext = event.context || {}; + const traits = { ...(event.traits || analyticsContext.traits || {}) }; + specialProperties.forEach(prop => { + if (traits[prop]) { + traits[`$${prop}`] = traits[prop]; + delete traits[prop]; + } + }); + const groupId = analyticsContext.groupId || traits.groupId; const groupKey = opts.groupKey || "$group_id"; delete traits.groupId; const customProperties = { - ...prefix(event.context?.campaign || {}, "utm_"), - ...(event.context?.page || {}), + ...utmFromCampaign(analyticsContext.campaign || {}), + ...(analyticsContext.page || {}), ...traits, + ...getPageOrScreenProps(event), ...(event.properties || {}), ...(groupId ? { [groupKey]: groupId } : {}), - userAgent: event.context?.userAgent, + userAgent: analyticsContext.userAgent, }; - return { - id: randomId(), - url: `https://api.mixpanel.com/import?strict=1&project_id=${opts.projectId}`, - headers: { - "Content-type": "application/json", - Accept: "application/json", - Authorization: `Basic ${getAuth(opts)}`, + const pageUrl = evict(customProperties, "url"); + + const app = analyticsContext.app || ({} as any); + const network = analyticsContext.network || ({} as any); + const screen = analyticsContext.screen || ({} as any); + const device = analyticsContext.device || ({} as any); + const ua = ctx.ua || ({} as any); + const uaDevice = ua.device || ({} as any); + const insertId = getInsertId(event.messageId, eventType); + const eventPayload = { + event: eventType, + properties: { + ...clickParams(pageUrl), + ...geoParams(analyticsContext.geo), + ...customProperties, + ip: analyticsContext.ip, + time: eventTimeSafeMs(event), + $device_id: deviceId, + distinct_id: distinctId, + $insert_id: insertId, + $user_id: event.userId ? `${event.userId}` : undefined, + $browser: ua.browser?.name, + $browser_version: ua.browser?.version, + $os: analyticsContext.os?.name || ua.os?.name, + $os_version: analyticsContext.os?.version || ua.os?.version, + $current_url: pageUrl, + current_page_title: evict(customProperties, "title"), + $referrer: evict(customProperties, "referrer"), + $referring_domain: evict(customProperties, "referring_domain"), + $session_id: analyticsContext.sessionId, + + //mobile + $app_namespace: app.namespace, + $app_name: app.name, + $app_build_number: app.build, + $app_release: app.build, + $app_version: app.version, + $app_version_string: app.version, + + $carrier: network.carrier, + $has_telephone: network.cellular, + $bluetooth_enabled: network.bluetooth, + $wifi: network.wifi, + + $screen_dpi: screen.density, + $screen_height: screen.height, + $screen_width: screen.width, + + // $bluetooth_version: "ble", + // $brand: "google", + // $had_persisted_distinct_id: false, + // $has_nfc: false, + $device_type: device.type || uaDevice.type, + $device_name: device.name || uaDevice.model, + $manufacturer: device.manufacturer || uaDevice.vendor, + $model: device.model || uaDevice.model, + advertising_id: device.advertisingId, + ad_tracking_enabled: device.adTrackingEnabled, }, - payload: [ - { - event: eventType, - properties: { - ip: event.context?.ip || event.request_ip, - time: new Date(event.timestamp as string).getTime(), - distinct_id: distinctId, - $insert_id: event.messageId + "-" + randomId(), - $user_id: event.userId ? event.userId : undefined, - // $browser: "", - // $browser_version: "", - $current_url: evict(customProperties, "url"), - $referrer: evict(customProperties, "referrer"), - $session_id: event.context?.sessionId, - - $screen_dpi: event.context?.screen?.density, - $screen_height: event.context?.screen?.height, - $screen_width: event.context?.screen?.width, - ...customProperties, - }, - }, - ], }; + if (ctx["connectionOptions"]?.mode === "batch" && bulkerBase) { + let tableName = "import"; + const metricsMeta: Omit = { + workspaceId: ctx.workspace.id, + streamId: ctx.source.id, + destinationId: ctx.destination.id, + connectionId: ctx.connection.id, + functionId: "builtin.destination.bulker", + }; + return { + url: `${bulkerBase}/post/${ctx.connection.id}?tableName=${tableName}&modeOverride=batch${bulkerPartitionParam( + ctx, + event + )}`, + eventType, + insertId, + headers: { + ...(bulkerAuthKey ? { Authorization: `Bearer ${bulkerAuthKey}` } : {}), + metricsMeta: JSON.stringify(metricsMeta), + }, + payload: eventPayload, + skipLogs: true, + }; + } else { + return { + url: `https://${apiHost(opts)}/import?strict=1&project_id=${opts.projectId}`, + eventType, + insertId, + headers: { + "Content-type": "application/json", + "Content-Encoding": "gzip", + Accept: "application/json", + Authorization: `Basic ${getAuth(opts)}`, + }, + payload: [eventPayload], + }; + } } -//See https://help.mixpanel.com/hc/en-us/articles/115004708186-Profile-Properties -export const specialProperties = [ - "avatar", - "email", - "phone", - "name", - "first_name", - "last_name", - "timezone", - "unsubscribed", -]; -function setProfileMessage(distinctId: string, event: AnalyticsServerEvent, opts: MixpanelCredentials): HttpRequest[] { - const traits = { ...(event.traits || event.context?.traits || {}) }; +function setProfileMessage( + ctx: FullContext, + distinctId: string, + event: AnalyticsServerEvent +): MixpanelRequest[] { + const opts = ctx.props; + + const analyticsContext = event.context || {}; + const traits = { ...(event.traits || analyticsContext.traits || {}) }; + const app = analyticsContext.app; + const device = analyticsContext.device; + const os = analyticsContext.os; + const page = analyticsContext.page; + const ua = ctx.ua; + specialProperties.forEach(prop => { if (traits[prop]) { traits[`$${prop}`] = traits[prop]; delete traits[prop]; } }); - const groupId = event.context?.groupId || traits.groupId; + const groupId = analyticsContext.groupId || traits.groupId; delete traits.groupId; - const setPayload: any = { - $token: opts.projectToken, - $distinct_id: distinctId, - $ip: event.context?.ip || event.request_ip, - $set: traits, - }; + let mobileDeviceInfo: any = {}; + if (os?.name === "Android" && app) { + mobileDeviceInfo = { + $android_app_version: app.version, + $android_app_version_code: app.build, + $android_manufacturer: device?.manufacturer, + $android_model: device?.model, + $android_os: os.name, + $android_os_version: os.version, + }; + } else if (device?.manufacturer === "Apple" && app) { + mobileDeviceInfo = { + $ios_app_version: app.build, + $ios_app_release: app.version, + $ios_device_model: device.model, + $ios_version: os?.version, + }; + } - const reqs = [ + const ip = analyticsContext.ip; + const engageUrl = `https://${apiHost(opts)}/engage?verbose=1${ip ? "" : "&ip=0"}`; + const reqs: MixpanelRequest[] = [ { - id: randomId(), - url: "https://api.mixpanel.com/engage?verbose=1#profile-set", + url: engageUrl + "#profile-set", headers: { "Content-type": "application/json", Accept: "text-plain", }, - payload: [setPayload], + eventType: "profile-set", + payload: [ + { + $token: opts.projectToken, + $distinct_id: distinctId, + $ip: ip, + $set: { + ...geoParams(analyticsContext.geo), + ...traits, + $browser: ua?.browser?.name, + $browser_version: ua?.browser?.version, + $os: ua?.os?.name, + ...mobileDeviceInfo, + }, + }, + ], }, ]; + if (page?.referrer || page?.referring_domain) { + const utm = { + ...utmFromCampaign(analyticsContext.campaign || {}, "initial_"), + ...extractUtmParams(event.properties || {}, "initial_"), + }; + reqs.push({ + url: engageUrl + "#profile-set-once", + headers: { + "Content-type": "application/json", + Accept: "text-plain", + }, + eventType: "profile-set-once", + payload: [ + { + $token: opts.projectToken, + $distinct_id: distinctId, + $ip: ip, + $set_once: { + ...utm, + $initial_referrer: page?.referrer, + $initial_referring_domain: page?.referring_domain, + }, + }, + ], + }); + } if (groupId) { const groupKey = opts.groupKey || "$group_id"; const unionPayload: any = { $token: opts.projectToken, $distinct_id: distinctId, - $ip: event.context?.ip || event.request_ip, + $ip: ip, $union: { [groupKey]: [groupId] }, }; reqs.push({ - id: randomId(), - url: "https://api.mixpanel.com/engage?verbose=1#profile-union", + url: engageUrl + "#profile-union", + eventType: "profile-union", headers: { "Content-type": "application/json", Accept: "text-plain", @@ -134,7 +379,8 @@ function setProfileMessage(distinctId: string, event: AnalyticsServerEvent, opts return reqs; } -function setGroupMessage(event: AnalyticsServerEvent, opts: MixpanelCredentials): HttpRequest { +function setGroupMessage(ctx: FullContext, event: AnalyticsServerEvent): MixpanelRequest { + const opts = ctx.props; const props = { ...(event.traits || {}) }; specialProperties.forEach(prop => { if (props[prop]) { @@ -151,8 +397,8 @@ function setGroupMessage(event: AnalyticsServerEvent, opts: MixpanelCredentials) }; return { - id: randomId(), - url: "https://api.mixpanel.com/groups?verbose=1#group-set", + url: `https://${apiHost(opts)}/groups?verbose=1#group-set`, + eventType: "group-set", headers: { "Content-type": "application/json", Accept: "text-plain", @@ -169,101 +415,233 @@ function getAuth(props: MixpanelCredentials) { return base64(`${props.serviceAccountUserName}:${props.serviceAccountPassword}`); } -function merge(primaryId: string, secondaryId: string, props: MixpanelCredentials): HttpRequest { - const basicAuth = getAuth(props); - return { - id: randomId(), - url: `https://api.mixpanel.com/import?strict=1&project_id=${props.projectId}`, - headers: { - "Content-type": "application/json", - Accept: "text-plain", - Authorization: `Basic ${basicAuth}`, - }, - payload: [ - { - event: "$merge", - properties: { - $distinct_ids: [primaryId, secondaryId], +function getInsertId(messageId: string, eventType: string) { + return hash("md5", (messageId || randomUUID()) + "_" + eventType); +} + +function merge( + ctx: FullContext, + messageId: string, + identifiedId: string, + anonymousId: string +): MixpanelRequest[] { + if (!anonymousId) { + return []; + } + const opts = ctx.props; + const basicAuth = getAuth(opts); + const insertId = getInsertId(messageId, "merge"); + + return [ + { + url: `https://${apiHost(opts)}/import?strict=1&project_id=${opts.projectId}`, + headers: { + "Content-type": "application/json", + Accept: "text-plain", + Authorization: `Basic ${basicAuth}`, + }, + eventType: "$merge", + insertId, + payload: [ + { + event: "$merge", + properties: { + $insert_id: insertId, + $distinct_ids: [identifiedId, anonymousId], + token: opts.projectToken, + }, }, + ], + }, + ]; +} + +function alias( + ctx: FullContext, + messageId: string, + identifiedId: string, + anonymousId: string +): MixpanelRequest[] { + if (!anonymousId) { + return []; + } + const opts = ctx.props; + const basicAuth = getAuth(opts); + const insertId = getInsertId(messageId, "alias"); + return [ + { + url: `https://${apiHost(opts)}/import?strict=1&project_id=${opts.projectId}`, + headers: { + "Content-type": "application/json", + Accept: "text-plain", + Authorization: `Basic ${basicAuth}`, }, - ], - }; + eventType: "$create_alias", + insertId, + payload: [ + { + event: "$create_alias", + properties: { + $insert_id: insertId, + distinct_id: anonymousId, + alias: identifiedId, + token: opts.projectToken, + }, + }, + ], + }, + ]; +} + +function isAnonymous(ctx: FullContext, event: AnalyticsServerEvent) { + if (ctx.props.simplifiedIdMerge) { + return !event.userId; + } else { + return !event.userId && !event.traits?.email && !event.context?.traits?.email; + } +} + +function getDistinctId(ctx: FullContext, event: AnalyticsServerEvent, deviceId: string) { + if (ctx.props.simplifiedIdMerge) { + return event.userId ? `${event.userId}` : `$device:${deviceId}`; + } else { + return `${(event.userId || event.traits?.email || event.context?.traits?.email || deviceId) ?? ""}`; + } +} + +function getDeviceId(ctx: FullContext, event: AnalyticsServerEvent) { + let deviceId = event.anonymousId; + if ( + !deviceId || + deviceId === "undefined" || + deviceId === "unknown" || + deviceId === "00000000-0000-0000-0000-000000000000" + ) { + const traits = event.traits || event.context?.traits; + const id = `${event.userId ?? ""}${event.context.ip ? `${event.context.ip}${event.context.userAgent ?? ""}` : ""}${ + traits ? JSON.stringify(traits) : "" + }`; + if (id) { + deviceId = hash("sha256", id); + } + } else { + if (typeof deviceId === "number") { + return `${deviceId}`; + } + if (typeof deviceId !== "string") { + deviceId = `${JSON.stringify(deviceId)}`; + } + if (deviceId.length > 250) { + deviceId = hash("sha256", deviceId); + } + } + return deviceId; } const MixpanelDestination: JitsuFunction = async (event, ctx) => { - ctx.log.debug(`Mixpanel destination (props=${JSON.stringify(ctx.props)}) received event ${JSON.stringify(event)}`); - const messages: HttpRequest[] = []; - if (event.type === "identify") { - if (!event.userId) { - const distinctId = event.anonymousId || event.traits?.email; - if (!distinctId) { - ctx.log.info(`No distinct id found for event ${JSON.stringify(event)}`); - } else if (ctx.props.enableAnonymousUserProfiles) { - messages.push(...setProfileMessage(distinctId as string, event, ctx.props)); - } - if (event.anonymousId && event.traits?.email) { - messages.push(merge(event.anonymousId, event.traits.email as string, ctx.props)); - } - } else { - if (event.anonymousId) { - messages.push(merge(event.userId, event.anonymousId, ctx.props)); - } - if (event.traits?.email) { - messages.push(merge(event.userId, event.traits?.email as string, ctx.props)); - } - messages.push(...setProfileMessage(event.userId, event, ctx.props)); + if (typeof ctx.props.filterBotTraffic === "undefined" || ctx.props.filterBotTraffic) { + if (ctx.ua?.bot) { + return; } - if (ctx.props.sendIdentifyEvents) { - const distinctId = event.userId || (event.traits?.email as string) || event.anonymousId; - if (distinctId) { - messages.push(trackEvent(distinctId, "Identify", event, ctx.props)); + } + // no userId or email + const anonymous = isAnonymous(ctx, event); + if (anonymous && !ctx.props.enableAnonymousUserProfiles) { + return; + } + + const trackPageView = typeof ctx.props.sendPageEvents === "undefined" || ctx.props.sendPageEvents; + const deviceId = getDeviceId(ctx, event); + if (!deviceId) { + ctx.log.warn( + `No anonymousId and there is no way to assume anonymous id from event: at least context.ip or any user trait is required. Skipping.` + ); + return; + } + const distinctId = getDistinctId(ctx, event, deviceId); + if (invalidDistinctIds.has(distinctId)) { + throw new Error(`Invalid distinctId '${distinctId}'. Skipping event: ${JSON.stringify(event)}`); + } + + try { + const messages: MixpanelRequest[] = []; + if (event.type === "identify") { + if (event.userId) { + messages.push(...setProfileMessage(ctx, distinctId, event)); } - } - } else if (event.type === "group" && ctx.props.enableGroupAnalytics) { - messages.push(setGroupMessage(event, ctx.props)); - } else if (event.type === "track") { - const distinctId = event.userId || event.anonymousId || (event.traits?.email as string); - if (!distinctId) { - ctx.log.info(`No distinct id found for event ${JSON.stringify(event)}`); - } else { - if (event.userId || ctx.props.enableAnonymousUserProfiles) { - messages.push(trackEvent(distinctId, event.event as string, event, ctx.props)); + if (!ctx.props.simplifiedIdMerge && !anonymous && ctx.props.enableAnonymousUserProfiles) { + // we merge distinctId with anonymousId. It makes sense only when enableAnonymousUserProfiles is enabled + if (event.userId) { + messages.push(...merge(ctx, event.messageId, distinctId, `${event.anonymousId}`)); + } else { + // when no userId, distinctId=email for non-anonymous events. make an alias + messages.push(...alias(ctx, event.messageId, distinctId, `${event.anonymousId}`)); + } + } + if (ctx.props.sendIdentifyEvents) { + messages.push(trackEvent(ctx, deviceId, distinctId, "Identify", event)); } - } - } else if (event.type === "page") { - const distinctId = event.userId || event.anonymousId || (event.traits?.email as string); - if (!distinctId) { - ctx.log.info(`No distinct id found for Page View event ${JSON.stringify(event)}`); } else { - if (event.userId || ctx.props.enableAnonymousUserProfiles) { - messages.push(trackEvent(distinctId, "Page View", event, ctx.props)); + if (event.type === "group" && ctx.props.enableGroupAnalytics) { + messages.push(setGroupMessage(ctx, event)); + } else if (event.type === "track") { + messages.push(trackEvent(ctx, deviceId, distinctId, event.event as string, event)); + } else if (event.type === "page" && trackPageView) { + messages.push(trackEvent(ctx, deviceId, distinctId, "$mp_web_page_view", event)); + } else if (event.type === "screen") { + messages.push(trackEvent(ctx, deviceId, distinctId, "Screen", event)); } } - } - for (const message of messages) { - const method = message.method || "POST"; - try { - const result = await ctx.fetch(message.url, { - method, - headers: message.headers, - ...(message.payload ? { body: JSON.stringify(message.payload) } : {}), - }); - const logMessage = `MixPanel ${method} ${message.url}:${ - message.payload ? `${JSON.stringify(message.payload)} --> ` : "" - }${result.status} ${await result.text()}`; + for (const message of messages) { + const method = message.method || "POST"; + const payload = message.payload ? JSON.stringify(message.payload) : "{}"; + const compressed = message.headers?.["Content-Encoding"] === "gzip" ? zlib.gzipSync(payload) : payload; + const result = await ctx.fetch( + message.url, + { + method, + headers: message.headers, + ...(message.payload ? { body: compressed } : {}), + }, + message.skipLogs ? { log: false } : undefined + ); if (result.status !== 200) { - ctx.log.error(logMessage); + if (message.skipLogs) { + throw new HTTPError( + `MixPanel event '${message.eventType}' $insert_id:${message.insertId} Failed to batch event. HTTP Error: ${result.status} ${result.statusText}`, + result.status, + await result.text() + ); + } else { + throw new Error( + `MixPanel${message.insertId ? " $insert_id:" + message.insertId : ""} ${method} ${message.url}:${ + message.payload + ? `${JSON.stringify(message.payload)} (size: ${payload.length} compressed: ${compressed.length}) --> ` + : "" + }${result.status} ${await result.text()}` + ); + } } else { - ctx.log.debug(logMessage); + if (message.skipLogs) { + ctx.log.debug(`MixPanel event '${message.eventType}' $insert_id:${message.insertId} added to batch.`); + } else { + ctx.log.debug( + `MixPanel${message.insertId ? " $insert_id:" + message.insertId : ""} ${method} ${message.url}: ${ + result.status + } ${await result.text()}` + ); + } } - } catch (e: any) { - throw new Error( - `Failed to send event to MixPanel: ${method} ${message.url} ${JSON.stringify(message.payload)}: ${e?.message}` - ); } + } catch (e: any) { + throw new RetryError(e.message); } }; +function apiHost(props: MixpanelCredentials) { + return props.dataResidency === "EU" ? "api-eu.mixpanel.com" : "api.mixpanel.com"; +} + MixpanelDestination.displayName = "mixpanel-destination"; MixpanelDestination.description = "This functions covers jitsu events and sends them to MixPanel"; diff --git a/libs/core-functions/src/functions/mongodb-destination.ts b/libs/core-functions/src/functions/mongodb-destination.ts index 1f9eef49a..38c5fa883 100644 --- a/libs/core-functions/src/functions/mongodb-destination.ts +++ b/libs/core-functions/src/functions/mongodb-destination.ts @@ -1,4 +1,5 @@ import { FullContext, JitsuFunction } from "@jitsu/protocols/functions"; +import { RetryError } from "@jitsu/functions-lib"; import type { AnalyticsServerEvent } from "@jitsu/protocols/analytics"; import { getSingleton } from "juava"; import { MongoClient } from "mongodb"; @@ -45,6 +46,7 @@ const MongodbDestination: JitsuFunction client.close(), } @@ -53,8 +55,8 @@ const MongodbDestination: JitsuFunction { delete obj.referer; @@ -69,43 +82,31 @@ const PosthogDestination: JitsuFunction { + // compatibility with configs that were created before this separate setting was introduced + const sendAnonymousEvents = + typeof props.sendAnonymousEvents !== "undefined" ? props.sendAnonymousEvents : props.enableAnonymousUserProfiles; const groupType = props.groupType || "group"; const client = new PostHog(props.key, { host: props.host || POSTHOG_DEFAULT_HOST, fetch: fetch }); try { if (event.type === "identify") { - if (!event.userId) { - const distinctId = event.anonymousId || event.traits?.email; - if (!distinctId) { - log.info(`No distinct id found for event ${JSON.stringify(event)}`); - } else if (props.enableAnonymousUserProfiles) { - client.identify({ - distinctId: distinctId as string, - properties: { $anon_distinct_id: event.anonymousId || undefined, ...event.traits }, - }); - } - } else { - client.identify({ + client.identify({ + distinctId: event.userId as string, + properties: { $anon_distinct_id: event.anonymousId || undefined, ...event.traits }, + }); + + /** + * If we've been tracking anonymous events under user/"Person" profiles + * in Posthog, we should merge the anonymous person with the identified + * one, so that the entire user event history is consolidated. + */ + const alias: string | undefined = event.anonymousId || (event.traits?.email as string); + if (sendAnonymousEvents && alias) { + client.alias({ distinctId: event.userId as string, - properties: { $anon_distinct_id: event.anonymousId || undefined, ...event.traits }, + alias: alias, }); - if (event.anonymousId) { - client.alias({ distinctId: event.userId as string, alias: event.anonymousId }); - } - if (event.traits?.email) { - client.alias({ distinctId: event.userId as string, alias: event.traits.email as string }); - } } - // if (props.sendIdentifyEvents) { - // const distinctId = event.userId || (event.traits?.email as string) || event.anonymousId; - // // if (distinctId) { - // // client.capture({ - // // distinctId: distinctId as string, - // // event: "Identify", - // // properties: getEventProperties(event), - // // }); - // // } - // } - } else if (event.type === "group") { + } else if (event.type === "group" && props.enableGroupAnalytics) { client.groupIdentify({ groupType: groupType, groupKey: event.groupId as string, @@ -120,34 +121,50 @@ const PosthogDestination: JitsuFunction(); +export type ProfilesConfig = z.infer; + +export const createClient = async (config: { mongoUrl: string }) => { + const mongoTimeout = parseNumber(process.env.MONGODB_TIMEOUT_MS, 1000); + let uri = config.mongoUrl!; + + // Create a new MongoClient + const client = new MongoClient(uri, { + compressors: process.env.MONGODB_NETWORK_COMPRESSION ? process.env.MONGODB_NETWORK_COMPRESSION : ["zstd"], + serverSelectionTimeoutMS: 60000, + maxPoolSize: 32, + connectTimeoutMS: 60000, + socketTimeoutMS: mongoTimeout, + }); + // Connect the client to the server (optional starting in v4.7) + await client.connect(); + // Establish and verify connection + await client.db().command({ ping: 1 }); + + return client; +}; + +export const ProfilesFunction: JitsuFunction = async (event, ctx) => { + const config = ProfilesConfig.parse(ctx.props || {}); + + const profileId = event[ProfileIdParameter] || event.userId; + if (!profileId) { + ctx.log.debug(`No profileId found. Skipping`); + return; + } + + try { + const mongoSingleton = config.mongoUrl + ? getSingleton( + `profiles-mongodb-${ctx.connection?.id}-${hash("md5", config.mongoUrl)}`, + () => { + ctx.log.info(`Connecting to MongoDB server.`); + const cl = createClient({ mongoUrl: config.mongoUrl! }); + ctx.log.info(`Connected successfully to MongoDB server.`); + return cl; + }, + { + optional: true, + ttlSec: 60 * 60 * 24, + cleanupFunc: client => client.close(), + } + ) + : mongodb; + const mongo = await mongoSingleton.waitInit(); + await pbEnsureMongoCollection(mongo, config.eventsDatabase, config.eventsCollectionName, config.profileWindowDays, [ + profileIdHashColumn, + profileIdColumn, + "type", + ]); + await pbEnsureMongoCollection( + mongo, + config.eventsDatabase, + config.traitsCollectionName, + config.profileWindowDays, + [profileIdColumn], + "updatedAt", + true + ); + + if (event.type === "identify") { + const d = new Date(); + const traits = await mongo + .db(config.eventsDatabase) + .collection(config.traitsCollectionName) + .findOneAndUpdate( + { [profileIdColumn]: profileId }, + [ + { + $set: { + [profileIdColumn]: profileId, + userId: { + $ifNull: ["$userId", event.userId], + }, + anonymousId: { + $ifNull: ["$anonymousId", event.anonymousId], + }, + traits: { + $mergeObjects: ["$traits", event.traits], + }, + createdAt: { + $ifNull: ["$createdAt", d], + }, + updatedAt: d, + }, + }, + ], + { + upsert: true, + returnDocument: "after", + } + ); + ctx.log.info(`Merged profile: ${JSON.stringify(traits)}`); + } + const obj = { + [profileIdHashColumn]: int32Hash(profileId), + [profileIdColumn]: profileId, + }; + transfer(obj, event, [ProfileIdParameter]); + obj["timestamp"] = parseDate(event.timestamp, new Date()); + + const res = await mongo + .db(config.eventsDatabase) + .collection(config.eventsCollectionName) + .insertOne(obj, { writeConcern: { w: 1, journal: false } }); + if (!res.acknowledged) { + ctx.log.error(`Failed to insert to MongoDB: ${JSON.stringify(res)}`); + } else { + ctx.log.debug(`Inserted to MongoDB: ${JSON.stringify(res)}`); + } + } catch (e: any) { + throw new Error(`Error while sending event to MongoDB: ${e}`); + } + + const priority = event[ProfilePriorityParameter] || 0; + + const bulkerRes = await request( + `${bulkerBase}/profiles/${config.profileBuilderId}/${priority}?profileId=${encodeURIComponent(profileId)}`, + { + method: "POST", + headers: { Authorization: `Bearer ${bulkerAuthKey}` }, + bodyTimeout: fetchTimeoutMs, + headersTimeout: fetchTimeoutMs, + dispatcher: undiciAgent, + } + ); + if (bulkerRes.statusCode != 200) { + throw new HTTPError(`HTTP Error: ${bulkerRes.statusCode}`, bulkerRes.statusCode, await bulkerRes.body.text()); + } else { + ctx.log.debug(`HTTP Status: ${bulkerRes.statusCode} Response: ${await bulkerRes.body.text()}`); + } +}; + +export async function pbEnsureMongoCollection( + mongo: MongoClient, + databaseName: string, + collectionName: string, + ttlDays: number, + indexFields: string[] = [], + ttlColumn: string = "timestamp", + unique?: boolean +) { + if (MongoCreatedCollections.has(collectionName)) { + return; + } + try { + const db = mongo.db(databaseName); + const collStatus = await db + .collection(collectionName) + .aggregate([{ $collStats: { count: {} } }]) + .next() + .catch(e => {}); + if (collStatus) { + //collection already exists + MongoCreatedCollections.add(collectionName); + return; + } + const collection = await db.createCollection(collectionName, { + clusteredIndex: { + key: { _id: 1 }, + unique: true, + }, + writeConcern: { w: 1, journal: false }, + storageEngine: { wiredTiger: { configString: "block_compressor=zstd" } }, + }); + await collection.createIndex({ [ttlColumn]: 1 }, { expireAfterSeconds: 60 * 60 * 24 * ttlDays }); + if (indexFields.length > 0) { + const index = {}; + indexFields.forEach(field => { + index[field] = 1; + }); + if (unique) { + await collection.createIndex(index, { unique: true }); + } else { + await collection.createIndex(index); + } + } + MongoCreatedCollections.add(collectionName); + } catch (err) { + throw new Error(`Failed to create collection ${collectionName}: ${err}`); + } +} diff --git a/libs/core-functions/src/functions/salesforce-destination.ts b/libs/core-functions/src/functions/salesforce-destination.ts new file mode 100644 index 000000000..722ca7130 --- /dev/null +++ b/libs/core-functions/src/functions/salesforce-destination.ts @@ -0,0 +1,644 @@ +import { FullContext, JitsuFunction } from "@jitsu/protocols/functions"; +import { RetryError } from "@jitsu/functions-lib"; +import type { AnalyticsServerEvent } from "@jitsu/protocols/analytics"; +import { SalesforceCredentials } from "../meta"; +import omit from "lodash/omit"; +import NodeCache from "node-cache"; +import { getOauthCreds } from "./lib/oauth"; +import { z } from "zod"; +import zlib from "zlib"; +import { parseNumber } from "juava"; + +const API_VERSION = "v64.0"; // Update this to the latest Salesforce API version if needed + +const credCache = new NodeCache({ stdTTL: 800, checkperiod: 30, useClones: false }); +const sobjectCacheTTL = 3600; // 1 hour +const sobjectCache = new NodeCache({ stdTTL: sobjectCacheTTL, checkperiod: 60, useClones: false }); +const sobjectMetaRefreshPeriodMs = 1000 * 60 * 10; // 10 minutes + +const Creds = z.object({ + instance_url: z.string(), + access_token: z.string(), +}); + +type Creds = z.infer; + +enum SalesforcePropertyPermissionBit { + Updateable, + Createable, + Filterable, + ExternalId, + Required, +} +type SalesforcePropertyPermissions = number; + +function hasPermission(prop: SalesforcePropertyPermissions, permissionBit: SalesforcePropertyPermissionBit): boolean { + return (prop & (1 << permissionBit)) > 0; +} + +type Sobject = { + name: string; + properties: Record; + lastModifiedDate: string; + lastChecked: Date; +}; + +const SalesforceEnvelope = z.object({ + SALESFORCE_OPERATION: z.enum(["insert", "update", "upsert", "delete"]).default("insert"), // Default to "insert" if not specified + SALESFORCE_SOBJECT: z.string().optional(), + SALESFORCE_MATCHERS_OPERATOR: z.enum(["or", "and", "OR", "AND"]).default("OR"), // Optional, defaults to "OR" + SALESFORCE_MATCHERS: z.record(z.any()).optional(), + SALESFORCE_PAYLOAD: z.record(z.any()).optional(), +}); + +type SalesforceEnvelope = z.infer; + +function lead(event: AnalyticsServerEvent, operation: SalesforceEnvelope["SALESFORCE_OPERATION"]): any { + const knownProps = [ + "company", + "last_name", + "first_name", + "email", + "city", + "state", + "country", + "postal_code", + "street", + ]; + const traits = event.traits || {}; + const properties = event.properties || {}; + const res = { + Company: traits.company || properties.company, + LastName: traits.last_name || properties.last_name, + FirstName: traits.first_name || properties.first_name, + Email: traits.email || properties.email, + City: traits.address?.["city"] || properties.address?.["city"], + State: traits.address?.["state"] || properties.address?.["state"], + Country: traits.address?.["country"] || properties.address?.["country"], + PostalCode: traits.address?.["postal_code"] || properties.address?.["postal_code"], + Street: traits.address?.["street"] || properties.address?.["street"], + ...omit(properties, knownProps), + ...omit(traits, knownProps), + }; + if (operation === "insert") { + if (!res.LastName) { + throw new Error("'Lead' object requires 'LastName' property"); + } + if (!res.Company) { + throw new Error("'Lead' object requires 'Company' property"); + } + } + return res; +} + +function account(event: AnalyticsServerEvent, operation: SalesforceEnvelope["SALESFORCE_OPERATION"]): any { + const knownProps = [ + "name", + "employees", + "city", + "state", + "country", + "postal_code", + "street", + "phone", + "description", + "website", + ]; + const traits = event.traits || {}; + const properties = event.properties || {}; + const res = { + Name: traits.name, + AccountNumber: event.groupId, + NumberOfEmployees: traits.employees || properties.employees, + BillingCity: traits.address?.["city"] || properties.address?.["city"], + BillingState: traits.address?.["state"] || properties.address?.["state"], + BillingCountry: traits.address?.["country"] || properties.address?.["country"], + BillingPostalCode: traits.address?.["postal_code"] || properties.address?.["postal_code"], + BillingStreet: traits.address?.["street"] || properties.address?.["street"], + Phone: traits.phone || properties.phone, + Description: traits.description || properties.description, + Website: traits.website || properties.website, + ...omit(properties, knownProps), + ...omit(traits, knownProps), + }; + if (operation === "insert") { + if (!res.Name) { + throw new Error("'Account' object requires 'Name' property"); + } + } + return res; +} + +function contact(event: AnalyticsServerEvent, operation: SalesforceEnvelope["SALESFORCE_OPERATION"]): any { + const knownProps = ["last_name", "first_name", "email", "city", "state", "country", "postal_code", "street"]; + const contextTraits = event.context?.traits || {}; + const traits = event.traits || {}; + const properties = event.properties || {}; + const res = { + LastName: traits.last_name || properties.last_name, + FirstName: traits.first_name || properties.first_name, + Email: traits.email || properties.email, + MailingCity: traits.address?.["city"] || properties.address?.["city"], + MailingState: traits.address?.["state"] || properties.address?.["state"], + MailingCountry: traits.address?.["country"] || properties.address?.["country"], + MailingPostalCode: traits.address?.["postal_code"] || properties.address?.["postal_code"], + MailingStreet: traits.address?.["street"] || properties.address?.["street"], + ...omit(contextTraits, knownProps), + ...omit(properties, knownProps), + ...omit(traits, knownProps), + }; + if (operation === "insert") { + if (!res.LastName) { + throw new Error("'Contact' object requires 'LastName' property"); + } + } + return res; +} + +function defaultMapping(event: AnalyticsServerEvent, operation: SalesforceEnvelope["SALESFORCE_OPERATION"]): any { + const contextTraits = event.context?.traits || {}; + const traits = event.traits || {}; + const properties = event.properties || {}; + return { + ...contextTraits, + ...properties, + ...traits, + }; +} + +async function filterOutAvailableProperties( + ctx: FullContext, + cred: Creds, + payload: any, + sobject: string, + operation: SalesforceEnvelope["SALESFORCE_OPERATION"], + logLevel: "warn" | "info" | "debug" | "error" = "debug" +) { + const log = ctx.log[logLevel]; + const availableProps = await availableProperties(ctx, cred, sobject); + if (!availableProps) { + throw new Error(`Object type '${sobject}' not found`); + } + // check for required properties for insert operation + if (operation === "insert") { + for (const [prop, mod] of Object.entries(availableProps)) { + if (hasPermission(mod, SalesforcePropertyPermissionBit.Required) && !payload[prop]) { + throw new Error(`'${sobject}' object requires '${prop}' property`); + } + } + } + for (const key of Object.keys(payload)) { + const prop = availableProps?.[key]; + if (typeof prop === "undefined") { + log(`Property '${key}' is not available in the '${sobject}' object's schema, removing from payload`); + delete payload[key]; // Remove unavailable properties + } else { + if (!hasPermission(prop, SalesforcePropertyPermissionBit.Updateable) && operation !== "insert") { + log(`'${sobject}' object's property '${key}' is not updateable, removing from payload`); + delete payload[key]; // Remove properties that are not updateable for update or upsert operations + } else if (!hasPermission(prop, SalesforcePropertyPermissionBit.Createable) && operation === "insert") { + log(`'${sobject}' object's property '${key}' is not createable, removing from payload`); + delete payload[key]; // Remove properties that are not createable for insert operations + } + } + } +} + +const SalesforceDestination: JitsuFunction = async (event, ctx) => { + const { props, log } = ctx; + if (!props.authorized) { + throw new Error("Salesforce destination is not authorized. Please authorize destination in Jitsu UI."); + } + const envelope = SalesforceEnvelope.parse(event || {}); + //log.info(`Processing Salesforce event with envelope: ${JSON.stringify(envelope)}`); + if (!envelope.SALESFORCE_SOBJECT) { + switch (event.type) { + case "identify": + envelope.SALESFORCE_SOBJECT = "Lead"; + break; + case "group": + envelope.SALESFORCE_SOBJECT = "Account"; + break; + default: + throw new Error( + `SALESFORCE_SOBJECT is not specified. And sobject cannot be determined based on event type '${event.type}'` + ); + } + } + if (["update", "upsert", "delete"].includes(envelope.SALESFORCE_OPERATION)) { + if (typeof envelope.SALESFORCE_MATCHERS !== "object" || Object.keys(envelope.SALESFORCE_MATCHERS).length === 0) { + throw new Error(`SALESFORCE_MATCHERS is required for SALESFORCE_OPERATION '${envelope.SALESFORCE_OPERATION}'`); + } + } + const updatedAtStr = ctx.destination.updatedAt?.toString(); + const cacheKey = `${ctx.destination.id}-${updatedAtStr}`; + let cred: Creds | undefined = credCache.get(cacheKey); + if (!cred) { + const oauth = await getOauthCreds(props.oauthIntegrationId!, props.oauthConnectionId!); + cred = Creds.parse(oauth.credentials.raw); + credCache.set(cacheKey, cred); + } + + let recordId; + if (["update", "upsert", "delete"].includes(envelope.SALESFORCE_OPERATION)) { + recordId = envelope.SALESFORCE_MATCHERS?.Id; + if (!recordId) { + // If Id is not provided, we need to look it up based on SALESFORCE_MATCHERS + recordId = await lookupMatchers( + ctx, + cred, + envelope.SALESFORCE_MATCHERS!, + envelope.SALESFORCE_SOBJECT, + envelope.SALESFORCE_MATCHERS_OPERATOR, + envelope.SALESFORCE_OPERATION + ); + if (recordId === 0) { + if (envelope.SALESFORCE_OPERATION !== "upsert") { + return; // No record found, nothing to update or delete + } else { + envelope.SALESFORCE_OPERATION = "insert"; // If upsert and no record found, change operation to insert + } + } else if (!recordId) { + return; + } + } + } + if (envelope.SALESFORCE_OPERATION === "delete") { + envelope.SALESFORCE_PAYLOAD = undefined; + } else if (!envelope.SALESFORCE_PAYLOAD) { + switch (envelope.SALESFORCE_SOBJECT) { + case "Lead": + envelope.SALESFORCE_PAYLOAD = lead(event, envelope.SALESFORCE_OPERATION); + break; + case "Account": + envelope.SALESFORCE_PAYLOAD = account(event, envelope.SALESFORCE_OPERATION); + break; + case "Contact": + envelope.SALESFORCE_PAYLOAD = contact(event, envelope.SALESFORCE_OPERATION); + break; + default: + envelope.SALESFORCE_PAYLOAD = defaultMapping(event, envelope.SALESFORCE_OPERATION); + break; + } + await filterOutAvailableProperties( + ctx, + cred, + envelope.SALESFORCE_PAYLOAD!, + envelope.SALESFORCE_SOBJECT, + envelope.SALESFORCE_OPERATION, + "debug" + ); + } else { + await filterOutAvailableProperties( + ctx, + cred, + envelope.SALESFORCE_PAYLOAD!, + envelope.SALESFORCE_SOBJECT, + envelope.SALESFORCE_OPERATION, + "warn" + ); + if (["update", "upsert", "insert"].includes(envelope.SALESFORCE_OPERATION)) { + const newPayload = await performOps( + ctx, + cred, + envelope.SALESFORCE_SOBJECT, + envelope.SALESFORCE_PAYLOAD!, + envelope.SALESFORCE_OPERATION, + recordId + ); + if (Object.keys(newPayload).length === 0) { + log.info( + `${envelope.SALESFORCE_SOBJECT} Id: ${recordId}. No properties changed, skipping ${envelope.SALESFORCE_OPERATION}` + ); + return; // No properties to update, skip the request + } + envelope.SALESFORCE_PAYLOAD = newPayload; + } + } + let httpMethod = "POST"; + let httpPath = `/sobjects/${envelope.SALESFORCE_SOBJECT}`; + if (envelope.SALESFORCE_OPERATION === "update" || envelope.SALESFORCE_OPERATION === "upsert") { + httpMethod = "PATCH"; + httpPath += "/" + recordId; + } else if (envelope.SALESFORCE_OPERATION === "delete") { + httpMethod = "DELETE"; + httpPath += "/" + recordId; + envelope.SALESFORCE_PAYLOAD = undefined; + } + const slash = cred.instance_url.endsWith("/") ? "" : "/"; + + const httpRequest = { + method: httpMethod, + path: httpPath, + payload: envelope.SALESFORCE_PAYLOAD, + }; + + try { + for (let i = 0; i < 2; i++) { + const method = httpRequest.method || "POST"; + const result = await ctx.fetch(`${cred.instance_url}${slash}services/data/${API_VERSION}${httpRequest.path}`, { + method, + headers: { + ...(httpRequest.payload ? { "Content-type": "application/json", "Content-Encoding": "gzip" } : {}), + Authorization: `Bearer ${cred.access_token}`, + }, + ...(httpRequest.payload ? { body: zlib.gzipSync(JSON.stringify(httpRequest.payload)) } : {}), + }); + if (!result.ok) { + if (result.status === 401) { + const oauth = await getOauthCreds(props.oauthIntegrationId!, props.oauthConnectionId!); + cred = Creds.parse(oauth.credentials.raw); + credCache.set(cacheKey, cred); + if (i === 0) { + log.info(`Retrying Salesforce ${method} ${httpRequest.path} after re-authentication`); + continue; // Retry once after re-authentication + } + } else if (result.status === 400 || result.status === 404) { + ctx.log.error( + `Salesforce ${method} ${httpRequest.path}:${ + httpRequest.payload ? `${JSON.stringify(httpRequest.payload)} --> ` : "" + }${result.status} ${await result.text()}` + ); + return; + } + throw new Error( + `Salesforce ${method} ${httpRequest.path}:${ + httpRequest.payload ? `${JSON.stringify(httpRequest.payload)} --> ` : "" + }${result.status} ${await result.text()}` + ); + } else { + return; + } + } + } catch (e: any) { + if (httpRequest.method === "PATCH" && e.message.includes("timeout")) { + // PATCH request may timeout while being successfully processed on Salesforce side + log.warn( + `Salesforce ${httpRequest.method} ${httpRequest.path}: Timeout error, but the request may have been successful. Error: ${e.message}` + ); + return; + } else { + throw new RetryError(e.message); + } + } +}; + +// Salesforce SOQL spec requires any single quotes to be escaped. +const escapeQuotes = (value: string) => value.replace(/'/g, "\\'"); + +// Salesforce field names should have only characters in {a-z, A-Z, 0-9, _}. +const removeInvalidChars = (value: string) => value.replace(/[^a-zA-Z0-9_]/g, ""); + +// Pre-formats trait values based on datatypes for correct SOQL syntax +const typecast = (value: any) => { + switch (typeof value) { + case "boolean": + return value; + case "number": + return value; + case "string": + return `'${escapeQuotes(value)}'`; + case "object": + if (value === null) { + return "null"; // Salesforce SOQL allows null values + } + default: + throw new Error("Unsupported datatype for record matcher traits - " + typeof value); + } +}; + +const buildQuery = ( + matchers: Record, + sobject: string, + soqlOperator: SalesforceEnvelope["SALESFORCE_MATCHERS_OPERATOR"] +) => { + let soql = `SELECT Id FROM ${sobject} WHERE `; + const entries = Object.entries(matchers); + let i = 0; + for (const [key, value] of entries) { + let token = `${removeInvalidChars(key)} = ${typecast(value)}`; + if (i < entries.length - 1) { + token += " " + soqlOperator + " "; + } + soql += token; + i += 1; + } + return soql; +}; + +const availableProperties = async ( + ctx: FullContext, + cred: Creds, + sobject: string +): Promise => { + try { + const cacheKey = `${sobject}-${cred.instance_url}`; + let sobjectData: Sobject | undefined = sobjectCache.get(cacheKey); + if (!sobjectData || Date.now() - sobjectData.lastChecked.getTime() > sobjectMetaRefreshPeriodMs) { + const slash = cred.instance_url.endsWith("/") ? "" : "/"; + const headers: Record = { + Authorization: `Bearer ${cred.access_token}`, + }; + if (sobjectData?.lastModifiedDate) { + headers["If-Modified-Since"] = sobjectData.lastModifiedDate; + } + const res = await ctx.fetch( + `${cred.instance_url}${slash}services/data/${API_VERSION}/sobjects/${sobject}/describe`, + { + headers, + } + ); + if (res.status === 304) { + ctx.log.debug(`Salesforce object ${sobject} description not modified, using cached data`); + sobjectData!.lastChecked = new Date(); + return sobjectData!.properties; // Return cached properties if not modified + } else if (res.status === 404) { + return undefined; // Return undefined if the object type is not found + } + if (!res.ok) { + throw new Error(`Failed to fetch Salesforce object description: ${res.status} ${await res.text()}`); + } + const data = await res.json(); + const properties = Object.fromEntries( + data.fields.map((f: any) => [ + f.name, + ((f.updateable ? 1 << SalesforcePropertyPermissionBit.Updateable : 0) | + (f.createable ? 1 << SalesforcePropertyPermissionBit.Createable : 0) | + (f.externalId ? 1 << SalesforcePropertyPermissionBit.ExternalId : 0) | + (f.filterable ? 1 << SalesforcePropertyPermissionBit.Filterable : 0) | + (f.createable && !f.nillable && !f.defaultedOnCreate + ? 1 << SalesforcePropertyPermissionBit.Required + : 0)) as SalesforcePropertyPermissions, + ]) + ); + sobjectData = { + name: sobject, + properties, + lastModifiedDate: res.headers.get("Last-Modified") || "", + lastChecked: new Date(), + }; + sobjectCache.set(cacheKey, sobjectData); + sobjectCache.ttl(cacheKey, sobjectCacheTTL); + return properties; + } else { + //ctx.log.info(`Using cached properties for Salesforce object '${sobject}': ${JSON.stringify(sobjectData)}`); + return sobjectData.properties; + } + } catch (e: any) { + throw new RetryError(`Error during SALESFORCE_PROPERTIES lookup: ${e.message}`); + } +}; + +const Operations: Record any> = { + add: (sourceValue: any, eventValue: any) => { + return parseNumber(sourceValue, 0) + parseNumber(eventValue, 0); + }, + dateAdd: (sourceValue: any, eventValue: any) => { + return new Date((sourceValue ? new Date(sourceValue).getTime() : Date.now()) + parseNumber(eventValue, 0) * 1000); // Add days to date + }, + subtract: (sourceValue: any, eventValue: any) => { + return parseNumber(sourceValue, 0) - parseNumber(eventValue, 0); + }, + dateSubtract: (sourceValue: any, eventValue: any) => { + return new Date((sourceValue ? new Date(sourceValue).getTime() : Date.now()) - parseNumber(eventValue, 0) * 1000); // Add days to date + }, + multiply: (sourceValue: any, eventValue: any) => { + return parseNumber(sourceValue, 0) * parseNumber(eventValue, 1); + }, + divide: (sourceValue: any, eventValue: any) => { + const divisor = parseNumber(eventValue, 1); // Avoid division by zero + if (divisor === 0) { + throw new Error("Division by zero is not allowed"); + } + return parseNumber(sourceValue, 0) / divisor; + }, + setOnce: (sourceValue: any, eventValue: any) => { + return sourceValue === undefined || sourceValue === null ? eventValue : sourceValue; // Set only if sourceValue is undefined + }, + concat: (sourceValue: any, eventValue: any) => { + return `${sourceValue ?? ""}${eventValue ?? ""}`; // Concatenate values, treating undefined as empty string + }, + prefix: (sourceValue: any, eventValue: any) => { + return `${eventValue ?? ""}${sourceValue ?? ""}`; // Prefix eventValue to sourceValue, treating undefined as empty string + }, +}; + +type Operation = { + op: keyof typeof Operations; + value?: any; +}; + +async function performOps( + ctx: FullContext, + cred: Creds, + sobject: string, + payload: Record, + operation: SalesforceEnvelope["SALESFORCE_OPERATION"], + recordId?: string +): Promise> { + const operations: Record = {}; + const newPayload: Record = {}; + // Check that payload has operation object as value + for (const [key, value] of Object.entries(payload)) { + if (typeof value === "object" && value?.op) { + operations[key] = value; + } + } + if (operation === "insert" && Object.keys(operations).length === 0) { + return payload; // No operations to perform, return original payload + } + let sourceData: any = {}; + + if (recordId) { + const slash = cred.instance_url.endsWith("/") ? "" : "/"; + const fieldsQuery = `?fields=${Object.keys(payload).join(",")}`; + const res = await ctx.fetch( + `${cred.instance_url}${slash}services/data/${API_VERSION}/sobjects/${sobject}/${recordId}${fieldsQuery}`, + { + headers: { + Authorization: `Bearer ${cred.access_token}`, + }, + } + ); + if (!res.ok) { + throw new Error(`Failed to fetch Salesforce object: ${res.status} ${await res.text()}`); + } + sourceData = await res.json(); + } + for (const [key, pValue] of Object.entries(payload)) { + const sourceValue = sourceData[key]; + let modifiedValue = pValue; + const op = operations[key]; + if (op) { + if (!Operations[op.op]) { + ctx.log.error(`Unsupported operation '${op.op}' for property '${key}', skipping`); + continue; // Skip unsupported operations + } + modifiedValue = Operations[op.op](sourceValue, op.value); + } + if (modifiedValue !== sourceValue) { + newPayload[key] = modifiedValue; + } + } + return newPayload; +} + +const lookupMatchers = async ( + ctx: FullContext, + cred: Creds, + matchers: Record, + sobject: string, + soqlOperator: SalesforceEnvelope["SALESFORCE_MATCHERS_OPERATOR"], + operation: SalesforceEnvelope["SALESFORCE_OPERATION"] +): Promise => { + try { + const SOQLQuery = buildQuery(matchers, sobject, soqlOperator); + const slash = cred.instance_url.endsWith("/") ? "" : "/"; + + const res = await ctx.fetch( + `${cred.instance_url}${slash}services/data/${API_VERSION}/query/?q=${encodeURIComponent(SOQLQuery)}`, + { + headers: { + Authorization: `Bearer ${cred.access_token}`, + }, + } + ); + if (!res.ok) { + if (res.status === 400) { + ctx.log.error(`SALESFORCE_MATCHERS lookup failed: ${res.status} ${await res.text()}`); + return; + } + throw new Error(`lookup failed: ${res.status} ${await res.text()}`); + } + const data = await res.json(); + if (!data || data.totalSize === undefined) { + throw new Error("lookup response missing expected fields"); + } + + if (data.totalSize === 0) { + if (operation === "upsert") { + ctx.log.debug("No record found with given SALESFORCE_MATCHERS: " + SOQLQuery + ", proceeding with insert"); + } else { + ctx.log.error("No record found with given SALESFORCE_MATCHERS: " + SOQLQuery); + } + return 0; // Return 0 to indicate no record found, which is useful for upsert operations + } + + if (data.totalSize > 1) { + ctx.log.error("Multiple records returned with given SALESFORCE_MATCHERS: " + SOQLQuery); + return; + } + + if (!data.records || !data.records[0] || !data.records[0].Id) { + throw new Error("lookup response missing expected fields"); + } + + return data.records[0].Id; + } catch (e: any) { + throw new RetryError(`Error during SALESFORCE_MATCHERS lookup: ${e.message}`); + } +}; + +SalesforceDestination.displayName = "salesforce-destination"; + +export default SalesforceDestination; diff --git a/libs/core-functions/src/functions/segment-destination.ts b/libs/core-functions/src/functions/segment-destination.ts index 220b02284..9770448c3 100644 --- a/libs/core-functions/src/functions/segment-destination.ts +++ b/libs/core-functions/src/functions/segment-destination.ts @@ -1,6 +1,8 @@ import { JitsuFunction } from "@jitsu/protocols/functions"; +import { RetryError } from "@jitsu/functions-lib"; import type { AnalyticsServerEvent } from "@jitsu/protocols/analytics"; import { SegmentCredentials } from "../meta"; +import omit from "lodash/omit"; export type HttpRequest = { method?: string; @@ -24,9 +26,13 @@ const SegmentDestination: JitsuFunction ` : "" - }${result.status} ${await result.text()}`; if (result.status !== 200) { - ctx.log.error(logMessage); + throw new RetryError( + `Segment ${httpRequest.method} ${httpRequest.url}:${ + httpRequest.payload ? `${JSON.stringify(httpRequest.payload)} --> ` : "" + }${result.status} ${await result.text()}` + ); } else { - ctx.log.debug(logMessage); + ctx.log.debug(`Segment ${httpRequest.method} ${httpRequest.url}: ${result.status} ${await result.text()}`); } } catch (e: any) { - throw new Error( - `Failed to send event to MixPanel: ${httpRequest.method} ${httpRequest.url} ${JSON.stringify( + throw new RetryError( + `Failed to send event to Segment: ${httpRequest.method} ${httpRequest.url} ${JSON.stringify( httpRequest.payload )}: ${e?.message}` ); diff --git a/libs/core-functions/src/functions/udf_isolated_vm.ts b/libs/core-functions/src/functions/udf_isolated_vm.ts deleted file mode 100644 index 3f781e6cf..000000000 --- a/libs/core-functions/src/functions/udf_isolated_vm.ts +++ /dev/null @@ -1,39 +0,0 @@ -// import { JitsuFunction } from "@jitsu/protocols/functions"; -// import { AnalyticsServerEvent } from "@jitsu/protocols/analytics"; -// import ivm from "isolated-vm"; -// -// export const UDFWrapper = (functionId, functionCode: string) => { -// const startMs = new Date().getTime(); -// const isolate = new ivm.Isolate({ memoryLimit: 10 }); -// const context = isolate.createContextSync(); -// const jail = context.global; -// // This make the global object available in the context as 'global'. We use 'derefInto()' here -// // because otherwise 'global' would actually be a Reference{} object in the new isolate. -// jail.setSync("global", jail.derefInto()); -// const module = isolate.compileModuleSync(functionCode, { filename: `${functionId}.js` }); -// module.instantiateSync(context, (specifier: string) => { -// throw new Error(`import not allowed: ${specifier}`); -// }); -// module.evaluateSync(); -// const exported = module.namespace; -// let userFunction = exported.getSync("default", { -// reference: true, -// }); -// if (!userFunction) { -// userFunction = exported.getSync("onEvent", { -// reference: true, -// }); -// } -// if (!userFunction) { -// throw new Error("Function not found. Please export default function or 'onEvent' function."); -// } -// console.log(`udf compile time ${new Date().getTime() - startMs}ms`); -// return { -// // userFunction: async (event, ctx) => { -// // return userFunction.apply(jail, [event, ctx], { arguments: { reference: true }, result: { promise: true } }); -// // }, -// close: () => { -// isolate.dispose(); -// }, -// }; -// }; diff --git a/libs/core-functions/src/functions/udf_vm2.ts b/libs/core-functions/src/functions/udf_vm2.ts deleted file mode 100644 index 7978176b6..000000000 --- a/libs/core-functions/src/functions/udf_vm2.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { NodeVM } from "vm2"; -import { getLog } from "juava"; -import * as swc from "@swc/core"; - -const log = getLog("udf-vm2-wrapper"); - -export const UDFWrapper = (functionId, name, functionCode: string) => { - log.atInfo().log(`[${functionId}] Compiling UDF function '${name}'`); - - const startMs = new Date().getTime(); - try { - functionCode = swc.transformSync(functionCode, { - filename: `index.js`, - module: { type: "commonjs" }, - }).code; - // functionCode = transformSync(functionCode, { - // presets: [preset], - // filename: `index.js`, - // plugins: [plugin], - // }).code; - const vm = new NodeVM({ - require: { - external: [functionId], - context: "sandbox", - }, - }); - - //const userFunction = vm.run(functionCode, `${functionId}.js`); - const userFunction = vm.run( - `${functionCode} -const exported = module.exports; -let userFunction; -if (typeof exported === "function") { - userFunction = exported; -} else { - userFunction = exported.default; - if (!userFunction && Object.keys(exported).length === 1) { - userFunction = exported[Object.keys(exported)[0]]; - } -} -const wrapped = async function(event, ctx) { - if (!userFunction || typeof userFunction !== "function") { - throw new Error("Function not found. Please export default function."); - } - console = {...console, - log: ctx.log.info, - error: ctx.log.error, - warn: ctx.log.warn, - debug: ctx.log.debug, - info: ctx.log.info, - assert: (asrt, ...args) => { - if (!asrt) { - ctx.log.error("Assertion failed",...args); - } - } - }; - return userFunction(event, ctx); -} -module.exports = wrapped; -`, - `${functionId}.js` - ); - if (!userFunction || typeof userFunction !== "function") { - throw new Error("Function not found. Please export default function."); - } - log.atInfo().log(`[${functionId}] udf compile time ${new Date().getTime() - startMs}ms`); - return { - userFunction, - close: () => {}, - }; - } catch (e) { - return { - userFunction: () => { - throw new Error(`Cannot compile function: ${e}`); - }, - close: () => {}, - }; - } -}; diff --git a/libs/core-functions/src/functions/user-recognition.ts b/libs/core-functions/src/functions/user-recognition.ts index fca9fc09f..e7f7de453 100644 --- a/libs/core-functions/src/functions/user-recognition.ts +++ b/libs/core-functions/src/functions/user-recognition.ts @@ -1,110 +1,134 @@ -import { z } from "zod"; -import { JitsuFunction, SystemContext } from "@jitsu/protocols/functions"; +import { AnonymousEventsStore, JitsuFunction } from "@jitsu/protocols/functions"; import { AnalyticsServerEvent } from "@jitsu/protocols/analytics"; -import { requireDefined } from "juava"; -import { get, set, merge } from "lodash"; +import { transfer } from "@jitsu/functions-lib"; -export const UserRecognitionConfig = z.object({ - /** - * Where to look for anonymous id, an array of JSON paths - * ["anonymousId", "context.AnonymousId"] - */ - anonymousIdFields: z.array(z.string()).default(["anonymousId"]), - identifierFields: z.array(z.string()).default(["userId", "context.traits"]), - eventTypes: z.array(z.string()).default(["page", "track", "screen"]), - lookbackWindowDays: z.number().default(30), - collectionId: z.string().default(""), -}); +export const UserRecognitionParameter = "_JITSU_UR_MESSAGE_ID"; -export type UserRecognitionConfig = z.infer; +const IDENTIFYING_TRAITS_ENV = "IDENTIFYING_TRAITS"; -// const DefaultConfig: UserRecognitionConfig = UserRecognitionConfig.parse({}); +const lookbackWindowDays = 30; +const eventTypes = ["page", "track", "screen"]; -const UserRecognitionFunction: JitsuFunction = async (event, ctx) => { - if (!ctx.connection?.options.deduplicate || !ctx.connection?.options.primaryKey) { +const UserRecognitionFunction: JitsuFunction = async (event, ctx) => { + if ( + (!ctx.connection.options.deduplicate || !ctx.connection.options.primaryKey) && + ctx.destination.type !== "profiles" + ) { ctx.log.error( `User Recognition function requires connection to be configured with 'deduplicate' and 'primaryKey' options.` ); return event; } - const config = UserRecognitionConfig.parse(ctx.props || {}); - if (!config.eventTypes.includes(event.type)) { - ctx.log.debug( - `Event type ${event.type} is not in the list of event types to process. Message ID:${event.messageId}` - ); - } - const systemContext = requireDefined((ctx as any as SystemContext).$system, `$system context is not available`); - const anonEvStore = systemContext.anonymousEventsStore; - - const collectionName = `UR_${config.collectionId ? `${config.collectionId}_` : ""}${ctx.connection?.id}`; - - const anonId = getAnonId(event, config.anonymousIdFields); + const anonId = event.anonymousId; if (!anonId) { ctx.log.warn(`No anonymous id found. Message ID:${event.messageId}`); return event; } - const identifiedFields = getIdentifiedFields(event, config.identifierFields); - if (!identifiedFields) { - try { - await anonEvStore.addEvent(collectionName, anonId, event, config.lookbackWindowDays); + const userId = event.userId; + const identifyingTraits = ctx.connection.options?.functionsEnv?.[IDENTIFYING_TRAITS_ENV] + ? ctx.connection.options.functionsEnv[IDENTIFYING_TRAITS_ENV].split(",").map((t: string) => t.trim()) + : []; + let identifiedEvent = !!userId; + + const anonEvStore = ctx["anonymousEventsStore"] as AnonymousEventsStore; + const collectionName = `UR_${ctx.connection?.id}`; + + if (event.type === "identify") { + if (!identifiedEvent && identifyingTraits.length > 0) { + const traits = event.traits || {}; + for (const trait of identifyingTraits) { + if (traits[trait]) { + identifiedEvent = true; + break; + } + } + } + if (identifiedEvent) { + const identifiedFields = { + userId, + context: { + traits: event.traits, + }, + }; + // evict anonymous events from user_recognition collection + const res = await anonEvStore.evictEvents(collectionName, anonId).then(evs => { + return evs.map(anonEvent => { + //merge anonymous event with identified fields + anonEvent.userId = userId; + anonEvent.context = anonEvent.context || {}; + if (!anonEvent.context.traits) { + anonEvent.context.traits = event.traits || {}; + } else { + transfer(anonEvent.context.traits, event.traits); + } + anonEvent[UserRecognitionParameter] = event.messageId; + return anonEvent; + }); + }); + if (res.length === 0) { + ctx.log.debug( + `No events found for anonymous id: ${anonId} with identified fields: ${JSON.stringify( + identifiedFields + )} Message ID:${event.messageId}` + ); + } else { + ctx.log.info( + `${res.length} events for anonymous id: ${anonId} was updated with id fields: ${JSON.stringify( + identifiedFields + )} by Message ID:${event.messageId}` + ); + return [event, ...res]; + } + } else { ctx.log.debug( - `Event for for anonymous id: ${anonId} inserted to User Recognition collection. Message ID:${event.messageId}` - ); - } catch (e) { - ctx.log.error( - `Failed to insert anonymous event for anonymous id: ${anonId} to User Recognition collection. Message ID:${event.messageId} Error: ${e}` + `Identify event with not enough identifying information. UserId: ${userId} and traits: ${JSON.stringify( + event.traits + )}. Message ID:${event.messageId}` ); } - return event; - } - // evict anonymous events from user_recognition collection - const res = await anonEvStore.evictEvents(collectionName, anonId).then(evs => { - return evs.map(anonEvent => { - //merge anonymous event with identified fields - return merge(anonEvent, identifiedFields); - }); - }); - if (res.length === 0) { - ctx.log.debug( - `No events found for anonymous id: ${anonId} with identified fields: ${JSON.stringify( - identifiedFields - )} Message ID:${event.messageId}` - ); - return event; + } else if (eventTypes.includes(event.type)) { + if (!identifiedEvent && identifyingTraits.length > 0) { + const traits = event.context?.traits || {}; + for (const trait of identifyingTraits) { + if (traits[trait]) { + identifiedEvent = true; + break; + } + } + } + if (!identifiedEvent) { + try { + await anonEvStore.addEvent(collectionName, anonId, event, lookbackWindowDays); + ctx.log.debug( + `Event for for anonymous id: ${anonId} inserted to User Recognition collection. Message ID:${event.messageId}` + ); + } catch (e) { + ctx.log.error( + `Failed to insert anonymous event for anonymous id: ${anonId} to User Recognition collection. Message ID:${event.messageId} Error: ${e}` + ); + } + } } else { - ctx.log.info( - `${res.length} events for anonymous id: ${anonId} was updated with id fields: ${JSON.stringify( - identifiedFields - )} by Message ID:${event.messageId}` + ctx.log.debug( + `Event type ${event.type} is not in the list of event types to process. Message ID:${event.messageId}` ); - return [event, ...res]; } }; -function getAnonId(event: AnalyticsServerEvent, anonymousId: string[]): string | undefined { - for (const path of anonymousId) { - const id = get(event, path); - if (id) { - return id; - } - } - return undefined; -} - -function getIdentifiedFields(event: AnalyticsServerEvent, identifierFields: string[]): any | undefined { - const res = {}; - let found = false; - for (const path of identifierFields) { - const f = get(event, path); - if (f && !(typeof f === "object" && Object.keys(f).length === 0)) { - found = true; - set(res, path, f); - } - } - if (!found) { - return undefined; - } - return res; -} +// function getIdentifiedFields(event: AnalyticsServerEvent, identifierFields: string[]): any | undefined { +// const res = {}; +// let found = false; +// for (const path of identifierFields) { +// const f = get(event, path); +// if (f && !(typeof f === "object" && Object.keys(f).length === 0)) { +// found = true; +// set(res, path, f); +// } +// } +// if (!found) { +// return undefined; +// } +// return res; +// } export default UserRecognitionFunction; diff --git a/libs/core-functions/src/functions/webhook-destination.ts b/libs/core-functions/src/functions/webhook-destination.ts index ef21af2e7..2bc5dac05 100644 --- a/libs/core-functions/src/functions/webhook-destination.ts +++ b/libs/core-functions/src/functions/webhook-destination.ts @@ -1,21 +1,110 @@ import { JitsuFunction } from "@jitsu/protocols/functions"; +import { HTTPError, RetryError } from "@jitsu/functions-lib"; import type { AnalyticsServerEvent } from "@jitsu/protocols/analytics"; import { WebhookDestinationConfig } from "../meta"; +import { bulkerPartitionParam, MetricsMeta } from "./lib"; + +const bulkerBase = process.env.BULKER_URL; +const bulkerAuthKey = process.env.BULKER_AUTH_KEY; + +const macrosPattern = /\{\{\s*([\w.-]+)\s*}}/g; const WebhookDestination: JitsuFunction = async (event, ctx) => { - const headers = ctx.props.headers || []; - await ctx.fetch(ctx.props.url, { - method: ctx.props.method || "POST", - body: JSON.stringify(event), - headers: { - "Content-Type": "application/json", - ...headers.reduce((res, header) => { - const [key, value] = header.split(":"); - return { ...res, [key]: value }; - }, {}), - }, - }); - return event; + if (ctx["connectionOptions"]?.mode === "batch" && bulkerBase) { + const metricsMeta: Omit = { + workspaceId: ctx.workspace.id, + streamId: ctx.source.id, + destinationId: ctx.destination.id, + connectionId: ctx.connection.id, + functionId: "builtin.destination.bulker", + }; + + try { + const res = await ctx.fetch( + `${bulkerBase}/post/${ctx.connection.id}?tableName=${ + event.event || event.type + }&modeOverride=batch${bulkerPartitionParam(ctx, event)}`, + { + method: "POST", + body: JSON.stringify(event), + headers: { + ...(bulkerAuthKey ? { Authorization: `Bearer ${bulkerAuthKey}` } : {}), + metricsMeta: JSON.stringify(metricsMeta), + }, + }, + { log: false } + ); + if (!res.ok) { + throw new HTTPError( + `Failed to batch event. HTTP Error: ${res.status} ${res.statusText}`, + res.status, + (await res.text())?.substring(0, 255) + ); + } else { + ctx.log.debug( + `Failed to batch event. HTTP Status: ${res.status} ${res.statusText} Response: ${( + await res.text() + )?.substring(0, 255)}` + ); + } + return event; + } catch (e: any) { + throw new RetryError(e.message); + } + } else { + try { + let payload: string; + if (ctx.props.customPayload) { + const cp = JSON.parse(ctx.props.payload ?? ""); + payload = cp.code.replace(macrosPattern, (match, macroName) => { + switch (macroName.toUpperCase()) { + case "EVENT": + return JSON.stringify(event); + case "EVENTS": + return JSON.stringify([event]); + case "EVENTS_COUNT": + return "1"; + case "NAME": + case "EVENTS_NAME": + return event.event || event.type; + default: + if (macroName.startsWith("env.")) { + return ctx.connection.options?.["functionsEnv"]?.[macroName.substring(4)] || ""; + } + return match; + } + }); + } else { + payload = JSON.stringify(event); + } + const headers = ctx.props.headers || []; + const res = await ctx.fetch(ctx.props.url, { + method: ctx.props.method || "POST", + body: payload, + headers: { + "Content-Type": "application/json", + ...headers.reduce((res, header) => { + const [key, value] = header.split(":"); + return { ...res, [key]: value }; + }, {}), + }, + }); + if (!res.ok) { + throw new HTTPError( + `HTTP Error: ${res.status} ${res.statusText}`, + res.status, + (await res.text())?.substring(0, 255) + ); + } else { + ctx.log.debug( + `HTTP Status: ${res.status} ${res.statusText} Response: ${(await res.text())?.substring(0, 255)}` + ); + } + return event; + } catch (e: any) { + throw new RetryError(e.message); + } + } }; WebhookDestination.displayName = "webhook-destination"; diff --git a/libs/core-functions/src/index.ts b/libs/core-functions/src/index.ts index e4c8e2d75..b6f053f8e 100644 --- a/libs/core-functions/src/index.ts +++ b/libs/core-functions/src/index.ts @@ -2,6 +2,7 @@ import { BuiltinDestinationFunctionName, BuiltinFunctionName, BuiltinTransformationFunctionName, + FuncReturn, JitsuFunction, } from "@jitsu/protocols/functions"; import BulkerDestination from "./functions/bulker-destination"; @@ -13,16 +14,35 @@ import UserRecognitionFunction from "./functions/user-recognition"; import MongodbDestination from "./functions/mongodb-destination"; import JuneDestination from "./functions/june-destination"; import SegmentDestination from "./functions/segment-destination"; +import AmplitudeDestination from "./functions/amplitude-destination"; +import FacebookConversionsApi from "./functions/facebook-conversions"; +import IntercomDestination from "./functions/intercom-destination"; +import HubspotDestination from "./functions/hubspot-destination"; +import BrazeDestination from "./functions/braze-destination"; +import { ProfilesFunction } from "./functions/profiles-functions"; +import SalesforceDestination from "./functions/salesforce-destination"; const builtinDestinations: Record = { "builtin.destination.bulker": BulkerDestination as JitsuFunction, "builtin.destination.mixpanel": MixpanelDestination as JitsuFunction, + "builtin.destination.intercom": IntercomDestination as JitsuFunction, "builtin.destination.segment-proxy": SegmentDestination as JitsuFunction, "builtin.destination.june": JuneDestination as JitsuFunction, + "builtin.destination.braze": BrazeDestination as JitsuFunction, "builtin.destination.ga4": Ga4Destination as JitsuFunction, "builtin.destination.webhook": WebhookDestination as JitsuFunction, "builtin.destination.posthog": PosthogDestination as JitsuFunction, "builtin.destination.mongodb": MongodbDestination as JitsuFunction, + "builtin.destination.amplitude": AmplitudeDestination as JitsuFunction, + "builtin.destination.facebook-conversions": FacebookConversionsApi as JitsuFunction, + "builtin.destination.hubspot": HubspotDestination as JitsuFunction, + "builtin.destination.salesforce": SalesforceDestination as JitsuFunction, + "builtin.destination.devnull": () => undefined, + "builtin.destination.tag": () => undefined, + "builtin.destination.gtm": () => undefined, + "builtin.destination.logrocket": () => undefined, + "builtin.destination.ga4-tag": () => undefined, + "builtin.destination.profiles": ProfilesFunction as JitsuFunction, } as const; const builtinTransformations: Record = { @@ -39,12 +59,51 @@ export function getBuiltinFunction(id: string): JitsuFunction | undefined { return builtinFunctions[fixedId]; } +export function isDropResult(result: FuncReturn): boolean { + return result === "drop" || (Array.isArray(result) && result.length === 0) || result === null || result === false; +} + export * as bulkerDestination from "./functions/bulker-destination"; -export { UDFWrapper } from "./functions/udf_vm2"; -export { createFullContext } from "./context"; +export { UserRecognitionParameter } from "./functions/user-recognition"; +export { UDFWrapper, UDFTestRun } from "./functions/lib/udf_wrapper"; +export type { UDFTestRequest, UDFTestResponse, logType } from "./functions/lib/udf_wrapper"; +export { ProfileUDFWrapper, ProfileUDFTestRun, mergeUserTraits } from "./functions/lib/profiles-udf-wrapper"; +export type { + ProfileUDFTestRequest, + ProfileUDFTestResponse, + ProfileUser, + ProfileUserProvider, + EventsProvider, + ProfileFunctionWrapper, + Profile, +} from "./functions/lib/profiles-udf-wrapper"; +export { makeLog, makeFetch, MultiEventsStore, DummyEventsStore, wrapperFunction } from "./functions/lib/index"; export * as mixpanelDestination from "./functions/mixpanel-destination"; export * as ga4Destination from "./functions/ga4-destination"; export * as webhookDestination from "./functions/webhook-destination"; export * as posthogDestination from "./functions/posthog-destination"; export * as mongodbDestination from "./functions/mongodb-destination"; export { mongodb, mongoAnonymousEventsStore } from "./functions/lib/mongodb"; +export type { + MetricsMeta, + RotorMetrics, + StoreMetrics, + FuncChainResult, + FunctionExecLog, + FunctionExecRes, + FunctionContext, + FunctionChainContext, + FetchType, + EventsStore, + JitsuFunctionWrapper, +} from "./functions/lib/index"; +export { httpAgent, httpsAgent } from "./functions/lib/http-agent"; +export * from "./functions/lib/store"; +export * from "./functions/lib/warehouse-store"; +export * from "./functions/lib/ua"; +export * from "./functions/lib/clickhouse-logger"; +export * from "./functions/profiles-functions"; +export * from "./functions/lib/crypto-code"; +export * from "./lib/inmem-store"; +export * from "./lib/config-types"; +export * from "./lib/entity-store"; diff --git a/libs/core-functions/src/lib/config-types.ts b/libs/core-functions/src/lib/config-types.ts new file mode 100644 index 000000000..83787d23a --- /dev/null +++ b/libs/core-functions/src/lib/config-types.ts @@ -0,0 +1,68 @@ +export type ShortDestinationConfig = { + id: string; + connectionId: string; + destinationType: string; + name: string; + credentials: any; + options: any; +}; + +export type StreamWithDestinations = { + stream: any; + backupEnabled: boolean; + destinations: ShortDestinationConfig[]; +}; + +export type FunctionConfig = { + id: string; + createdAt: Date; + updatedAt: Date; + workspaceId: string; + name: string; + code: string; + codeHash: string; +}; + +export type EnrichedConnectionConfig = { + id: string; + workspaceId: string; + special?: string; + updatedAt?: Date; + destinationId: string; + streamId: string; + streamName?: string; + metricsKeyPrefix: string; + usesBulker: boolean; + //destinationType + type: string; + options: any; + optionsHash: string; + + credentials: { + [key: string]: any; + }; + credentialsHash: string; +}; + +export type WorkspaceWithProfiles = { + id: string; + createdAt: Date; + updatedAt: Date; + name: string; + slug: string; + featuresEnabled: string[]; + profileBuilders: ProfileBuilder[]; +}; + +export type ProfileBuilder = { + id: string; + createdAt: Date; + updatedAt: Date; + debugTill: Date; + version: number; + workspaceId: string; + intermediateStorageCredentials: any; + connectionOptions: any; + destinationId: string; + functions: FunctionConfig[]; +}; diff --git a/libs/core-functions/src/lib/entity-store.ts b/libs/core-functions/src/lib/entity-store.ts new file mode 100644 index 000000000..800d11019 --- /dev/null +++ b/libs/core-functions/src/lib/entity-store.ts @@ -0,0 +1,128 @@ +import { createInMemoryStore } from "./inmem-store"; +import { getLog } from "juava"; + +const log = getLog("entity-store"); + +export type EntityStore = { + getObject: (id: string) => T | undefined; + getAll: () => Record; + toJSON: () => string; + enabled: boolean; + lastModified?: Date; +}; + +const DisabledStore: EntityStore = { + enabled: false, + getObject: () => undefined, + getAll: () => { + return {}; + }, + toJSON: () => "disabled", +}; + +const EmptyStore: EntityStore = { + enabled: true, + getObject: () => undefined, + getAll: () => { + return {}; + }, + toJSON: () => "", +}; + +function refreshFunc(storeId: string) { + return async ( + ifModifiedSince?: Date + ): Promise<{ lastModified: Date | undefined; store: EntityStore } | "not_modified"> => { + const repositoryBase = process.env.REPOSITORY_BASE_URL; + if (repositoryBase) { + const objs: Record = {}; + const headers: Record = {}; + let lastModified: Date | undefined = undefined; + if (process.env.REPOSITORY_AUTH_TOKEN) { + headers["Authorization"] = `Bearer ${process.env.REPOSITORY_AUTH_TOKEN}`; + } + if (ifModifiedSince) { + headers["If-Modified-Since"] = ifModifiedSince.toUTCString(); + } + try { + const base = repositoryBase.endsWith("/") ? repositoryBase : `${repositoryBase}/`; + const res = await fetch(`${base}${storeId}`, { + method: "GET", + headers: headers, + keepalive: true, + }); + if (res.status === 304) { + log.atDebug().log(`${storeId} nod modified: ${ifModifiedSince}`); + await res.text(); + return "not_modified"; + } + if (!res.ok) { + throw new Error(`Failed to load ${storeId} from repository: ${res.status} response: ${await res.text()}`); + } + const json: any = await res.json(); + for (const fn of json) { + objs[fn.id] = fn; + } + const lmString = res.headers.get("Last-Modified"); + if (lmString) { + lastModified = new Date(lmString); + } + log.atInfo().log(`${storeId} updated: ${lastModified} previous update date: ${ifModifiedSince}`); + return { + store: { + enabled: true, + getObject: (key: string) => { + return objs[key]; + }, + getAll() { + return objs; + }, + toJSON: () => { + return JSON.stringify(objs); + }, + lastModified: lastModified, + }, + lastModified: lastModified, + }; + } catch (e) { + throw new Error(`Failed to load ${storeId} from repository: ${e}`); + } + } else { + return { store: DisabledStore, lastModified: new Date() }; + } + }; +} + +export function storeFunc(storeId: string) { + return createInMemoryStore>({ + refreshIntervalMillis: process.env.REPOSITORY_REFRESH_PERIOD_SEC + ? parseInt(process.env.REPOSITORY_REFRESH_PERIOD_SEC) * 1000 + : 2000, + name: `${storeId}-store`, + localDir: process.env.REPOSITORY_CACHE_DIR, + serializer: (store: EntityStore) => (store.enabled ? store.toJSON() : ""), + deserializer: (serialized: string) => { + if (serialized) { + if (serialized === "disabled") { + return DisabledStore; + } + const store = JSON.parse(serialized); + return { + enabled: true, + getObject: (key: string): any => { + return store?.[key]; + }, + getAll: () => { + return store || {}; + }, + toJSON: () => { + return store ? JSON.stringify(store) : ""; + }, + }; + } else { + return EmptyStore; + } + }, + refresh: refreshFunc(storeId), + }); +} diff --git a/libs/core-functions/src/lib/inmem-store.ts b/libs/core-functions/src/lib/inmem-store.ts new file mode 100644 index 000000000..2f261f715 --- /dev/null +++ b/libs/core-functions/src/lib/inmem-store.ts @@ -0,0 +1,173 @@ +import { getErrorMessage, getLog } from "juava"; +import fs from "fs"; +import path from "path"; + +export type StoreDefinition = { + refreshIntervalMillis: number; + name: string; + refresh: (ifModifiedSince?: Date) => Promise<{ lastModified: Date | undefined; store: T } | "not_modified">; + serializer?: (arg: T) => string; + deserializer?: (arg: string) => T; + localDir?: string; +}; + +const log = getLog("inmem-store"); + +type Status = "ok" | "initializing" | "outdated" | "failed" | "stopped"; +export type InMemoryStore = { + status(): Status; + get(): Promise; + getCurrent(): T | undefined; + lastRefresh(): Date | undefined; + lastModified(): Date | undefined; + stop(): void; +}; + +function saveLocalCache(definition: StoreDefinition, instance: any) { + if (definition.localDir) { + fs.mkdirSync(definition.localDir, { recursive: true }); + const serialized = definition.serializer ? definition.serializer(instance) : JSON.stringify(instance); + const cacheFile = path.join(definition.localDir, `${definition.name}-latest.json`); + fs.writeFile(cacheFile, serialized, err => { + if (err) { + log.atWarn().withCause(err).log(`Failed to save local cache for ${definition.name}`); + } + }); + } +} + +function loadFromCache(definition: StoreDefinition): { updateDate: Date; instance: any } | undefined { + if (definition.localDir) { + const cacheFile = path.join(definition.localDir, `${definition.name}-latest.json`); + try { + const cached = fs.readFileSync(cacheFile).toString(); + return { + updateDate: new Date(fs.statSync(cacheFile).mtime), + instance: definition.deserializer ? definition.deserializer(cached) : JSON.parse(cached), + }; + } catch (e) { + log.atWarn().withCause(e).log(`Failed to load local cache for ${definition.name}`); + } + } else { + log.atWarn().log(`Local cache is not configured for ${definition.name}`); + } +} + +export const createInMemoryStore = (definition: StoreDefinition): InMemoryStore => { + let status: Status = "initializing"; + let instance: T | undefined = undefined; + let lastRefresh: Date | undefined = undefined; + let lastModified: Date | undefined = undefined; + let stopping: boolean = false; + let intervalToClear: NodeJS.Timeout | string | number | undefined = undefined; + + function scheduleStoreRefresh() { + const refresh = async () => { + try { + const newDef = await definition.refresh(lastModified); + if (newDef !== "not_modified") { + saveLocalCache(definition, newDef.store); + if (newDef.lastModified) { + lastModified = newDef.lastModified; + } + instance = newDef.store; + } + status = "ok"; + lastRefresh = new Date(); + } catch (e) { + log.atWarn().withCause(e).log(`Failed to refresh store ${definition.name}. Using an old value`); + status = "outdated"; + } + }; + if (definition.refreshIntervalMillis > 0) { + intervalToClear = setInterval(async () => { + if (stopping) { + return; + } + await refresh(); + }, definition.refreshIntervalMillis); + } else { + (function loop() { + if (stopping) { + return; + } + setTimeout(async () => { + await refresh(); + loop(); + }, 1); + })(); + } + } + + let instancePromise = new Promise((resolve, reject) => { + definition + .refresh() + .then(res => { + if (res === "not_modified") { + throw new Error("Not modified. (must never happen on first load)"); + } + const store = res.store; + lastModified = res.lastModified; + instance = store; + saveLocalCache(definition, store); + status = "ok"; + lastRefresh = new Date(); + log.atInfo().log(`Initial version of store ${definition.name} has been loaded`); + scheduleStoreRefresh(); + + resolve(store); + }) + .catch(e => { + log + .atWarn() + .withCause(e) + .log(`Failed to initialize store ${definition.name}. Attempting to load from local cache`); + const cachedInstance = loadFromCache(definition); + if (!cachedInstance) { + status = "failed"; + reject( + new Error( + `Failed to initialize store ${definition.name}. Initial load failed with ${getErrorMessage( + e + )} and no local cache found` + ) + ); + } else { + log + .atWarn() + .log( + `Initial store update failed, see errors above. Serving local cache of store ${ + definition.name + } updated at ${cachedInstance.updateDate.toISOString()}` + ); + instance = cachedInstance.instance; + lastRefresh = cachedInstance.updateDate; + status = "outdated"; + scheduleStoreRefresh(); + resolve(cachedInstance.instance); + } + }); + }); + return { + get() { + if (instance) { + return Promise.resolve(instance); + } + return instancePromise; + }, + getCurrent() { + return instance; + }, + status: () => status, + lastRefresh: () => lastRefresh, + lastModified: () => lastModified, + stop: () => { + log.atInfo().log(`Stopping store ${definition.name}`); + stopping = true; + if (intervalToClear) { + clearInterval(intervalToClear); + } + status = "stopped"; + }, + }; +}; diff --git a/libs/core-functions/src/meta.ts b/libs/core-functions/src/meta.ts index 63ec2a751..77ea2b9de 100644 --- a/libs/core-functions/src/meta.ts +++ b/libs/core-functions/src/meta.ts @@ -1,5 +1,34 @@ import { z } from "zod"; +const eventsParamDescription = ` +List of events to send, delimited by comma. Following page, screen, or any arbitrary event (name of track event). +Special values: empty string - send only track events, * - send all events useful if you want to filter events with Functions +`; +export const FacebookConversionApiCredentials = z.object({ + pixelId: z.string().describe("Facebook Pixel ID"), + accessToken: z.string().describe("Facebook Access Token"), + actionSource: z + .enum(["email", "website", "app", "phone_call", "chat", "physical_store", "system_generated", "other"]) + .default("website") + .describe("Action Source"), + events: z.string().optional().default("").describe(eventsParamDescription), + phoneFieldName: z + .string() + .optional() + .default("") + .describe( + "Name of the field in the event user traits that contains the phone number. Expected format could be E.164 or international format. If empty, phone number hash will not be sent." + ), +}); + +export const FacebookConversionApiCredentialsUi = { + accessToken: { + password: true, + }, +}; + +export type FacebookConversionApiCredentials = z.infer; + export const WebhookDestinationConfig = z.object({ url: z.string().url().describe("Webhook URL"), method: z @@ -7,6 +36,17 @@ export const WebhookDestinationConfig = z.object({ .default("POST") .describe("HTTP method. Can be GET, POST, PUT, DELETE"), headers: z.array(z.string()).optional().describe("List of headers in format key: value"), + customPayload: z + .boolean() + .optional() + .default(false) + .describe("Enable custom payload. If disabled, the event payload will be sent as is."), + payload: z + .string() + .optional() + .describe( + "Payload Template::Template for the webhook payload. The following macros are supported:

  • {{ EVENT }} - event json object for stream mode or batches with size=1
  • {{ EVENTS }} - for batch mode - json array of events
  • {{ EVENTS_COUNT }} - count of events in batch
  • {{ NAME }} - event name
  • {{ env.VAR_NAME }} - value of VAR_NAME environment variable
" + ), }); export type WebhookDestinationConfig = z.infer; @@ -14,7 +54,31 @@ export type WebhookDestinationConfig = z.infer; const MixpanelServiceAccountDocumentation = 'See how to create service account'; +export const IntercomDestinationCredentials = z.object({ + accessToken: z + .string() + .describe( + "Intercom Access Token. You should first create an app in Intercom Developer Hub, and then generate an access token in the app settings. See a detailed guide" + ), + updateLastSeenOnEveryEvent: z + .boolean() + .optional() + .describe( + "By default, the last seen property will be updated only on .identify() calls. If enabled, the property will be updated on every event. However, enabling this option may lead to higher API usage." + ), +}); + +export type IntercomDestinationCredentials = z.infer; + export const MixpanelCredentials = z.object({ + dataResidency: z.enum(["US", "EU"]).optional().default("US"), + simplifiedIdMerge: z + .boolean() + .optional() + .default(false) + .describe( + `Simplified Identity Merge::Use Mixpanel Simplified Identity Merge feature.
Enable this option if your Mixpanel project has the corresponding feature enabled.
Using this feature is highly recommended to achieve better quality Identity Merge` + ), projectId: z .string() .describe( @@ -26,6 +90,11 @@ export const MixpanelCredentials = z.object({ //apiSecret: z.string(), serviceAccountUserName: z.string().describe(MixpanelServiceAccountDocumentation), serviceAccountPassword: z.string().describe(MixpanelServiceAccountDocumentation), + sendPageEvents: z + .boolean() + .optional() + .default(true) + .describe("If enabled, all page view events will be sent to Mixpanel."), sendIdentifyEvents: z .boolean() .optional() @@ -40,6 +109,7 @@ export const MixpanelCredentials = z.object({ .describe( "Mixpanel Group Analytics allows behavioral data analysis at a customized group level. Group Analytics is available as an add-on package to customers on Growth and Enterprise plans." ), + filterBotTraffic: z.boolean().optional().default(true).describe("Don't send traffic from known bots to Mixpanel"), groupKey: z .string() .optional() @@ -56,7 +126,7 @@ export const MixpanelCredentials = z.object({ export type MixpanelCredentials = z.infer; export const MixpanelCredentialsUi: Partial< - Record + Record > = { serviceAccountPassword: { password: true, @@ -77,6 +147,62 @@ export const JuneCredentials = z.object({ }); export type JuneCredentials = z.infer; +export const SalesforceCredentials = z.object({ + authorized: z.boolean().optional().default(false), + oauthIntegrationId: z.string().optional().default("jitsu-cloud-dst-salesforce"), + oauthConnectionId: z.string().optional(), + isSandbox: z + .boolean() + .optional() + .default(false) + .describe( + "Sandbox Instance::You can log in to a sandbox by switching the login form to use Custom Domain, and providing the login URL of your sandbox, e.g.: MyDomainName--SandboxName.sandbox.my.salesforce.com

Alternatively, you can use the general login form by appending the sandbox name to your Salesforce username. For example, if the production username is user@example.com and the sandbox is named sndbx, the sandbox username would be: user@example.com.sndbx

If you are already authenticated, please Re-Sign In with your sandbox username." + ), +}); +export type SalesforceCredentials = z.infer; + +export const BrazeCredentials = z.object({ + apiKey: z.string().describe(`API Key::Created under Developer Console in the Braze Dashboard.`), + endpoint: z + .enum([ + "US-01 : dashboard-01.braze.com", + "US-02 : dashboard-02.braze.com", + "US-03 : dashboard-03.braze.com", + "US-04 : dashboard-04.braze.com", + "US-05 : dashboard-05.braze.com", + "US-06 : dashboard-06.braze.com", + "US-07 : dashboard-07.braze.com", + "US-08 : dashboard-08.braze.com", + "US-09 : dashboard-09.braze.com", + "EU-01 : dashboard-01.braze.eu", + "EU-02 : dashboard-02.braze.eu", + ]) + .optional() + .default("US-01 : dashboard-01.braze.com") + .describe( + "Your Braze REST endpoint. More details" + ), + appId: z + .string() + .optional() + .describe( + "App ID::The app identifier used to reference specific Apps in requests made to the Braze API. Created under Developer Console in the Braze Dashboard." + ), + useJitsuAnonymousIdAlias: z + .boolean() + .optional() + .default(false) + .describe( + "Use Anonymous Id Alias::Use Jitsu anonymousId as an alias for identified and anonymous profiles. Enables support for anonymous (alias-only) profiles." + ), + sendPageEvents: z + .boolean() + .optional() + .default(false) + .describe("Send page and screen events as Braze Custom Events"), +}); +export type BrazeCredentials = z.infer; + export const SegmentCredentials = z.object({ apiBase: z.string().default("https://api.segment.io/v1").describe("API Base::Segment API Base"), writeKey: z @@ -93,7 +219,7 @@ export const PosthogDestinationConfig = z.object({ key: z .string() .describe( - "API Key::Posthog API key. How to obtain" + "Project API Key::Posthog Project API Key. Can be found in Project Settings" ), host: z.string().optional().default(POSTHOG_DEFAULT_HOST).describe("Posthog host"), enableGroupAnalytics: z @@ -110,16 +236,68 @@ export const PosthogDestinationConfig = z.object({ .describe( "Group type is the abstract type of whatever our group represents (e.g. company, team, chat, post, etc.). Groups vs. group types." ), + sendAnonymousEvents: z + .boolean() + .optional() + .default(false) + .describe("Send events from anonymous users. If disabled, only events from identified users will be tracked."), enableAnonymousUserProfiles: z .boolean() .optional() .default(false) - .describe("If enabled, anonymous users will be tracked in Posthog"), - // sendIdentifyEvents: z.boolean().optional().default(false), + .describe( + "Enable Anonymous Person Profiles::Create Person profiles for anonymous users. If disabled, only identified users will have profiles." + ), }); +export const PosthogDestinationConfigUi: Partial< + Record +> = { + sendAnonymousEvents: { + // assumes value of this freshly added property from the value of the `enableAnonymousUserProfiles` property + correction: obj => + typeof obj?.sendAnonymousEvents === "undefined" ? obj?.enableAnonymousUserProfiles : obj?.sendAnonymousEvents, + }, + enableAnonymousUserProfiles: { + hidden: obj => + typeof obj?.sendAnonymousEvents === "undefined" ? !obj.enableAnonymousUserProfiles : !obj.sendAnonymousEvents, + }, +}; + export type PosthogDestinationConfig = z.infer; +export const AmplitudeDestinationConfig = z.object({ + key: z.string().describe("Project API Key::Amplitude Project API Key."), + enableGroupAnalytics: z + .boolean() + .optional() + .default(false) + .describe( + "Build an analysis around aggregated units of measure like accounts, charts, or order IDs. Requires The Amplitude Accounts add-on. Learn more." + ), + groupType: z + .string() + .optional() + .default("company") + .describe( + "Group type is the abstract type of whatever our group represents (e.g. accounts, charts, or order IDs)." + ), + enableAnonymousUserProfiles: z + .boolean() + .optional() + .default(false) + .describe("If enabled, anonymous users will be tracked in Amplitude"), + dataResidency: z.enum(["US", "EU"]).optional().default("US"), + sessionWindow: z.number().optional().default(30).describe("Session window in minutes"), + minIdLength: z + .number() + .optional() + .default(5) + .describe("Minimum ID Length::Overrides the Amplitude's default minimum ID length of 5 characters."), +}); + +export type AmplitudeDestinationConfig = z.infer; + export const MongodbDestinationConfig = z.object({ url: z.string().optional(), protocol: z @@ -143,7 +321,12 @@ export const MongodbDestinationConfig = z.object({ export const MongodbDestinationConfigUi: Partial< Record< keyof MongodbDestinationConfig, - { documentation?: string; editor?: string; hidden?: boolean; password?: boolean } + { + documentation?: string; + editor?: string; + hidden?: boolean; + password?: boolean; + } > > = { hosts: { @@ -169,9 +352,39 @@ export const Ga4Credentials = z.object({ measurementId: z .string() .describe( - "The measurement ID associated with a stream. Found in the Google Analytics UI under:
" + - "Admin > Data Streams > choose your stream > Measurement ID" + "The measurement ID associated with a stream.
For Web: found in the Google Analytics UI under: " + + "Admin > Data Streams > choose your stream > Measurement ID
For Apps: the Firebase App ID, found in the Firebase console under: Project Settings > General > Your Apps > App ID" ), - //validationMode: z.boolean().default(false).optional(), + url: z + .string() + .url() + .describe( + "Measurement Protocol URL.
Default: https://www.google-analytics.com/mp/collect
Default debug url: https://www.google-analytics.com/debug/mp/collect" + ) + .default("https://www.google-analytics.com/mp/collect"), + events: z.string().optional().default("").describe(eventsParamDescription), }); export type Ga4Credentials = z.infer; + +export const HubspotCredentials = z.object({ + accessToken: z + .string() + .describe( + [ + "To obtain an access secret, go to Settings » Account Setup » Private Apps, create a new private app copy the Access token. See detailed guide.", + "Please make sure to grant an application all read and write permissions under CRM section", + ].join("\n") + ), + sendPageViewEvents: z + .boolean() + .optional() + .describe("When enabled, Jitsu will send page view events to hubspot (only events with a known email)"), + autoCreateCustomProperties: z + .boolean() + .optional() + .describe( + "When enabled, Jitsu will automatically create HubSpot custom properties for Contacts and Companies to capture every new trait. Otherwise, only known properties are sent." + ), +}); + +export type HubspotCredentials = z.infer; diff --git a/libs/core-functions/tsconfig.json b/libs/core-functions/tsconfig.json index 87a377bb5..fbf2dc67c 100644 --- a/libs/core-functions/tsconfig.json +++ b/libs/core-functions/tsconfig.json @@ -4,8 +4,13 @@ "outDir": "./dist", "declaration": true, "esModuleInterop": true, + "moduleResolution": "node", + "target": "ESNext", "module": "commonjs", - "lib": ["ES2019", "dom"], + "lib": [ + "ESNext", + "DOM" + ], "noEmit": true }, "exclude": [ diff --git a/libs/functions/__tests__/classic-mapping.test.ts b/libs/functions/__tests__/classic-mapping.test.ts new file mode 100644 index 000000000..24b830764 --- /dev/null +++ b/libs/functions/__tests__/classic-mapping.test.ts @@ -0,0 +1,315 @@ +import { AnalyticsServerEvent } from "@jitsu/protocols/analytics"; +import type { Event as JitsuLegacyEvent } from "@jitsu/sdk-js"; +import { FullContext, UserAgent } from "@jitsu/protocols/functions"; +import { fromJitsuClassic, removeUndefined, TableNameParameter, toJitsuClassic, toSnakeCase } from "../src"; +import { classicEvents } from "./data/classic-events"; + +const identify: AnalyticsServerEvent = { + writeKey: "writeKey", + messageId: "a6c09b16-c2bc-4193-990f-5e2b694ae610", + anonymousId: "6638caf0-d2c2-4bc0-aecf-8b290b559a37", + context: { + ip: "141.136.89.181", + campaign: { + medium: "medium", + name: "campaign", + source: "source", + }, + library: { + name: "jitsu-js", + version: "1.0.0", + }, + locale: "en-US", + page: { + host: "localhost:3088", + path: "/basic.html", + referrer: "https://referrer.com", + referring_domain: "referrer.com", + search: "?utm_source=source&utm_medium=medium&utm_campaign=campaign", + title: "Tracking page", + url: "https://localhost:3088/basic.html?utm_source=source&utm_medium=medium&utm_campaign=campaign", + }, + screen: { + density: 1, + height: 720, + innerHeight: 720, + innerWidth: 1280, + width: 1280, + }, + userAgent: + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/107.0.5304.18 Safari/537.36", + }, + sentAt: "2022-11-14T08:56:34.387Z", + receivedAt: "2022-11-14T08:56:40.000Z", + timestamp: "2022-11-14T08:56:34.387Z", + traits: { + caseName: "basic-identify", + CaseLastName: "Doe", + User_Name: "jj", + email: "john.doe2@gmail.com", + }, + type: "identify", + userId: "userId2", +}; + +const legacyIdentifyExpectedS3: Omit & { local_tz_offset?: number } = { + _timestamp: "2022-11-14T08:56:40.000Z", + anon_ip: "141.136.89.0", + doc_path: "/basic.html", + doc_search: "?utm_source=source&utm_medium=medium&utm_campaign=campaign", + source_ip: "141.136.89.181", + api_key: "writeKey", + doc_host: "localhost:3088", + eventn_ctx_event_id: "a6c09b16-c2bc-4193-990f-5e2b694ae610", + event_type: "identify", + page_title: "Tracking page", + referer: "https://referrer.com", + src: "jitsu", + url: "https://localhost:3088/basic.html?utm_source=source&utm_medium=medium&utm_campaign=campaign", + user: { + anonymous_id: "6638caf0-d2c2-4bc0-aecf-8b290b559a37", + id: "userId2", + email: "john.doe2@gmail.com", + caseName: "basic-identify", + CaseLastName: "Doe", + User_Name: "jj", + }, + user_agent: + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/107.0.5304.18 Safari/537.36", + user_language: "en-US", + utc_time: "2022-11-14T08:56:34.387Z", + utm: { + medium: "medium", + name: "campaign", + source: "source", + }, + screen_resolution: "1280x720", + vp_size: "1280x720", +}; + +const legacyIdentifyExpectedWarehouse: Omit & { local_tz_offset?: number } = { + _timestamp: "2022-11-14T08:56:40.000Z", + anon_ip: "141.136.89.0", + doc_path: "/basic.html", + doc_search: "?utm_source=source&utm_medium=medium&utm_campaign=campaign", + source_ip: "141.136.89.181", + api_key: "writeKey", + doc_host: "localhost:3088", + eventn_ctx_event_id: "a6c09b16-c2bc-4193-990f-5e2b694ae610", + event_type: "identify", + page_title: "Tracking page", + referer: "https://referrer.com", + src: "jitsu", + url: "https://localhost:3088/basic.html?utm_source=source&utm_medium=medium&utm_campaign=campaign", + user: { + anonymous_id: "6638caf0-d2c2-4bc0-aecf-8b290b559a37", + id: "userId2", + email: "john.doe2@gmail.com", + casename: "basic-identify", + caselastname: "Doe", + user_name: "jj", + }, + user_agent: + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/107.0.5304.18 Safari/537.36", + user_language: "en-US", + utc_time: "2022-11-14T08:56:34.387Z", + utm: { + medium: "medium", + name: "campaign", + source: "source", + }, + screen_resolution: "1280x720", + vp_size: "1280x720", +}; + +const page: AnalyticsServerEvent = { + messageId: "d0c6abf6-97f7-487a-a197-8f236c728fa8", + anonymousId: "6638caf0-d2c2-4bc0-aecf-8b290b559a37", + context: { + campaign: { + medium: "medium", + name: "campaign", + source: "source", + }, + library: { + name: "jitsu-js", + version: "1.0.0", + }, + locale: "en-US", + page: { + host: "localhost:3088", + path: "/basic.html", + referrer: "https://referrer.com", + referring_domain: "", + search: "?utm_source=source&utm_medium=medium&utm_campaign=campaign", + title: "Tracking page", + url: "https://localhost:3088/basic.html?utm_source=source&utm_medium=medium&utm_campaign=campaign", + }, + screen: { + density: 1, + height: 720, + innerHeight: 720, + innerWidth: 1280, + width: 1280, + }, + traits: { + caseName: "identify-without-user-id", + CaseLastName: "Doe", + User_Name: "jj", + email: "john.doe3@gmail.com", + }, + userAgent: + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/107.0.5304.18 Safari/537.36", + }, + properties: { + caseName: "page-with-name", + nestedProp: { + "my-Prop1": "myValue", + myProp2: "myValue", + }, + hash: "", + height: 720, + name: "test-page", + path: "/basic.html", + search: "?utm_source=source&utm_medium=medium&utm_campaign=campaign", + title: "Tracking page", + url: "https://localhost:3088/basic.html?utm_source=source&utm_medium=medium&utm_campaign=campaign", + width: 1280, + }, + receivedAt: "2022-11-14T08:56:40.000Z", + sentAt: "2022-11-14T08:56:34.395Z", + timestamp: "2022-11-14T08:56:34.395Z", + type: "page", + userId: "userId2", +}; + +const legacyPageExpectedS3 = { + _timestamp: "2022-11-14T08:56:40.000Z", + api_key: "", + doc_host: "localhost:3088", + doc_path: "/basic.html", + doc_search: "?utm_source=source&utm_medium=medium&utm_campaign=campaign", + eventn_ctx_event_id: "d0c6abf6-97f7-487a-a197-8f236c728fa8", + event_type: "page", + page_title: "Tracking page", + referer: "https://referrer.com", + src: "jitsu", + url: "https://localhost:3088/basic.html?utm_source=source&utm_medium=medium&utm_campaign=campaign", + user: { + anonymous_id: "6638caf0-d2c2-4bc0-aecf-8b290b559a37", + id: "userId2", + email: "john.doe3@gmail.com", + caseName: "identify-without-user-id", + CaseLastName: "Doe", + User_Name: "jj", + }, + user_agent: + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/107.0.5304.18 Safari/537.36", + user_language: "en-US", + utc_time: "2022-11-14T08:56:34.395Z", + utm: { + medium: "medium", + name: "campaign", + source: "source", + }, + caseName: "page-with-name", + nestedProp: { + "my-Prop1": "myValue", + myProp2: "myValue", + }, + hash: "", + name: "test-page", + screen_resolution: "1280x720", + vp_size: "1280x720", +}; + +const legacyPageExpectedWarehouse = { + _timestamp: "2022-11-14T08:56:40.000Z", + api_key: "", + doc_host: "localhost:3088", + doc_path: "/basic.html", + doc_search: "?utm_source=source&utm_medium=medium&utm_campaign=campaign", + eventn_ctx_event_id: "d0c6abf6-97f7-487a-a197-8f236c728fa8", + event_type: "page", + page_title: "Tracking page", + referer: "https://referrer.com", + src: "jitsu", + url: "https://localhost:3088/basic.html?utm_source=source&utm_medium=medium&utm_campaign=campaign", + user: { + anonymous_id: "6638caf0-d2c2-4bc0-aecf-8b290b559a37", + id: "userId2", + email: "john.doe3@gmail.com", + casename: "identify-without-user-id", + caselastname: "Doe", + user_name: "jj", + }, + user_agent: + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/107.0.5304.18 Safari/537.36", + user_language: "en-US", + utc_time: "2022-11-14T08:56:34.395Z", + utm: { + medium: "medium", + name: "campaign", + source: "source", + }, + casename: "page-with-name", + nestedprop: { + my_prop1: "myValue", + myprop2: "myValue", + }, + hash: "", + name: "test-page", + screen_resolution: "1280x720", + vp_size: "1280x720", +}; + +test("legacy event s3", () => { + const identifyLegacyResult = toJitsuClassic(identify, { + props: { keepOriginalNames: true }, + destination: { type: "s3" }, + } as unknown as FullContext); + const pageLegacyResult = toJitsuClassic(page, { + props: { keepOriginalNames: true }, + destination: { type: "s3" }, + } as unknown as FullContext); + console.log(JSON.stringify(identifyLegacyResult, null, 2)); + expect(identifyLegacyResult).toStrictEqual(legacyIdentifyExpectedS3); + + console.log(JSON.stringify(pageLegacyResult, null, 2)); + expect(pageLegacyResult).toStrictEqual(legacyPageExpectedS3); +}); + +test("legacy event warehouse", () => { + const identifyLegacyResult = toJitsuClassic(identify, { + props: { keepOriginalNames: true }, + destination: { type: "postgres" }, + } as unknown as FullContext); + const pageLegacyResult = toJitsuClassic(page, { + props: { keepOriginalNames: true }, + destination: { type: "postgres" }, + } as unknown as FullContext); + console.log(JSON.stringify(identifyLegacyResult, null, 2)); + expect(identifyLegacyResult).toStrictEqual(legacyIdentifyExpectedWarehouse); + + console.log(JSON.stringify(pageLegacyResult, null, 2)); + expect(pageLegacyResult).toStrictEqual(legacyPageExpectedWarehouse); +}); + +test("classic events mapping", () => { + //load events from json + for (const event of classicEvents) { + if (Object.keys(event.click_id || {}).length === 0) { + delete event.click_id; + } + if (Object.keys(event.ids || {}).length === 0) { + delete event.ids; + } + const restored = fromJitsuClassic(event); + const mapped = toJitsuClassic( + restored as AnalyticsServerEvent, + { props: { keepOriginalNames: true }, destination: { type: "s3" } } as unknown as FullContext + ); + delete mapped.anon_ip; + expect(mapped).toStrictEqual(event); + } +}); diff --git a/libs/functions/__tests__/data/classic-events.ts b/libs/functions/__tests__/data/classic-events.ts new file mode 100644 index 000000000..3d27cc31c --- /dev/null +++ b/libs/functions/__tests__/data/classic-events.ts @@ -0,0 +1,168 @@ +export const classicEvents = [ + { + _timestamp: "2024-12-25T14:23:48.606561Z", + api_key: "js.123.123", + click_id: {}, + doc_encoding: "UTF-8", + doc_host: "events.example.com", + doc_path: "/a-v/123", + doc_search: "", + event_type: "user_identify", + eventn_ctx_event_id: "1d199165-fdf3-4051-82fc-08a8c5502f33", + ids: { + ga: "GA1.1.123.123", + }, + local_tz_offset: 420, + page_title: "aa.aa.24", + referer: "", + screen_resolution: "1680x1050", + source_ip: "127.0.13.36", + src: "jitsu", + url: "https://events.example.com/a-v/123", + user: { + anonymous_id: "5adv25gea6", + email: "user@example.com", + hashed_anonymous_id: "41217f4f06dba2b145dbd7d0879a871e", + name: "John Doe", + uid: "123", + }, + user_agent: + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36", + user_language: "en-US", + utc_time: "2024-12-25T14:23:46.483000Z", + utm: {}, + vp_size: "1384x775", + }, + { + _timestamp: "2024-12-25T14:31:48.875505Z", + api_key: "s2s.123.123", + common_properties: { + c_version: "1.1.11", + client: { + creation_timestamp: "2024-12-25T14:31:48.794613+00:00", + id: "123", + }, + in_v: false, + a_version: null, + }, + event_type: "serve_init", + eventn_ctx_event_id: "99406a96-3bc3-4939-a3cc-6f5eeb458078", + session_id: "abc", + source_ip: "127.0.41.132", + src: "api", + }, + { + _timestamp: "2024-12-25T14:26:22.484904Z", + api_key: "js.123.123", + chromeVersion: "131", + click_id: {}, + companyId: 123, + email: "john.doe@example.com", + event_type: "workflow-complete", + eventn_ctx_event_id: "62615da7-a605-420f-9b73-d88ca488fa6d", + ids: {}, + info: { + billing: { + nodeTypeCounts: { + "click-element": 13, + }, + ticketDurationsMs: [], + }, + callCount: 1, + metrics: { + actual: {}, + }, + results: [ + { + callCount: 1, + }, + ], + status: "complete", + }, + jobId: "f3b97b73-15e5-4d5b-af73-43bbbf237b24", + local_tz_offset: 0, + manifestVersion: "1.0.0", + runtimeEnvironment: "production", + source_ip: "127.0.32.11", + src: "jitsu", + teamId: 1, + timestamp: 1735136772062, + type: "workflow-complete", + user: { + anonymous_id: "", + hashed_anonymous_id: "bef91c4d78dbf5b10b791d1f483faa9a", + }, + user_agent: + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36", + utc_time: "2024-12-25T14:26:12.097000Z", + utm: {}, + }, + { + _timestamp: "2024-12-25T00:26:56.305721Z", + api_key: "js.123.123", + click_id: { + gclid: "123", + dclid: "1233", + }, + doc_encoding: "UTF-8", + doc_host: "app.example.com", + doc_path: "/login", + doc_search: "?next=%2F", + event_type: "pageview", + eventn_ctx_event_id: "15fea8d7-14af-4488-92a9-0ae52eba5499", + ids: {}, + local_tz_offset: 300, + page_title: "Login - Litnerd", + referer: "", + screen_resolution: "1920x1080", + source_ip: "127.0.56.1", + src: "jitsu", + url: "https://app.example.com/login?next=%2F", + user: { + anonymous_id: "123", + hashed_anonymous_id: "49a8381a0fe28827b68a48416dd69a23", + id: "student:123", + name: "JOHN DOE", + role: "student", + school_id: 123, + }, + user_agent: + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36", + user_language: "en-US", + utc_time: "2024-12-25T00:26:56.481000Z", + utm: {}, + vp_size: "1920x911", + }, + { + _timestamp: "2024-12-25T15:33:08.369746Z", + api_key: "s2s.123.123", + click_id: { + gclid: "123", + dclid: "1233", + }, + doc_host: "localhost:3000", + doc_path: "/auth/login", + doc_search: "", + event_type: "page_view", + eventn_ctx_event_id: "98373e80-6228-4163-957e-90703182e9de", + ids: { + ga: "GA1.1.123.123", + }, + isAuthorized: false, + nextPrefetch: false, + onVercel: false, + page_title: "", + page_url: "http://localhost:3000/auth/login", + referer: "", + source_ip: "", + src: "api", + url: "http://localhost:3000/auth/login", + user: { + anonymous_id: "123", + hashed_anonymous_id: "ee3cd46ca7a46adb9fa1c9599e64249f", + }, + user_agent: "Wget/1.21.3", + utc_time: "2024-12-25T15:33:07.992Z", + utm: {}, + }, +]; diff --git a/libs/functions/jest.config.js b/libs/functions/jest.config.js new file mode 100644 index 000000000..fd6a1a448 --- /dev/null +++ b/libs/functions/jest.config.js @@ -0,0 +1,8 @@ +/** @type {import("ts-jest").JestConfigWithTsJest} */ +module.exports = { + preset: "ts-jest", + testMatch: ["**/__tests__/**/*.test.ts"], + testEnvironment: "node", + runner: "jest-runner", + "setupFiles": ["./jest.setup.js"] +}; diff --git a/libs/functions/jest.setup.js b/libs/functions/jest.setup.js new file mode 100644 index 000000000..847bac649 --- /dev/null +++ b/libs/functions/jest.setup.js @@ -0,0 +1,8 @@ + +global.console = { + log: message => process.stdout.write(message + '\n'), + error: console.error, + warn: console.warn, + info: console.info, + debug: console.debug, +}; diff --git a/libs/functions/package.json b/libs/functions/package.json new file mode 100644 index 000000000..344e856aa --- /dev/null +++ b/libs/functions/package.json @@ -0,0 +1,32 @@ +{ + "name": "@jitsu/functions-lib", + "version": "0.0.0", + "main": "dist/index.cjs.js", + "module": "dist/index.es.js", + "types": "dist/index.d.ts", + "description": "", + "author": "Jitsu Dev Team ", + "publishConfig": { + "access": "public" + }, + "license": "MIT", + "private": false, + "scripts": { + "compile": "tsc -p .", + "build": "pnpm compile && rollup -c", + "test": "tsc -p . && jest --verbose" + }, + "devDependencies": { + "@jitsu/protocols": "workspace:*", + "rollup": "^4.24.3", + "@rollup/plugin-typescript": "^11.1.5", + "@rollup/plugin-node-resolve": "^16.0.0", + "@rollup/plugin-commonjs": "^28.0.2", + "@types/jest": "^29.1.1", + "@types/node": "^18.15.3", + "jest": "^29.1.2", + "ts-jest": "29.0.5", + "tslib": "^2.6.3", + "@jitsu/sdk-js": "^3.1.5" + } +} diff --git a/libs/functions/rollup.config.js b/libs/functions/rollup.config.js new file mode 100644 index 000000000..ae5eed5ed --- /dev/null +++ b/libs/functions/rollup.config.js @@ -0,0 +1,14 @@ +const typescript = require("@rollup/plugin-typescript"); +const resolve = require("@rollup/plugin-node-resolve"); +const commonjs = require("@rollup/plugin-commonjs"); + +module.exports = [ + { + plugins: [typescript(), resolve({ preferBuiltins: false }), commonjs()], + input: ["./src/index.ts"], + output: [ + { file: "dist/index.es.js", format: "es" }, + { file: "dist/index.cjs.js", format: "cjs" }, + ], + }, +]; diff --git a/libs/functions/src/index.ts b/libs/functions/src/index.ts new file mode 100644 index 000000000..a41f06d46 --- /dev/null +++ b/libs/functions/src/index.ts @@ -0,0 +1,3 @@ +export * from "./lib/functions"; +export * from "./lib/objects"; +export * from "./lib/strings"; diff --git a/libs/functions/src/lib/functions.ts b/libs/functions/src/lib/functions.ts new file mode 100644 index 000000000..1e33649ab --- /dev/null +++ b/libs/functions/src/lib/functions.ts @@ -0,0 +1,341 @@ +import { AnyEvent, FullContext, UserAgent } from "@jitsu/protocols/functions"; +import { AnalyticsServerEvent } from "@jitsu/protocols/analytics"; +import { removeUndefined, transferAsSnakeCase, transfer, transferAsClassic } from "./objects"; + +export const TableNameParameter = "JITSU_TABLE_NAME"; + +export const DropRetryErrorName = "Drop & RetryError"; +export const RetryErrorName = "RetryError"; + +export class RetryError extends Error { + status: number; + response: string; + message: string; + constructor(message?: any, options?: { drop: boolean }) { + if (typeof message === "object") { + super(message.message); + this.message = message.message; + this.status = message.status; + this.response = message.response; + } else { + super(message); + this.message = message; + } + this.name = options?.drop ? `${DropRetryErrorName}` : `${RetryErrorName}`; + } + toJSON() { + return { + name: this.name, + message: this.message, + status: this.status, + response: this.response, + }; + } +} + +export class HTTPError extends Error { + status: number; + response: string; + message: string; + constructor(message: string, status: number, response: string) { + super(message); + this.message = message; + this.name = "HTTPError"; + this.status = status; + this.response = response.length > 1000 ? response.slice(0, 1000) + "..." : response; + } + + toJSON() { + return { + name: this.name, + message: this.message, + status: this.status, + response: this.response, + }; + } +} + +function anonymizeIp(ip: string | undefined) { + if (!ip) { + return; + } + const parts = ip.split("."); + if (parts.length === 4) { + return `${parts[0]}.${parts[1]}.${parts[2]}.0`; + } +} + +export function toJitsuClassic(event: AnalyticsServerEvent, ctx: FullContext): AnyEvent { + const keepOriginalNames = !!ctx.props.keepOriginalNames; + const fileStorage = ctx.destination.type === "s3" || ctx.destination.type === "gcs"; + let transferFunc = transferAsSnakeCase; + if (keepOriginalNames) { + if (fileStorage) { + transferFunc = transfer; + } else { + transferFunc = transferAsClassic; + } + } + let url: URL | undefined = undefined; + const analyticsContext = event.context || {}; + const urlStr = analyticsContext.page?.url || event.properties?.url; + try { + if (urlStr) { + url = new URL(urlStr as string); + } + } catch (e) {} + const click_id = {}; + transferFunc(click_id, analyticsContext.clientIds, ["ga4", "fbp", "fbc"]); + let ids: any = {}; + if (Object.keys(analyticsContext.clientIds || {}).length > 0) { + ids = removeUndefined({ + ga: analyticsContext.clientIds.ga4?.clientId, + fbp: analyticsContext.clientIds.fbp, + fbc: analyticsContext.clientIds.fbc, + }); + } + const geo = analyticsContext.geo || {}; + const ua: UserAgent = ctx?.ua || ({} as UserAgent); + const user = removeUndefined({ + id: event.userId, + anonymous_id: event.anonymousId, + email: (analyticsContext.traits?.email || event.traits?.email || undefined) as string | undefined, + name: (analyticsContext.traits?.name || event.traits?.name || undefined) as string | undefined, + }); + transferFunc(user, analyticsContext.traits, ["email", "name"]); + transferFunc(user, event.traits, ["email", "name"]); + const classic = { + [TableNameParameter]: event[TableNameParameter], + anon_ip: analyticsContext.ip ? anonymizeIp(analyticsContext.ip) : undefined, + api_key: event.writeKey || "", + click_id: Object.keys(click_id).length > 0 ? click_id : undefined, + doc_encoding: analyticsContext.page?.encoding ?? event.properties?.encoding, + doc_host: (analyticsContext.page?.host ?? event.properties?.host) || url?.host, + doc_path: (analyticsContext.page?.path ?? event.properties?.path) || url?.pathname, + doc_search: (analyticsContext.page?.search ?? event.properties?.search) || url?.search, + eventn_ctx_event_id: event.messageId, + event_type: event.event || event.type, + local_tz_offset: analyticsContext.page?.timezoneOffset ?? event.properties?.timezoneOffset, + page_title: analyticsContext.page?.title, + referer: analyticsContext.page?.referrer, + screen_resolution: + Object.keys(analyticsContext.screen || {}).length > 0 + ? Math.max(analyticsContext.screen.width || 0) + "x" + Math.max(analyticsContext.screen.height || 0) + : undefined, + source_ip: analyticsContext.ip, + src: event.properties?.src || "jitsu", + url: urlStr as string, + user: Object.keys(user).length > 0 ? user : undefined, + location: + Object.keys(geo).length > 0 + ? { + city: geo.city?.name, + continent: geo.continent?.name, + country: geo.country?.code, + country_name: geo.country?.name, + latitude: geo.location?.latitude, + longitude: geo.location?.longitude, + region: geo.region?.code, + zip: geo.postalCode?.code, + timezone: geo.location?.timezone, + autonomous_system_number: geo.provider?.as?.num, + autonomous_system_organization: geo.provider?.as?.name, + isp: geo.provider?.isp, + domain: geo.provider?.domain, + } + : undefined, + ids: Object.keys(ids).length > 0 ? ids : undefined, + parsed_ua: + event.parsed_ua || + (Object.keys(ua).length > 0 + ? { + os_family: ua.os?.name, + os_version: ua.os?.version, + ua_family: ua.browser?.name, + ua_version: ua.browser?.version, + device_brand: ua.device?.vendor, + device_type: ua.device?.type, + device_model: ua.device?.model, + bot: ua.bot, + } + : undefined), + user_agent: analyticsContext.userAgent, + user_language: analyticsContext.locale, + utc_time: event.timestamp, + _timestamp: event.receivedAt, + utm: analyticsContext.campaign, + vp_size: + Object.keys(analyticsContext.screen || {}).length > 0 + ? Math.max(analyticsContext.screen.innerWidth || 0) + "x" + Math.max(analyticsContext.screen.innerHeight || 0) + : undefined, + }; + if (event.type === "track") { + transferFunc(classic, event.properties); + } else { + transferFunc(classic, event.properties, ["url", "title", "referrer", "search", "host", "path", "width", "height"]); + } + + return removeUndefined(classic); +} + +export function fromJitsuClassic(event: AnyEvent): AnyEvent { + let type = "track"; + let eventName: string | undefined = undefined; + switch ((event.event_type ?? "").toLowerCase()) { + case "pageview": + case "page_view": + case "page": + type = "page"; + eventName = event.event_type; + break; + case "identify": + type = "identify"; + break; + case "screen": + type = "screen"; + break; + case "group": + type = "group"; + break; + case "alias": + type = "alias"; + break; + default: + type = "track"; + eventName = event.event_type; + break; + } + const clientIds = + Object.keys(event.ids || event.click_id || {}).length > 0 + ? { + ga4: event.ids?.ga + ? { + clientId: event.ids.ga, + } + : undefined, + fbp: event.ids?.fbp, + fbc: event.ids?.fbc, + ...event.click_id, + } + : undefined; + const loc = event.location || {}; + const geo = + Object.keys(loc).length > 0 + ? { + city: { + name: loc.city, + }, + continent: { + code: loc.continent, + }, + country: { + code: loc.country, + name: loc.country_name, + }, + location: { + latitude: loc.latitude, + longitude: loc.longitude, + timezone: loc.timezone, + }, + region: { + code: loc.region, + }, + postalCode: { + code: loc.zip, + }, + provider: { + as: { + num: loc.autonomous_system_number, + name: loc.autonomous_system_organization, + }, + isp: loc.isp, + domain: loc.domain, + }, + } + : undefined; + const traits = {}; + transfer(traits, event.user, ["id", "anonymous_id"]); + const properties: any = {}; + transfer(properties, event, [ + TableNameParameter, + "anon_ip", + "api_key", + "click_id", + "doc_encoding", + "doc_host", + "doc_path", + "doc_search", + "eventn_ctx_event_id", + "event_type", + "local_tz_offset", + "page_title", + "referer", + "screen_resolution", + "source_ip", + "url", + "user", + "location", + "parsed_ua", + "user_agent", + "user_language", + "utc_time", + "_timestamp", + "utm", + "vp_size", + ]); + if (type === "page") { + properties.url = event.url; + properties.title = event.page_title; + properties.referrer = event.referer; + properties.search = event.doc_search; + properties.host = event.doc_host; + properties.path = event.doc_path; + properties.width = parseInt(event.vp_size?.split("x")[0]); + properties.height = parseInt(event.vp_size?.split("x")[1]); + } + const screen: any = {}; + const sr = event.screen_resolution?.split("x"); + if (sr?.length === 2) { + screen.width = parseInt(sr[0]); + screen.height = parseInt(sr[1]); + } + const vs = event.vp_size?.split("x"); + if (vs?.length === 2) { + screen.innerWidth = parseInt(vs[0]); + screen.innerHeight = parseInt(vs[1]); + } + + return removeUndefined({ + [TableNameParameter]: event[TableNameParameter], + messageId: event.eventn_ctx_event_id, + userId: event.user?.id, + anonymousId: event.user?.anonymous_id, + timestamp: event.utc_time, + receivedAt: event._timestamp, + writeKey: event.api_key, + type, + event: eventName, + context: { + ip: event.source_ip, + locale: event.user_language, + userAgent: event.user_agent, + page: { + url: event.url, + title: event.page_title, + referrer: event.referer, + search: event.doc_search, + host: event.doc_host, + path: event.doc_path, + encoding: event.doc_encoding, + timezoneOffset: event.local_tz_offset, + }, + screen: Object.keys(screen).length > 0 ? screen : undefined, + clientIds, + campaign: event.utm, + traits, + geo, + }, + properties, + traits: type === "identify" || type === "group" ? traits : undefined, + }); +} diff --git a/libs/functions/src/lib/objects.ts b/libs/functions/src/lib/objects.ts new file mode 100644 index 000000000..16c06ead4 --- /dev/null +++ b/libs/functions/src/lib/objects.ts @@ -0,0 +1,96 @@ +import { idToClassic, idToSnakeCaseFast } from "./strings"; + +export function toSnakeCase(param: any): any { + if (Array.isArray(param)) { + return param.map(toSnakeCase); + } else if (typeof param === "object" && param !== null) { + const r = {}; + for (const [key, value] of Object.entries(param)) { + r[idToSnakeCaseFast(key)] = toSnakeCase(value); + } + return r; + } else { + return param; + } +} + +export function toClassic(param: any): any { + if (Array.isArray(param)) { + return param.map(toClassic); + } else if (typeof param === "object" && param !== null) { + const r = {}; + for (const [key, value] of Object.entries(param)) { + r[idToClassic(key)] = toClassic(value); + } + return r; + } else { + return param; + } +} + +export function removeUndefined(param: any): any { + if (Array.isArray(param)) { + return param.map(removeUndefined); + } else if (typeof param === "object" && param !== null) { + for (const [key, value] of Object.entries(param)) { + switch (typeof value) { + case "undefined": + delete param[key]; + break; + case "object": + if (value !== null) { + removeUndefined(value); + } + break; + } + } + } + return param; +} + +export function transferAsSnakeCase(target: Record, source: any, omit?: string[]) { + if (typeof source !== "object") { + return; + } + for (const [k, v] of Object.entries(source)) { + if (!omit || !omit.includes(k)) { + target[idToSnakeCaseFast(k)] = toSnakeCase(v); + } + } +} + +export function transferAsClassic(target: Record, source: any, omit?: string[]) { + if (typeof source !== "object") { + return; + } + for (const [k, v] of Object.entries(source)) { + if (!omit || !omit.includes(k)) { + target[idToClassic(k)] = toClassic(v); + } + } +} + +export function transferValueAsSnakeCase(target: Record, property: string, source: any) { + if (typeof source === "undefined") { + return; + } + target[property] = toSnakeCase(source); +} + +export function transfer(target: Record, source: any, omit?: string[]) { + if (typeof source !== "object") { + return; + } + for (const [k, v] of Object.entries(source)) { + if (!omit || !omit.includes(k)) { + target[k] = v; + } + } +} + +export function transferValue(target: Record, property: string, source: any) { + if (typeof source === "undefined") { + return; + } + target[property] = source; +} diff --git a/libs/functions/src/lib/strings.ts b/libs/functions/src/lib/strings.ts new file mode 100644 index 000000000..bd41ef283 --- /dev/null +++ b/libs/functions/src/lib/strings.ts @@ -0,0 +1,60 @@ +const ACode = "A".charCodeAt(0); +const ZCode = "Z".charCodeAt(0); +const aCode = "a".charCodeAt(0); +const zCode = "z".charCodeAt(0); +const zeroCode = "0".charCodeAt(0); +const nineCode = "9".charCodeAt(0); +const spaceCode = " ".charCodeAt(0); +const underscodeCode = "_".charCodeAt(0); + +export function idToSnakeCaseFast(id: string) { + let res = ""; + let concatIndex = 0; + let i = 0; + let needUnderscore = false; + for (; i < id.length; i++) { + const c = id.charCodeAt(i); + if (c >= ACode && c <= ZCode) { + res += id.substring(concatIndex, i) + (needUnderscore ? "_" : "") + id.charAt(i).toLowerCase(); + concatIndex = i + 1; + } else if (c == spaceCode) { + res += id.substring(concatIndex, i) + "_"; + concatIndex = i + 1; + } + // needUnderscore is used in case next char is a capital latin letter + // we add underscore only between latin letters + needUnderscore = (c >= aCode && c <= zCode) || (c >= ACode && c <= ZCode); + } + if (concatIndex == 0) { + return id; + } else if (concatIndex < i) { + res += id.substring(concatIndex, i); + } + return res; +} + +export function idToClassic(id: string) { + let needConvert = false; + for (let i = 0; i < id.length; i++) { + const c = id.charCodeAt(i); + if (!((c >= aCode && c <= zCode) || (c >= zeroCode && c <= nineCode) || c == underscodeCode)) { + needConvert = true; + break; + } + } + if (!needConvert) { + return id; + } + let res = ""; + for (let i = 0; i < id.length; i++) { + const c = id.charCodeAt(i); + if (c >= ACode && c <= ZCode) { + res += id.charAt(i).toLowerCase(); + } else if ((c >= aCode && c <= zCode) || (c >= zeroCode && c <= nineCode)) { + res += id.charAt(i); + } else { + res += "_"; + } + } + return res; +} diff --git a/libs/functions/tsconfig.json b/libs/functions/tsconfig.json new file mode 100644 index 000000000..a72f93aac --- /dev/null +++ b/libs/functions/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "lib": ["dom", "esnext"], + "rootDir": "./src", + "outDir": "./dist", + "declaration": true, + "esModuleInterop": true, + "moduleResolution": "node", + "noEmit": true, + "target": "esnext", + "skipLibCheck": true + }, + "exclude": [ + "__tests__", + "node_modules", + "dist", + "test_projects", + "test", + "templates" + ] +} diff --git a/libs/jitsu-js/CHANGELOG.md b/libs/jitsu-js/CHANGELOG.md new file mode 100644 index 000000000..e5a65bd1a --- /dev/null +++ b/libs/jitsu-js/CHANGELOG.md @@ -0,0 +1,26 @@ +# v1.10.4 + +- fix(jitsu-js): support for parsing GA4 session id from new GA4 Session Cookie format. #1219 +- fix(jitsu-js): fix regression introduced by #1135. Canonical URLs once again not used for page events properties +- fix(jitsu-js): prevent changes made by functions for one sync destination to affect event in another sync destination on the same stream + +# v1.10.3 + +- fix(jitsu-js): added getConfiguration method + +# v1.10.2 + +- fix(jitsu-react): support for react 19.x and react-router-dom 7.x #1203 + +# v1.10.1 + +- fix(jitsu-js): added setContextProperty & getContextProperty methods +- fix(jitsu-js): fix for type definitions +- fix(jitsu-js): added additional file dist/jitsu-no-ext.cjs.js that doesnt include plugins executing external scripts + + + + + + + diff --git a/libs/jitsu-js/README.md b/libs/jitsu-js/README.md new file mode 100644 index 000000000..f75630eb1 --- /dev/null +++ b/libs/jitsu-js/README.md @@ -0,0 +1,9 @@ +## Install + +```bash +npm install --save @jitsu/jitsu-js +``` + +## Usage + +**Please read a documentation on [Jitsu Docs](https://docs.jitsu.com/sending-data/npm)** diff --git a/libs/jitsu-js/__tests__/node/nodejs.test.ts b/libs/jitsu-js/__tests__/node/nodejs.test.ts index fa67d7f80..2a37fbad4 100644 --- a/libs/jitsu-js/__tests__/node/nodejs.test.ts +++ b/libs/jitsu-js/__tests__/node/nodejs.test.ts @@ -1,5 +1,6 @@ import { createServer, SimpleSyrup } from "../simple-syrup"; import { AnalyticsClientEvent, AnalyticsInterface } from "@jitsu/protocols/analytics"; +import { getTopLevelDomain } from "../../src/tlds"; const jitsuAnalytics = require("../../dist/jitsu.cjs.js").jitsuAnalytics; const fetchImpl = require("node-fetch-commonjs"); @@ -10,18 +11,23 @@ describe("Test Jitsu NodeJS client", () => { let requestLog: { type: string; body: AnalyticsClientEvent }[] = []; const startServer = async () => { + requestLog = []; + let handler = (req, res) => { + res.setHeader("Content-Type", "text/javascript"); + res.send({ ok: true }); + requestLog.push({ + type: req.params.type, + body: req.body, + }); + }; server = await createServer({ port: 3088, https: false, handlers: { - "/api/s/:type": (req, res) => { - res.setHeader("Content-Type", "text/javascript"); - res.send({ ok: true }); - requestLog.push({ - type: req.params.type, - body: req.body, - }); - }, + //we're using same handler for s2s and browser events since + //we don't check types anyway + "/api/s/:type": handler, + "/api/s/s2s/:type": handler, }, }); console.log("Running on " + server.baseUrl); @@ -29,64 +35,319 @@ describe("Test Jitsu NodeJS client", () => { const shutdownServer = async () => { console.log("Shutting down server " + server.baseUrl); await server.close(); + console.log("Server is down " + server.baseUrl); }; - test("node js", async () => { + beforeAll(async () => { await startServer(); - try { - const jitsu: AnalyticsInterface = jitsuAnalytics({ - writeKey: "key:secret", - host: server.baseUrl, - debug: true, - fetch: fetchImpl, - }); - await jitsu.track("testTrack"); + }); - await jitsu.identify("testUser", { - email: "test@test.com", - }); + beforeEach(() => { + requestLog = []; + }); - await jitsu.group("testGroup", { - name: "Test Group", - }); + afterAll(async () => { + await shutdownServer(); + }); + + test("setAnonymousId test2", async () => { + const config = { + host: server.baseUrl, + writeKey: "key:secret", + debug: true, + }; + + console.log("[JITSU TEST] Initializing Jitsu"); + const client = jitsuAnalytics(config); + console.log("[JITSU TEST] Jitsu instance", client); + + const anonymousId = "anonymous_id_test"; + console.log("[JITSU TEST] Setting anonymous id to " + anonymousId); + await client.setAnonymousId(anonymousId); + console.log("user state", client.getState("user")); + + expect(requestLog.length).toBe(0); + console.log("[JITSU TEST] Sending event EVENT_1"); + await client.track("EVENT_1"); + await client.track("EVENT_2"); + await client.track("groupId"); + + await new Promise(resolve => setTimeout(resolve, 1000)); + + expect(requestLog.length).toBe(3); + expect(requestLog[1].body.anonymousId).toBe("anonymous_id_test"); + expect(requestLog[0].body.anonymousId).toBe("anonymous_id_test"); + expect(requestLog[2].body.anonymousId).toBe("anonymous_id_test"); + }); + + test("test privacy dontSend", async () => { + const config = { + host: server.baseUrl, + writeKey: "key:secret", + debug: true, + privacy: { + dontSend: true, + }, + }; + const client = jitsuAnalytics(config); + expect(requestLog.length).toBe(0); + await client.identify("myUserId", { email: "myUserId@example.com" }); + await client.group("myGroupId", { name: "myGroupId" }); + await client.track("myEvent", { prop1: "value1" }); + await new Promise(resolve => setTimeout(resolve, 1000)); + expect(requestLog.length).toBe(0); + }); + + test("test privacy dontSend then consent", async () => { + const config = { + host: server.baseUrl, + writeKey: "key:secret", + debug: true, + privacy: { + dontSend: true, + }, + }; + const client = jitsuAnalytics(config); + expect(requestLog.length).toBe(0); + await client.identify("myUserId", { email: "myUserId@example.com" }); + await client.group("myGroupId", { name: "myGroupId" }); + await client.track("myEvent", { prop1: "value1" }); + await new Promise(resolve => setTimeout(resolve, 1000)); + expect(requestLog.length).toBe(0); - await jitsu.page({ - name: "test", - environment: "nodejs", - context: { - page: { - url: "http://server.com", - }, + client.configure({ + privacy: { + dontSend: false, + consentCategories: { + analytics: true, }, - }); - await new Promise(resolve => setTimeout(resolve, 1000)); - expect(requestLog.length).toBe(4); - expect(requestLog[0].type).toBe("track"); - expect(requestLog[1].type).toBe("identify"); - expect(requestLog[2].type).toBe("group"); - expect(requestLog[3].type).toBe("page"); - - const track = requestLog[0].body as AnalyticsClientEvent; - const identify = requestLog[1].body as AnalyticsClientEvent; - const group = requestLog[2].body as AnalyticsClientEvent; - const page = requestLog[3].body as AnalyticsClientEvent; - - //expect(track.userId).toBe(undefined); - expect(page.properties.name).toBe("test"); - expect(page.properties?.environment).toBe("nodejs"); - expect(page.context.page.url).toBe("http://server.com"); - expect(page.userId).toBe("testUser"); - expect(identify.traits.email).toBe("test@test.com"); - expect(identify.anonymousId).toBe(page.anonymousId); - expect(group.traits.name).toBe("Test Group"); - expect(group.anonymousId).toBe(page.anonymousId); - expect(group.userId).toBe("testUser"); - expect(group.groupId).toBe("testGroup"); - - const pagePayload = requestLog[0].body; - console.log("pagePayload", pagePayload); - } finally { - await shutdownServer(); - } + }, + }); + await client.identify("myUserId", { email: "myUserId@example.com" }); + await client.group("myGroupId", { name: "myGroupId" }); + await client.track("myEvent", { prop1: "value1" }); + await new Promise(resolve => setTimeout(resolve, 1000)); + + expect(requestLog.length).toBe(3); + const p = requestLog[2]; + expect(p.type).toEqual("track"); + expect(p.body.event).toBe("myEvent"); + expect(p.body.userId).toEqual("myUserId"); + expect(p.body.groupId).toEqual("myGroupId"); + expect(p.body.context?.traits?.email).toEqual("myUserId@example.com"); + expect(p.body.context?.consent?.categoryPreferences).toEqual({ analytics: true }); + expect((p.body.anonymousId ?? "").length).toBeGreaterThan(0); + }); + + test("test privacy disableUserIds", async () => { + const config = { + host: server.baseUrl, + writeKey: "key:secret", + debug: true, + privacy: { + disableUserIds: true, + }, + }; + const client = jitsuAnalytics(config); + expect(requestLog.length).toBe(0); + await client.identify("myUserId", { email: "myUserId@example.com" }); + await client.group("myGroupId", { name: "myGroupId" }); + await client.track("myEvent", { prop1: "value1" }); + await new Promise(resolve => setTimeout(resolve, 1000)); + expect(requestLog.length).toBe(1); + const p = requestLog[0]; + expect(p.type).toEqual("track"); + expect(p.body.event).toBe("myEvent"); + expect(p.body.userId).toBeUndefined(); + expect(p.body.groupId).toBeUndefined(); + expect(p.body.context?.traits?.email).toBeUndefined(); + expect(p.body.anonymousId).toBeUndefined(); + expect(p.body.properties?.prop1).toBe("value1"); + }); + + test("test privacy disableUserIds then consent", async () => { + const config = { + host: server.baseUrl, + writeKey: "key:secret", + debug: true, + privacy: { + disableUserIds: true, + }, + }; + const client = jitsuAnalytics(config); + expect(requestLog.length).toBe(0); + await client.identify("myUserId", { email: "myUserId@example.com" }); + await client.group("myGroupId", { name: "myGroupId" }); + await client.track("myEvent", { prop1: "value1" }); + await new Promise(resolve => setTimeout(resolve, 1000)); + expect(requestLog.length).toBe(1); + let p = requestLog[0]; + expect(p.type).toEqual("track"); + expect(p.body.event).toBe("myEvent"); + expect(p.body.userId).toBeUndefined(); + expect(p.body.groupId).toBeUndefined(); + expect(p.body.context?.traits?.email).toBeUndefined(); + expect(p.body.anonymousId).toBeUndefined(); + expect(p.body.properties?.prop1).toBe("value1"); + + client.configure({ + privacy: { + disableUserIds: false, + consentCategories: { + analytics: true, + }, + }, + }); + await client.identify("myUserId", { email: "myUserId@example.com" }); + await client.group("myGroupId", { name: "myGroupId" }); + await client.track("myEvent", { prop1: "value1" }); + await new Promise(resolve => setTimeout(resolve, 1000)); + + expect(requestLog.length).toBe(4); + p = requestLog[3]; + expect(p.type).toEqual("track"); + expect(p.body.event).toBe("myEvent"); + expect(p.body.userId).toEqual("myUserId"); + expect(p.body.groupId).toEqual("myGroupId"); + expect(p.body.context?.traits?.email).toEqual("myUserId@example.com"); + expect(p.body.context?.consent?.categoryPreferences).toEqual({ analytics: true }); + + expect((p.body.anonymousId ?? "").length).toBeGreaterThan(0); + }); + + test("test setContextProperty", async () => { + const config = { + host: server.baseUrl, + writeKey: "key:secret", + debug: true, + }; + const client = jitsuAnalytics(config); + expect(requestLog.length).toBe(0); + await client.track("myEvent", { prop1: "value1" }); + client.setContextProperty("awesomeIdentifier", "awesome-identifier"); + client.setContextProperty("awesome", { + nestedKey: "awesome-key", + }); + await client.track("myEvent2", { prop1: "value2" }); + const v = client.getContextProperty("awesomeIdentifier"); + expect(v).toBe("awesome-identifier"); + client.setContextProperty("awesomeIdentifier", undefined); + client.setContextProperty("awesome", { + nestedKey: "awesome-key2", + }); + await client.track("myEvent3", { prop1: "value3" }); + + await new Promise(resolve => setTimeout(resolve, 1000)); + expect(requestLog.length).toBe(3); + expect(requestLog[0].body.context.awesomeIdentifier).toBeUndefined(); + expect(requestLog[0].body.context.awesome).toBeUndefined(); + expect(requestLog[1].body.context.awesomeIdentifier).toBe("awesome-identifier"); + expect(requestLog[1].body.context.awesome.nestedKey).toBe("awesome-key"); + expect(requestLog[2].body.context.awesomeIdentifier).toBeUndefined(); + expect(requestLog[2].body.context.awesome.nestedKey).toBe("awesome-key2"); + }); + + test("test defaultPayloadContext", async () => { + const config = { + host: server.baseUrl, + writeKey: "key:secret", + debug: true, + defaultPayloadContext: { + awesomeIdentifier: "awesome-identifier", + awesome: { + nestedKey: "awesome-key", + }, + }, + }; + const client = jitsuAnalytics(config); + expect(requestLog.length).toBe(0); + await client.identify("myUserId", { email: "myUserId@example.com" }); + await client.group("myGroupId", { name: "myGroupId" }); + await client.track("myEvent", { prop1: "value1" }); + await new Promise(resolve => setTimeout(resolve, 1000)); + expect(requestLog.length).toBe(3); + expect(requestLog[0].body.context.awesomeIdentifier).toBe("awesome-identifier"); + expect(requestLog[0].body.context.awesome.nestedKey).toBe("awesome-key"); + expect(requestLog[1].body.context.awesomeIdentifier).toBe("awesome-identifier"); + expect(requestLog[1].body.context.awesome.nestedKey).toBe("awesome-key"); + expect(requestLog[2].body.context.awesomeIdentifier).toBe("awesome-identifier"); + expect(requestLog[2].body.context.awesome.nestedKey).toBe("awesome-key"); + }); + + test("node-js", async () => { + const jitsu: AnalyticsInterface = jitsuAnalytics({ + writeKey: "key:secret", + host: server.baseUrl, + debug: true, + fetch: fetchImpl, + }); + await jitsu.track("testTrack"); + + await jitsu.identify("testUser", { + email: "test@test.com", + }); + + await jitsu.group("testGroup", { + name: "Test Group", + }); + + await jitsu.page({ + name: "test", + environment: "nodejs", + context: { + page: { + url: "http://server.com", + }, + }, + }); + await jitsu.track( + "email.open", + {}, + { + userId: "myUserId", + traits: { + email: "john.doe@gmail.com", + }, + } + ); + await new Promise(resolve => setTimeout(resolve, 1000)); + expect(requestLog.length).toBe(5); + expect(requestLog[0].type).toBe("track"); + expect(requestLog[1].type).toBe("identify"); + expect(requestLog[2].type).toBe("group"); + expect(requestLog[3].type).toBe("page"); + + const track = requestLog[0].body as AnalyticsClientEvent; + const identify = requestLog[1].body as AnalyticsClientEvent; + const group = requestLog[2].body as AnalyticsClientEvent; + const page = requestLog[3].body as AnalyticsClientEvent; + + //expect(track.userId).toBe(undefined); + expect(page.properties.name).toBe("test"); + expect(page.properties?.environment).toBe("nodejs"); + expect(page.context.page.url).toBe("http://server.com"); + expect(page.userId).toBe("testUser"); + expect(identify.traits.email).toBe("test@test.com"); + expect(identify.anonymousId).toBe(page.anonymousId); + expect(group.traits.name).toBe("Test Group"); + expect(group.anonymousId).toBe(page.anonymousId); + expect(group.userId).toBe("testUser"); + expect(group.groupId).toBe("testGroup"); + + const pagePayload = requestLog[0].body; + console.log("pagePayload", pagePayload); + + const emailOpenPayload = requestLog[4].body; + console.log("emailOpenPayload", emailOpenPayload); + expect(emailOpenPayload.userId).toBe("myUserId"); + }); + + test("tld", async () => { + expect(getTopLevelDomain("www.google.com")).toBe("google.com"); + expect(getTopLevelDomain("www.trendstyle.com.au")).toBe("trendstyle.com.au"); + expect(getTopLevelDomain("localhost:3000")).toBe("localhost"); + expect(getTopLevelDomain("use.jitsu.com")).toBe("jitsu.com"); + expect(getTopLevelDomain("use.jitsu.com")).toBe("jitsu.com"); + //console.log(parse("http://localhost:3000")); }); }); diff --git a/libs/jitsu-js/__tests__/playwright/cases/anonymous-id-bug.html b/libs/jitsu-js/__tests__/playwright/cases/anonymous-id-bug.html new file mode 100644 index 000000000..9f3bf9046 --- /dev/null +++ b/libs/jitsu-js/__tests__/playwright/cases/anonymous-id-bug.html @@ -0,0 +1,26 @@ + + + + + + + + Tracking page + + + + + +

Test

+ + diff --git a/libs/jitsu-js/__tests__/playwright/cases/basic.html b/libs/jitsu-js/__tests__/playwright/cases/basic.html index 4a9440d2b..fd1cd727b 100644 --- a/libs/jitsu-js/__tests__/playwright/cases/basic.html +++ b/libs/jitsu-js/__tests__/playwright/cases/basic.html @@ -7,8 +7,13 @@ Tracking page diff --git a/libs/jitsu-js/__tests__/playwright/cases/callbacks.html b/libs/jitsu-js/__tests__/playwright/cases/callbacks.html new file mode 100644 index 000000000..e2c379168 --- /dev/null +++ b/libs/jitsu-js/__tests__/playwright/cases/callbacks.html @@ -0,0 +1,21 @@ + + + + + + + Tracking page + + + + + +

Test

+ + diff --git a/libs/jitsu-js/__tests__/playwright/cases/cookie-names.html b/libs/jitsu-js/__tests__/playwright/cases/cookie-names.html new file mode 100644 index 000000000..bc939a21c --- /dev/null +++ b/libs/jitsu-js/__tests__/playwright/cases/cookie-names.html @@ -0,0 +1,22 @@ + + + + + + + + Tracking page + + + + +

Test

+ + diff --git a/libs/jitsu-js/__tests__/playwright/cases/disable-user-ids.html b/libs/jitsu-js/__tests__/playwright/cases/disable-user-ids.html new file mode 100644 index 000000000..0557c6159 --- /dev/null +++ b/libs/jitsu-js/__tests__/playwright/cases/disable-user-ids.html @@ -0,0 +1,23 @@ + + + + + + + + Tracking page + + + + + +

Test

+ + diff --git a/libs/jitsu-js/__tests__/playwright/cases/dont-send.html b/libs/jitsu-js/__tests__/playwright/cases/dont-send.html new file mode 100644 index 000000000..b02bdbf28 --- /dev/null +++ b/libs/jitsu-js/__tests__/playwright/cases/dont-send.html @@ -0,0 +1,22 @@ + + + + + + + + Tracking page + + + + + +

Test

+ + diff --git a/libs/jitsu-js/__tests__/playwright/cases/ip-policy.html b/libs/jitsu-js/__tests__/playwright/cases/ip-policy.html new file mode 100644 index 000000000..fbe9951b0 --- /dev/null +++ b/libs/jitsu-js/__tests__/playwright/cases/ip-policy.html @@ -0,0 +1,22 @@ + + + + + + + + Tracking page + + + + + +

Test

+ + diff --git a/libs/jitsu-js/__tests__/playwright/cases/reset.html b/libs/jitsu-js/__tests__/playwright/cases/reset.html new file mode 100644 index 000000000..4b3637f69 --- /dev/null +++ b/libs/jitsu-js/__tests__/playwright/cases/reset.html @@ -0,0 +1,32 @@ + + + + + + + + Tracking page + + + + + +

Test

+ + diff --git a/libs/jitsu-js/__tests__/playwright/cases/spa-navigation.html b/libs/jitsu-js/__tests__/playwright/cases/spa-navigation.html new file mode 100644 index 000000000..7e8ebf46f --- /dev/null +++ b/libs/jitsu-js/__tests__/playwright/cases/spa-navigation.html @@ -0,0 +1,70 @@ + + + + + + + SPA Navigation Test + + + + +

SPA Navigation Test

+
+

This page simulates a Single Page Application navigation

+
+ + diff --git a/libs/jitsu-js/__tests__/playwright/cases/url-bug.html b/libs/jitsu-js/__tests__/playwright/cases/url-bug.html new file mode 100644 index 000000000..c58dba8b2 --- /dev/null +++ b/libs/jitsu-js/__tests__/playwright/cases/url-bug.html @@ -0,0 +1,20 @@ + + + + + + + + Tracking page + + + + + +

Test

+ + diff --git a/libs/jitsu-js/__tests__/playwright/integration.test.ts b/libs/jitsu-js/__tests__/playwright/integration.test.ts index e9d494abc..6d40dc184 100644 --- a/libs/jitsu-js/__tests__/playwright/integration.test.ts +++ b/libs/jitsu-js/__tests__/playwright/integration.test.ts @@ -5,7 +5,6 @@ import * as path from "path"; import ejs from "ejs"; // import chalk from "chalk"; import * as process from "process"; -import * as console from "console"; import { AnalyticsClientEvent, AnalyticsInterface } from "@jitsu/protocols/analytics.d"; test.use({ @@ -24,7 +23,7 @@ const app = express(); let server: SimpleSyrup; -let requestLog: { type: string; body: AnalyticsClientEvent }[] = []; +let requestLog: { type: string; body: AnalyticsClientEvent; headers: any }[] = []; test.beforeAll(async () => { const testCasesHandlers = fs.readdirSync(path.join(__dirname, "cases")).reduce((res, file) => { @@ -52,11 +51,15 @@ test.beforeAll(async () => { res.setHeader("Content-Type", "text/javascript"); res.send(fs.readFileSync(path.join(__dirname, "../../dist/web/p.js.txt")).toString()); }, - "/api/s/:type": (req, res) => { + "/api/s/:type": async (req, res) => { + //sleep for 30ms to simulate network latency. It helps catch bugs with async processing + await new Promise(resolve => setTimeout(resolve, 50)); + res.setHeader("Content-Type", "text/javascript"); res.send({ ok: true }); requestLog.push({ type: req.params.type, + headers: req.headers, body: req.body, }); }, @@ -112,12 +115,14 @@ const generateTestEvents = async () => { const analytics = (window["analytics"] || window["jitsu"]) as AnalyticsInterface; console.log(`Generating test events. Implementation ${implName}: ${Object.keys(analytics)}`); await analytics.identify("userId2", { email: "john.doe2@gmail.com", caseName: "basic-identify" }); + await analytics.page("test-page-right-after-identify", { caseName: "test-page-right-after-identify" }); // jitsu must extract traits even from 'id' object await analytics.identify({ email: "john.doe3@gmail.com", caseName: "identify-without-user-id" }); await analytics.group("group1", { name: "Group 1", caseName: "basic-group" }); await analytics.page({ caseName: "page-without-name", context: { page: { title: "Synthetic Title" } } }); await analytics.page("test-page", { caseName: "page-with-name" }); await analytics.track("testEvent", { caseName: "track-with-name" }); + await analytics.identify(9292649175 as any, { caseName: "identify-with-numeric-id-1" }); console.log(`Test events for ${implName} has been generated`); }; @@ -162,24 +167,489 @@ test("segment-reference", async ({ browser }) => { {} ); console.log("🍪 Segment Cookies", cookies); - + let counter = 1; for (const type of Object.keys(requests)) { for (const { payload } of requests[type]) { const dir = path.join(__dirname, "artifacts", "segment-reference"); fs.mkdirSync(dir, { recursive: true }); const file = path.join( dir, - `${payload.traits?.caseName || payload.properties?.caseName || payload.context?.caseName}.json` + `${counter++} - ${payload.traits?.caseName || payload.properties?.caseName || payload.context?.caseName}.json` ); fs.writeFileSync(file, JSON.stringify(sortKeysRecursively(payload), null, 2)); } } }); +function describeEvent(type: string, body: any) { + const params = [ + body.userId ? "userId=" + body.userId : undefined, + body.anonymousId ? "anonId=" + body.anonymousId : undefined, + body.traits ? ["traits=" + JSON.stringify(body.traits)] : [], + ] + .filter(x => !!x) + .join(", "); + return `${type}${type === "track" ? `(${body.event})` : ""}[${params}]`; +} + +function clearRequestLog() { + requestLog.length = 0; +} + +test("jitsu-queue-callbacks", async ({ browser }) => { + clearRequestLog(); + const browserContext = await browser.newContext(); + const { page, uncaughtErrors } = await createLoggingPage(browserContext); + const [pageResult] = await Promise.all([page.goto(`${server.baseUrl}/callbacks.html`)]); + await page.waitForFunction(() => window["jitsu"] !== undefined, undefined, { + timeout: 1000, + polling: 100, + }); + //wait for some time since the server has an artificial latency of 30ms + await new Promise(resolve => setTimeout(resolve, 1000)); + console.log( + `📝 Request log size of ${requestLog.length}`, + requestLog.map(x => describeEvent(x.type, x.body)) + ); + expect(requestLog.length).toBe(3); +}); + +// Skip this test because jitsu-js no longer relies on canonical URL +test.skip("url-bug", async ({ browser, context }) => { + //tests a bug in getanalytics.io where the url without slash provided by + // causes incorrect page path + const browserContext = await browser.newContext(); + const { page, uncaughtErrors } = await createLoggingPage(browserContext); + const [pageResult] = await Promise.all([page.goto(`${server.baseUrl}/url-bug.html`)]); + + await page.waitForFunction(() => window["jitsu"] !== undefined, undefined, { + timeout: 1000, + polling: 100, + }); + expect(pageResult.status()).toBe(200); + //wait for some time since the server has an artificial latency of 30ms + await new Promise(resolve => setTimeout(resolve, 1000)); + expect(uncaughtErrors.length).toEqual(0); + expect(requestLog.length).toBe(2); + console.log( + `📝 Request log size of ${requestLog.length}`, + requestLog.map(x => describeEvent(x.type, x.body)) + ); + //a track contains a valid URL, probably because analytics can't grab the canonical URL yet + const trackEvent = requestLog.find(x => x.type === "page"); + expect(trackEvent).toBeDefined(); + const pagePath = trackEvent.body.context.page.path; + expect(pagePath).toBeDefined(); + //it's "//localhost:3088" when the bug is present + expect(pagePath).toEqual("/"); +}); + +test("reset", async ({ browser }) => { + clearRequestLog(); + const browserContext = await browser.newContext(); + const { page, uncaughtErrors } = await createLoggingPage(browserContext); + const [pageResult] = await Promise.all([page.goto(`${server.baseUrl}/reset.html`)]); + await page.waitForFunction(() => window["jitsu"] !== undefined, undefined, { + timeout: 1000, + polling: 100, + }); + expect(pageResult.status()).toBe(200); + //wait for some time since the server has an artificial latency of 30ms + await new Promise(resolve => setTimeout(resolve, 1000)); + expect(uncaughtErrors.length).toEqual(0); + expect(requestLog.length).toBe(3); + console.log( + `📝 Request log size of ${requestLog.length}`, + requestLog.map(x => describeEvent(x.type, x.body)) + ); + const [identifyEvent, firstTrack, secondTrack] = requestLog; + expect(firstTrack.body.anonymousId).toEqual("john-doe-id-1"); + + const cookies = await browserContext.cookies(); + // all cookies should be cleared by .reset() + // but new cookie for new anonymousId should be set + expect(cookies.length).toBe(1); + expect(cookies[0].name).toEqual("__eventn_id"); + const newAnonymousId = cookies[0].value; + console.log(`🍪Cookies`, cookies); + + //second identify call should not reach the server, but it should change the traits + expect(firstTrack.body.context.traits?.email).toEqual("john2@example.com"); + + expect(secondTrack.body.anonymousId).not.toBeNull(); + expect(secondTrack.body.anonymousId).toBeDefined(); + expect(secondTrack.body.anonymousId).toEqual(newAnonymousId); + expect(secondTrack.body.anonymousId).not.toEqual("john-doe-id-1"); +}); + +const generateEventsForConsentTests = async () => { + const analytics = window["jitsu"] as AnalyticsInterface; + await analytics.identify("myUserId", { email: "myUserId@example.com" }); + await analytics.group("myGroupId", { name: "myGroupId" }); + await analytics.page("myPage"); +}; + +test("ip-policy", async ({ browser }) => { + clearRequestLog(); + const browserContext = await browser.newContext(); + const { page, uncaughtErrors } = await createLoggingPage(browserContext); + const [pageResult] = await Promise.all([page.goto(`${server.baseUrl}/ip-policy.html`)]); + await page.waitForFunction(() => window["jitsu"] !== undefined, undefined, { + timeout: 1000, + polling: 100, + }); + expect(pageResult?.status()).toBe(200); + //wait for some time since the server has an artificial latency of 30ms + await new Promise(resolve => setTimeout(resolve, 1000)); + expect(uncaughtErrors.length).toEqual(0); + expect(requestLog.length).toBe(1); + const p = requestLog[0]; + expect(p.headers?.["x-ip-policy"]).toEqual("stripLastOctet"); +}); + +test("dont-send", async ({ browser }) => { + clearRequestLog(); + const browserContext = await browser.newContext(); + const { page, uncaughtErrors } = await createLoggingPage(browserContext); + const [pageResult] = await Promise.all([page.goto(`${server.baseUrl}/dont-send.html`)]); + await page.waitForFunction(() => window["jitsu"] !== undefined, undefined, { + timeout: 1000, + polling: 100, + }); + expect(pageResult?.status()).toBe(200); + //wait for some time since the server has an artificial latency of 30ms + await new Promise(resolve => setTimeout(resolve, 1000)); + expect(uncaughtErrors.length).toEqual(0); + expect(requestLog.length).toBe(0); + await page.evaluate(generateEventsForConsentTests); + + const cookies = await browserContext.cookies(); + + expect(uncaughtErrors.length).toEqual(0); + expect(requestLog.length).toBe(0); + expect(cookies.length).toBe(0); +}); + +test("dont-send-then-consent", async ({ browser }) => { + clearRequestLog(); + const browserContext = await browser.newContext(); + const { page, uncaughtErrors } = await createLoggingPage(browserContext); + const [pageResult] = await Promise.all([page.goto(`${server.baseUrl}/dont-send.html`)]); + await page.waitForFunction(() => window["jitsu"] !== undefined, undefined, { + timeout: 1000, + polling: 100, + }); + expect(pageResult?.status()).toBe(200); + //wait for some time since the server has an artificial latency of 30ms + await new Promise(resolve => setTimeout(resolve, 1000)); + expect(uncaughtErrors.length).toEqual(0); + expect(requestLog.length).toBe(0); + + await page.evaluate(async () => { + const analytics = window["jitsu"] as AnalyticsInterface; + analytics.configure({ + privacy: { + dontSend: false, + consentCategories: { + analytics: true, + }, + }, + }); + }); + await page.evaluate(generateEventsForConsentTests); + await new Promise(resolve => setTimeout(resolve, 1000)); + + const cookies = await browserContext.cookies(); + expect(uncaughtErrors.length).toEqual(0); + expect(requestLog.length).toBe(3); + expect(cookies.length).toBe(5); + const p = requestLog[2]; + expect(p.type).toEqual("page"); + expect(p.body.userId).toEqual("myUserId"); + expect(p.body.groupId).toEqual("myGroupId"); + expect(p.body.context?.traits?.email).toEqual("myUserId@example.com"); + expect(p.body.context?.consent?.categoryPreferences).toEqual({ analytics: true }); + expect((p.body.anonymousId ?? "").length).toBeGreaterThan(0); +}); + +test("disable-user-ids", async ({ browser }) => { + clearRequestLog(); + const browserContext = await browser.newContext(); + const { page, uncaughtErrors } = await createLoggingPage(browserContext); + const [pageResult] = await Promise.all([page.goto(`${server.baseUrl}/disable-user-ids.html`)]); + await page.waitForFunction(() => window["jitsu"] !== undefined, undefined, { + timeout: 1000, + polling: 100, + }); + expect(pageResult?.status()).toBe(200); + //wait for some time since the server has an artificial latency of 30ms + await new Promise(resolve => setTimeout(resolve, 1000)); + expect(uncaughtErrors.length).toEqual(0); + + expect(requestLog.length).toBe(0); + await page.evaluate(generateEventsForConsentTests); + + const cookies = await browserContext.cookies(); + + expect(uncaughtErrors.length).toEqual(0); + expect(cookies.length).toBe(0); + expect(requestLog.length).toBe(1); + const p = requestLog[0]; + expect(p.type).toEqual("page"); + expect(p.body.userId).toBeUndefined(); + expect(p.body.groupId).toBeUndefined(); + expect(p.body.context?.traits?.email).toBeUndefined(); + expect(p.body.anonymousId).toBeUndefined(); + expect(p.body.properties?.path).toBe("/disable-user-ids.html"); +}); + +test("disable-user-ids-then-consent", async ({ browser }) => { + clearRequestLog(); + const browserContext = await browser.newContext(); + const { page, uncaughtErrors } = await createLoggingPage(browserContext); + const [pageResult] = await Promise.all([page.goto(`${server.baseUrl}/disable-user-ids.html`)]); + await page.waitForFunction(() => window["jitsu"] !== undefined, undefined, { + timeout: 1000, + polling: 100, + }); + expect(pageResult?.status()).toBe(200); + //wait for some time since the server has an artificial latency of 30ms + await new Promise(resolve => setTimeout(resolve, 1000)); + expect(uncaughtErrors.length).toEqual(0); + expect(requestLog.length).toBe(0); + await page.evaluate(generateEventsForConsentTests); + let cookies = await browserContext.cookies(); + expect(uncaughtErrors.length).toEqual(0); + expect(cookies.length).toBe(0); + expect(requestLog.length).toBe(1); + + await page.evaluate(async () => { + const analytics = window["jitsu"] as AnalyticsInterface; + analytics.configure({ + privacy: { + disableUserIds: false, + consentCategories: { + analytics: true, + }, + }, + }); + }); + await page.evaluate(generateEventsForConsentTests); + await new Promise(resolve => setTimeout(resolve, 1000)); + + cookies = await browserContext.cookies(); + expect(uncaughtErrors.length).toEqual(0); + expect(requestLog.length).toBe(4); + expect(cookies.length).toBe(5); + const p = requestLog[3]; + expect(p.type).toEqual("page"); + expect(p.body.userId).toEqual("myUserId"); + expect(p.body.groupId).toEqual("myGroupId"); + expect(p.body.context?.traits?.email).toEqual("myUserId@example.com"); + expect(p.body.context?.consent?.categoryPreferences).toEqual({ analytics: true }); + expect((p.body.anonymousId ?? "").length).toBeGreaterThan(0); +}); + +test("anonymous-id-bug", async ({ browser }) => { + clearRequestLog(); + const anonymousId = "1724633695283.638279"; + const browserContext = await browser.newContext(); + await browserContext.addCookies([{ name: "__eventn_id", value: anonymousId, url: server.baseUrl }]); + const { page, uncaughtErrors } = await createLoggingPage(browserContext); + const [pageResult] = await Promise.all([page.goto(`${server.baseUrl}/anonymous-id-bug.html`)]); + await page.waitForFunction(() => window["jitsu"] !== undefined, undefined, { + timeout: 1000, + polling: 100, + }); + expect(pageResult.status()).toBe(200); + const cookies = (await browserContext.cookies()).reduce( + (res, cookie) => ({ + ...res, + [cookie.name]: cookie.value, + }), + {} + ); + console.log("🍪 Jitsu Cookies", cookies); + //wait for some time since the server has an artificial latency of 30ms + await new Promise(resolve => setTimeout(resolve, 1000)); + expect(uncaughtErrors.length).toEqual(0); + console.log( + `📝 Request log size of ${requestLog.length}`, + requestLog.map(x => describeEvent(x.type, x.body)) + ); + const p = requestLog[0]; + console.log(chalk.bold("📝 Checking page event"), JSON.stringify(p, null, 3)); + expect(p.body.anonymousId).toEqual(anonymousId); +}); + +test("cookie-names", async ({ browser }) => { + clearRequestLog(); + const browserContext = await browser.newContext(); + const { page, uncaughtErrors } = await createLoggingPage(browserContext); + const pageResult = await page.goto(`${server.baseUrl}/cookie-names.html`); + await page.waitForFunction(() => window["jitsu"] !== undefined, undefined, { + timeout: 1000, + polling: 100, + }); + expect(pageResult.status()).toBe(200); + const cookies = (await browserContext.cookies()).reduce( + (res, cookie) => ({ + ...res, + [cookie.name]: cookie.value, + }), + {} + ); + console.log("🍪 Jitsu Cookies", cookies); + expect(cookies).toHaveProperty("my_anon_ck"); + const anonymousId = cookies["my_anon_ck"]; + + //wait for some time since the server has an artificial latency of 30ms + await new Promise(resolve => setTimeout(resolve, 1000)); + expect(uncaughtErrors.length).toEqual(0); + console.log( + `📝 Request log size of ${requestLog.length}`, + requestLog.map(x => describeEvent(x.type, x.body)) + ); + expect(requestLog[0].body.anonymousId).toEqual(anonymousId); + + const { page: secondPage } = await createLoggingPage(browserContext); + const pageResult2 = await secondPage.goto( + `${server.baseUrl}/cookie-names.html?utm_source=source&utm_medium=medium&utm_campaign=campaign` + ); + await secondPage.waitForFunction(() => window["jitsu"] !== undefined, undefined, { + timeout: 1000, + polling: 100, + }); + expect(pageResult2.status()).toBe(200); + const cookies2 = (await browserContext.cookies()).reduce( + (res, cookie) => ({ + ...res, + [cookie.name]: cookie.value, + }), + {} + ); + console.log("🍪 Jitsu Cookies", cookies2); + expect(cookies2).toHaveProperty("my_anon_ck"); + expect(cookies["my_anon_ck"]).toEqual(anonymousId); + + //wait for some time since the server has an artificial latency of 30ms + await new Promise(resolve => setTimeout(resolve, 1000)); + expect(uncaughtErrors.length).toEqual(0); + console.log( + `📝 Request log size of ${requestLog.length}`, + requestLog.map(x => describeEvent(x.type, x.body)) + ); + expect(requestLog[1].body.anonymousId).toEqual(anonymousId); +}); + +test("spa-navigation", async ({ browser }) => { + clearRequestLog(); + const browserContext = await browser.newContext(); + const { page, uncaughtErrors } = await createLoggingPage(browserContext); + + const initialUrl = `${server.baseUrl}/spa-navigation.html`; + const pageResult = await page.goto(initialUrl); + + await page.waitForFunction(() => window["jitsu"] !== undefined, undefined, { + timeout: 1000, + polling: 100, + }); + + expect(pageResult.status()).toBe(200); + + await page.waitForFunction(() => window["__spaTestComplete"] === true, undefined, { + timeout: 2000, + polling: 100, + }); + + await new Promise(resolve => setTimeout(resolve, 500)); + + expect(uncaughtErrors.length).toEqual(0); + + console.log( + `📝 Request log size of ${requestLog.length}`, + requestLog.map(x => describeEvent(x.type, x.body)) + ); + + const pageEvents = requestLog.filter(x => x.type === "page"); + const trackEvents = requestLog.filter(x => x.type === "track"); + + expect(pageEvents.length).toBe(6); + expect(trackEvents.length).toBe(6); + + //expect(pageEvents[0].body.name).toBe("initial-page"); + expect(pageEvents[0].body.properties.path).toBe("/spa-navigation.html"); + expect(pageEvents[0].body.properties.url).toContain("/spa-navigation.html"); + expect(pageEvents[0].body.context.page.path).toBe("/spa-navigation.html"); + expect(pageEvents[0].body.context.page.url).toContain("/spa-navigation.html"); + + expect(pageEvents[1].body.properties.path).toBe("/page1"); + expect(pageEvents[1].body.properties.url).toContain("/page1"); + expect(pageEvents[1].body.properties.search).toBe(""); + expect(pageEvents[1].body.properties.hash).toBe(""); + expect(pageEvents[1].body.context.page.path).toBe("/page1"); + expect(pageEvents[1].body.context.page.url).toContain("/page1"); + expect(pageEvents[1].body.context.page.search).toBe(""); + + expect(pageEvents[2].body.properties.path).toBe("/page2"); + expect(pageEvents[2].body.properties.url).toContain("/page2?param1=value1"); + expect(pageEvents[2].body.properties.search).toBe("?param1=value1"); + expect(pageEvents[2].body.properties.hash).toBe(""); + expect(pageEvents[2].body.context.page.path).toBe("/page2"); + expect(pageEvents[2].body.context.page.url).toContain("/page2?param1=value1"); + expect(pageEvents[2].body.context.page.search).toBe("?param1=value1"); + + expect(pageEvents[3].body.properties.path).toBe("/page3"); + expect(pageEvents[3].body.properties.url).toContain("/page3?param1=value1¶m2=value2"); + expect(pageEvents[3].body.properties.search).toBe("?param1=value1¶m2=value2"); + expect(pageEvents[3].body.properties.hash).toBe("#section1"); + expect(pageEvents[3].body.context.page.path).toBe("/page3"); + expect(pageEvents[3].body.context.page.url).toContain("/page3?param1=value1¶m2=value2"); + expect(pageEvents[3].body.context.page.search).toBe("?param1=value1¶m2=value2"); + + expect(pageEvents[4].body.properties.path).toBe("/page4/subpage"); + expect(pageEvents[4].body.properties.url).toContain("/page4/subpage"); + expect(pageEvents[4].body.properties.search).toBe(""); + expect(pageEvents[4].body.properties.hash).toBe("#section2"); + expect(pageEvents[4].body.context.page.path).toBe("/page4/subpage"); + expect(pageEvents[4].body.context.page.url).toContain("/page4/subpage"); + expect(pageEvents[4].body.context.page.search).toBe(""); + + expect(pageEvents[5].body.properties.path).toBe("/final"); + expect(pageEvents[5].body.properties.url).toContain("/final?utm_source=test&utm_medium=spa"); + expect(pageEvents[5].body.properties.search).toBe("?utm_source=test&utm_medium=spa"); + expect(pageEvents[5].body.properties.hash).toBe("#top"); + expect(pageEvents[5].body.context.page.path).toBe("/final"); + expect(pageEvents[5].body.context.page.url).toContain("/final?utm_source=test&utm_medium=spa"); + expect(pageEvents[5].body.context.page.search).toBe("?utm_source=test&utm_medium=spa"); + + const navTrackEvents = trackEvents.filter(x => x.body.event === "navigation"); + expect(navTrackEvents.length).toBe(5); + + expect(navTrackEvents[0].body.context.page.path).toBe("/page1"); + expect(navTrackEvents[1].body.context.page.url).toContain("/page2?param1=value1"); + expect(navTrackEvents[1].body.context.page.path).toBe("/page2"); + expect(navTrackEvents[1].body.context.page.search).toBe("?param1=value1"); + expect(navTrackEvents[2].body.context.page.url).toContain("/page3?param1=value1¶m2=value2#section1"); + expect(navTrackEvents[2].body.context.page.path).toBe("/page3"); + expect(navTrackEvents[2].body.context.page.search).toBe("?param1=value1¶m2=value2"); + expect(navTrackEvents[3].body.context.page.url).toContain("/page4/subpage#section2"); + expect(navTrackEvents[3].body.context.page.path).toBe("/page4/subpage"); + expect(navTrackEvents[4].body.context.page.url).toContain("/final?utm_source=test&utm_medium=spa#top"); + expect(navTrackEvents[4].body.context.page.path).toBe("/final"); + expect(navTrackEvents[4].body.context.page.search).toBe("?utm_source=test&utm_medium=spa"); +}); + test("basic", async ({ browser }) => { + clearRequestLog(); const browserContext = await browser.newContext(); + await browserContext.addCookies([ + { name: "_fbc", value: "fbc-id", url: server.baseUrl }, + { name: "_fbp", value: "fbp-id", url: server.baseUrl }, + { name: "_ttp", value: "ttp-id", url: server.baseUrl }, + ]); - const { page: firstPage, uncaughtErrors: fistPageErrors } = await createLoggingPage(browserContext); + const { page: firstPage, uncaughtErrors: firstPageErrors } = await createLoggingPage(browserContext); const [pageResult] = await Promise.all([ firstPage.goto(`${server.baseUrl}/basic.html?utm_source=source&utm_medium=medium&utm_campaign=campaign`), ]); @@ -197,12 +667,18 @@ test("basic", async ({ browser }) => { {} ); console.log("🍪 Jitsu Cookies", cookies); - expect(fistPageErrors.length).toEqual(0); + //wait for some time since the server has an artificial latency of 30ms + await new Promise(resolve => setTimeout(resolve, 1000)); + expect(firstPageErrors.length).toEqual(0); const anonymousId = cookies["__eventn_id"]; expect(anonymousId).toBeDefined(); - expect(cookies["__eventn_uid"]).toBe("user1"); + expect(cookies["__eventn_uid"]).toBe("john-doe-id-1"); expect(cookies["__eventn_id_usr"]).toBeDefined(); - expect(JSON.parse(cookies["__eventn_id_usr"]).email).toEqual("john.doe@gmail.com"); + expect(JSON.parse(decodeURIComponent(cookies["__eventn_id_usr"])).email).toEqual("john.doe@gmail.com"); + console.log( + `📝 Request log size of ${requestLog.length}`, + requestLog.map(x => describeEvent(x.type, x.body)) + ); let identifies = requestLog.filter(x => x.type === "identify"); let pages = requestLog.filter(x => x.type === "page"); let tracks = requestLog.filter(x => x.type === "track"); @@ -217,19 +693,25 @@ test("basic", async ({ browser }) => { console.log(chalk.bold("📝 Checking track event"), JSON.stringify(track, null, 3)); expect(track.properties.trackParam).toEqual("trackValue"); expect(track.type).toEqual("track"); + expect(track.context.clientIds).toHaveProperty("fbc", "fbc-id"); + expect(track.context.clientIds).toHaveProperty("fbp", "fbp-id"); + expect(track.context.clientIds).toHaveProperty("ttp", "ttp-id"); expect(track.context.traits.email).toEqual("john.doe@gmail.com"); - expect(track.userId).toEqual("user1"); + expect(track.userId).toEqual("john-doe-id-1"); expect(track.event).toEqual("pageLoaded"); console.log(chalk.bold("📝 Checking identify event"), JSON.stringify(identify, null, 3)); expect(identify.traits.email).toEqual("john.doe@gmail.com"); - expect(identify.userId).toEqual("user1"); + expect(identify.userId).toEqual("john-doe-id-1"); expect(identify.anonymousId).toEqual(anonymousId); console.log(chalk.bold("📝 Checking page event"), JSON.stringify(page, null, 3)); expect(page.anonymousId).toEqual(anonymousId); + expect(page.context.clientIds).toHaveProperty("fbc", "fbc-id"); + expect(page.context.clientIds).toHaveProperty("fbp", "fbp-id"); + expect(page.context.clientIds).toHaveProperty("ttp", "ttp-id"); expect(page.context.traits.email).toEqual("john.doe@gmail.com"); - expect(page.userId).toEqual("user1"); + expect(page.userId).toEqual("john-doe-id-1"); expect(page.context.campaign.source).toEqual("source"); @@ -239,17 +721,59 @@ test("basic", async ({ browser }) => { timeout: 1000, polling: 100, }); - requestLog.length = 0; + clearRequestLog(); await secondPage.evaluate(generateTestEvents); expect(secondPageErrors.length).toBe(0); + let counter = 1; requestLog.forEach(({ body: payload }) => { const dir = path.join(__dirname, "artifacts", "requests"); fs.mkdirSync(dir, { recursive: true }); const file = path.join( dir, - `${payload.traits?.caseName || payload.properties?.caseName || payload.context?.caseName}.json` + `${counter++} - ${payload.traits?.caseName || payload.properties?.caseName || payload.context?.caseName}.json` ); fs.writeFileSync(file, JSON.stringify(sortKeysRecursively(payload), null, 2)); }); }); + +test("ga4_cookies", async ({ browser }) => { + clearRequestLog(); + const browserContext = await browser.newContext(); + await browserContext.addCookies([ + // client id cookie + { name: "_ga", value: "GA1.1.808865741.1700468121", url: server.baseUrl }, + // old session id cookies + { name: "_ga_OLDDEFGHI1", value: "GS1.1.1711045472.9.0.1711045472.0.0.0", url: server.baseUrl }, + // new session id cookies + { name: "_ga_NEWDEFGHI2", value: "GS2.1.s1756461685$o733$g1$t1756461704$j41$l0$h0", url: server.baseUrl }, + { name: "_ga_NEWDEFGHI3", value: "GS2.1.o733$g1$t1756461704$j41$l0$h0$s1756461686", url: server.baseUrl }, + ]); + + const { page: firstPage, uncaughtErrors: firstPageErrors } = await createLoggingPage(browserContext); + const [pageResult] = await Promise.all([ + firstPage.goto(`${server.baseUrl}/basic.html?utm_source=source&utm_medium=medium&utm_campaign=campaign`), + ]); + + await firstPage.waitForFunction(() => window["jitsu"] !== undefined, undefined, { + timeout: 1000, + polling: 100, + }); + expect(pageResult.status()).toBe(200); + await new Promise(resolve => setTimeout(resolve, 1000)); + + console.log( + `📝 Request log size of ${requestLog.length}`, + requestLog.map(x => describeEvent(x.type, x.body)) + ); + let pages = requestLog.filter(x => x.type === "page"); + expect(pages.length).toBe(1); + + const page = pages[0].body as AnalyticsClientEvent; + + console.log(chalk.bold("📝 Checking page event"), JSON.stringify(page, null, 3)); + expect(page.context.clientIds.ga4.clientId).toBe("808865741.1700468121"); + expect(page.context.clientIds.ga4.sessionIds).toHaveProperty("OLDDEFGHI1", "1711045472"); + expect(page.context.clientIds.ga4.sessionIds).toHaveProperty("NEWDEFGHI2", "1756461685"); + expect(page.context.clientIds.ga4.sessionIds).toHaveProperty("NEWDEFGHI3", "1756461686"); +}); diff --git a/libs/jitsu-js/package.json b/libs/jitsu-js/package.json index dcf5a37dc..22f790599 100644 --- a/libs/jitsu-js/package.json +++ b/libs/jitsu-js/package.json @@ -4,47 +4,53 @@ "description": "", "author": "Jitsu Dev Team ", "main": "dist/jitsu.cjs.js", + "files": ["dist"], "module": "dist/jitsu.es.js", - "types": "dist/index.d.ts", + "types": "dist/jitsu.d.ts", "publishConfig": { "access": "public" }, "license": "MIT", "private": false, "scripts": { - "clean": "rm -rf ./dist", + "clean": "rm -rf ./dist ./compiled", "test:node": "jest", "test:playwright": "playwright test ./__tests__/playwright", "test": "pnpm run test:node && pnpm run test:playwright", "compile": "tsc -p .", - "build": "tsc -p . && rollup -c && cp compiled/src/*.d.ts dist" + "build": "pnpm run clean && pnpm run compile && rollup -c" }, "devDependencies": { - "@jitsu/protocols": "workspace:*", - "@playwright/test": "1.31.2", - "@rollup/plugin-commonjs": "^23.0.2", + "tslib": "^2.6.3", + "@playwright/test": "1.39.0", + "@rollup/plugin-commonjs": "^28.0.2", + "rollup-plugin-dts": "^6.2.1", "@rollup/plugin-json": "^5.0.1", "@rollup/plugin-multi-entry": "^6.0.0", - "@rollup/plugin-node-resolve": "^15.0.1", + "@rollup/plugin-node-resolve": "^16.0.0", "@rollup/plugin-replace": "^5.0.1", "@rollup/plugin-terser": "^0.1.0", - "@segment/analytics-next": "^1.46.0", + "@segment/analytics-next": "^1.75.0", "@types/chalk": "^2.2.0", "@types/jest": "^29.2.0", - "@types/node": "^16.11.7", + "@types/node": "^18.15.3", "chalk": "^4.1.2", - "cookie-parser": "^1.4.6", + "cookie-parser": "^1.4.7", "ejs": "^3.1.8", - "express": "^4.18.2", + "express": "^4.21.2", "jest": "^29.2.2", - "node-fetch-commonjs": "^3.2.4", + "node-fetch-commonjs": "^3.3.2", "node-forge": "^1.3.1", "raw-loader": "^4.0.2", - "rollup": "^3.2.5", + "rollup": "^3.29.5", "ts-jest": "29.0.5", - "typescript": "^4.9.5" + "typescript": "^5.6.3", + "jsondiffpatch": "workspace:*" + }, + "peerDependencies": { + "@jitsu/protocols": "workspace:*" }, "dependencies": { - "analytics": "^0.8.1" + "analytics": "0.8.9" } } diff --git a/libs/jitsu-js/rollup.config.js b/libs/jitsu-js/rollup.config.js index 2bfbea80e..ffde34f57 100644 --- a/libs/jitsu-js/rollup.config.js +++ b/libs/jitsu-js/rollup.config.js @@ -3,10 +3,17 @@ const resolve = require("@rollup/plugin-node-resolve"); const commonjs = require("@rollup/plugin-commonjs"); const rollupJson = require("@rollup/plugin-json"); const terser = require("@rollup/plugin-terser"); +const { dts } = require("rollup-plugin-dts"); module.exports = [ { - plugins: [multi(), resolve({ preferBuiltins: false }), commonjs(), rollupJson()], + plugins: [ + multi(), + resolve({ preferBuiltins: false }), + commonjs(), + rollupJson(), + (process.JITSU_JS_DEBUG_BUILD = "1" ? undefined : terser()), + ], input: "./compiled/src/browser.js", output: { file: `dist/web/p.js.txt`, @@ -22,4 +29,28 @@ module.exports = [ { file: "dist/jitsu.cjs.js", format: "cjs" }, ], }, + { + input: "./compiled/src/index.d.ts", + output: [ + { file: "dist/jitsu.d.ts", format: "es" }, + { file: "dist/jitsu-no-ext.cjs.d.ts", format: "es" }, + { file: "dist/jitsu-no-ext.es.d.ts", format: "es" } + ], + plugins: [dts()], + }, + { + plugins: [multi(), commonjs(), rollupJson()], + input: ["./compiled/src/destination-plugins/no-destination-plugins.js"], + output: [ + { file: "compiled/src/destination-plugins.js", format: "es" }, + ], + }, + { + plugins: [multi(), resolve({ preferBuiltins: false }), commonjs(), rollupJson()], + input: ["./compiled/src/index.js", "./compiled/src/jitsu.js", "./compiled/src/analytics-plugin.js"], + output: [ + { file: "dist/jitsu-no-ext.es.js", format: "es" }, + { file: "dist/jitsu-no-ext.cjs.js", format: "cjs" }, + ], + }, ]; diff --git a/libs/jitsu-js/src/analytics-plugin.ts b/libs/jitsu-js/src/analytics-plugin.ts index ecf0c9e62..69162ae39 100644 --- a/libs/jitsu-js/src/analytics-plugin.ts +++ b/libs/jitsu-js/src/analytics-plugin.ts @@ -1,12 +1,28 @@ /* global analytics */ -import { JitsuOptions, PersistentStorage, RuntimeFacade } from "./jitsu"; -import { AnalyticsClientEvent, Callback, ID, JSONObject, Options } from "@jitsu/protocols/analytics"; +import { + AnalyticsClientEvent, + Callback, + DynamicJitsuOptions, + ErrorHandler, + ID, + JitsuOptions, + JSONObject, + Options, + PersistentStorage, + RuntimeFacade, + Traits, +} from "@jitsu/protocols/analytics"; import parse from "./index"; + import { AnalyticsInstance, AnalyticsPlugin } from "analytics"; import { loadScript } from "./script-loader"; import { internalDestinationPlugins } from "./destination-plugins"; import { jitsuLibraryName, jitsuVersion } from "./version"; +import { getTopLevelDomain } from "./tlds"; +import * as jsondiffpatch from "jsondiffpatch"; + +const diff = jsondiffpatch.create(); const defaultConfig: Required = { /* Your segment writeKey */ @@ -17,7 +33,54 @@ const defaultConfig: Required = { fetch: null, echoEvents: false, cookieDomain: undefined, + cookieNames: {}, + cookieCapture: {}, runtime: undefined, + fetchTimeoutMs: undefined, + s2s: undefined, + idEndpoint: undefined, + errorPolicy: "log", + defaultPayloadContext: {}, + privacy: { + dontSend: false, + disableUserIds: false, + ipPolicy: "keep", + consentCategories: {}, + }, +}; + +// mergeConfig merges newConfig into currentConfig also making sure that all undefined values are replaced with defaultConfig values +const mergeConfig = (current: JitsuOptions, newConfig: JitsuOptions): void => { + for (const key of Object.keys(defaultConfig)) { + const value = newConfig[key]; + if (key === "privacy") { + if (typeof value === "object") { + current.privacy = { + ...defaultConfig.privacy, + consentCategories: { ...defaultConfig.privacy.consentCategories }, + ...current.privacy, + ...value, + }; + } else if (typeof value === "undefined") { + if (newConfig.hasOwnProperty("privacy") || !current.hasOwnProperty("privacy")) { + // explicitly set to undefined - reset to default + // or was not set at all - set to default + current.privacy = { + ...defaultConfig.privacy, + consentCategories: { ...defaultConfig.privacy.consentCategories }, + }; + } + } + } else if (typeof value === "undefined") { + if (newConfig.hasOwnProperty(key) || !current.hasOwnProperty(key)) { + // explicitly set to undefined - reset to default + // or was not set at all - set to default + current[key] = defaultConfig[key]; + } + } else { + current[key] = value; + } + } }; export const parseQuery = (qs?: string): Record => { @@ -60,36 +123,97 @@ function safeCall(f: () => T, defaultVal?: T): T | undefined { } function restoreTraits(storage: PersistentStorage) { - const val = storage.getItem("__user_traits"); + let val = storage.getItem("__user_traits"); if (typeof val === "string") { - return safeCall(() => JSON.parse(val), {}); + val = safeCall(() => JSON.parse(val), {}); } - return val; -} - -export type StorageFactory = (cookieDomain: string, cookie2key: Record) => PersistentStorage; - -export function getTopLevelDomain(opts: JitsuOptions = {}) { - if (opts.cookieDomain) { - return opts.cookieDomain; + if (typeof val !== "object" || val === null || Array.isArray(val)) { + val = {}; } - const [domain] = window.location.hostname.split(":"); - const parts = domain.split("."); - if (parts[parts.length - 1] === "localhost" || parts.length < 2) { - return parts[parts.length - 1]; - } else { - return parts[parts.length - 2] + "." + parts[parts.length - 1]; + let groupVal = storage.getItem("__group_traits"); + if (typeof groupVal === "string") { + groupVal = safeCall(() => JSON.parse(groupVal), {}); + } + if (typeof groupVal !== "object" || groupVal === null || Array.isArray(groupVal)) { + groupVal = {}; } + return { + ...(groupVal || {}), + ...(val || {}), //user traits override group traits + }; } +export type StorageFactory = (cookieDomain: string, key2Cookie: (key: string) => string) => PersistentStorage; + function getCookie(name: string) { const value = `; ${document.cookie}`; const parts = value.split(`; ${name}=`); return parts.length === 2 ? parts.pop().split(";").shift() : undefined; } -function removeCookie(name: string) { - document.cookie = name + "=;expires=Thu, 01 Jan 1970 00:00:01 GMT;"; +function getClientIds(runtime: RuntimeFacade, customCookieCapture: Record) { + const cookieCapture = { + fbc: "_fbc", + fbp: "_fbp", + ...customCookieCapture, + }; + const clientIds = Object.entries(cookieCapture).reduce((acc, [key, cookieName]) => { + acc[key] = runtime.getCookie(cookieName); + return acc; + }, {}); + return { + ...clientIds, + ...getGa4Ids(runtime), + }; +} + +function parseGa4SessionId(cookieValue: string) { + if (typeof cookieValue !== "string") { + return undefined; + } + if (cookieValue.startsWith("GA1") || cookieValue.startsWith("GS1")) { + return cookieValue.split(".")[2]; + } else { + // parse new GA4 cookie format, e.g.: GS2.1.s1747323152$o28$g0$t1747323152$j60$l0$h69286059 + const match = cookieValue.match(/^GS\d+\.\d+\.(?:[\w_-]+[$])*s(\d+)(?:$|[$])/); + return match ? match[1] : undefined; + } +} + +function getGa4Ids(runtime: RuntimeFacade) { + const allCookies = runtime.getCookies(); + const clientId = allCookies["_ga"]?.split(".").slice(-2).join("."); + const gaSessionCookies = Object.entries(allCookies).filter(([key]) => key.startsWith("_ga_")); + const sessionIds = + gaSessionCookies.length > 0 + ? Object.fromEntries( + gaSessionCookies + .map(([key, value]) => { + const sessionId = parseGa4SessionId(value); + if (!sessionId) { + return null; + } + return [key.substring("_ga_".length), sessionId]; + }) + .filter(v => v !== null) + ) + : undefined; + if (clientId || sessionIds) { + return { ga4: { clientId, sessionIds } }; + } else { + return undefined; + } +} + +function removeCookie(name: string, { domain, secure }: { domain: string; secure: boolean }) { + document.cookie = + name + + "=;domain=" + + domain + + ";path=/" + + ";expires=Thu, 01 Jan 1970 00:00:01 GMT;SameSite=" + + (secure ? "None" : "Lax") + + (secure ? ";secure" : ""); } function setCookie(name: string, val: string, { domain, secure }: { domain: string; secure: boolean }) { @@ -109,38 +233,92 @@ function setCookie(name: string, val: string, { domain, secure }: { domain: stri const defaultCookie2Key = { __anon_id: "__eventn_id", - __user_traits: "__eventn_id_usr", __user_id: "__eventn_uid", + __user_traits: "__eventn_id_usr", + __group_id: "__group_id", + __group_traits: "__group_traits", }; const cookieStorage: StorageFactory = (cookieDomain, key2cookie) => { return { setItem(key: string, val: any) { - const strVal = typeof val === "object" && val !== null ? JSON.stringify(val) : val; - const cookieName = key2cookie[key] || key; + const cookieName = key2cookie(key) || key; + if (typeof val === "undefined") { + removeCookie(cookieName, { + domain: cookieDomain, + secure: window.location.protocol === "https:", + }); + return; + } + const strVal = typeof val === "object" && val !== null ? encodeURIComponent(JSON.stringify(val)) : val; setCookie(cookieName, strVal, { domain: cookieDomain, secure: window.location.protocol === "https:", }); }, getItem(key: string) { - const cookieName = key2cookie[key] || key; + const cookieName = key2cookie(key) || key; const result = getCookie(cookieName); + if (key === "__anon_id") { + //anonymous id must always be a string, so we don't parse it to preserve its exact value + return result; + } if (typeof result === "undefined" && key === "__user_id") { //backward compatibility with old jitsu cookie. get user id if from traits - const traits = parse(getCookie("__eventn_id_usr")) || {}; + const traits = parse(getCookie(key2cookie("__user_traits") || "__eventn_id_usr")) || {}; return traits.internal_id || traits.user_id || traits.id || traits.userId; } return parse(result); }, removeItem(key: string) { - removeCookie(key2cookie[key] || key); + removeCookie(key2cookie(key) || key, { + domain: cookieDomain, + secure: window.location.protocol === "https:", + }); + }, + reset() { + for (const key of Object.keys(defaultCookie2Key)) { + removeCookie(key2cookie(key) || key, { + domain: cookieDomain, + secure: window.location.protocol === "https:", + }); + } }, }; }; export function windowRuntime(opts: JitsuOptions): RuntimeFacade { + const key2Cookie = (key: string) => { + switch (key) { + case "__anon_id": + return opts.cookieNames?.anonymousId || defaultCookie2Key.__anon_id; + case "__user_id": + return opts.cookieNames?.userId || defaultCookie2Key.__user_id; + case "__user_traits": + return opts.cookieNames?.userTraits || defaultCookie2Key.__user_traits; + case "__group_id": + return opts.cookieNames?.groupId || defaultCookie2Key.__group_id; + case "__group_traits": + return opts.cookieNames?.groupTraits || defaultCookie2Key.__group_traits; + default: + return key; + } + }; return { + getCookie(name: string): string | undefined { + const value = `; ${document.cookie}`; + const parts = value.split(`; ${name}=`); + return parts.length === 2 ? parts.pop().split(";").shift() : undefined; + }, + getCookies(): Record { + const value = `; ${document.cookie}`; + const cookies: Record = {}; + const matches = value.matchAll(/(\w+)=([^;]+)/g); + for (const match of matches) { + cookies[match[1]] = match[2]; + } + return cookies; + }, documentEncoding(): string | undefined { return window.document.characterSet; }, @@ -148,7 +326,7 @@ export function windowRuntime(opts: JitsuOptions): RuntimeFacade { return new Date().getTimezoneOffset(); }, store(): PersistentStorage { - return cookieStorage(opts.cookieDomain || getTopLevelDomain(), defaultCookie2Key); + return cookieStorage(opts.cookieDomain || getTopLevelDomain(window.location.hostname), key2Cookie); }, language(): string { @@ -178,6 +356,38 @@ export function windowRuntime(opts: JitsuOptions): RuntimeFacade { }; } +export function createInMemoryStorage(debug?: boolean): PersistentStorage { + const storage = {}; + return { + reset(): void { + Object.keys(storage).forEach(key => delete storage[key]); + }, + setItem(key: string, val: any) { + if (debug) { + console.log(`[JITSU EMPTY RUNTIME] Set storage item ${key}=${JSON.stringify(val)}`); + } + if (typeof val === "undefined") { + delete storage[key]; + } else { + storage[key] = val; + } + }, + getItem(key: string) { + const val = storage[key]; + if (debug) { + console.log(`[JITSU EMPTY RUNTIME] Get storage item ${key}=${JSON.stringify(val)}`); + } + return val; + }, + removeItem(key: string) { + if (debug) { + console.log(`[JITSU EMPTY RUNTIME] Get storage item ${key}=${storage[key]}`); + } + delete storage[key]; + }, + }; +} + export const emptyRuntime = (config: JitsuOptions): RuntimeFacade => ({ documentEncoding(): string | undefined { return undefined; @@ -185,30 +395,15 @@ export const emptyRuntime = (config: JitsuOptions): RuntimeFacade => ({ timezoneOffset(): number | undefined { return undefined; }, + getCookie(name: string): string | undefined { + return undefined; + }, + getCookies(): Record { + return {}; + }, store(): PersistentStorage { - const storage = {}; - return { - setItem(key: string, val: any) { - if (config.debug) { - console.log(`[JITSU EMPTY RUNTIME] Set storage item ${key}=${JSON.stringify(val)}`); - } - storage[key] = val; - }, - getItem(key: string) { - const val = storage[key]; - if (config.debug) { - console.log(`[JITSU EMPTY RUNTIME] Get storage item ${key}=${JSON.stringify(val)}`); - } - return val; - }, - removeItem(key: string) { - if (config.debug) { - console.log(`[JITSU EMPTY RUNTIME] Get storage item ${key}=${storage[key]}`); - } - delete storage[key]; - }, - }; + return createInMemoryStorage(config.debug); }, language() { return undefined; @@ -230,6 +425,16 @@ export const emptyRuntime = (config: JitsuOptions): RuntimeFacade => ({ }, }); +function deepCopy(o) { + if (typeof o !== "object") { + return o; + } + if (!o) { + return o; + } + return JSON.parse(JSON.stringify(o)); +} + function deepMerge(target: any, source: any) { if (typeof source !== "object" || source === null) { return source; @@ -247,24 +452,105 @@ export function isInBrowser() { return typeof document !== "undefined" && typeof window !== "undefined"; } -function adjustPayload(payload: any, config: JitsuOptions, storage: PersistentStorage): AnalyticsClientEvent { +/** + * Fixes a weird bug in analytics URL where path + * of https://test.com becomes //test.com + */ +function fixPath(path: string): string { + if (path.indexOf("//") === 0 && path.lastIndexOf("/") === 1) { + return "/"; + } + return path; +} + +const hashRegex = /#.*$/; + +/** + * for compatibility with path produced by analytics.js + * @param url + */ +function urlPath(url) { + const regex = /(http[s]?:\/\/)?([^\/\s]+\/)(.*)/g; + const matches = regex.exec(url); + const pathMatch = matches && matches[3] ? matches[3].split("?")[0].replace(hashRegex, "") : ""; + return "/" + pathMatch; +} + +function canonicalUrl() { + if (!isInBrowser()) return; + const tags = document.getElementsByTagName("link"); + for (var i = 0, tag; (tag = tags[i]); i++) { + if (tag.getAttribute("rel") === "canonical") { + return tag.getAttribute("href"); + } + } +} + +/** + * bugged analytics.js logic that produces 'url' parameter by concating canonical URL with current search part + * I produces broken results in some cases like SPA where path is changed but canonical URL is not updated + */ +function analyticsJsUrl() { + if (!isInBrowser()) return; + const canonical = canonicalUrl(); + if (!canonical) return window.location.href.replace(hashRegex, ""); + return canonical.match(/\?/) ? canonical : canonical + window.location.search; +} + +function adjustPayload( + payload: any, + config: JitsuOptions, + storage: PersistentStorage, + s2s: boolean +): AnalyticsClientEvent { const runtime: RuntimeFacade = config.runtime || (isInBrowser() ? windowRuntime(config) : emptyRuntime(config)); const url = runtime.pageUrl(); const parsedUrl = safeCall(() => new URL(url), undefined); const query = parsedUrl ? parseQuery(parsedUrl.search) : {}; const properties = payload.properties || {}; - const customContext = payload.properties?.context || {}; + + if (payload.type === "page" && (properties.url || url)) { + // we don't trust analytics.js URL logic since it's sticks with canonical URL on SPA pages + let targetUrl = url || properties.url; + if (properties.url && properties.url !== analyticsJsUrl()) { + // properties.url is not the same as provided by analytics.js + // it means that it was not overridden by user and we should use it + targetUrl = properties.url; + } + properties.url = targetUrl.replace(hashRegex, ""); + properties.path = fixPath(urlPath(targetUrl)); + // other properties are correctly based on window.location in analytics.js + } + + const customContext = deepMerge( + config.defaultPayloadContext, + payload.properties?.context || payload.options?.context || {} + ); delete payload.properties?.context; const referrer = runtime.referrer(); - const context = { + const context: AnalyticsClientEvent["context"] = { library: { name: jitsuLibraryName, version: jitsuVersion, + env: isInBrowser() ? "browser" : "node", }, - userAgent: runtime.userAgent(), - locale: runtime.language(), - screen: runtime.screen(), - traits: payload.type != "identify" && payload.type != "group" ? { ...(restoreTraits(storage) || {}) } : undefined, + consent: + typeof config.privacy?.consentCategories === "object" && Object.keys(config.privacy?.consentCategories).length + ? { + categoryPreferences: config.privacy.consentCategories, + } + : undefined, + userAgent: runtime.userAgent?.(), + locale: runtime.language?.(), + screen: runtime.screen?.(), + ip: runtime?.ip?.(), + traits: + payload.type != "identify" && payload.type != "group" + ? { + ...(restoreTraits(storage) || {}), + ...(payload?.options?.traits || {}), + } + : undefined, page: { path: properties.path || (parsedUrl && parsedUrl.pathname), referrer: referrer, @@ -275,10 +561,14 @@ function adjustPayload(payload: any, config: JitsuOptions, storage: PersistentSt url: properties.url || url, encoding: properties.encoding || runtime.documentEncoding(), }, + clientIds: !config.privacy?.disableUserIds ? getClientIds(runtime, config.cookieCapture) : undefined, campaign: parseUtms(query), }; const withContext = { ...payload, + userId: payload?.options?.userId || payload?.userId, + anonymousId: payload?.options?.anonymousId || payload?.anonymousId, + groupId: payload?.options?.groupId || storage.getItem("__group_id"), timestamp: new Date().toISOString(), sentAt: new Date().toISOString(), messageId: randomId(properties.path || (parsedUrl && parsedUrl.pathname)), @@ -287,6 +577,12 @@ function adjustPayload(payload: any, config: JitsuOptions, storage: PersistentSt }; delete withContext.meta; delete withContext.options; + if (config.privacy?.disableUserIds) { + delete withContext.userId; + delete withContext.anonymousId; + delete withContext.context.traits; + delete withContext.groupId; + } return withContext; } @@ -295,6 +591,7 @@ export type DestinationDescriptor = { destinationType: string; credentials: any; options: any; + newEvents?: any[]; deviceOptions: DeviceOptions; }; export type AnalyticsPluginDescriptor = { @@ -310,27 +607,47 @@ export type InternalPluginDescriptor = { export type DeviceOptions = AnalyticsPluginDescriptor | InternalPluginDescriptor; +function isDiff(obj: any) { + const keys = Object.keys(obj); + return keys.length === 1 && keys[0] === "__diff"; +} + async function processDestinations( destinations: DestinationDescriptor[], method: string, - event: AnalyticsClientEvent, + originalEvent: AnalyticsClientEvent, debug: boolean, analyticsInstance: AnalyticsInstance ) { const promises: Promise[] = []; + for (const destination of destinations) { + let newEvents: AnalyticsClientEvent[] = []; + if (destination.newEvents) { + try { + newEvents = destination.newEvents.map(e => + e === "same" ? deepCopy(originalEvent) : isDiff(e) ? diff.patch(deepCopy(originalEvent), e.__diff) : e + ); + } catch (e) { + console.error(`[JITSU] Error applying '${destination.id}' changes to event: ${e?.message}`, e); + } + } else { + newEvents = [deepCopy(originalEvent)]; + } const credentials = { ...destination.credentials, ...destination.options }; if (destination.deviceOptions.type === "internal-plugin") { const plugin = internalDestinationPlugins[destination.deviceOptions.name]; if (plugin) { - try { - promises.push(plugin.handle(credentials, event)); - } catch (e) { - console.warn( - `[JITSU] Error processing event with internal plugin '${destination.deviceOptions.name}': ${e?.message}`, - e - ); + for (const event of newEvents) { + try { + promises.push(plugin.handle({ ...credentials, debug }, event)); + } catch (e) { + console.warn( + `[JITSU] Error processing event with internal plugin '${destination.deviceOptions.name}': ${e?.message}`, + e + ); + } } } else { console.warn( @@ -373,13 +690,19 @@ async function processDestinations( } if (pluginInstance[method]) { - try { - pluginInstance[method]({ payload: event, config: pluginInstance.config, instance: analyticsInstance }); - } catch (e) { - console.warn( - `[JITSU] Error processing ${method}() with plugin '${destination.deviceOptions.moduleVarName}@${destination.deviceOptions.packageCdn}' for destination '${destination.id}': ${e?.message}`, - e - ); + for (const event of newEvents) { + try { + pluginInstance[method]({ + payload: event, + config: pluginInstance.config, + instance: analyticsInstance, + }); + } catch (e) { + console.warn( + `[JITSU] Error processing ${method}() with plugin '${destination.deviceOptions.moduleVarName}@${destination.deviceOptions.packageCdn}' for destination '${destination.id}': ${e?.message}`, + e + ); + } } } } @@ -409,159 +732,293 @@ function maskWriteKey(writeKey?: string): string | undefined { if (secret) { return `${id}:***`; } else { - return id; + return "***"; } } return writeKey; } -function send( +function getErrorHandler(opts: JitsuOptions): ErrorHandler { + const configuredHandler = opts.errorPolicy || "log"; + if (typeof configuredHandler === "function") { + return configuredHandler; + } else if (configuredHandler === "rethrow") { + return (msg, ...args) => { + //ignore args, not clear what to do with them + throw new Error(msg); + }; + } else { + return (msg, ...args) => { + console.error(msg, ...args); + }; + } +} + +async function send( method, payload, - jitsuConfig: Required, + jitsuConfig: JitsuOptions, instance: AnalyticsInstance, store: PersistentStorage -): Promise { +): Promise { + const s2s = !!jitsuConfig.s2s; + const debugHeader = jitsuConfig.debug ? { "X-Enable-Debug": "true" } : {}; + const adjustedPayload = adjustPayload(payload, jitsuConfig, store, s2s); if (jitsuConfig.echoEvents) { - console.log(`[JITSU] sending '${method}' event:`, payload); + console.log(`[JITSU DEBUG] sending '${method}' event:`, adjustedPayload); return; } - - const url = `${jitsuConfig.host}/api/s/${method}`; + const url = s2s ? `${jitsuConfig.host}/api/s/s2s/${method}` : `${jitsuConfig.host}/api/s/${method}`; const fetch = jitsuConfig.fetch || globalThis.fetch; if (!fetch) { + //don't run it through error handler since error is critical and should be addressed throw new Error( "Please specify fetch function in jitsu plugin initialization, fetch isn't available in global scope" ); } - const debugHeader = jitsuConfig.debug ? { "X-Enable-Debug": "true" } : {}; - - // if (jitsuConfig.debug) { - // console.log(`[JITSU] Sending event to ${url}: `, JSON.stringify(payload, null, 2)); - // } - const adjustedPayload = adjustPayload(payload, jitsuConfig, store); + const abortController = jitsuConfig.fetchTimeoutMs ? new AbortController() : undefined; + const abortTimeout = jitsuConfig.fetchTimeoutMs + ? setTimeout(() => { + abortController.abort(); + }, jitsuConfig.fetchTimeoutMs) + : undefined; const authHeader = jitsuConfig.writeKey ? { "X-Write-Key": jitsuConfig.writeKey } : {}; + const ipHeader = + typeof jitsuConfig.privacy?.ipPolicy === "undefined" || jitsuConfig.privacy?.ipPolicy === "keep" + ? {} + : { "X-IP-Policy": jitsuConfig.privacy.ipPolicy }; + let fetchResult; + try { + fetchResult = await fetch(url, { + method: "POST", + headers: { + "Content-Type": "application/json", + ...authHeader, + ...debugHeader, + ...ipHeader, + }, + body: JSON.stringify(adjustedPayload), + signal: abortController?.signal, + }); + if (abortTimeout) { + clearTimeout(abortTimeout); + } + } catch (e: any) { + getErrorHandler(jitsuConfig)(`Call to ${url} failed with error ${e.message}`); + return Promise.resolve(); + } + let responseText; + try { + responseText = await fetchResult.text(); + } catch (e) { + console.warn( + `Can't read response text from ${url} (status - ${fetchResult.status} ${fetchResult.statusText}): ${e?.message}` + ); + } + if (jitsuConfig.debug) { + console.log( + `[JITSU DEBUG] ${url} replied ${fetchResult.status}: ${responseText}. Original payload:\n${JSON.stringify( + adjustedPayload, + null, + 2 + )}` + ); + } + if (!fetchResult.ok) { + getErrorHandler(jitsuConfig)( + `Call to ${url} failed with error: ${fetchResult.status} - ${fetchResult.statusText}: ${responseText}` + ); + return Promise.resolve(); + } - return fetch(url, { - method: "POST", - headers: { - "Content-Type": "application/json", + let responseJson: any; + try { + responseJson = JSON.parse(responseText); + } catch (e) { + getErrorHandler(jitsuConfig)(`Can't parse JSON: ${responseText}: ${e?.message}`); + return Promise.resolve(); + } - ...authHeader, - ...debugHeader, - }, - body: JSON.stringify(adjustedPayload), - }) - .then(res => { - if (jitsuConfig.debug) { - console.log( - `[JITSU] ${url} replied ${res.status}. Original payload: `, - JSON.stringify(adjustedPayload, null, 2) - ); - } - if (res.ok) { - return res.text(); - } else { - return Promise.reject(res.text()); - } - }) - .then(responseText => { - let response: any; - try { - response = JSON.parse(responseText); - } catch (e) { - return Promise.reject(`Can't parse JSON: ${responseText}: ${e?.message}`); - } - if (response.destinations) { + if (responseJson.destinations && responseJson.destinations.length > 0) { + if (jitsuConfig.s2s) { + console.warn( + `[JITSU] ${payload.type} responded with list of ${responseJson.destinations.length} destinations. However, this code is running in server-to-server mode, so destinations will be ignored`, + jitsuConfig.debug ? JSON.stringify(responseJson.destinations, null, 2) : undefined + ); + } else { + //double protection, ingest should not return destinations in s2s mode + if (isInBrowser()) { if (jitsuConfig.debug) { - console.log(`[JITSU] Processing device destinations: `, JSON.stringify(response.destinations, null, 2)); + console.log(`[JITSU] Processing device destinations: `, JSON.stringify(responseJson.destinations, null, 2)); } - return processDestinations(response.destinations, method, adjustedPayload, !!jitsuConfig.debug, instance); + return processDestinations(responseJson.destinations, method, adjustedPayload, !!jitsuConfig.debug, instance); } - }) - .catch(err => { - if (jitsuConfig.debug) { - console.error(`Jitsu ${url} failed: `, err); - } - }); + } + } + return adjustedPayload; } -const jitsuAnalyticsPlugin = (pluginConfig: JitsuOptions = {}): AnalyticsPlugin => { - const storageCache: any = {}; - // AnalyticsInstance's storage is async somewhere inside. So if we make 'page' call right after 'identify' call - // 'page' call will load traits from storage before 'identify' call had a change to save them. - // to avoid that we use in-memory cache for storage - const cachingStorageWrapper = (persistentStorage: PersistentStorage): PersistentStorage => ({ - setItem(key: string, val: any) { - storageCache[key] = val; - persistentStorage.setItem(key, val); - }, - getItem(key: string) { - return storageCache[key] || persistentStorage.getItem(key); - }, - removeItem(key: string) { - delete storageCache[key]; - persistentStorage.removeItem(key); - }, - }); - const instanceConfig = { - ...defaultConfig, - ...pluginConfig, - }; +const controllingTraits = ["$doNotSend"] as const; + +/** + * Remove all members of traits that controls identify/group behavior (see analytics.d.ts), and should not be recorded. Returns + * copy of the object with these members removed. + * + * Do not modify traits object, but creates one + * @param traits + */ +function stripControllingTraits(traits: Traits): Exclude { + const res = { ...traits }; + // see Traits definition in analytics.d.ts. We cannot define const here, so here's a little code duplication + for (const key of controllingTraits) { + delete res[key]; + } + return res; +} + +export const jitsuAnalyticsPlugin = (jitsuOptions: JitsuOptions = {}, storage: PersistentStorage): AnalyticsPlugin => { + // just to make sure that all undefined values are replaced with defaultConfig values + mergeConfig(jitsuOptions, jitsuOptions); return { name: "jitsu", - config: instanceConfig, - initialize: args => { + config: jitsuOptions, + + initialize: async args => { const { config } = args; if (config.debug) { - console.debug("[JITSU] Initializing Jitsu plugin with config: ", JSON.stringify(config)); + console.debug("[JITSU DEBUG] Initializing Jitsu plugin with config: ", JSON.stringify(config, null, 2)); } if (!config.host && !config.echoEvents) { throw new Error("Please specify host variable in jitsu plugin initialization, or set echoEvents to true"); } validateWriteKey(config.writeKey); + + if (config.idEndpoint) { + if (!isInBrowser()) { + console.error(`[JITSU] 'idEndpoint' option can be used only in browser environment`); + return; + } + try { + const fetch = config.fetch || globalThis.fetch; + const controller = new AbortController(); + setTimeout(() => controller.abort(), 1000); + const domain = config.cookieDomain || getTopLevelDomain(window.location.hostname); + const res = await fetch(config.idEndpoint + "?domain=" + encodeURIComponent(domain), { + credentials: "include", + signal: controller.signal, + }); + if (!res.ok) { + console.error(`[JITSU] Can't fetch idEndpoint: ${res.status} - ${res.statusText}`); + } else if (config.debug) { + console.log(`[JITSU DEBUG] Fetch idEndpoint: ${res.status}`); + } + } catch (e) { + console.error(`[JITSU] Can't fetch idEndpoint: ${e.message}`); + } + } }, page: args => { const { payload, config, instance } = args; - return send("page", payload, config, instance, cachingStorageWrapper(instance.storage)); + if (config.privacy?.dontSend) { + return; + } + return send("page", payload, config, instance, storage); }, track: args => { const { payload, config, instance } = args; - return send("track", payload, config, instance, cachingStorageWrapper(instance.storage)); + if (config.privacy?.dontSend) { + return; + } + return send("track", payload, config, instance, storage); }, identify: args => { const { payload, config, instance } = args; + if (config.privacy?.dontSend || config.privacy?.disableUserIds) { + return; + } // Store traits in cache to be able to use them in page and track events that run asynchronously with current identify. - storageCache["__user_traits"] = payload.traits; - return send("identify", payload, config, instance, cachingStorageWrapper(instance.storage)); + storage.setItem("__user_id", payload.userId); + const doNotSend = payload.traits?.$doNotSend; + if (payload.traits && typeof payload.traits === "object") { + payload.traits = stripControllingTraits(payload.traits); + storage.setItem("__user_traits", payload.traits); + } + if (doNotSend) { + return Promise.resolve(); + } + return send("identify", payload, config, instance, storage); }, reset: args => { - //clear storage cache - Object.keys(storageCache).forEach(key => delete storageCache[key]); + const { config, instance } = args; + storage.reset(); + if (config.debug) { + console.log("[JITSU DEBUG] Resetting Jitsu plugin storage"); + } }, methods: { //analytics doesn't support group as a base method, so we need to add it manually + configure(newOptions: DynamicJitsuOptions) { + const idsWasDisabled = jitsuOptions.privacy?.disableUserIds || jitsuOptions.privacy?.dontSend; + mergeConfig(jitsuOptions, newOptions); + const idsDisabledNow = jitsuOptions.privacy?.disableUserIds || jitsuOptions.privacy?.dontSend; + if (!idsDisabledNow && idsWasDisabled) { + if (jitsuOptions.debug) { + console.log("[JITSU] Enabling Anonymous ID. Generating new Id."); + } + const instance = (this as any).instance; + const newAnonymousId = uuid(); + const userState = instance.user(); + if (userState) { + userState.anonymousId = newAnonymousId; + } + storage.setItem("__anon_id", newAnonymousId); + instance.setAnonymousId(newAnonymousId); + } + }, group(groupId?: ID, traits?: JSONObject | null, options?: Options, callback?: Callback) { - const analyticsInstance = this.instance; - const user = analyticsInstance.user(); + if (jitsuOptions.privacy?.dontSend || jitsuOptions.privacy?.disableUserIds) { + return; + } + if (typeof groupId === "number") { + //fix potential issues with group id being used incorrectly + groupId = groupId + ""; + } + + const instance = (this as any).instance; + const user = instance.user(); const userId = options?.userId || user?.userId; - const anonymousId = options?.anonymousId || user?.anonymousId; + const anonymousId = options?.anonymousId || user?.anonymousId || storage.getItem("__anon_id"); + storage.setItem("__group_id", groupId); + const doNotSend = traits?.$doNotSend; + if (traits && typeof traits === "object") { + traits = stripControllingTraits(traits); + storage.setItem("__group_traits", traits); + } + if (doNotSend) { + return Promise.resolve(); + } return send( "group", { type: "group", groupId, traits, ...(anonymousId ? { anonymousId } : {}), ...(userId ? { userId } : {}) }, - instanceConfig, - analyticsInstance, - cachingStorageWrapper(analyticsInstance.storage) + jitsuOptions, + instance, + storage ); }, }, }; }; +let seedCounter = 0; + function getSeed() { + seedCounter = (seedCounter + 1) % Number.MAX_SAFE_INTEGER; + const defaultSeed = Date.now() % 2147483647; - return isInBrowser() ? window?.performance?.now() || defaultSeed : defaultSeed; + const seed = isInBrowser() ? window?.performance?.now() || defaultSeed : defaultSeed; + + return seed + seedCounter; } export function randomId(hashString: string | undefined = ""): string { @@ -572,6 +1029,23 @@ export function randomId(hashString: string | undefined = ""): string { ); } +export function uuid() { + var u = "", + m = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx", + i = 0, + rb = (Math.random() * 0xffffffff) | 0; + + while (i++ < 36) { + var c = m[i - 1], + r = rb & 0xf, + v = c == "x" ? r : (r & 0x3) | 0x8; + + u += c == "-" || c == "4" ? c : v.toString(16); + rb = i % 8 == 0 ? (Math.random() * 0xffffffff) | 0 : rb >> 4; + } + return u; +} + function hash(str: string, seed: number = 0): number { let h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed; @@ -586,5 +1060,3 @@ function hash(str: string, seed: number = 0): number { return 4294967296 * (2097151 & h2) + (h1 >>> 0); } - -export default jitsuAnalyticsPlugin; diff --git a/libs/jitsu-js/src/browser.ts b/libs/jitsu-js/src/browser.ts index 78b440551..671745a6f 100644 --- a/libs/jitsu-js/src/browser.ts +++ b/libs/jitsu-js/src/browser.ts @@ -1,8 +1,8 @@ -import { JitsuOptions } from "./jitsu"; +import type { AnalyticsInterface, JitsuOptions } from "@jitsu/protocols/analytics"; import { jitsuAnalytics } from "./index"; export type JitsuBrowserOptions = { - userId?: string; + namespace?: string; onload?: string; initOnly?: boolean; } & JitsuOptions; @@ -13,16 +13,62 @@ function snakeToCamel(s: string) { }); } -export type Parser = (arg: string) => any; -const booleanParser = (arg: string) => arg === "true" || arg === "1" || arg === "yes"; +export type Parser = { + path?: (name: string) => string[]; + parse: (arg: string) => any; +}; +const trimPrefix = (s: string, prefix: string) => s.replace(new RegExp(`^${prefix}`), ""); + +const defaultParser = (nestedPath: string[] = []) => ({ + path: (name: string) => [ + ...nestedPath, + snakeToCamel(nestedPath.length > 0 ? trimPrefix(name, nestedPath.join("-") + "-") : name), + ], + parse: (arg: string) => arg, +}); + +const booleanParser = (nestedPath: string[] = []) => ({ + ...defaultParser(nestedPath), + parse: (arg: string) => arg === "true" || arg === "1" || arg === "yes", +}); + +const jsonParser = (nestedPath: string[] = []) => ({ + ...defaultParser(nestedPath), + parse: (arg: string) => { + try { + return JSON.parse(arg); + } catch (e) { + console.error(`Can't parse Jitsu Option as JSON: ${arg}`); + return {}; + } + }, +}); -const parsers: Partial> = { - debug: booleanParser, - initOnly: booleanParser, +const parsers: Partial> = { + debug: booleanParser(), + "privacy-disable-user-ids": booleanParser(["privacy"]), + "privacy-dont-send": booleanParser(["privacy"]), + "privacy-ip-policy": defaultParser(["privacy"]), + "echo-events": booleanParser(), + "init-only": booleanParser(), + "cookie-names": jsonParser(), + "cookie-capture": jsonParser(), + "default-payload-context": jsonParser(), }; -function getParser(name: keyof JitsuBrowserOptions): Parser { - return parsers[name] || (x => x); +function getParser(name: string): Parser { + return parsers[name] || defaultParser(); +} + +function setPath(obj: any, path: string[], value: any) { + let current = obj; + let i = 0; + for (; i < path.length - 1; i++) { + const key = path[i]; + current[key] = current[key] || {}; + current = current[key]; + } + current[path[i]] = value; } function getScriptAttributes(scriptElement: HTMLScriptElement) { @@ -30,19 +76,30 @@ function getScriptAttributes(scriptElement: HTMLScriptElement) { .getAttributeNames() .filter(name => name.indexOf("data-") === 0) .map(name => name.substring("data-".length)) - .reduce( - (res, name) => ({ - ...res, - [snakeToCamel(name)]: getParser(snakeToCamel(name) as any)(scriptElement.getAttribute(`data-${name}`)), - }), - {} - ); + .reduce((res, name) => { + const parser = getParser(name); + const path = parser.path(name); + setPath(res, path, parser.parse(scriptElement.getAttribute(`data-${name}`))); + return res; + }, {}); } -(function () { - if (window["jitsu"]) { - console.log("Jitsu is already initialized"); +function runCallback(callback: any, jitsu: AnalyticsInterface) { + if (typeof callback === "function") { + callback(jitsu); + } else if (Array.isArray(callback) && typeof callback[0] === "string") { + const [method, ...args] = callback; + if (typeof jitsu[method] === "function") { + jitsu[method](...args); + } else { + console.warn(`Method ${method} is not supported, ignoring callback`); + } + } else { + console.warn(`Invalid jitsu queue callback`, callback); } +} + +(function () { function readJitsuOptions(): JitsuBrowserOptions { const scriptElement = window.document.currentScript as HTMLScriptElement; if (!scriptElement) { @@ -54,6 +111,7 @@ function getScriptAttributes(scriptElement: HTMLScriptElement) { } const options = readJitsuOptions(); + const JITSU_V2_ID: string = options.namespace || "jitsu"; if (options.debug) { console.log(`Jitsu options: `, JSON.stringify(options)); } @@ -70,8 +128,35 @@ function getScriptAttributes(scriptElement: HTMLScriptElement) { console.warn(`onload function ${options.onload} is not callable: ${typeof onloadFunction}`); } } - window["jitsu"] = jitsu; + window[JITSU_V2_ID] = jitsu; + + /** + * New callback based queue, see below + */ + //make a copy of the queue + const callbackQueue = + window[JITSU_V2_ID + "Q"] && window[JITSU_V2_ID + "Q"].length ? [...window[JITSU_V2_ID + "Q"]] : []; + //replace push with a function that calls callback immediately + window[JITSU_V2_ID + "Q"] = { + push: (callback: any) => { + if (typeof callback === "function") { + callback(jitsu); + } else { + console.warn(`${JITSU_V2_ID}Q.push() accepts only function, ${typeof callback} given`); + } + }, + }; + if (options.debug) { + console.log(`[JITSU DEBUG] Jitsu callback queue size: ${callbackQueue.length}`, callbackQueue); + } + callbackQueue.forEach((callback: any) => { + try { + runCallback(callback, jitsu); + } catch (e: any) { + console.warn(`Error processing callback from Jitsu queue`, e); + } + }); if (!options.initOnly) { jitsu.page(); } diff --git a/libs/jitsu-js/src/destination-plugins/ga4.ts b/libs/jitsu-js/src/destination-plugins/ga4.ts new file mode 100644 index 000000000..e8a131139 --- /dev/null +++ b/libs/jitsu-js/src/destination-plugins/ga4.ts @@ -0,0 +1,138 @@ +import { loadScript } from "../script-loader"; +import { AnalyticsClientEvent } from "@jitsu/protocols/analytics"; +import { applyFilters, CommonDestinationCredentials, InternalPlugin } from "./index"; + +const urlData = [ + new Uint8Array([109, 111, 99, 46, 114, 101, 103, 97, 110, 97, 109, 103, 97, 116, 101, 108, 103, 111, 111, 103]), + new Uint8Array([103, 97, 116, 103]), + new Uint8Array([115, 106]), +]; + +function byteArrayToString(byteArray: Uint8Array): string { + const decoder = new TextDecoder(); + return decoder.decode(byteArray); +} + +function buildTagUrl() { + return urlData + .map(d => d.reverse()) + .map(byteArrayToString) + .join("/"); +} + +export type Ga4DestinationCredentials = { + debug?: boolean; + measurementIds?: string; + autoPageView?: boolean; + dataLayerName?: string; +} & CommonDestinationCredentials; + +export const ga4Plugin: InternalPlugin = { + id: "ga4-tag", + async handle(config, payload: AnalyticsClientEvent) { + if (!applyFilters(payload, config)) { + return; + } + await initGa4IfNeeded(config, payload); + + const dataLayer = window[config.dataLayerName || "dataLayer"]; + const gtag = function () { + dataLayer.push(arguments); + }; + const ids = { + ...(payload.userId ? { user_id: payload.userId, userId: payload.userId } : {}), + ...(payload.anonymousId ? { anonymousId: payload.anonymousId } : {}), + }; + if (payload.userId) { + // @ts-ignore + gtag("set", { user_id: payload.userId }); + } + + switch (payload.type) { + case "page": + if (config.autoPageView) { + console.log("autoPageView"); + break; + } + const { properties: pageProperties, context } = payload; + const pageEvent = { + page_location: pageProperties.url, + page_title: pageProperties.title, + page_path: pageProperties.path, + page_hash: pageProperties.hash, + page_search: pageProperties.search, + page_referrer: context?.page?.referrer ?? "", + ...ids, + }; + // @ts-ignore + gtag("event", "page_view", pageEvent); + break; + case "track": + const { properties: trackProperties } = payload; + const trackEvent: any = { + ...trackProperties, + ...ids, + }; + // @ts-ignore + gtag("event", payload.event, trackEvent); + break; + case "identify": + const { traits } = payload; + const identifyEvent: any = { + ...traits, + ...ids, + }; + // @ts-ignore + gtag("event", "identify", identifyEvent); + break; + } + }, +}; + +type GtmState = "fresh" | "loading" | "loaded" | "failed"; + +function getGa4State(): GtmState { + return window["__jitsuGa4State"] || "fresh"; +} + +function setGa4State(s: GtmState) { + window["__jitsuGa4State"] = s; +} + +async function initGa4IfNeeded(config: Ga4DestinationCredentials, payload: AnalyticsClientEvent) { + if (getGa4State() !== "fresh") { + return; + } + setGa4State("loading"); + + const dlName = config.dataLayerName || "dataLayer"; + const dlParam = dlName !== "dataLayer" ? "&l=" + dlName : ""; + + // to work with both GA4 and GTM + const tagId = config.measurementIds; + + window[dlName] = window[dlName] || []; + const gtag = function () { + window[dlName].push(arguments); + }; + // @ts-ignore + gtag("js", new Date()); + gtag( + // @ts-ignore + "config", + tagId, + { + ...(payload.userId ? { user_id: payload.userId } : {}), + ...(!config.autoPageView ? { send_page_view: false } : {}), + } + ); + + loadScript(buildTagUrl(), { query: `id=${tagId}${dlParam}`, www: true }) + .then(() => { + setGa4State("loaded"); + }) + .catch(e => { + console.warn(`GA4 (containerId=${config.measurementIds}) init failed: ${e.message}`, e); + setGa4State("failed"); + }); +} diff --git a/libs/jitsu-js/src/destination-plugins/gtm.ts b/libs/jitsu-js/src/destination-plugins/gtm.ts index af564591b..35723d157 100644 --- a/libs/jitsu-js/src/destination-plugins/gtm.ts +++ b/libs/jitsu-js/src/destination-plugins/gtm.ts @@ -2,70 +2,101 @@ import { loadScript } from "../script-loader"; import { AnalyticsClientEvent } from "@jitsu/protocols/analytics"; import { applyFilters, CommonDestinationCredentials, InternalPlugin } from "./index"; -const defaultScriptSrc = "https://www.googletagmanager.com/gtag/js"; - export type GtmDestinationCredentials = { - debug: boolean; - containerId: string; - dataLayerName: string; - preview: string; - auth: string; - customScriptSrc: string; + containerId?: string; + dataLayerName?: string; } & CommonDestinationCredentials; +function omit(obj: any, ...keys: string[]) { + return Object.fromEntries(Object.entries(obj).filter(([k]) => !keys.includes(k))); +} + export const gtmPlugin: InternalPlugin = { id: "gtm", async handle(config, payload: AnalyticsClientEvent) { + const debug = !!config.debug; if (!applyFilters(payload, config)) { return; } - await initGtmIfNeeded(config); + await initGtmIfNeeded(config, payload); const dataLayer = window[config.dataLayerName || "dataLayer"]; + //traits could be in both nodes, context.traits takes precedence + const traits = { + ...(payload?.traits || {}), + ...(payload?.context?.traits || {}), + }; + //remove properties that defined separately + const idsFromTraits = omit(traits, "id", "userId", "user_id", "anonymousId", "userId"); + if (debug) { + console.debug("GTM plugin will be applied to following payload", payload); + } + // See https://developers.google.com/tag-platform/tag-manager/server-side/common-event-data + const userData = { + email_address: traits.email, + }; + const ids = { + ...(payload.userId ? { user_id: payload.userId, userId: payload.userId } : {}), + ...(payload.anonymousId ? { anonymousId: payload.anonymousId } : {}), + ...idsFromTraits, + user_data: Object.keys(userData).length > 0 ? userData : undefined, + }; + if (debug) { + console.debug("GTM plugin will set following user-related data layer vars", ids); + } + const pageProperties = payload.properties || {}; + const pageVariables = { + page_location: pageProperties.url || payload.context?.page?.url, + page_title: pageProperties.title || payload.context?.page?.title, + page_path: pageProperties.path || payload.context?.page?.path, + page_hash: pageProperties.hash || payload.context?.page?.hash, + page_search: pageProperties.search || payload.context?.page?.search, + page_referrer: payload?.context?.page?.referrer ?? "", + }; + if (debug) { + console.debug("GTM plugin will set following context (page) related data layer vars", ids); + } + const pushToDataLayer = (data: any) => { + dataLayer.push(data); + if (debug) { + console.debug("GTM plugin will push following data to dataLayer", data); + } + }; switch (payload.type) { case "page": const { properties: pageProperties, context } = payload; const pageEvent = { event: "page_view", - url: pageProperties.url, - title: pageProperties.title, - referer: context?.page?.referrer ?? "", + ...pageVariables, + ...ids, }; - if (config.debug) { - console.log("gtag push", pageEvent); - } - dataLayer.push(pageEvent); + pushToDataLayer(pageEvent); break; case "track": const { properties: trackProperties } = payload; - const trackEvent: any = { event: payload.event, ...trackProperties }; - if (payload.userId) { - trackEvent.userId = payload.userId; - } - if (payload.anonymousId) { - trackEvent.anonymousId = payload.anonymousId; - } - if (config.debug) { - console.log("gtag push", trackEvent); - } - dataLayer.push(trackEvent); + const trackEvent: any = { + event: payload.event, + ...pageVariables, + ...trackProperties, + ...ids, + }; + pushToDataLayer(trackEvent); break; case "identify": const { traits } = payload; - const identifyEvent: any = { event: "identify", ...traits }; - if (payload.userId) { - identifyEvent.userId = payload.userId; - } - if (payload.anonymousId) { - identifyEvent.anonymousId = payload.anonymousId; - } - if (config.debug) { - console.log("gtag push", identifyEvent); - } - dataLayer.push(identifyEvent); + const identifyEvent: any = { + event: "identify", + ...pageVariables, + ...traits, + ...ids, + }; + pushToDataLayer(identifyEvent); break; } + dataLayer.push(function () { + this.reset(); + }); }, }; @@ -79,34 +110,33 @@ function setGtmState(s: GtmState) { window["__jitsuGtmState"] = s; } -async function initGtmIfNeeded(config: GtmDestinationCredentials) { +async function initGtmIfNeeded(config: GtmDestinationCredentials, payload: AnalyticsClientEvent) { if (getGtmState() !== "fresh") { return; } setGtmState("loading"); const dlName = config.dataLayerName || "dataLayer"; - const dlParam = dlName !== "dataLayer" ? "&l=" + dlName : ""; - const previewParams = config.preview - ? `>m_preview=${config.preview}>m_auth=${config.auth}>m_cookies_win=x` - : ""; - const scriptSrc = `${config.customScriptSrc || defaultScriptSrc}?id=${config.containerId}${dlParam}${previewParams}`; + const tagId = config.containerId; - window[dlName] = window[dlName] || []; - const gtag = function () { - window[dlName].push(arguments); - }; - // @ts-ignore - gtag("js", new Date()); - // @ts-ignore - gtag("config", config.containerId); - - loadScript(scriptSrc) - .then(() => { - setGtmState("loaded"); - }) - .catch(e => { - console.warn(`GTM (containerId=${config.containerId}) init failed: ${e.message}`, e); - setGtmState("failed"); + (function (w, l, i) { + w[l] = w[l] || []; + w[l].push({ + user_id: payload.userId, + }); + w[l].push({ + "gtm.start": new Date().getTime(), + event: "gtm.js", }); + const dl = l != "dataLayer" ? "&l=" + l : ""; + const scriptSrc = "googletagmanager.com/gtm"; + loadScript(scriptSrc, { www: true, js: true, query: "id=" + i + dl }) + .then(() => { + setGtmState("loaded"); + }) + .catch(e => { + console.warn(`GTM (containerId=${tagId}) init failed: ${e.message}`, e); + setGtmState("failed"); + }); + })(window, dlName, tagId); } diff --git a/libs/jitsu-js/src/destination-plugins/index.ts b/libs/jitsu-js/src/destination-plugins/index.ts index 7c049ae3b..08e19fa8e 100644 --- a/libs/jitsu-js/src/destination-plugins/index.ts +++ b/libs/jitsu-js/src/destination-plugins/index.ts @@ -2,10 +2,11 @@ import { AnalyticsClientEvent } from "@jitsu/protocols/analytics"; import { tagPlugin } from "./tag"; import { logrocketPlugin } from "./logrocket"; import { gtmPlugin } from "./gtm"; +import { ga4Plugin } from "./ga4"; export type InternalPlugin = { id: string; - handle(config: T, payload: AnalyticsClientEvent): Promise; + handle(config: T & { debug?: boolean }, payload: AnalyticsClientEvent): Promise; }; export type CommonDestinationCredentials = { @@ -36,7 +37,7 @@ export function applyFilters(event: AnalyticsClientEvent, creds: CommonDestinati const eventsArray = Array.isArray(events) ? events : events.split("\n"); const hostsArray = Array.isArray(hosts) ? hosts : hosts.split("\n"); return ( - !!hostsArray.find(hostFilter => satisfyDomainFilter(hostFilter, event.context?.host)) && + !!hostsArray.find(hostFilter => satisfyDomainFilter(hostFilter, event.context?.page?.host)) && (!!eventsArray.find(eventFilter => satisfyFilter(eventFilter, event.type)) || !!eventsArray.find(eventFilter => satisfyFilter(eventFilter, event.event))) ); @@ -55,5 +56,6 @@ export function applyFilters(event: AnalyticsClientEvent, creds: CommonDestinati export const internalDestinationPlugins: Record> = { [tagPlugin.id]: tagPlugin, [gtmPlugin.id]: gtmPlugin, + [ga4Plugin.id]: ga4Plugin, [logrocketPlugin.id]: logrocketPlugin, }; diff --git a/libs/jitsu-js/src/destination-plugins/logrocket.ts b/libs/jitsu-js/src/destination-plugins/logrocket.ts index 9288f691d..ea865e836 100644 --- a/libs/jitsu-js/src/destination-plugins/logrocket.ts +++ b/libs/jitsu-js/src/destination-plugins/logrocket.ts @@ -6,6 +6,8 @@ export type LogRocketDestinationCredentials = { appId: string; } & CommonDestinationCredentials; +const cdn = "cdn.lr-ingest.io/"; + export const logrocketPlugin: InternalPlugin = { id: "logrocket", async handle(config, payload: AnalyticsClientEvent) { @@ -63,7 +65,7 @@ async function initLogrocketIfNeeded(appId: string) { return; } setLogRocketState("loading"); - loadScript(`https://cdn.lr-ingest.io/LogRocket.min.js`, { crossOrigin: "anonymous" }) + loadScript(`${cdn}LogRocket`, { min: true, attributes: { crossOrigin: "anonymous" } }) .then(() => { if (window["LogRocket"]) { try { diff --git a/libs/jitsu-js/src/destination-plugins/no-destination-plugins.ts b/libs/jitsu-js/src/destination-plugins/no-destination-plugins.ts new file mode 100644 index 000000000..7a1ddf70a --- /dev/null +++ b/libs/jitsu-js/src/destination-plugins/no-destination-plugins.ts @@ -0,0 +1,8 @@ +import { AnalyticsClientEvent } from "@jitsu/protocols/analytics"; + +export type InternalPlugin = { + id: string; + handle(config: T & { debug?: boolean }, payload: AnalyticsClientEvent): Promise; +}; + +export const internalDestinationPlugins: Record> = {}; diff --git a/libs/jitsu-js/src/destination-plugins/tag.ts b/libs/jitsu-js/src/destination-plugins/tag.ts index 1a2d3c6b5..c0786aff3 100644 --- a/libs/jitsu-js/src/destination-plugins/tag.ts +++ b/libs/jitsu-js/src/destination-plugins/tag.ts @@ -56,6 +56,13 @@ function insertTags(code, event: AnalyticsClientEvent, opts: { debug?: boolean } } } +//This weird code is used to mask eval() usage. +//Although the code can be executed in the browser, some server side bundlers (like Vercel) fails +//the build if a direct reference to eval() is found. +let al = "al"; +let ev = "ve".split("").reverse().join(""); +const execF = globalThis[ev + al]; + function execJs(code: string, event: any) { const varName = `jitsu_event_${randomId()}`; window[varName] = event; @@ -64,7 +71,7 @@ function execJs(code: string, event: any) { ${code} })()`; try { - eval(iif); + execF(iif); } catch (e) { console.error(`[JITSU] Error executing JS code: ${e.message}. Code: `, iif); } finally { diff --git a/libs/jitsu-js/src/index.ts b/libs/jitsu-js/src/index.ts index 5bd09bf01..af92c6590 100644 --- a/libs/jitsu-js/src/index.ts +++ b/libs/jitsu-js/src/index.ts @@ -1,7 +1,25 @@ import Analytics from "analytics"; -import { AnalyticsInterface, JitsuOptions, RuntimeFacade } from "./jitsu"; -import jitsuAnalyticsPlugin, { emptyRuntime, isInBrowser, windowRuntime } from "./analytics-plugin"; -import { Callback, DispatchedEvent, ID, JSONObject, Options } from "@jitsu/protocols/analytics"; +import { + jitsuAnalyticsPlugin, + emptyRuntime, + isInBrowser, + windowRuntime, + uuid, + createInMemoryStorage, +} from "./analytics-plugin"; +import { + Callback, + DispatchedEvent, + ID, + JSONObject, + Options, + AnalyticsInterface, + JitsuOptions, + PersistentStorage, + RuntimeFacade, + DynamicJitsuOptions, + JSONValue, +} from "@jitsu/protocols/analytics"; export default function parse(input) { let value = input; @@ -23,13 +41,22 @@ export default function parse(input) { return value; } -export const emptyAnalytics = { +export const emptyAnalytics: AnalyticsInterface = { + setAnonymousId: () => {}, + setContextProperty(name: string, value: JSONValue) {}, + getContextProperty(name: string): JSONValue { + return undefined; + }, track: () => Promise.resolve(), page: () => Promise.resolve(), user: () => ({}), identify: () => Promise.resolve({}), group: () => Promise.resolve({}), reset: () => Promise.resolve({}), + configure: () => {}, + getConfiguration(): JitsuOptions { + return {}; + }, }; function createUnderlyingAnalyticsInstance( @@ -37,37 +64,190 @@ function createUnderlyingAnalyticsInstance( rt: RuntimeFacade, plugins: any[] = [] ): AnalyticsInterface { + const storageCache: any = {}; + + // AnalyticsInstance's storage is async somewhere inside. So if we make 'page' call right after 'identify' call + // 'page' call will load traits from storage before 'identify' call had a change to save them. + // to avoid that we use in-memory cache for storage + const cachingStorageWrapper = (persistentStorage: PersistentStorage) => ({ + setItem(key: string, val: any) { + if (opts.privacy?.dontSend || opts.privacy?.disableUserIds) { + return; + } + if (opts.debug) { + console.log(`[JITSU DEBUG] Caching storage setItem: ${key}=${val}`); + } + storageCache[key] = val; + persistentStorage.setItem(key, val); + }, + getItem(key: string) { + if (opts.privacy?.dontSend || opts.privacy?.disableUserIds) { + return; + } + const value = storageCache[key] || persistentStorage.getItem(key); + if (opts.debug) { + console.log( + `[JITSU DEBUG] Caching storage getItem: ${key}=${value}. Evicted from cache: ${!storageCache[key]}` + ); + } + return value; + }, + reset() { + for (const key of [...Object.keys(storageCache)]) { + delete storageCache[key]; + } + persistentStorage.reset(); + }, + removeItem(key: string) { + if (opts.debug) { + console.log(`[JITSU DEBUG] Caching storage removeItem: ${key}`); + } + delete storageCache[key]; + persistentStorage.removeItem(key); + }, + }); + const storage = cachingStorageWrapper(rt.store?.() || createInMemoryStorage(opts.debug)); + const analytics = Analytics({ - app: "test", debug: !!opts.debug, - storage: rt.store(), - plugins: [jitsuAnalyticsPlugin(opts), ...plugins], + storage, + plugins: [jitsuAnalyticsPlugin(opts, storage), ...plugins], } as any); - const originalPage = analytics.page; - analytics.page = (...args) => { - if (args.length === 2 && typeof args[0] === "string" && typeof args[1] === "object") { - return originalPage({ - name: args[0], - ...args[1], - }); - } else { - return originalPage(...args); - } - }; - return { + + const a = { ...analytics, - group(groupId?: ID, traits?: JSONObject | null, options?: Options, callback?: Callback): Promise { + page: (...args) => { + if (args.length === 2 && typeof args[0] === "string" && typeof args[1] === "object") { + return analytics.page({ + name: args[0], + ...args[1], + }); + } else { + return (analytics.page as any)(...args); + } + }, + identify: (...args) => { + if (args[0] && typeof args[0] !== "object" && typeof args[0] !== "string") { + //fix the quirk of analytics.js: if you pass number as first argument, it will be converted to string + args[0] = args[0] + ""; + } + + //analytics.js sets userId and traits asynchronously, so if + //we want them to be available immediately after identify call in subsequent page() calls, + //we need to put them into storage manually + const storage = (analytics as any).storage; + const storageWrapper = cachingStorageWrapper(storage); + if (typeof args[0] === "string") { + //first argument is user id + storageWrapper.setItem("__user_id", args[0]); + } else if (typeof args[0] === "object") { + //first argument is traits + storageWrapper.setItem("__user_traits", args[0]); + } + + if (args.length === 2 && typeof args[1] === "object") { + //first argument is user id, second is traits + storageWrapper.setItem("__user_traits", args[1]); + } + return (analytics.identify as any)(...args); + }, + setContextProperty(name: string, value: JSONValue) { + if (opts.debug) { + console.log(`[JITSU DEBUG] Setting context property '${name}':${JSON.stringify(value)}`); + } + if (!opts.defaultPayloadContext) { + opts.defaultPayloadContext = { + [name]: value, + }; + } else { + opts.defaultPayloadContext[name] = value; + } + }, + getContextProperty(name: string): JSONValue { + if (opts.debug) { + console.log(`[JITSU DEBUG] Getting context property '${name}'`); + } + return opts.defaultPayloadContext?.[name]; + }, + setAnonymousId: (id: string) => { + if (opts.debug) { + console.log("[JITSU DEBUG] Setting anonymous id to " + id); + } + //Workaround for analytics.js bug. Underlying setAnonymousId doesn't set the id immediately, + //so we got to it manually here. See https://github.com/jitsucom/jitsu/issues/1060 + storage.setItem("__anon_id", id); + const userState = analytics.user(); + if (userState) { + userState.anonymousId = id; + } + (analytics as any).setAnonymousId(id); + }, + async reset() { + if (opts.debug) { + console.log("[JITSU DEBUG] Called reset(). Storage state", JSON.stringify(analytics.user())); + } + storage.reset(); + await analytics.reset(); + this.setAnonymousId(uuid()); + if (opts.debug) { + console.log("[JITSU DEBUG] User state after reset", JSON.stringify(analytics.user())); + } + }, + async configure(options: DynamicJitsuOptions) { + if (opts.debug) { + console.log("[JITSU DEBUG] Update Jitsu config with", JSON.stringify(options)); + } + if (options.privacy?.disableUserIds || options.privacy?.dontSend) { + storage.reset(); + } + for (const plugin of Object.values(analytics.plugins)) { + if (typeof plugin["configure"] === "function") { + plugin["configure"](options); + } + } + }, + async group( + groupId?: ID, + traits?: JSONObject | null, + options?: Options, + callback?: Callback + ): Promise { + const results: any[] = []; for (const plugin of Object.values(analytics.plugins)) { if (plugin["group"]) { - plugin["group"](groupId, traits, options, callback); + results.push(await plugin["group"](groupId, traits, options, callback)); } } - return Promise.resolve({}); + //It's incorrect at many levels. First, it's not a dispatched event. Second, we take a first result + //However, since returned values are used for debugging purposes only, it's ok + return results[0]; + }, + getConfiguration(): JitsuOptions { + return opts; }, } as AnalyticsInterface; + if (opts.privacy?.disableUserIds || opts.privacy?.dontSend) { + storage.reset(); + } + return a; +} + +/** + * Fix common mistakes in jitsu configuration + * @param opts + */ +function fixOptions(opts: JitsuOptions): JitsuOptions { + return { + ...opts, + host: + (opts.host ?? "").indexOf("https://") !== 0 && (opts.host ?? "").indexOf("http://") !== 0 + ? `https://${opts.host}` + : opts.host, + }; } -export function jitsuAnalytics(opts: JitsuOptions): AnalyticsInterface { +export function jitsuAnalytics(_opts: JitsuOptions): AnalyticsInterface { + const opts = fixOptions(_opts); const inBrowser = isInBrowser(); const rt = opts.runtime || (inBrowser ? windowRuntime(opts) : emptyRuntime(opts)); return createUnderlyingAnalyticsInstance(opts, rt); @@ -96,5 +276,16 @@ export function jitsuAnalytics(opts: JitsuOptions): AnalyticsInterface { // } } -export * from "./jitsu"; +export { + Callback, + DispatchedEvent, + ID, + JSONObject, + Options, + AnalyticsInterface, + JitsuOptions, + PersistentStorage, + RuntimeFacade, + DynamicJitsuOptions, +}; export * from "./analytics-plugin"; diff --git a/libs/jitsu-js/src/jitsu.ts b/libs/jitsu-js/src/jitsu.ts deleted file mode 100644 index 3a038de45..000000000 --- a/libs/jitsu-js/src/jitsu.ts +++ /dev/null @@ -1,73 +0,0 @@ -import type { AnalyticsInterface } from "@jitsu/protocols/analytics"; -import type { AnalyticsPlugin } from "analytics"; - -type JitsuOptions = { - /** - * API Key. Optional. If not set, Jitsu will send event to the server without auth, and server - * will link the call to configured source by domain name - */ - writeKey?: string; - /** - * API Host. Default value: same host as script origin - */ - host?: string; - /** - * To enable debug logging - */ - debug?: boolean; - /** - * Explicitly specify cookie domain. If not set, cookie domain will be set to top level - * of the current domain. Example: if JS lives on "app.example.com", cookie domain will be - * set to ".example.com". If it lives on "example.com", cookie domain will be set to ".example.com" too - */ - cookieDomain?: string; - /** - * Provide fetch implementation. It is required if you want to use Jitsu in NodeJS - */ - fetch?: typeof fetch; - /** - * Which runtime to use. Runtime is used for obtaining context of the event: cookes, - * url, etc. At the moment, Jitsu supports browser runtime and NodeJS runtime, but you - * can provide your own implementation. - * - * If it's not set, the runtime will be detected automatically by presense of `window` object - */ - runtime?: RuntimeFacade; - /** - * If set to true, jitsu will output events in console. In this case you don't need to set - * writeKey / host. It's useful for debugging development environment - */ - echoEvents?: boolean; -}; - -type PersistentStorage = { - getItem: (key: string, options?: any) => any; - setItem: (key: string, value: any, options?: any) => void; - removeItem: (key: string, options?: any) => void; -}; - -type RuntimeFacade = { - store(): PersistentStorage; - userAgent(): string | undefined; - language(): string | undefined; - pageUrl(): string | undefined; - documentEncoding(): string | undefined; - timezoneOffset(): number | undefined; - screen(): - | { - width: number; - height: number; - innerWidth: number; - innerHeight: number; - density: number; - } - | undefined; - referrer(): string | undefined; - pageTitle(): string | undefined; -}; - -export declare function jitsuAnalytics(opts: JitsuOptions): AnalyticsInterface; - -export declare const jitsuAnalyticsPlugin: AnalyticsPlugin; - -export { AnalyticsInterface, JitsuOptions, PersistentStorage, RuntimeFacade }; diff --git a/libs/jitsu-js/src/script-loader.ts b/libs/jitsu-js/src/script-loader.ts index 43be0e220..70a1a1ce2 100644 --- a/libs/jitsu-js/src/script-loader.ts +++ b/libs/jitsu-js/src/script-loader.ts @@ -3,7 +3,32 @@ function findScript(src: string): HTMLScriptElement | undefined { return scripts.find(s => s.src === src); } -export function loadScript(src: string, attributes?: Record): Promise { +export type ScriptOptions = { + attributes?: Record; + www?: boolean; + js?: boolean; + min?: boolean; + query?: string; +}; + +function buildScriptSrc(src: string, options?: ScriptOptions): string { + let result = src; + if (!result.startsWith("http")) { + result = `https://${options?.www ? "www." : ""}${result}`; + } + if (options?.min) { + result = result + ".min.js"; + } else if (options?.js) { + result = result + ".js"; + } + + if (options?.query) { + result += "?" + options.query; + } + return result; +} + +export function loadScript(src: string, options?: ScriptOptions): Promise { const found = findScript(src); if (found !== undefined) { @@ -25,11 +50,11 @@ export function loadScript(src: string, attributes?: Record): Pr const script = window.document.createElement("script"); script.type = "text/javascript"; - script.src = src; + script.src = buildScriptSrc(src, options); script.async = true; script.setAttribute("status", "loading"); - for (const [k, v] of Object.entries(attributes ?? {})) { + for (const [k, v] of Object.entries(options?.attributes ?? {})) { script.setAttribute(k, v); } diff --git a/libs/jitsu-js/src/tlds.ts b/libs/jitsu-js/src/tlds.ts new file mode 100644 index 000000000..b48292613 --- /dev/null +++ b/libs/jitsu-js/src/tlds.ts @@ -0,0 +1,27 @@ +export const publicSuffixes = + "myshopify.com,ac,com.ac,edu.ac,gov.ac,net.ac,mil.ac,org.ac,ad,nom.ad,ae,co.ae,net.ae,org.ae,sch.ae,ac.ae,gov.ae,mil.ae,aero,af,gov.af,com.af,org.af,net.af,edu.af,ag,com.ag,org.ag,net.ag,co.ag,nom.ag,ai,off.ai,com.ai,net.ai,org.ai,al,com.al,edu.al,gov.al,mil.al,net.al,org.al,am,co.am,com.am,commune.am,net.am,org.am,ao,ed.ao,gv.ao,og.ao,co.ao,pb.ao,it.ao,aq,ar,bet.ar,com.ar,coop.ar,edu.ar,gob.ar,gov.ar,int.ar,mil.ar,musica.ar,mutual.ar,net.ar,org.ar,senasa.ar,tur.ar,arpa,e164.arpa,in-addr.arpa,ip6.arpa,iris.arpa,uri.arpa,urn.arpa,as,gov.as,asia,at,ac.at,co.at,gv.at,or.at,sth.ac.at,au,com.au,net.au,org.au,edu.au,gov.au,asn.au,id.au,act.au,nsw.au,nt.au,qld.au,sa.au,tas.au,vic.au,wa.au,aw,com.aw,ax,az,com.az,net.az,int.az,gov.az,org.az,edu.az,info.az,pp.az,mil.az,name.az,pro.az,biz.az,ba,com.ba,edu.ba,gov.ba,mil.ba,net.ba,org.ba,bb,biz.bb,co.bb,com.bb,edu.bb,gov.bb,info.bb,net.bb,org.bb,store.bb,tv.bb,bd,be,ac.be,bf,gov.bf,bg,a.bg,b.bg,c.bg,d.bg,e.bg,f.bg,g.bg,h.bg,i.bg,j.bg,k.bg,l.bg,m.bg,n.bg,o.bg,p.bg,q.bg,r.bg,s.bg,t.bg,u.bg,v.bg,w.bg,x.bg,y.bg,z.bg,0.bg,1.bg,2.bg,3.bg,4.bg,5.bg,6.bg,7.bg,8.bg,9.bg,bh,com.bh,edu.bh,net.bh,org.bh,gov.bh,bi,co.bi,com.bi,edu.bi,or.bi,org.bi,biz,bj,africa.bj,agro.bj,architectes.bj,assur.bj,avocats.bj,co.bj,com.bj,eco.bj,econo.bj,edu.bj,info.bj,loisirs.bj,money.bj,net.bj,org.bj,ote.bj,resto.bj,restaurant.bj,tourism.bj,univ.bj,bm,com.bm,edu.bm,gov.bm,net.bm,org.bm,bn,com.bn,edu.bn,gov.bn,net.bn,org.bn,bo,com.bo,edu.bo,gob.bo,int.bo,org.bo,net.bo,mil.bo,tv.bo,web.bo,br,9guacu.br,abc.br,adm.br,adv.br,agr.br,aju.br,am.br,anani.br,aparecida.br,app.br,arq.br,art.br,ato.br,b.br,barueri.br,belem.br,bhz.br,bib.br,bio.br,blog.br,bmd.br,boavista.br,bsb.br,campinagrande.br,campinas.br,caxias.br,cim.br,cng.br,cnt.br,com.br,contagem.br,coop.br,coz.br,cri.br,cuiaba.br,curitiba.br,def.br,des.br,det.br,dev.br,ecn.br,eco.br,edu.br,emp.br,enf.br,eng.br,esp.br,etc.br,eti.br,far.br,feira.br,flog.br,floripa.br,fm.br,fnd.br,fortal.br,fot.br,foz.br,fst.br,g12.br,geo.br,ggf.br,goiania.br,gov.br,gru.br,imb.br,ind.br,inf.br,jab.br,jampa.br,jdf.br,joinville.br,jor.br,jus.br,leg.br,lel.br,log.br,londrina.br,macapa.br,maceio.br,manaus.br,maringa.br,mat.br,med.br,mil.br,morena.br,mp.br,mus.br,natal.br,net.br,niteroi.br,nom.br,not.br,ntr.br,odo.br,ong.br,org.br,osasco.br,palmas.br,poa.br,ppg.br,pro.br,psc.br,psi.br,pvh.br,qsl.br,radio.br,rec.br,recife.br,rep.br,ribeirao.br,rio.br,riobranco.br,riopreto.br,salvador.br,sampa.br,santamaria.br,santoandre.br,saobernardo.br,saogonca.br,seg.br,sjc.br,slg.br,slz.br,sorocaba.br,srv.br,taxi.br,tc.br,tec.br,teo.br,the.br,tmp.br,trd.br,tur.br,tv.br,udi.br,vet.br,vix.br,vlog.br,wiki.br,zlg.br,bs,com.bs,net.bs,org.bs,edu.bs,gov.bs,bt,com.bt,edu.bt,gov.bt,net.bt,org.bt,bv,bw,co.bw,org.bw,by,gov.by,mil.by,com.by,of.by,bz,com.bz,net.bz,org.bz,edu.bz,gov.bz,ca,ab.ca,bc.ca,mb.ca,nb.ca,nf.ca,nl.ca,ns.ca,nt.ca,nu.ca,on.ca,pe.ca,qc.ca,sk.ca,yk.ca,gc.ca,cat,cc,cd,gov.cd,cf,cg,ch,ci,org.ci,or.ci,com.ci,co.ci,edu.ci,ed.ci,ac.ci,net.ci,go.ci,asso.ci,aéroport.ci,int.ci,presse.ci,md.ci,gouv.ci,ck,cl,co.cl,gob.cl,gov.cl,mil.cl,cm,co.cm,com.cm,gov.cm,net.cm,cn,ac.cn,com.cn,edu.cn,gov.cn,net.cn,org.cn,mil.cn,co,arts.co,com.co,edu.co,firm.co,gov.co,info.co,int.co,mil.co,net.co,nom.co,org.co,rec.co,web.co,com,coop,cr,ac.cr,co.cr,ed.cr,fi.cr,go.cr,or.cr,sa.cr,cu,com.cu,edu.cu,org.cu,net.cu,gov.cu,inf.cu,cv,com.cv,edu.cv,int.cv,nome.cv,org.cv,cw,com.cw,edu.cw,net.cw,org.cw,cx,gov.cx,cy,ac.cy,biz.cy,com.cy,ekloges.cy,gov.cy,ltd.cy,mil.cy,net.cy,org.cy,press.cy,pro.cy,tm.cy,cz,de,dj,dk,dm,com.dm,net.dm,org.dm,edu.dm,gov.dm,do,art.do,com.do,edu.do,gob.do,gov.do,mil.do,net.do,org.do,sld.do,web.do,dz,art.dz,asso.dz,com.dz,edu.dz,gov.dz,org.dz,net.dz,pol.dz,soc.dz,tm.dz,ec,com.ec,info.ec,net.ec,fin.ec,k12.ec,med.ec,pro.ec,org.ec,edu.ec,gov.ec,gob.ec,mil.ec,edu,ee,edu.ee,gov.ee,riik.ee,lib.ee,med.ee,com.ee,pri.ee,aip.ee,org.ee,fie.ee,eg,com.eg,edu.eg,eun.eg,gov.eg,mil.eg,name.eg,net.eg,org.eg,sci.eg,er,es,com.es,nom.es,org.es,gob.es,edu.es,et,com.et,gov.et,org.et,edu.et,biz.et,name.et,info.et,net.et,eu,fi,aland.fi,fj,ac.fj,biz.fj,com.fj,gov.fj,info.fj,mil.fj,name.fj,net.fj,org.fj,pro.fj,fk,com.fm,edu.fm,net.fm,org.fm,fm,fo,fr,asso.fr,com.fr,gouv.fr,nom.fr,prd.fr,tm.fr,aeroport.fr,avocat.fr,avoues.fr,cci.fr,chambagri.fr,chirurgiens-dentistes.fr,experts-comptables.fr,geometre-expert.fr,greta.fr,huissier-justice.fr,medecin.fr,notaires.fr,pharmacien.fr,port.fr,veterinaire.fr,ga,gb,edu.gd,gov.gd,gd,ge,com.ge,edu.ge,gov.ge,org.ge,mil.ge,net.ge,pvt.ge,gf,gg,co.gg,net.gg,org.gg,gh,com.gh,edu.gh,gov.gh,org.gh,mil.gh,gi,com.gi,ltd.gi,gov.gi,mod.gi,edu.gi,org.gi,gl,co.gl,com.gl,edu.gl,net.gl,org.gl,gm,gn,ac.gn,com.gn,edu.gn,gov.gn,org.gn,net.gn,gov,gp,com.gp,net.gp,mobi.gp,edu.gp,org.gp,asso.gp,gq,gr,com.gr,edu.gr,net.gr,org.gr,gov.gr,gs,gt,com.gt,edu.gt,gob.gt,ind.gt,mil.gt,net.gt,org.gt,gu,com.gu,edu.gu,gov.gu,guam.gu,info.gu,net.gu,org.gu,web.gu,gw,gy,co.gy,com.gy,edu.gy,gov.gy,net.gy,org.gy,hk,com.hk,edu.hk,gov.hk,idv.hk,net.hk,org.hk,hm,hn,com.hn,edu.hn,org.hn,net.hn,mil.hn,gob.hn,hr,iz.hr,from.hr,name.hr,com.hr,ht,com.ht,shop.ht,firm.ht,info.ht,adult.ht,net.ht,pro.ht,org.ht,med.ht,art.ht,coop.ht,pol.ht,asso.ht,edu.ht,rel.ht,gouv.ht,perso.ht,hu,co.hu,info.hu,org.hu,priv.hu,sport.hu,tm.hu,2000.hu,agrar.hu,bolt.hu,casino.hu,city.hu,erotica.hu,erotika.hu,film.hu,forum.hu,games.hu,hotel.hu,ingatlan.hu,jogasz.hu,konyvelo.hu,lakas.hu,media.hu,news.hu,reklam.hu,sex.hu,shop.hu,suli.hu,szex.hu,tozsde.hu,utazas.hu,video.hu,id,ac.id,biz.id,co.id,desa.id,go.id,mil.id,my.id,net.id,or.id,ponpes.id,sch.id,web.id,ie,gov.ie,il,ac.il,co.il,gov.il,idf.il,k12.il,muni.il,net.il,org.il,im,ac.im,co.im,com.im,ltd.co.im,net.im,org.im,plc.co.im,tt.im,tv.im,in,5g.in,6g.in,ac.in,ai.in,am.in,bihar.in,biz.in,business.in,ca.in,cn.in,co.in,com.in,coop.in,cs.in,delhi.in,dr.in,edu.in,er.in,firm.in,gen.in,gov.in,gujarat.in,ind.in,info.in,int.in,internet.in,io.in,me.in,mil.in,net.in,nic.in,org.in,pg.in,post.in,pro.in,res.in,travel.in,tv.in,uk.in,up.in,us.in,info,int,eu.int,io,com.io,iq,gov.iq,edu.iq,mil.iq,com.iq,org.iq,net.iq,ir,ac.ir,co.ir,gov.ir,id.ir,net.ir,org.ir,sch.ir,is,net.is,com.is,edu.is,gov.is,org.is,int.is,it,gov.it,edu.it,je,co.je,net.je,org.je,jm,jo,com.jo,org.jo,net.jo,edu.jo,sch.jo,gov.jo,mil.jo,name.jo,jobs,jp,ac.jp,ad.jp,co.jp,ed.jp,go.jp,gr.jp,lg.jp,ne.jp,or.jp,ke,ac.ke,co.ke,go.ke,info.ke,me.ke,mobi.ke,ne.ke,or.ke,sc.ke,kg,org.kg,net.kg,com.kg,edu.kg,gov.kg,mil.kg,kh,ki,edu.ki,biz.ki,net.ki,org.ki,gov.ki,info.ki,com.ki,km,org.km,nom.km,gov.km,prd.km,tm.km,edu.km,mil.km,ass.km,com.km,kn,net.kn,org.kn,edu.kn,gov.kn,kp,com.kp,edu.kp,gov.kp,org.kp,rep.kp,tra.kp,kr,ac.kr,co.kr,es.kr,go.kr,hs.kr,kg.kr,mil.kr,ms.kr,ne.kr,or.kr,pe.kr,re.kr,sc.kr,kw,com.kw,edu.kw,emb.kw,gov.kw,ind.kw,net.kw,org.kw,ky,com.ky,edu.ky,net.ky,org.ky,kz,org.kz,edu.kz,net.kz,gov.kz,mil.kz,com.kz,la,int.la,net.la,info.la,edu.la,gov.la,per.la,com.la,org.la,lb,com.lb,edu.lb,gov.lb,net.lb,org.lb,lc,com.lc,net.lc,co.lc,org.lc,edu.lc,gov.lc,li,lk,gov.lk,sch.lk,net.lk,int.lk,com.lk,org.lk,edu.lk,ngo.lk,soc.lk,web.lk,ltd.lk,assn.lk,grp.lk,hotel.lk,ac.lk,lr,com.lr,edu.lr,gov.lr,org.lr,net.lr,ls,ac.ls,biz.ls,co.ls,edu.ls,gov.ls,info.ls,net.ls,org.ls,sc.ls,lt,gov.lt,lu,lv,com.lv,edu.lv,gov.lv,org.lv,mil.lv,id.lv,net.lv,asn.lv,conf.lv,ly,com.ly,net.ly,gov.ly,plc.ly,edu.ly,sch.ly,med.ly,org.ly,id.ly,ma,co.ma,net.ma,gov.ma,org.ma,ac.ma,press.ma,mc,tm.mc,asso.mc,md,me,co.me,net.me,org.me,edu.me,ac.me,gov.me,its.me,priv.me,mg,org.mg,nom.mg,gov.mg,prd.mg,tm.mg,edu.mg,mil.mg,com.mg,co.mg,mh,mil,mk,com.mk,org.mk,net.mk,edu.mk,gov.mk,inf.mk,name.mk,ml,com.ml,edu.ml,gouv.ml,gov.ml,net.ml,org.ml,presse.ml,mm,mn,gov.mn,edu.mn,org.mn,mo,com.mo,net.mo,org.mo,edu.mo,gov.mo,mobi,mp,mq,mr,gov.mr,ms,com.ms,edu.ms,gov.ms,net.ms,org.ms,mt,com.mt,edu.mt,net.mt,org.mt,mu,com.mu,net.mu,org.mu,gov.mu,ac.mu,co.mu,or.mu,museum,mv,aero.mv,biz.mv,com.mv,coop.mv,edu.mv,gov.mv,info.mv,int.mv,mil.mv,museum.mv,name.mv,net.mv,org.mv,pro.mv,mw,ac.mw,biz.mw,co.mw,com.mw,coop.mw,edu.mw,gov.mw,int.mw,museum.mw,net.mw,org.mw,mx,com.mx,org.mx,gob.mx,edu.mx,net.mx,my,biz.my,com.my,edu.my,gov.my,mil.my,name.my,net.my,org.my,mz,ac.mz,adv.mz,co.mz,edu.mz,gov.mz,mil.mz,net.mz,org.mz,na,info.na,pro.na,name.na,school.na,or.na,dr.na,us.na,mx.na,ca.na,in.na,cc.na,tv.na,ws.na,mobi.na,co.na,com.na,org.na,name,nc,asso.nc,nom.nc,ne,net,nf,com.nf,net.nf,per.nf,rec.nf,web.nf,arts.nf,firm.nf,info.nf,other.nf,store.nf,ng,com.ng,edu.ng,gov.ng,i.ng,mil.ng,mobi.ng,name.ng,net.ng,org.ng,sch.ng,ni,ac.ni,biz.ni,co.ni,com.ni,edu.ni,gob.ni,in.ni,info.ni,int.ni,mil.ni,net.ni,nom.ni,org.ni,web.ni,nl,no,fhs.no,vgs.no,fylkesbibl.no,folkebibl.no,museum.no,idrett.no,priv.no,mil.no,stat.no,dep.no,kommune.no,herad.no,np,nr,biz.nr,info.nr,gov.nr,edu.nr,org.nr,net.nr,com.nr,nu,nz,ac.nz,co.nz,cri.nz,geek.nz,gen.nz,govt.nz,health.nz,iwi.nz,kiwi.nz,maori.nz,mil.nz,māori.nz,net.nz,org.nz,parliament.nz,school.nz,om,co.om,com.om,edu.om,gov.om,med.om,museum.om,net.om,org.om,pro.om,onion,org,pa,ac.pa,gob.pa,com.pa,org.pa,sld.pa,edu.pa,net.pa,ing.pa,abo.pa,med.pa,nom.pa,pe,edu.pe,gob.pe,nom.pe,mil.pe,org.pe,com.pe,net.pe,pf,com.pf,org.pf,edu.pf,pg,ph,com.ph,net.ph,org.ph,gov.ph,edu.ph,ngo.ph,mil.ph,i.ph,pk,com.pk,net.pk,edu.pk,org.pk,fam.pk,biz.pk,web.pk,gov.pk,gob.pk,gok.pk,gon.pk,gop.pk,gos.pk,info.pk,pl,com.pl,net.pl,org.pl,aid.pl,agro.pl,atm.pl,auto.pl,biz.pl,edu.pl,gmina.pl,gsm.pl,info.pl,mail.pl,miasta.pl,media.pl,mil.pl,nieruchomosci.pl,nom.pl,pc.pl,powiat.pl,priv.pl,realestate.pl,rel.pl,sex.pl,shop.pl,sklep.pl,sos.pl,szkola.pl,targi.pl,tm.pl,tourism.pl,travel.pl,turystyka.pl,pm,pn,gov.pn,co.pn,org.pn,edu.pn,net.pn,post,pr,com.pr,net.pr,org.pr,gov.pr,edu.pr,isla.pr,pro.pr,biz.pr,info.pr,name.pr,est.pr,prof.pr,ac.pr,pro,aaa.pro,aca.pro,acct.pro,avocat.pro,bar.pro,cpa.pro,eng.pro,jur.pro,law.pro,med.pro,recht.pro,ps,edu.ps,gov.ps,sec.ps,plo.ps,com.ps,org.ps,net.ps,pt,net.pt,gov.pt,org.pt,edu.pt,int.pt,publ.pt,com.pt,nome.pt,pw,co.pw,ne.pw,or.pw,ed.pw,go.pw,belau.pw,py,com.py,coop.py,edu.py,gov.py,mil.py,net.py,org.py,qa,com.qa,edu.qa,gov.qa,mil.qa,name.qa,net.qa,org.qa,sch.qa,re,asso.re,com.re,nom.re,ro,arts.ro,com.ro,firm.ro,info.ro,nom.ro,nt.ro,org.ro,rec.ro,store.ro,tm.ro,www.ro,rs,ac.rs,co.rs,edu.rs,gov.rs,in.rs,org.rs,ru,rw,ac.rw,co.rw,coop.rw,gov.rw,mil.rw,net.rw,org.rw,sa,com.sa,net.sa,org.sa,gov.sa,med.sa,pub.sa,edu.sa,sch.sa,sb,com.sb,edu.sb,gov.sb,net.sb,org.sb,sc,com.sc,gov.sc,net.sc,org.sc,edu.sc,sd,com.sd,net.sd,org.sd,edu.sd,med.sd,tv.sd,gov.sd,info.sd,se,a.se,ac.se,b.se,bd.se,brand.se,c.se,d.se,e.se,f.se,fh.se,fhsk.se,fhv.se,g.se,h.se,i.se,k.se,komforb.se,kommunalforbund.se,komvux.se,l.se,lanbib.se,m.se,n.se,naturbruksgymn.se,o.se,org.se,p.se,parti.se,pp.se,press.se,r.se,s.se,t.se,tm.se,u.se,w.se,x.se,y.se,z.se,sg,com.sg,net.sg,org.sg,gov.sg,edu.sg,per.sg,sh,com.sh,net.sh,gov.sh,org.sh,mil.sh,si,sj,sk,sl,com.sl,net.sl,edu.sl,gov.sl,org.sl,sm,sn,art.sn,com.sn,edu.sn,gouv.sn,org.sn,perso.sn,univ.sn,so,com.so,edu.so,gov.so,me.so,net.so,org.so,sr,ss,biz.ss,com.ss,edu.ss,gov.ss,me.ss,net.ss,org.ss,sch.ss,st,co.st,com.st,consulado.st,edu.st,embaixada.st,mil.st,net.st,org.st,principe.st,saotome.st,store.st,su,sv,com.sv,edu.sv,gob.sv,org.sv,red.sv,sx,gov.sx,sy,edu.sy,gov.sy,net.sy,mil.sy,com.sy,org.sy,sz,co.sz,ac.sz,org.sz,tc,td,tel,tf,tg,th,ac.th,co.th,go.th,in.th,mi.th,net.th,or.th,tj,ac.tj,biz.tj,co.tj,com.tj,edu.tj,go.tj,gov.tj,int.tj,mil.tj,name.tj,net.tj,nic.tj,org.tj,test.tj,web.tj,tk,tl,gov.tl,tm,com.tm,co.tm,org.tm,net.tm,nom.tm,gov.tm,mil.tm,edu.tm,tn,com.tn,ens.tn,fin.tn,gov.tn,ind.tn,info.tn,intl.tn,mincom.tn,nat.tn,net.tn,org.tn,perso.tn,tourism.tn,to,com.to,gov.to,net.to,org.to,edu.to,mil.to,tr,av.tr,bbs.tr,bel.tr,biz.tr,com.tr,dr.tr,edu.tr,gen.tr,gov.tr,info.tr,mil.tr,k12.tr,kep.tr,name.tr,net.tr,org.tr,pol.tr,tel.tr,tsk.tr,tv.tr,web.tr,nc.tr,tt,co.tt,com.tt,org.tt,net.tt,biz.tt,info.tt,pro.tt,int.tt,coop.tt,jobs.tt,mobi.tt,travel.tt,museum.tt,aero.tt,name.tt,gov.tt,edu.tt,tv,tw,edu.tw,gov.tw,mil.tw,com.tw,net.tw,org.tw,idv.tw,game.tw,ebiz.tw,club.tw,tz,ac.tz,co.tz,go.tz,hotel.tz,info.tz,me.tz,mil.tz,mobi.tz,ne.tz,or.tz,sc.tz,tv.tz,ua,com.ua,edu.ua,gov.ua,in.ua,net.ua,org.ua,ug,co.ug,or.ug,ac.ug,sc.ug,go.ug,ne.ug,com.ug,org.ug,uk,ac.uk,co.uk,gov.uk,ltd.uk,me.uk,net.uk,nhs.uk,org.uk,plc.uk,police.uk,us,dni.us,fed.us,isa.us,kids.us,nsn.us,ak.us,al.us,ar.us,as.us,az.us,ca.us,co.us,ct.us,dc.us,de.us,fl.us,ga.us,gu.us,hi.us,ia.us,id.us,il.us,in.us,ks.us,ky.us,la.us,ma.us,md.us,me.us,mi.us,mn.us,mo.us,ms.us,mt.us,nc.us,nd.us,ne.us,nh.us,nj.us,nm.us,nv.us,ny.us,oh.us,ok.us,or.us,pa.us,pr.us,ri.us,sc.us,sd.us,tn.us,tx.us,ut.us,vi.us,vt.us,va.us,wa.us,wi.us,wv.us,wy.us,uy,com.uy,edu.uy,gub.uy,mil.uy,net.uy,org.uy,uz,co.uz,com.uz,net.uz,org.uz,va,vc,com.vc,net.vc,org.vc,gov.vc,mil.vc,edu.vc,ve,arts.ve,bib.ve,co.ve,com.ve,e12.ve,edu.ve,firm.ve,gob.ve,gov.ve,info.ve,int.ve,mil.ve,net.ve,nom.ve,org.ve,rar.ve,rec.ve,store.ve,tec.ve,web.ve,vg,vi,co.vi,com.vi,k12.vi,net.vi,org.vi,vn,ac.vn,ai.vn,biz.vn,com.vn,edu.vn,gov.vn,health.vn,id.vn,info.vn,int.vn,io.vn,name.vn,net.vn,org.vn,pro.vn,vu,com.vu,edu.vu,net.vu,org.vu,wf,ws,com.ws,net.ws,org.ws,gov.ws,edu.ws,yt,ye,com.ye,edu.ye,gov.ye,net.ye,mil.ye,org.ye,ac.za,agric.za,alt.za,co.za,edu.za,gov.za,grondar.za,law.za,mil.za,net.za,ngo.za,nic.za,nis.za,nom.za,org.za,school.za,tm.za,web.za,zm,ac.zm,biz.zm,co.zm,com.zm,edu.zm,gov.zm,info.zm,mil.zm,net.zm,org.zm,sch.zm,zw,ac.zw,co.zw,gov.zw,mil.zw,org.zw".split( + "," + ); + +//convert to Map +const _publicSuffixesMap: Record = {}; +publicSuffixes.forEach(tld => { + _publicSuffixesMap[tld] = true; +}); + +export const publicSuffixesMap = _publicSuffixesMap; + +export function getTopLevelDomain(hostname: string) { + const [domain] = hostname.split(":"); + const parts = domain.split("."); + if (parts[parts.length - 1] === "localhost" || parts.length < 2) { + return parts[parts.length - 1]; + } else { + const d = parts[parts.length - 2] + "." + parts[parts.length - 1]; + if (parts.length > 2 && publicSuffixesMap[d]) { + return parts[parts.length - 3] + "." + d; + } else { + return d; + } + } +} diff --git a/libs/jitsu-js/src/version.ts b/libs/jitsu-js/src/version.ts index c82ee3fd0..46441f83a 100644 --- a/libs/jitsu-js/src/version.ts +++ b/libs/jitsu-js/src/version.ts @@ -1,3 +1,6 @@ -import { name as jitsuLibraryName, version as jitsuVersion } from "../package.json"; +import pkg from "../package.json"; + +const jitsuVersion = pkg.version !== "0.0.0" ? pkg.version : "2.0.0"; +const jitsuLibraryName = "@jitsu/js"; export { jitsuVersion, jitsuLibraryName }; diff --git a/libs/jitsu-js/tsconfig.json b/libs/jitsu-js/tsconfig.json index 82b49e613..85ba693af 100644 --- a/libs/jitsu-js/tsconfig.json +++ b/libs/jitsu-js/tsconfig.json @@ -7,7 +7,7 @@ "moduleResolution": "Node", "resolveJsonModule": true, "target": "ES2015", - "lib": ["es2017", "dom"], + "lib": ["es2020", "dom"], //this makes typescript igore @types/node during compilation "types": [] }, diff --git a/libs/jitsu-react/README.md b/libs/jitsu-react/README.md index b9c5b2f04..eaa56cc03 100644 --- a/libs/jitsu-react/README.md +++ b/libs/jitsu-react/README.md @@ -11,42 +11,4 @@ npm install --save @jitsu/jitsu-react ## Usage -To setup Jitsu-React library you need to add `JitsuProvider` component close to the root level of your app: - -```tsx -import React from "react"; -import { JitsuProvider } from "@jitsu/jitsu-react"; - -export default function App() { - return .d.jitsu.com" }}> - - ; -} -``` - -Then use `useJitsu` hook in components where you want to track events. - -```tsx -import * as React from "react"; -import { useJitsu } from "@jitsu/jitsu-react"; -import { useEffect } from "react"; - -export default function Page() { - const { analytics } = useJitsu(); - useEffect(() => { - // Track page view - analytics.track("event", { prop: "value" }); - }, [location]); - - return ( -
- -
- ); -} -``` - -As `location` you should use an value that changes on every navigation. Examples: - * React Router: `useLocationHook()` - * Next.js: `const rounter = useRouter()`; then `router.asPath` - * Others: `[window.location.pathname, window.location.search, window.location.hash]` +**Please read a documentation on [Jitsu Docs](https://docs.jitsu.com/sending-data/react)** diff --git a/libs/jitsu-react/package.json b/libs/jitsu-react/package.json index 28b0fab5a..973b088eb 100644 --- a/libs/jitsu-react/package.json +++ b/libs/jitsu-react/package.json @@ -11,7 +11,7 @@ "node": ">=10" }, "scripts": { - "build": "microbundle-crl --no-compress --format es,cjs", + "build": "pnpm compile && microbundle build --jsx React.createElement --no-compress --format es,cjs", "compile": "tsc -p .", "clean": "rm -rf dist" }, @@ -19,9 +19,9 @@ "@jitsu/js": "workspace:*" }, "peerDependencies": { - "react": "15.x || 16.x || 17.x || 18.x", - "@types/react": "15.x || 16.x || 17.x || 18.x", - "react-router-dom": "5.x || 6.x" + "react": "15.x || 16.x || 17.x || 18.x || 19.x", + "@types/react": "15.x || 16.x || 17.x || 18.x || 19.x", + "react-router-dom": "5.x || 6.x || 7.x" }, "peerDependenciesMeta": { "react-router-dom": { @@ -30,14 +30,14 @@ }, "devDependencies": { "ts-toolbelt": "^9.6.0", - "@testing-library/jest-dom": "^5.16.5", + "@testing-library/jest-dom": "^6.1.3", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^14.4.3", "@types/jest": "^29.2.3", - "@types/node": "^18.11.9", - "microbundle-crl": "^0.13.11", - "typescript": "^4.9.5", - "@babel/plugin-transform-regenerator": "^7.20.5" + "@types/node": "^18.15.3", + "microbundle": "^0.15.1", + "typescript": "^5.6.3", + "@babel/plugin-transform-regenerator": "^7.22.10" }, "files": [ "dist" diff --git a/libs/jitsu-react/src/JitsuContext.tsx b/libs/jitsu-react/src/JitsuContext.tsx index 5ec0efdf9..d12b83127 100644 --- a/libs/jitsu-react/src/JitsuContext.tsx +++ b/libs/jitsu-react/src/JitsuContext.tsx @@ -1,3 +1,5 @@ +"use client"; + import { createContext } from "react"; import { AnalyticsInterface } from "@jitsu/js"; diff --git a/libs/jitsu-react/src/JitsuProvider.tsx b/libs/jitsu-react/src/JitsuProvider.tsx index ee8c2542e..669997d90 100644 --- a/libs/jitsu-react/src/JitsuProvider.tsx +++ b/libs/jitsu-react/src/JitsuProvider.tsx @@ -1,3 +1,5 @@ +"use client"; + import * as React from "react"; import { PropsWithChildren, useMemo } from "react"; import JitsuContext, { JitsuInstance } from "./JitsuContext"; diff --git a/libs/jitsu-react/src/index.ts b/libs/jitsu-react/src/index.ts index e36890ca5..d1494dc96 100644 --- a/libs/jitsu-react/src/index.ts +++ b/libs/jitsu-react/src/index.ts @@ -1,3 +1,5 @@ +"use client"; + export { default as JitsuContext } from "./JitsuContext"; export { default as JitsuProvider } from "./JitsuProvider"; export { default as useJitsu } from "./useJitsu"; diff --git a/libs/jitsu-react/src/useJitsu.ts b/libs/jitsu-react/src/useJitsu.ts index 5755c7b5b..e239996f2 100644 --- a/libs/jitsu-react/src/useJitsu.ts +++ b/libs/jitsu-react/src/useJitsu.ts @@ -1,3 +1,5 @@ +"use client"; + import { useContext } from "react"; import JitsuContext from "./JitsuContext"; diff --git a/libs/jitsu-react/tsconfig.json b/libs/jitsu-react/tsconfig.json index 48d120b4e..cd2d72b27 100644 --- a/libs/jitsu-react/tsconfig.json +++ b/libs/jitsu-react/tsconfig.json @@ -10,9 +10,8 @@ "esModuleInterop": true, "noImplicitReturns": true, "noImplicitThis": true, - "noImplicitAny": true, + "noImplicitAny": false, "strictNullChecks": true, - "suppressImplicitAnyIndexErrors": true, "noUnusedLocals": true, "noUnusedParameters": true, "allowSyntheticDefaultImports": true diff --git a/libs/jsondiffpatch/.eslintignore b/libs/jsondiffpatch/.eslintignore new file mode 100644 index 000000000..ee60e3fcf --- /dev/null +++ b/libs/jsondiffpatch/.eslintignore @@ -0,0 +1,7 @@ +**/*{.,-}min.js +node_modules +build +.git +coverage +dist +lib diff --git a/libs/jsondiffpatch/.eslintrc.cjs b/libs/jsondiffpatch/.eslintrc.cjs new file mode 100644 index 000000000..7a496e685 --- /dev/null +++ b/libs/jsondiffpatch/.eslintrc.cjs @@ -0,0 +1,22 @@ +module.exports = { + root: true, + extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended-type-checked"], + plugins: ["@typescript-eslint"], + parser: "@typescript-eslint/parser", + parserOptions: { + project: true, + tsconfigRootDir: __dirname, + }, + overrides: [ + { + files: ["test/**/*.ts"], + extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended-type-checked"], + plugins: ["@typescript-eslint"], + parser: "@typescript-eslint/parser", + parserOptions: { + project: "./test/tsconfig.json", + tsconfigRootDir: __dirname, + }, + }, + ], +}; diff --git a/libs/jsondiffpatch/jest.config.cjs b/libs/jsondiffpatch/jest.config.cjs new file mode 100644 index 000000000..8d9565ceb --- /dev/null +++ b/libs/jsondiffpatch/jest.config.cjs @@ -0,0 +1,26 @@ +/** @type {import('ts-jest').JestConfigWithTsJest} */ +module.exports = { + preset: "ts-jest", + testEnvironment: "node", + extensionsToTreatAsEsm: [".ts"], + moduleNameMapper: { + "^(\\.{1,2}/.*)\\.js$": "$1", + }, + transform: { + "^.+\\.ts$": [ + "ts-jest", + { + tsconfig: "test/tsconfig.json", + useESM: true, + }, + ], + }, + coverageThreshold: { + global: { + branches: 50, + functions: 50, + lines: 50, + statements: 50, + }, + }, +}; diff --git a/libs/jsondiffpatch/package.json b/libs/jsondiffpatch/package.json new file mode 100644 index 000000000..8bb488942 --- /dev/null +++ b/libs/jsondiffpatch/package.json @@ -0,0 +1,50 @@ +{ + "name": "jsondiffpatch", + "version": "0.6.0", + "author": "Benjamin Eidelman ", + "description": "Diff & Patch for Javascript objects", + "contributors": [ + "Benjamin Eidelman " + ], + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "files": [ + "src/index.ts" + ], + "scripts": { + "build": "tsc", + "clean": "rm -rf ./dist", + "type-check": "tsc --noEmit", + "lint": "eslint . --ext .ts", + "test": "jest --coverage", + "prepack": "pnpm build", + "prepublishOnly": "pnpm test && pnpm run lint" + }, + "repository": { + "type": "git", + "url": "https://github.com/benjamine/jsondiffpatch.git" + }, + "keywords": [ + "json", + "diff", + "patch" + ], + "dependencies": { + }, + "devDependencies": { + "@types/jest": "^29.5.10", + "@typescript-eslint/eslint-plugin": "^8.12.2", + "@typescript-eslint/parser": "^8.12.2", + "eslint": "^8.57.0", + "eslint-config-prettier": "^9.1.0", + "jest": "^29.7.0", + "ts-jest": "^29.1.1", + "tslib": "^2.6.3", + "typescript": "^5.6.3" + }, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "homepage": "https://github.com/benjamine/jsondiffpatch" +} diff --git a/libs/jsondiffpatch/src/contexts/context.ts b/libs/jsondiffpatch/src/contexts/context.ts new file mode 100644 index 000000000..dbe394fdc --- /dev/null +++ b/libs/jsondiffpatch/src/contexts/context.ts @@ -0,0 +1,46 @@ +import type { Options } from "../types.js"; + +export default abstract class Context { + abstract pipe: string; + + result?: TResult; + hasResult?: boolean; + exiting?: boolean; + parent?: this; + childName?: string | number; + root?: this; + options?: Options; + children?: this[]; + nextAfterChildren?: this | null; + next?: this | null; + + setResult(result: TResult) { + this.result = result; + this.hasResult = true; + return this; + } + + exit() { + this.exiting = true; + return this; + } + + push(child: this, name?: string | number) { + child.parent = this; + if (typeof name !== "undefined") { + child.childName = name; + } + child.root = this.root || this; + child.options = child.options || this.options; + if (!this.children) { + this.children = [child]; + this.nextAfterChildren = this.next || null; + this.next = child; + } else { + this.children[this.children.length - 1].next = child; + this.children.push(child); + } + child.next = this; + return this; + } +} diff --git a/libs/jsondiffpatch/src/contexts/diff.ts b/libs/jsondiffpatch/src/contexts/diff.ts new file mode 100644 index 000000000..0e6241f15 --- /dev/null +++ b/libs/jsondiffpatch/src/contexts/diff.ts @@ -0,0 +1,26 @@ +import Context from "./context.js"; +import type { Delta } from "../types.js"; + +class DiffContext extends Context { + left: unknown; + right: unknown; + pipe: "diff"; + + leftType?: string; + rightType?: string; + leftIsArray?: boolean; + rightIsArray?: boolean; + + constructor(left: unknown, right: unknown) { + super(); + this.left = left; + this.right = right; + this.pipe = "diff"; + } + + setResult(result: Delta) { + return super.setResult(result); + } +} + +export default DiffContext; diff --git a/libs/jsondiffpatch/src/contexts/patch.ts b/libs/jsondiffpatch/src/contexts/patch.ts new file mode 100644 index 000000000..a66c52012 --- /dev/null +++ b/libs/jsondiffpatch/src/contexts/patch.ts @@ -0,0 +1,19 @@ +import Context from "./context.js"; +import type { Delta } from "../types.js"; + +class PatchContext extends Context { + left: unknown; + delta: Delta; + pipe: "patch"; + + nested?: boolean; + + constructor(left: unknown, delta: Delta) { + super(); + this.left = left; + this.delta = delta; + this.pipe = "patch"; + } +} + +export default PatchContext; diff --git a/libs/jsondiffpatch/src/diffpatcher.ts b/libs/jsondiffpatch/src/diffpatcher.ts new file mode 100644 index 000000000..783c74d5c --- /dev/null +++ b/libs/jsondiffpatch/src/diffpatcher.ts @@ -0,0 +1,41 @@ +import Processor from "./processor.js"; +import Pipe from "./pipe.js"; +import DiffContext from "./contexts/diff.js"; +import PatchContext from "./contexts/patch.js"; + +import * as trivial from "./filters/trivial.js"; +import * as nested from "./filters/nested.js"; +import * as dates from "./filters/dates.js"; +import type { Delta, Options } from "./types.js"; + +class DiffPatcher { + processor: Processor; + + constructor(options?: Options) { + this.processor = new Processor(options); + this.processor.pipe( + new Pipe("diff") + .append(nested.collectChildrenDiffFilter, trivial.diffFilter, dates.diffFilter, nested.objectsDiffFilter) + .shouldHaveResult()! + ); + this.processor.pipe( + new Pipe("patch") + .append(nested.collectChildrenPatchFilter, trivial.patchFilter, nested.patchFilter) + .shouldHaveResult()! + ); + } + + options(options: Options) { + return this.processor.options(options); + } + + diff(left: unknown, right: unknown) { + return this.processor.process(new DiffContext(left, right)); + } + + patch(left: unknown, delta: Delta) { + return this.processor.process(new PatchContext(left, delta)); + } +} + +export default DiffPatcher; diff --git a/libs/jsondiffpatch/src/filters/dates.ts b/libs/jsondiffpatch/src/filters/dates.ts new file mode 100644 index 000000000..d697fa379 --- /dev/null +++ b/libs/jsondiffpatch/src/filters/dates.ts @@ -0,0 +1,20 @@ +import type { Filter } from "../types.js"; +import type DiffContext from "../contexts/diff.js"; + +export const diffFilter: Filter = function datesDiffFilter(context) { + if (context.left instanceof Date) { + if (context.right instanceof Date) { + if (context.left.getTime() !== context.right.getTime()) { + context.setResult([context.left, context.right]); + } else { + context.setResult(undefined); + } + } else { + context.setResult([context.left, context.right]); + } + context.exit(); + } else if (context.right instanceof Date) { + context.setResult([context.left, context.right]).exit(); + } +}; +diffFilter.filterName = "dates"; diff --git a/libs/jsondiffpatch/src/filters/nested.ts b/libs/jsondiffpatch/src/filters/nested.ts new file mode 100644 index 000000000..edeb31968 --- /dev/null +++ b/libs/jsondiffpatch/src/filters/nested.ts @@ -0,0 +1,110 @@ +import DiffContext from "../contexts/diff.js"; +import PatchContext from "../contexts/patch.js"; +import type { ArrayDelta, Delta, Filter, ObjectDelta } from "../types.js"; + +export const collectChildrenDiffFilter: Filter = context => { + if (!context || !context.children) { + return; + } + const length = context.children.length; + let child; + let result = context.result as ObjectDelta | ArrayDelta; + for (let index = 0; index < length; index++) { + child = context.children[index]; + if (typeof child.result === "undefined") { + continue; + } + result = result || {}; + (result as Record)[child.childName!] = child.result; + } + if (result && context.leftIsArray) { + result._t = "a"; + } + context.setResult(result).exit(); +}; +collectChildrenDiffFilter.filterName = "collectChildren"; + +export const objectsDiffFilter: Filter = context => { + if (context.leftIsArray || context.leftType !== "object") { + return; + } + + const left = context.left as Record; + const right = context.right as Record; + + let name; + let child; + const propertyFilter = context.options!.propertyFilter; + for (name in left) { + if (!Object.prototype.hasOwnProperty.call(left, name)) { + continue; + } + if (propertyFilter && !propertyFilter(name, context)) { + continue; + } + child = new DiffContext(left[name], right[name]); + context.push(child, name); + } + for (name in right) { + if (!Object.prototype.hasOwnProperty.call(right, name)) { + continue; + } + if (propertyFilter && !propertyFilter(name, context)) { + continue; + } + if (typeof left[name] === "undefined") { + child = new DiffContext(undefined, right[name]); + context.push(child, name); + } + } + + if (!context.children || context.children.length === 0) { + context.setResult(undefined).exit(); + return; + } + context.exit(); +}; +objectsDiffFilter.filterName = "objects"; + +export const patchFilter: Filter = function nestedPatchFilter(context) { + if (!context.nested) { + return; + } + const nestedDelta = context.delta as ObjectDelta | ArrayDelta; + if (nestedDelta._t) { + return; + } + const objectDelta = nestedDelta as ObjectDelta; + let name; + let child; + for (name in objectDelta) { + child = new PatchContext((context.left as Record)[name], objectDelta[name]); + context.push(child, name); + } + context.exit(); +}; +patchFilter.filterName = "objects"; + +export const collectChildrenPatchFilter: Filter = function collectChildrenPatchFilter(context) { + if (!context || !context.children) { + return; + } + const deltaWithChildren = context.delta as ObjectDelta | ArrayDelta; + if (deltaWithChildren._t) { + return; + } + const object = context.left as Record; + const length = context.children.length; + let child; + for (let index = 0; index < length; index++) { + child = context.children[index]; + const property = child.childName as string; + if (Object.prototype.hasOwnProperty.call(context.left, property) && child.result === undefined) { + delete object[property]; + } else if (object[property] !== child.result) { + object[property] = child.result; + } + } + context.setResult(object).exit(); +}; +collectChildrenPatchFilter.filterName = "collectChildren"; diff --git a/libs/jsondiffpatch/src/filters/trivial.ts b/libs/jsondiffpatch/src/filters/trivial.ts new file mode 100644 index 000000000..42ca3025e --- /dev/null +++ b/libs/jsondiffpatch/src/filters/trivial.ts @@ -0,0 +1,106 @@ +import type DiffContext from "../contexts/diff.js"; +import type PatchContext from "../contexts/patch.js"; +import type { AddedDelta, DeletedDelta, Filter, ModifiedDelta } from "../types.js"; + +function arrayEquals(a: unknown[], b: unknown[]): boolean { + if (a.length !== b.length) { + return false; + } + for (let i = 0; i < a.length; i++) { + if (a[i] !== b[i]) { + return false; + } + } + return true; +} + +export const diffFilter: Filter = function trivialMatchesDiffFilter(context) { + if (context.left === context.right) { + context.setResult(undefined).exit(); + return; + } + if (typeof context.left === "undefined") { + if (typeof context.right === "function") { + throw new Error("functions are not supported"); + } + context.setResult([context.right]).exit(); + return; + } + if (typeof context.right === "undefined") { + context.setResult([context.left, 0, 0]).exit(); + return; + } + if (typeof context.left === "function" || typeof context.right === "function") { + throw new Error("functions are not supported"); + } + context.leftType = context.left === null ? "null" : typeof context.left; + context.rightType = context.right === null ? "null" : typeof context.right; + if (context.leftType !== context.rightType) { + context.setResult([context.left, context.right]).exit(); + return; + } + if (context.leftType === "boolean" || context.leftType === "number" || context.leftType === "string") { + context.setResult([context.left, context.right]).exit(); + return; + } + if (context.leftType === "object") { + context.leftIsArray = Array.isArray(context.left); + } + if (context.rightType === "object") { + context.rightIsArray = Array.isArray(context.right); + } + if (context.leftIsArray !== context.rightIsArray) { + context.setResult([context.left, context.right]).exit(); + return; + } + if (context.leftIsArray && context.rightIsArray) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + if (arrayEquals(context.left as unknown[], context.right as unknown[])) { + context.setResult(undefined).exit(); + return; + } else { + context.setResult([context.left, context.right]).exit(); + return; + } + } + + if (context.left instanceof RegExp) { + if (context.right instanceof RegExp) { + context.setResult([context.left.toString(), context.right.toString()]).exit(); + } else { + context.setResult([context.left, context.right]).exit(); + } + } +}; +diffFilter.filterName = "trivial"; + +export const patchFilter: Filter = function trivialMatchesPatchFilter(context) { + if (typeof context.delta === "undefined") { + context.setResult(context.left).exit(); + return; + } + context.nested = !Array.isArray(context.delta); + if (context.nested) { + return; + } + const nonNestedDelta = context.delta as AddedDelta | ModifiedDelta | DeletedDelta; + if (nonNestedDelta.length === 1) { + context.setResult(nonNestedDelta[0]).exit(); + return; + } + if (nonNestedDelta.length === 2) { + if (context.left instanceof RegExp) { + const regexArgs = /^\/(.*)\/([gimyu]+)$/.exec(nonNestedDelta[1] as string); + if (regexArgs) { + context.setResult(new RegExp(regexArgs[1], regexArgs[2])).exit(); + return; + } + } + context.setResult(nonNestedDelta[1]).exit(); + return; + } + if (nonNestedDelta.length === 3 && nonNestedDelta[2] === 0) { + context.setResult(undefined).exit(); + } +}; +patchFilter.filterName = "trivial"; diff --git a/libs/jsondiffpatch/src/index.ts b/libs/jsondiffpatch/src/index.ts new file mode 100644 index 000000000..65263b6bd --- /dev/null +++ b/libs/jsondiffpatch/src/index.ts @@ -0,0 +1,30 @@ +import DiffPatcher from "./diffpatcher.js"; +import type { Delta, Options } from "./types.js"; +import type Context from "./contexts/context.js"; +import type DiffContext from "./contexts/diff.js"; +import type PatchContext from "./contexts/patch.js"; + +export { DiffPatcher }; + +export type * from "./types.js"; +export type { Context, DiffContext, PatchContext }; + +export function create(options?: Options) { + return new DiffPatcher(options); +} + +let defaultInstance: DiffPatcher; + +export function diff(left: unknown, right: unknown) { + if (!defaultInstance) { + defaultInstance = new DiffPatcher(); + } + return defaultInstance.diff(left, right); +} + +export function patch(left: unknown, delta: Delta) { + if (!defaultInstance) { + defaultInstance = new DiffPatcher(); + } + return defaultInstance.patch(left, delta); +} diff --git a/libs/jsondiffpatch/src/pipe.ts b/libs/jsondiffpatch/src/pipe.ts new file mode 100644 index 000000000..83bd79c71 --- /dev/null +++ b/libs/jsondiffpatch/src/pipe.ts @@ -0,0 +1,121 @@ +import type Context from "./contexts/context.js"; +import type Processor from "./processor.js"; +import type { Filter } from "./types.js"; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +class Pipe> { + name: string; + filters: Filter[]; + processor?: Processor; + debug?: boolean; + resultCheck?: ((context: TContext) => void) | null; + + constructor(name: string) { + this.name = name; + this.filters = []; + } + + process(input: TContext) { + if (!this.processor) { + throw new Error("add this pipe to a processor before using it"); + } + const debug = this.debug; + const length = this.filters.length; + const context = input; + for (let index = 0; index < length; index++) { + const filter = this.filters[index]; + if (debug) { + this.log(`filter: ${filter.filterName}`); + } + filter(context); + if (typeof context === "object" && context.exiting) { + context.exiting = false; + break; + } + } + if (!context.next && this.resultCheck) { + this.resultCheck(context); + } + } + + log(msg: string) { + console.log(`[jsondiffpatch] ${this.name} pipe, ${msg}`); + } + + append(...args: Filter[]) { + this.filters.push(...args); + return this; + } + + prepend(...args: Filter[]) { + this.filters.unshift(...args); + return this; + } + + indexOf(filterName: string) { + if (!filterName) { + throw new Error("a filter name is required"); + } + for (let index = 0; index < this.filters.length; index++) { + const filter = this.filters[index]; + if (filter.filterName === filterName) { + return index; + } + } + throw new Error(`filter not found: ${filterName}`); + } + + list() { + return this.filters.map(f => f.filterName); + } + + after(filterName: string, ...params: Filter[]) { + const index = this.indexOf(filterName); + this.filters.splice(index + 1, 0, ...params); + return this; + } + + before(filterName: string, ...params: Filter[]) { + const index = this.indexOf(filterName); + this.filters.splice(index, 0, ...params); + return this; + } + + replace(filterName: string, ...params: Filter[]) { + const index = this.indexOf(filterName); + this.filters.splice(index, 1, ...params); + return this; + } + + remove(filterName: string) { + const index = this.indexOf(filterName); + this.filters.splice(index, 1); + return this; + } + + clear() { + this.filters.length = 0; + return this; + } + + shouldHaveResult(should?: boolean) { + if (should === false) { + this.resultCheck = null; + return; + } + if (this.resultCheck) { + return; + } + this.resultCheck = context => { + if (!context.hasResult) { + console.log(context); + const error: Error & { noResult?: boolean } = new Error(`${this.name} failed`); + error.noResult = true; + throw error; + } + }; + return this; + } +} + +export default Pipe; diff --git a/libs/jsondiffpatch/src/processor.ts b/libs/jsondiffpatch/src/processor.ts new file mode 100644 index 000000000..41c9a0dd7 --- /dev/null +++ b/libs/jsondiffpatch/src/processor.ts @@ -0,0 +1,89 @@ +import type Context from "./contexts/context.js"; +import type Pipe from "./pipe.js"; +import type { Options } from "./types.js"; +import type DiffContext from "./contexts/diff.js"; +import type PatchContext from "./contexts/patch.js"; + +class Processor { + selfOptions: Options; + pipes: { + diff: Pipe; + patch: Pipe; + }; + + constructor(options?: Options) { + this.selfOptions = options || {}; + this.pipes = {} as { + diff: Pipe; + patch: Pipe; + }; + } + + options(options?: Options) { + if (options) { + this.selfOptions = options; + } + return this.selfOptions; + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + pipe>(name: string | Pipe, pipeArg?: Pipe) { + let pipe = pipeArg; + if (typeof name === "string") { + if (typeof pipe === "undefined") { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion + return this.pipes[name as keyof typeof this.pipes]!; + } else { + this.pipes[name as keyof typeof this.pipes] = pipe as Pipe< + // eslint-disable-next-line @typescript-eslint/no-explicit-any + Context + >; + } + } + if (name && (name as Pipe).name) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + pipe = name as Pipe>; + if (pipe.processor === this) { + return pipe; + } + this.pipes[pipe.name as keyof typeof this.pipes] = pipe as Pipe< + // eslint-disable-next-line @typescript-eslint/no-explicit-any + Context + >; + } + pipe!.processor = this; + return pipe!; + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + process>(input: TContext, pipe?: Pipe): TContext["result"] | undefined { + let context = input; + context.options = this.options(); + let nextPipe: Pipe | string | null = pipe || input.pipe || "default"; + let lastPipe; + while (nextPipe) { + if (typeof context.nextAfterChildren !== "undefined") { + // children processed and coming back to parent + context.next = context.nextAfterChildren; + context.nextAfterChildren = null; + } + + if (typeof nextPipe === "string") { + nextPipe = this.pipe(nextPipe) as Pipe; + } + nextPipe.process(context); + lastPipe = nextPipe; + nextPipe = null; + if (context) { + if (context.next) { + context = context.next; + nextPipe = context.pipe || lastPipe; + } + } + } + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return context.hasResult ? context.result : undefined; + } +} + +export default Processor; diff --git a/libs/jsondiffpatch/src/types.ts b/libs/jsondiffpatch/src/types.ts new file mode 100644 index 000000000..efeccd45a --- /dev/null +++ b/libs/jsondiffpatch/src/types.ts @@ -0,0 +1,46 @@ +import type Context from "./contexts/context.js"; +import type DiffContext from "./contexts/diff.js"; + +export interface Options { + objectHash?: (item: object, index?: number) => string | undefined; + matchByPosition?: boolean; + arrays?: { + detectMove?: boolean; + includeValueOnMove?: boolean; + }; + propertyFilter?: (name: string, context: DiffContext) => boolean; +} + +export type AddedDelta = [unknown]; +export type ModifiedDelta = [unknown, unknown]; +export type DeletedDelta = [unknown, 0, 0]; + +export interface ObjectDelta { + [property: string]: Delta; +} + +export interface ArrayDelta { + _t: "a"; + [index: number | `${number}`]: Delta; + [index: `_${number}`]: DeletedDelta | MovedDelta; +} + +export type MovedDelta = [unknown, number, 3]; + +export type TextDiffDelta = [string, 0, 2]; + +export type Delta = + | AddedDelta + | ModifiedDelta + | DeletedDelta + | ObjectDelta + | ArrayDelta + | MovedDelta + | TextDiffDelta + | undefined; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export interface Filter> { + (context: TContext): void; + filterName: string; +} diff --git a/libs/jsondiffpatch/test/examples/diffpatch.ts b/libs/jsondiffpatch/test/examples/diffpatch.ts new file mode 100644 index 000000000..f775822c8 --- /dev/null +++ b/libs/jsondiffpatch/test/examples/diffpatch.ts @@ -0,0 +1,1375 @@ +import type { Delta, Options } from "../../src"; + +interface Example { + name?: string; + options?: Options; + left: unknown; + right: unknown; + delta?: Delta; + reverse?: Delta; + exactReverse?: boolean; + noPatch?: boolean; + error?: RegExp; +} + +type ExampleGroup = Example[]; + +const exampleDate = () => new Date(2020, 10, 30, 15, 10, 3); + +const atomicValues: ExampleGroup = [ + // undefined + { + left: undefined, + right: undefined, + delta: undefined, + reverse: undefined, + }, + { + left: undefined, + right: null, + delta: [null], + reverse: [null, 0, 0], + }, + { + left: undefined, + right: false, + delta: [false], + reverse: [false, 0, 0], + }, + { + left: undefined, + right: true, + delta: [true], + reverse: [true, 0, 0], + }, + { + left: undefined, + right: 42, + delta: [42], + reverse: [42, 0, 0], + }, + { + left: undefined, + right: "some text", + delta: ["some text"], + reverse: ["some text", 0, 0], + }, + { + left: undefined, + right: exampleDate(), + delta: [exampleDate()], + reverse: [exampleDate(), 0, 0], + }, + { + left: undefined, + right: { + a: 1, + b: 2, + }, + delta: [ + { + a: 1, + b: 2, + }, + ], + reverse: [ + { + a: 1, + b: 2, + }, + 0, + 0, + ], + }, + { + left: undefined, + right: [1, 2, 3], + delta: [[1, 2, 3]], + reverse: [[1, 2, 3], 0, 0], + }, + { + left: undefined, + right(x: number) { + return x * x; + }, + error: /not supported/, + }, + + // null + { + left: null, + right: null, + delta: undefined, + reverse: undefined, + }, + { + left: null, + right: false, + delta: [null, false], + reverse: [false, null], + }, + { + left: null, + right: true, + delta: [null, true], + reverse: [true, null], + }, + { + left: null, + right: 42, + delta: [null, 42], + reverse: [42, null], + }, + { + left: null, + right: "some text", + delta: [null, "some text"], + reverse: ["some text", null], + }, + { + left: null, + right: exampleDate(), + delta: [null, exampleDate()], + reverse: [exampleDate(), null], + }, + { + left: null, + right: { + a: 1, + b: 2, + }, + delta: [ + null, + { + a: 1, + b: 2, + }, + ], + reverse: [ + { + a: 1, + b: 2, + }, + null, + ], + }, + { + left: null, + right(x: number) { + return x * x; + }, + error: /not supported/, + }, + + // false + { + left: false, + right: false, + delta: undefined, + reverse: undefined, + }, + { + left: false, + right: true, + delta: [false, true], + reverse: [true, false], + }, + { + left: false, + right: 42, + delta: [false, 42], + reverse: [42, false], + }, + { + left: false, + right: "some text", + delta: [false, "some text"], + reverse: ["some text", false], + }, + { + left: false, + right: exampleDate(), + delta: [false, exampleDate()], + reverse: [exampleDate(), false], + }, + { + left: false, + right: { + a: 1, + b: 2, + }, + delta: [ + false, + { + a: 1, + b: 2, + }, + ], + reverse: [ + { + a: 1, + b: 2, + }, + false, + ], + }, + { + left: false, + right: [1, 2, 3], + delta: [false, [1, 2, 3]], + reverse: [[1, 2, 3], false], + }, + { + left: false, + right(x: number) { + return x * x; + }, + error: /not supported/, + }, + + // true + { + left: true, + right: true, + delta: undefined, + reverse: undefined, + }, + { + left: true, + right: 42, + delta: [true, 42], + reverse: [42, true], + }, + { + left: true, + right: "some text", + delta: [true, "some text"], + reverse: ["some text", true], + }, + { + left: true, + right: exampleDate(), + delta: [true, exampleDate()], + reverse: [exampleDate(), true], + }, + { + left: true, + right: { + a: 1, + b: 2, + }, + delta: [ + true, + { + a: 1, + b: 2, + }, + ], + reverse: [ + { + a: 1, + b: 2, + }, + true, + ], + }, + { + left: true, + right: [1, 2, 3], + delta: [true, [1, 2, 3]], + reverse: [[1, 2, 3], true], + }, + { + left: true, + right(x: number) { + return x * x; + }, + error: /not supported/, + }, + + // number + { + name: "number -> same number", + left: 42, + right: 42, + delta: undefined, + reverse: undefined, + }, + { + left: 42, + right: -1, + delta: [42, -1], + reverse: [-1, 42], + }, + { + left: 42, + right: "some text", + delta: [42, "some text"], + reverse: ["some text", 42], + }, + { + left: 42, + right: exampleDate(), + delta: [42, exampleDate()], + reverse: [exampleDate(), 42], + }, + { + left: 42, + right: { + a: 1, + b: 2, + }, + delta: [ + 42, + { + a: 1, + b: 2, + }, + ], + reverse: [ + { + a: 1, + b: 2, + }, + 42, + ], + }, + { + left: 42, + right: [1, 2, 3], + delta: [42, [1, 2, 3]], + reverse: [[1, 2, 3], 42], + }, + { + left: 42, + right(x: number) { + return x * x; + }, + error: /not supported/, + }, + + // string + { + name: "string -> same string", + left: "some text", + right: "some text", + delta: undefined, + reverse: undefined, + }, + { + left: "some text", + right: "some fext", + delta: ["some text", "some fext"], + reverse: ["some fext", "some text"], + }, + { + left: "some text", + right: exampleDate(), + delta: ["some text", exampleDate()], + reverse: [exampleDate(), "some text"], + }, + { + left: "some text", + right: { + a: 1, + b: 2, + }, + delta: [ + "some text", + { + a: 1, + b: 2, + }, + ], + reverse: [ + { + a: 1, + b: 2, + }, + "some text", + ], + }, + { + left: "some text", + right: [1, 2, 3], + delta: ["some text", [1, 2, 3]], + reverse: [[1, 2, 3], "some text"], + }, + + // Date + { + name: "Date -> same Date", + left: exampleDate(), + right: exampleDate(), + delta: undefined, + reverse: undefined, + }, + { + left: exampleDate(), + right: new Date(2020, 5, 31, 15, 12, 30), + delta: [exampleDate(), new Date(2020, 5, 31, 15, 12, 30)], + reverse: [new Date(2020, 5, 31, 15, 12, 30), exampleDate()], + }, + { + left: exampleDate(), + right: { + a: 1, + b: 2, + }, + delta: [ + exampleDate(), + { + a: 1, + b: 2, + }, + ], + reverse: [ + { + a: 1, + b: 2, + }, + exampleDate(), + ], + }, + { + left: exampleDate(), + right: [1, 2, 3], + delta: [exampleDate(), [1, 2, 3]], + reverse: [[1, 2, 3], exampleDate()], + }, + { + left: exampleDate(), + right(x: number) { + return x * x; + }, + error: /not supported/, + }, + + // Function + { + name: "string -> Function", + left: "some text", + right(x: number) { + return x * x; + }, + error: /not supported/, + }, + + // RegExp + { + name: "RegExp -> RegExp", + left: /regex/g, + right: /another regex/gi, + delta: ["/regex/g", "/another regex/gi"], + reverse: ["/another regex/gi", "/regex/g"], + }, + + // object + { + name: "object -> same object", + left: { + a: 1, + b: 2, + }, + right: { + a: 1, + b: 2, + }, + delta: undefined, + reverse: undefined, + }, + { + left: { + a: 1, + b: 2, + }, + right: [1, 2, 3], + delta: [ + { + a: 1, + b: 2, + }, + [1, 2, 3], + ], + reverse: [ + [1, 2, 3], + { + a: 1, + b: 2, + }, + ], + }, + { + left: { + a: 1, + b: 2, + }, + right(x: number) { + return x * x; + }, + error: /not supported/, + }, + + // array + { + name: "array -> same array", + left: [1, 2, 3], + right: [1, 2, 3], + delta: undefined, + reverse: undefined, + }, + { + left: [1, 2, 3], + right(x: number) { + return x * x; + }, + error: /not supported/, + }, +]; + +const objects: ExampleGroup = [ + { + name: "first level", + left: { + a: 1, + b: 2, + }, + right: { + a: 42, + b: 2, + }, + delta: { + a: [1, 42], + }, + reverse: { + a: [42, 1], + }, + }, + { + name: "deep level", + left: { + a: { + j: { + k: { + l: { + m: { + n: { + o: 3, + }, + }, + }, + }, + }, + }, + b: 2, + }, + right: { + a: { + j: { + k: { + l: { + m: { + n: { + o: true, + }, + }, + }, + }, + }, + }, + b: 2, + }, + delta: { + a: { + j: { + k: { + l: { + m: { + n: { + o: [3, true], + }, + }, + }, + }, + }, + }, + }, + reverse: { + a: { + j: { + k: { + l: { + m: { + n: { + o: [true, 3], + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "multiple changes", + left: { + a: { + j: { + k: { + l: { + m: { + n: { + o: 3, + }, + }, + }, + }, + }, + }, + b: 2, + c: 5, + }, + right: { + a: { + j: { + k: { + l: { + m: { + n: { + o: 5, + w: 12, + }, + }, + }, + }, + }, + }, + b: 2, + }, + delta: { + a: { + j: { + k: { + l: { + m: { + n: { + o: [3, 5], + w: [12], + }, + }, + }, + }, + }, + }, + c: [5, 0, 0], + }, + reverse: { + a: { + j: { + k: { + l: { + m: { + n: { + o: [5, 3], + w: [12, 0, 0], + }, + }, + }, + }, + }, + }, + c: [5], + }, + }, + { + name: "key removed", + left: { + a: 1, + b: 2, + }, + right: { + a: 1, + }, + delta: { + b: [2, 0, 0], + }, + reverse: { + b: [2], + }, + }, + { + name: "hasOwnProperty", + left: { + hasOwnProperty: true, + }, + right: { + hasOwnProperty: true, + }, + }, +]; + +const arrays: ExampleGroup = [ + { + name: "simple values", + left: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + right: [1, 3, 4, 5, 8, 9, 9.1, 10], + delta: [ + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + [1, 3, 4, 5, 8, 9, 9.1, 10], + ], + reverse: [ + [1, 3, 4, 5, 8, 9, 9.1, 10], + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + ], + }, + { + name: "nested array", + left: { arr: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] }, + right: { arr: [1, 3, 4, 5, 8, 9, 9.1, 10] }, + delta: { + arr: [ + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + [1, 3, 4, 5, 8, 9, 9.1, 10], + ], + }, + reverse: { + arr: [ + [1, 3, 4, 5, 8, 9, 9.1, 10], + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + ], + }, + }, + { + name: "removed array", + left: { arr: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] }, + right: { arr2: [1, 3, 4, 5, 8, 9, 9.1, 10] }, + delta: { + arr: [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 0, 0], + arr2: [[1, 3, 4, 5, 8, 9, 9.1, 10]], + }, + reverse: { + arr2: [[1, 3, 4, 5, 8, 9, 9.1, 10], 0, 0], + arr: [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]], + }, + }, + { + name: "added block", + left: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + right: [1, 2, 3, 4, 5, 5.1, 5.2, 5.3, 6, 7, 8, 9, 10], + delta: [ + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + [1, 2, 3, 4, 5, 5.1, 5.2, 5.3, 6, 7, 8, 9, 10], + ], + reverse: [ + [1, 2, 3, 4, 5, 5.1, 5.2, 5.3, 6, 7, 8, 9, 10], + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + ], + }, + { + name: "movements", + left: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + right: [1, 2, 3, 7, 5, 6, 8, 9, 4, 10], + delta: [ + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + [1, 2, 3, 7, 5, 6, 8, 9, 4, 10], + ], + reverse: [ + [1, 2, 3, 7, 5, 6, 8, 9, 4, 10], + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + ], + }, + { + name: "movements(2)", + left: [1, 2, 3, 4], + right: [2, 4, 1, 3], + delta: [ + [1, 2, 3, 4], + [2, 4, 1, 3], + ], + reverse: [ + [2, 4, 1, 3], + [1, 2, 3, 4], + ], + exactReverse: false, + }, + { + name: "nested", + options: { + objectHash(obj: { id?: string }) { + if (obj && obj.id) { + return obj.id; + } + }, + }, + left: [ + 1, + 2, + { + id: 4, + width: 10, + }, + 4, + { + id: "five", + width: 4, + }, + 6, + 7, + 8, + 9, + 10, + ], + right: [ + 1, + 2, + { + id: 4, + width: 12, + }, + 4, + { + id: "five", + width: 4, + }, + 6, + 7, + 8, + 9, + 10, + ], + delta: [ + [ + 1, + 2, + { + id: 4, + width: 10, + }, + 4, + { + id: "five", + width: 4, + }, + 6, + 7, + 8, + 9, + 10, + ], + [ + 1, + 2, + { + id: 4, + width: 12, + }, + 4, + { + id: "five", + width: 4, + }, + 6, + 7, + 8, + 9, + 10, + ], + ], + reverse: [ + [ + 1, + 2, + { + id: 4, + width: 12, + }, + 4, + { + id: "five", + width: 4, + }, + 6, + 7, + 8, + 9, + 10, + ], + [ + 1, + 2, + { + id: 4, + width: 10, + }, + 4, + { + id: "five", + width: 4, + }, + 6, + 7, + 8, + 9, + 10, + ], + ], + }, + // { + // name: "nested with movement", + // options: { + // objectHash(obj: { id?: string }) { + // if (obj && obj.id) { + // return obj.id; + // } + // }, + // }, + // left: [ + // 1, + // 2, + // 4, + // { + // id: "five", + // width: 4, + // }, + // 6, + // 7, + // 8, + // { + // id: 4, + // width: 10, + // height: 3, + // }, + // 9, + // 10, + // ], + // right: [ + // 1, + // 2, + // { + // id: 4, + // width: 12, + // }, + // 4, + // { + // id: "five", + // width: 4, + // }, + // 6, + // 7, + // 8, + // 9, + // 10, + // ], + // delta: { + // _t: "a", + // 2: { + // width: [10, 12], + // height: [3, 0, 0], + // }, + // _7: ["", 2, 3], + // }, + // reverse: { + // _t: "a", + // 7: { + // width: [12, 10], + // height: [3], + // }, + // _2: ["", 7, 3], + // }, + // }, + // { + // name: "nested changes among array insertions and deletions", + // options: { + // objectHash(obj: { id?: string }) { + // if (obj && obj.id) { + // return obj.id; + // } + // }, + // }, + // left: [ + // { + // id: 1, + // }, + // { + // id: 2, + // }, + // { + // id: 4, + // }, + // { + // id: 5, + // }, + // { + // id: 6, + // inner: { + // property: "abc", + // }, + // }, + // { + // id: 7, + // }, + // { + // id: 8, + // }, + // { + // id: 10, + // }, + // { + // id: 11, + // }, + // { + // id: 12, + // }, + // ], + // right: [ + // { + // id: 3, + // }, + // { + // id: 4, + // }, + // { + // id: 6, + // inner: { + // property: "abcd", + // }, + // }, + // { + // id: 9, + // }, + // ], + // delta: { + // _t: "a", + // 0: [{ id: 3 }], + // 2: { + // inner: { + // property: ["abc", "abcd"], + // }, + // }, + // 3: [{ id: 9 }], + // _0: [{ id: 1 }, 0, 0], + // _1: [{ id: 2 }, 0, 0], + // _3: [{ id: 5 }, 0, 0], + // _5: [{ id: 7 }, 0, 0], + // _6: [{ id: 8 }, 0, 0], + // _7: [{ id: 10 }, 0, 0], + // _8: [{ id: 11 }, 0, 0], + // _9: [{ id: 12 }, 0, 0], + // }, + // reverse: { + // _t: "a", + // 0: [{ id: 1 }], + // 1: [{ id: 2 }], + // 3: [{ id: 5 }], + // 4: { + // inner: { + // property: ["abcd", "abc"], + // }, + // }, + // 5: [{ id: 7 }], + // 6: [{ id: 8 }], + // 7: [{ id: 10 }], + // 8: [{ id: 11 }], + // 9: [{ id: 12 }], + // _0: [{ id: 3 }, 0, 0], + // _3: [{ id: 9 }, 0, 0], + // }, + // }, + // { + // name: "nested change with item moved above", + // options: { + // objectHash(obj: { id?: string }) { + // if (obj && obj.id) { + // return obj.id; + // } + // }, + // }, + // left: [ + // { + // id: 1, + // }, + // { + // id: 2, + // }, + // { + // id: 3, + // inner: { + // property: "abc", + // }, + // }, + // { + // id: 4, + // }, + // { + // id: 5, + // }, + // { + // id: 6, + // }, + // ], + // right: [ + // { + // id: 1, + // }, + // { + // id: 2, + // }, + // { + // id: 6, + // }, + // { + // id: 3, + // inner: { + // property: "abcd", + // }, + // }, + // { + // id: 4, + // }, + // { + // id: 5, + // }, + // ], + // delta: { + // _t: "a", + // 3: { + // inner: { + // property: ["abc", "abcd"], + // }, + // }, + // _5: ["", 2, 3], + // }, + // reverse: { + // _t: "a", + // 2: { + // inner: { + // property: ["abcd", "abc"], + // }, + // }, + // _2: ["", 5, 3], + // }, + // }, + // { + // name: "nested change with item moved right above", + // options: { + // objectHash(obj: { id?: string }) { + // if (obj && obj.id) { + // return obj.id; + // } + // }, + // }, + // left: [ + // { + // id: 1, + // }, + // { + // id: 2, + // inner: { + // property: "abc", + // }, + // }, + // { + // id: 3, + // }, + // ], + // right: [ + // { + // id: 1, + // }, + // { + // id: 3, + // }, + // { + // id: 2, + // inner: { + // property: "abcd", + // }, + // }, + // ], + // delta: { + // _t: "a", + // 2: { + // inner: { + // property: ["abc", "abcd"], + // }, + // }, + // _2: ["", 1, 3], + // }, + // reverse: { + // _t: "a", + // 1: { + // inner: { + // property: ["abcd", "abc"], + // }, + // }, + // _2: ["", 1, 3], + // }, + // exactReverse: false, + // }, + // { + // name: "nested change with item moved right below", + // options: { + // objectHash(obj: { id?: string }) { + // if (obj && obj.id) { + // return obj.id; + // } + // }, + // }, + // left: [ + // { + // id: 1, + // }, + // { + // id: 2, + // }, + // { + // id: 3, + // inner: { + // property: "abc", + // }, + // }, + // { + // id: 4, + // }, + // ], + // right: [ + // { + // id: 2, + // }, + // { + // id: 3, + // inner: { + // property: "abcd", + // }, + // }, + // { + // id: 1, + // }, + // { + // id: 4, + // }, + // ], + // delta: { + // _t: "a", + // 1: { + // inner: { + // property: ["abc", "abcd"], + // }, + // }, + // _0: ["", 2, 3], + // }, + // reverse: { + // _t: "a", + // 2: { + // inner: { + // property: ["abcd", "abc"], + // }, + // }, + // _2: ["", 0, 3], + // }, + // }, + // { + // name: "nested with movements using custom objectHash", + // options: { + // objectHash(obj: { itemKey?: string }) { + // if (obj && obj.itemKey) { + // return obj.itemKey; + // } + // }, + // }, + // left: [ + // 1, + // 2, + // 4, + // { + // itemKey: "five", + // width: 4, + // }, + // 6, + // 7, + // 8, + // { + // itemKey: 4, + // width: 10, + // height: 3, + // }, + // 9, + // 10, + // ], + // right: [ + // 1, + // 2, + // { + // itemKey: 4, + // width: 12, + // }, + // 4, + // { + // itemKey: "five", + // width: 4, + // }, + // 6, + // 7, + // 8, + // 9, + // 10, + // ], + // delta: { + // _t: "a", + // 2: { + // width: [10, 12], + // height: [3, 0, 0], + // }, + // _7: ["", 2, 3], + // }, + // reverse: { + // _t: "a", + // 7: { + // width: [12, 10], + // height: [3], + // }, + // _2: ["", 7, 3], + // }, + // }, + { + name: "using property filter", + options: { + propertyFilter(name /*, context */) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-call + return name.slice(0, 1) !== "$"; + }, + }, + left: { + inner: { + $volatileData: 345, + $oldVolatileData: 422, + nonVolatile: 432, + }, + }, + right: { + inner: { + $volatileData: 346, + $newVolatileData: 32, + nonVolatile: 431, + }, + }, + delta: { + inner: { + nonVolatile: [432, 431], + }, + }, + reverse: { + inner: { + nonVolatile: [431, 432], + }, + }, + noPatch: true, + }, +]; + +const examples: Record = { + atomicValues, + objects, + arrays, +}; +export default examples; diff --git a/libs/jsondiffpatch/test/index.spec.ts b/libs/jsondiffpatch/test/index.spec.ts new file mode 100644 index 000000000..6e0571e8c --- /dev/null +++ b/libs/jsondiffpatch/test/index.spec.ts @@ -0,0 +1,84 @@ +import * as jsondiffpatch from "../src/index.js"; + +import examples from "./examples/diffpatch.js"; + +const DiffPatcher = jsondiffpatch.DiffPatcher; + +const valueDescription = (value: unknown) => { + if (value === null) { + return "null"; + } + if (typeof value === "boolean") { + return value.toString(); + } + if (value instanceof Date) { + return "Date"; + } + if (value instanceof RegExp) { + return "RegExp"; + } + if (Array.isArray(value)) { + return "array"; + } + if (typeof value === "string") { + if (value.length >= 60) { + return "large text"; + } + } + return typeof value; +}; + +describe("DiffPatcher", () => { + Object.keys(examples).forEach(groupName => { + const group = examples[groupName]; + describe(groupName, () => { + group.forEach(example => { + if (!example) { + return; + } + const name = example.name || `${valueDescription(example.left)} -> ${valueDescription(example.right)}`; + describe(name, () => { + let instance: jsondiffpatch.DiffPatcher; + beforeAll(function () { + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + instance = new DiffPatcher(example.options); + }); + if (example.error) { + it(`diff should fail with: ${example.error}`, function () { + expect(() => { + instance.diff(example.left, example.right); + }).toThrow(example.error); + }); + return; + } + it("can diff", function () { + const delta = instance.diff(example.left, example.right); + expect(delta).toEqual(example.delta); + }); + it("can diff backwards", function () { + const reverse = instance.diff(example.right, example.left); + expect(reverse).toEqual(example.reverse); + }); + if (!example.noPatch) { + it("can patch", function () { + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + const right = instance.patch(example.left, example.delta); + expect(right).toEqual(example.right); + }); + } + }); + }); + }); + }); + + describe("static shortcuts", () => { + it("diff", () => { + const delta = jsondiffpatch.diff(4, 5); + expect(delta).toEqual([4, 5]); + }); + it("patch", () => { + const right = jsondiffpatch.patch(4, [4, 5]); + expect(right).toEqual(5); + }); + }); +}); diff --git a/libs/jsondiffpatch/test/tsconfig.json b/libs/jsondiffpatch/test/tsconfig.json new file mode 100644 index 000000000..5f07a6ed9 --- /dev/null +++ b/libs/jsondiffpatch/test/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "es6", + "module": "node16", + "types": ["jest"], + "isolatedModules": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": false + } +} diff --git a/libs/jsondiffpatch/tsconfig.json b/libs/jsondiffpatch/tsconfig.json new file mode 100644 index 000000000..14340da01 --- /dev/null +++ b/libs/jsondiffpatch/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "ES2015", + "declaration": true, + "outDir": "dist", + "isolatedModules": true, + "moduleResolution": "node", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": false + }, + "include": ["src"] +} diff --git a/libs/juava/__tests__/id.test.ts b/libs/juava/__tests__/id.test.ts index 02f1e0450..98e5b595f 100644 --- a/libs/juava/__tests__/id.test.ts +++ b/libs/juava/__tests__/id.test.ts @@ -1,4 +1,4 @@ -import { checkHash, createHash, randomId } from "../src"; +import { randomId } from "../src"; test("id test", () => { const id1 = randomId(); diff --git a/libs/juava/__tests__/security.test.ts b/libs/juava/__tests__/security.test.ts index 216066155..426ef48a0 100644 --- a/libs/juava/__tests__/security.test.ts +++ b/libs/juava/__tests__/security.test.ts @@ -1,4 +1,4 @@ -import { createHash, checkHash, createAuthorized } from "../src/security"; +import { createHash, createAuthorized, checkHash, checkRawToken } from "../src/security"; test("security", () => { const password = "secretPassword"; @@ -11,9 +11,13 @@ test("security", () => { test("authorizer", () => { const hashedSecret = "215ef940-8f78-42bf-ab36-185090b9b62e"; const plaintextSecret = "af0e7958-5a10-4264-af4e-2516a630b602"; - const secrets = `${plaintextSecret},${createHash(hashedSecret)}`; - const auth = createAuthorized(secrets); - expect(auth(plaintextSecret)).toBe(true); + let auth = createAuthorized(createHash(hashedSecret), checkHash); + expect(auth(plaintextSecret)).toBe(false); expect(auth(hashedSecret)).toBe(true); expect(auth("wrong")).toBe(false); + + auth = createAuthorized(plaintextSecret, checkRawToken); + expect(auth(plaintextSecret)).toBe(true); + expect(auth(hashedSecret)).toBe(false); + expect(auth("wrong")).toBe(false); }); diff --git a/libs/juava/package.json b/libs/juava/package.json index b54b11af6..2685dca6c 100644 --- a/libs/juava/package.json +++ b/libs/juava/package.json @@ -12,16 +12,17 @@ "private": false, "scripts": { "compile": "tsc -p .", + "build": "pnpm compile", "test": "tsc -p . && jest --verbose" }, "devDependencies": { "@types/jest": "^29.1.1", - "@types/node": "^18.11.10", + "@types/node": "^18.15.3", "jest": "^29.1.2", "ts-jest": "29.0.5" }, "dependencies": { "lodash": "^4.17.21", - "tslib": "^2.4.0" + "tslib": "^2.6.3" } } diff --git a/libs/juava/src/asserts.ts b/libs/juava/src/asserts.ts index bc14f2930..599995769 100644 --- a/libs/juava/src/asserts.ts +++ b/libs/juava/src/asserts.ts @@ -18,7 +18,7 @@ export function assertTrue(obj: boolean | null | undefined, error?: ErrorLike } } -export function assertFalse(obj: boolean, error?: ErrorLike): asserts obj is false { +export function assertFalse(obj: boolean, error?: ErrorLike): asserts obj is false { if (obj === true) { throw toError(error || "Object is true"); } diff --git a/libs/juava/src/boolean.ts b/libs/juava/src/boolean.ts new file mode 100644 index 000000000..b4b218dfa --- /dev/null +++ b/libs/juava/src/boolean.ts @@ -0,0 +1,3 @@ +export function isTruish(val: any) { + return val === "true" || val === "1" || val === "yes" || val === true || val === 1; +} diff --git a/libs/juava/src/date.ts b/libs/juava/src/date.ts new file mode 100644 index 000000000..66e839366 --- /dev/null +++ b/libs/juava/src/date.ts @@ -0,0 +1,10 @@ +export function parseDate(dateStr: string | undefined, defaultValue: Date): Date { + if (!dateStr) { + return defaultValue; + } + const parsed = Date.parse(dateStr); + if (isNaN(parsed)) { + return defaultValue; + } + return new Date(parsed); +} diff --git a/libs/juava/src/debounce.ts b/libs/juava/src/debounce.ts new file mode 100644 index 000000000..b45cbcc05 --- /dev/null +++ b/libs/juava/src/debounce.ts @@ -0,0 +1,130 @@ +function debounce(func, wait) { + let lastArgs, lastThis, maxWait, result, timerId, lastCallTime; + + let lastInvokeTime = 0; + let leading = false; + let maxing = false; + let trailing = true; + + if (typeof func !== "function") { + throw new TypeError("Expected a function"); + } + wait = +wait || 0; + + function invokeFunc(time) { + const args = lastArgs; + const thisArg = lastThis; + + lastArgs = lastThis = undefined; + lastInvokeTime = time; + result = func.apply(thisArg, args); + return result; + } + + function startTimer(pendingFunc, wait) { + return setTimeout(pendingFunc, wait); + } + + function cancelTimer(id) { + clearTimeout(id); + } + + function leadingEdge(time) { + // Reset any `maxWait` timer. + lastInvokeTime = time; + // Start the timer for the trailing edge. + timerId = startTimer(timerExpired, wait); + // Invoke the leading edge. + return leading ? invokeFunc(time) : result; + } + + function remainingWait(time) { + const timeSinceLastCall = time - lastCallTime; + const timeSinceLastInvoke = time - lastInvokeTime; + const timeWaiting = wait - timeSinceLastCall; + + return maxing ? Math.min(timeWaiting, maxWait - timeSinceLastInvoke) : timeWaiting; + } + + function shouldInvoke(time) { + const timeSinceLastCall = time - lastCallTime; + const timeSinceLastInvoke = time - lastInvokeTime; + + // Either this is the first call, activity has stopped and we're at the + // trailing edge, the system time has gone backwards and we're treating + // it as the trailing edge, or we've hit the `maxWait` limit. + return ( + lastCallTime === undefined || + timeSinceLastCall >= wait || + timeSinceLastCall < 0 || + (maxing && timeSinceLastInvoke >= maxWait) + ); + } + + function timerExpired() { + const time = Date.now(); + if (shouldInvoke(time)) { + return trailingEdge(time); + } + // Restart the timer. + timerId = startTimer(timerExpired, remainingWait(time)); + } + + function trailingEdge(time) { + timerId = undefined; + + // Only invoke if we have `lastArgs` which means `func` has been + // debounced at least once. + if (trailing && lastArgs) { + return invokeFunc(time); + } + lastArgs = lastThis = undefined; + return result; + } + + function cancel() { + if (timerId !== undefined) { + cancelTimer(timerId); + } + lastInvokeTime = 0; + lastArgs = lastCallTime = lastThis = timerId = undefined; + } + + function flush() { + return timerId === undefined ? result : trailingEdge(Date.now()); + } + + function pending() { + return timerId !== undefined; + } + + function debounced(this: any, ...args) { + const time = Date.now(); + const isInvoking = shouldInvoke(time); + + lastArgs = args; + lastThis = this; + lastCallTime = time; + + if (isInvoking) { + if (timerId === undefined) { + return leadingEdge(lastCallTime); + } + if (maxing) { + // Handle invocations in a tight loop. + timerId = startTimer(timerExpired, wait); + return invokeFunc(lastCallTime); + } + } + if (timerId === undefined) { + timerId = startTimer(timerExpired, wait); + } + return result; + } + debounced.cancel = cancel; + debounced.flush = flush; + debounced.pending = pending; + return debounced; +} + +export { debounce }; diff --git a/libs/juava/src/id.ts b/libs/juava/src/id.ts index 96c974820..cc45a67f3 100644 --- a/libs/juava/src/id.ts +++ b/libs/juava/src/id.ts @@ -25,3 +25,8 @@ function randomChar(noDigits?: boolean) { } } } + +//sanitizes string for usage in file name +export function sanitize(name: string, replacement = "-") { + return name.replace(/[^a-zA-Z0-9]+/gi, replacement).toLowerCase(); +} diff --git a/libs/juava/src/index.ts b/libs/juava/src/index.ts index dd5bd0395..4b0f1931a 100644 --- a/libs/juava/src/index.ts +++ b/libs/juava/src/index.ts @@ -10,3 +10,9 @@ export * from "./rpc"; export * from "./singleton"; export * from "./cache"; export * from "./sql-params"; +export * from "./boolean"; +export * from "./number"; +export * from "./throttle"; +export * from "./objects"; +export * from "./strings"; +export * from "./date"; diff --git a/libs/juava/src/log.ts b/libs/juava/src/log.ts index 5b7ac21fd..75b4fc7da 100644 --- a/libs/juava/src/log.ts +++ b/libs/juava/src/log.ts @@ -1,3 +1,5 @@ +import * as process from "process"; + export type LogLevel = "debug" | "info" | "warn" | "error"; export type LoggerOpts = { @@ -15,8 +17,8 @@ function getComponent() { return undefined; } -let globalLogLevel: LogLevel = "info" as LogLevel; -let enableServerLogsColoring: boolean = true; +let globalLogLevel: LogLevel = (process.env.JUAVA_LOG_LEVEL || "info") as LogLevel; +let enableServerLogsColoring: boolean = !(process.env.CI === "1" || process.env.CI === "true"); let enableJsonFormat: boolean = false; export function setGlobalLogLevel(level: LogLevel) { @@ -34,23 +36,28 @@ export function setServerJsonFormat(enableJson: boolean) { export function getLog(_opts?: LoggerOpts | string): LogFactory { const opts = typeof _opts === "string" ? { component: _opts } : _opts || {}; const { level, component = getComponent() } = opts; + const minSeverity = levelSeverities[level || globalLogLevel || "info"]; return { atDebug(): LogMessageBuilder { - const minSeverity = levelSeverities[level || globalLogLevel || "info"]; return minSeverity <= levelSeverities.debug ? logMessageBuilder(component, "debug") : noopLogMessageBuilder; }, + inDebug(cb: (l: LogMessageBuilder) => void) { + if (minSeverity <= levelSeverities.debug) { + cb(logMessageBuilder(component, "debug")); + } + }, atError(): LogMessageBuilder { - const minSeverity = levelSeverities[level || globalLogLevel || "info"]; return minSeverity <= levelSeverities.error ? logMessageBuilder(component, "error") : noopLogMessageBuilder; }, atInfo(): LogMessageBuilder { - const minSeverity = levelSeverities[level || globalLogLevel || "info"]; return minSeverity <= levelSeverities.info ? logMessageBuilder(component, "info") : noopLogMessageBuilder; }, atWarn(): LogMessageBuilder { - const minSeverity = levelSeverities[level || globalLogLevel || "info"]; return minSeverity <= levelSeverities.warn ? logMessageBuilder(component, "warn") : noopLogMessageBuilder; }, + at(level: string): LogMessageBuilder { + return (this[`at${level.charAt(0).toUpperCase() + level.slice(1).toLowerCase()}`] || this.atInfo)(); + }, }; } @@ -78,21 +85,33 @@ const levelColors: Record = { function inBrowser() { // @ts-ignore - return typeof window !== "undefined"; + return typeof document !== "undefined" && typeof window !== "undefined"; } -function colorConsoleMessage(color: Color, str: string): string { - return !inBrowser() && enableServerLogsColoring && color !== undefined - ? str - .split("\n") - .map(line => `${colorsAnsi[color]}${line}\x1b[0m`) - .join("\n") - : str; -} +export const logFormat = { + bold(msg: string): string { + return msg; + }, + italic(msg: string): string { + return msg; + }, + color(color: Color, str: string): string { + //to implement bold and italic we should split string by [0m and apply bold/italic to each part + return !inBrowser() && enableServerLogsColoring && color !== undefined + ? str + .split("\n") + .map(line => `${colorsAnsi[color]}${line}\x1b[0m`) + .join("\n") + : str; + }, +}; + +//process.stdout is not available on Vercel's edge runtime +const writeln = console.log; function dispatch(msg: LogMessage) { const levelColor = levelColors[msg.level]; - let logPrefix = `${msg.component ? `[${msg.component}]` : ""}`; + let logPrefix = `${msg.component ? ` [${msg.component}]: ` : ": "}`; if (!enableJsonFormat) { const timeFormatted = msg.date.toISOString().split("T").join(" "); const levelFormatted = msg.level.toUpperCase().padEnd(5); @@ -101,9 +120,27 @@ function dispatch(msg: LogMessage) { if (inBrowser()) { const color = browserColors[levelColor || ""] || levelColor; const fullArgs = [...(msg.args || []), ...(msg.errorCause ? [msg.errorCause] : [])]; - console.log(`%c${logPrefix}: ${msg.message}`, `color: ${color}`, ...fullArgs); + console.log(`%c${logPrefix}${msg.message}`, `color: ${color}`, ...fullArgs); } else { - const lines = [...`${logPrefix}: ${msg.message}${msg.args ? " " + msg.args.join(" ") : ""}`.split("\n")]; + const lines = [ + ...`${logPrefix}${msg.message}${ + msg.args + ? " " + + msg.args + .map(a => { + if (typeof a === "object" && a !== null) { + try { + return JSON.stringify(a); + } catch (e) { + return String(a); + } + } + return String(a); + }) + .join(" ") + : "" + }`.split("\n"), + ]; if (msg.errorCause) { if (msg.errorCause.message && !msg.errorCause.stack) { lines.push("Error! - " + msg.errorCause.message); @@ -113,13 +150,11 @@ function dispatch(msg: LogMessage) { } } if (enableJsonFormat) { - process.stdout.write(JSON.stringify({ time: msg.date, level: msg.level, msg: lines.join("\n") })); - process.stdout.write("\n"); + writeln(JSON.stringify({ time: msg.date, level: msg.level, msg: lines.join("\n") })); } else { - const border = "│"; + const border = ""; // = "|"; const messageFormatted = lines.join(`\n${border} `); - process.stdout.write(colorConsoleMessage(levelColor, messageFormatted)); - process.stdout.write("\n"); + writeln(logFormat.color(levelColor, messageFormatted)); } } msg.dispatched = true; @@ -159,6 +194,8 @@ export type LogFactory = { atWarn(): LogMessageBuilder; atError(): LogMessageBuilder; atDebug(): LogMessageBuilder; + inDebug(cb: (l: LogMessageBuilder) => void): void; + at(level: string): any; }; export type LogMessageBuilder = { diff --git a/libs/juava/src/number.ts b/libs/juava/src/number.ts new file mode 100644 index 000000000..60a6e22f8 --- /dev/null +++ b/libs/juava/src/number.ts @@ -0,0 +1,23 @@ +export function parseNumber(val: string | undefined, defaultValue?: T): T | number { + if (val) { + const n = parseFloat(val); + if (!Number.isFinite(n)) { + return defaultValue as T | number; + } + return n; + } else { + return defaultValue as T | number; + } +} + +export function parseRequiredNumber(val: string | undefined, error?: string): number { + if (val) { + const n = parseFloat(val); + if (!Number.isFinite(n)) { + throw new Error(error || "Object is not a finite number"); + } + return n; + } else { + throw new Error(error || "Object is not defined"); + } +} diff --git a/libs/juava/src/objects.ts b/libs/juava/src/objects.ts new file mode 100644 index 000000000..eb251ff53 --- /dev/null +++ b/libs/juava/src/objects.ts @@ -0,0 +1,21 @@ +export function deepMerge(target: any, source: any) { + if (typeof source !== "object" || source === null) { + return source; + } + if (typeof target !== "object" || target === null) { + return source; + } + return Object.entries(source).reduce((acc, [key, value]) => { + acc[key] = deepMerge(target[key], value); + return acc; + }, target); +} + +export function isEqual(x: any, y: any) { + const ok = Object.keys, + tx = typeof x, + ty = typeof y; + return x && y && tx === "object" && tx === ty + ? ok(x).length === ok(y).length && ok(x).every(key => isEqual(x[key], y[key])) + : x === y; +} diff --git a/libs/juava/src/rpc.ts b/libs/juava/src/rpc.ts index 8ba56c2d3..656c146e5 100644 --- a/libs/juava/src/rpc.ts +++ b/libs/juava/src/rpc.ts @@ -38,7 +38,7 @@ export class ApiResponseError extends Error { public request: object; constructor(message: string, response: object, request: object) { - super(message + ": " + JSON.stringify(response)); + super(message); this.response = response; this.request = request; } @@ -58,6 +58,10 @@ export interface RpcFunc, Payload = an useFetch(fetchImpl: FetchType); } +function extractString(obj: any): string | undefined { + return typeof obj === "string" ? obj : undefined; +} + export const rpc: RpcFunc = async (url, { body, ...rest } = {}) => { const urlWithQuery = notEmpty(rest?.query) ? urlWithQueryString(url, rest?.query || {}) : url; @@ -86,17 +90,40 @@ export const rpc: RpcFunc = async (url, { body, ...rest } = {}) => { if (!result.ok) { let errorText = await getErrorText(result); const errorJson = tryJson(errorText); - const message = `Error ${result.status} on ${method} ${url}`; - console.log(message, errorJson); - throw typeof errorJson === "string" - ? new Error(`${message}: ${errorJson}`) - : new ApiResponseError(message, errorJson, { - url: urlWithQuery, - ...requestParams, - body: body || undefined, - }); + const defaultErrorMessage = `Error ${result.status} on ${method} ${url}`; + console.error(defaultErrorMessage, errorJson); + + //Try to extract meaningful error message from response. We don't need to include a full message since it will be visible + //in the logs. On the other hand, error message could be displayed in UI + const errorMessage = + extractString(errorJson.message) || + extractString(errorJson.error) || + extractString(errorJson.error?.error) || + `${result.status} ${result.statusText}`; + throw new ApiResponseError(errorMessage, typeof errorJson === "string" ? undefined : errorJson, { + url: urlWithQuery, + ...requestParams, + body: body || undefined, + }); + } + if ((result.headers.get("Content-Type") ?? "").startsWith("application/json")) { + return await parseJsonResponse(result, method, url); + } else if ((result.headers.get("Content-Type") ?? "").startsWith("application/x-ndjson")) { + const text = await result.text(); + return text + .split("\n") + .filter(line => line.length > 0) + .map(line => { + try { + return JSON.parse(line); + } catch (e) { + console.error(`Error parsing JSON line from ${method} ${url}: ${getErrorMessage(e)}`); + return undefined; + } + }); + } else { + return await result.text(); } - return await parseJsonResponse(result, method, url); }; let rpcFetchImpl: FetchType | undefined; diff --git a/libs/juava/src/security.ts b/libs/juava/src/security.ts index 8505cca10..39bea699e 100644 --- a/libs/juava/src/security.ts +++ b/libs/juava/src/security.ts @@ -1,8 +1,17 @@ const crypto = require("crypto"); +export const idHash32MaxValue = 2147483647; + const defaultSeed = "dea42a58-acf4-45af-85bb-e77e94bd5025"; -const globalSeed: string[] = (process.env.GLOBAL_HASH_SECRET || defaultSeed).split(",").map(s => s.trim()); +const globalSeed: string[] = ( + process.env.GLOBAL_HASH_SECRET || + process.env.CONSOLE_TOKEN_SECRET || + process.env.ROTOR_TOKEN_SECRET || + defaultSeed +) + .split(",") + .map(s => s.trim()); function hashInternal(secret: string, randomSeed: string, globalSeed: string) { return `${randomSeed}.${hash("sha512", secret + randomSeed + globalSeed)}`; @@ -14,6 +23,14 @@ export function hash(algorithm: string, value: string): string { return hash.digest("hex"); } +export function int32Hash(value) { + // Hash the value using SHA-256 (or another algorithm if desired) + const h = hash("sha256", value); + + // Convert the first 8 characters of the hash (or more) to an integer + return parseInt(h.substring(0, 8), 16) % idHash32MaxValue; +} + export function hint(key: string) { return key.substring(0, 3) + "*" + key.substring(key.length - 3); } @@ -23,7 +40,15 @@ export function createHash(secret: string): string { return hashInternal(secret, randomSeed, globalSeed[0]); } +export function checkRawToken(hashOrPlain: string, secret: string): boolean { + return secret === hashOrPlain; +} + export function checkHash(hash: string, secret: string): boolean { + if (hash.indexOf(".") < 0) { + console.error(`Invalid auth token: ${hash} Should be in format \$\{salt\}.\$\{hash\}`); + return false; + } const [randomSeed] = hash.split("."); return globalSeed.find(seed => hash === hashInternal(secret, randomSeed, seed)) !== undefined; } @@ -35,18 +60,39 @@ export function isValidSecret(secret: string): boolean { export type Authorizer = (secret: string) => boolean; /** - * Creates an authorizer agains a string that contains a comma separated list of + * Creates an authorizer against a string that contains a comma separated list of * tokens. Each token can be a plain string or a hash of a string. The hash is * something that contains '.'. */ -export function createAuthorized(tokens: string): Authorizer { +export function createAuthorized( + tokens: string, + checkFunc: (hashOrPlain: string, secret: string) => boolean +): Authorizer { const authorizers = tokens .split(",") .map(tok => tok.trim()) - .map(hashOrPlain => - hashOrPlain.indexOf(".") === -1 - ? (secret: string) => secret === hashOrPlain - : (secret: string) => checkHash(hashOrPlain, secret) - ); + .map(hashOrPlain => (secret: string) => checkFunc(hashOrPlain, secret)); return (secret: string) => authorizers.find(auth => auth(secret)) !== undefined; } + +export function encrypt(secret: string, iv: string, value: string): string { + const cipher = crypto.createCipheriv("aes-256-ctr", Buffer.from(secret), Buffer.from(iv)); + const encrypted = Buffer.concat([cipher.update(value), cipher.final()]); + return encrypted.toString("hex"); +} + +export function decrypt(secret: string, iv: string, value: string): string { + const decipher = crypto.createDecipheriv("aes-256-ctr", Buffer.from(secret), Buffer.from(iv)); + const decrypted = Buffer.concat([decipher.update(Buffer.from(value, "hex")), decipher.final()]); + return decrypted.toString(); +} + +export function hideSensitiveInfo(url: string) { + try { + const parsedUrl = new URL(url); + parsedUrl.password = "****"; + return parsedUrl.toString(); + } catch (e) { + return "***"; + } +} diff --git a/libs/juava/src/singleton.ts b/libs/juava/src/singleton.ts index 4e0de320a..cb97aa9c5 100644 --- a/libs/juava/src/singleton.ts +++ b/libs/juava/src/singleton.ts @@ -1,6 +1,6 @@ import { getErrorMessage, getLog, newError, requireDefined } from "./index"; -import { debounce } from "lodash"; import * as process from "process"; +import { debounce } from "./debounce"; const log = getLog("singleton"); export type CachedValue = ( @@ -19,9 +19,10 @@ export type CachedValue = ( export interface Singleton { (): T; waitInit: () => Promise; + close: () => Promise; } -function clearSingleton(globalName: string, cleanupFunc?: (t: T) => {}) { +function clearSingleton(globalName: string, cleanupFunc?: (t: T) => void) { log.atInfo().log(`️🚮🚮🚮 ${globalName} deleting stale singleton`); const cachedValue = global[`singletons_${globalName}`]; if (cachedValue?.success) { @@ -46,7 +47,18 @@ function getDisableServiceVar(globalName: string) { export function getSingleton( globalName: string, factory: () => T | Promise, - opts: { ttlSec: number; errorTtlSec?: number; cleanupFunc?: (t: T) => {} } = { ttlSec: 0, errorTtlSec: 0 } + opts: { + ttlSec?: number; + optional?: boolean; + silent?: boolean; + errorTtlSec?: number; + cleanupFunc?: (t: T) => void; + } = { + ttlSec: 0, + errorTtlSec: 0, + optional: false, + silent: false, + } ): Singleton { const disableServiceVar = getDisableServiceVar(globalName); if (process.env[disableServiceVar]) { @@ -67,26 +79,35 @@ export function getSingleton( success: true, value, debounceCleanup: - opts.ttlSec > 0 ? debounce(() => clearSingleton(globalName, opts.cleanupFunc), 1000 * opts.ttlSec) : () => {}, + opts.ttlSec && opts.ttlSec > 0 + ? debounce(() => clearSingleton(globalName, opts.cleanupFunc), 1000 * opts.ttlSec) + : () => {}, }; - log.atInfo().log(`️⚡️⚡️⚡️ ${globalName} connected in ${Date.now() - startedAtTs}ms!`); + if (!opts.silent) { + log.atInfo().log(`️⚡️⚡️⚡️ ${globalName} connected in ${Date.now() - startedAtTs}ms!`); + } return value; }; const handleError = (error: any): any => { - log.atError().log(`❌ ❌ ❌ ${globalName} connection failed. The application isn't functional.`, error); + if (!opts.optional) { + log.atError().log(`❌ ❌ ❌ ${globalName} connection failed. The application isn't functional.`, error); + } global[`singletons_${globalName}`] = { success: false, error, - debounceCleanup: - opts.errorTtlSec && opts.errorTtlSec > 0 - ? debounce(() => clearSingleton(globalName, opts.cleanupFunc), 1000 * opts.errorTtlSec) - : () => {}, + debounceCleanup: () => {}, }; - if (process.env.FAIL_ON_DB_CONNECTION_ERRORS === "true" || process.env.FAIL_ON_DB_CONNECTION_ERRORS === "1") { + if ( + !opts.optional && + (process.env.FAIL_ON_DB_CONNECTION_ERRORS === "true" || process.env.FAIL_ON_DB_CONNECTION_ERRORS === "1") + ) { log.atInfo().log("❌ ❌ ❌ Shutting down the application"); process.exit(1); } + if (opts.errorTtlSec && opts.errorTtlSec > 0) { + setTimeout(() => clearSingleton(globalName, opts.cleanupFunc), 1000 * opts.errorTtlSec); + } return error; }; @@ -95,32 +116,34 @@ export function getSingleton( cachedValue.debounceCleanup(); const result = () => cachedValue.value as T; result.waitInit = () => Promise.resolve(cachedValue.value as T); + result.close = () => { + if (opts.cleanupFunc) { + opts.cleanupFunc(cachedValue.value); + } + return Promise.resolve(); + }; return result; } else if (cachedValue && !cachedValue.success) { - cachedValue.debounceCleanup(); throw newError( `${globalName} failed during initialization: ${getErrorMessage(cachedValue.error)}`, cachedValue.error ); } log.atDebug().log(`Creating ${globalName} connection...`); - let newInstance: Promise | T; - const startedAtTs = Date.now(); + let newInstance: Promise | T | undefined; + let startedAtTs = Date.now(); try { newInstance = factory(); } catch (error) { handleError(error); - throw error; + const singleton: Partial> = () => Promise.reject(error); + singleton.waitInit = () => Promise.reject(error); + return singleton as Singleton; } if (newInstance instanceof Promise) { - const awaiter = newInstance.then(instance => handleSuccess(instance, startedAtTs)).catch(handleError); - const result = () => { - const globalInstance = requireDefined( - global[`singletons_${globalName}`], - `The ${globalName} connection isn't ready yet` - ); - globalInstance.debounceCleanup(); + const resultFunc = (globalInstance: any) => { if (globalInstance.success) { + globalInstance.debounceCleanup(); return globalInstance.value; } else { throw newError( @@ -129,12 +152,52 @@ export function getSingleton( ); } }; - result.waitInit = () => awaiter; + const result = () => { + const globalInstance = requireDefined( + global[`singletons_${globalName}`], + `The ${globalName} connection isn't ready yet` + ); + return resultFunc(globalInstance); + }; + result.waitInit = () => { + const globalInstance = global[`singletons_${globalName}`]; + if (globalInstance) { + return Promise.resolve(resultFunc(globalInstance)); + } else { + if (!(newInstance instanceof Promise)) { + startedAtTs = Date.now(); + newInstance = Promise.resolve(factory()); + } + return newInstance + .then(instance => handleSuccess(instance, startedAtTs)) + .catch(e => { + handleError(e); + throw e; + }) + .finally(() => { + newInstance = undefined; + }); + } + }; + result.close = () => { + const globalInstance = global[`singletons_${globalName}`]; + if (opts.cleanupFunc && globalInstance.success) { + opts.cleanupFunc(globalInstance.value); + } + return Promise.resolve(); + }; + result.waitInit().catch(e => {}); return result; } else { handleSuccess(newInstance, startedAtTs); const result = () => newInstance as T; result.waitInit = () => Promise.resolve(newInstance as T); + result.close = () => { + if (opts.cleanupFunc) { + opts.cleanupFunc(newInstance as T); + } + return Promise.resolve(); + }; return result; } } diff --git a/libs/juava/src/stopwatch.ts b/libs/juava/src/stopwatch.ts index 9e1e18955..06089124f 100644 --- a/libs/juava/src/stopwatch.ts +++ b/libs/juava/src/stopwatch.ts @@ -1,6 +1,7 @@ export type Stopwatch = { startedAt: number; elapsedMs(): number; + lapMs(): number; elapsedPretty(): string; }; @@ -15,6 +16,8 @@ function formatMs(ms: number) { } export function stopwatch(): Stopwatch { + let startedAt = Date.now(); + let lastLap = startedAt; return { elapsedPretty(): string { return formatMs(this.elapsedMs()); @@ -22,6 +25,12 @@ export function stopwatch(): Stopwatch { elapsedMs(): number { return Date.now() - this.startedAt; }, - startedAt: Date.now(), + lapMs(): number { + const now = Date.now(); + const lap = now - lastLap; + lastLap = now; + return lap; + }, + startedAt: startedAt, }; } diff --git a/libs/juava/src/strings.ts b/libs/juava/src/strings.ts new file mode 100644 index 000000000..05b360370 --- /dev/null +++ b/libs/juava/src/strings.ts @@ -0,0 +1,28 @@ +export function trimMiddle(str: string, maxLen: number, ellisis = "...") { + if (str.length <= maxLen) { + return str; + } else { + return str.substring(0, maxLen / 2 - (ellisis.length - 1)) + ellisis + str.substring(str.length - maxLen / 2 + 1); + } +} + +export function trimEnd(str: string, maxLen: number, ellisis = "...") { + if (str.length <= maxLen) { + return str; + } else { + return str.substring(0, maxLen - (ellisis.length - 1)) + ellisis; + } +} + +export function trimSuffix(str: string, suffix: string): string { + if (str.endsWith(suffix)) { + return str.slice(0, -suffix.length); + } + return str; +} +export function trimPrefix(str: string, prefix: string): string { + if (str.startsWith(prefix)) { + return str.slice(prefix.length); + } + return str; +} diff --git a/libs/juava/src/throttle.ts b/libs/juava/src/throttle.ts new file mode 100644 index 000000000..4b3caa7c9 --- /dev/null +++ b/libs/juava/src/throttle.ts @@ -0,0 +1,55 @@ +export interface Throttle { + throttle(): number; + fail: () => void; + success: () => void; +} + +export function noThrottle(): Throttle { + return { + throttle: () => 0, + fail: () => {}, + success: () => {}, + }; +} + +export function alwaysThrottle(): Throttle { + return { + throttle: () => 1, + fail: () => {}, + success: () => {}, + }; +} + +export function getThrottle(calculatePeriodMs: number): Throttle { + let previousThrottleTime = Date.now(); + let currentThrottle = 0; + let fails = 0; + let successes = 0; + + function recalculateThrottle() { + const total = fails + successes; + const now = Date.now(); + if (total > 100 || (total >= 10 && now - previousThrottleTime > calculatePeriodMs)) { + previousThrottleTime = now; + + const l = Math.min(100, total); + currentThrottle = Math.min(fails / total, (l - 1) / l); + fails = 0; + successes = 0; + } + } + + return { + fail: () => { + fails++; + recalculateThrottle(); + }, + success: () => { + successes++; + recalculateThrottle(); + }, + throttle: () => { + return currentThrottle; + }, + }; +} diff --git a/libs/juava/tsconfig.json b/libs/juava/tsconfig.json index 3ee0b8f53..cef1218c5 100644 --- a/libs/juava/tsconfig.json +++ b/libs/juava/tsconfig.json @@ -2,7 +2,6 @@ "compilerOptions": { "lib": ["es2015", "dom", "es2017"], "rootDir": "./src", - "outDir": "./dist", "declaration": true, "esModuleInterop": true, "noEmit": true diff --git a/package.json b/package.json index 157f8ec06..b1504fa3a 100644 --- a/package.json +++ b/package.json @@ -3,18 +3,23 @@ "version": "0.0.0", "private": false, "scripts": { - "format:check": "prettier --check --config ./.prettierrc.json --ignore-path ./.prettierignore .", - "format": "prettier --write --config ./.prettierrc.json --ignore-path ./.prettierignore .", + "build-scripts": "pnpm --filter ./cli/build-scripts run exec", + "format:check": "prettier --ignore-unknown --check --config ./.prettierrc.json --ignore-path ./.prettierignore $(git diff --name-only --diff-filter d | xargs)", + "format:check:all": "prettier --check --config ./.prettierrc.json --ignore-path ./.prettierignore .", + "format": "prettier --ignore-unknown --write --config ./.prettierrc.json --ignore-path ./.prettierignore $(git diff --name-only --diff-filter d | xargs)", + "format:all": "prettier --write --config ./.prettierrc.json --ignore-path ./.prettierignore .", "tool:hash": "turbo run tool:hash --", + "ci:no-test": "pnpm format:check:all && pnpm lint && pnpm build", + "ci:all": "pnpm ci:no-test && pnpm test", "prepare": "husky install", "pre-commit": "pnpm format:check", "build": "turbo run build", "ee-api:dev": "dotenv -e .env.local -- turbo run ee-api:dev", - "compile": "turbo run compile", "console:dev": "dotenv -e .env.local -- turbo run console:dev", "rotor:dev": "dotenv -e .env.local -- turbo run rotor:dev", - "console:start": "dotenv -e .env.local -- pnpm run --filter=console console:start", + "console:start": "dotenv -e .env.local -- turbo run console:start", "console:db-prepare": "dotenv -e .env.local -- pnpm run --filter=console db:update-schema", + "console:db-prepare-force": "dotenv -e .env.local -- pnpm run --filter=console db:update-schema-force", "dev": "dotenv -e .env.local -- turbo run dev", "test": "turbo run test", "jitsu-cli": "turbo run build --filter=jitsu-cli && node --enable-source-maps core/jitsu-cli/lib/index.js", @@ -22,26 +27,57 @@ "factory-reset": "pnpm clean ; pnpm clean:turbo ; rm -rf `find . -type d -name node_modules`", "clean": "turbo run clean || exit 0", "lint": "turbo run lint --", - "release": "monorel --filter ./libs/jitsu-js --filter ./libs/jitsu-react --npm-tag latest", - "release:canary": "monorel --filter ./libs/jitsu-js --filter ./libs/jitsu-react --version '1.1.0-canary.{rev}.{time}' --npm-tag canary --git-tag 'canary-v{version}' --push-tag --publish" + "release": "pnpm build && pnpm test && monorel --filter ./types/protocols --filter ./cli/jitsu-cli --filter ./libs/functions --filter ./libs/jitsu-js --filter ./libs/jitsu-react --npm-tag latest --git-tag 'jitsu-js-libs-v{version}' --push-tag --version '1.10.4'", + "release:build-scripts": "pnpm build --filter ./cli/build-scripts && monorel --filter ./cli/build-scripts --npm-tag latest --version 0.0.4-dev.{rev}.{time} --publish --git-tag 'jitsu-build-scripts-v{version}'", + "release:canary": "monorel --filter ./types/protocols --filter ./cli/jitsu-cli --filter ./libs/functions --filter ./libs/jitsu-js --filter ./libs/jitsu-react --version '1.10.5-canary.{rev}.{time}' --npm-tag canary --git-tag 'jitsu-js-libs-canary-v{version}' --push-tag" }, "devDependencies": { + "@playwright/test": "1.39.0", "dotenv-cli": "^6.0.0", "husky": "^8.0.3", "monorel": "0.4.2", "prettier": "^2.8.7", - "ts-node": "~10.8.2", + "ts-node": "^10.9.2", "turbo": "~1.2.16", - "typescript": "^4.9.5", - "vercel": "28.18.5" + "typescript": "^5.6.3" }, "engines": { "yarn": ">=1000", "pnpm": ">=8", "npm": ">=1000", - "node": "16.x" + "node": "18.x" + }, + "pnpm": { + "overrides": { + "form-data": "^4.0.4", + "zstd-napi": "^0.0.10", + "nth-check": "2.1.1", + "nanoid": "3.3.8", + "postcss": "^8.4.47", + "elliptic": "^6.6.1", + "cross-spawn": "^7.0.6", + "path-to-regexp": "^0.1.12" + }, + "onlyBuiltDependencies": [ + "@confluentinc/kafka-javascript", + "@firebase/util", + "@mongodb-js/zstd", + "@prisma/client", + "@prisma/engines", + "@swc/core", + "core-js", + "core-js-pure", + "cpu-features", + "esbuild", + "isolated-vm", + "prisma", + "protobufjs", + "sharp", + "ssh2", + "turbo", + "zstd-napi" + ] }, - "packageManager": "pnpm@8.2.0", "workspaces": [ "examples/*", "e2e", @@ -51,4 +87,4 @@ "cli/*", "libs/*" ] -} \ No newline at end of file +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5c57ef92e..8f5de4466 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,9 +1,26 @@ -lockfileVersion: '6.0' +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +overrides: + form-data: ^4.0.4 + zstd-napi: ^0.0.10 + nth-check: 2.1.1 + nanoid: 3.3.8 + postcss: ^8.4.47 + elliptic: ^6.6.1 + cross-spawn: ^7.0.6 + path-to-regexp: ^0.1.12 importers: .: devDependencies: + '@playwright/test': + specifier: 1.39.0 + version: 1.39.0 dotenv-cli: specifier: ^6.0.0 version: 6.0.0 @@ -15,62 +32,219 @@ importers: version: 0.4.2 prettier: specifier: ^2.8.7 - version: 2.8.7 + version: 2.8.8 ts-node: - specifier: ~10.8.2 - version: 10.8.2(@types/node@18.15.11)(typescript@4.9.5) + specifier: ^10.9.2 + version: 10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@22.14.1)(typescript@5.6.3) turbo: specifier: ~1.2.16 version: 1.2.16 typescript: - specifier: ^4.9.5 - version: 4.9.5 - vercel: - specifier: 28.18.5 - version: 28.18.5(@types/node@18.15.11)(ts-node@10.8.2) + specifier: ^5.6.3 + version: 5.6.3 - cli/jitsu-cli: + cli/build-scripts: dependencies: - chalk: - specifier: ^5.0.1 - version: 5.2.0 + boxen: + specifier: ^7.1.1 + version: 7.1.1 + colorette: + specifier: ^2.0.20 + version: 2.0.20 + semver: + specifier: ^7.5.4 + version: 7.6.3 + simple-git: + specifier: ^3.22.0 + version: 3.27.0 + string-width: + specifier: ^7.0.0 + version: 7.2.0 + tslib: + specifier: ^2.6.3 + version: 2.8.0 + devDependencies: + '@babel/preset-env': + specifier: ^7.23.2 + version: 7.26.0(@babel/core@7.26.10) + '@babel/preset-typescript': + specifier: ^7.23.2 + version: 7.26.0(@babel/core@7.26.10) + '@types/node': + specifier: ^18.15.3 + version: 18.19.61 + '@types/semver': + specifier: ^7.5.6 + version: 7.5.8 + babel-loader: + specifier: ^9.1.3 + version: 9.2.1(@babel/core@7.26.10)(webpack@5.99.5) + commander: + specifier: ^11.0.0 + version: 11.1.0 juava: specifier: workspace:* version: link:../../libs/juava - minimist: - specifier: ^1.2.6 - version: 1.2.8 + webpack: + specifier: ^5.99.5 + version: 5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(webpack-cli@6.0.1) + webpack-cli: + specifier: ^6.0.1 + version: 6.0.1(webpack@5.99.5) + + cli/jitsu-cli: + dependencies: + figlet: + specifier: ^1.6.0 + version: 1.8.0 + jest-cli: + specifier: ^29.7.0 + version: 29.7.0(@types/node@18.19.61)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)) tslib: - specifier: ^2.4.0 - version: 2.5.0 + specifier: ^2.6.3 + version: 2.8.0 + typescript: + specifier: ^5.6.3 + version: 5.6.3 devDependencies: + '@babel/preset-env': + specifier: ^7.23.2 + version: 7.26.0(@babel/core@7.26.0) + '@babel/preset-typescript': + specifier: ^7.23.2 + version: 7.26.0(@babel/core@7.26.0) + '@jitsu/functions-lib': + specifier: workspace:* + version: link:../../libs/functions + '@jitsu/protocols': + specifier: workspace:* + version: link:../../types/protocols + '@rollup/plugin-commonjs': + specifier: ^28.0.2 + version: 28.0.2(rollup@3.29.5) + '@rollup/plugin-json': + specifier: ^6.0.0 + version: 6.1.0(rollup@3.29.5) + '@rollup/plugin-node-resolve': + specifier: ^16.0.0 + version: 16.0.0(rollup@3.29.5) + '@rollup/plugin-terser': + specifier: ^0.4.3 + version: 0.4.4(rollup@3.29.5) + '@rollup/plugin-typescript': + specifier: ^11.1.3 + version: 11.1.6(rollup@3.29.5)(tslib@2.8.0)(typescript@5.6.3) '@types/chalk': specifier: ^2.2.0 - version: 2.2.0 - '@types/minimist': - specifier: ^1.2.2 - version: 1.2.2 + version: 2.2.4 + '@types/commander': + specifier: ^2.12.2 + version: 2.12.5 + '@types/express': + specifier: ^4.17.21 + version: 4.17.21 + '@types/inquirer': + specifier: ^9.0.3 + version: 9.0.7 + '@types/jest': + specifier: ^29.5.5 + version: 29.5.14 + '@types/lodash': + specifier: ^4.14.198 + version: 4.17.13 + '@types/node': + specifier: ^18.15.3 + version: 18.19.61 + '@types/webpack': + specifier: ^5.28.5 + version: 5.28.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(webpack-cli@6.0.1(webpack@5.99.5)) + '@webpack-cli/generators': + specifier: ^3.0.7 + version: 3.0.7(encoding@0.1.13)(mem-fs@2.3.0)(prettier@2.8.8)(webpack-cli@6.0.1(webpack@5.99.5))(webpack@5.99.5) + babel-loader: + specifier: ^9.1.3 + version: 9.2.1(@babel/core@7.26.0)(webpack@5.99.5) + chalk: + specifier: ^5.3.0 + version: 5.3.0 + commander: + specifier: ^11.0.0 + version: 11.1.0 + cross-fetch: + specifier: ^4.0.0 + version: 4.0.0(encoding@0.1.13) + cuid: + specifier: ^2.1.8 + version: 2.1.8 + etag: + specifier: ^1.8.1 + version: 1.8.1 + inquirer: + specifier: ^9.2.11 + version: 9.3.7 + jest: + specifier: ^29.7.0 + version: 29.7.0(@types/node@18.19.61)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)) + json5: + specifier: ^2.2.3 + version: 2.2.3 + juava: + specifier: workspace:* + version: link:../../libs/juava + lodash: + specifier: ^4.17.21 + version: 4.17.21 + node-fetch: + specifier: ^3.3.2 + version: 3.3.2 + node-loader: + specifier: ^2.0.0 + version: 2.0.0(webpack@5.99.5) + prismjs: + specifier: ^1.30.0 + version: 1.30.0 + rollup: + specifier: ^3.29.5 + version: 3.29.5 + semver: + specifier: ^7.5.4 + version: 7.6.3 + ts-jest: + specifier: ^29.1.1 + version: 29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(jest@29.7.0(@types/node@18.19.61)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)))(typescript@5.6.3) + ts-loader: + specifier: ^9.5.1 + version: 9.5.1(typescript@5.6.3)(webpack@5.99.5) + ts-node: + specifier: ^10.9.2 + version: 10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3) + webpack: + specifier: ^5.99.5 + version: 5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(webpack-cli@6.0.1) + webpack-cli: + specifier: ^6.0.1 + version: 6.0.1(webpack@5.99.5) e2e: devDependencies: '@jest/globals': specifier: ^29.3.1 - version: 29.5.0 + version: 29.7.0 '@jitsu-internal/console': specifier: workspace:* version: link:../webapps/console '@types/jest': specifier: ^29.1.1 - version: 29.5.0 + version: 29.5.14 find-free-port: specifier: ^2.0.0 version: 2.0.0 ioredis: - specifier: ^5.2.3 - version: 5.3.1 + specifier: ^5.3.2 + version: 5.4.1 jest: specifier: ^29.3.1 - version: 29.5.0(@types/node@18.15.11)(ts-node@10.8.2) + version: 29.7.0(@types/node@22.14.1)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@22.14.1)(typescript@5.6.3)) json5: specifier: ^2.1.0 version: 2.2.3 @@ -82,13 +256,13 @@ importers: version: 0.6.2 testcontainers: specifier: ^9.0.0 - version: 9.4.0 + version: 9.12.0(encoding@0.1.13) ts-jest: specifier: 29.0.5 - version: 29.0.5(@babel/core@7.21.4)(esbuild@0.16.3)(jest@29.5.0)(typescript@4.9.5) + version: 29.0.5(@babel/core@7.26.10)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.10))(jest@29.7.0(@types/node@22.14.1)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@22.14.1)(typescript@5.6.3)))(typescript@5.6.3) tslib: - specifier: ^2.4.0 - version: 2.5.0 + specifier: ^2.6.3 + version: 2.8.0 examples/react-app: dependencies: @@ -96,54 +270,81 @@ importers: specifier: workspace:* version: link:../../libs/jitsu-react '@types/node': - specifier: ^16.18.3 - version: 16.18.23 + specifier: ^18.15.3 + version: 18.19.61 lodash: specifier: ^4.17.21 version: 4.17.21 react: - specifier: ^18.2.0 - version: 18.2.0 + specifier: ^18.3.1 + version: 18.3.1 react-dom: - specifier: ^18.2.0 - version: 18.2.0(react@18.2.0) + specifier: ^18.3.1 + version: 18.3.1(react@18.3.1) + react-router: + specifier: ^6.25.1 + version: 6.27.0(react@18.3.1) react-router-dom: - specifier: ^6.4.3 - version: 6.10.0(react-dom@18.2.0)(react@18.2.0) - react-scripts: - specifier: ^5.1.0-next.14 - version: 5.1.0-next.14(@babel/plugin-syntax-flow@7.21.4)(@babel/plugin-transform-react-jsx@7.21.0)(esbuild@0.16.3)(react@18.2.0)(ts-node@10.8.2)(typescript@4.9.5) + specifier: ^6.25.1 + version: 6.27.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) web-vitals: specifier: ^2.1.4 version: 2.1.4 devDependencies: '@types/lodash': specifier: ^4.14.185 - version: 4.14.192 + version: 4.17.13 '@types/react': - specifier: 18.0.28 - version: 18.0.28 + specifier: ^18.3.3 + version: 18.3.3 '@types/react-dom': - specifier: 18.0.11 - version: 18.0.11 + specifier: ^18.3.0 + version: 18.3.0 + react-scripts: + specifier: ^5.0.1 + version: 5.0.1(@babel/plugin-syntax-flow@7.26.0(@babel/core@7.26.0))(@babel/plugin-transform-react-jsx@7.25.9(@babel/core@7.26.0))(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/babel__core@7.20.5)(@types/webpack@5.28.5(@swc/core@1.11.10(@swc/helpers@0.5.15)))(eslint@8.57.1)(react@18.3.1)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3))(type-fest@3.13.1)(typescript@5.6.3)(webpack-hot-middleware@2.26.1) tailwindcss: - specifier: ^3.0.24 - version: 3.3.1(postcss@8.4.22)(ts-node@10.8.2) + specifier: ^3.4.14 + version: 3.4.14(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)) typescript: - specifier: ^4.9.3 - version: 4.9.5 + specifier: ^5.6.3 + version: 5.6.3 libs/core-functions: dependencies: - '@swc/core': - specifier: ^1.3.40 - version: 1.3.49 - '@swc/wasm': - specifier: ^1.3.40 - version: 1.3.49 + '@amplitude/ua-parser-js': + specifier: ^0.7.33 + version: 0.7.33 + '@clickhouse/client': + specifier: ^1.10.1 + version: 1.10.1 + '@hubspot/api-client': + specifier: ^11.1.0 + version: 11.2.0(encoding@0.1.13) + '@mongodb-js/zstd': + specifier: ^2.0.1 + version: 2.0.1 agentkeepalive: specifier: 4.3.0 version: 4.3.0 + axios: + specifier: 1.8.2 + version: 1.8.2 + dayjs: + specifier: ^1.11.10 + version: 1.11.13 + google-ads-api: + specifier: ^17.1.0-rest-beta + version: 17.1.0-rest-beta(encoding@0.1.13) + ioredis: + specifier: ^5.3.2 + version: 5.4.1 + ip: + specifier: ^2.0.1 + version: 2.0.1 + isolated-vm: + specifier: 6.0.0 + version: 6.0.0 juava: specifier: workspace:* version: link:../juava @@ -151,124 +352,184 @@ importers: specifier: ^4.17.21 version: 4.17.21 mongodb: - specifier: ^5.1.0 - version: 5.2.0 + specifier: ^6.16.0 + version: 6.16.0(@mongodb-js/zstd@2.0.1)(socks@2.8.3) + node-cache: + specifier: ^5.1.2 + version: 5.1.2 + node-sql-parser: + specifier: ^5.3.8 + version: 5.3.8 + parse-duration: + specifier: ^1.1.2 + version: 1.1.2 posthog-node: - specifier: ^2.4.0 - version: 2.6.0 + specifier: ^4.2.1 + version: 4.2.1 tslib: - specifier: ^2.4.0 - version: 2.5.0 - vm2: - specifier: ^3.9.14 - version: 3.9.16 + specifier: ^2.6.3 + version: 2.8.0 + undici: + specifier: ^7.11.0 + version: 7.11.0 zod: - specifier: 3.21.4 - version: 3.21.4 + specifier: ^3.23.8 + version: 3.23.8 devDependencies: + '@jitsu/functions-lib': + specifier: workspace:* + version: link:../functions '@jitsu/protocols': specifier: workspace:* version: link:../../types/protocols '@jitsu/sdk-js': - specifier: ^3.1.3 + specifier: ^3.1.5 version: 3.1.5 '@types/jest': - specifier: ^29.1.1 - version: 29.5.0 + specifier: ^29.5.12 + version: 29.5.14 '@types/lodash': specifier: ^4.14.185 - version: 4.14.192 + version: 4.17.13 '@types/node': specifier: ^18.15.3 - version: 18.15.11 + version: 18.19.61 + express: + specifier: ^4.21.2 + version: 4.21.2 jest: - specifier: ^29.5.0 - version: 29.5.0(@types/node@18.15.11)(ts-node@10.8.2) + specifier: ^29.7.0 + version: 29.7.0(@types/node@18.19.61)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@22.14.1)(typescript@5.6.3)) json5: specifier: ^2.1.0 version: 2.2.3 node-fetch-commonjs: - specifier: ^3.2.4 - version: 3.2.4 + specifier: ^3.3.2 + version: 3.3.2 + ts-jest: + specifier: ^29.2.3 + version: 29.2.5(@babel/core@7.26.10)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.10))(jest@29.7.0(@types/node@18.19.61)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@22.14.1)(typescript@5.6.3)))(typescript@5.6.3) + + libs/functions: + devDependencies: + '@jitsu/protocols': + specifier: workspace:* + version: link:../../types/protocols + '@jitsu/sdk-js': + specifier: ^3.1.5 + version: 3.1.5 + '@rollup/plugin-commonjs': + specifier: ^28.0.2 + version: 28.0.2(rollup@4.24.3) + '@rollup/plugin-node-resolve': + specifier: ^16.0.0 + version: 16.0.0(rollup@4.24.3) + '@rollup/plugin-typescript': + specifier: ^11.1.5 + version: 11.1.6(rollup@4.24.3)(tslib@2.8.0)(typescript@5.6.3) + '@types/jest': + specifier: ^29.1.1 + version: 29.5.14 + '@types/node': + specifier: ^18.15.3 + version: 18.19.61 + jest: + specifier: ^29.1.2 + version: 29.7.0(@types/node@18.19.61)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@22.14.1)(typescript@5.6.3)) + rollup: + specifier: ^4.24.3 + version: 4.24.3 ts-jest: specifier: 29.0.5 - version: 29.0.5(@babel/core@7.21.4)(esbuild@0.16.3)(jest@29.5.0)(typescript@4.9.5) + version: 29.0.5(@babel/core@7.26.10)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.10))(jest@29.7.0(@types/node@18.19.61)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@22.14.1)(typescript@5.6.3)))(typescript@5.6.3) + tslib: + specifier: ^2.6.3 + version: 2.8.0 libs/jitsu-js: dependencies: - analytics: - specifier: ^0.8.1 - version: 0.8.1(@types/dlv@1.1.2) - devDependencies: '@jitsu/protocols': specifier: workspace:* version: link:../../types/protocols + analytics: + specifier: 0.8.9 + version: 0.8.9(@types/dlv@1.1.4) + devDependencies: '@playwright/test': - specifier: 1.31.2 - version: 1.31.2 + specifier: 1.39.0 + version: 1.39.0 '@rollup/plugin-commonjs': - specifier: ^23.0.2 - version: 23.0.7(rollup@3.20.2) + specifier: ^28.0.2 + version: 28.0.2(rollup@3.29.5) '@rollup/plugin-json': specifier: ^5.0.1 - version: 5.0.2(rollup@3.20.2) + version: 5.0.2(rollup@3.29.5) '@rollup/plugin-multi-entry': specifier: ^6.0.0 - version: 6.0.0(rollup@3.20.2) + version: 6.0.1(rollup@3.29.5) '@rollup/plugin-node-resolve': - specifier: ^15.0.1 - version: 15.0.2(rollup@3.20.2) + specifier: ^16.0.0 + version: 16.0.0(rollup@3.29.5) '@rollup/plugin-replace': specifier: ^5.0.1 - version: 5.0.2(rollup@3.20.2) + version: 5.0.7(rollup@3.29.5) '@rollup/plugin-terser': specifier: ^0.1.0 - version: 0.1.0(rollup@3.20.2) + version: 0.1.0(rollup@3.29.5) '@segment/analytics-next': - specifier: ^1.46.0 - version: 1.51.3 + specifier: ^1.75.0 + version: 1.75.0(encoding@0.1.13) '@types/chalk': specifier: ^2.2.0 - version: 2.2.0 + version: 2.2.4 '@types/jest': specifier: ^29.2.0 - version: 29.5.0 + version: 29.5.14 '@types/node': - specifier: ^16.11.7 - version: 16.18.23 + specifier: ^18.15.3 + version: 18.19.61 chalk: specifier: ^4.1.2 version: 4.1.2 cookie-parser: - specifier: ^1.4.6 - version: 1.4.6 + specifier: ^1.4.7 + version: 1.4.7 ejs: specifier: ^3.1.8 - version: 3.1.9 + version: 3.1.10 express: - specifier: ^4.18.2 - version: 4.18.2 + specifier: ^4.21.2 + version: 4.21.2 jest: specifier: ^29.2.2 - version: 29.5.0(@types/node@16.18.23)(ts-node@10.8.2) + version: 29.7.0(@types/node@18.19.61)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@22.14.1)(typescript@5.6.3)) + jsondiffpatch: + specifier: workspace:* + version: link:../jsondiffpatch node-fetch-commonjs: - specifier: ^3.2.4 - version: 3.2.4 + specifier: ^3.3.2 + version: 3.3.2 node-forge: specifier: ^1.3.1 version: 1.3.1 raw-loader: specifier: ^4.0.2 - version: 4.0.2(webpack@5.79.0) + version: 4.0.2(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))) rollup: - specifier: ^3.2.5 - version: 3.20.2 + specifier: ^3.29.5 + version: 3.29.5 + rollup-plugin-dts: + specifier: ^6.2.1 + version: 6.2.1(rollup@3.29.5)(typescript@5.6.3) ts-jest: specifier: 29.0.5 - version: 29.0.5(@babel/core@7.21.4)(esbuild@0.16.3)(jest@29.5.0)(typescript@4.9.5) + version: 29.0.5(@babel/core@7.26.10)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.10))(jest@29.7.0(@types/node@18.19.61)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@22.14.1)(typescript@5.6.3)))(typescript@5.6.3) + tslib: + specifier: ^2.6.3 + version: 2.8.0 typescript: - specifier: ^4.9.5 - version: 4.9.5 + specifier: ^5.6.3 + version: 5.6.3 libs/jitsu-react: dependencies: @@ -276,42 +537,72 @@ importers: specifier: workspace:* version: link:../jitsu-js '@types/react': - specifier: 15.x || 16.x || 17.x || 18.x - version: 18.0.28 + specifier: 15.x || 16.x || 17.x || 18.x || 19.x + version: 18.3.3 react: - specifier: 15.x || 16.x || 17.x || 18.x - version: 18.2.0 + specifier: 15.x || 16.x || 17.x || 18.x || 19.x + version: 18.3.1 react-router-dom: - specifier: 5.x || 6.x - version: 6.10.0(react-dom@18.2.0)(react@18.2.0) + specifier: 5.x || 6.x || 7.x + version: 6.27.0(react-dom@19.0.0(react@18.3.1))(react@18.3.1) devDependencies: '@babel/plugin-transform-regenerator': - specifier: ^7.20.5 - version: 7.20.5(@babel/core@7.21.4) + specifier: ^7.22.10 + version: 7.25.9(@babel/core@7.26.0) '@testing-library/jest-dom': - specifier: ^5.16.5 - version: 5.16.5 + specifier: ^6.1.3 + version: 6.6.2 '@testing-library/react': specifier: ^13.4.0 - version: 13.4.0(react-dom@18.2.0)(react@18.2.0) + version: 13.4.0(react-dom@19.0.0(react@18.3.1))(react@18.3.1) '@testing-library/user-event': specifier: ^14.4.3 - version: 14.4.3(@testing-library/dom@9.2.0) + version: 14.5.2(@testing-library/dom@10.4.0) '@types/jest': specifier: ^29.2.3 - version: 29.5.0 + version: 29.5.14 '@types/node': - specifier: ^18.11.9 - version: 18.15.11 - microbundle-crl: - specifier: ^0.13.11 - version: 0.13.11 + specifier: ^18.15.3 + version: 18.19.61 + microbundle: + specifier: ^0.15.1 + version: 0.15.1(@types/babel__core@7.20.5)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)) ts-toolbelt: specifier: ^9.6.0 version: 9.6.0 typescript: - specifier: ^4.9.5 - version: 4.9.5 + specifier: ^5.6.3 + version: 5.6.3 + + libs/jsondiffpatch: + devDependencies: + '@types/jest': + specifier: ^29.5.10 + version: 29.5.14 + '@typescript-eslint/eslint-plugin': + specifier: ^8.12.2 + version: 8.12.2(@typescript-eslint/parser@8.12.2(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1)(typescript@5.6.3) + '@typescript-eslint/parser': + specifier: ^8.12.2 + version: 8.12.2(eslint@8.57.1)(typescript@5.6.3) + eslint: + specifier: ^8.57.0 + version: 8.57.1 + eslint-config-prettier: + specifier: ^9.1.0 + version: 9.1.0(eslint@8.57.1) + jest: + specifier: ^29.7.0 + version: 29.7.0(@types/node@22.14.1)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@22.14.1)(typescript@5.6.3)) + ts-jest: + specifier: ^29.1.1 + version: 29.2.5(@babel/core@7.26.10)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.10))(jest@29.7.0(@types/node@22.14.1)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@22.14.1)(typescript@5.6.3)))(typescript@5.6.3) + tslib: + specifier: ^2.6.3 + version: 2.8.0 + typescript: + specifier: ^5.6.3 + version: 5.6.3 libs/juava: dependencies: @@ -319,177 +610,370 @@ importers: specifier: ^4.17.21 version: 4.17.21 tslib: - specifier: ^2.4.0 - version: 2.5.0 + specifier: ^2.6.3 + version: 2.8.0 devDependencies: '@types/jest': specifier: ^29.1.1 - version: 29.5.0 + version: 29.5.14 '@types/node': - specifier: ^18.11.10 - version: 18.15.11 + specifier: ^18.15.3 + version: 18.19.61 jest: specifier: ^29.1.2 - version: 29.5.0(@types/node@18.15.11)(ts-node@10.8.2) + version: 29.7.0(@types/node@18.19.61)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@22.14.1)(typescript@5.6.3)) ts-jest: specifier: 29.0.5 - version: 29.0.5(@babel/core@7.21.4)(esbuild@0.16.3)(jest@29.5.0)(typescript@4.9.5) + version: 29.0.5(@babel/core@7.26.10)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.10))(jest@29.7.0(@types/node@18.19.61)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@22.14.1)(typescript@5.6.3)))(typescript@5.6.3) - services/rotor: + services/profiles: dependencies: - '@jitsu-internal/console': + '@clickhouse/client': + specifier: ^1.10.1 + version: 1.10.1 + '@confluentinc/kafka-javascript': + specifier: ^1.4.1 + version: 1.4.1(encoding@0.1.13) + '@jitsu/core-functions': + specifier: workspace:* + version: link:../../libs/core-functions + '@jitsu/functions-lib': + specifier: workspace:* + version: link:../../libs/functions + express: + specifier: ^4.21.2 + version: 4.21.2 + isolated-vm: + specifier: 6.0.0 + version: 6.0.0 + juava: + specifier: workspace:* + version: link:../../libs/juava + mongodb: + specifier: ^6.16.0 + version: 6.16.0(@mongodb-js/zstd@2.0.1)(socks@2.8.3) + node-cache: + specifier: ^5.1.2 + version: 5.1.2 + p-queue: + specifier: ^6.6.2 + version: 6.6.2 + pg: + specifier: ~8.11.6 + version: 8.11.6 + pg-cursor: + specifier: ^2.11.0 + version: 2.12.1(pg@8.11.6) + prom-client: + specifier: ^15.1.3 + version: 15.1.3 + tslib: + specifier: ^2.6.3 + version: 2.8.0 + devDependencies: + '@babel/preset-env': + specifier: ^7.25.2 + version: 7.26.0(@babel/core@7.26.10) + '@babel/preset-react': + specifier: ^7.24.7 + version: 7.25.9(@babel/core@7.26.10) + '@babel/preset-typescript': + specifier: ^7.24.7 + version: 7.26.0(@babel/core@7.26.10) + '@jest/globals': + specifier: ^29.7.0 + version: 29.7.0 + '@jest/types': + specifier: ^29.6.3 + version: 29.6.3 + '@jitsu/protocols': specifier: workspace:* - version: link:../../webapps/console + version: link:../../types/protocols + '@types/express': + specifier: ^4.17.21 + version: 4.17.21 + '@types/jest': + specifier: ^29.5.11 + version: 29.5.14 + '@types/lodash': + specifier: ^4.17.7 + version: 4.17.13 + '@types/node': + specifier: ^18.15.3 + version: 18.19.61 + '@types/pg': + specifier: ~8.11.15 + version: 8.11.15 + '@types/pg-cursor': + specifier: ^2.7.2 + version: 2.7.2 + '@types/web': + specifier: ^0.0.152 + version: 0.0.152 + '@types/webpack': + specifier: ^5.28.5 + version: 5.28.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(webpack-cli@5.1.4) + '@webpack-cli/generators': + specifier: ^3.0.7 + version: 3.0.7(encoding@0.1.13)(mem-fs@2.3.0)(prettier@2.8.8)(webpack-cli@5.1.4)(webpack@5.95.0) + babel-loader: + specifier: ^9.1.3 + version: 9.2.1(@babel/core@7.26.10)(webpack@5.95.0) + clean-webpack-plugin: + specifier: ^4.0.0 + version: 4.0.0(webpack@5.95.0) + copy-webpack-plugin: + specifier: ^12.0.2 + version: 12.0.2(webpack@5.95.0) + declaration-bundler-webpack-plugin: + specifier: ^1.0.3 + version: 1.0.3 + dotenv-cli: + specifier: ^7.4.2 + version: 7.4.2 + jest: + specifier: ^29.7.0 + version: 29.7.0(@types/node@18.19.61)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)) + lodash: + specifier: ^4.17.21 + version: 4.17.21 + node-loader: + specifier: ^2.0.0 + version: 2.0.0(webpack@5.95.0) + nodemon: + specifier: ^3.1.4 + version: 3.1.7 + ts-jest: + specifier: ^29.2.3 + version: 29.2.5(@babel/core@7.26.10)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.10))(jest@29.7.0(@types/node@18.19.61)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)))(typescript@5.6.3) + ts-loader: + specifier: ^9.5.1 + version: 9.5.1(typescript@5.6.3)(webpack@5.95.0) + ts-node: + specifier: ^10.9.2 + version: 10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3) + ts-node-dev: + specifier: ^2.0.0 + version: 2.0.0(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3) + typescript: + specifier: ^5.6.3 + version: 5.6.3 + webpack: + specifier: ^5.95.0 + version: 5.95.0(@swc/core@1.11.10(@swc/helpers@0.5.15))(webpack-cli@5.1.4) + webpack-cli: + specifier: ^5.1.4 + version: 5.1.4(@webpack-cli/generators@3.0.7)(webpack@5.95.0) + + services/rotor: + dependencies: + '@amplitude/ua-parser-js': + specifier: ^0.7.33 + version: 0.7.33 + '@aws-sdk/client-s3': + specifier: ^3.621.0 + version: 3.682.0 + '@clickhouse/client': + specifier: ^1.10.1 + version: 1.10.1 + '@confluentinc/kafka-javascript': + specifier: ^1.4.1 + version: 1.4.1(encoding@0.1.13) '@jitsu/core-functions': specifier: workspace:* version: link:../../libs/core-functions + '@jitsu/functions-lib': + specifier: workspace:* + version: link:../../libs/functions + '@maxmind/geoip2-node': + specifier: ^5.0.0 + version: 5.0.0 + '@types/pg-cursor': + specifier: ^2.7.2 + version: 2.7.2 + dayjs: + specifier: ^1.11.12 + version: 1.11.13 express: - specifier: ^4.18.2 - version: 4.18.2 - glob: - specifier: ^8.0.3 - version: 8.1.0 + specifier: ^4.21.2 + version: 4.21.2 ioredis: - specifier: ^5.2.3 - version: 5.3.1 - json-grab: - specifier: 0.1.0-alpha.2 - version: 0.1.0-alpha.2 + specifier: ^5.4.1 + version: 5.4.1 json5: - specifier: ^2.1.0 + specifier: ^2.2.3 version: 2.2.3 + jsondiffpatch: + specifier: workspace:* + version: link:../../libs/jsondiffpatch juava: specifier: workspace:* version: link:../../libs/juava - minimist: - specifier: ^1.2.7 - version: 1.2.8 node-cache: specifier: ^5.1.2 version: 5.1.2 node-fetch-commonjs: - specifier: ^3.2.4 - version: 3.2.4 + specifier: ^3.3.2 + version: 3.3.2 object-hash: specifier: ^3.0.0 version: 3.0.0 + p-queue: + specifier: ^6.6.2 + version: 6.6.2 + pg: + specifier: ~8.11.6 + version: 8.11.6 + pg-cursor: + specifier: ^2.11.0 + version: 2.12.1(pg@8.11.6) + prom-client: + specifier: ^15.1.3 + version: 15.1.3 + semver: + specifier: ^7.6.3 + version: 7.6.3 + tar: + specifier: ^7.4.3 + version: 7.4.3 tslib: - specifier: ^2.4.0 - version: 2.5.0 + specifier: ^2.6.3 + version: 2.8.0 + undici: + specifier: ^7.11.0 + version: 7.11.0 devDependencies: '@babel/preset-env': - specifier: ^7.20.2 - version: 7.21.4(@babel/core@7.21.4) + specifier: ^7.25.2 + version: 7.26.0(@babel/core@7.26.10) '@babel/preset-react': - specifier: ^7.18.6 - version: 7.18.6(@babel/core@7.21.4) + specifier: ^7.24.7 + version: 7.25.9(@babel/core@7.26.10) '@babel/preset-typescript': - specifier: ^7.21.0 - version: 7.21.4(@babel/core@7.21.4) + specifier: ^7.24.7 + version: 7.26.0(@babel/core@7.26.10) + '@jest/globals': + specifier: ^29.7.0 + version: 29.7.0 + '@jest/types': + specifier: ^29.6.3 + version: 29.6.3 '@jitsu/protocols': specifier: workspace:* version: link:../../types/protocols - '@rollup/plugin-babel': - specifier: ^6.0.2 - version: 6.0.3(@babel/core@7.21.4)(rollup@3.20.2) - '@rollup/plugin-commonjs': - specifier: ^23.0.2 - version: 23.0.7(rollup@3.20.2) - '@rollup/plugin-json': - specifier: ^5.0.1 - version: 5.0.2(rollup@3.20.2) - '@rollup/plugin-multi-entry': - specifier: ^6.0.0 - version: 6.0.0(rollup@3.20.2) - '@rollup/plugin-node-resolve': - specifier: ^15.0.1 - version: 15.0.2(rollup@3.20.2) - '@rollup/plugin-terser': - specifier: ^0.1.0 - version: 0.1.0(rollup@3.20.2) - '@rollup/plugin-typescript': - specifier: ^9.0.2 - version: 9.0.2(rollup@3.20.2)(tslib@2.5.0)(typescript@4.9.5) - '@types/glob': - specifier: ^8.0.0 - version: 8.1.0 - '@types/minimist': - specifier: ^1.2.2 - version: 1.2.2 + '@types/express': + specifier: ^4.17.21 + version: 4.17.21 + '@types/jest': + specifier: ^29.5.11 + version: 29.5.14 + '@types/lodash': + specifier: ^4.17.7 + version: 4.17.13 '@types/node': - specifier: 16.11.6 - version: 16.11.6 + specifier: ^18.15.3 + version: 18.19.61 + '@types/pg': + specifier: ~8.11.15 + version: 8.11.15 '@types/web': - specifier: 0.0.69 - version: 0.0.69 + specifier: ^0.0.152 + version: 0.0.152 '@types/webpack': - specifier: ^5.28.0 - version: 5.28.1(esbuild@0.16.3)(webpack-cli@5.0.1) + specifier: ^5.28.5 + version: 5.28.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(webpack-cli@5.1.4) '@webpack-cli/generators': - specifier: ^3.0.0 - version: 3.0.1(mem-fs-editor@9.7.0)(mem-fs@2.3.0)(prettier@2.8.7)(webpack-cli@5.0.1)(webpack@5.78.0) + specifier: ^3.0.7 + version: 3.0.7(encoding@0.1.13)(mem-fs@2.3.0)(prettier@2.8.8)(webpack-cli@5.1.4)(webpack@5.95.0) babel-loader: - specifier: ^9.1.2 - version: 9.1.2(@babel/core@7.21.4)(webpack@5.78.0) + specifier: ^9.1.3 + version: 9.2.1(@babel/core@7.26.10)(webpack@5.95.0) clean-webpack-plugin: specifier: ^4.0.0 - version: 4.0.0(webpack@5.78.0) + version: 4.0.0(webpack@5.95.0) copy-webpack-plugin: - specifier: ^11.0.0 - version: 11.0.0(webpack@5.78.0) + specifier: ^12.0.2 + version: 12.0.2(webpack@5.95.0) declaration-bundler-webpack-plugin: specifier: ^1.0.3 version: 1.0.3 dotenv-cli: - specifier: ^6.0.0 - version: 6.0.0 + specifier: ^7.4.2 + version: 7.4.2 + jest: + specifier: ^29.7.0 + version: 29.7.0(@types/node@18.19.61)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)) + lodash: + specifier: ^4.17.21 + version: 4.17.21 node-loader: specifier: ^2.0.0 - version: 2.0.0(webpack@5.78.0) + version: 2.0.0(webpack@5.95.0) nodemon: - specifier: ^2.0.20 - version: 2.0.22 - rollup: - specifier: ^3.2.5 - version: 3.20.2 + specifier: ^3.1.4 + version: 3.1.7 + ts-jest: + specifier: ^29.2.3 + version: 29.2.5(@babel/core@7.26.10)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.10))(jest@29.7.0(@types/node@18.19.61)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)))(typescript@5.6.3) ts-loader: - specifier: ^9.4.2 - version: 9.4.2(typescript@4.9.5)(webpack@5.78.0) + specifier: ^9.5.1 + version: 9.5.1(typescript@5.6.3)(webpack@5.95.0) ts-node: - specifier: ~10.8.1 - version: 10.8.2(@types/node@16.11.6)(typescript@4.9.5) + specifier: ^10.9.2 + version: 10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3) + ts-node-dev: + specifier: ^2.0.0 + version: 2.0.0(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3) typescript: - specifier: ^4.9.3 - version: 4.9.5 + specifier: ^5.6.3 + version: 5.6.3 webpack: - specifier: ^5.75.0 - version: 5.78.0(esbuild@0.16.3)(webpack-cli@5.0.1) + specifier: ^5.95.0 + version: 5.95.0(@swc/core@1.11.10(@swc/helpers@0.5.15))(webpack-cli@5.1.4) webpack-cli: - specifier: ^5.0.0 - version: 5.0.1(@webpack-cli/generators@3.0.1)(webpack@5.78.0) - wepack-cli: - specifier: 0.0.1-security - version: 0.0.1-security + specifier: ^5.1.4 + version: 5.1.4(@webpack-cli/generators@3.0.7)(webpack@5.95.0) types/protocols: {} webapps/console: dependencies: '@ant-design/cssinjs': - specifier: ^1.5.6 - version: 1.8.1(react-dom@18.2.0)(react@18.2.0) + specifier: ^1.21.1 + version: 1.21.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@clickhouse/client': - specifier: ^0.0.11 - version: 0.0.11 + specifier: ^1.10.1 + version: 1.10.1 + '@dnd-kit/core': + specifier: ^6.0.8 + version: 6.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@dnd-kit/modifiers': + specifier: ^6.0.1 + version: 6.0.1(@dnd-kit/core@6.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) + '@dnd-kit/sortable': + specifier: ^7.0.2 + version: 7.0.2(@dnd-kit/core@6.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) + '@dnd-kit/utilities': + specifier: ^3.2.1 + version: 3.2.2(react@18.3.1) '@fontsource/inter': specifier: ^4.5.13 version: 4.5.15 '@fontsource/roboto-mono': specifier: ^4.5.8 version: 4.5.10 + '@google-cloud/scheduler': + specifier: ^4.0.0 + version: 4.3.0(encoding@0.1.13) + '@jitsu-internal/webapps-shared': + specifier: workspace:* + version: link:../shared '@jitsu/core-functions': specifier: workspace:* version: link:../../libs/core-functions + '@jitsu/functions-lib': + specifier: workspace:* + version: link:../../libs/functions '@jitsu/jitsu-react': specifier: workspace:* version: link:../../libs/jitsu-react @@ -501,296 +985,332 @@ importers: version: link:../../types/protocols '@loadable/component': specifier: ^5.15.3 - version: 5.15.3(react@18.2.0) + version: 5.16.4(react@18.3.1) '@monaco-editor/react': - specifier: 4.5.0 - version: 4.5.0(monaco-editor@0.37.1)(react-dom@18.2.0)(react@18.2.0) + specifier: ^4.6.0 + version: 4.6.0(monaco-editor@0.52.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@nangohq/frontend': specifier: ^0.21.11 - version: 0.21.11 + version: 0.21.17 '@prisma/client': - specifier: ^4.12.0 - version: 4.12.0(prisma@4.12.0) + specifier: ^6.5.0 + version: 6.5.0(prisma@6.5.0(typescript@5.6.3))(typescript@5.6.3) + '@react-email/components': + specifier: ^0.3.1 + version: 0.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@react-email/render': + specifier: ^1.1.3 + version: 1.1.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@rjsf/antd': - specifier: 5.3.1 - version: 5.3.1(@ant-design/icons@5.0.1)(@rjsf/core@5.3.1)(@rjsf/utils@5.3.1)(antd@5.4.2)(dayjs@1.11.7)(react-dom@18.2.0)(react@18.2.0) + specifier: ^5.22.4 + version: 5.22.4(@ant-design/icons@5.5.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@rjsf/core@5.22.4(@rjsf/utils@5.22.4(react@18.3.1))(react@18.3.1))(@rjsf/utils@5.22.4(react@18.3.1))(antd@5.22.0(date-fns@2.30.0)(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(dayjs@1.11.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@rjsf/core': - specifier: 5.3.1 - version: 5.3.1(@rjsf/utils@5.3.1)(react@18.2.0) + specifier: ^5.22.4 + version: 5.22.4(@rjsf/utils@5.22.4(react@18.3.1))(react@18.3.1) '@rjsf/utils': - specifier: 5.3.1 - version: 5.3.1(react@18.2.0) - '@rjsf/validator-ajv6': - specifier: 5.3.1 - version: 5.3.1(@rjsf/utils@5.3.1) - '@sensejs/kafkajs-zstd-support': - specifier: ^0.9.2 - version: 0.9.2(kafkajs@2.2.4) + specifier: ^5.22.4 + version: 5.22.4(react@18.3.1) + '@rjsf/validator-ajv8': + specifier: ^5.22.4 + version: 5.22.4(@rjsf/utils@5.22.4(react@18.3.1)) '@tanstack/react-query': - specifier: ^4.2.1 - version: 4.29.1(react-dom@18.2.0)(react@18.2.0) + specifier: ^4.36.1 + version: 4.36.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@types/cookie': - specifier: ^0.5.1 - version: 0.5.1 + specifier: ^1.0.0 + version: 1.0.0 '@types/pg-promise': specifier: ^5.4.3 - version: 5.4.3 + version: 5.4.3(pg-query-stream@4.7.1(pg@8.11.6)) agentkeepalive: specifier: ^4.2.1 version: 4.3.0 + ajv: + specifier: ^8.17.1 + version: 8.17.1 + chart.js: + specifier: ^4.4.1 + version: 4.4.6 classnames: specifier: ^2.3.1 - version: 2.3.2 + version: 2.5.1 cookie: - specifier: ^0.5.0 - version: 0.5.0 + specifier: 1.0.1 + version: 1.0.1 cuid: specifier: ^2.1.8 version: 2.1.8 dayjs: - specifier: ^1.11.7 - version: 1.11.7 + specifier: ^1.11.10 + version: 1.11.13 firebase: - specifier: ^9.17.1 - version: 9.19.1 + specifier: ^11.4.0 + version: 11.4.0 firebase-admin: - specifier: ^11.5.0 - version: 11.6.0 + specifier: ^13.2.0 + version: 13.2.0(encoding@0.1.13) + google-ads-api: + specifier: ^17.1.0-rest-beta + version: 17.1.0-rest-beta(encoding@0.1.13) googleapis: - specifier: ^100.0.0 - version: 100.0.0 + specifier: ^126.0.1 + version: 126.0.1(encoding@0.1.13) highlight.js: specifier: ^11.6.0 - version: 11.7.0 + version: 11.10.0 ioredis: - specifier: ^5.2.3 - version: 5.3.1 + specifier: ^5.3.2 + version: 5.4.1 js-yaml: specifier: ^4.1.0 version: 4.1.0 - json-schema-faker: - specifier: ~0.5.3 - version: 0.5.3 json5: specifier: ^2.2.3 version: 2.2.3 + jsondiffpatch: + specifier: ^0.5.0 + version: 0.5.0 jsonwebtoken: - specifier: ^8.5.1 - version: 8.5.1 + specifier: ^9.0.2 + version: 9.0.2 juava: specifier: workspace:* version: link:../../libs/juava - kafkajs: - specifier: ^2.2.4 - version: 2.2.4 - kafkajs-snappy: - specifier: ^1.1.0 - version: 1.1.0 + jwks-rsa: + specifier: ^3.2.0 + version: 3.2.0 lodash: specifier: ^4.17.21 version: 4.17.21 lucide-react: - specifier: ^0.125.0 - version: 0.125.0(react@18.2.0) + specifier: ^0.447.0 + version: 0.447.0(react@18.3.1) mjml: - specifier: ^4.13.0 - version: 4.14.1 + specifier: ^5.0.0-alpha.6 + version: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) mjml-react: specifier: ^2.0.8 - version: 2.0.8(mjml@4.14.1)(react-dom@18.2.0)(react@18.2.0) + version: 2.0.8(mjml@5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) monaco-editor: - specifier: 0.37.1 - version: 0.37.1 + specifier: ^0.52.0 + version: 0.52.0 next: - specifier: ^13.3.0 - version: 13.3.0(@babel/core@7.21.4)(react-dom@18.2.0)(react@18.2.0) + specifier: ^15.5.4 + version: 15.5.4(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.39.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) next-auth: - specifier: ^4.22.0 - version: 4.22.0(next@13.3.0)(nodemailer@6.9.1)(react-dom@18.2.0)(react@18.2.0) + specifier: ^4.24.11 + version: 4.24.11(next@15.5.4(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.39.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(nodemailer@6.10.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + node-cache: + specifier: ^5.1.2 + version: 5.1.2 node-fetch-commonjs: - specifier: ^3.2.4 - version: 3.2.4 + specifier: ^3.3.2 + version: 3.3.2 node-sql-parser: - specifier: ^4.6.5 - version: 4.6.6 + specifier: ^5.3.8 + version: 5.3.8 nodemailer: - specifier: ^6.8.0 - version: 6.9.1 + specifier: ^6.10.1 + version: 6.10.1 object-hash: specifier: ^3.0.0 version: 3.0.0 pg: - specifier: ^8.7.3 - version: 8.10.0 - pg-query-stream: - specifier: ^4.2.4 - version: 4.4.0(pg@8.10.0) + specifier: ~8.11.6 + version: 8.11.6 + pg-cursor: + specifier: ~2.10.1 + version: 2.10.6(pg@8.11.6) prisma: - specifier: ^4.12.0 - version: 4.12.0 + specifier: ^6.5.0 + version: 6.5.0(typescript@5.6.3) react: - specifier: ^18.2.0 - version: 18.2.0 + specifier: ^18.3.1 + version: 18.3.1 react-dom: - specifier: ^18.2.0 - version: 18.2.0(react@18.2.0) + specifier: ^18.3.1 + version: 18.3.1(react@18.3.1) react-icons: - specifier: ^4.8.0 - version: 4.8.0(react@18.2.0) + specifier: ^4.10.1 + version: 4.12.0(react@18.3.1) react-json-view: specifier: ^1.19.1 - version: 1.21.3(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0) + version: 1.21.3(@types/react@18.3.3)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-use: specifier: ^17.4.0 - version: 17.4.0(react-dom@18.2.0)(react@18.2.0) + version: 17.5.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + recharts: + specifier: ^2.10.4 + version: 2.13.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) stable-hash: specifier: 0.0.3 version: 0.0.3 stopwatch-node: specifier: ^1.1.0 version: 1.1.0 + timezones-list: + specifier: ^3.0.2 + version: 3.0.3 tslib: - specifier: ^2.4.0 - version: 2.5.0 + specifier: ^2.6.3 + version: 2.8.0 + use-debounce: + specifier: ^10.0.4 + version: 10.0.5(react@18.3.1) zod: - specifier: 3.21.4 - version: 3.21.4 + specifier: ^3.23.8 + version: 3.23.8 zod-to-json-schema: - specifier: 3.20.4 - version: 3.20.4(zod@3.21.4) + specifier: ^3.23.2 + version: 3.23.5(zod@3.23.8) devDependencies: '@ant-design/icons': - specifier: ^5.0.1 - version: 5.0.1(react-dom@18.2.0)(react@18.2.0) + specifier: ^5.5.1 + version: 5.5.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@next/bundle-analyzer': + specifier: ^15.5.4 + version: 15.5.4 '@tailwindcss/typography': - specifier: ^0.5.7 - version: 0.5.9(tailwindcss@3.3.1) + specifier: ^0.5.15 + version: 0.5.15(tailwindcss@3.4.14(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3))) '@testing-library/jest-dom': - specifier: ^5.16.4 - version: 5.16.5 + specifier: ^6.1.3 + version: 6.6.2 '@testing-library/react': specifier: ^13.1.1 - version: 13.4.0(react-dom@18.2.0)(react@18.2.0) + version: 13.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@types/cli-progress': specifier: ^3.11.0 - version: 3.11.0 - '@types/firebase': - specifier: ^3.2.1 - version: 3.2.1 + version: 3.11.6 '@types/js-yaml': specifier: ^4.0.5 - version: 4.0.5 + version: 4.0.9 '@types/lodash': specifier: ^4.14.185 - version: 4.14.192 + version: 4.17.13 '@types/node': specifier: ^18.15.3 - version: 18.15.11 + version: 18.19.61 '@types/nodemailer': - specifier: ^6.4.6 - version: 6.4.7 + specifier: ^6.4.17 + version: 6.4.17 '@types/pg': - specifier: ^8.6.5 - version: 8.6.6 + specifier: ~8.11.15 + version: 8.11.15 '@types/pg-query-stream': specifier: ^3.4.0 - version: 3.4.0(pg@8.10.0) + version: 3.4.0(pg@8.11.6) '@types/react': - specifier: 18.0.28 - version: 18.0.28 + specifier: 18.3.3 + version: 18.3.3 '@types/react-dom': - specifier: 18.0.11 - version: 18.0.11 + specifier: 18.3.0 + version: 18.3.0 + ansi-to-html: + specifier: ^0.7.2 + version: 0.7.2 antd: - specifier: ^5.2.1 - version: 5.4.2(react-dom@18.2.0)(react@18.2.0) + specifier: ^5.22.0 + version: 5.22.0(date-fns@2.30.0)(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) autoprefixer: - specifier: ^10.4.5 - version: 10.4.14(postcss@8.4.21) + specifier: ^10.4.20 + version: 10.4.20(postcss@8.4.47) dotenv-cli: specifier: ^6.0.0 version: 6.0.0 + elliptic: + specifier: ^6.6.1 + version: 6.6.1 eslint: - specifier: 8.24.0 - version: 8.24.0 + specifier: ^8.57.0 + version: 8.57.1 eslint-config-next: - specifier: ^13.3.0 - version: 13.3.0(eslint@8.24.0)(typescript@4.9.5) + specifier: ^15.5.4 + version: 15.5.4(eslint@8.57.1)(typescript@5.6.3) eslint-plugin-unused-imports: - specifier: ^2.0.0 - version: 2.0.0(eslint@8.24.0) + specifier: ^4.0.1 + version: 4.1.4(@typescript-eslint/eslint-plugin@8.12.2(@typescript-eslint/parser@8.12.2(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1) + http-proxy-middleware: + specifier: ^2.0.7 + version: 2.0.7(@types/express@4.17.21) jest: specifier: ^28.0.0 - version: 28.1.3(@types/node@18.15.11)(ts-node@10.8.2) + version: 28.1.3(@types/node@18.19.61)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)) less: specifier: ^4.1.2 - version: 4.1.3 + version: 4.2.0 less-loader: specifier: ^10.2.0 - version: 10.2.0(less@4.1.3)(webpack@5.79.0) + version: 10.2.0(less@4.2.0)(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(esbuild@0.23.1)) minimist: specifier: ^1.2.7 version: 1.2.8 - next-transpile-modules: - specifier: ^10.0.0 - version: 10.0.0 postcss: - specifier: ^8.4.12 - version: 8.4.21 + specifier: ^8.4.47 + version: 8.4.47 raw-loader: specifier: ^4.0.2 - version: 4.0.2(webpack@5.79.0) + version: 4.0.2(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(esbuild@0.23.1)) + react-email: + specifier: ^4.1.3 + version: 4.1.3 tailwindcss: - specifier: ^3.0.24 - version: 3.3.1(postcss@8.4.21)(ts-node@10.8.2) + specifier: ^3.4.14 + version: 3.4.14(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)) ts-node: - specifier: ~10.8.1 - version: 10.8.2(@types/node@18.15.11)(typescript@4.9.5) + specifier: ^10.9.2 + version: 10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3) type-fest: specifier: ^3.5.7 - version: 3.8.0 + version: 3.13.1 typescript: - specifier: ^4.8.4 - version: 4.9.5 + specifier: ^5.6.3 + version: 5.6.3 zod-prisma: - specifier: 0.5.4 - version: 0.5.4(prisma@4.12.0)(zod@3.21.4) + specifier: ^0.5.4 + version: 0.5.4(decimal.js@10.4.3)(prisma@6.5.0(typescript@5.6.3))(zod@3.23.8) webapps/ee-api: dependencies: '@aws-sdk/client-s3': - specifier: ~3.341.0 - version: 3.341.0 + specifier: ^3.418.0 + version: 3.682.0 '@clickhouse/client': - specifier: ^0.0.11 - version: 0.0.11 + specifier: ^1.10.1 + version: 1.10.1 + '@react-email/components': + specifier: ^0.3.1 + version: 0.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@types/node': - specifier: 18.11.12 - version: 18.11.12 + specifier: ^18.15.3 + version: 18.19.61 '@types/react': - specifier: 18.0.28 - version: 18.0.28 + specifier: 18.3.3 + version: 18.3.3 '@types/react-dom': - specifier: 18.0.11 - version: 18.0.11 + specifier: 18.3.0 + version: 18.3.0 crypto-js: specifier: ^4.1.1 - version: 4.1.1 - eslint: - specifier: 8.29.0 - version: 8.29.0 - eslint-config-next: - specifier: 13.0.0 - version: 13.0.0(eslint@8.29.0)(typescript@4.9.4) + version: 4.2.0 + dayjs: + specifier: ^1.11.10 + version: 1.11.13 firebase-admin: - specifier: ^11.5.0 - version: 11.6.0 + specifier: ^13.2.0 + version: 13.2.0(encoding@0.1.13) + intercom-client: + specifier: ^5.0.0 + version: 5.0.0 + is-valid-domain: + specifier: ^0.1.6 + version: 0.1.6 json5: specifier: ^2.2.3 version: 2.2.3 jsonwebtoken: - specifier: ^8.5.1 - version: 8.5.1 + specifier: ^9.0.2 + version: 9.0.2 juava: specifier: workspace:* version: link:../../libs/juava @@ -798,3662 +1318,2209 @@ importers: specifier: ^4.17.21 version: 4.17.21 next: - specifier: 13.0.0 - version: 13.0.0(@babel/core@7.21.4)(react-dom@18.2.0)(react@18.2.0) + specifier: ^15.5.4 + version: 15.5.4(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.39.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) node-fetch-commonjs: - specifier: ^3.2.4 - version: 3.2.4 + specifier: ^3.3.2 + version: 3.3.2 pg: - specifier: ^8.7.3 - version: 8.10.0 + specifier: ~8.11.6 + version: 8.11.6 react: - specifier: 18.2.0 - version: 18.2.0 + specifier: ^18.3.1 + version: 18.3.1 react-dom: - specifier: 18.2.0 - version: 18.2.0(react@18.2.0) + specifier: 18.3.1 + version: 18.3.1(react@18.3.1) + resend: + specifier: ^4.0.0 + version: 4.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) stripe: specifier: ^11.15.0 version: 11.18.0 tslib: - specifier: ^2.4.0 - version: 2.5.0 + specifier: ^2.6.3 + version: 2.8.0 + zod: + specifier: ^3.23.8 + version: 3.23.8 devDependencies: + '@react-email/preview-server': + specifier: ^4.1.3 + version: 4.1.3(@opentelemetry/api@1.9.0)(@playwright/test@1.39.0)(@swc/core@1.11.10(@swc/helpers@0.5.15))(postcss@8.4.47)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)) '@types/crypto-js': specifier: ^4.1.1 - version: 4.1.1 + version: 4.2.2 '@types/jsonwebtoken': - specifier: ^8.5.9 - version: 8.5.9 + specifier: ^9.0.3 + version: 9.0.7 '@types/lodash': specifier: ^4.14.185 - version: 4.14.192 + version: 4.17.13 '@types/pg': - specifier: ^8.6.5 - version: 8.6.6 + specifier: ~8.11.15 + version: 8.11.15 '@types/stripe': specifier: ^8.0.417 version: 8.0.417 - next-transpile-modules: - specifier: ^10.0.0 - version: 10.0.0 + dotenv: + specifier: ^16.3.1 + version: 16.4.5 + eslint: + specifier: ^8.57.0 + version: 8.57.1 + eslint-config-next: + specifier: ^15.5.4 + version: 15.5.4(eslint@8.57.1)(typescript@5.6.3) + raw-loader: + specifier: ^4.0.2 + version: 4.0.2(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))) + react-email: + specifier: ^4.1.3 + version: 4.1.3 + ts-node: + specifier: ^10.9.2 + version: 10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3) + type-fest: + specifier: ^3.5.7 + version: 3.13.1 + typescript: + specifier: ^5.6.3 + version: 5.6.3 + + webapps/shared: + dependencies: + '@react-email/components': + specifier: ^0.3.1 + version: 0.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@react-email/render': + specifier: ^1.1.3 + version: 1.1.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@types/node': + specifier: ^18.15.3 + version: 18.19.61 + '@types/react': + specifier: 18.3.3 + version: 18.3.3 + '@types/react-dom': + specifier: 18.3.0 + version: 18.3.0 + dayjs: + specifier: ^1.11.10 + version: 1.11.13 + juava: + specifier: workspace:* + version: link:../../libs/juava + lodash: + specifier: ^4.17.21 + version: 4.17.21 + nodemailer: + specifier: ^6.10.1 + version: 6.10.1 + react: + specifier: ^18.3.1 + version: 18.3.1 + react-dom: + specifier: 18.3.1 + version: 18.3.1(react@18.3.1) + tslib: + specifier: ^2.6.3 + version: 2.8.0 + zod: + specifier: ^3.23.8 + version: 3.23.8 + devDependencies: + '@types/lodash': + specifier: ^4.14.185 + version: 4.17.13 + '@types/nodemailer': + specifier: ^6.4.17 + version: 6.4.17 + eslint: + specifier: ^8.57.0 + version: 8.57.1 + ts-node: + specifier: ^10.9.2 + version: 10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3) + type-fest: + specifier: ^3.5.7 + version: 3.13.1 typescript: - specifier: 4.9.4 - version: 4.9.4 + specifier: ^5.6.3 + version: 5.6.3 packages: - /@adobe/css-tools@4.2.0: - resolution: {integrity: sha512-E09FiIft46CmH5Qnjb0wsW54/YQd69LsxeKUOWawmws1XWvyFGURnAChH0mlr7YPFR1ofwvUQfcL0J3lMxXqPA==} - dev: true + '@adobe/css-tools@4.4.0': + resolution: {integrity: sha512-Ff9+ksdQQB3rMncgqDK78uLznstjyfIf2Arnh22pW8kBpLs6rpKDwgnZT46hin5Hl1WzazzK64DOrhSwYpS7bQ==} + + '@alloc/quick-lru@5.2.0': + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + + '@amplitude/ua-parser-js@0.7.33': + resolution: {integrity: sha512-wKEtVR4vXuPT9cVEIJkYWnlF++Gx3BdLatPBM+SZ1ztVIvnhdGBZR/mn9x/PzyrMcRlZmyi6L56I2J3doVBnjA==} - /@ampproject/remapping@2.2.1: - resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==} + '@ampproject/remapping@2.3.0': + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} - dependencies: - '@jridgewell/gen-mapping': 0.3.3 - '@jridgewell/trace-mapping': 0.3.18 - /@analytics/cookie-utils@0.2.10: - resolution: {integrity: sha512-k7oyW1PkMCnEdCYmHCLLXAsKavQh5ZRh5EeugBEUTlPJs8x8ajF+W1T0PYVDS26Impkea1sJR2etv/6l2pKtlg==} - dependencies: - '@analytics/global-storage-utils': 0.1.5 - dev: false + '@analytics/cookie-utils@0.2.12': + resolution: {integrity: sha512-2h/yuIu3kmu+ZJlKmlT6GoRvUEY2k1BbQBezEv5kGhnn9KpmzPz715Y3GmM2i+m7Y0QmBdVUoA260dQZkofs2A==} - /@analytics/core@0.11.1(@types/dlv@1.1.2): - resolution: {integrity: sha512-vHuiMpeGV6jGRRoOuScUJ4CHpVd1Zu9M+fjEWgN+boMAxIwFCtRTkSbhhIP337JuEBLpMT8w37OxA4Yh3NZFZw==} - dependencies: - '@analytics/global-storage-utils': 0.1.5 - '@analytics/type-utils': 0.6.0 - analytics-utils: 1.0.10(@types/dlv@1.1.2) - transitivePeerDependencies: - - '@types/dlv' - dev: false + '@analytics/core@0.12.15': + resolution: {integrity: sha512-Y+zxTNIbONXKxeEUOtcXs4b3uuiGjF5sy1zHl8ZNkIBwrOpTM8ZGNhi0xGL8ZhaQLGbi03BrT6DaoNNG3sBQOg==} - /@analytics/global-storage-utils@0.1.5: - resolution: {integrity: sha512-DzOCd0qK7PGnz/dgBYNzLmaFBkxcEHM6/CpWMY7hel1IBUldMsyet4sdRgI2Nk+Wc6B/YvqVqos68p5ntB59zA==} - dependencies: - '@analytics/type-utils': 0.6.0 - dev: false + '@analytics/global-storage-utils@0.1.7': + resolution: {integrity: sha512-V+spzGLZYm4biZT4uefaylm80SrLXf8WOTv9hCgA46cLcyxx3LD4GCpssp1lj+RcWLl/uXJQBRO4Mnn/o1x6Gw==} - /@analytics/localstorage-utils@0.1.8: - resolution: {integrity: sha512-tU5rLEHdYRc4EmRXkWacMJSmu4SeJfQuxvthSHHRVx3hGBQ00MJRDGyDHMXcrLKQXrBpsJo6t/8YRavHLOxDPQ==} - dependencies: - '@analytics/global-storage-utils': 0.1.5 - dev: false + '@analytics/localstorage-utils@0.1.10': + resolution: {integrity: sha512-uJS+Jp1yLG5VFCgA5T82ZODYBS0xuDQx0NtAZrgbqt9j51BX3TcgmOez5LVkrUNu/lpbxjCLq35I4TKj78VmOQ==} - /@analytics/session-storage-utils@0.0.5: - resolution: {integrity: sha512-BXRNQ73GSkkkny//a/SNCYRyrIhEJBwOTcdNkr2gq6ghXgekQGrTfVzwybZ/tAEHA0XYkkaOszelRsDPxa+UOQ==} - dependencies: - '@analytics/global-storage-utils': 0.1.5 - dev: false + '@analytics/session-storage-utils@0.0.7': + resolution: {integrity: sha512-PSv40UxG96HVcjY15e3zOqU2n8IqXnH8XvTkg1X43uXNTKVSebiI2kUjA3Q7ESFbw5DPwcLbJhV7GforpuBLDw==} - /@analytics/storage-utils@0.4.0: - resolution: {integrity: sha512-RY9nhoSQcuQxkmVUZBG3ULOwiFxi+H5N0WHn+FPUScnoM4b+tC48dWzyFxD9qKyWPah8/ndjCj6r+6wTGSl1zw==} - dependencies: - '@analytics/cookie-utils': 0.2.10 - '@analytics/global-storage-utils': 0.1.5 - '@analytics/localstorage-utils': 0.1.8 - '@analytics/session-storage-utils': 0.0.5 - '@analytics/type-utils': 0.6.0 - dev: false + '@analytics/storage-utils@0.4.2': + resolution: {integrity: sha512-AXObwyVQw9h2uJh1t2hUgabtVxzYpW+7uKVbdHQK80vr3Td5rrmCxrCxarh7HUuAgSDZ0bZWqmYxVgmwKceaLg==} - /@analytics/type-utils@0.6.0: - resolution: {integrity: sha512-1Yw7u/COtxx06BfwlI+kVhsa/upKYzmCNrT4c8QDeCY2KMYlnijkUjtHiPU08HxyTIVB5j6d75O0YWVIHwQS8g==} - dev: false + '@analytics/type-utils@0.6.2': + resolution: {integrity: sha512-TD+xbmsBLyYy/IxFimW/YL/9L2IEnM7/EoV9Aeh56U64Ify8o27HJcKjo38XY9Tcn0uOq1AX3thkKgvtWvwFQg==} - /@ant-design/colors@7.0.0: - resolution: {integrity: sha512-iVm/9PfGCbC0dSMBrz7oiEXZaaGH7ceU40OJEfKmyuzR9R5CRimJYPlRiFtMQGQcbNMea/ePcoIebi4ASGYXtg==} - dependencies: - '@ctrl/tinycolor': 3.6.0 + '@ant-design/colors@7.1.0': + resolution: {integrity: sha512-MMoDGWn1y9LdQJQSHiCC20x3uZ3CwQnv9QMz6pCmJOrqdgM9YxsoVVY0wtrdXbmfSgnV0KNk6zi09NAhMR2jvg==} + + '@ant-design/cssinjs-utils@1.1.1': + resolution: {integrity: sha512-2HAiyGGGnM0es40SxdszeQAU5iWp41wBIInq+ONTCKjlSKOrzQfnw4JDtB8IBmqE6tQaEKwmzTP2LGdt5DSwYQ==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' - /@ant-design/cssinjs@1.8.1(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-pOQJV9H9viB6qB9u7hkpKEOIQGx4dd8zjpwzF1v8YNwjffbZTlyUNQYln56gwpFF7SFskpYpnSfgoqTK4sFE/Q==} + '@ant-design/cssinjs@1.21.1': + resolution: {integrity: sha512-tyWnlK+XH7Bumd0byfbCiZNK43HEubMoCcu9VxwsAwiHdHTgWa+tMN0/yvxa+e8EzuFP1WdUNNPclRpVtD33lg==} peerDependencies: react: '>=16.0.0' react-dom: '>=16.0.0' - dependencies: - '@babel/runtime': 7.21.0 - '@emotion/hash': 0.8.0 - '@emotion/unitless': 0.7.5 - classnames: 2.3.2 - csstype: 3.1.2 - rc-util: 5.29.3(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - stylis: 4.1.3 - - /@ant-design/icons-svg@4.2.1: - resolution: {integrity: sha512-EB0iwlKDGpG93hW8f85CTJTs4SvMX7tt5ceupvhALp1IF44SeUFOMhKUOYqpsoYWQKAOuTRDMqn75rEaKDp0Xw==} - - /@ant-design/icons@5.0.1(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-ZyF4ksXCcdtwA/1PLlnFLcF/q8/MhwxXhKHh4oCHDA4Ip+ZzAHoICtyp4wZWfiCVDP0yuz3HsjyvuldHFb3wjA==} + + '@ant-design/fast-color@2.0.6': + resolution: {integrity: sha512-y2217gk4NqL35giHl72o6Zzqji9O7vHh9YmhUVkPtAOpoTCH4uWxo/pr4VE8t0+ChEPs0qo4eJRC5Q1eXWo3vA==} + engines: {node: '>=8.x'} + + '@ant-design/icons-svg@4.4.2': + resolution: {integrity: sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA==} + + '@ant-design/icons@5.5.1': + resolution: {integrity: sha512-0UrM02MA2iDIgvLatWrj6YTCYe0F/cwXvVE0E2SqGrL7PZireQwgEKTKBisWpZyal5eXZLvuM98kju6YtYne8w==} engines: {node: '>=8'} peerDependencies: react: '>=16.0.0' react-dom: '>=16.0.0' - dependencies: - '@ant-design/colors': 7.0.0 - '@ant-design/icons-svg': 4.2.1 - '@babel/runtime': 7.21.0 - classnames: 2.3.2 - rc-util: 5.29.3(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - /@ant-design/react-slick@1.0.0(react@18.2.0): - resolution: {integrity: sha512-OKxZsn8TAf8fYxP79rDXgLs9zvKMTslK6dJ4iLhDXOujUqC5zJPBRszyrcEHXcMPOm1Sgk40JgyF3yiL/Swd7w==} + '@ant-design/react-slick@1.1.2': + resolution: {integrity: sha512-EzlvzE6xQUBrZuuhSAFTdsr4P2bBBHGZwKFemEfq8gIGyIQCxalYfZW/T2ORbtQx5rU69o+WycP3exY/7T1hGA==} peerDependencies: react: '>=16.9.0' - dependencies: - '@babel/runtime': 7.21.0 - classnames: 2.3.2 - json2mq: 0.2.0 - react: 18.2.0 - resize-observer-polyfill: 1.5.1 - throttle-debounce: 5.0.0 - /@apideck/better-ajv-errors@0.3.6(ajv@8.12.0): + '@apideck/better-ajv-errors@0.3.6': resolution: {integrity: sha512-P+ZygBLZtkp0qqOAJJVX4oX/sFo5JR3eBWwwuqHHhK0GIgQOKWrAfiAaWX0aArHkRWHMuggFEgAZNxVPwPZYaA==} engines: {node: '>=10'} peerDependencies: ajv: '>=8' - dependencies: - ajv: 8.12.0 - json-schema: 0.4.0 - jsonpointer: 5.0.1 - leven: 3.1.0 - dev: false - - /@aws-crypto/crc32@3.0.0: - resolution: {integrity: sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA==} - dependencies: - '@aws-crypto/util': 3.0.0 - '@aws-sdk/types': 3.341.0 - tslib: 1.14.1 - dev: false - - /@aws-crypto/crc32c@3.0.0: - resolution: {integrity: sha512-ENNPPManmnVJ4BTXlOjAgD7URidbAznURqD0KvfREyc4o20DPYdEldU1f5cQ7Jbj0CJJSPaMIk/9ZshdB3210w==} - dependencies: - '@aws-crypto/util': 3.0.0 - '@aws-sdk/types': 3.341.0 - tslib: 1.14.1 - dev: false - /@aws-crypto/ie11-detection@3.0.0: - resolution: {integrity: sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==} - dependencies: - tslib: 1.14.1 - dev: false + '@aws-crypto/crc32@5.2.0': + resolution: {integrity: sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==} + engines: {node: '>=16.0.0'} - /@aws-crypto/sha1-browser@3.0.0: - resolution: {integrity: sha512-NJth5c997GLHs6nOYTzFKTbYdMNA6/1XlKVgnZoaZcQ7z7UJlOgj2JdbHE8tiYLS3fzXNCguct77SPGat2raSw==} - dependencies: - '@aws-crypto/ie11-detection': 3.0.0 - '@aws-crypto/supports-web-crypto': 3.0.0 - '@aws-crypto/util': 3.0.0 - '@aws-sdk/types': 3.341.0 - '@aws-sdk/util-locate-window': 3.310.0 - '@aws-sdk/util-utf8-browser': 3.259.0 - tslib: 1.14.1 - dev: false - - /@aws-crypto/sha256-browser@3.0.0: - resolution: {integrity: sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ==} - dependencies: - '@aws-crypto/ie11-detection': 3.0.0 - '@aws-crypto/sha256-js': 3.0.0 - '@aws-crypto/supports-web-crypto': 3.0.0 - '@aws-crypto/util': 3.0.0 - '@aws-sdk/types': 3.341.0 - '@aws-sdk/util-locate-window': 3.310.0 - '@aws-sdk/util-utf8-browser': 3.259.0 - tslib: 1.14.1 - dev: false + '@aws-crypto/crc32c@5.2.0': + resolution: {integrity: sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==} - /@aws-crypto/sha256-js@3.0.0: - resolution: {integrity: sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==} - dependencies: - '@aws-crypto/util': 3.0.0 - '@aws-sdk/types': 3.341.0 - tslib: 1.14.1 - dev: false + '@aws-crypto/sha1-browser@5.2.0': + resolution: {integrity: sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==} - /@aws-crypto/supports-web-crypto@3.0.0: - resolution: {integrity: sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg==} - dependencies: - tslib: 1.14.1 - dev: false + '@aws-crypto/sha256-browser@5.2.0': + resolution: {integrity: sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==} - /@aws-crypto/util@3.0.0: - resolution: {integrity: sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==} - dependencies: - '@aws-sdk/types': 3.341.0 - '@aws-sdk/util-utf8-browser': 3.259.0 - tslib: 1.14.1 - dev: false + '@aws-crypto/sha256-js@5.2.0': + resolution: {integrity: sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==} + engines: {node: '>=16.0.0'} - /@aws-sdk/abort-controller@3.341.0: - resolution: {integrity: sha512-D27hLlUPJTM4rNtMBPduD1vdPSmbUs0Eat8DZWCv3bg+v60loairha87oYL5x6Kj4IhbK93shI1OfzbxDqG1Yw==} - engines: {node: '>=14.0.0'} - dependencies: - '@aws-sdk/types': 3.341.0 - tslib: 2.5.0 - dev: false + '@aws-crypto/supports-web-crypto@5.2.0': + resolution: {integrity: sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==} - /@aws-sdk/chunked-blob-reader@3.310.0: - resolution: {integrity: sha512-CrJS3exo4mWaLnWxfCH+w88Ou0IcAZSIkk4QbmxiHl/5Dq705OLoxf4385MVyExpqpeVJYOYQ2WaD8i/pQZ2fg==} - dependencies: - tslib: 2.5.0 - dev: false + '@aws-crypto/util@5.2.0': + resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} - /@aws-sdk/client-s3@3.341.0: - resolution: {integrity: sha512-ftIH5JuL7JhEKQVsaHUx+kBcTA1t6FNDeb12yOoG5KS9ituoyl7tY2emdXM2rBuQZa2bKOSjls2GS03vdlF/BQ==} - engines: {node: '>=14.0.0'} - dependencies: - '@aws-crypto/sha1-browser': 3.0.0 - '@aws-crypto/sha256-browser': 3.0.0 - '@aws-crypto/sha256-js': 3.0.0 - '@aws-sdk/client-sts': 3.341.0 - '@aws-sdk/config-resolver': 3.341.0 - '@aws-sdk/credential-provider-node': 3.341.0 - '@aws-sdk/eventstream-serde-browser': 3.341.0 - '@aws-sdk/eventstream-serde-config-resolver': 3.341.0 - '@aws-sdk/eventstream-serde-node': 3.341.0 - '@aws-sdk/fetch-http-handler': 3.341.0 - '@aws-sdk/hash-blob-browser': 3.341.0 - '@aws-sdk/hash-node': 3.341.0 - '@aws-sdk/hash-stream-node': 3.341.0 - '@aws-sdk/invalid-dependency': 3.341.0 - '@aws-sdk/md5-js': 3.341.0 - '@aws-sdk/middleware-bucket-endpoint': 3.341.0 - '@aws-sdk/middleware-content-length': 3.341.0 - '@aws-sdk/middleware-endpoint': 3.341.0 - '@aws-sdk/middleware-expect-continue': 3.341.0 - '@aws-sdk/middleware-flexible-checksums': 3.341.0 - '@aws-sdk/middleware-host-header': 3.341.0 - '@aws-sdk/middleware-location-constraint': 3.341.0 - '@aws-sdk/middleware-logger': 3.341.0 - '@aws-sdk/middleware-recursion-detection': 3.341.0 - '@aws-sdk/middleware-retry': 3.341.0 - '@aws-sdk/middleware-sdk-s3': 3.341.0 - '@aws-sdk/middleware-serde': 3.341.0 - '@aws-sdk/middleware-signing': 3.341.0 - '@aws-sdk/middleware-ssec': 3.341.0 - '@aws-sdk/middleware-stack': 3.341.0 - '@aws-sdk/middleware-user-agent': 3.341.0 - '@aws-sdk/node-config-provider': 3.341.0 - '@aws-sdk/node-http-handler': 3.341.0 - '@aws-sdk/signature-v4-multi-region': 3.341.0 - '@aws-sdk/smithy-client': 3.341.0 - '@aws-sdk/types': 3.341.0 - '@aws-sdk/url-parser': 3.341.0 - '@aws-sdk/util-base64': 3.310.0 - '@aws-sdk/util-body-length-browser': 3.310.0 - '@aws-sdk/util-body-length-node': 3.310.0 - '@aws-sdk/util-defaults-mode-browser': 3.341.0 - '@aws-sdk/util-defaults-mode-node': 3.341.0 - '@aws-sdk/util-endpoints': 3.341.0 - '@aws-sdk/util-retry': 3.341.0 - '@aws-sdk/util-stream-browser': 3.341.0 - '@aws-sdk/util-stream-node': 3.341.0 - '@aws-sdk/util-user-agent-browser': 3.341.0 - '@aws-sdk/util-user-agent-node': 3.341.0 - '@aws-sdk/util-utf8': 3.310.0 - '@aws-sdk/util-waiter': 3.341.0 - '@aws-sdk/xml-builder': 3.310.0 - '@smithy/protocol-http': 1.0.1 - '@smithy/types': 1.0.0 - fast-xml-parser: 4.1.2 - tslib: 2.5.0 - transitivePeerDependencies: - - '@aws-sdk/signature-v4-crt' - - aws-crt - dev: false + '@aws-sdk/client-s3@3.682.0': + resolution: {integrity: sha512-gn8yPhOmExhqRENnR/vKvsbTw9jaRPbfNE8fQ2j91ejXhpj632QDNdobY8TxxPm2UEW2ISAVM55r2/UPl0YP1Q==} + engines: {node: '>=16.0.0'} - /@aws-sdk/client-sso-oidc@3.341.0: - resolution: {integrity: sha512-BjG4E7E1StsHCM0Mex7EZA6oPMQ+PLZY5axr554ErYxlzYlzE8b/Aq3N/mCCqeUSIdz4zAGr8di7XYCUB3CRdg==} - engines: {node: '>=14.0.0'} - dependencies: - '@aws-crypto/sha256-browser': 3.0.0 - '@aws-crypto/sha256-js': 3.0.0 - '@aws-sdk/config-resolver': 3.341.0 - '@aws-sdk/fetch-http-handler': 3.341.0 - '@aws-sdk/hash-node': 3.341.0 - '@aws-sdk/invalid-dependency': 3.341.0 - '@aws-sdk/middleware-content-length': 3.341.0 - '@aws-sdk/middleware-endpoint': 3.341.0 - '@aws-sdk/middleware-host-header': 3.341.0 - '@aws-sdk/middleware-logger': 3.341.0 - '@aws-sdk/middleware-recursion-detection': 3.341.0 - '@aws-sdk/middleware-retry': 3.341.0 - '@aws-sdk/middleware-serde': 3.341.0 - '@aws-sdk/middleware-stack': 3.341.0 - '@aws-sdk/middleware-user-agent': 3.341.0 - '@aws-sdk/node-config-provider': 3.341.0 - '@aws-sdk/node-http-handler': 3.341.0 - '@aws-sdk/smithy-client': 3.341.0 - '@aws-sdk/types': 3.341.0 - '@aws-sdk/url-parser': 3.341.0 - '@aws-sdk/util-base64': 3.310.0 - '@aws-sdk/util-body-length-browser': 3.310.0 - '@aws-sdk/util-body-length-node': 3.310.0 - '@aws-sdk/util-defaults-mode-browser': 3.341.0 - '@aws-sdk/util-defaults-mode-node': 3.341.0 - '@aws-sdk/util-endpoints': 3.341.0 - '@aws-sdk/util-retry': 3.341.0 - '@aws-sdk/util-user-agent-browser': 3.341.0 - '@aws-sdk/util-user-agent-node': 3.341.0 - '@aws-sdk/util-utf8': 3.310.0 - '@smithy/protocol-http': 1.0.1 - '@smithy/types': 1.0.0 - tslib: 2.5.0 - transitivePeerDependencies: - - aws-crt - dev: false + '@aws-sdk/client-sso-oidc@3.682.0': + resolution: {integrity: sha512-ZPZ7Y/r/w3nx/xpPzGSqSQsB090Xk5aZZOH+WBhTDn/pBEuim09BYXCLzvvxb7R7NnuoQdrTJiwimdJAhHl7ZQ==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-sts': ^3.682.0 - /@aws-sdk/client-sso@3.341.0: - resolution: {integrity: sha512-RWFKz3DBgEy92mce3TTgcq/UOKr/LKNyC189M8UXhWRyyoaQpE9gIyHUQwuh7a2bbnI7XKgpa2rO54Ns0rFJzw==} - engines: {node: '>=14.0.0'} - dependencies: - '@aws-crypto/sha256-browser': 3.0.0 - '@aws-crypto/sha256-js': 3.0.0 - '@aws-sdk/config-resolver': 3.341.0 - '@aws-sdk/fetch-http-handler': 3.341.0 - '@aws-sdk/hash-node': 3.341.0 - '@aws-sdk/invalid-dependency': 3.341.0 - '@aws-sdk/middleware-content-length': 3.341.0 - '@aws-sdk/middleware-endpoint': 3.341.0 - '@aws-sdk/middleware-host-header': 3.341.0 - '@aws-sdk/middleware-logger': 3.341.0 - '@aws-sdk/middleware-recursion-detection': 3.341.0 - '@aws-sdk/middleware-retry': 3.341.0 - '@aws-sdk/middleware-serde': 3.341.0 - '@aws-sdk/middleware-stack': 3.341.0 - '@aws-sdk/middleware-user-agent': 3.341.0 - '@aws-sdk/node-config-provider': 3.341.0 - '@aws-sdk/node-http-handler': 3.341.0 - '@aws-sdk/smithy-client': 3.341.0 - '@aws-sdk/types': 3.341.0 - '@aws-sdk/url-parser': 3.341.0 - '@aws-sdk/util-base64': 3.310.0 - '@aws-sdk/util-body-length-browser': 3.310.0 - '@aws-sdk/util-body-length-node': 3.310.0 - '@aws-sdk/util-defaults-mode-browser': 3.341.0 - '@aws-sdk/util-defaults-mode-node': 3.341.0 - '@aws-sdk/util-endpoints': 3.341.0 - '@aws-sdk/util-retry': 3.341.0 - '@aws-sdk/util-user-agent-browser': 3.341.0 - '@aws-sdk/util-user-agent-node': 3.341.0 - '@aws-sdk/util-utf8': 3.310.0 - '@smithy/protocol-http': 1.0.1 - '@smithy/types': 1.0.0 - tslib: 2.5.0 - transitivePeerDependencies: - - aws-crt - dev: false + '@aws-sdk/client-sso@3.682.0': + resolution: {integrity: sha512-PYH9RFUMYLFl66HSBq4tIx6fHViMLkhJHTYJoJONpBs+Td+NwVJ895AdLtDsBIhMS0YseCbPpuyjUCJgsUrwUw==} + engines: {node: '>=16.0.0'} - /@aws-sdk/client-sts@3.341.0: - resolution: {integrity: sha512-Ec8lzyPkd7N6VE4O73RuY04hgxMSXCD+uyWmYCoCCuamAx+xEP4ifVL0spApr8tttm51QLBgc01pVKNL62Oprw==} - engines: {node: '>=14.0.0'} - dependencies: - '@aws-crypto/sha256-browser': 3.0.0 - '@aws-crypto/sha256-js': 3.0.0 - '@aws-sdk/config-resolver': 3.341.0 - '@aws-sdk/credential-provider-node': 3.341.0 - '@aws-sdk/fetch-http-handler': 3.341.0 - '@aws-sdk/hash-node': 3.341.0 - '@aws-sdk/invalid-dependency': 3.341.0 - '@aws-sdk/middleware-content-length': 3.341.0 - '@aws-sdk/middleware-endpoint': 3.341.0 - '@aws-sdk/middleware-host-header': 3.341.0 - '@aws-sdk/middleware-logger': 3.341.0 - '@aws-sdk/middleware-recursion-detection': 3.341.0 - '@aws-sdk/middleware-retry': 3.341.0 - '@aws-sdk/middleware-sdk-sts': 3.341.0 - '@aws-sdk/middleware-serde': 3.341.0 - '@aws-sdk/middleware-signing': 3.341.0 - '@aws-sdk/middleware-stack': 3.341.0 - '@aws-sdk/middleware-user-agent': 3.341.0 - '@aws-sdk/node-config-provider': 3.341.0 - '@aws-sdk/node-http-handler': 3.341.0 - '@aws-sdk/smithy-client': 3.341.0 - '@aws-sdk/types': 3.341.0 - '@aws-sdk/url-parser': 3.341.0 - '@aws-sdk/util-base64': 3.310.0 - '@aws-sdk/util-body-length-browser': 3.310.0 - '@aws-sdk/util-body-length-node': 3.310.0 - '@aws-sdk/util-defaults-mode-browser': 3.341.0 - '@aws-sdk/util-defaults-mode-node': 3.341.0 - '@aws-sdk/util-endpoints': 3.341.0 - '@aws-sdk/util-retry': 3.341.0 - '@aws-sdk/util-user-agent-browser': 3.341.0 - '@aws-sdk/util-user-agent-node': 3.341.0 - '@aws-sdk/util-utf8': 3.310.0 - '@smithy/protocol-http': 1.0.1 - '@smithy/types': 1.0.0 - fast-xml-parser: 4.1.2 - tslib: 2.5.0 - transitivePeerDependencies: - - aws-crt - dev: false + '@aws-sdk/client-sts@3.682.0': + resolution: {integrity: sha512-xKuo4HksZ+F8m9DOfx/ZuWNhaPuqZFPwwy0xqcBT6sWH7OAuBjv/fnpOTzyQhpVTWddlf+ECtMAMrxjxuOExGQ==} + engines: {node: '>=16.0.0'} - /@aws-sdk/config-resolver@3.341.0: - resolution: {integrity: sha512-BpU6JTvT2SJLrsKxe4hjDDVbFnLc5iRcD+dwF/uV4rltFlXPOhqrx1S4NtRLpRaT3oYwA3DnBGC5CkV6QHWW5Q==} - engines: {node: '>=14.0.0'} - dependencies: - '@aws-sdk/types': 3.341.0 - '@aws-sdk/util-config-provider': 3.310.0 - '@aws-sdk/util-middleware': 3.341.0 - tslib: 2.5.0 - dev: false + '@aws-sdk/core@3.679.0': + resolution: {integrity: sha512-CS6PWGX8l4v/xyvX8RtXnBisdCa5+URzKd0L6GvHChype9qKUVxO/Gg6N/y43Hvg7MNWJt9FBPNWIxUB+byJwg==} + engines: {node: '>=16.0.0'} - /@aws-sdk/credential-provider-env@3.341.0: - resolution: {integrity: sha512-oqFMmGvtKjqcY6Io4CweHz0blgyyZtlfxWJbttPK7dSLk9EtRUuvVilcRtzXWXuAB2hk9zUN9wavd8nyaTcidA==} - engines: {node: '>=14.0.0'} - dependencies: - '@aws-sdk/property-provider': 3.341.0 - '@aws-sdk/types': 3.341.0 - tslib: 2.5.0 - dev: false + '@aws-sdk/credential-provider-env@3.679.0': + resolution: {integrity: sha512-EdlTYbzMm3G7VUNAMxr9S1nC1qUNqhKlAxFU8E7cKsAe8Bp29CD5HAs3POc56AVo9GC4yRIS+/mtlZSmrckzUA==} + engines: {node: '>=16.0.0'} - /@aws-sdk/credential-provider-imds@3.341.0: - resolution: {integrity: sha512-dkvc7WcKxFR0KgAScbRQ16wg0qH8tKxaVS3hfgHDYJR4UIKqV/sM6r5H2OvAdegY9/T180O2srtq2N4pyywPSQ==} - engines: {node: '>=14.0.0'} - dependencies: - '@aws-sdk/node-config-provider': 3.341.0 - '@aws-sdk/property-provider': 3.341.0 - '@aws-sdk/types': 3.341.0 - '@aws-sdk/url-parser': 3.341.0 - tslib: 2.5.0 - dev: false + '@aws-sdk/credential-provider-http@3.679.0': + resolution: {integrity: sha512-ZoKLubW5DqqV1/2a3TSn+9sSKg0T8SsYMt1JeirnuLJF0mCoYFUaWMyvxxKuxPoqvUsaycxKru4GkpJ10ltNBw==} + engines: {node: '>=16.0.0'} - /@aws-sdk/credential-provider-ini@3.341.0: - resolution: {integrity: sha512-o+44n3VFErRNOHZi4Psbu0JQWB7RT15QJzRtYquAPohgKNDT9jQ/VN0JesEVyktpIklsPJBNAbDb68fExsjKUg==} - engines: {node: '>=14.0.0'} - dependencies: - '@aws-sdk/credential-provider-env': 3.341.0 - '@aws-sdk/credential-provider-imds': 3.341.0 - '@aws-sdk/credential-provider-process': 3.341.0 - '@aws-sdk/credential-provider-sso': 3.341.0 - '@aws-sdk/credential-provider-web-identity': 3.341.0 - '@aws-sdk/property-provider': 3.341.0 - '@aws-sdk/shared-ini-file-loader': 3.341.0 - '@aws-sdk/types': 3.341.0 - tslib: 2.5.0 - transitivePeerDependencies: - - aws-crt - dev: false + '@aws-sdk/credential-provider-ini@3.682.0': + resolution: {integrity: sha512-6eqWeHdK6EegAxqDdiCi215nT3QZPwukgWAYuVxNfJ/5m0/P7fAzF+D5kKVgByUvGJEbq/FEL8Fw7OBe64AA+g==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-sts': ^3.682.0 - /@aws-sdk/credential-provider-node@3.341.0: - resolution: {integrity: sha512-O4gxP5PLu86361sGgnyxcwP5L2Ek7N65KM3MYJNgDpXRig+FP/pxBmdOtYkTYJYymPspGxnsNM6p2tbARJ1WMw==} - engines: {node: '>=14.0.0'} - dependencies: - '@aws-sdk/credential-provider-env': 3.341.0 - '@aws-sdk/credential-provider-imds': 3.341.0 - '@aws-sdk/credential-provider-ini': 3.341.0 - '@aws-sdk/credential-provider-process': 3.341.0 - '@aws-sdk/credential-provider-sso': 3.341.0 - '@aws-sdk/credential-provider-web-identity': 3.341.0 - '@aws-sdk/property-provider': 3.341.0 - '@aws-sdk/shared-ini-file-loader': 3.341.0 - '@aws-sdk/types': 3.341.0 - tslib: 2.5.0 - transitivePeerDependencies: - - aws-crt - dev: false + '@aws-sdk/credential-provider-node@3.682.0': + resolution: {integrity: sha512-HSmDqZcBVZrTctHCT9m++vdlDfJ1ARI218qmZa+TZzzOFNpKWy6QyHMEra45GB9GnkkMmV6unoDSPMuN0AqcMg==} + engines: {node: '>=16.0.0'} - /@aws-sdk/credential-provider-process@3.341.0: - resolution: {integrity: sha512-t+12surwkdZO7dAdtA7xz+O6Hk0H3yOVqXH+Y8UINXbFc9/uUgstPZq5qhpQIeDPRes/lqYUiZ4tho5mhpGItw==} - engines: {node: '>=14.0.0'} - dependencies: - '@aws-sdk/property-provider': 3.341.0 - '@aws-sdk/shared-ini-file-loader': 3.341.0 - '@aws-sdk/types': 3.341.0 - tslib: 2.5.0 - dev: false + '@aws-sdk/credential-provider-process@3.679.0': + resolution: {integrity: sha512-u/p4TV8kQ0zJWDdZD4+vdQFTMhkDEJFws040Gm113VHa/Xo1SYOjbpvqeuFoz6VmM0bLvoOWjxB9MxnSQbwKpQ==} + engines: {node: '>=16.0.0'} - /@aws-sdk/credential-provider-sso@3.341.0: - resolution: {integrity: sha512-ooOntFPVwawHO8WwwTSDFysno+nZFt1+GPlAr9N9QxIWQSRZYLaNFeplnxT/58jJoMcyHH5JjY4Zu4g46htmkw==} - engines: {node: '>=14.0.0'} - dependencies: - '@aws-sdk/client-sso': 3.341.0 - '@aws-sdk/property-provider': 3.341.0 - '@aws-sdk/shared-ini-file-loader': 3.341.0 - '@aws-sdk/token-providers': 3.341.0 - '@aws-sdk/types': 3.341.0 - tslib: 2.5.0 - transitivePeerDependencies: - - aws-crt - dev: false + '@aws-sdk/credential-provider-sso@3.682.0': + resolution: {integrity: sha512-h7IH1VsWgV6YAJSWWV6y8uaRjGqLY3iBpGZlXuTH/c236NMLaNv+WqCBLeBxkFGUb2WeQ+FUPEJDCD69rgLIkg==} + engines: {node: '>=16.0.0'} - /@aws-sdk/credential-provider-web-identity@3.341.0: - resolution: {integrity: sha512-NxoyxnxiycO1IG3FG7/soE3CYLLwuc4pv5PWoMHwy6VjH5yiUIeC8CQHHCF5ndCvB/p5XW1TT5z3YmWKknGqaw==} - engines: {node: '>=14.0.0'} - dependencies: - '@aws-sdk/property-provider': 3.341.0 - '@aws-sdk/types': 3.341.0 - tslib: 2.5.0 - dev: false + '@aws-sdk/credential-provider-web-identity@3.679.0': + resolution: {integrity: sha512-a74tLccVznXCaBefWPSysUcLXYJiSkeUmQGtalNgJ1vGkE36W5l/8czFiiowdWdKWz7+x6xf0w+Kjkjlj42Ung==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-sts': ^3.679.0 - /@aws-sdk/eventstream-codec@3.341.0: - resolution: {integrity: sha512-pzrtuDTqiZG9L7ArOzhVHmXwtcVUsf5nckjerTSSjYeGTb4SPMVAas95NkawBJ9bTUGQZKH0aQGqplVLL6i98Q==} - dependencies: - '@aws-crypto/crc32': 3.0.0 - '@aws-sdk/types': 3.341.0 - '@aws-sdk/util-hex-encoding': 3.310.0 - tslib: 2.5.0 - dev: false + '@aws-sdk/middleware-bucket-endpoint@3.679.0': + resolution: {integrity: sha512-5EpiPhhGgnF+uJR4DzWUk6Lx3pOn9oM6JGXxeHsiynfoBfq7vHMleq+uABHHSQS+y7XzbyZ7x8tXNQlliMwOsg==} + engines: {node: '>=16.0.0'} - /@aws-sdk/eventstream-serde-browser@3.341.0: - resolution: {integrity: sha512-9Z0fyF+9hlHfxL+yo2zwuKxWO07Qd+8fwwy1zTa0Gwd5mI5jm0idBMHdayIlX7MgijDX5Be+hzjAR2nmAlYKLg==} - engines: {node: '>=14.0.0'} - dependencies: - '@aws-sdk/eventstream-serde-universal': 3.341.0 - '@aws-sdk/types': 3.341.0 - tslib: 2.5.0 - dev: false + '@aws-sdk/middleware-expect-continue@3.679.0': + resolution: {integrity: sha512-nYsh9PdWrF4EahTRdXHGlNud82RPc508CNGdh1lAGfPU3tNveGfMBX3PcGBtPOse3p9ebNKRWVmUc9eXSjGvHA==} + engines: {node: '>=16.0.0'} - /@aws-sdk/eventstream-serde-config-resolver@3.341.0: - resolution: {integrity: sha512-+MzhKD3pqTGrN2jTbmvoQ1b3yX94TjTyTUFZHaaTUEoSPPnFmaAIBH29EvfsyukwqcDJZ4+/6a8VKZz6IfRIJQ==} - engines: {node: '>=14.0.0'} - dependencies: - '@aws-sdk/types': 3.341.0 - tslib: 2.5.0 - dev: false + '@aws-sdk/middleware-flexible-checksums@3.682.0': + resolution: {integrity: sha512-5u1STth6iZUtAvPDO0NJVYKUX2EYKU7v84MYYaZ3O27HphRjFqDos0keL2KTnHn/KmMD68rM3yiUareWR8hnAQ==} + engines: {node: '>=16.0.0'} - /@aws-sdk/eventstream-serde-node@3.341.0: - resolution: {integrity: sha512-4Px+8phhjgcPjSaOdxqxbsrwuLsqoVhDOray+DZXxjvOTnRjnfzoAXXhhzcxTbRttyDJZ9KLSfuDl1YI8yXPoA==} - engines: {node: '>=14.0.0'} - dependencies: - '@aws-sdk/eventstream-serde-universal': 3.341.0 - '@aws-sdk/types': 3.341.0 - tslib: 2.5.0 - dev: false + '@aws-sdk/middleware-host-header@3.679.0': + resolution: {integrity: sha512-y176HuQ8JRY3hGX8rQzHDSbCl9P5Ny9l16z4xmaiLo+Qfte7ee4Yr3yaAKd7GFoJ3/Mhud2XZ37fR015MfYl2w==} + engines: {node: '>=16.0.0'} - /@aws-sdk/eventstream-serde-universal@3.341.0: - resolution: {integrity: sha512-LUn2k/3KJtfBnvcXNGFPOJxJu6FOm3aksdl+NQ6Qr3T3DbsbOdjakZrXv93POcxAyXqtLs9ItJcC75dm1pOQ8Q==} - engines: {node: '>=14.0.0'} - dependencies: - '@aws-sdk/eventstream-codec': 3.341.0 - '@aws-sdk/types': 3.341.0 - tslib: 2.5.0 - dev: false + '@aws-sdk/middleware-location-constraint@3.679.0': + resolution: {integrity: sha512-SA1C1D3XgoKTGxyNsOqd016ONpk46xJLWDgJUd00Zb21Ox5wYCoY6aDRKiaMRW+1VfCJdezs1Do3XLyIU9KxyA==} + engines: {node: '>=16.0.0'} - /@aws-sdk/fetch-http-handler@3.341.0: - resolution: {integrity: sha512-GclHYOFKmhft9Bp+wX4CgVv4q48P/md/xiKN7kJpGUvazCRhBN7DqfcvMoMCPhobh+llYSefyiqJBxEccEq/TA==} - dependencies: - '@aws-sdk/protocol-http': 3.341.0 - '@aws-sdk/querystring-builder': 3.341.0 - '@aws-sdk/types': 3.341.0 - '@aws-sdk/util-base64': 3.310.0 - tslib: 2.5.0 - dev: false + '@aws-sdk/middleware-logger@3.679.0': + resolution: {integrity: sha512-0vet8InEj7nvIvGKk+ch7bEF5SyZ7Us9U7YTEgXPrBNStKeRUsgwRm0ijPWWd0a3oz2okaEwXsFl7G/vI0XiEA==} + engines: {node: '>=16.0.0'} - /@aws-sdk/hash-blob-browser@3.341.0: - resolution: {integrity: sha512-qlSGkcbSpJs/vQYuD4dEnbHAFi8/+ealpAL46Lg3hkjFKiL6khoMwnEljF5QdmHtI/LNN9Hhtz6oJu2ipFAXRg==} - dependencies: - '@aws-sdk/chunked-blob-reader': 3.310.0 - '@aws-sdk/types': 3.341.0 - tslib: 2.5.0 - dev: false + '@aws-sdk/middleware-recursion-detection@3.679.0': + resolution: {integrity: sha512-sQoAZFsQiW/LL3DfKMYwBoGjYDEnMbA9WslWN8xneCmBAwKo6IcSksvYs23PP8XMIoBGe2I2J9BSr654XWygTQ==} + engines: {node: '>=16.0.0'} - /@aws-sdk/hash-node@3.341.0: - resolution: {integrity: sha512-legGgjmhQnA8veIuVjJEVNE18YIhWj6JaLNm47xgiBbEoZ1BApoBY1GsX1AunV6FrA18fRpCeTynW3u4MVIIow==} - engines: {node: '>=14.0.0'} - dependencies: - '@aws-sdk/types': 3.341.0 - '@aws-sdk/util-buffer-from': 3.310.0 - '@aws-sdk/util-utf8': 3.310.0 - tslib: 2.5.0 - dev: false + '@aws-sdk/middleware-sdk-s3@3.682.0': + resolution: {integrity: sha512-Tqndx8elRD4xDR8f5Cng6jpZ/odcm1ZTOtGRFMzHgOCij4BeMf4+/+ecQScobcrAZpUTCUTCzaTvdCdJw8MYJA==} + engines: {node: '>=16.0.0'} - /@aws-sdk/hash-stream-node@3.341.0: - resolution: {integrity: sha512-6IXzBgEt5+IK5OCD2F6dR8uA1MHzaDkU1n/vyhNl3w9jadtt0gcyinnTFJHbVmyKM+HcZFgr/nRaoUIwNJlFKA==} - engines: {node: '>=14.0.0'} - dependencies: - '@aws-sdk/types': 3.341.0 - '@aws-sdk/util-utf8': 3.310.0 - tslib: 2.5.0 - dev: false + '@aws-sdk/middleware-ssec@3.679.0': + resolution: {integrity: sha512-4GNUxXbs1M71uFHRiCAZtN0/g23ogI9YjMe5isAuYMHXwDB3MhqF7usKf954mBP6tplvN44vYlbJ84faaLrTtg==} + engines: {node: '>=16.0.0'} - /@aws-sdk/invalid-dependency@3.341.0: - resolution: {integrity: sha512-1GNk+J/Q76bzuj27OqPOqTmFGEYXtyB02afn0cIeadDf0qbjo3G7qkRd3jrJrBIdHNg+DYbBYEHfFB5EuK1s8g==} - dependencies: - '@aws-sdk/types': 3.341.0 - tslib: 2.5.0 - dev: false + '@aws-sdk/middleware-user-agent@3.682.0': + resolution: {integrity: sha512-7TyvYR9HdGH1/Nq0eeApUTM4izB6rExiw87khVYuJwZHr6FmvIL1FsOVFro/4WlXa0lg4LiYOm/8H8dHv+fXTg==} + engines: {node: '>=16.0.0'} - /@aws-sdk/is-array-buffer@3.310.0: - resolution: {integrity: sha512-urnbcCR+h9NWUnmOtet/s4ghvzsidFmspfhYaHAmSRdy9yDjdjBJMFjjsn85A1ODUktztm+cVncXjQ38WCMjMQ==} - engines: {node: '>=14.0.0'} - dependencies: - tslib: 2.5.0 - dev: false + '@aws-sdk/region-config-resolver@3.679.0': + resolution: {integrity: sha512-Ybx54P8Tg6KKq5ck7uwdjiKif7n/8g1x+V0V9uTjBjRWqaIgiqzXwKWoPj6NCNkE7tJNtqI4JrNxp/3S3HvmRw==} + engines: {node: '>=16.0.0'} - /@aws-sdk/md5-js@3.341.0: - resolution: {integrity: sha512-0e24Kf8Wu0my2sSQvNUoKqFVvUEflBkZv4ZNiJTm2ccKy6cRbMaTcHrPWj9mc8RdTzgGKB8lSoafNRGpVN/oJw==} - dependencies: - '@aws-sdk/types': 3.341.0 - '@aws-sdk/util-utf8': 3.310.0 - tslib: 2.5.0 - dev: false + '@aws-sdk/signature-v4-multi-region@3.682.0': + resolution: {integrity: sha512-y7RAQSCb9pH8wCX5We9UXfiqPVwBLLvSljhuXC31mibHmYaZnpNEwHiQlRNQPblyaNpiKnXXQ0H3Ns3FDyDYdQ==} + engines: {node: '>=16.0.0'} - /@aws-sdk/middleware-bucket-endpoint@3.341.0: - resolution: {integrity: sha512-SiufX5/msEn9fBhGMM0gXBYZPSvnRGKOOkxcygTan6OGLpKctZL5+MVTzZ2BkcMsqNQ2YtAmYVDW/UrmrS0Yow==} - engines: {node: '>=14.0.0'} - dependencies: - '@aws-sdk/protocol-http': 3.341.0 - '@aws-sdk/types': 3.341.0 - '@aws-sdk/util-arn-parser': 3.310.0 - '@aws-sdk/util-config-provider': 3.310.0 - tslib: 2.5.0 - dev: false + '@aws-sdk/token-providers@3.679.0': + resolution: {integrity: sha512-1/+Zso/x2jqgutKixYFQEGli0FELTgah6bm7aB+m2FAWH4Hz7+iMUsazg6nSWm714sG9G3h5u42Dmpvi9X6/hA==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-sso-oidc': ^3.679.0 - /@aws-sdk/middleware-content-length@3.341.0: - resolution: {integrity: sha512-m663QyL1qvMnXOsXUtLvQz5B5l1kulQMTOwVxxhefPxO0nZbIlXdMQurEOwI1FHfaMnR8UQ1FfPLY6GIkJkX/g==} - engines: {node: '>=14.0.0'} - dependencies: - '@aws-sdk/protocol-http': 3.341.0 - '@aws-sdk/types': 3.341.0 - tslib: 2.5.0 - dev: false + '@aws-sdk/types@3.679.0': + resolution: {integrity: sha512-NwVq8YvInxQdJ47+zz4fH3BRRLC6lL+WLkvr242PVBbUOLRyK/lkwHlfiKUoeVIMyK5NF+up6TRg71t/8Bny6Q==} + engines: {node: '>=16.0.0'} - /@aws-sdk/middleware-endpoint@3.341.0: - resolution: {integrity: sha512-IucQO8JjAjxnA+7RI1tkxOjUtpuUpkS3v56u5vk6Oypq1iovVBvD8yiFBR2wPh9X/O+3Ts7eWsZ4jhuf4JQQUQ==} - engines: {node: '>=14.0.0'} - dependencies: - '@aws-sdk/middleware-serde': 3.341.0 - '@aws-sdk/types': 3.341.0 - '@aws-sdk/url-parser': 3.341.0 - '@aws-sdk/util-middleware': 3.341.0 - tslib: 2.5.0 - dev: false + '@aws-sdk/util-arn-parser@3.679.0': + resolution: {integrity: sha512-CwzEbU8R8rq9bqUFryO50RFBlkfufV9UfMArHPWlo+lmsC+NlSluHQALoj6Jkq3zf5ppn1CN0c1DDLrEqdQUXg==} + engines: {node: '>=16.0.0'} - /@aws-sdk/middleware-expect-continue@3.341.0: - resolution: {integrity: sha512-bu8ICil5HFVJO8wohLIah+Nq863Y6EiLeSYfY2Sh5q6cJakt5uS6yRJ4nICLN9mf4nMD79wqH3pSLfCdVMBlSA==} - engines: {node: '>=14.0.0'} - dependencies: - '@aws-sdk/protocol-http': 3.341.0 - '@aws-sdk/types': 3.341.0 - tslib: 2.5.0 - dev: false + '@aws-sdk/util-endpoints@3.679.0': + resolution: {integrity: sha512-YL6s4Y/1zC45OvddvgE139fjeWSKKPgLlnfrvhVL7alNyY9n7beR4uhoDpNrt5mI6sn9qiBF17790o+xLAXjjg==} + engines: {node: '>=16.0.0'} - /@aws-sdk/middleware-flexible-checksums@3.341.0: - resolution: {integrity: sha512-EM6qls2110+9Ioz1XHIDOtHrNTfDzcnqHm5YV51o2srD3nyR0uPLZgBMxNqolnRIsAn+1TVNQAlML/bEP4ZUEw==} - engines: {node: '>=14.0.0'} - dependencies: - '@aws-crypto/crc32': 3.0.0 - '@aws-crypto/crc32c': 3.0.0 - '@aws-sdk/is-array-buffer': 3.310.0 - '@aws-sdk/protocol-http': 3.341.0 - '@aws-sdk/types': 3.341.0 - '@aws-sdk/util-utf8': 3.310.0 - tslib: 2.5.0 - dev: false - - /@aws-sdk/middleware-host-header@3.341.0: - resolution: {integrity: sha512-1AidcDDtGd7LUGtDV+F+FuLb6NmBlpupY2FpuqasUDH5f7ExcEbPaGYjOp4ozktQ6Qb0c0oJ4qxhv5sDos5Qww==} - engines: {node: '>=14.0.0'} - dependencies: - '@aws-sdk/protocol-http': 3.341.0 - '@aws-sdk/types': 3.341.0 - tslib: 2.5.0 - dev: false + '@aws-sdk/util-locate-window@3.679.0': + resolution: {integrity: sha512-zKTd48/ZWrCplkXpYDABI74rQlbR0DNHs8nH95htfSLj9/mWRSwaGptoxwcihaq/77vi/fl2X3y0a1Bo8bt7RA==} + engines: {node: '>=16.0.0'} - /@aws-sdk/middleware-location-constraint@3.341.0: - resolution: {integrity: sha512-uBiiEfM0ctvdHIADZkcMnrv5ZxcA1KPkelTrjsXod9IfiWIMAhCGJVS1OLbjCQevGB1e1XdgNrFmRVq2Dvs7hQ==} - engines: {node: '>=14.0.0'} - dependencies: - '@aws-sdk/types': 3.341.0 - tslib: 2.5.0 - dev: false + '@aws-sdk/util-user-agent-browser@3.679.0': + resolution: {integrity: sha512-CusSm2bTBG1kFypcsqU8COhnYc6zltobsqs3nRrvYqYaOqtMnuE46K4XTWpnzKgwDejgZGOE+WYyprtAxrPvmQ==} - /@aws-sdk/middleware-logger@3.341.0: - resolution: {integrity: sha512-JQgnPQMlNs542SL/ANbQSWbPMeWeY4+GW/fEfSRgM9uVBZaKPO5dZW7F761odxoK7f3qPSujEZb0ECmTYaEqgg==} - engines: {node: '>=14.0.0'} - dependencies: - '@aws-sdk/types': 3.341.0 - tslib: 2.5.0 - dev: false + '@aws-sdk/util-user-agent-node@3.682.0': + resolution: {integrity: sha512-so5s+j0gPoTS0HM4HPL+G0ajk0T6cQAg8JXzRgvyiQAxqie+zGCZAV3VuVeMNWMVbzsgZl0pYZaatPFTLG/AxA==} + engines: {node: '>=16.0.0'} + peerDependencies: + aws-crt: '>=1.0.0' + peerDependenciesMeta: + aws-crt: + optional: true - /@aws-sdk/middleware-recursion-detection@3.341.0: - resolution: {integrity: sha512-DAfZgW7Bb4MB4ZHsjAhiaCXKqTeUFWazrgmNHAa8FkSE8MmgJv/4k8tvnWDtxkAO2GBOwqg7wG9LspeBG/RGMw==} - engines: {node: '>=14.0.0'} - dependencies: - '@aws-sdk/protocol-http': 3.341.0 - '@aws-sdk/types': 3.341.0 - tslib: 2.5.0 - dev: false + '@aws-sdk/xml-builder@3.679.0': + resolution: {integrity: sha512-nPmhVZb39ty5bcQ7mAwtjezBcsBqTYZ9A2D9v/lE92KCLdu5RhSkPH7O71ZqbZx1mUSg9fAOxHPiG79U5VlpLQ==} + engines: {node: '>=16.0.0'} - /@aws-sdk/middleware-retry@3.341.0: - resolution: {integrity: sha512-EyL2N4ohUQc4CdlccydPuybd4ngVx12MTSIGQVc0PcMZ39ayPbgYOZyCkAGb1rRCEIMayMrF9hF4GfNo4BvV5g==} - engines: {node: '>=14.0.0'} - dependencies: - '@aws-sdk/protocol-http': 3.341.0 - '@aws-sdk/service-error-classification': 3.341.0 - '@aws-sdk/types': 3.341.0 - '@aws-sdk/util-middleware': 3.341.0 - '@aws-sdk/util-retry': 3.341.0 - tslib: 2.5.0 - uuid: 8.3.2 - dev: false + '@babel/code-frame@7.26.0': + resolution: {integrity: sha512-INCKxTtbXtcNbUZ3YXutwMpEleqttcswhAdee7dhuoVrD2cnuc3PqtERBtxkX5nziX9vnBL8WXmSGwv8CuPV6g==} + engines: {node: '>=6.9.0'} - /@aws-sdk/middleware-sdk-s3@3.341.0: - resolution: {integrity: sha512-xN5e8JCq6TfBSWLjPfAbKW5ISk80fUVOE2yen0KA0HuqVI0LW6CaIvQuezRtZRpnzFVG/cgLzIBFaUktTh48Aw==} - engines: {node: '>=14.0.0'} - dependencies: - '@aws-sdk/protocol-http': 3.341.0 - '@aws-sdk/types': 3.341.0 - '@aws-sdk/util-arn-parser': 3.310.0 - tslib: 2.5.0 - dev: false + '@babel/code-frame@7.26.2': + resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} + engines: {node: '>=6.9.0'} - /@aws-sdk/middleware-sdk-sts@3.341.0: - resolution: {integrity: sha512-A7sXKhL9wzVqeq7sy3eWx7D5FPWdzrJTVpRRBYez/qBY4UjfLcxF9zpB0mw/xhQ7PUta7iJ5tSlPmZZQ4MBnmg==} - engines: {node: '>=14.0.0'} - dependencies: - '@aws-sdk/middleware-signing': 3.341.0 - '@aws-sdk/types': 3.341.0 - tslib: 2.5.0 - dev: false + '@babel/code-frame@7.27.1': + resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} + engines: {node: '>=6.9.0'} - /@aws-sdk/middleware-serde@3.341.0: - resolution: {integrity: sha512-FaPpbEGo/OETxnexg+TYet71fP6dM+y3NPXRUrd5dAulVhmr+CkdzOxqkqtMC1HinKAqHdUpjlsuf7gYg14deQ==} - engines: {node: '>=14.0.0'} - dependencies: - '@aws-sdk/types': 3.341.0 - tslib: 2.5.0 - dev: false + '@babel/compat-data@7.26.0': + resolution: {integrity: sha512-qETICbZSLe7uXv9VE8T/RWOdIE5qqyTucOt4zLYMafj2MRO271VGgLd4RACJMeBO37UPWhXiKMBk7YlJ0fOzQA==} + engines: {node: '>=6.9.0'} - /@aws-sdk/middleware-signing@3.341.0: - resolution: {integrity: sha512-KbyBIblgrGRizFJF3sLvx+ON84qOBHrDztNc++Xg2/UmdjLuEtm1sVvpQ2SxrKcfumx+ztVOuDpbD5pd0lBkOA==} - engines: {node: '>=14.0.0'} - dependencies: - '@aws-sdk/property-provider': 3.341.0 - '@aws-sdk/protocol-http': 3.341.0 - '@aws-sdk/signature-v4': 3.341.0 - '@aws-sdk/types': 3.341.0 - '@aws-sdk/util-middleware': 3.341.0 - tslib: 2.5.0 - dev: false + '@babel/compat-data@7.28.0': + resolution: {integrity: sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==} + engines: {node: '>=6.9.0'} - /@aws-sdk/middleware-ssec@3.341.0: - resolution: {integrity: sha512-Ij7u1R5aIrBk08MGTU2tXejKYeGEeayapmAg3TbwJroYZ78ZueWf/lJKWOOhLKQ9O3Jd31sQEAMt2n5rDaMCuA==} - engines: {node: '>=14.0.0'} - dependencies: - '@aws-sdk/types': 3.341.0 - tslib: 2.5.0 - dev: false + '@babel/core@7.26.0': + resolution: {integrity: sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==} + engines: {node: '>=6.9.0'} - /@aws-sdk/middleware-stack@3.341.0: - resolution: {integrity: sha512-pBAN9T4tBSHp7gJKu0Ma0MepZqP3GvecEh7eZWwTrJrRMgY1k8M1zpUOXBFG6EVRzVdyy7A4DWSuD4kcXE+BIw==} - engines: {node: '>=14.0.0'} - dependencies: - tslib: 2.5.0 - dev: false + '@babel/core@7.26.10': + resolution: {integrity: sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==} + engines: {node: '>=6.9.0'} - /@aws-sdk/middleware-user-agent@3.341.0: - resolution: {integrity: sha512-9gpVXPMkgwv0yV1Lbuc9bvb3MHLEjj5whgbM1WNrtRiogeFwWB0cxOQQgb2cY+xgQLMLnH7VvfRGN79TNVnI7w==} - engines: {node: '>=14.0.0'} - dependencies: - '@aws-sdk/protocol-http': 3.341.0 - '@aws-sdk/types': 3.341.0 - '@aws-sdk/util-endpoints': 3.341.0 - tslib: 2.5.0 - dev: false + '@babel/eslint-parser@7.25.9': + resolution: {integrity: sha512-5UXfgpK0j0Xr/xIdgdLEhOFxaDZ0bRPWJJchRpqOSur/3rZoPbqqki5mm0p4NE2cs28krBEiSM2MB7//afRSQQ==} + engines: {node: ^10.13.0 || ^12.13.0 || >=14.0.0} + peerDependencies: + '@babel/core': ^7.11.0 + eslint: ^7.5.0 || ^8.0.0 || ^9.0.0 - /@aws-sdk/node-config-provider@3.341.0: - resolution: {integrity: sha512-Le/WSf9t0ItrHuGhOlEPiA+J/nYpYJaC1WgLefRG9j17xKF/fp7X+XSylF1xQ+cOMbHFMi7SNcXRhyfwFreU0w==} - engines: {node: '>=14.0.0'} - dependencies: - '@aws-sdk/property-provider': 3.341.0 - '@aws-sdk/shared-ini-file-loader': 3.341.0 - '@aws-sdk/types': 3.341.0 - tslib: 2.5.0 - dev: false + '@babel/generator@7.26.0': + resolution: {integrity: sha512-/AIkAmInnWwgEAJGQr9vY0c66Mj6kjkE2ZPB1PurTRaRAh3U+J45sAQMjQDJdh4WbR3l0x5xkimXBKyBXXAu2w==} + engines: {node: '>=6.9.0'} - /@aws-sdk/node-http-handler@3.341.0: - resolution: {integrity: sha512-pXkX1amhWhkgffs76bf+pR814JnBUa199Nb6Wm2l20LLPgSsQrRX0IwucM/KtAdEhFeJTR8bLhRJBzrm3ONMwQ==} - engines: {node: '>=14.0.0'} - dependencies: - '@aws-sdk/abort-controller': 3.341.0 - '@aws-sdk/protocol-http': 3.341.0 - '@aws-sdk/querystring-builder': 3.341.0 - '@aws-sdk/types': 3.341.0 - tslib: 2.5.0 - dev: false + '@babel/generator@7.28.0': + resolution: {integrity: sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==} + engines: {node: '>=6.9.0'} - /@aws-sdk/property-provider@3.341.0: - resolution: {integrity: sha512-5Ynks9iUS/VJcZa/J3c5G+XAaUw4SkWltQp4Y7dzWpJXS3AaUm9gby7DsWYopbH5dLeeSmvKENHTsKpsM5pQoQ==} - engines: {node: '>=14.0.0'} - dependencies: - '@aws-sdk/types': 3.341.0 - tslib: 2.5.0 - dev: false + '@babel/helper-annotate-as-pure@7.25.9': + resolution: {integrity: sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==} + engines: {node: '>=6.9.0'} - /@aws-sdk/protocol-http@3.341.0: - resolution: {integrity: sha512-Av+qcOA+gYXWgYYtzbTn2goHGlNdt+nbrzhUI6dWczYPzBe8aNnMR5d/ACNt0mD7O41muULmSTQBHOKBmOmNJA==} - engines: {node: '>=14.0.0'} - dependencies: - '@aws-sdk/types': 3.341.0 - tslib: 2.5.0 - dev: false + '@babel/helper-builder-binary-assignment-operator-visitor@7.25.9': + resolution: {integrity: sha512-C47lC7LIDCnz0h4vai/tpNOI95tCd5ZT3iBt/DBH5lXKHZsyNQv18yf1wIIg2ntiQNgmAvA+DgZ82iW8Qdym8g==} + engines: {node: '>=6.9.0'} - /@aws-sdk/querystring-builder@3.341.0: - resolution: {integrity: sha512-71RNU1VB8tSfqeu9t61YWDmMS/5C9NH8ua6LXb5HGzlnq68BSLq3lGa4zx/h2K0OpZ6ERUORFdw6pmqA17pDmg==} - engines: {node: '>=14.0.0'} - dependencies: - '@aws-sdk/types': 3.341.0 - '@aws-sdk/util-uri-escape': 3.310.0 - tslib: 2.5.0 - dev: false + '@babel/helper-compilation-targets@7.25.9': + resolution: {integrity: sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==} + engines: {node: '>=6.9.0'} - /@aws-sdk/querystring-parser@3.341.0: - resolution: {integrity: sha512-7O61bRUdo2x73TTeyyriibLpx9tUPO13gBdI9oHpJTZEkOiUYxoxjMdo5s7ofis12MdmzelqIUjPmAvJNtB5wA==} - engines: {node: '>=14.0.0'} - dependencies: - '@aws-sdk/types': 3.341.0 - tslib: 2.5.0 - dev: false + '@babel/helper-compilation-targets@7.27.2': + resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} + engines: {node: '>=6.9.0'} - /@aws-sdk/service-error-classification@3.341.0: - resolution: {integrity: sha512-mqf9rV/j8Ry2RWDQsaJDPyDm/VDQ4ZDdmi8rzm/AeSgEDaNWublt31zlWtgUWfjISfTtbBgzI48n6ZBJqyYmUA==} - engines: {node: '>=14.0.0'} - dev: false + '@babel/helper-create-class-features-plugin@7.25.9': + resolution: {integrity: sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 - /@aws-sdk/shared-ini-file-loader@3.341.0: - resolution: {integrity: sha512-/ikCc2XRL8zG//EB2g6SXFvMMa7KD2T8RpM4dYEisdvHDoF9khVX3/SLLUs2Y32rr1x9tnh7X9DdV8lX9MP/ZA==} - engines: {node: '>=14.0.0'} - dependencies: - '@aws-sdk/types': 3.341.0 - tslib: 2.5.0 - dev: false + '@babel/helper-create-regexp-features-plugin@7.25.9': + resolution: {integrity: sha512-ORPNZ3h6ZRkOyAa/SaHU+XsLZr0UQzRwuDQ0cczIA17nAzZ+85G5cVkOJIj7QavLZGSe8QXUmNFxSZzjcZF9bw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 - /@aws-sdk/signature-v4-multi-region@3.341.0: - resolution: {integrity: sha512-1YCciA4740Ev9Rj2YSTzuN3ea/xgGxlcrwC4OBOKslO4YQkwFvIogL7GToZzrWeObGImW6PPsS+tN44Rkk2Jyg==} - engines: {node: '>=14.0.0'} + '@babel/helper-define-polyfill-provider@0.6.2': + resolution: {integrity: sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ==} peerDependencies: - '@aws-sdk/signature-v4-crt': ^3.118.0 - peerDependenciesMeta: - '@aws-sdk/signature-v4-crt': - optional: true - dependencies: - '@aws-sdk/protocol-http': 3.341.0 - '@aws-sdk/signature-v4': 3.341.0 - '@aws-sdk/types': 3.341.0 - tslib: 2.5.0 - dev: false + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 - /@aws-sdk/signature-v4@3.341.0: - resolution: {integrity: sha512-4nWgDiw68lEFBwrQY1JsKBvcMmcRhClpWzi3iKHwU1PJogId6g4XRU89Zo0QzBQihmFZIKklRSF/150pbNGdcg==} - engines: {node: '>=14.0.0'} - dependencies: - '@aws-sdk/is-array-buffer': 3.310.0 - '@aws-sdk/types': 3.341.0 - '@aws-sdk/util-hex-encoding': 3.310.0 - '@aws-sdk/util-middleware': 3.341.0 - '@aws-sdk/util-uri-escape': 3.310.0 - '@aws-sdk/util-utf8': 3.310.0 - tslib: 2.5.0 - dev: false - - /@aws-sdk/smithy-client@3.341.0: - resolution: {integrity: sha512-4ReKR+UciZ012W8OimFZfGy1LRZeWcl/i7h6lTIiXasni4Q+5k1rqneCRtJpBgfB5YbJg4EawmWxsxChrX9pwQ==} - engines: {node: '>=14.0.0'} - dependencies: - '@aws-sdk/middleware-stack': 3.341.0 - '@aws-sdk/types': 3.341.0 - tslib: 2.5.0 - dev: false + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} - /@aws-sdk/token-providers@3.341.0: - resolution: {integrity: sha512-PNskQMWVV6hUTPZmwcfAU0nknY0+Ge60ZVdNx6B4bpor6oEXl/jUCDOmymyHJl85LiIN7AZDrwq1gpdsDDVhZw==} - engines: {node: '>=14.0.0'} - dependencies: - '@aws-sdk/client-sso-oidc': 3.341.0 - '@aws-sdk/property-provider': 3.341.0 - '@aws-sdk/shared-ini-file-loader': 3.341.0 - '@aws-sdk/types': 3.341.0 - tslib: 2.5.0 - transitivePeerDependencies: - - aws-crt - dev: false + '@babel/helper-member-expression-to-functions@7.25.9': + resolution: {integrity: sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==} + engines: {node: '>=6.9.0'} - /@aws-sdk/types@3.341.0: - resolution: {integrity: sha512-2KJf64BhJly/Ty35oWKlCElIqUP4kQ0LA+meSrgAmwl7oE0AYuO7V0ar1nsTGlsubYkLRvOuEhMcuNuumaUdoQ==} - engines: {node: '>=14.0.0'} - dependencies: - tslib: 2.5.0 - dev: false + '@babel/helper-module-imports@7.25.9': + resolution: {integrity: sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==} + engines: {node: '>=6.9.0'} - /@aws-sdk/url-parser@3.341.0: - resolution: {integrity: sha512-pAT+4Yp3NkHKnLhYjbFj9DGKTRavBIVoe0fbQLm1G/MD11FAlUOlswf5EYFjgz0wfdgnbyFE3ai837/eO0fGjw==} - dependencies: - '@aws-sdk/querystring-parser': 3.341.0 - '@aws-sdk/types': 3.341.0 - tslib: 2.5.0 - dev: false + '@babel/helper-module-transforms@7.26.0': + resolution: {integrity: sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 - /@aws-sdk/util-arn-parser@3.310.0: - resolution: {integrity: sha512-jL8509owp/xB9+Or0pvn3Fe+b94qfklc2yPowZZIFAkFcCSIdkIglz18cPDWnYAcy9JGewpMS1COXKIUhZkJsA==} - engines: {node: '>=14.0.0'} - dependencies: - tslib: 2.5.0 - dev: false - - /@aws-sdk/util-base64@3.310.0: - resolution: {integrity: sha512-v3+HBKQvqgdzcbL+pFswlx5HQsd9L6ZTlyPVL2LS9nNXnCcR3XgGz9jRskikRUuUvUXtkSG1J88GAOnJ/apTPg==} - engines: {node: '>=14.0.0'} - dependencies: - '@aws-sdk/util-buffer-from': 3.310.0 - tslib: 2.5.0 - dev: false - - /@aws-sdk/util-body-length-browser@3.310.0: - resolution: {integrity: sha512-sxsC3lPBGfpHtNTUoGXMQXLwjmR0zVpx0rSvzTPAuoVILVsp5AU/w5FphNPxD5OVIjNbZv9KsKTuvNTiZjDp9g==} - dependencies: - tslib: 2.5.0 - dev: false - - /@aws-sdk/util-body-length-node@3.310.0: - resolution: {integrity: sha512-2tqGXdyKhyA6w4zz7UPoS8Ip+7sayOg9BwHNidiGm2ikbDxm1YrCfYXvCBdwaJxa4hJfRVz+aL9e+d3GqPI9pQ==} - engines: {node: '>=14.0.0'} - dependencies: - tslib: 2.5.0 - dev: false - - /@aws-sdk/util-buffer-from@3.310.0: - resolution: {integrity: sha512-i6LVeXFtGih5Zs8enLrt+ExXY92QV25jtEnTKHsmlFqFAuL3VBeod6boeMXkN2p9lbSVVQ1sAOOYZOHYbYkntw==} - engines: {node: '>=14.0.0'} - dependencies: - '@aws-sdk/is-array-buffer': 3.310.0 - tslib: 2.5.0 - dev: false - - /@aws-sdk/util-config-provider@3.310.0: - resolution: {integrity: sha512-xIBaYo8dwiojCw8vnUcIL4Z5tyfb1v3yjqyJKJWV/dqKUFOOS0U591plmXbM+M/QkXyML3ypon1f8+BoaDExrg==} - engines: {node: '>=14.0.0'} - dependencies: - tslib: 2.5.0 - dev: false - - /@aws-sdk/util-defaults-mode-browser@3.341.0: - resolution: {integrity: sha512-FjZg0T/7lcEgeMTdC8iMaNcHlHwqVzS0FlyUdJrMZqXEo70h3KlByijv8N9VlV8XQUfiCPuTNlhpLpDEDMdtbw==} - engines: {node: '>= 10.0.0'} - dependencies: - '@aws-sdk/property-provider': 3.341.0 - '@aws-sdk/types': 3.341.0 - bowser: 2.11.0 - tslib: 2.5.0 - dev: false - - /@aws-sdk/util-defaults-mode-node@3.341.0: - resolution: {integrity: sha512-I943vmtUHqOdHvWCCnO3Vv/XXKCZ4O3XPNl9jvIC8m0n6CXqf7qjs9R72t3PQCTBAbsbIRGfkKMKfH2ab5ci/g==} - engines: {node: '>= 10.0.0'} - dependencies: - '@aws-sdk/config-resolver': 3.341.0 - '@aws-sdk/credential-provider-imds': 3.341.0 - '@aws-sdk/node-config-provider': 3.341.0 - '@aws-sdk/property-provider': 3.341.0 - '@aws-sdk/types': 3.341.0 - tslib: 2.5.0 - dev: false - - /@aws-sdk/util-endpoints@3.341.0: - resolution: {integrity: sha512-J15adxRV+YwWasOcCHDZoxXXQS7l8yBgWbId2ozGond/GwHifKhElF0RX9QKB4R/Vq1Cj7WVTyQsugYJAlLWrw==} - engines: {node: '>=14.0.0'} - dependencies: - '@aws-sdk/types': 3.341.0 - tslib: 2.5.0 - dev: false - - /@aws-sdk/util-hex-encoding@3.310.0: - resolution: {integrity: sha512-sVN7mcCCDSJ67pI1ZMtk84SKGqyix6/0A1Ab163YKn+lFBQRMKexleZzpYzNGxYzmQS6VanP/cfU7NiLQOaSfA==} - engines: {node: '>=14.0.0'} - dependencies: - tslib: 2.5.0 - dev: false - - /@aws-sdk/util-locate-window@3.310.0: - resolution: {integrity: sha512-qo2t/vBTnoXpjKxlsC2e1gBrRm80M3bId27r0BRB2VniSSe7bL1mmzM+/HFtujm0iAxtPM+aLEflLJlJeDPg0w==} - engines: {node: '>=14.0.0'} - dependencies: - tslib: 2.5.0 - dev: false - - /@aws-sdk/util-middleware@3.341.0: - resolution: {integrity: sha512-JsuZGZw9pMYMHzaJW31Y3Zl1Dh/R2PNVxnlQPsPy37RhHoSLPZ6wR9G828Y1ZY2thBpR+DU3D/VfNJOxBmWGkw==} - engines: {node: '>=14.0.0'} - dependencies: - tslib: 2.5.0 - dev: false - - /@aws-sdk/util-retry@3.341.0: - resolution: {integrity: sha512-UxAZiajjj65Zi9Q8PJ53vGydsa6dv9ZJC+bfO70czGGTWjB+L7kb2UoRdtGo9NBzRTFYXV6OjP6yBw5yFb39kw==} - engines: {node: '>= 14.0.0'} - dependencies: - '@aws-sdk/service-error-classification': 3.341.0 - tslib: 2.5.0 - dev: false - - /@aws-sdk/util-stream-browser@3.341.0: - resolution: {integrity: sha512-TLP5CBDebTtYZYpvFE+2L8jrcuFPCuhNDehhnp0NJZ2Uf2e7KlD8tn1Y40CctVOcpZ6vUQtGvHcc5ZWiNbHTLQ==} - dependencies: - '@aws-sdk/fetch-http-handler': 3.341.0 - '@aws-sdk/types': 3.341.0 - '@aws-sdk/util-base64': 3.310.0 - '@aws-sdk/util-hex-encoding': 3.310.0 - '@aws-sdk/util-utf8': 3.310.0 - tslib: 2.5.0 - dev: false - - /@aws-sdk/util-stream-node@3.341.0: - resolution: {integrity: sha512-tdPrw1YieZYwTJgOwBqNSwaVsiEov9LwegC+1fme9vw3KOmFHuEOXb0myAdrNP+u38Yn/G2C/SzcnEIQQtOkTQ==} - engines: {node: '>=14.0.0'} - dependencies: - '@aws-sdk/node-http-handler': 3.341.0 - '@aws-sdk/types': 3.341.0 - '@aws-sdk/util-buffer-from': 3.310.0 - tslib: 2.5.0 - dev: false - - /@aws-sdk/util-uri-escape@3.310.0: - resolution: {integrity: sha512-drzt+aB2qo2LgtDoiy/3sVG8w63cgLkqFIa2NFlGpUgHFWTXkqtbgf4L5QdjRGKWhmZsnqkbtL7vkSWEcYDJ4Q==} - engines: {node: '>=14.0.0'} - dependencies: - tslib: 2.5.0 - dev: false - - /@aws-sdk/util-user-agent-browser@3.341.0: - resolution: {integrity: sha512-VCgWM03lbcfjBQFhJJcF7Gs+XKxdtSYuK5txFiXy/glin5eCY+9VB3vqMrYwvEgIlwZynUYXvh9+OkU2AeRaBg==} - dependencies: - '@aws-sdk/types': 3.341.0 - bowser: 2.11.0 - tslib: 2.5.0 - dev: false - - /@aws-sdk/util-user-agent-node@3.341.0: - resolution: {integrity: sha512-ac1VcSQOn4fhif51hoSZtrfkFGZHNqXXv2mWzNo1xgsyDwCJh2/H6fPBBGtjT3TNc/o5yOuyBFRP4BIjJ5GkPA==} - engines: {node: '>=14.0.0'} - peerDependencies: - aws-crt: '>=1.0.0' - peerDependenciesMeta: - aws-crt: - optional: true - dependencies: - '@aws-sdk/node-config-provider': 3.341.0 - '@aws-sdk/types': 3.341.0 - tslib: 2.5.0 - dev: false - - /@aws-sdk/util-utf8-browser@3.259.0: - resolution: {integrity: sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==} - dependencies: - tslib: 2.5.0 - dev: false - - /@aws-sdk/util-utf8@3.310.0: - resolution: {integrity: sha512-DnLfFT8uCO22uOJc0pt0DsSNau1GTisngBCDw8jQuWT5CqogMJu4b/uXmwEqfj8B3GX6Xsz8zOd6JpRlPftQoA==} - engines: {node: '>=14.0.0'} - dependencies: - '@aws-sdk/util-buffer-from': 3.310.0 - tslib: 2.5.0 - dev: false - - /@aws-sdk/util-waiter@3.341.0: - resolution: {integrity: sha512-YNKFk3xwq3lgyZ+udgvwACE0thV6bWtSBeEoSuVIOVz6yR0Td9NsA+5gOBgAMKLWSoWP/4II57+ZnEEuhuyzzg==} - engines: {node: '>=14.0.0'} - dependencies: - '@aws-sdk/abort-controller': 3.341.0 - '@aws-sdk/types': 3.341.0 - tslib: 2.5.0 - dev: false - - /@aws-sdk/xml-builder@3.310.0: - resolution: {integrity: sha512-TqELu4mOuSIKQCqj63fGVs86Yh+vBx5nHRpWKNUNhB2nPTpfbziTs5c1X358be3peVWA4wPxW7Nt53KIg1tnNw==} - engines: {node: '>=14.0.0'} - dependencies: - tslib: 2.5.0 - dev: false - - /@babel/code-frame@7.21.4: - resolution: {integrity: sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/highlight': 7.18.6 - - /@babel/compat-data@7.21.4: - resolution: {integrity: sha512-/DYyDpeCfaVinT40FPGdkkb+lYSKvsVuMjDAG7jPOWWiM1ibOaB9CXJAlc4d1QpP/U2q2P9jbrSlClKSErd55g==} - engines: {node: '>=6.9.0'} - - /@babel/core@7.21.4: - resolution: {integrity: sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA==} - engines: {node: '>=6.9.0'} - dependencies: - '@ampproject/remapping': 2.2.1 - '@babel/code-frame': 7.21.4 - '@babel/generator': 7.21.4 - '@babel/helper-compilation-targets': 7.21.4(@babel/core@7.21.4) - '@babel/helper-module-transforms': 7.21.2 - '@babel/helpers': 7.21.0 - '@babel/parser': 7.21.4 - '@babel/template': 7.20.7 - '@babel/traverse': 7.21.4 - '@babel/types': 7.21.4 - convert-source-map: 1.9.0 - debug: 4.3.4 - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.0 - transitivePeerDependencies: - - supports-color - - /@babel/eslint-parser@7.21.3(@babel/core@7.21.4)(eslint@8.38.0): - resolution: {integrity: sha512-kfhmPimwo6k4P8zxNs8+T7yR44q1LdpsZdE1NkCsVlfiuTPRfnGgjaF8Qgug9q9Pou17u6wneYF0lDCZJATMFg==} - engines: {node: ^10.13.0 || ^12.13.0 || >=14.0.0} - peerDependencies: - '@babel/core': '>=7.11.0' - eslint: ^7.5.0 || ^8.0.0 - dependencies: - '@babel/core': 7.21.4 - '@nicolo-ribaudo/eslint-scope-5-internals': 5.1.1-v1 - eslint: 8.38.0 - eslint-visitor-keys: 2.1.0 - semver: 6.3.0 - dev: false - - /@babel/generator@7.21.4: - resolution: {integrity: sha512-NieM3pVIYW2SwGzKoqfPrQsf4xGs9M9AIG3ThppsSRmO+m7eQhmI6amajKMUeIO37wFfsvnvcxQFx6x6iqxDnA==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.21.4 - '@jridgewell/gen-mapping': 0.3.3 - '@jridgewell/trace-mapping': 0.3.18 - jsesc: 2.5.2 - - /@babel/helper-annotate-as-pure@7.18.6: - resolution: {integrity: sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.21.4 - - /@babel/helper-builder-binary-assignment-operator-visitor@7.18.9: - resolution: {integrity: sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw==} + '@babel/helper-optimise-call-expression@7.25.9': + resolution: {integrity: sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==} engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-explode-assignable-expression': 7.18.6 - '@babel/types': 7.21.4 - /@babel/helper-compilation-targets@7.21.4(@babel/core@7.21.4): - resolution: {integrity: sha512-Fa0tTuOXZ1iL8IeDFUWCzjZcn+sJGd9RZdH9esYVjEejGmzf+FFYQpMi/kZUk2kPy/q1H3/GPw7np8qar/stfg==} + '@babel/helper-plugin-utils@7.25.9': + resolution: {integrity: sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==} engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/compat-data': 7.21.4 - '@babel/core': 7.21.4 - '@babel/helper-validator-option': 7.21.0 - browserslist: 4.21.5 - lru-cache: 5.1.1 - semver: 6.3.0 - /@babel/helper-create-class-features-plugin@7.21.4(@babel/core@7.21.4): - resolution: {integrity: sha512-46QrX2CQlaFRF4TkwfTt6nJD7IHq8539cCL7SDpqWSDeJKY1xylKKY5F/33mJhLZ3mFvKv2gGrVS6NkyF6qs+Q==} + '@babel/helper-remap-async-to-generator@7.25.9': + resolution: {integrity: sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-annotate-as-pure': 7.18.6 - '@babel/helper-environment-visitor': 7.18.9 - '@babel/helper-function-name': 7.21.0 - '@babel/helper-member-expression-to-functions': 7.21.0 - '@babel/helper-optimise-call-expression': 7.18.6 - '@babel/helper-replace-supers': 7.20.7 - '@babel/helper-skip-transparent-expression-wrappers': 7.20.0 - '@babel/helper-split-export-declaration': 7.18.6 - transitivePeerDependencies: - - supports-color - /@babel/helper-create-regexp-features-plugin@7.21.4(@babel/core@7.21.4): - resolution: {integrity: sha512-M00OuhU+0GyZ5iBBN9czjugzWrEq2vDpf/zCYHxxf93ul/Q5rv+a5h+/+0WnI1AebHNVtl5bFV0qsJoH23DbfA==} + '@babel/helper-replace-supers@7.25.9': + resolution: {integrity: sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-annotate-as-pure': 7.18.6 - regexpu-core: 5.3.2 - - /@babel/helper-define-polyfill-provider@0.3.3(@babel/core@7.21.4): - resolution: {integrity: sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==} - peerDependencies: - '@babel/core': ^7.4.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-compilation-targets': 7.21.4(@babel/core@7.21.4) - '@babel/helper-plugin-utils': 7.20.2 - debug: 4.3.4 - lodash.debounce: 4.0.8 - resolve: 1.22.2 - semver: 6.3.0 - transitivePeerDependencies: - - supports-color - - /@babel/helper-environment-visitor@7.18.9: - resolution: {integrity: sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==} - engines: {node: '>=6.9.0'} - - /@babel/helper-explode-assignable-expression@7.18.6: - resolution: {integrity: sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.21.4 - - /@babel/helper-function-name@7.21.0: - resolution: {integrity: sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/template': 7.20.7 - '@babel/types': 7.21.4 - - /@babel/helper-hoist-variables@7.18.6: - resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.21.4 - - /@babel/helper-member-expression-to-functions@7.21.0: - resolution: {integrity: sha512-Muu8cdZwNN6mRRNG6lAYErJ5X3bRevgYR2O8wN0yn7jJSnGDu6eG59RfT29JHxGUovyfrh6Pj0XzmR7drNVL3Q==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.21.4 - - /@babel/helper-module-imports@7.21.4: - resolution: {integrity: sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.21.4 - - /@babel/helper-module-transforms@7.21.2: - resolution: {integrity: sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-environment-visitor': 7.18.9 - '@babel/helper-module-imports': 7.21.4 - '@babel/helper-simple-access': 7.20.2 - '@babel/helper-split-export-declaration': 7.18.6 - '@babel/helper-validator-identifier': 7.19.1 - '@babel/template': 7.20.7 - '@babel/traverse': 7.21.4 - '@babel/types': 7.21.4 - transitivePeerDependencies: - - supports-color - - /@babel/helper-optimise-call-expression@7.18.6: - resolution: {integrity: sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.21.4 - /@babel/helper-plugin-utils@7.20.2: - resolution: {integrity: sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==} + '@babel/helper-simple-access@7.25.9': + resolution: {integrity: sha512-c6WHXuiaRsJTyHYLJV75t9IqsmTbItYfdj99PnzYGQZkYKvan5/2jKJ7gu31J3/BJ/A18grImSPModuyG/Eo0Q==} engines: {node: '>=6.9.0'} - /@babel/helper-remap-async-to-generator@7.18.9(@babel/core@7.21.4): - resolution: {integrity: sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==} + '@babel/helper-skip-transparent-expression-wrappers@7.25.9': + resolution: {integrity: sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==} engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-annotate-as-pure': 7.18.6 - '@babel/helper-environment-visitor': 7.18.9 - '@babel/helper-wrap-function': 7.20.5 - '@babel/types': 7.21.4 - transitivePeerDependencies: - - supports-color - /@babel/helper-replace-supers@7.20.7: - resolution: {integrity: sha512-vujDMtB6LVfNW13jhlCrp48QNslK6JXi7lQG736HVbHz/mbf4Dc7tIRh1Xf5C0rF7BP8iiSxGMCmY6Ci1ven3A==} + '@babel/helper-string-parser@7.25.9': + resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==} engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-environment-visitor': 7.18.9 - '@babel/helper-member-expression-to-functions': 7.21.0 - '@babel/helper-optimise-call-expression': 7.18.6 - '@babel/template': 7.20.7 - '@babel/traverse': 7.21.4 - '@babel/types': 7.21.4 - transitivePeerDependencies: - - supports-color - /@babel/helper-simple-access@7.20.2: - resolution: {integrity: sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==} + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.21.4 - /@babel/helper-skip-transparent-expression-wrappers@7.20.0: - resolution: {integrity: sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==} + '@babel/helper-validator-identifier@7.25.9': + resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==} engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.21.4 - /@babel/helper-split-export-declaration@7.18.6: - resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==} + '@babel/helper-validator-identifier@7.27.1': + resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.21.4 - /@babel/helper-string-parser@7.19.4: - resolution: {integrity: sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==} + '@babel/helper-validator-option@7.25.9': + resolution: {integrity: sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==} engines: {node: '>=6.9.0'} - /@babel/helper-validator-identifier@7.19.1: - resolution: {integrity: sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==} + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} engines: {node: '>=6.9.0'} - /@babel/helper-validator-option@7.21.0: - resolution: {integrity: sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==} + '@babel/helper-wrap-function@7.25.9': + resolution: {integrity: sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g==} engines: {node: '>=6.9.0'} - /@babel/helper-wrap-function@7.20.5: - resolution: {integrity: sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q==} + '@babel/helpers@7.26.0': + resolution: {integrity: sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==} engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-function-name': 7.21.0 - '@babel/template': 7.20.7 - '@babel/traverse': 7.21.4 - '@babel/types': 7.21.4 - transitivePeerDependencies: - - supports-color - /@babel/helpers@7.21.0: - resolution: {integrity: sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==} + '@babel/helpers@7.27.6': + resolution: {integrity: sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==} engines: {node: '>=6.9.0'} - dependencies: - '@babel/template': 7.20.7 - '@babel/traverse': 7.21.4 - '@babel/types': 7.21.4 - transitivePeerDependencies: - - supports-color - /@babel/highlight@7.18.6: - resolution: {integrity: sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-validator-identifier': 7.19.1 - chalk: 2.4.2 - js-tokens: 4.0.0 + '@babel/parser@7.26.1': + resolution: {integrity: sha512-reoQYNiAJreZNsJzyrDNzFQ+IQ5JFiIzAHJg9bn94S3l+4++J7RsIhNMoB+lgP/9tpmiAQqspv+xfdxTSzREOw==} + engines: {node: '>=6.0.0'} + hasBin: true - /@babel/parser@7.21.4: - resolution: {integrity: sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw==} + '@babel/parser@7.28.0': + resolution: {integrity: sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==} engines: {node: '>=6.0.0'} hasBin: true - dependencies: - '@babel/types': 7.21.4 - /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.18.6(@babel/core@7.21.4): - resolution: {integrity: sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==} + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.25.9': + resolution: {integrity: sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - - /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.20.7(@babel/core@7.21.4): - resolution: {integrity: sha512-sbr9+wNE5aXMBBFBICk01tt7sBf2Oc9ikRFEcem/ZORup9IMUdNhW7/wVLEbbtlWOsEubJet46mHAL2C8+2jKQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.13.0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-skip-transparent-expression-wrappers': 7.20.0 - '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.21.4) - - /@babel/plugin-proposal-async-generator-functions@7.20.7(@babel/core@7.21.4): - resolution: {integrity: sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-environment-visitor': 7.18.9 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-remap-async-to-generator': 7.18.9(@babel/core@7.21.4) - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.21.4) - transitivePeerDependencies: - - supports-color - /@babel/plugin-proposal-class-properties@7.18.6(@babel/core@7.21.4): - resolution: {integrity: sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==} + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.25.9': + resolution: {integrity: sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw==} engines: {node: '>=6.9.0'} peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-create-class-features-plugin': 7.21.4(@babel/core@7.21.4) - '@babel/helper-plugin-utils': 7.20.2 - transitivePeerDependencies: - - supports-color - - /@babel/plugin-proposal-class-properties@7.7.4(@babel/core@7.21.4): - resolution: {integrity: sha512-EcuXeV4Hv1X3+Q1TsuOmyyxeTRiSqurGJ26+I/FW1WbymmRRapVORm6x1Zl3iDIHyRxEs+VXWp6qnlcfcJSbbw==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-create-class-features-plugin': 7.21.4(@babel/core@7.21.4) - '@babel/helper-plugin-utils': 7.20.2 - transitivePeerDependencies: - - supports-color - dev: true + '@babel/core': ^7.0.0 - /@babel/plugin-proposal-class-static-block@7.21.0(@babel/core@7.21.4): - resolution: {integrity: sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw==} + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.25.9': + resolution: {integrity: sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug==} engines: {node: '>=6.9.0'} peerDependencies: - '@babel/core': ^7.12.0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-create-class-features-plugin': 7.21.4(@babel/core@7.21.4) - '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.21.4) - transitivePeerDependencies: - - supports-color + '@babel/core': ^7.0.0 - /@babel/plugin-proposal-decorators@7.21.0(@babel/core@7.21.4): - resolution: {integrity: sha512-MfgX49uRrFUTL/HvWtmx3zmpyzMMr4MTj3d527MLlr/4RTT9G/ytFFP7qet2uM2Ve03b+BkpWUpK+lRXnQ+v9w==} + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.25.9': + resolution: {integrity: sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g==} engines: {node: '>=6.9.0'} peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-create-class-features-plugin': 7.21.4(@babel/core@7.21.4) - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-replace-supers': 7.20.7 - '@babel/helper-split-export-declaration': 7.18.6 - '@babel/plugin-syntax-decorators': 7.21.0(@babel/core@7.21.4) - transitivePeerDependencies: - - supports-color + '@babel/core': ^7.13.0 - /@babel/plugin-proposal-dynamic-import@7.18.6(@babel/core@7.21.4): - resolution: {integrity: sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==} + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.25.9': + resolution: {integrity: sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg==} engines: {node: '>=6.9.0'} peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.21.4) + '@babel/core': ^7.0.0 - /@babel/plugin-proposal-export-namespace-from@7.18.9(@babel/core@7.21.4): - resolution: {integrity: sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==} - engines: {node: '>=6.9.0'} + '@babel/plugin-proposal-class-properties@7.12.1': + resolution: {integrity: sha512-cKp3dlQsFsEs5CWKnN7BnSHOd0EOW8EKpEjkoz1pO2E5KzIDNV9Ros1b0CnmbVgAGXJubOYVBOGCT1OmJwOI7w==} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead. peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.21.4) - /@babel/plugin-proposal-json-strings@7.18.6(@babel/core@7.21.4): - resolution: {integrity: sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==} + '@babel/plugin-proposal-class-properties@7.18.6': + resolution: {integrity: sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==} engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead. peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.21.4) - /@babel/plugin-proposal-logical-assignment-operators@7.20.7(@babel/core@7.21.4): - resolution: {integrity: sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==} + '@babel/plugin-proposal-decorators@7.25.9': + resolution: {integrity: sha512-smkNLL/O1ezy9Nhy4CNosc4Va+1wo5w4gzSZeLe6y6dM4mmHfYOCPolXQPHQxonZCF+ZyebxN9vqOolkYrSn5g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.21.4) - /@babel/plugin-proposal-nullish-coalescing-operator@7.18.6(@babel/core@7.21.4): + '@babel/plugin-proposal-nullish-coalescing-operator@7.18.6': resolution: {integrity: sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==} engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-nullish-coalescing-operator instead. peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.21.4) - /@babel/plugin-proposal-numeric-separator@7.18.6(@babel/core@7.21.4): + '@babel/plugin-proposal-numeric-separator@7.18.6': resolution: {integrity: sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==} engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-numeric-separator instead. peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.21.4) - - /@babel/plugin-proposal-object-rest-spread@7.20.7(@babel/core@7.21.4): - resolution: {integrity: sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/compat-data': 7.21.4 - '@babel/core': 7.21.4 - '@babel/helper-compilation-targets': 7.21.4(@babel/core@7.21.4) - '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.21.4) - '@babel/plugin-transform-parameters': 7.21.3(@babel/core@7.21.4) - - /@babel/plugin-proposal-optional-catch-binding@7.18.6(@babel/core@7.21.4): - resolution: {integrity: sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.21.4) - /@babel/plugin-proposal-optional-chaining@7.21.0(@babel/core@7.21.4): + '@babel/plugin-proposal-optional-chaining@7.21.0': resolution: {integrity: sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==} engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-chaining instead. peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-skip-transparent-expression-wrappers': 7.20.0 - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.21.4) - /@babel/plugin-proposal-private-methods@7.18.6(@babel/core@7.21.4): + '@babel/plugin-proposal-private-methods@7.18.6': resolution: {integrity: sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==} engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-methods instead. peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-create-class-features-plugin': 7.21.4(@babel/core@7.21.4) - '@babel/helper-plugin-utils': 7.20.2 - transitivePeerDependencies: - - supports-color - /@babel/plugin-proposal-private-property-in-object@7.21.0(@babel/core@7.21.4): - resolution: {integrity: sha512-ha4zfehbJjc5MmXBlHec1igel5TJXXLDDRbuJ4+XT2TJcyD9/V1919BA8gMvsdHcNMBy4WBUBiRb3nw/EQUtBw==} + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2': + resolution: {integrity: sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-annotate-as-pure': 7.18.6 - '@babel/helper-create-class-features-plugin': 7.21.4(@babel/core@7.21.4) - '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.21.4) - transitivePeerDependencies: - - supports-color - /@babel/plugin-proposal-unicode-property-regex@7.18.6(@babel/core@7.21.4): - resolution: {integrity: sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==} - engines: {node: '>=4'} + '@babel/plugin-proposal-private-property-in-object@7.21.11': + resolution: {integrity: sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw==} + engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-property-in-object instead. peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-create-regexp-features-plugin': 7.21.4(@babel/core@7.21.4) - '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.21.4): + '@babel/plugin-syntax-async-generators@7.8.4': resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.21.4): + '@babel/plugin-syntax-bigint@7.8.3': resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.21.4): + '@babel/plugin-syntax-class-properties@7.12.13': resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.21.4): + '@babel/plugin-syntax-class-static-block@7.14.5': resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-syntax-decorators@7.21.0(@babel/core@7.21.4): - resolution: {integrity: sha512-tIoPpGBR8UuM4++ccWN3gifhVvQu7ZizuR1fklhRJrd5ewgbkUS+0KVFeWWxELtn18NTLoW32XV7zyOgIAiz+w==} + '@babel/plugin-syntax-decorators@7.25.9': + resolution: {integrity: sha512-ryzI0McXUPJnRCvMo4lumIKZUzhYUO/ScI+Mz4YVaTLt04DHNSjEUjKVvbzQjZFLuod/cYEc07mJWhzl6v4DPg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - - /@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.21.4): - resolution: {integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.21.4): - resolution: {integrity: sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==} + '@babel/plugin-syntax-flow@7.26.0': + resolution: {integrity: sha512-B+O2DnPc0iG+YXFqOxv2WNuNU97ToWjOomUQ78DouOENWUaM5sVrmet9mcomUGQFwpJd//gvUagXBSdzO1fRKg==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-syntax-flow@7.21.4(@babel/core@7.21.4): - resolution: {integrity: sha512-l9xd3N+XG4fZRxEP3vXdK6RW7vN1Uf5dxzRC/09wV86wqZ/YYQooBIGNsiRdfNR3/q2/5pPzV4B54J/9ctX5jw==} + '@babel/plugin-syntax-import-assertions@7.26.0': + resolution: {integrity: sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-syntax-import-assertions@7.20.0(@babel/core@7.21.4): - resolution: {integrity: sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ==} + '@babel/plugin-syntax-import-attributes@7.26.0': + resolution: {integrity: sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.21.4): + '@babel/plugin-syntax-import-meta@7.10.4': resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.21.4): + '@babel/plugin-syntax-json-strings@7.8.3': resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-syntax-jsx@7.21.4(@babel/core@7.21.4): - resolution: {integrity: sha512-5hewiLct5OKyh6PLKEYaFclcqtIgCb6bmELouxjF6up5q3Sov7rOayW4RwhbaBL0dit8rA80GNfY+UuDp2mBbQ==} + '@babel/plugin-syntax-jsx@7.25.9': + resolution: {integrity: sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.21.4): + '@babel/plugin-syntax-logical-assignment-operators@7.10.4': resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.21.4): + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3': resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.21.4): + '@babel/plugin-syntax-numeric-separator@7.10.4': resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.21.4): + '@babel/plugin-syntax-object-rest-spread@7.8.3': resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.21.4): + '@babel/plugin-syntax-optional-catch-binding@7.8.3': resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.21.4): + '@babel/plugin-syntax-optional-chaining@7.8.3': resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.21.4): + '@babel/plugin-syntax-private-property-in-object@7.14.5': resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.21.4): + '@babel/plugin-syntax-top-level-await@7.14.5': resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-syntax-typescript@7.21.4(@babel/core@7.21.4): - resolution: {integrity: sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA==} + '@babel/plugin-syntax-typescript@7.25.9': + resolution: {integrity: sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-transform-arrow-functions@7.20.7(@babel/core@7.21.4): - resolution: {integrity: sha512-3poA5E7dzDomxj9WXWwuD6A5F3kc7VXwIJO+E+J8qtDtS+pXPAhrgEyh+9GBwBgPq1Z+bB+/JD60lp5jsN7JPQ==} + '@babel/plugin-syntax-unicode-sets-regex@7.18.6': + resolution: {integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==} engines: {node: '>=6.9.0'} peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/core': ^7.0.0 - /@babel/plugin-transform-async-to-generator@7.20.7(@babel/core@7.21.4): - resolution: {integrity: sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q==} + '@babel/plugin-transform-arrow-functions@7.25.9': + resolution: {integrity: sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-module-imports': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-remap-async-to-generator': 7.18.9(@babel/core@7.21.4) - transitivePeerDependencies: - - supports-color - /@babel/plugin-transform-block-scoped-functions@7.18.6(@babel/core@7.21.4): - resolution: {integrity: sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==} + '@babel/plugin-transform-async-generator-functions@7.25.9': + resolution: {integrity: sha512-RXV6QAzTBbhDMO9fWwOmwwTuYaiPbggWQ9INdZqAYeSHyG7FzQ+nOZaUUjNwKv9pV3aE4WFqFm1Hnbci5tBCAw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-transform-block-scoping@7.21.0(@babel/core@7.21.4): - resolution: {integrity: sha512-Mdrbunoh9SxwFZapeHVrwFmri16+oYotcZysSzhNIVDwIAb1UV+kvnxULSYq9J3/q5MDG+4X6w8QVgD1zhBXNQ==} + '@babel/plugin-transform-async-to-generator@7.25.9': + resolution: {integrity: sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-transform-classes@7.21.0(@babel/core@7.21.4): - resolution: {integrity: sha512-RZhbYTCEUAe6ntPehC4hlslPWosNHDox+vAs4On/mCLRLfoDVHf6hVEd7kuxr1RnHwJmxFfUM3cZiZRmPxJPXQ==} + '@babel/plugin-transform-block-scoped-functions@7.25.9': + resolution: {integrity: sha512-toHc9fzab0ZfenFpsyYinOX0J/5dgJVA2fm64xPewu7CoYHWEivIWKxkK2rMi4r3yQqLnVmheMXRdG+k239CgA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-annotate-as-pure': 7.18.6 - '@babel/helper-compilation-targets': 7.21.4(@babel/core@7.21.4) - '@babel/helper-environment-visitor': 7.18.9 - '@babel/helper-function-name': 7.21.0 - '@babel/helper-optimise-call-expression': 7.18.6 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-replace-supers': 7.20.7 - '@babel/helper-split-export-declaration': 7.18.6 - globals: 11.12.0 - transitivePeerDependencies: - - supports-color - /@babel/plugin-transform-computed-properties@7.20.7(@babel/core@7.21.4): - resolution: {integrity: sha512-Lz7MvBK6DTjElHAmfu6bfANzKcxpyNPeYBGEafyA6E5HtRpjpZwU+u7Qrgz/2OR0z+5TvKYbPdphfSaAcZBrYQ==} + '@babel/plugin-transform-block-scoping@7.25.9': + resolution: {integrity: sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/template': 7.20.7 - /@babel/plugin-transform-destructuring@7.21.3(@babel/core@7.21.4): - resolution: {integrity: sha512-bp6hwMFzuiE4HqYEyoGJ/V2LeIWn+hLVKc4pnj++E5XQptwhtcGmSayM029d/j2X1bPKGTlsyPwAubuU22KhMA==} + '@babel/plugin-transform-class-properties@7.25.9': + resolution: {integrity: sha512-bbMAII8GRSkcd0h0b4X+36GksxuheLFjP65ul9w6C3KgAamI3JqErNgSrosX6ZPj+Mpim5VvEbawXxJCyEUV3Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-transform-dotall-regex@7.18.6(@babel/core@7.21.4): - resolution: {integrity: sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==} + '@babel/plugin-transform-class-static-block@7.26.0': + resolution: {integrity: sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.12.0 + + '@babel/plugin-transform-classes@7.25.9': + resolution: {integrity: sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-create-regexp-features-plugin': 7.21.4(@babel/core@7.21.4) - '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-transform-duplicate-keys@7.18.9(@babel/core@7.21.4): - resolution: {integrity: sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==} + '@babel/plugin-transform-computed-properties@7.25.9': + resolution: {integrity: sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-transform-exponentiation-operator@7.18.6(@babel/core@7.21.4): - resolution: {integrity: sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==} + '@babel/plugin-transform-destructuring@7.25.9': + resolution: {integrity: sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-builder-binary-assignment-operator-visitor': 7.18.9 - '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-transform-flow-strip-types@7.21.0(@babel/core@7.21.4): - resolution: {integrity: sha512-FlFA2Mj87a6sDkW4gfGrQQqwY/dLlBAyJa2dJEZ+FHXUVHBflO2wyKvg+OOEzXfrKYIa4HWl0mgmbCzt0cMb7w==} + '@babel/plugin-transform-dotall-regex@7.25.9': + resolution: {integrity: sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-flow': 7.21.4(@babel/core@7.21.4) - /@babel/plugin-transform-for-of@7.21.0(@babel/core@7.21.4): - resolution: {integrity: sha512-LlUYlydgDkKpIY7mcBWvyPPmMcOphEyYA27Ef4xpbh1IiDNLr0kZsos2nf92vz3IccvJI25QUwp86Eo5s6HmBQ==} + '@babel/plugin-transform-duplicate-keys@7.25.9': + resolution: {integrity: sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-transform-function-name@7.18.9(@babel/core@7.21.4): - resolution: {integrity: sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==} + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.25.9': + resolution: {integrity: sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-dynamic-import@7.25.9': + resolution: {integrity: sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-compilation-targets': 7.21.4(@babel/core@7.21.4) - '@babel/helper-function-name': 7.21.0 - '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-transform-literals@7.18.9(@babel/core@7.21.4): - resolution: {integrity: sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==} + '@babel/plugin-transform-exponentiation-operator@7.25.9': + resolution: {integrity: sha512-KRhdhlVk2nObA5AYa7QMgTMTVJdfHprfpAk4DjZVtllqRg9qarilstTKEhpVjyt+Npi8ThRyiV8176Am3CodPA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-transform-member-expression-literals@7.18.6(@babel/core@7.21.4): - resolution: {integrity: sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==} + '@babel/plugin-transform-export-namespace-from@7.25.9': + resolution: {integrity: sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-transform-modules-amd@7.20.11(@babel/core@7.21.4): - resolution: {integrity: sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g==} + '@babel/plugin-transform-flow-strip-types@7.25.9': + resolution: {integrity: sha512-/VVukELzPDdci7UUsWQaSkhgnjIWXnIyRpM02ldxaVoFK96c41So8JcKT3m0gYjyv7j5FNPGS5vfELrWalkbDA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-module-transforms': 7.21.2 - '@babel/helper-plugin-utils': 7.20.2 - transitivePeerDependencies: - - supports-color - /@babel/plugin-transform-modules-commonjs@7.21.2(@babel/core@7.21.4): - resolution: {integrity: sha512-Cln+Yy04Gxua7iPdj6nOV96smLGjpElir5YwzF0LBPKoPlLDNJePNlrGGaybAJkd0zKRnOVXOgizSqPYMNYkzA==} + '@babel/plugin-transform-for-of@7.25.9': + resolution: {integrity: sha512-LqHxduHoaGELJl2uhImHwRQudhCM50pT46rIBNvtT/Oql3nqiS3wOwP+5ten7NpYSXrrVLgtZU3DZmPtWZo16A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-module-transforms': 7.21.2 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-simple-access': 7.20.2 - transitivePeerDependencies: - - supports-color - /@babel/plugin-transform-modules-systemjs@7.20.11(@babel/core@7.21.4): - resolution: {integrity: sha512-vVu5g9BPQKSFEmvt2TA4Da5N+QVS66EX21d8uoOihC+OCpUoGvzVsXeqFdtAEfVa5BILAeFt+U7yVmLbQnAJmw==} + '@babel/plugin-transform-function-name@7.25.9': + resolution: {integrity: sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-hoist-variables': 7.18.6 - '@babel/helper-module-transforms': 7.21.2 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-validator-identifier': 7.19.1 - transitivePeerDependencies: - - supports-color - /@babel/plugin-transform-modules-umd@7.18.6(@babel/core@7.21.4): - resolution: {integrity: sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==} + '@babel/plugin-transform-json-strings@7.25.9': + resolution: {integrity: sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-literals@7.25.9': + resolution: {integrity: sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-logical-assignment-operators@7.25.9': + resolution: {integrity: sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-member-expression-literals@7.25.9': + resolution: {integrity: sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-amd@7.25.9': + resolution: {integrity: sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-commonjs@7.25.9': + resolution: {integrity: sha512-dwh2Ol1jWwL2MgkCzUSOvfmKElqQcuswAZypBSUsScMXvgdT8Ekq5YA6TtqpTVWH+4903NmboMuH1o9i8Rxlyg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-systemjs@7.25.9': + resolution: {integrity: sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-umd@7.25.9': + resolution: {integrity: sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-module-transforms': 7.21.2 - '@babel/helper-plugin-utils': 7.20.2 - transitivePeerDependencies: - - supports-color - /@babel/plugin-transform-named-capturing-groups-regex@7.20.5(@babel/core@7.21.4): - resolution: {integrity: sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA==} + '@babel/plugin-transform-named-capturing-groups-regex@7.25.9': + resolution: {integrity: sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-create-regexp-features-plugin': 7.21.4(@babel/core@7.21.4) - '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-transform-new-target@7.18.6(@babel/core@7.21.4): - resolution: {integrity: sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==} + '@babel/plugin-transform-new-target@7.25.9': + resolution: {integrity: sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-transform-object-super@7.18.6(@babel/core@7.21.4): - resolution: {integrity: sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==} + '@babel/plugin-transform-nullish-coalescing-operator@7.25.9': + resolution: {integrity: sha512-ENfftpLZw5EItALAD4WsY/KUWvhUlZndm5GC7G3evUsVeSJB6p0pBeLQUnRnBCBx7zV0RKQjR9kCuwrsIrjWog==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-replace-supers': 7.20.7 - transitivePeerDependencies: - - supports-color - /@babel/plugin-transform-parameters@7.21.3(@babel/core@7.21.4): - resolution: {integrity: sha512-Wxc+TvppQG9xWFYatvCGPvZ6+SIUxQ2ZdiBP+PHYMIjnPXD+uThCshaz4NZOnODAtBjjcVQQ/3OKs9LW28purQ==} + '@babel/plugin-transform-numeric-separator@7.25.9': + resolution: {integrity: sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-transform-property-literals@7.18.6(@babel/core@7.21.4): - resolution: {integrity: sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==} + '@babel/plugin-transform-object-rest-spread@7.25.9': + resolution: {integrity: sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-transform-react-constant-elements@7.21.3(@babel/core@7.21.4): - resolution: {integrity: sha512-4DVcFeWe/yDYBLp0kBmOGFJ6N2UYg7coGid1gdxb4co62dy/xISDMaYBXBVXEDhfgMk7qkbcYiGtwd5Q/hwDDQ==} + '@babel/plugin-transform-object-super@7.25.9': + resolution: {integrity: sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-transform-react-display-name@7.18.6(@babel/core@7.21.4): - resolution: {integrity: sha512-TV4sQ+T013n61uMoygyMRm+xf04Bd5oqFpv2jAEQwSZ8NwQA7zeRPg1LMVg2PWi3zWBz+CLKD+v5bcpZ/BS0aA==} + '@babel/plugin-transform-optional-catch-binding@7.25.9': + resolution: {integrity: sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-transform-react-jsx-development@7.18.6(@babel/core@7.21.4): - resolution: {integrity: sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA==} + '@babel/plugin-transform-optional-chaining@7.25.9': + resolution: {integrity: sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/plugin-transform-react-jsx': 7.21.0(@babel/core@7.21.4) - /@babel/plugin-transform-react-jsx@7.21.0(@babel/core@7.21.4): - resolution: {integrity: sha512-6OAWljMvQrZjR2DaNhVfRz6dkCAVV+ymcLUmaf8bccGOHn2v5rHJK3tTpij0BuhdYWP4LLaqj5lwcdlpAAPuvg==} + '@babel/plugin-transform-parameters@7.25.9': + resolution: {integrity: sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-annotate-as-pure': 7.18.6 - '@babel/helper-module-imports': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-jsx': 7.21.4(@babel/core@7.21.4) - '@babel/types': 7.21.4 - /@babel/plugin-transform-react-pure-annotations@7.18.6(@babel/core@7.21.4): - resolution: {integrity: sha512-I8VfEPg9r2TRDdvnHgPepTKvuRomzA8+u+nhY7qSI1fR2hRNebasZEETLyM5mAUr0Ku56OkXJ0I7NHJnO6cJiQ==} + '@babel/plugin-transform-private-methods@7.25.9': + resolution: {integrity: sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-annotate-as-pure': 7.18.6 - '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-transform-regenerator@7.20.5(@babel/core@7.21.4): - resolution: {integrity: sha512-kW/oO7HPBtntbsahzQ0qSE3tFvkFwnbozz3NWFhLGqH75vLEg+sCGngLlhVkePlCs3Jv0dBBHDzCHxNiFAQKCQ==} + '@babel/plugin-transform-private-property-in-object@7.25.9': + resolution: {integrity: sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - regenerator-transform: 0.15.1 - /@babel/plugin-transform-reserved-words@7.18.6(@babel/core@7.21.4): - resolution: {integrity: sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==} + '@babel/plugin-transform-property-literals@7.25.9': + resolution: {integrity: sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-transform-runtime@7.21.4(@babel/core@7.21.4): - resolution: {integrity: sha512-1J4dhrw1h1PqnNNpzwxQ2UBymJUF8KuPjAAnlLwZcGhHAIqUigFW7cdK6GHoB64ubY4qXQNYknoUeks4Wz7CUA==} + '@babel/plugin-transform-react-constant-elements@7.25.9': + resolution: {integrity: sha512-Ncw2JFsJVuvfRsa2lSHiC55kETQVLSnsYGQ1JDDwkUeWGTL/8Tom8aLTnlqgoeuopWrbbGndrc9AlLYrIosrow==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-module-imports': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - babel-plugin-polyfill-corejs2: 0.3.3(@babel/core@7.21.4) - babel-plugin-polyfill-corejs3: 0.6.0(@babel/core@7.21.4) - babel-plugin-polyfill-regenerator: 0.4.1(@babel/core@7.21.4) - semver: 6.3.0 - transitivePeerDependencies: - - supports-color - dev: false - /@babel/plugin-transform-shorthand-properties@7.18.6(@babel/core@7.21.4): - resolution: {integrity: sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==} + '@babel/plugin-transform-react-display-name@7.25.9': + resolution: {integrity: sha512-KJfMlYIUxQB1CJfO3e0+h0ZHWOTLCPP115Awhaz8U0Zpq36Gl/cXlpoyMRnUWlhNUBAzldnCiAZNvCDj7CrKxQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-transform-spread@7.20.7(@babel/core@7.21.4): - resolution: {integrity: sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw==} + '@babel/plugin-transform-react-jsx-development@7.25.9': + resolution: {integrity: sha512-9mj6rm7XVYs4mdLIpbZnHOYdpW42uoiBCTVowg7sP1thUOiANgMb4UtpRivR0pp5iL+ocvUv7X4mZgFRpJEzGw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-skip-transparent-expression-wrappers': 7.20.0 - /@babel/plugin-transform-sticky-regex@7.18.6(@babel/core@7.21.4): - resolution: {integrity: sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==} + '@babel/plugin-transform-react-jsx@7.25.9': + resolution: {integrity: sha512-s5XwpQYCqGerXl+Pu6VDL3x0j2d82eiV77UJ8a2mDHAW7j9SWRqQ2y1fNo1Z74CdcYipl5Z41zvjj4Nfzq36rw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-transform-template-literals@7.18.9(@babel/core@7.21.4): - resolution: {integrity: sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==} + '@babel/plugin-transform-react-pure-annotations@7.25.9': + resolution: {integrity: sha512-KQ/Takk3T8Qzj5TppkS1be588lkbTp5uj7w6a0LeQaTMSckU/wK0oJ/pih+T690tkgI5jfmg2TqDJvd41Sj1Cg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-transform-typeof-symbol@7.18.9(@babel/core@7.21.4): - resolution: {integrity: sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==} + '@babel/plugin-transform-regenerator@7.25.9': + resolution: {integrity: sha512-vwDcDNsgMPDGP0nMqzahDWE5/MLcX8sv96+wfX7as7LoF/kr97Bo/7fI00lXY4wUXYfVmwIIyG80fGZ1uvt2qg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-transform-typescript@7.21.3(@babel/core@7.21.4): - resolution: {integrity: sha512-RQxPz6Iqt8T0uw/WsJNReuBpWpBqs/n7mNo18sKLoTbMp+UrEekhH+pKSVC7gWz+DNjo9gryfV8YzCiT45RgMw==} + '@babel/plugin-transform-regexp-modifiers@7.26.0': + resolution: {integrity: sha512-vN6saax7lrA2yA/Pak3sCxuD6F5InBjn9IcrIKQPjpsLvuHYLVroTxjdlVRHjjBWxKOqIwpTXDkOssYT4BFdRw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-reserved-words@7.25.9': + resolution: {integrity: sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-annotate-as-pure': 7.18.6 - '@babel/helper-create-class-features-plugin': 7.21.4(@babel/core@7.21.4) - '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-typescript': 7.21.4(@babel/core@7.21.4) - transitivePeerDependencies: - - supports-color - /@babel/plugin-transform-unicode-escapes@7.18.10(@babel/core@7.21.4): - resolution: {integrity: sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ==} + '@babel/plugin-transform-runtime@7.25.9': + resolution: {integrity: sha512-nZp7GlEl+yULJrClz0SwHPqir3lc0zsPrDHQUcxGspSL7AKrexNSEfTbfqnDNJUO13bgKyfuOLMF8Xqtu8j3YQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - /@babel/plugin-transform-unicode-regex@7.18.6(@babel/core@7.21.4): - resolution: {integrity: sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==} + '@babel/plugin-transform-shorthand-properties@7.25.9': + resolution: {integrity: sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-create-regexp-features-plugin': 7.21.4(@babel/core@7.21.4) - '@babel/helper-plugin-utils': 7.20.2 - /@babel/preset-env@7.21.4(@babel/core@7.21.4): - resolution: {integrity: sha512-2W57zHs2yDLm6GD5ZpvNn71lZ0B/iypSdIeq25OurDKji6AdzV07qp4s3n1/x5BqtiGaTrPN3nerlSCaC5qNTw==} + '@babel/plugin-transform-spread@7.25.9': + resolution: {integrity: sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/compat-data': 7.21.4 - '@babel/core': 7.21.4 - '@babel/helper-compilation-targets': 7.21.4(@babel/core@7.21.4) - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-validator-option': 7.21.0 - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.20.7(@babel/core@7.21.4) - '@babel/plugin-proposal-async-generator-functions': 7.20.7(@babel/core@7.21.4) - '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-proposal-class-static-block': 7.21.0(@babel/core@7.21.4) - '@babel/plugin-proposal-dynamic-import': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-proposal-export-namespace-from': 7.18.9(@babel/core@7.21.4) - '@babel/plugin-proposal-json-strings': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-proposal-logical-assignment-operators': 7.20.7(@babel/core@7.21.4) - '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-proposal-numeric-separator': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-proposal-object-rest-spread': 7.20.7(@babel/core@7.21.4) - '@babel/plugin-proposal-optional-catch-binding': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.21.4) - '@babel/plugin-proposal-private-methods': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-proposal-private-property-in-object': 7.21.0(@babel/core@7.21.4) - '@babel/plugin-proposal-unicode-property-regex': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.21.4) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.21.4) - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.21.4) - '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.21.4) - '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.21.4) - '@babel/plugin-syntax-import-assertions': 7.20.0(@babel/core@7.21.4) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.21.4) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.21.4) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.21.4) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.21.4) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.21.4) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.21.4) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.21.4) - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.21.4) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.21.4) - '@babel/plugin-transform-arrow-functions': 7.20.7(@babel/core@7.21.4) - '@babel/plugin-transform-async-to-generator': 7.20.7(@babel/core@7.21.4) - '@babel/plugin-transform-block-scoped-functions': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-transform-block-scoping': 7.21.0(@babel/core@7.21.4) - '@babel/plugin-transform-classes': 7.21.0(@babel/core@7.21.4) - '@babel/plugin-transform-computed-properties': 7.20.7(@babel/core@7.21.4) - '@babel/plugin-transform-destructuring': 7.21.3(@babel/core@7.21.4) - '@babel/plugin-transform-dotall-regex': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-transform-duplicate-keys': 7.18.9(@babel/core@7.21.4) - '@babel/plugin-transform-exponentiation-operator': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-transform-for-of': 7.21.0(@babel/core@7.21.4) - '@babel/plugin-transform-function-name': 7.18.9(@babel/core@7.21.4) - '@babel/plugin-transform-literals': 7.18.9(@babel/core@7.21.4) - '@babel/plugin-transform-member-expression-literals': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-transform-modules-amd': 7.20.11(@babel/core@7.21.4) - '@babel/plugin-transform-modules-commonjs': 7.21.2(@babel/core@7.21.4) - '@babel/plugin-transform-modules-systemjs': 7.20.11(@babel/core@7.21.4) - '@babel/plugin-transform-modules-umd': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-transform-named-capturing-groups-regex': 7.20.5(@babel/core@7.21.4) - '@babel/plugin-transform-new-target': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-transform-object-super': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-transform-parameters': 7.21.3(@babel/core@7.21.4) - '@babel/plugin-transform-property-literals': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-transform-regenerator': 7.20.5(@babel/core@7.21.4) - '@babel/plugin-transform-reserved-words': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-transform-shorthand-properties': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-transform-spread': 7.20.7(@babel/core@7.21.4) - '@babel/plugin-transform-sticky-regex': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-transform-template-literals': 7.18.9(@babel/core@7.21.4) - '@babel/plugin-transform-typeof-symbol': 7.18.9(@babel/core@7.21.4) - '@babel/plugin-transform-unicode-escapes': 7.18.10(@babel/core@7.21.4) - '@babel/plugin-transform-unicode-regex': 7.18.6(@babel/core@7.21.4) - '@babel/preset-modules': 0.1.5(@babel/core@7.21.4) - '@babel/types': 7.21.4 - babel-plugin-polyfill-corejs2: 0.3.3(@babel/core@7.21.4) - babel-plugin-polyfill-corejs3: 0.6.0(@babel/core@7.21.4) - babel-plugin-polyfill-regenerator: 0.4.1(@babel/core@7.21.4) - core-js-compat: 3.30.1 - semver: 6.3.0 - transitivePeerDependencies: - - supports-color - - /@babel/preset-flow@7.21.4(@babel/core@7.21.4): - resolution: {integrity: sha512-F24cSq4DIBmhq4OzK3dE63NHagb27OPE3eWR+HLekt4Z3Y5MzIIUGF3LlLgV0gN8vzbDViSY7HnrReNVCJXTeA==} + + '@babel/plugin-transform-sticky-regex@7.25.9': + resolution: {integrity: sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-validator-option': 7.21.0 - '@babel/plugin-transform-flow-strip-types': 7.21.0(@babel/core@7.21.4) - dev: true - /@babel/preset-modules@0.1.5(@babel/core@7.21.4): - resolution: {integrity: sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==} + '@babel/plugin-transform-template-literals@7.25.9': + resolution: {integrity: sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-proposal-unicode-property-regex': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-transform-dotall-regex': 7.18.6(@babel/core@7.21.4) - '@babel/types': 7.21.4 - esutils: 2.0.3 - /@babel/preset-react@7.18.6(@babel/core@7.21.4): - resolution: {integrity: sha512-zXr6atUmyYdiWRVLOZahakYmOBHtWc2WGCkP8PYTgZi0iJXDY2CN180TdrIW4OGOAdLc7TifzDIvtx6izaRIzg==} + '@babel/plugin-transform-typeof-symbol@7.25.9': + resolution: {integrity: sha512-v61XqUMiueJROUv66BVIOi0Fv/CUuZuZMl5NkRoCVxLAnMexZ0A3kMe7vvZ0nulxMuMp0Mk6S5hNh48yki08ZA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-validator-option': 7.21.0 - '@babel/plugin-transform-react-display-name': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-transform-react-jsx': 7.21.0(@babel/core@7.21.4) - '@babel/plugin-transform-react-jsx-development': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-transform-react-pure-annotations': 7.18.6(@babel/core@7.21.4) - /@babel/preset-typescript@7.21.4(@babel/core@7.21.4): - resolution: {integrity: sha512-sMLNWY37TCdRH/bJ6ZeeOH1nPuanED7Ai9Y/vH31IPqalioJ6ZNFUWONsakhv4r4n+I6gm5lmoE0olkgib/j/A==} + '@babel/plugin-transform-typescript@7.25.9': + resolution: {integrity: sha512-7PbZQZP50tzv2KGGnhh82GSyMB01yKY9scIjf1a+GfZCtInOWqUH5+1EBU4t9fyR5Oykkkc9vFTs4OHrhHXljQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-validator-option': 7.21.0 - '@babel/plugin-syntax-jsx': 7.21.4(@babel/core@7.21.4) - '@babel/plugin-transform-modules-commonjs': 7.21.2(@babel/core@7.21.4) - '@babel/plugin-transform-typescript': 7.21.3(@babel/core@7.21.4) - transitivePeerDependencies: - - supports-color - /@babel/regjsgen@0.8.0: - resolution: {integrity: sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==} + '@babel/plugin-transform-unicode-escapes@7.25.9': + resolution: {integrity: sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - /@babel/runtime@7.12.1: - resolution: {integrity: sha512-J5AIf3vPj3UwXaAzb5j1xM4WAQDX3EMgemF8rjCP3SoW09LfRKAXQKt6CoVYl230P6iWdRcBbnLDDdnqWxZSCA==} - dependencies: - regenerator-runtime: 0.13.11 - dev: true + '@babel/plugin-transform-unicode-property-regex@7.25.9': + resolution: {integrity: sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 - /@babel/runtime@7.21.0: - resolution: {integrity: sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==} + '@babel/plugin-transform-unicode-regex@7.25.9': + resolution: {integrity: sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA==} engines: {node: '>=6.9.0'} - dependencies: - regenerator-runtime: 0.13.11 + peerDependencies: + '@babel/core': ^7.0.0-0 - /@babel/template@7.20.7: - resolution: {integrity: sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==} + '@babel/plugin-transform-unicode-sets-regex@7.25.9': + resolution: {integrity: sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ==} engines: {node: '>=6.9.0'} - dependencies: - '@babel/code-frame': 7.21.4 - '@babel/parser': 7.21.4 - '@babel/types': 7.21.4 + peerDependencies: + '@babel/core': ^7.0.0 - /@babel/traverse@7.21.4: - resolution: {integrity: sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q==} + '@babel/preset-env@7.26.0': + resolution: {integrity: sha512-H84Fxq0CQJNdPFT2DrfnylZ3cf5K43rGfWK4LJGPpjKHiZlk0/RzwEus3PDDZZg+/Er7lCA03MVacueUuXdzfw==} engines: {node: '>=6.9.0'} - dependencies: - '@babel/code-frame': 7.21.4 - '@babel/generator': 7.21.4 - '@babel/helper-environment-visitor': 7.18.9 - '@babel/helper-function-name': 7.21.0 - '@babel/helper-hoist-variables': 7.18.6 - '@babel/helper-split-export-declaration': 7.18.6 - '@babel/parser': 7.21.4 - '@babel/types': 7.21.4 - debug: 4.3.4 - globals: 11.12.0 - transitivePeerDependencies: - - supports-color + peerDependencies: + '@babel/core': ^7.0.0-0 - /@babel/types@7.21.4: - resolution: {integrity: sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA==} + '@babel/preset-flow@7.25.9': + resolution: {integrity: sha512-EASHsAhE+SSlEzJ4bzfusnXSHiU+JfAYzj+jbw2vgQKgq5HrUr8qs+vgtiEL5dOH6sEweI+PNt2D7AqrDSHyqQ==} engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-string-parser': 7.19.4 - '@babel/helper-validator-identifier': 7.19.1 - to-fast-properties: 2.0.0 + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/preset-modules@0.1.6-no-external-plugins': + resolution: {integrity: sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==} + peerDependencies: + '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0 - /@balena/dockerignore@1.0.2: + '@babel/preset-react@7.25.9': + resolution: {integrity: sha512-D3to0uSPiWE7rBrdIICCd0tJSIGpLaaGptna2+w7Pft5xMqLpA1sz99DK5TZ1TjGbdQ/VI1eCSZ06dv3lT4JOw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/preset-typescript@7.26.0': + resolution: {integrity: sha512-NMk1IGZ5I/oHhoXEElcm+xUnL/szL6xflkFZmoEU9xj1qSJXpiS7rsspYo92B4DRCDvZn2erT5LdsCeXAKNCkg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/runtime@7.26.0': + resolution: {integrity: sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.25.9': + resolution: {integrity: sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.27.2': + resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.25.9': + resolution: {integrity: sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.28.0': + resolution: {integrity: sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.26.0': + resolution: {integrity: sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.28.1': + resolution: {integrity: sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ==} + engines: {node: '>=6.9.0'} + + '@balena/dockerignore@1.0.2': resolution: {integrity: sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q==} - dev: true - /@bcoe/v8-coverage@0.2.3: + '@bcoe/v8-coverage@0.2.3': resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} - /@clickhouse/client@0.0.11: - resolution: {integrity: sha512-ZxabGakXyRmtKqjyFKHtKrWcNsTdVmeHCZF4JuXHS72m+YmAjTUdAasaHN6xkqRzhkw/Dj94SBxWWTi5gg+ClA==} - engines: {node: '>=14'} - dependencies: - node-abort-controller: 3.1.1 - dev: false + '@clickhouse/client-common@1.10.1': + resolution: {integrity: sha512-Duh3cign2ChvXABpjVj9Hkz5y20Zf48OE0Y50S4qBVPdhI81S4Rh4MI/bEwvwMnzHubSkiEQ+VhC5HzV8ybnpg==} - /@cspotcode/source-map-support@0.8.1: + '@clickhouse/client@1.10.1': + resolution: {integrity: sha512-Ot/6l4hFALK6NtZDS2UegukfRXWkkftWHCnzKUwanpOQ3Jd+RVKx5dxQreeBG5XcRjt1xyf5904PFjbCnaulXg==} + engines: {node: '>=16'} + + '@confluentinc/kafka-javascript@1.4.1': + resolution: {integrity: sha512-oB/6ZeBHqqAwgBSGhf6uO/BeOdMN1psOs1yENsjtMC6IQv9VOX/OM6WpyikZqFwuuZTE9mLM0/9UESgBphCYbA==} + engines: {node: '>=18.0.0'} + + '@cspotcode/source-map-support@0.8.1': resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} - dependencies: - '@jridgewell/trace-mapping': 0.3.9 - /@csstools/normalize.css@12.0.0: - resolution: {integrity: sha512-M0qqxAcwCsIVfpFQSlGN5XjXWu8l5JDZN+fPt1LeW5SZexQTgnaEvgXAY+CeygRw0EeppWHi12JxESWiWrB0Sg==} - dev: false + '@csstools/normalize.css@12.1.1': + resolution: {integrity: sha512-YAYeJ+Xqh7fUou1d1j9XHl44BmsuThiTr4iNrgCQ3J27IbhXsxXDGZ1cXv8Qvs99d4rBbLiSKy3+WZiet32PcQ==} - /@csstools/postcss-cascade-layers@1.1.1(postcss@8.4.21): + '@csstools/postcss-cascade-layers@1.1.1': resolution: {integrity: sha512-+KdYrpKC5TgomQr2DlZF4lDEpHcoxnj5IGddYYfBWJAKfj1JtuHUIqMa+E1pJJ+z3kvDViWMqyqPlG4Ja7amQA==} engines: {node: ^12 || ^14 || >=16} peerDependencies: - postcss: ^8.2 - dependencies: - '@csstools/selector-specificity': 2.2.0(postcss-selector-parser@6.0.11) - postcss: 8.4.21 - postcss-selector-parser: 6.0.11 - dev: false + postcss: ^8.4.47 - /@csstools/postcss-color-function@1.1.1(postcss@8.4.21): + '@csstools/postcss-color-function@1.1.1': resolution: {integrity: sha512-Bc0f62WmHdtRDjf5f3e2STwRAl89N2CLb+9iAwzrv4L2hncrbDwnQD9PCq0gtAt7pOI2leIV08HIBUd4jxD8cw==} engines: {node: ^12 || ^14 || >=16} peerDependencies: - postcss: ^8.2 - dependencies: - '@csstools/postcss-progressive-custom-properties': 1.3.0(postcss@8.4.21) - postcss: 8.4.21 - postcss-value-parser: 4.2.0 - dev: false + postcss: ^8.4.47 - /@csstools/postcss-font-format-keywords@1.0.1(postcss@8.4.21): + '@csstools/postcss-font-format-keywords@1.0.1': resolution: {integrity: sha512-ZgrlzuUAjXIOc2JueK0X5sZDjCtgimVp/O5CEqTcs5ShWBa6smhWYbS0x5cVc/+rycTDbjjzoP0KTDnUneZGOg==} engines: {node: ^12 || ^14 || >=16} peerDependencies: - postcss: ^8.2 - dependencies: - postcss: 8.4.21 - postcss-value-parser: 4.2.0 - dev: false + postcss: ^8.4.47 - /@csstools/postcss-hwb-function@1.0.2(postcss@8.4.21): + '@csstools/postcss-hwb-function@1.0.2': resolution: {integrity: sha512-YHdEru4o3Rsbjmu6vHy4UKOXZD+Rn2zmkAmLRfPet6+Jz4Ojw8cbWxe1n42VaXQhD3CQUXXTooIy8OkVbUcL+w==} engines: {node: ^12 || ^14 || >=16} peerDependencies: - postcss: ^8.2 - dependencies: - postcss: 8.4.21 - postcss-value-parser: 4.2.0 - dev: false + postcss: ^8.4.47 - /@csstools/postcss-ic-unit@1.0.1(postcss@8.4.21): + '@csstools/postcss-ic-unit@1.0.1': resolution: {integrity: sha512-Ot1rcwRAaRHNKC9tAqoqNZhjdYBzKk1POgWfhN4uCOE47ebGcLRqXjKkApVDpjifL6u2/55ekkpnFcp+s/OZUw==} engines: {node: ^12 || ^14 || >=16} peerDependencies: - postcss: ^8.2 - dependencies: - '@csstools/postcss-progressive-custom-properties': 1.3.0(postcss@8.4.21) - postcss: 8.4.21 - postcss-value-parser: 4.2.0 - dev: false + postcss: ^8.4.47 - /@csstools/postcss-is-pseudo-class@2.0.7(postcss@8.4.21): + '@csstools/postcss-is-pseudo-class@2.0.7': resolution: {integrity: sha512-7JPeVVZHd+jxYdULl87lvjgvWldYu+Bc62s9vD/ED6/QTGjy0jy0US/f6BG53sVMTBJ1lzKZFpYmofBN9eaRiA==} engines: {node: ^12 || ^14 || >=16} peerDependencies: - postcss: ^8.2 - dependencies: - '@csstools/selector-specificity': 2.2.0(postcss-selector-parser@6.0.11) - postcss: 8.4.21 - postcss-selector-parser: 6.0.11 - dev: false + postcss: ^8.4.47 - /@csstools/postcss-nested-calc@1.0.0(postcss@8.4.21): + '@csstools/postcss-nested-calc@1.0.0': resolution: {integrity: sha512-JCsQsw1wjYwv1bJmgjKSoZNvf7R6+wuHDAbi5f/7MbFhl2d/+v+TvBTU4BJH3G1X1H87dHl0mh6TfYogbT/dJQ==} engines: {node: ^12 || ^14 || >=16} peerDependencies: - postcss: ^8.2 - dependencies: - postcss: 8.4.21 - postcss-value-parser: 4.2.0 - dev: false + postcss: ^8.4.47 - /@csstools/postcss-normalize-display-values@1.0.1(postcss@8.4.21): + '@csstools/postcss-normalize-display-values@1.0.1': resolution: {integrity: sha512-jcOanIbv55OFKQ3sYeFD/T0Ti7AMXc9nM1hZWu8m/2722gOTxFg7xYu4RDLJLeZmPUVQlGzo4jhzvTUq3x4ZUw==} engines: {node: ^12 || ^14 || >=16} peerDependencies: - postcss: ^8.2 - dependencies: - postcss: 8.4.21 - postcss-value-parser: 4.2.0 - dev: false + postcss: ^8.4.47 - /@csstools/postcss-oklab-function@1.1.1(postcss@8.4.21): + '@csstools/postcss-oklab-function@1.1.1': resolution: {integrity: sha512-nJpJgsdA3dA9y5pgyb/UfEzE7W5Ka7u0CX0/HIMVBNWzWemdcTH3XwANECU6anWv/ao4vVNLTMxhiPNZsTK6iA==} engines: {node: ^12 || ^14 || >=16} peerDependencies: - postcss: ^8.2 - dependencies: - '@csstools/postcss-progressive-custom-properties': 1.3.0(postcss@8.4.21) - postcss: 8.4.21 - postcss-value-parser: 4.2.0 - dev: false + postcss: ^8.4.47 - /@csstools/postcss-progressive-custom-properties@1.3.0(postcss@8.4.21): + '@csstools/postcss-progressive-custom-properties@1.3.0': resolution: {integrity: sha512-ASA9W1aIy5ygskZYuWams4BzafD12ULvSypmaLJT2jvQ8G0M3I8PRQhC0h7mG0Z3LI05+agZjqSR9+K9yaQQjA==} engines: {node: ^12 || ^14 || >=16} peerDependencies: - postcss: ^8.3 - dependencies: - postcss: 8.4.21 - postcss-value-parser: 4.2.0 - dev: false + postcss: ^8.4.47 - /@csstools/postcss-stepped-value-functions@1.0.1(postcss@8.4.21): + '@csstools/postcss-stepped-value-functions@1.0.1': resolution: {integrity: sha512-dz0LNoo3ijpTOQqEJLY8nyaapl6umbmDcgj4AD0lgVQ572b2eqA1iGZYTTWhrcrHztWDDRAX2DGYyw2VBjvCvQ==} engines: {node: ^12 || ^14 || >=16} peerDependencies: - postcss: ^8.2 - dependencies: - postcss: 8.4.21 - postcss-value-parser: 4.2.0 - dev: false + postcss: ^8.4.47 - /@csstools/postcss-text-decoration-shorthand@1.0.0(postcss@8.4.21): + '@csstools/postcss-text-decoration-shorthand@1.0.0': resolution: {integrity: sha512-c1XwKJ2eMIWrzQenN0XbcfzckOLLJiczqy+YvfGmzoVXd7pT9FfObiSEfzs84bpE/VqfpEuAZ9tCRbZkZxxbdw==} engines: {node: ^12 || ^14 || >=16} peerDependencies: - postcss: ^8.2 - dependencies: - postcss: 8.4.21 - postcss-value-parser: 4.2.0 - dev: false + postcss: ^8.4.47 - /@csstools/postcss-trigonometric-functions@1.0.2(postcss@8.4.21): + '@csstools/postcss-trigonometric-functions@1.0.2': resolution: {integrity: sha512-woKaLO///4bb+zZC2s80l+7cm07M7268MsyG3M0ActXXEFi6SuhvriQYcb58iiKGbjwwIU7n45iRLEHypB47Og==} engines: {node: ^14 || >=16} peerDependencies: - postcss: ^8.2 - dependencies: - postcss: 8.4.21 - postcss-value-parser: 4.2.0 - dev: false + postcss: ^8.4.47 - /@csstools/postcss-unset-value@1.0.2(postcss@8.4.21): + '@csstools/postcss-unset-value@1.0.2': resolution: {integrity: sha512-c8J4roPBILnelAsdLr4XOAR/GsTm0GJi4XpcfvoWk3U6KiTCqiFYc63KhRMQQX35jYMp4Ao8Ij9+IZRgMfJp1g==} engines: {node: ^12 || ^14 || >=16} peerDependencies: - postcss: ^8.2 - dependencies: - postcss: 8.4.21 - dev: false + postcss: ^8.4.47 - /@csstools/selector-specificity@2.2.0(postcss-selector-parser@6.0.11): + '@csstools/selector-specificity@2.2.0': resolution: {integrity: sha512-+OJ9konv95ClSTOJCmMZqpd5+YGsB2S+x6w3E1oaM8UuR5j8nTNHYSz8c9BEPGDOCMQYIEEGlVPj/VY64iTbGw==} engines: {node: ^14 || ^16 || >=18} peerDependencies: postcss-selector-parser: ^6.0.10 - dependencies: - postcss-selector-parser: 6.0.11 - dev: false - /@ctrl/tinycolor@3.6.0: - resolution: {integrity: sha512-/Z3l6pXthq0JvMYdUFyX9j0MaCltlIn6mfh9jLyQwg5aPKxkyNa0PTHtU1AlFXLNk55ZuAeJRcpvq+tmLfKmaQ==} + '@ctrl/tinycolor@3.6.1': + resolution: {integrity: sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==} engines: {node: '>=10'} - /@discoveryjs/json-ext@0.5.7: + '@discoveryjs/json-ext@0.5.7': resolution: {integrity: sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==} engines: {node: '>=10.0.0'} - /@edge-runtime/format@1.1.0: - resolution: {integrity: sha512-MkLDDtPhXZIMx83NykdFmOpF7gVWIdd6GBHYb8V/E+PKWvD2pK/qWx9B30oN1iDJ2XBm0SGDjz02S8nDHI9lMQ==} - dev: true + '@discoveryjs/json-ext@0.6.3': + resolution: {integrity: sha512-4B4OijXeVNOPZlYA2oEwWOTkzyltLao+xbotHQeqN++Rv27Y6s818+n2Qkp8q+Fxhn0t/5lA5X1Mxktud8eayQ==} + engines: {node: '>=14.17.0'} - /@edge-runtime/primitives@2.0.0: - resolution: {integrity: sha512-AXqUq1zruTJAICrllUvZcgciIcEGHdF6KJ3r6FM0n4k8LpFxZ62tPWVIJ9HKm+xt+ncTBUZxwgUaQ73QMUQEKw==} - dev: true + '@dnd-kit/accessibility@3.1.0': + resolution: {integrity: sha512-ea7IkhKvlJUv9iSHJOnxinBcoOI3ppGnnL+VDJ75O45Nss6HtZd8IdN8touXPDtASfeI2T2LImb8VOZcL47wjQ==} + peerDependencies: + react: '>=16.8.0' - /@edge-runtime/vm@2.0.0: - resolution: {integrity: sha512-BOLrAX8IWHRXu1siZocwLguKJPEUv7cr+rG8tI4hvHgMdIsBWHJlLeB8EjuUVnIURFrUiM49lVKn8DRrECmngw==} - dependencies: - '@edge-runtime/primitives': 2.0.0 - dev: true + '@dnd-kit/core@6.1.0': + resolution: {integrity: sha512-J3cQBClB4TVxwGo3KEjssGEXNJqGVWx17aRTZ1ob0FliR5IjYgTxl5YJbKTzA6IzrtelotH19v6y7uoIRUZPSg==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' - /@emotion/hash@0.8.0: - resolution: {integrity: sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==} + '@dnd-kit/modifiers@6.0.1': + resolution: {integrity: sha512-rbxcsg3HhzlcMHVHWDuh9LCjpOVAgqbV78wLGI8tziXY3+qcMQ61qVXIvNKQFuhj75dSfD+o+PYZQ/NUk2A23A==} + peerDependencies: + '@dnd-kit/core': ^6.0.6 + react: '>=16.8.0' + + '@dnd-kit/sortable@7.0.2': + resolution: {integrity: sha512-wDkBHHf9iCi1veM834Gbk1429bd4lHX4RpAwT0y2cHLf246GAvU2sVw/oxWNpPKQNQRQaeGXhAVgrOl1IT+iyA==} + peerDependencies: + '@dnd-kit/core': ^6.0.7 + react: '>=16.8.0' + + '@dnd-kit/utilities@3.2.2': + resolution: {integrity: sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==} + peerDependencies: + react: '>=16.8.0' + + '@emnapi/runtime@1.4.4': + resolution: {integrity: sha512-hHyapA4A3gPaDCNfiqyZUStTMqIkKRshqPIuDOXv1hcBnD4U3l8cP0T1HMCfGRxQ6V64TGCcoswChANyOAwbQg==} - /@emotion/hash@0.9.0: - resolution: {integrity: sha512-14FtKiHhy2QoPIzdTcvh//8OyBlknNs2nXRwIhG904opCby3l+9Xaf/wuPvICBF0rc1ZCNBd3nKe9cd2mecVkQ==} - dev: true + '@emotion/hash@0.8.0': + resolution: {integrity: sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==} - /@emotion/unitless@0.7.5: + '@emotion/unitless@0.7.5': resolution: {integrity: sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==} - /@esbuild-plugins/node-modules-polyfill@0.1.4(esbuild@0.16.3): - resolution: {integrity: sha512-uZbcXi0zbmKC/050p3gJnne5Qdzw8vkXIv+c2BW0Lsc1ji1SkrxbKPUy5Efr0blbTu1SL8w4eyfpnSdPg3G0Qg==} - peerDependencies: - esbuild: '*' - dependencies: - esbuild: 0.16.3 - escape-string-regexp: 4.0.0 - rollup-plugin-node-polyfills: 0.2.1 - dev: true + '@esbuild/aix-ppc64@0.23.1': + resolution: {integrity: sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] - /@esbuild/android-arm64@0.16.3: - resolution: {integrity: sha512-RolFVeinkeraDvN/OoRf1F/lP0KUfGNb5jxy/vkIMeRRChkrX/HTYN6TYZosRJs3a1+8wqpxAo5PI5hFmxyPRg==} - engines: {node: '>=12'} + '@esbuild/aix-ppc64@0.25.6': + resolution: {integrity: sha512-ShbM/3XxwuxjFiuVBHA+d3j5dyac0aEVVq1oluIDf71hUw0aRF59dV/efUsIwFnR6m8JNM2FjZOzmaZ8yG61kw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.23.1': + resolution: {integrity: sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==} + engines: {node: '>=18'} cpu: [arm64] os: [android] - requiresBuild: true - optional: true - /@esbuild/android-arm64@0.17.6: - resolution: {integrity: sha512-YnYSCceN/dUzUr5kdtUzB+wZprCafuD89Hs0Aqv9QSdwhYQybhXTaSTcrl6X/aWThn1a/j0eEpUBGOE7269REg==} - engines: {node: '>=12'} + '@esbuild/android-arm64@0.25.6': + resolution: {integrity: sha512-hd5zdUarsK6strW+3Wxi5qWws+rJhCCbMiC9QZyzoxfk5uHRIE8T287giQxzVpEvCwuJ9Qjg6bEjcRJcgfLqoA==} + engines: {node: '>=18'} cpu: [arm64] os: [android] - requiresBuild: true - dev: true - optional: true - /@esbuild/android-arm@0.16.3: - resolution: {integrity: sha512-mueuEoh+s1eRbSJqq9KNBQwI4QhQV6sRXIfTyLXSHGMpyew61rOK4qY21uKbXl1iBoMb0AdL1deWFCQVlN2qHA==} - engines: {node: '>=12'} + '@esbuild/android-arm@0.23.1': + resolution: {integrity: sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==} + engines: {node: '>=18'} cpu: [arm] os: [android] - requiresBuild: true - optional: true - /@esbuild/android-arm@0.17.6: - resolution: {integrity: sha512-bSC9YVUjADDy1gae8RrioINU6e1lCkg3VGVwm0QQ2E1CWcC4gnMce9+B6RpxuSsrsXsk1yojn7sp1fnG8erE2g==} - engines: {node: '>=12'} + '@esbuild/android-arm@0.25.6': + resolution: {integrity: sha512-S8ToEOVfg++AU/bHwdksHNnyLyVM+eMVAOf6yRKFitnwnbwwPNqKr3srzFRe7nzV69RQKb5DgchIX5pt3L53xg==} + engines: {node: '>=18'} cpu: [arm] os: [android] - requiresBuild: true - dev: true - optional: true - /@esbuild/android-x64@0.16.3: - resolution: {integrity: sha512-SFpTUcIT1bIJuCCBMCQWq1bL2gPTjWoLZdjmIhjdcQHaUfV41OQfho6Ici5uvvkMmZRXIUGpM3GxysP/EU7ifQ==} - engines: {node: '>=12'} + '@esbuild/android-x64@0.23.1': + resolution: {integrity: sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==} + engines: {node: '>=18'} cpu: [x64] os: [android] - requiresBuild: true - optional: true - /@esbuild/android-x64@0.17.6: - resolution: {integrity: sha512-MVcYcgSO7pfu/x34uX9u2QIZHmXAB7dEiLQC5bBl5Ryqtpj9lT2sg3gNDEsrPEmimSJW2FXIaxqSQ501YLDsZQ==} - engines: {node: '>=12'} + '@esbuild/android-x64@0.25.6': + resolution: {integrity: sha512-0Z7KpHSr3VBIO9A/1wcT3NTy7EB4oNC4upJ5ye3R7taCc2GUdeynSLArnon5G8scPwaU866d3H4BCrE5xLW25A==} + engines: {node: '>=18'} cpu: [x64] os: [android] - requiresBuild: true - dev: true - optional: true - /@esbuild/darwin-arm64@0.16.3: - resolution: {integrity: sha512-DO8WykMyB+N9mIDfI/Hug70Dk1KipavlGAecxS3jDUwAbTpDXj0Lcwzw9svkhxfpCagDmpaTMgxWK8/C/XcXvw==} - engines: {node: '>=12'} + '@esbuild/darwin-arm64@0.23.1': + resolution: {integrity: sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==} + engines: {node: '>=18'} cpu: [arm64] os: [darwin] - requiresBuild: true - optional: true - /@esbuild/darwin-arm64@0.17.6: - resolution: {integrity: sha512-bsDRvlbKMQMt6Wl08nHtFz++yoZHsyTOxnjfB2Q95gato+Yi4WnRl13oC2/PJJA9yLCoRv9gqT/EYX0/zDsyMA==} - engines: {node: '>=12'} + '@esbuild/darwin-arm64@0.25.6': + resolution: {integrity: sha512-FFCssz3XBavjxcFxKsGy2DYK5VSvJqa6y5HXljKzhRZ87LvEi13brPrf/wdyl/BbpbMKJNOr1Sd0jtW4Ge1pAA==} + engines: {node: '>=18'} cpu: [arm64] os: [darwin] - requiresBuild: true - dev: true - optional: true - /@esbuild/darwin-x64@0.16.3: - resolution: {integrity: sha512-uEqZQ2omc6BvWqdCiyZ5+XmxuHEi1SPzpVxXCSSV2+Sh7sbXbpeNhHIeFrIpRjAs0lI1FmA1iIOxFozKBhKgRQ==} - engines: {node: '>=12'} + '@esbuild/darwin-x64@0.23.1': + resolution: {integrity: sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==} + engines: {node: '>=18'} cpu: [x64] os: [darwin] - requiresBuild: true - optional: true - /@esbuild/darwin-x64@0.17.6: - resolution: {integrity: sha512-xh2A5oPrYRfMFz74QXIQTQo8uA+hYzGWJFoeTE8EvoZGHb+idyV4ATaukaUvnnxJiauhs/fPx3vYhU4wiGfosg==} - engines: {node: '>=12'} + '@esbuild/darwin-x64@0.25.6': + resolution: {integrity: sha512-GfXs5kry/TkGM2vKqK2oyiLFygJRqKVhawu3+DOCk7OxLy/6jYkWXhlHwOoTb0WqGnWGAS7sooxbZowy+pK9Yg==} + engines: {node: '>=18'} cpu: [x64] os: [darwin] - requiresBuild: true - dev: true - optional: true - /@esbuild/freebsd-arm64@0.16.3: - resolution: {integrity: sha512-nJansp3sSXakNkOD5i5mIz2Is/HjzIhFs49b1tjrPrpCmwgBmH9SSzhC/Z1UqlkivqMYkhfPwMw1dGFUuwmXhw==} - engines: {node: '>=12'} + '@esbuild/freebsd-arm64@0.23.1': + resolution: {integrity: sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==} + engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - requiresBuild: true - optional: true - /@esbuild/freebsd-arm64@0.17.6: - resolution: {integrity: sha512-EnUwjRc1inT4ccZh4pB3v1cIhohE2S4YXlt1OvI7sw/+pD+dIE4smwekZlEPIwY6PhU6oDWwITrQQm5S2/iZgg==} - engines: {node: '>=12'} + '@esbuild/freebsd-arm64@0.25.6': + resolution: {integrity: sha512-aoLF2c3OvDn2XDTRvn8hN6DRzVVpDlj2B/F66clWd/FHLiHaG3aVZjxQX2DYphA5y/evbdGvC6Us13tvyt4pWg==} + engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - requiresBuild: true - dev: true - optional: true - /@esbuild/freebsd-x64@0.16.3: - resolution: {integrity: sha512-TfoDzLw+QHfc4a8aKtGSQ96Wa+6eimljjkq9HKR0rHlU83vw8aldMOUSJTUDxbcUdcgnJzPaX8/vGWm7vyV7ug==} - engines: {node: '>=12'} + '@esbuild/freebsd-x64@0.23.1': + resolution: {integrity: sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==} + engines: {node: '>=18'} cpu: [x64] os: [freebsd] - requiresBuild: true - optional: true - /@esbuild/freebsd-x64@0.17.6: - resolution: {integrity: sha512-Uh3HLWGzH6FwpviUcLMKPCbZUAFzv67Wj5MTwK6jn89b576SR2IbEp+tqUHTr8DIl0iDmBAf51MVaP7pw6PY5Q==} - engines: {node: '>=12'} + '@esbuild/freebsd-x64@0.25.6': + resolution: {integrity: sha512-2SkqTjTSo2dYi/jzFbU9Plt1vk0+nNg8YC8rOXXea+iA3hfNJWebKYPs3xnOUf9+ZWhKAaxnQNUf2X9LOpeiMQ==} + engines: {node: '>=18'} cpu: [x64] os: [freebsd] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-arm64@0.16.3: - resolution: {integrity: sha512-7I3RlsnxEFCHVZNBLb2w7unamgZ5sVwO0/ikE2GaYvYuUQs9Qte/w7TqWcXHtCwxvZx/2+F97ndiUQAWs47ZfQ==} - engines: {node: '>=12'} + '@esbuild/linux-arm64@0.23.1': + resolution: {integrity: sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==} + engines: {node: '>=18'} cpu: [arm64] os: [linux] - requiresBuild: true - optional: true - /@esbuild/linux-arm64@0.17.6: - resolution: {integrity: sha512-bUR58IFOMJX523aDVozswnlp5yry7+0cRLCXDsxnUeQYJik1DukMY+apBsLOZJblpH+K7ox7YrKrHmJoWqVR9w==} - engines: {node: '>=12'} + '@esbuild/linux-arm64@0.25.6': + resolution: {integrity: sha512-b967hU0gqKd9Drsh/UuAm21Khpoh6mPBSgz8mKRq4P5mVK8bpA+hQzmm/ZwGVULSNBzKdZPQBRT3+WuVavcWsQ==} + engines: {node: '>=18'} cpu: [arm64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-arm@0.16.3: - resolution: {integrity: sha512-VwswmSYwVAAq6LysV59Fyqk3UIjbhuc6wb3vEcJ7HEJUtFuLK9uXWuFoH1lulEbE4+5GjtHi3MHX+w1gNHdOWQ==} - engines: {node: '>=12'} + '@esbuild/linux-arm@0.23.1': + resolution: {integrity: sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==} + engines: {node: '>=18'} cpu: [arm] os: [linux] - requiresBuild: true - optional: true - /@esbuild/linux-arm@0.17.6: - resolution: {integrity: sha512-7YdGiurNt7lqO0Bf/U9/arrPWPqdPqcV6JCZda4LZgEn+PTQ5SMEI4MGR52Bfn3+d6bNEGcWFzlIxiQdS48YUw==} - engines: {node: '>=12'} + '@esbuild/linux-arm@0.25.6': + resolution: {integrity: sha512-SZHQlzvqv4Du5PrKE2faN0qlbsaW/3QQfUUc6yO2EjFcA83xnwm91UbEEVx4ApZ9Z5oG8Bxz4qPE+HFwtVcfyw==} + engines: {node: '>=18'} cpu: [arm] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-ia32@0.16.3: - resolution: {integrity: sha512-X8FDDxM9cqda2rJE+iblQhIMYY49LfvW4kaEjoFbTTQ4Go8G96Smj2w3BRTwA8IHGoi9dPOPGAX63dhuv19UqA==} - engines: {node: '>=12'} + '@esbuild/linux-ia32@0.23.1': + resolution: {integrity: sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==} + engines: {node: '>=18'} cpu: [ia32] os: [linux] - requiresBuild: true - optional: true - /@esbuild/linux-ia32@0.17.6: - resolution: {integrity: sha512-ujp8uoQCM9FRcbDfkqECoARsLnLfCUhKARTP56TFPog8ie9JG83D5GVKjQ6yVrEVdMie1djH86fm98eY3quQkQ==} - engines: {node: '>=12'} + '@esbuild/linux-ia32@0.25.6': + resolution: {integrity: sha512-aHWdQ2AAltRkLPOsKdi3xv0mZ8fUGPdlKEjIEhxCPm5yKEThcUjHpWB1idN74lfXGnZ5SULQSgtr5Qos5B0bPw==} + engines: {node: '>=18'} cpu: [ia32] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-loong64@0.16.3: - resolution: {integrity: sha512-hIbeejCOyO0X9ujfIIOKjBjNAs9XD/YdJ9JXAy1lHA+8UXuOqbFe4ErMCqMr8dhlMGBuvcQYGF7+kO7waj2KHw==} - engines: {node: '>=12'} + '@esbuild/linux-loong64@0.23.1': + resolution: {integrity: sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==} + engines: {node: '>=18'} cpu: [loong64] os: [linux] - requiresBuild: true - optional: true - /@esbuild/linux-loong64@0.17.6: - resolution: {integrity: sha512-y2NX1+X/Nt+izj9bLoiaYB9YXT/LoaQFYvCkVD77G/4F+/yuVXYCWz4SE9yr5CBMbOxOfBcy/xFL4LlOeNlzYQ==} - engines: {node: '>=12'} + '@esbuild/linux-loong64@0.25.6': + resolution: {integrity: sha512-VgKCsHdXRSQ7E1+QXGdRPlQ/e08bN6WMQb27/TMfV+vPjjTImuT9PmLXupRlC90S1JeNNW5lzkAEO/McKeJ2yg==} + engines: {node: '>=18'} cpu: [loong64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-mips64el@0.16.3: - resolution: {integrity: sha512-znFRzICT/V8VZQMt6rjb21MtAVJv/3dmKRMlohlShrbVXdBuOdDrGb+C2cZGQAR8RFyRe7HS6klmHq103WpmVw==} - engines: {node: '>=12'} + '@esbuild/linux-mips64el@0.23.1': + resolution: {integrity: sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==} + engines: {node: '>=18'} cpu: [mips64el] os: [linux] - requiresBuild: true - optional: true - /@esbuild/linux-mips64el@0.17.6: - resolution: {integrity: sha512-09AXKB1HDOzXD+j3FdXCiL/MWmZP0Ex9eR8DLMBVcHorrWJxWmY8Nms2Nm41iRM64WVx7bA/JVHMv081iP2kUA==} - engines: {node: '>=12'} + '@esbuild/linux-mips64el@0.25.6': + resolution: {integrity: sha512-WViNlpivRKT9/py3kCmkHnn44GkGXVdXfdc4drNmRl15zVQ2+D2uFwdlGh6IuK5AAnGTo2qPB1Djppj+t78rzw==} + engines: {node: '>=18'} cpu: [mips64el] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-ppc64@0.16.3: - resolution: {integrity: sha512-EV7LuEybxhXrVTDpbqWF2yehYRNz5e5p+u3oQUS2+ZFpknyi1NXxr8URk4ykR8Efm7iu04//4sBg249yNOwy5Q==} - engines: {node: '>=12'} + '@esbuild/linux-ppc64@0.23.1': + resolution: {integrity: sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==} + engines: {node: '>=18'} cpu: [ppc64] os: [linux] - requiresBuild: true - optional: true - /@esbuild/linux-ppc64@0.17.6: - resolution: {integrity: sha512-AmLhMzkM8JuqTIOhxnX4ubh0XWJIznEynRnZAVdA2mMKE6FAfwT2TWKTwdqMG+qEaeyDPtfNoZRpJbD4ZBv0Tg==} - engines: {node: '>=12'} + '@esbuild/linux-ppc64@0.25.6': + resolution: {integrity: sha512-wyYKZ9NTdmAMb5730I38lBqVu6cKl4ZfYXIs31Baf8aoOtB4xSGi3THmDYt4BTFHk7/EcVixkOV2uZfwU3Q2Jw==} + engines: {node: '>=18'} cpu: [ppc64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-riscv64@0.16.3: - resolution: {integrity: sha512-uDxqFOcLzFIJ+r/pkTTSE9lsCEaV/Y6rMlQjUI9BkzASEChYL/aSQjZjchtEmdnVxDKETnUAmsaZ4pqK1eE5BQ==} - engines: {node: '>=12'} + '@esbuild/linux-riscv64@0.23.1': + resolution: {integrity: sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==} + engines: {node: '>=18'} cpu: [riscv64] os: [linux] - requiresBuild: true - optional: true - /@esbuild/linux-riscv64@0.17.6: - resolution: {integrity: sha512-Y4Ri62PfavhLQhFbqucysHOmRamlTVK10zPWlqjNbj2XMea+BOs4w6ASKwQwAiqf9ZqcY9Ab7NOU4wIgpxwoSQ==} - engines: {node: '>=12'} + '@esbuild/linux-riscv64@0.25.6': + resolution: {integrity: sha512-KZh7bAGGcrinEj4qzilJ4hqTY3Dg2U82c8bv+e1xqNqZCrCyc+TL9AUEn5WGKDzm3CfC5RODE/qc96OcbIe33w==} + engines: {node: '>=18'} cpu: [riscv64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-s390x@0.16.3: - resolution: {integrity: sha512-NbeREhzSxYwFhnCAQOQZmajsPYtX71Ufej3IQ8W2Gxskfz9DK58ENEju4SbpIj48VenktRASC52N5Fhyf/aliQ==} - engines: {node: '>=12'} + '@esbuild/linux-s390x@0.23.1': + resolution: {integrity: sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==} + engines: {node: '>=18'} cpu: [s390x] os: [linux] - requiresBuild: true - optional: true - /@esbuild/linux-s390x@0.17.6: - resolution: {integrity: sha512-SPUiz4fDbnNEm3JSdUW8pBJ/vkop3M1YwZAVwvdwlFLoJwKEZ9L98l3tzeyMzq27CyepDQ3Qgoba44StgbiN5Q==} - engines: {node: '>=12'} + '@esbuild/linux-s390x@0.25.6': + resolution: {integrity: sha512-9N1LsTwAuE9oj6lHMyyAM+ucxGiVnEqUdp4v7IaMmrwb06ZTEVCIs3oPPplVsnjPfyjmxwHxHMF8b6vzUVAUGw==} + engines: {node: '>=18'} cpu: [s390x] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-x64@0.16.3: - resolution: {integrity: sha512-SDiG0nCixYO9JgpehoKgScwic7vXXndfasjnD5DLbp1xltANzqZ425l7LSdHynt19UWOcDjG9wJJzSElsPvk0w==} - engines: {node: '>=12'} + '@esbuild/linux-x64@0.23.1': + resolution: {integrity: sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==} + engines: {node: '>=18'} cpu: [x64] os: [linux] - requiresBuild: true - optional: true - /@esbuild/linux-x64@0.17.6: - resolution: {integrity: sha512-a3yHLmOodHrzuNgdpB7peFGPx1iJ2x6m+uDvhP2CKdr2CwOaqEFMeSqYAHU7hG+RjCq8r2NFujcd/YsEsFgTGw==} - engines: {node: '>=12'} + '@esbuild/linux-x64@0.25.6': + resolution: {integrity: sha512-A6bJB41b4lKFWRKNrWoP2LHsjVzNiaurf7wyj/XtFNTsnPuxwEBWHLty+ZE0dWBKuSK1fvKgrKaNjBS7qbFKig==} + engines: {node: '>=18'} cpu: [x64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/netbsd-x64@0.16.3: - resolution: {integrity: sha512-AzbsJqiHEq1I/tUvOfAzCY15h4/7Ivp3ff/o1GpP16n48JMNAtbW0qui2WCgoIZArEHD0SUQ95gvR0oSO7ZbdA==} - engines: {node: '>=12'} + '@esbuild/netbsd-arm64@0.25.6': + resolution: {integrity: sha512-IjA+DcwoVpjEvyxZddDqBY+uJ2Snc6duLpjmkXm/v4xuS3H+3FkLZlDm9ZsAbF9rsfP3zeA0/ArNDORZgrxR/Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.23.1': + resolution: {integrity: sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==} + engines: {node: '>=18'} cpu: [x64] os: [netbsd] - requiresBuild: true - optional: true - /@esbuild/netbsd-x64@0.17.6: - resolution: {integrity: sha512-EanJqcU/4uZIBreTrnbnre2DXgXSa+Gjap7ifRfllpmyAU7YMvaXmljdArptTHmjrkkKm9BK6GH5D5Yo+p6y5A==} - engines: {node: '>=12'} + '@esbuild/netbsd-x64@0.25.6': + resolution: {integrity: sha512-dUXuZr5WenIDlMHdMkvDc1FAu4xdWixTCRgP7RQLBOkkGgwuuzaGSYcOpW4jFxzpzL1ejb8yF620UxAqnBrR9g==} + engines: {node: '>=18'} cpu: [x64] os: [netbsd] - requiresBuild: true - dev: true - optional: true - /@esbuild/openbsd-x64@0.16.3: - resolution: {integrity: sha512-gSABi8qHl8k3Cbi/4toAzHiykuBuWLZs43JomTcXkjMZVkp0gj3gg9mO+9HJW/8GB5H89RX/V0QP4JGL7YEEVg==} - engines: {node: '>=12'} + '@esbuild/openbsd-arm64@0.23.1': + resolution: {integrity: sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-arm64@0.25.6': + resolution: {integrity: sha512-l8ZCvXP0tbTJ3iaqdNf3pjaOSd5ex/e6/omLIQCVBLmHTlfXW3zAxQ4fnDmPLOB1x9xrcSi/xtCWFwCZRIaEwg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.23.1': + resolution: {integrity: sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==} + engines: {node: '>=18'} cpu: [x64] os: [openbsd] - requiresBuild: true - optional: true - /@esbuild/openbsd-x64@0.17.6: - resolution: {integrity: sha512-xaxeSunhQRsTNGFanoOkkLtnmMn5QbA0qBhNet/XLVsc+OVkpIWPHcr3zTW2gxVU5YOHFbIHR9ODuaUdNza2Vw==} - engines: {node: '>=12'} + '@esbuild/openbsd-x64@0.25.6': + resolution: {integrity: sha512-hKrmDa0aOFOr71KQ/19JC7az1P0GWtCN1t2ahYAf4O007DHZt/dW8ym5+CUdJhQ/qkZmI1HAF8KkJbEFtCL7gw==} + engines: {node: '>=18'} cpu: [x64] os: [openbsd] - requiresBuild: true - dev: true - optional: true - /@esbuild/sunos-x64@0.16.3: - resolution: {integrity: sha512-SF9Kch5Ete4reovvRO6yNjMxrvlfT0F0Flm+NPoUw5Z4Q3r1d23LFTgaLwm3Cp0iGbrU/MoUI+ZqwCv5XJijCw==} - engines: {node: '>=12'} + '@esbuild/openharmony-arm64@0.25.6': + resolution: {integrity: sha512-+SqBcAWoB1fYKmpWoQP4pGtx+pUUC//RNYhFdbcSA16617cchuryuhOCRpPsjCblKukAckWsV+aQ3UKT/RMPcA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.23.1': + resolution: {integrity: sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==} + engines: {node: '>=18'} cpu: [x64] os: [sunos] - requiresBuild: true - optional: true - /@esbuild/sunos-x64@0.17.6: - resolution: {integrity: sha512-gnMnMPg5pfMkZvhHee21KbKdc6W3GR8/JuE0Da1kjwpK6oiFU3nqfHuVPgUX2rsOx9N2SadSQTIYV1CIjYG+xw==} - engines: {node: '>=12'} + '@esbuild/sunos-x64@0.25.6': + resolution: {integrity: sha512-dyCGxv1/Br7MiSC42qinGL8KkG4kX0pEsdb0+TKhmJZgCUDBGmyo1/ArCjNGiOLiIAgdbWgmWgib4HoCi5t7kA==} + engines: {node: '>=18'} cpu: [x64] os: [sunos] - requiresBuild: true - dev: true - optional: true - /@esbuild/win32-arm64@0.16.3: - resolution: {integrity: sha512-u5aBonZIyGopAZyOnoPAA6fGsDeHByZ9CnEzyML9NqntK6D/xl5jteZUKm/p6nD09+v3pTM6TuUIqSPcChk5gg==} - engines: {node: '>=12'} + '@esbuild/win32-arm64@0.23.1': + resolution: {integrity: sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==} + engines: {node: '>=18'} cpu: [arm64] os: [win32] - requiresBuild: true - optional: true - /@esbuild/win32-arm64@0.17.6: - resolution: {integrity: sha512-G95n7vP1UnGJPsVdKXllAJPtqjMvFYbN20e8RK8LVLhlTiSOH1sd7+Gt7rm70xiG+I5tM58nYgwWrLs6I1jHqg==} - engines: {node: '>=12'} + '@esbuild/win32-arm64@0.25.6': + resolution: {integrity: sha512-42QOgcZeZOvXfsCBJF5Afw73t4veOId//XD3i+/9gSkhSV6Gk3VPlWncctI+JcOyERv85FUo7RxuxGy+z8A43Q==} + engines: {node: '>=18'} cpu: [arm64] os: [win32] - requiresBuild: true - dev: true - optional: true - /@esbuild/win32-ia32@0.16.3: - resolution: {integrity: sha512-GlgVq1WpvOEhNioh74TKelwla9KDuAaLZrdxuuUgsP2vayxeLgVc+rbpIv0IYF4+tlIzq2vRhofV+KGLD+37EQ==} - engines: {node: '>=12'} + '@esbuild/win32-ia32@0.23.1': + resolution: {integrity: sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==} + engines: {node: '>=18'} cpu: [ia32] os: [win32] - requiresBuild: true - optional: true - /@esbuild/win32-ia32@0.17.6: - resolution: {integrity: sha512-96yEFzLhq5bv9jJo5JhTs1gI+1cKQ83cUpyxHuGqXVwQtY5Eq54ZEsKs8veKtiKwlrNimtckHEkj4mRh4pPjsg==} - engines: {node: '>=12'} + '@esbuild/win32-ia32@0.25.6': + resolution: {integrity: sha512-4AWhgXmDuYN7rJI6ORB+uU9DHLq/erBbuMoAuB4VWJTu5KtCgcKYPynF0YI1VkBNuEfjNlLrFr9KZPJzrtLkrQ==} + engines: {node: '>=18'} cpu: [ia32] os: [win32] - requiresBuild: true - dev: true - optional: true - /@esbuild/win32-x64@0.16.3: - resolution: {integrity: sha512-5/JuTd8OWW8UzEtyf19fbrtMJENza+C9JoPIkvItgTBQ1FO2ZLvjbPO6Xs54vk0s5JB5QsfieUEshRQfu7ZHow==} - engines: {node: '>=12'} + '@esbuild/win32-x64@0.23.1': + resolution: {integrity: sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==} + engines: {node: '>=18'} cpu: [x64] os: [win32] - requiresBuild: true - optional: true - /@esbuild/win32-x64@0.17.6: - resolution: {integrity: sha512-n6d8MOyUrNp6G4VSpRcgjs5xj4A91svJSaiwLIDWVWEsZtpN5FA9NlBbZHDmAJc2e8e6SF4tkBD3HAvPF+7igA==} - engines: {node: '>=12'} + '@esbuild/win32-x64@0.25.6': + resolution: {integrity: sha512-NgJPHHbEpLQgDH2MjQu90pzW/5vvXIZ7KOnPyNBm92A6WgZ/7b6fJyUBjoumLqeOQQGqY2QjQxRo97ah4Sj0cA==} + engines: {node: '>=18'} cpu: [x64] os: [win32] - requiresBuild: true - dev: true - optional: true - /@eslint-community/eslint-utils@4.4.0(eslint@8.38.0): - resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} + '@eslint-community/eslint-utils@4.4.1': + resolution: {integrity: sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - dependencies: - eslint: 8.38.0 - eslint-visitor-keys: 3.4.0 - dev: false - /@eslint-community/regexpp@4.5.0: - resolution: {integrity: sha512-vITaYzIcNmjn5tF5uxcZ/ft7/RXGrMUIS9HalWckEOF6ESiwXKoMzAQf2UW0aVd6rnOeExTJVd5hmWXucBKGXQ==} + '@eslint-community/regexpp@4.12.1': + resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - dev: false - - /@eslint/eslintrc@1.4.1: - resolution: {integrity: sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - ajv: 6.12.6 - debug: 4.3.4 - espree: 9.5.1 - globals: 13.20.0 - ignore: 5.2.4 - import-fresh: 3.3.0 - js-yaml: 4.1.0 - minimatch: 3.1.2 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color - /@eslint/eslintrc@2.0.2: - resolution: {integrity: sha512-3W4f5tDUra+pA+FzgugqL2pRimUTDJWKr7BINqOpkZrC0uYI0NIc0/JFgBROCU07HR6GieA5m3/rsPIhDmCXTQ==} + '@eslint/eslintrc@2.1.4': + resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - ajv: 6.12.6 - debug: 4.3.4 - espree: 9.5.1 - globals: 13.20.0 - ignore: 5.2.4 - import-fresh: 3.3.0 - js-yaml: 4.1.0 - minimatch: 3.1.2 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color - dev: false - /@eslint/js@8.38.0: - resolution: {integrity: sha512-IoD2MfUnOV58ghIHCiil01PcohxjbYR/qCxsoC+xNgUwh1EY8jOOrYmu3d3a71+tJJ23uscEV4X2HJWMsPJu4g==} + '@eslint/js@8.57.1': + resolution: {integrity: sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dev: false - /@fastify/busboy@1.2.1: - resolution: {integrity: sha512-7PQA7EH43S0CxcOa9OeAnaeA0oQ+e/DHNPZwSQM9CQHW76jle5+OvLdibRp/Aafs9KXbLhxyjOTkRjWUbQEd3Q==} - engines: {node: '>=14'} - dependencies: - text-decoding: 1.0.0 - dev: false + '@fastify/busboy@3.0.0': + resolution: {integrity: sha512-83rnH2nCvclWaPQQKvkJ2pdOjG4TZyEVuFDnlOF6KP08lDaaceVyw/W63mDuafQT+MKHCvXIPpE5uYWeM0rT4w==} - /@firebase/analytics-compat@0.2.5(@firebase/app-compat@0.2.7)(@firebase/app@0.9.7): - resolution: {integrity: sha512-ohKUrwSoXvyUJdSLuDr82mOqrzgWKyHMUt9/TfYKkyDXnFjNlBcFBpkpl/UHMAOJe0M60YYXiVCZoGQYldCslA==} + '@firebase/analytics-compat@0.2.18': + resolution: {integrity: sha512-Hw9mzsSMZaQu6wrTbi3kYYwGw9nBqOHr47pVLxfr5v8CalsdrG5gfs9XUlPOZjHRVISp3oQrh1j7d3E+ulHPjQ==} peerDependencies: '@firebase/app-compat': 0.x - dependencies: - '@firebase/analytics': 0.9.5(@firebase/app@0.9.7) - '@firebase/analytics-types': 0.8.0 - '@firebase/app-compat': 0.2.7 - '@firebase/component': 0.6.4 - '@firebase/util': 1.9.3 - tslib: 2.5.0 - transitivePeerDependencies: - - '@firebase/app' - /@firebase/analytics-types@0.8.0: - resolution: {integrity: sha512-iRP+QKI2+oz3UAh4nPEq14CsEjrjD6a5+fuypjScisAh9kXKFvdJOZJDwk7kikLvWVLGEs9+kIUS4LPQV7VZVw==} + '@firebase/analytics-types@0.8.3': + resolution: {integrity: sha512-VrIp/d8iq2g501qO46uGz3hjbDb8xzYMrbu8Tp0ovzIzrvJZ2fvmj649gTjge/b7cCCcjT0H37g1gVtlNhnkbg==} - /@firebase/analytics@0.9.5(@firebase/app@0.9.7): - resolution: {integrity: sha512-hJTVs2jLxPXE7hs7D/jaEsgGivrm7tSEl65kb5NkDBWV7QQBUnRfVML/xra9nTFLLJhAdbExZPHg6HfIuMSYEQ==} + '@firebase/analytics@0.10.12': + resolution: {integrity: sha512-iDCGnw6qdFqwI5ywkgece99WADJNoymu+nLIQI4fZM/vCZ3bEo4wlpEetW71s1HqGpI0hQStiPhqVjFxDb2yyw==} peerDependencies: '@firebase/app': 0.x - dependencies: - '@firebase/app': 0.9.7 - '@firebase/component': 0.6.4 - '@firebase/installations': 0.6.4(@firebase/app@0.9.7) - '@firebase/logger': 0.4.0 - '@firebase/util': 1.9.3 - tslib: 2.5.0 - /@firebase/app-check-compat@0.3.4(@firebase/app-compat@0.2.7)(@firebase/app@0.9.7): - resolution: {integrity: sha512-s6ON0ixPKe99M1DNYMI2eR5aLwQZgy0z8fuW1tnEbzg5p/N/GKFmqiIHSV4gfp8+X7Fw5NLm7qMfh4xrcPgQCw==} + '@firebase/app-check-compat@0.3.19': + resolution: {integrity: sha512-G8FMiqhrKc4gEEujrBDBBrbRav8MGqoLObWj1hy/riCSg4XlRYhpnq3ev8E9HTirqU1tAGH6oJl7vr+jfM7YNA==} + engines: {node: '>=18.0.0'} peerDependencies: '@firebase/app-compat': 0.x - dependencies: - '@firebase/app-check': 0.6.4(@firebase/app@0.9.7) - '@firebase/app-check-types': 0.5.0 - '@firebase/app-compat': 0.2.7 - '@firebase/component': 0.6.4 - '@firebase/logger': 0.4.0 - '@firebase/util': 1.9.3 - tslib: 2.5.0 - transitivePeerDependencies: - - '@firebase/app' - /@firebase/app-check-interop-types@0.2.0: - resolution: {integrity: sha512-+3PQIeX6/eiVK+x/yg8r6xTNR97fN7MahFDm+jiQmDjcyvSefoGuTTNQuuMScGyx3vYUBeZn+Cp9kC0yY/9uxQ==} + '@firebase/app-check-interop-types@0.3.3': + resolution: {integrity: sha512-gAlxfPLT2j8bTI/qfe3ahl2I2YcBQ8cFIBdhAQA4I2f3TndcO+22YizyGYuttLHPQEpWkhmpFW60VCFEPg4g5A==} - /@firebase/app-check-types@0.5.0: - resolution: {integrity: sha512-uwSUj32Mlubybw7tedRzR24RP8M8JUVR3NPiMk3/Z4bCmgEKTlQBwMXrehDAZ2wF+TsBq0SN1c6ema71U/JPyQ==} + '@firebase/app-check-types@0.5.3': + resolution: {integrity: sha512-hyl5rKSj0QmwPdsAxrI5x1otDlByQ7bvNvVt8G/XPO2CSwE++rmSVf3VEhaeOR4J8ZFaF0Z0NDSmLejPweZ3ng==} - /@firebase/app-check@0.6.4(@firebase/app@0.9.7): - resolution: {integrity: sha512-M9qyVTWkEkHXmgwGtObvXQqKcOe9iKAOPqm0pCe74mzgKVTNq157ff39+fxHPb4nFbipToY+GuvtabLUzkHehQ==} + '@firebase/app-check@0.8.12': + resolution: {integrity: sha512-LxjcoIFOU4sgK07ZWb8XDHxuVB+UKs41vPK+Sg9PeZMvEoz84fndFAx8Nz2nipiya2EmyxBgVhff8Hi6GBt+XA==} + engines: {node: '>=18.0.0'} peerDependencies: '@firebase/app': 0.x - dependencies: - '@firebase/app': 0.9.7 - '@firebase/component': 0.6.4 - '@firebase/logger': 0.4.0 - '@firebase/util': 1.9.3 - tslib: 2.5.0 - /@firebase/app-compat@0.2.7: - resolution: {integrity: sha512-KYBUKoRrvSGW8jqKgARRsma0lJie9M0zyWhPF3PNjqc9pYsw7SZXp5s5SzsheeCXzIDFydP5uEA4f1Z87D7CxQ==} - dependencies: - '@firebase/app': 0.9.7 - '@firebase/component': 0.6.4 - '@firebase/logger': 0.4.0 - '@firebase/util': 1.9.3 - tslib: 2.5.0 + '@firebase/app-compat@0.2.51': + resolution: {integrity: sha512-pxF1+coABt+ugqNI0YXDlmkKv4kh3pjI5BqIJJ1VXBo42OZbKMsQbFeos14YBrWwiqqSjUvQ70FBNsv5E2wuxg==} + engines: {node: '>=18.0.0'} - /@firebase/app-types@0.9.0: - resolution: {integrity: sha512-AeweANOIo0Mb8GiYm3xhTEBVCmPwTYAu9Hcd2qSkLuga/6+j9b1Jskl5bpiSQWy9eJ/j5pavxj6eYogmnuzm+Q==} + '@firebase/app-types@0.9.3': + resolution: {integrity: sha512-kRVpIl4vVGJ4baogMDINbyrIOtOxqhkZQg4jTq3l8Lw6WSk0xfpEYzezFu+Kl4ve4fbPl79dvwRtaFqAC/ucCw==} - /@firebase/app@0.9.7: - resolution: {integrity: sha512-ADnRXaW4XQF11QYYhZQEJEtOGnmLkGl2FCixCxPighLrmJmGwCZrzSFtwITd8w/EU3dRYaU5Og37VfnY+gKxGw==} - dependencies: - '@firebase/component': 0.6.4 - '@firebase/logger': 0.4.0 - '@firebase/util': 1.9.3 - idb: 7.0.1 - tslib: 2.5.0 + '@firebase/app@0.11.2': + resolution: {integrity: sha512-bFee0hPJZBzNtiizRxdgsu8C9DW3mn1y0OJJ4zHQsccjDYzGOfvN0G3CMGyBIiwNctsFpQa8orbp2IKywoUeqA==} + engines: {node: '>=18.0.0'} - /@firebase/auth-compat@0.3.7(@firebase/app-compat@0.2.7)(@firebase/app-types@0.9.0)(@firebase/app@0.9.7): - resolution: {integrity: sha512-+r8/++hYZLA/to6Iq8A70LTUsZvhkdT2R4mB4oJGxryJ7vNjpuP5m5hfAd42h/VvX8eT1OXJCENCfEZoDyhksA==} + '@firebase/auth-compat@0.5.19': + resolution: {integrity: sha512-v898POphOIBJliKF76SiGOXh4EdhO5fM6S9a2ZKf/8wHdBea/qwxwZoVVya4DW6Mi7vWyp1lIzHbFgwRz8G9TA==} + engines: {node: '>=18.0.0'} peerDependencies: '@firebase/app-compat': 0.x - dependencies: - '@firebase/app-compat': 0.2.7 - '@firebase/auth': 0.22.0(@firebase/app@0.9.7) - '@firebase/auth-types': 0.12.0(@firebase/app-types@0.9.0)(@firebase/util@1.9.3) - '@firebase/component': 0.6.4 - '@firebase/util': 1.9.3 - node-fetch: 2.6.7 - tslib: 2.5.0 - transitivePeerDependencies: - - '@firebase/app' - - '@firebase/app-types' - - encoding - /@firebase/auth-interop-types@0.2.1: - resolution: {integrity: sha512-VOaGzKp65MY6P5FI84TfYKBXEPi6LmOCSMMzys6o2BN2LOsqy7pCuZCup7NYnfbk5OkkQKzvIfHOzTm0UDpkyg==} + '@firebase/auth-interop-types@0.2.4': + resolution: {integrity: sha512-JPgcXKCuO+CWqGDnigBtvo09HeBs5u/Ktc2GaFj2m01hLarbxthLNm7Fk8iOP1aqAtXV+fnnGj7U28xmk7IwVA==} - /@firebase/auth-types@0.12.0(@firebase/app-types@0.9.0)(@firebase/util@1.9.3): - resolution: {integrity: sha512-pPwaZt+SPOshK8xNoiQlK5XIrS97kFYc3Rc7xmy373QsOJ9MmqXxLaYssP5Kcds4wd2qK//amx/c+A8O2fVeZA==} + '@firebase/auth-types@0.13.0': + resolution: {integrity: sha512-S/PuIjni0AQRLF+l9ck0YpsMOdE8GO2KU6ubmBB7P+7TJUCQDa3R1dlgYm9UzGbbePMZsp0xzB93f2b/CgxMOg==} peerDependencies: '@firebase/app-types': 0.x '@firebase/util': 1.x - dependencies: - '@firebase/app-types': 0.9.0 - '@firebase/util': 1.9.3 - /@firebase/auth@0.22.0(@firebase/app@0.9.7): - resolution: {integrity: sha512-4PiaDJEhJ7FNo48WG0TAlqHiCuRBXxUow2q+0emh+PhmM0cLT1UdqK1EuWWGc5CY+ztNQZUh+Yzeh+nv9tZL0w==} + '@firebase/auth@1.9.1': + resolution: {integrity: sha512-9KKo5SNVkyJzftsW+daS+PGDbeJ+MFJWXQFHDqqPPH3acWHtiNnGHH5HGpIJErEELrsm9xMPie5zfZ0XpGU8+w==} + engines: {node: '>=18.0.0'} peerDependencies: '@firebase/app': 0.x - dependencies: - '@firebase/app': 0.9.7 - '@firebase/component': 0.6.4 - '@firebase/logger': 0.4.0 - '@firebase/util': 1.9.3 - node-fetch: 2.6.7 - tslib: 2.5.0 - transitivePeerDependencies: - - encoding + '@react-native-async-storage/async-storage': ^1.18.1 + peerDependenciesMeta: + '@react-native-async-storage/async-storage': + optional: true - /@firebase/component@0.6.4: - resolution: {integrity: sha512-rLMyrXuO9jcAUCaQXCMjCMUsWrba5fzHlNK24xz5j2W6A/SRmK8mZJ/hn7V0fViLbxC0lPMtrK1eYzk6Fg03jA==} - dependencies: - '@firebase/util': 1.9.3 - tslib: 2.5.0 + '@firebase/component@0.6.13': + resolution: {integrity: sha512-I/Eg1NpAtZ8AAfq8mpdfXnuUpcLxIDdCDtTzWSh+FXnp/9eCKJ3SNbOCKrUCyhLzNa2SiPJYruei0sxVjaOTeg==} + engines: {node: '>=18.0.0'} - /@firebase/database-compat@0.3.4: - resolution: {integrity: sha512-kuAW+l+sLMUKBThnvxvUZ+Q1ZrF/vFJ58iUY9kAcbX48U03nVzIF6Tmkf0p3WVQwMqiXguSgtOPIB6ZCeF+5Gg==} - dependencies: - '@firebase/component': 0.6.4 - '@firebase/database': 0.14.4 - '@firebase/database-types': 0.10.4 - '@firebase/logger': 0.4.0 - '@firebase/util': 1.9.3 - tslib: 2.5.0 + '@firebase/data-connect@0.3.1': + resolution: {integrity: sha512-PNlfAJ2mcbyRlWfm41nfk8EksTuvMFTFIX+puNzeUa6OTIDtyp1IX1NJVc7n6WpfbErN7tNqcOEMe6BMtpcjVA==} + peerDependencies: + '@firebase/app': 0.x - /@firebase/database-types@0.10.4: - resolution: {integrity: sha512-dPySn0vJ/89ZeBac70T+2tWWPiJXWbmRygYv0smT5TfE3hDrQ09eKMF3Y+vMlTdrMWq7mUdYW5REWPSGH4kAZQ==} - dependencies: - '@firebase/app-types': 0.9.0 - '@firebase/util': 1.9.3 + '@firebase/database-compat@2.0.4': + resolution: {integrity: sha512-4qsptwZ3DTGNBje56ETItZQyA/HMalOelnLmkC3eR0M6+zkzOHjNHyWUWodW2mqxRKAM0sGkn+aIwYHKZFJXug==} + engines: {node: '>=18.0.0'} - /@firebase/database@0.14.4: - resolution: {integrity: sha512-+Ea/IKGwh42jwdjCyzTmeZeLM3oy1h0mFPsTy6OqCWzcu/KFqRAr5Tt1HRCOBlNOdbh84JPZC47WLU18n2VbxQ==} - dependencies: - '@firebase/auth-interop-types': 0.2.1 - '@firebase/component': 0.6.4 - '@firebase/logger': 0.4.0 - '@firebase/util': 1.9.3 - faye-websocket: 0.11.4 - tslib: 2.5.0 + '@firebase/database-types@1.0.9': + resolution: {integrity: sha512-uCntrxPbJHhZsNRpMhxNCm7GzhYWX+7J2e57wq1ZZ4NJrQw5DORgkAzJMByYZcVAjgADnCxxhK/GkoypH+XpvQ==} - /@firebase/firestore-compat@0.3.6(@firebase/app-compat@0.2.7)(@firebase/app-types@0.9.0)(@firebase/app@0.9.7): - resolution: {integrity: sha512-svS8oV0nwTyoHW5mslFV0gRb3FLpRQGjz2F7nc5imnPUTjSJmAfXECtgs5HG5MSJM/laSimfAeGuQVh5FM1AEw==} + '@firebase/database@1.0.13': + resolution: {integrity: sha512-cdc+LuseKdJXzlrCx8ePMXyctSWtYS9SsP3y7EeA85GzNh/IL0b7HOq0eShridL935iQ0KScZCj5qJtKkGE53g==} + engines: {node: '>=18.0.0'} + + '@firebase/firestore-compat@0.3.44': + resolution: {integrity: sha512-4Lv2TyHEW+FugXPgmQ0ZylSbh9uFuKDP0lCL1hX9cbxXaafhC/Nww+DWokUQ2zZcynjc8fxFunw6Xbd3QHAlgA==} + engines: {node: '>=18.0.0'} peerDependencies: '@firebase/app-compat': 0.x - dependencies: - '@firebase/app-compat': 0.2.7 - '@firebase/component': 0.6.4 - '@firebase/firestore': 3.10.0(@firebase/app@0.9.7) - '@firebase/firestore-types': 2.5.1(@firebase/app-types@0.9.0)(@firebase/util@1.9.3) - '@firebase/util': 1.9.3 - tslib: 2.5.0 - transitivePeerDependencies: - - '@firebase/app' - - '@firebase/app-types' - - encoding - /@firebase/firestore-types@2.5.1(@firebase/app-types@0.9.0)(@firebase/util@1.9.3): - resolution: {integrity: sha512-xG0CA6EMfYo8YeUxC8FeDzf6W3FX1cLlcAGBYV6Cku12sZRI81oWcu61RSKM66K6kUENP+78Qm8mvroBcm1whw==} + '@firebase/firestore-types@3.0.3': + resolution: {integrity: sha512-hD2jGdiWRxB/eZWF89xcK9gF8wvENDJkzpVFb4aGkzfEaKxVRD1kjz1t1Wj8VZEp2LCB53Yx1zD8mrhQu87R6Q==} peerDependencies: '@firebase/app-types': 0.x '@firebase/util': 1.x - dependencies: - '@firebase/app-types': 0.9.0 - '@firebase/util': 1.9.3 - /@firebase/firestore@3.10.0(@firebase/app@0.9.7): - resolution: {integrity: sha512-St6yy2r7zYxJiAEiI19aQJqxVV8LDvlmeK52R9KMn2nZsgdDVOurch1cH7bBl0OxEgfiVxBmAQJLYvZc+qwAgw==} - engines: {node: '>=10.10.0'} + '@firebase/firestore@4.7.9': + resolution: {integrity: sha512-uq/bUtHDqJ5ZqPHAJIlNzHpXUtcVYcASz2V6y7UmP1WLlRKEt1yf1OcQW5u8pY2yq7162OnCl5J5mkOdMTMLZw==} + engines: {node: '>=18.0.0'} peerDependencies: '@firebase/app': 0.x - dependencies: - '@firebase/app': 0.9.7 - '@firebase/component': 0.6.4 - '@firebase/logger': 0.4.0 - '@firebase/util': 1.9.3 - '@firebase/webchannel-wrapper': 0.9.0 - '@grpc/grpc-js': 1.7.3 - '@grpc/proto-loader': 0.6.13 - node-fetch: 2.6.7 - tslib: 2.5.0 - transitivePeerDependencies: - - encoding - /@firebase/functions-compat@0.3.4(@firebase/app-compat@0.2.7)(@firebase/app@0.9.7): - resolution: {integrity: sha512-kxVxTGyLV1MBR3sp3mI+eQ6JBqz0G5bk310F8eX4HzDFk4xjk5xY0KdHktMH+edM2xs1BOg0vwvvsAHczIjB+w==} + '@firebase/functions-compat@0.3.20': + resolution: {integrity: sha512-iIudmYDAML6n3c7uXO2YTlzra2/J6lnMzmJTXNthvrKVMgNMaseNoQP1wKfchK84hMuSF8EkM4AvufwbJ+Juew==} + engines: {node: '>=18.0.0'} peerDependencies: '@firebase/app-compat': 0.x - dependencies: - '@firebase/app-compat': 0.2.7 - '@firebase/component': 0.6.4 - '@firebase/functions': 0.9.4(@firebase/app@0.9.7) - '@firebase/functions-types': 0.6.0 - '@firebase/util': 1.9.3 - tslib: 2.5.0 - transitivePeerDependencies: - - '@firebase/app' - - encoding - /@firebase/functions-types@0.6.0: - resolution: {integrity: sha512-hfEw5VJtgWXIRf92ImLkgENqpL6IWpYaXVYiRkFY1jJ9+6tIhWM7IzzwbevwIIud/jaxKVdRzD7QBWfPmkwCYw==} + '@firebase/functions-types@0.6.3': + resolution: {integrity: sha512-EZoDKQLUHFKNx6VLipQwrSMh01A1SaL3Wg6Hpi//x6/fJ6Ee4hrAeswK99I5Ht8roiniKHw4iO0B1Oxj5I4plg==} - /@firebase/functions@0.9.4(@firebase/app@0.9.7): - resolution: {integrity: sha512-3H2qh6U+q+nepO5Hds+Ddl6J0pS+zisuBLqqQMRBHv9XpWfu0PnDHklNmE8rZ+ccTEXvBj6zjkPfdxt6NisvlQ==} + '@firebase/functions@0.12.3': + resolution: {integrity: sha512-Wv7JZMUkKLb1goOWRtsu3t7m97uK6XQvjQLPvn8rncY91+VgdU72crqnaYCDI/ophNuBEmuK8mn0/pAnjUeA6A==} + engines: {node: '>=18.0.0'} peerDependencies: '@firebase/app': 0.x - dependencies: - '@firebase/app': 0.9.7 - '@firebase/app-check-interop-types': 0.2.0 - '@firebase/auth-interop-types': 0.2.1 - '@firebase/component': 0.6.4 - '@firebase/messaging-interop-types': 0.2.0 - '@firebase/util': 1.9.3 - node-fetch: 2.6.7 - tslib: 2.5.0 - transitivePeerDependencies: - - encoding - /@firebase/installations-compat@0.2.4(@firebase/app-compat@0.2.7)(@firebase/app-types@0.9.0)(@firebase/app@0.9.7): - resolution: {integrity: sha512-LI9dYjp0aT9Njkn9U4JRrDqQ6KXeAmFbRC0E7jI7+hxl5YmRWysq5qgQl22hcWpTk+cm3es66d/apoDU/A9n6Q==} + '@firebase/installations-compat@0.2.13': + resolution: {integrity: sha512-f/o6MqCI7LD/ulY9gvgkv6w5k6diaReD8BFHd/y/fEdpsXmFWYS/g28GXCB72bRVBOgPpkOUNl+VsMvDwlRKmw==} peerDependencies: '@firebase/app-compat': 0.x - dependencies: - '@firebase/app-compat': 0.2.7 - '@firebase/component': 0.6.4 - '@firebase/installations': 0.6.4(@firebase/app@0.9.7) - '@firebase/installations-types': 0.5.0(@firebase/app-types@0.9.0) - '@firebase/util': 1.9.3 - tslib: 2.5.0 - transitivePeerDependencies: - - '@firebase/app' - - '@firebase/app-types' - /@firebase/installations-types@0.5.0(@firebase/app-types@0.9.0): - resolution: {integrity: sha512-9DP+RGfzoI2jH7gY4SlzqvZ+hr7gYzPODrbzVD82Y12kScZ6ZpRg/i3j6rleto8vTFC8n6Len4560FnV1w2IRg==} + '@firebase/installations-types@0.5.3': + resolution: {integrity: sha512-2FJI7gkLqIE0iYsNQ1P751lO3hER+Umykel+TkLwHj6plzWVxqvfclPUZhcKFVQObqloEBTmpi2Ozn7EkCABAA==} peerDependencies: '@firebase/app-types': 0.x - dependencies: - '@firebase/app-types': 0.9.0 - /@firebase/installations@0.6.4(@firebase/app@0.9.7): - resolution: {integrity: sha512-u5y88rtsp7NYkCHC3ElbFBrPtieUybZluXyzl7+4BsIz4sqb4vSAuwHEUgCgCeaQhvsnxDEU6icly8U9zsJigA==} + '@firebase/installations@0.6.13': + resolution: {integrity: sha512-6ZpkUiaygPFwgVneYxuuOuHnSPnTA4KefLEaw/sKk/rNYgC7X6twaGfYb0sYLpbi9xV4i5jXsqZ3WO+yaguNgg==} peerDependencies: '@firebase/app': 0.x - dependencies: - '@firebase/app': 0.9.7 - '@firebase/component': 0.6.4 - '@firebase/util': 1.9.3 - idb: 7.0.1 - tslib: 2.5.0 - /@firebase/logger@0.4.0: - resolution: {integrity: sha512-eRKSeykumZ5+cJPdxxJRgAC3G5NknY2GwEbKfymdnXtnT0Ucm4pspfR6GT4MUQEDuJwRVbVcSx85kgJulMoFFA==} - dependencies: - tslib: 2.5.0 + '@firebase/logger@0.4.4': + resolution: {integrity: sha512-mH0PEh1zoXGnaR8gD1DeGeNZtWFKbnz9hDO91dIml3iou1gpOnLqXQ2dJfB71dj6dpmUjcQ6phY3ZZJbjErr9g==} + engines: {node: '>=18.0.0'} - /@firebase/messaging-compat@0.2.4(@firebase/app-compat@0.2.7)(@firebase/app@0.9.7): - resolution: {integrity: sha512-lyFjeUhIsPRYDPNIkYX1LcZMpoVbBWXX4rPl7c/rqc7G+EUea7IEtSt4MxTvh6fDfPuzLn7+FZADfscC+tNMfg==} + '@firebase/messaging-compat@0.2.17': + resolution: {integrity: sha512-5Q+9IG7FuedusdWHVQRjpA3OVD9KUWp/IPegcv0s5qSqRLBjib7FlAeWxN+VL0Ew43tuPJBY2HKhEecuizmO1Q==} peerDependencies: '@firebase/app-compat': 0.x - dependencies: - '@firebase/app-compat': 0.2.7 - '@firebase/component': 0.6.4 - '@firebase/messaging': 0.12.4(@firebase/app@0.9.7) - '@firebase/util': 1.9.3 - tslib: 2.5.0 - transitivePeerDependencies: - - '@firebase/app' - /@firebase/messaging-interop-types@0.2.0: - resolution: {integrity: sha512-ujA8dcRuVeBixGR9CtegfpU4YmZf3Lt7QYkcj693FFannwNuZgfAYaTmbJ40dtjB81SAu6tbFPL9YLNT15KmOQ==} + '@firebase/messaging-interop-types@0.2.3': + resolution: {integrity: sha512-xfzFaJpzcmtDjycpDeCUj0Ge10ATFi/VHVIvEEjDNc3hodVBQADZ7BWQU7CuFpjSHE+eLuBI13z5F/9xOoGX8Q==} - /@firebase/messaging@0.12.4(@firebase/app@0.9.7): - resolution: {integrity: sha512-6JLZct6zUaex4g7HI3QbzeUrg9xcnmDAPTWpkoMpd/GoSVWH98zDoWXMGrcvHeCAIsLpFMe4MPoZkJbrPhaASw==} + '@firebase/messaging@0.12.17': + resolution: {integrity: sha512-W3CnGhTm6Nx8XGb6E5/+jZTuxX/EK8Vur4QXvO1DwZta/t0xqWMRgO9vNsZFMYBqFV4o3j4F9qK/iddGYwWS6g==} peerDependencies: '@firebase/app': 0.x - dependencies: - '@firebase/app': 0.9.7 - '@firebase/component': 0.6.4 - '@firebase/installations': 0.6.4(@firebase/app@0.9.7) - '@firebase/messaging-interop-types': 0.2.0 - '@firebase/util': 1.9.3 - idb: 7.0.1 - tslib: 2.5.0 - /@firebase/performance-compat@0.2.4(@firebase/app-compat@0.2.7)(@firebase/app@0.9.7): - resolution: {integrity: sha512-nnHUb8uP9G8islzcld/k6Bg5RhX62VpbAb/Anj7IXs/hp32Eb2LqFPZK4sy3pKkBUO5wcrlRWQa6wKOxqlUqsg==} + '@firebase/performance-compat@0.2.14': + resolution: {integrity: sha512-/crPg0fDqHIx+FjFoEqWxNp+lJSF40ZG7x43AAJGRaUaWLJDncQm3UJB5/mABaRZb7obs1CQAcRtd4phZFkmZg==} peerDependencies: '@firebase/app-compat': 0.x - dependencies: - '@firebase/app-compat': 0.2.7 - '@firebase/component': 0.6.4 - '@firebase/logger': 0.4.0 - '@firebase/performance': 0.6.4(@firebase/app@0.9.7) - '@firebase/performance-types': 0.2.0 - '@firebase/util': 1.9.3 - tslib: 2.5.0 - transitivePeerDependencies: - - '@firebase/app' - /@firebase/performance-types@0.2.0: - resolution: {integrity: sha512-kYrbr8e/CYr1KLrLYZZt2noNnf+pRwDq2KK9Au9jHrBMnb0/C9X9yWSXmZkFt4UIdsQknBq8uBB7fsybZdOBTA==} + '@firebase/performance-types@0.2.3': + resolution: {integrity: sha512-IgkyTz6QZVPAq8GSkLYJvwSLr3LS9+V6vNPQr0x4YozZJiLF5jYixj0amDtATf1X0EtYHqoPO48a9ija8GocxQ==} - /@firebase/performance@0.6.4(@firebase/app@0.9.7): - resolution: {integrity: sha512-HfTn/bd8mfy/61vEqaBelNiNnvAbUtME2S25A67Nb34zVuCSCRIX4SseXY6zBnOFj3oLisaEqhVcJmVPAej67g==} + '@firebase/performance@0.7.1': + resolution: {integrity: sha512-SkEUurawojCjav2V2AXo6BQLDtv02NxgXPLCiAvrkn95IAKI4W/UbLKYQvMbEez/nqvmnucLyklcMlB0Q5a1iw==} peerDependencies: '@firebase/app': 0.x - dependencies: - '@firebase/app': 0.9.7 - '@firebase/component': 0.6.4 - '@firebase/installations': 0.6.4(@firebase/app@0.9.7) - '@firebase/logger': 0.4.0 - '@firebase/util': 1.9.3 - tslib: 2.5.0 - /@firebase/remote-config-compat@0.2.4(@firebase/app-compat@0.2.7)(@firebase/app@0.9.7): - resolution: {integrity: sha512-FKiki53jZirrDFkBHglB3C07j5wBpitAaj8kLME6g8Mx+aq7u9P7qfmuSRytiOItADhWUj7O1JIv7n9q87SuwA==} + '@firebase/remote-config-compat@0.2.13': + resolution: {integrity: sha512-UmHoO7TxAEJPIZf8e1Hy6CeFGMeyjqSCpgoBkQZYXFI2JHhzxIyDpr8jVKJJN1dmAePKZ5EX7dC13CmcdTOl7Q==} peerDependencies: '@firebase/app-compat': 0.x - dependencies: - '@firebase/app-compat': 0.2.7 - '@firebase/component': 0.6.4 - '@firebase/logger': 0.4.0 - '@firebase/remote-config': 0.4.4(@firebase/app@0.9.7) - '@firebase/remote-config-types': 0.3.0 - '@firebase/util': 1.9.3 - tslib: 2.5.0 - transitivePeerDependencies: - - '@firebase/app' - /@firebase/remote-config-types@0.3.0: - resolution: {integrity: sha512-RtEH4vdcbXZuZWRZbIRmQVBNsE7VDQpet2qFvq6vwKLBIQRQR5Kh58M4ok3A3US8Sr3rubYnaGqZSurCwI8uMA==} + '@firebase/remote-config-types@0.4.0': + resolution: {integrity: sha512-7p3mRE/ldCNYt8fmWMQ/MSGRmXYlJ15Rvs9Rk17t8p0WwZDbeK7eRmoI1tvCPaDzn9Oqh+yD6Lw+sGLsLg4kKg==} - /@firebase/remote-config@0.4.4(@firebase/app@0.9.7): - resolution: {integrity: sha512-x1ioTHGX8ZwDSTOVp8PBLv2/wfwKzb4pxi0gFezS5GCJwbLlloUH4YYZHHS83IPxnua8b6l0IXUaWd0RgbWwzQ==} + '@firebase/remote-config@0.6.0': + resolution: {integrity: sha512-Yrk4l5+6FJLPHC6irNHMzgTtJ3NfHXlAXVChCBdNFtgmzyGmufNs/sr8oA0auEfIJ5VpXCaThRh3P4OdQxiAlQ==} peerDependencies: '@firebase/app': 0.x - dependencies: - '@firebase/app': 0.9.7 - '@firebase/component': 0.6.4 - '@firebase/installations': 0.6.4(@firebase/app@0.9.7) - '@firebase/logger': 0.4.0 - '@firebase/util': 1.9.3 - tslib: 2.5.0 - /@firebase/storage-compat@0.3.2(@firebase/app-compat@0.2.7)(@firebase/app-types@0.9.0)(@firebase/app@0.9.7): - resolution: {integrity: sha512-wvsXlLa9DVOMQJckbDNhXKKxRNNewyUhhbXev3t8kSgoCotd1v3MmqhKKz93ePhDnhHnDs7bYHy+Qa8dRY6BXw==} + '@firebase/storage-compat@0.3.17': + resolution: {integrity: sha512-CBlODWEZ5b6MJWVh21VZioxwxNwVfPA9CAdsk+ZgVocJQQbE2oDW1XJoRcgthRY1HOitgbn4cVrM+NlQtuUYhw==} + engines: {node: '>=18.0.0'} peerDependencies: '@firebase/app-compat': 0.x - dependencies: - '@firebase/app-compat': 0.2.7 - '@firebase/component': 0.6.4 - '@firebase/storage': 0.11.2(@firebase/app@0.9.7) - '@firebase/storage-types': 0.8.0(@firebase/app-types@0.9.0)(@firebase/util@1.9.3) - '@firebase/util': 1.9.3 - tslib: 2.5.0 - transitivePeerDependencies: - - '@firebase/app' - - '@firebase/app-types' - - encoding - /@firebase/storage-types@0.8.0(@firebase/app-types@0.9.0)(@firebase/util@1.9.3): - resolution: {integrity: sha512-isRHcGrTs9kITJC0AVehHfpraWFui39MPaU7Eo8QfWlqW7YPymBmRgjDrlOgFdURh6Cdeg07zmkLP5tzTKRSpg==} + '@firebase/storage-types@0.8.3': + resolution: {integrity: sha512-+Muk7g9uwngTpd8xn9OdF/D48uiQ7I1Fae7ULsWPuKoCH3HU7bfFPhxtJYzyhjdniowhuDpQcfPmuNRAqZEfvg==} peerDependencies: '@firebase/app-types': 0.x '@firebase/util': 1.x - dependencies: - '@firebase/app-types': 0.9.0 - '@firebase/util': 1.9.3 - /@firebase/storage@0.11.2(@firebase/app@0.9.7): - resolution: {integrity: sha512-CtvoFaBI4hGXlXbaCHf8humajkbXhs39Nbh6MbNxtwJiCqxPy9iH3D3CCfXAvP0QvAAwmJUTK3+z9a++Kc4nkA==} + '@firebase/storage@0.13.7': + resolution: {integrity: sha512-FkRyc24rK+Y6EaQ1tYFm3TevBnnfSNA0VyTfew2hrYyL/aYfatBg7HOgktUdB4kWMHNA9VoTotzZTGoLuK92wg==} + engines: {node: '>=18.0.0'} peerDependencies: '@firebase/app': 0.x - dependencies: - '@firebase/app': 0.9.7 - '@firebase/component': 0.6.4 - '@firebase/util': 1.9.3 - node-fetch: 2.6.7 - tslib: 2.5.0 - transitivePeerDependencies: - - encoding - /@firebase/util@1.9.3: - resolution: {integrity: sha512-DY02CRhOZwpzO36fHpuVysz6JZrscPiBXD0fXp6qSrL9oNOx5KWICKdR95C0lSITzxp0TZosVyHqzatE8JbcjA==} - dependencies: - tslib: 2.5.0 + '@firebase/util@1.11.0': + resolution: {integrity: sha512-PzSrhIr++KI6y4P6C/IdgBNMkEx0Ex6554/cYd0Hm+ovyFSJtJXqb/3OSIdnBoa2cpwZT1/GW56EmRc5qEc5fQ==} + engines: {node: '>=18.0.0'} + + '@firebase/vertexai@1.1.0': + resolution: {integrity: sha512-K8CgIFKJrfrf5lYhKnDXOu08FEmIzVExK+ApUZx4Bw2GAmLEA3wDVrsjuupuvpXZSp8QlzvEiXwqshqqc4v0pA==} + engines: {node: '>=18.0.0'} + peerDependencies: + '@firebase/app': 0.x + '@firebase/app-types': 0.x + + '@firebase/webchannel-wrapper@1.0.3': + resolution: {integrity: sha512-2xCRM9q9FlzGZCdgDMJwc0gyUkWFtkosy7Xxr6sFgQwn+wMNIWd7xIvYNauU1r64B5L5rsGKy/n9TKJ0aAFeqQ==} - /@firebase/webchannel-wrapper@0.9.0: - resolution: {integrity: sha512-BpiZLBWdLFw+qFel9p3Zs1jD6QmH7Ii4aTDu6+vx8ShdidChZUXqDhYJly4ZjSgQh54miXbBgBrk0S+jTIh/Qg==} + '@floating-ui/core@1.7.2': + resolution: {integrity: sha512-wNB5ooIKHQc+Kui96jE/n69rHFWAVoxn5CAzL1Xdd8FG03cgY3MLO+GF9U3W737fYDSgPWA6MReKhBQBop6Pcw==} - /@fontsource/inter@4.5.15: + '@floating-ui/dom@1.7.2': + resolution: {integrity: sha512-7cfaOQuCS27HD7DX+6ib2OrnW+b4ZBwDNnCcT0uTyidcmyWb03FnQqJybDBoCnpdxwBSfA94UAYlRCt7mV+TbA==} + + '@floating-ui/react-dom@2.1.4': + resolution: {integrity: sha512-JbbpPhp38UmXDDAu60RJmbeme37Jbgsm7NrHGgzYYFKmblzRUh6Pa641dII6LsjwF4XlScDrde2UAzDo/b9KPw==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@floating-ui/utils@0.2.10': + resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} + + '@fontsource/inter@4.5.15': resolution: {integrity: sha512-FzleM9AxZQK2nqsTDtBiY0PMEVWvnKnuu2i09+p6DHvrHsuucoV2j0tmw+kAT3L4hvsLdAIDv6MdGehsPIdT+Q==} - dev: false - /@fontsource/roboto-mono@4.5.10: + '@fontsource/roboto-mono@4.5.10': resolution: {integrity: sha512-KrJdmkqz6DszT2wV/bbhXef4r0hV3B0vw2mAqei8A2kRnvq+gcJLmmIeQ94vu9VEXrUQzos5M9lH1TAAXpRphw==} - dev: false - /@gar/promisify@1.1.3: + '@gar/promisify@1.1.3': resolution: {integrity: sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==} - /@google-cloud/firestore@6.5.0: - resolution: {integrity: sha512-U0QwG6pEQxO5c0v0eUylswozmuvlvz7iXSW+I18jzqR2hAFrUq2Weu1wm3NaH8wGD4ZL7W9Be4cMHG5CYU8LuQ==} - engines: {node: '>=12.0.0'} - requiresBuild: true - dependencies: - fast-deep-equal: 3.1.3 - functional-red-black-tree: 1.0.1 - google-gax: 3.6.0 - protobufjs: 7.2.3 - transitivePeerDependencies: - - encoding - - supports-color - dev: false - optional: true + '@google-cloud/firestore@7.11.0': + resolution: {integrity: sha512-88uZ+jLsp1aVMj7gh3EKYH1aulTAMFAp8sH/v5a9w8q8iqSG27RiWLoxSAFr/XocZ9hGiWH1kEnBw+zl3xAgNA==} + engines: {node: '>=14.0.0'} - /@google-cloud/paginator@3.0.7: - resolution: {integrity: sha512-jJNutk0arIQhmpUUQJPJErsojqo834KcyB6X7a1mxuic8i1tKXxde8E69IZxNZawRIlZdIK2QY4WALvlK5MzYQ==} - engines: {node: '>=10'} - dependencies: - arrify: 2.0.1 - extend: 3.0.2 - dev: false - optional: true + '@google-cloud/paginator@5.0.2': + resolution: {integrity: sha512-DJS3s0OVH4zFDB1PzjxAsHqJT6sKVbRwwML0ZBP9PbU7Yebtu/7SWMRzvO2J3nUi9pRNITCfu4LJeooM2w4pjg==} + engines: {node: '>=14.0.0'} - /@google-cloud/projectify@3.0.0: - resolution: {integrity: sha512-HRkZsNmjScY6Li8/kb70wjGlDDyLkVk3KvoEo9uIoxSjYLJasGiCch9+PqRVDOCGUFvEIqyogl+BeqILL4OJHA==} - engines: {node: '>=12.0.0'} - dev: false - optional: true + '@google-cloud/projectify@4.0.0': + resolution: {integrity: sha512-MmaX6HeSvyPbWGwFq7mXdo0uQZLGBYCwziiLIGq5JVX+/bdI3SAq6bP98trV5eTWfLuvsMcIC1YJOF2vfteLFA==} + engines: {node: '>=14.0.0'} - /@google-cloud/promisify@3.0.1: - resolution: {integrity: sha512-z1CjRjtQyBOYL+5Qr9DdYIfrdLBe746jRTYfaYU6MeXkqp7UfYs/jX16lFFVzZ7PGEJvqZNqYUEtb1mvDww4pA==} - engines: {node: '>=12'} - dev: false - optional: true + '@google-cloud/promisify@4.0.0': + resolution: {integrity: sha512-Orxzlfb9c67A15cq2JQEyVc7wEsmFBmHjZWZYQMUyJ1qivXyMwdyNOs9odi79hze+2zqdTtu1E19IM/FtqZ10g==} + engines: {node: '>=14'} - /@google-cloud/storage@6.9.5: - resolution: {integrity: sha512-fcLsDA8YKcGuqvhk0XTjJGVpG9dzs5Em8IcUjSjspYvERuHYqMy9CMChWapSjv3Lyw//exa3mv4nUxPlV93BnA==} - engines: {node: '>=12'} - requiresBuild: true - dependencies: - '@google-cloud/paginator': 3.0.7 - '@google-cloud/projectify': 3.0.0 - '@google-cloud/promisify': 3.0.1 - abort-controller: 3.0.0 - async-retry: 1.3.3 - compressible: 2.0.18 - duplexify: 4.1.2 - ent: 2.2.0 - extend: 3.0.2 - gaxios: 5.1.0 - google-auth-library: 8.7.0 - mime: 3.0.0 - mime-types: 2.1.35 - p-limit: 3.1.0 - retry-request: 5.0.2 - teeny-request: 8.0.3 - uuid: 8.3.2 - transitivePeerDependencies: - - encoding - - supports-color - dev: false - optional: true + '@google-cloud/scheduler@4.3.0': + resolution: {integrity: sha512-3VeVu/YVkJ0NIC0zDRucmY6o4wm7ii5TGHJLKtJUz4osm8f143uJ/FDSJz1vXMAg6GseZ/yzLvBQ5r/Sb+U2Ow==} + engines: {node: '>=14.0.0'} - /@grpc/grpc-js@1.7.3: - resolution: {integrity: sha512-H9l79u4kJ2PVSxUNA08HMYAnUBLj9v6KjYQ7SQ71hOZcEXhShE/y5iQCesP8+6/Ik/7i2O0a10bPquIcYfufog==} - engines: {node: ^8.13.0 || >=10.10.0} - dependencies: - '@grpc/proto-loader': 0.7.6 - '@types/node': 18.15.11 + '@google-cloud/storage@7.15.2': + resolution: {integrity: sha512-+2k+mcQBb9zkaXMllf2wwR/rI07guAx+eZLWsGTDihW2lJRGfiqB7xu1r7/s4uvSP/T+nAumvzT5TTscwHKJ9A==} + engines: {node: '>=14'} - /@grpc/grpc-js@1.8.13: - resolution: {integrity: sha512-iY3jsdfbc0ARoCLFvbvUB8optgyb0r1XLPb142u+QtgBcKJYkCIFt3Fd/881KqjLYWjsBJF57N3b8Eop9NDfUA==} - engines: {node: ^8.13.0 || >=10.10.0} - dependencies: - '@grpc/proto-loader': 0.7.6 - '@types/node': 18.15.11 - dev: false - optional: true + '@grpc/grpc-js@1.12.2': + resolution: {integrity: sha512-bgxdZmgTrJZX50OjyVwz3+mNEnCTNkh3cIqGPWVNeW9jX6bn1ZkU80uPd+67/ZpIJIjRQ9qaHCjhavyoWYxumg==} + engines: {node: '>=12.10.0'} - /@grpc/proto-loader@0.6.13: - resolution: {integrity: sha512-FjxPYDRTn6Ec3V0arm1FtSpmP6V50wuph2yILpyvTKzjc76oDdoihXqM1DzOW5ubvCC8GivfCnNtfaRE8myJ7g==} - engines: {node: '>=6'} - hasBin: true - dependencies: - '@types/long': 4.0.2 - lodash.camelcase: 4.3.0 - long: 4.0.0 - protobufjs: 6.11.3 - yargs: 16.2.0 + '@grpc/grpc-js@1.9.15': + resolution: {integrity: sha512-nqE7Hc0AzI+euzUwDAy0aY5hCp10r734gMGRdU+qOPX0XSceI2ULrcXB5U2xSc5VkWwalCj4M7GzCAygZl2KoQ==} + engines: {node: ^8.13.0 || >=10.10.0} - /@grpc/proto-loader@0.7.6: - resolution: {integrity: sha512-QyAXR8Hyh7uMDmveWxDSUcJr9NAWaZ2I6IXgAYvQmfflwouTM+rArE2eEaCtLlRqO81j7pRLCt81IefUei6Zbw==} + '@grpc/proto-loader@0.7.13': + resolution: {integrity: sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==} engines: {node: '>=6'} hasBin: true - dependencies: - '@types/long': 4.0.2 - lodash.camelcase: 4.3.0 - long: 4.0.0 - protobufjs: 7.2.3 - yargs: 16.2.0 - /@humanwhocodes/config-array@0.10.7: - resolution: {integrity: sha512-MDl6D6sBsaV452/QSdX+4CXIjZhIcI0PELsxUjk4U828yd58vk3bTIvk/6w5FY+4hIy9sLW0sfrV7K7Kc++j/w==} - engines: {node: '>=10.10.0'} - dependencies: - '@humanwhocodes/object-schema': 1.2.1 - debug: 4.3.4 - minimatch: 3.1.2 - transitivePeerDependencies: - - supports-color - dev: true + '@hubspot/api-client@11.2.0': + resolution: {integrity: sha512-OWf/vwuOw3lMRywdyBprAl5nt3a3kRVpLcIwz6hT7uxobNOPBElFMbp3nv0dM5JZqMD78NMT7VUTArKjKhRQKg==} - /@humanwhocodes/config-array@0.11.8: - resolution: {integrity: sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==} + '@humanwhocodes/config-array@0.13.0': + resolution: {integrity: sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==} engines: {node: '>=10.10.0'} - dependencies: - '@humanwhocodes/object-schema': 1.2.1 - debug: 4.3.4 - minimatch: 3.1.2 - transitivePeerDependencies: - - supports-color - - /@humanwhocodes/gitignore-to-minimatch@1.0.2: - resolution: {integrity: sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA==} - dev: true + deprecated: Use @eslint/config-array instead - /@humanwhocodes/module-importer@1.0.1: + '@humanwhocodes/module-importer@1.0.1': resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} engines: {node: '>=12.22'} - /@humanwhocodes/object-schema@1.2.1: - resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} + '@humanwhocodes/object-schema@2.0.3': + resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} + deprecated: Use @eslint/object-schema instead - /@ioredis/commands@1.2.0: - resolution: {integrity: sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==} + '@img/sharp-darwin-arm64@0.34.1': + resolution: {integrity: sha512-pn44xgBtgpEbZsu+lWf2KNb6OAf70X68k+yk69Ic2Xz11zHR/w24/U49XT7AeRwJ0Px+mhALhU5LPci1Aymk7A==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [darwin] - /@isaacs/string-locale-compare@1.1.0: - resolution: {integrity: sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ==} + '@img/sharp-darwin-arm64@0.34.3': + resolution: {integrity: sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [darwin] - /@istanbuljs/load-nyc-config@1.1.0: - resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} - engines: {node: '>=8'} - dependencies: - camelcase: 5.3.1 - find-up: 4.1.0 - get-package-type: 0.1.0 - js-yaml: 3.14.1 - resolve-from: 5.0.0 + '@img/sharp-darwin-x64@0.34.1': + resolution: {integrity: sha512-VfuYgG2r8BpYiOUN+BfYeFo69nP/MIwAtSJ7/Zpxc5QF3KS22z8Pvg3FkrSFJBPNQ7mmcUcYQFBmEQp7eu1F8Q==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [darwin] - /@istanbuljs/schema@0.1.3: - resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} - engines: {node: '>=8'} + '@img/sharp-darwin-x64@0.34.3': + resolution: {integrity: sha512-yHpJYynROAj12TA6qil58hmPmAwxKKC7reUqtGLzsOHfP7/rniNGTL8tjWX6L3CTV4+5P4ypcS7Pp+7OB+8ihA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [darwin] - /@jest/console@27.5.1: - resolution: {integrity: sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - dependencies: - '@jest/types': 27.5.1 - '@types/node': 18.15.11 - chalk: 4.1.2 - jest-message-util: 27.5.1 - jest-util: 27.5.1 - slash: 3.0.0 - dev: false + '@img/sharp-libvips-darwin-arm64@1.1.0': + resolution: {integrity: sha512-HZ/JUmPwrJSoM4DIQPv/BfNh9yrOA8tlBbqbLz4JZ5uew2+o22Ik+tHQJcih7QJuSa0zo5coHTfD5J8inqj9DA==} + cpu: [arm64] + os: [darwin] - /@jest/console@28.1.3: - resolution: {integrity: sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dependencies: - '@jest/types': 28.1.3 - '@types/node': 18.15.11 - chalk: 4.1.2 - jest-message-util: 28.1.3 - jest-util: 28.1.3 - slash: 3.0.0 + '@img/sharp-libvips-darwin-arm64@1.2.0': + resolution: {integrity: sha512-sBZmpwmxqwlqG9ueWFXtockhsxefaV6O84BMOrhtg/YqbTaRdqDE7hxraVE3y6gVM4eExmfzW4a8el9ArLeEiQ==} + cpu: [arm64] + os: [darwin] - /@jest/console@29.5.0: - resolution: {integrity: sha512-NEpkObxPwyw/XxZVLPmAGKE89IQRp4puc6IQRPru6JKd1M3fW9v1xM1AnzIJE65hbCkzQAdnL8P47e9hzhiYLQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.5.0 - '@types/node': 18.15.11 - chalk: 4.1.2 - jest-message-util: 29.5.0 - jest-util: 29.5.0 - slash: 3.0.0 - dev: true + '@img/sharp-libvips-darwin-x64@1.1.0': + resolution: {integrity: sha512-Xzc2ToEmHN+hfvsl9wja0RlnXEgpKNmftriQp6XzY/RaSfwD9th+MSh0WQKzUreLKKINb3afirxW7A0fz2YWuQ==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.2.0': + resolution: {integrity: sha512-M64XVuL94OgiNHa5/m2YvEQI5q2cl9d/wk0qFTDVXcYzi43lxuiFTftMR1tOnFQovVXNZJ5TURSDK2pNe9Yzqg==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.1.0': + resolution: {integrity: sha512-IVfGJa7gjChDET1dK9SekxFFdflarnUB8PwW8aGwEoF3oAsSDuNUTYS+SKDOyOJxQyDC1aPFMuRYLoDInyV9Ew==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linux-arm64@1.2.0': + resolution: {integrity: sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linux-arm@1.1.0': + resolution: {integrity: sha512-s8BAd0lwUIvYCJyRdFqvsj+BJIpDBSxs6ivrOPm/R7piTs5UIwY5OjXrP2bqXC9/moGsyRa37eYWYCOGVXxVrA==} + cpu: [arm] + os: [linux] + + '@img/sharp-libvips-linux-arm@1.2.0': + resolution: {integrity: sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw==} + cpu: [arm] + os: [linux] + + '@img/sharp-libvips-linux-ppc64@1.1.0': + resolution: {integrity: sha512-tiXxFZFbhnkWE2LA8oQj7KYR+bWBkiV2nilRldT7bqoEZ4HiDOcePr9wVDAZPi/Id5fT1oY9iGnDq20cwUz8lQ==} + cpu: [ppc64] + os: [linux] + + '@img/sharp-libvips-linux-ppc64@1.2.0': + resolution: {integrity: sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ==} + cpu: [ppc64] + os: [linux] + + '@img/sharp-libvips-linux-s390x@1.1.0': + resolution: {integrity: sha512-xukSwvhguw7COyzvmjydRb3x/09+21HykyapcZchiCUkTThEQEOMtBj9UhkaBRLuBrgLFzQ2wbxdeCCJW/jgJA==} + cpu: [s390x] + os: [linux] + + '@img/sharp-libvips-linux-s390x@1.2.0': + resolution: {integrity: sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw==} + cpu: [s390x] + os: [linux] + + '@img/sharp-libvips-linux-x64@1.1.0': + resolution: {integrity: sha512-yRj2+reB8iMg9W5sULM3S74jVS7zqSzHG3Ol/twnAAkAhnGQnpjj6e4ayUz7V+FpKypwgs82xbRdYtchTTUB+Q==} + cpu: [x64] + os: [linux] + + '@img/sharp-libvips-linux-x64@1.2.0': + resolution: {integrity: sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg==} + cpu: [x64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-arm64@1.1.0': + resolution: {integrity: sha512-jYZdG+whg0MDK+q2COKbYidaqW/WTz0cc1E+tMAusiDygrM4ypmSCjOJPmFTvHHJ8j/6cAGyeDWZOsK06tP33w==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-arm64@1.2.0': + resolution: {integrity: sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-x64@1.1.0': + resolution: {integrity: sha512-wK7SBdwrAiycjXdkPnGCPLjYb9lD4l6Ze2gSdAGVZrEL05AOUJESWU2lhlC+Ffn5/G+VKuSm6zzbQSzFX/P65A==} + cpu: [x64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-x64@1.2.0': + resolution: {integrity: sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q==} + cpu: [x64] + os: [linux] + + '@img/sharp-linux-arm64@0.34.1': + resolution: {integrity: sha512-kX2c+vbvaXC6vly1RDf/IWNXxrlxLNpBVWkdpRq5Ka7OOKj6nr66etKy2IENf6FtOgklkg9ZdGpEu9kwdlcwOQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linux-arm64@0.34.3': + resolution: {integrity: sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linux-arm@0.34.1': + resolution: {integrity: sha512-anKiszvACti2sGy9CirTlNyk7BjjZPiML1jt2ZkTdcvpLU1YH6CXwRAZCA2UmRXnhiIftXQ7+Oh62Ji25W72jA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm] + os: [linux] + + '@img/sharp-linux-arm@0.34.3': + resolution: {integrity: sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm] + os: [linux] + + '@img/sharp-linux-ppc64@0.34.3': + resolution: {integrity: sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ppc64] + os: [linux] + + '@img/sharp-linux-s390x@0.34.1': + resolution: {integrity: sha512-7s0KX2tI9mZI2buRipKIw2X1ufdTeaRgwmRabt5bi9chYfhur+/C1OXg3TKg/eag1W+6CCWLVmSauV1owmRPxA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [s390x] + os: [linux] + + '@img/sharp-linux-s390x@0.34.3': + resolution: {integrity: sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [s390x] + os: [linux] + + '@img/sharp-linux-x64@0.34.1': + resolution: {integrity: sha512-wExv7SH9nmoBW3Wr2gvQopX1k8q2g5V5Iag8Zk6AVENsjwd+3adjwxtp3Dcu2QhOXr8W9NusBU6XcQUohBZ5MA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-linux-x64@0.34.3': + resolution: {integrity: sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-linuxmusl-arm64@0.34.1': + resolution: {integrity: sha512-DfvyxzHxw4WGdPiTF0SOHnm11Xv4aQexvqhRDAoD00MzHekAj9a/jADXeXYCDFH/DzYruwHbXU7uz+H+nWmSOQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linuxmusl-arm64@0.34.3': + resolution: {integrity: sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linuxmusl-x64@0.34.1': + resolution: {integrity: sha512-pax/kTR407vNb9qaSIiWVnQplPcGU8LRIJpDT5o8PdAx5aAA7AS3X9PS8Isw1/WfqgQorPotjrZL3Pqh6C5EBg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-linuxmusl-x64@0.34.3': + resolution: {integrity: sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-wasm32@0.34.1': + resolution: {integrity: sha512-YDybQnYrLQfEpzGOQe7OKcyLUCML4YOXl428gOOzBgN6Gw0rv8dpsJ7PqTHxBnXnwXr8S1mYFSLSa727tpz0xg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [wasm32] + + '@img/sharp-wasm32@0.34.3': + resolution: {integrity: sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [wasm32] + + '@img/sharp-win32-arm64@0.34.3': + resolution: {integrity: sha512-MjnHPnbqMXNC2UgeLJtX4XqoVHHlZNd+nPt1kRPmj63wURegwBhZlApELdtxM2OIZDRv/DFtLcNhVbd1z8GYXQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [win32] + + '@img/sharp-win32-ia32@0.34.1': + resolution: {integrity: sha512-WKf/NAZITnonBf3U1LfdjoMgNO5JYRSlhovhRhMxXVdvWYveM4kM3L8m35onYIdh75cOMCo1BexgVQcCDzyoWw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-ia32@0.34.3': + resolution: {integrity: sha512-xuCdhH44WxuXgOM714hn4amodJMZl3OEvf0GVTm0BEyMeA2to+8HEdRPShH0SLYptJY1uBw+SCFP9WVQi1Q/cw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-x64@0.34.1': + resolution: {integrity: sha512-hw1iIAHpNE8q3uMIRCgGOeDoz9KtFNarFLQclLxr/LK1VBkj8nby18RjFvr6aP7USRYAjTZW6yisnBWMX571Tw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [win32] + + '@img/sharp-win32-x64@0.34.3': + resolution: {integrity: sha512-OWwz05d++TxzLEv4VnsTz5CmZ6mI6S05sfQGEMrNrQcOEERbX46332IvE7pO/EUiw7jUrrS40z/M7kPyjfl04g==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [win32] + + '@inquirer/figures@1.0.7': + resolution: {integrity: sha512-m+Trk77mp54Zma6xLkLuY+mvanPxlE4A7yNKs2HBiyZ4UkVs28Mv5c/pgWrHeInx+USHeX/WEPzjrWrcJiQgjw==} + engines: {node: '>=18'} + + '@ioredis/commands@1.2.0': + resolution: {integrity: sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==} + + '@isaacs/balanced-match@4.0.1': + resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==} + engines: {node: 20 || >=22} + + '@isaacs/brace-expansion@5.0.0': + resolution: {integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==} + engines: {node: 20 || >=22} + + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + + '@isaacs/fs-minipass@4.0.1': + resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} + engines: {node: '>=18.0.0'} + + '@isaacs/string-locale-compare@1.1.0': + resolution: {integrity: sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ==} + + '@isaacs/ttlcache@1.4.1': + resolution: {integrity: sha512-RQgQ4uQ+pLbqXfOmieB91ejmLwvSgv9nLx6sT6sD83s7umBypgg+OIBOBbEUiJXrfpnp9j0mRhYYdzp9uqq3lA==} + engines: {node: '>=12'} + + '@istanbuljs/load-nyc-config@1.1.0': + resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} + engines: {node: '>=8'} + + '@istanbuljs/schema@0.1.3': + resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} + engines: {node: '>=8'} + + '@jest/console@27.5.1': + resolution: {integrity: sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + '@jest/console@28.1.3': + resolution: {integrity: sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + + '@jest/console@29.7.0': + resolution: {integrity: sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - /@jest/core@27.5.1(ts-node@10.8.2): + '@jest/core@27.5.1': resolution: {integrity: sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} peerDependencies: @@ -4461,44 +3528,8 @@ packages: peerDependenciesMeta: node-notifier: optional: true - dependencies: - '@jest/console': 27.5.1 - '@jest/reporters': 27.5.1 - '@jest/test-result': 27.5.1 - '@jest/transform': 27.5.1 - '@jest/types': 27.5.1 - '@types/node': 18.15.11 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - emittery: 0.8.1 - exit: 0.1.2 - graceful-fs: 4.2.11 - jest-changed-files: 27.5.1 - jest-config: 27.5.1(ts-node@10.8.2) - jest-haste-map: 27.5.1 - jest-message-util: 27.5.1 - jest-regex-util: 27.5.1 - jest-resolve: 27.5.1 - jest-resolve-dependencies: 27.5.1 - jest-runner: 27.5.1 - jest-runtime: 27.5.1 - jest-snapshot: 27.5.1 - jest-util: 27.5.1 - jest-validate: 27.5.1 - jest-watcher: 27.5.1 - micromatch: 4.0.5 - rimraf: 3.0.2 - slash: 3.0.0 - strip-ansi: 6.0.1 - transitivePeerDependencies: - - bufferutil - - canvas - - supports-color - - ts-node - - utf-8-validate - dev: false - /@jest/core@28.1.3(ts-node@10.8.2): + '@jest/core@28.1.3': resolution: {integrity: sha512-CIKBrlaKOzA7YG19BEqCw3SLIsEwjZkeJzf5bdooVnW4bH5cktqe3JX+G2YV1aK5vP8N9na1IGWFzYaTp6k6NA==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} peerDependencies: @@ -4506,216 +3537,69 @@ packages: peerDependenciesMeta: node-notifier: optional: true - dependencies: - '@jest/console': 28.1.3 - '@jest/reporters': 28.1.3 - '@jest/test-result': 28.1.3 - '@jest/transform': 28.1.3 - '@jest/types': 28.1.3 - '@types/node': 18.15.11 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - ci-info: 3.8.0 - exit: 0.1.2 - graceful-fs: 4.2.11 - jest-changed-files: 28.1.3 - jest-config: 28.1.3(@types/node@18.15.11)(ts-node@10.8.2) - jest-haste-map: 28.1.3 - jest-message-util: 28.1.3 - jest-regex-util: 28.0.2 - jest-resolve: 28.1.3 - jest-resolve-dependencies: 28.1.3 - jest-runner: 28.1.3 - jest-runtime: 28.1.3 - jest-snapshot: 28.1.3 - jest-util: 28.1.3 - jest-validate: 28.1.3 - jest-watcher: 28.1.3 - micromatch: 4.0.5 - pretty-format: 28.1.3 - rimraf: 3.0.2 - slash: 3.0.0 - strip-ansi: 6.0.1 - transitivePeerDependencies: - - supports-color - - ts-node - dev: true - /@jest/core@29.5.0(ts-node@10.8.2): - resolution: {integrity: sha512-28UzQc7ulUrOQw1IsN/kv1QES3q2kkbl/wGslyhAclqZ/8cMdB5M68BffkIdSJgKBUt50d3hbwJ92XESlE7LiQ==} + '@jest/core@29.7.0': + resolution: {integrity: sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 peerDependenciesMeta: node-notifier: optional: true - dependencies: - '@jest/console': 29.5.0 - '@jest/reporters': 29.5.0 - '@jest/test-result': 29.5.0 - '@jest/transform': 29.5.0 - '@jest/types': 29.5.0 - '@types/node': 18.15.11 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - ci-info: 3.8.0 - exit: 0.1.2 - graceful-fs: 4.2.11 - jest-changed-files: 29.5.0 - jest-config: 29.5.0(@types/node@18.15.11)(ts-node@10.8.2) - jest-haste-map: 29.5.0 - jest-message-util: 29.5.0 - jest-regex-util: 29.4.3 - jest-resolve: 29.5.0 - jest-resolve-dependencies: 29.5.0 - jest-runner: 29.5.0 - jest-runtime: 29.5.0 - jest-snapshot: 29.5.0 - jest-util: 29.5.0 - jest-validate: 29.5.0 - jest-watcher: 29.5.0 - micromatch: 4.0.5 - pretty-format: 29.5.0 - slash: 3.0.0 - strip-ansi: 6.0.1 - transitivePeerDependencies: - - supports-color - - ts-node - dev: true - /@jest/environment@27.5.1: + '@jest/environment@27.5.1': resolution: {integrity: sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - dependencies: - '@jest/fake-timers': 27.5.1 - '@jest/types': 27.5.1 - '@types/node': 18.15.11 - jest-mock: 27.5.1 - dev: false - /@jest/environment@28.1.3: + '@jest/environment@28.1.3': resolution: {integrity: sha512-1bf40cMFTEkKyEf585R9Iz1WayDjHoHqvts0XFYEqyKM3cFWDpeMoqKKTAF9LSYQModPUlh8FKptoM2YcMWAXA==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dependencies: - '@jest/fake-timers': 28.1.3 - '@jest/types': 28.1.3 - '@types/node': 18.15.11 - jest-mock: 28.1.3 - dev: true - /@jest/environment@29.5.0: - resolution: {integrity: sha512-5FXw2+wD29YU1d4I2htpRX7jYnAyTRjP2CsXQdo9SAM8g3ifxWPSV0HnClSn71xwctr0U3oZIIH+dtbfmnbXVQ==} + '@jest/environment@29.7.0': + resolution: {integrity: sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/fake-timers': 29.5.0 - '@jest/types': 29.5.0 - '@types/node': 18.15.11 - jest-mock: 29.5.0 - dev: true - /@jest/expect-utils@28.1.3: + '@jest/expect-utils@28.1.3': resolution: {integrity: sha512-wvbi9LUrHJLn3NlDW6wF2hvIMtd4JUl2QNVrjq+IBSHirgfrR3o9RnVtxzdEGO2n9JyIWwHnLfby5KzqBGg2YA==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dependencies: - jest-get-type: 28.0.2 - dev: true - /@jest/expect-utils@29.5.0: - resolution: {integrity: sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg==} + '@jest/expect-utils@29.7.0': + resolution: {integrity: sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - jest-get-type: 29.4.3 - dev: true - /@jest/expect@28.1.3: + '@jest/expect@28.1.3': resolution: {integrity: sha512-lzc8CpUbSoE4dqT0U+g1qODQjBRHPpCPXissXD4mS9+sWQdmmpeJ9zSH1rS1HEkrsMN0fb7nKrJ9giAR1d3wBw==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dependencies: - expect: 28.1.3 - jest-snapshot: 28.1.3 - transitivePeerDependencies: - - supports-color - dev: true - /@jest/expect@29.5.0: - resolution: {integrity: sha512-PueDR2HGihN3ciUNGr4uelropW7rqUfTiOn+8u0leg/42UhblPxHkfoh0Ruu3I9Y1962P3u2DY4+h7GVTSVU6g==} + '@jest/expect@29.7.0': + resolution: {integrity: sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - expect: 29.5.0 - jest-snapshot: 29.5.0 - transitivePeerDependencies: - - supports-color - dev: true - /@jest/fake-timers@27.5.1: + '@jest/fake-timers@27.5.1': resolution: {integrity: sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - dependencies: - '@jest/types': 27.5.1 - '@sinonjs/fake-timers': 8.1.0 - '@types/node': 18.15.11 - jest-message-util: 27.5.1 - jest-mock: 27.5.1 - jest-util: 27.5.1 - dev: false - /@jest/fake-timers@28.1.3: + '@jest/fake-timers@28.1.3': resolution: {integrity: sha512-D/wOkL2POHv52h+ok5Oj/1gOG9HSywdoPtFsRCUmlCILXNn5eIWmcnd3DIiWlJnpGvQtmajqBP95Ei0EimxfLw==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dependencies: - '@jest/types': 28.1.3 - '@sinonjs/fake-timers': 9.1.2 - '@types/node': 18.15.11 - jest-message-util: 28.1.3 - jest-mock: 28.1.3 - jest-util: 28.1.3 - dev: true - /@jest/fake-timers@29.5.0: - resolution: {integrity: sha512-9ARvuAAQcBwDAqOnglWq2zwNIRUDtk/SCkp/ToGEhFv5r86K21l+VEs0qNTaXtyiY0lEePl3kylijSYJQqdbDg==} + '@jest/fake-timers@29.7.0': + resolution: {integrity: sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.5.0 - '@sinonjs/fake-timers': 10.0.2 - '@types/node': 18.15.11 - jest-message-util: 29.5.0 - jest-mock: 29.5.0 - jest-util: 29.5.0 - dev: true - /@jest/globals@27.5.1: + '@jest/globals@27.5.1': resolution: {integrity: sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - dependencies: - '@jest/environment': 27.5.1 - '@jest/types': 27.5.1 - expect: 27.5.1 - dev: false - /@jest/globals@28.1.3: + '@jest/globals@28.1.3': resolution: {integrity: sha512-XFU4P4phyryCXu1pbcqMO0GSQcYe1IsalYCDzRNyhetyeyxMcIxa11qPNDpVNLeretItNqEmYYQn1UYz/5x1NA==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dependencies: - '@jest/environment': 28.1.3 - '@jest/expect': 28.1.3 - '@jest/types': 28.1.3 - transitivePeerDependencies: - - supports-color - dev: true - /@jest/globals@29.5.0: - resolution: {integrity: sha512-S02y0qMWGihdzNbUiqSAiKSpSozSuHX5UYc7QbnHP+D9Lyw8DgGGCinrN9uSuHPeKgSSzvPom2q1nAtBvUsvPQ==} + '@jest/globals@29.7.0': + resolution: {integrity: sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/environment': 29.5.0 - '@jest/expect': 29.5.0 - '@jest/types': 29.5.0 - jest-mock: 29.5.0 - transitivePeerDependencies: - - supports-color - dev: true - /@jest/reporters@27.5.1: + '@jest/reporters@27.5.1': resolution: {integrity: sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} peerDependencies: @@ -4723,37 +3607,8 @@ packages: peerDependenciesMeta: node-notifier: optional: true - dependencies: - '@bcoe/v8-coverage': 0.2.3 - '@jest/console': 27.5.1 - '@jest/test-result': 27.5.1 - '@jest/transform': 27.5.1 - '@jest/types': 27.5.1 - '@types/node': 18.15.11 - chalk: 4.1.2 - collect-v8-coverage: 1.0.1 - exit: 0.1.2 - glob: 7.2.3 - graceful-fs: 4.2.11 - istanbul-lib-coverage: 3.2.0 - istanbul-lib-instrument: 5.2.1 - istanbul-lib-report: 3.0.0 - istanbul-lib-source-maps: 4.0.1 - istanbul-reports: 3.1.5 - jest-haste-map: 27.5.1 - jest-resolve: 27.5.1 - jest-util: 27.5.1 - jest-worker: 27.5.1 - slash: 3.0.0 - source-map: 0.6.1 - string-length: 4.0.2 - terminal-link: 2.1.1 - v8-to-istanbul: 8.1.1 - transitivePeerDependencies: - - supports-color - dev: false - /@jest/reporters@28.1.3: + '@jest/reporters@28.1.3': resolution: {integrity: sha512-JuAy7wkxQZVNU/V6g9xKzCGC5LVXx9FDcABKsSXp5MiKPEE2144a/vXTEDoyzjUpZKfVwp08Wqg5A4WfTMAzjg==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} peerDependencies: @@ -4761,926 +3616,410 @@ packages: peerDependenciesMeta: node-notifier: optional: true - dependencies: - '@bcoe/v8-coverage': 0.2.3 - '@jest/console': 28.1.3 - '@jest/test-result': 28.1.3 - '@jest/transform': 28.1.3 - '@jest/types': 28.1.3 - '@jridgewell/trace-mapping': 0.3.18 - '@types/node': 18.15.11 - chalk: 4.1.2 - collect-v8-coverage: 1.0.1 - exit: 0.1.2 - glob: 7.2.3 - graceful-fs: 4.2.11 - istanbul-lib-coverage: 3.2.0 - istanbul-lib-instrument: 5.2.1 - istanbul-lib-report: 3.0.0 - istanbul-lib-source-maps: 4.0.1 - istanbul-reports: 3.1.5 - jest-message-util: 28.1.3 - jest-util: 28.1.3 - jest-worker: 28.1.3 - slash: 3.0.0 - string-length: 4.0.2 - strip-ansi: 6.0.1 - terminal-link: 2.1.1 - v8-to-istanbul: 9.1.0 - transitivePeerDependencies: - - supports-color - dev: true - /@jest/reporters@29.5.0: - resolution: {integrity: sha512-D05STXqj/M8bP9hQNSICtPqz97u7ffGzZu+9XLucXhkOFBqKcXe04JLZOgIekOxdb73MAoBUFnqvf7MCpKk5OA==} + '@jest/reporters@29.7.0': + resolution: {integrity: sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 peerDependenciesMeta: node-notifier: optional: true - dependencies: - '@bcoe/v8-coverage': 0.2.3 - '@jest/console': 29.5.0 - '@jest/test-result': 29.5.0 - '@jest/transform': 29.5.0 - '@jest/types': 29.5.0 - '@jridgewell/trace-mapping': 0.3.18 - '@types/node': 18.15.11 - chalk: 4.1.2 - collect-v8-coverage: 1.0.1 - exit: 0.1.2 - glob: 7.2.3 - graceful-fs: 4.2.11 - istanbul-lib-coverage: 3.2.0 - istanbul-lib-instrument: 5.2.1 - istanbul-lib-report: 3.0.0 - istanbul-lib-source-maps: 4.0.1 - istanbul-reports: 3.1.5 - jest-message-util: 29.5.0 - jest-util: 29.5.0 - jest-worker: 29.5.0 - slash: 3.0.0 - string-length: 4.0.2 - strip-ansi: 6.0.1 - v8-to-istanbul: 9.1.0 - transitivePeerDependencies: - - supports-color - dev: true - /@jest/schemas@28.1.3: + '@jest/schemas@28.1.3': resolution: {integrity: sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dependencies: - '@sinclair/typebox': 0.24.51 - /@jest/schemas@29.4.3: - resolution: {integrity: sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==} + '@jest/schemas@29.6.3': + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@sinclair/typebox': 0.25.24 - dev: true - /@jest/source-map@27.5.1: + '@jest/source-map@27.5.1': resolution: {integrity: sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - dependencies: - callsites: 3.1.0 - graceful-fs: 4.2.11 - source-map: 0.6.1 - dev: false - /@jest/source-map@28.1.2: + '@jest/source-map@28.1.2': resolution: {integrity: sha512-cV8Lx3BeStJb8ipPHnqVw/IM2VCMWO3crWZzYodSIkxXnRcXJipCdx1JCK0K5MsJJouZQTH73mzf4vgxRaH9ww==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dependencies: - '@jridgewell/trace-mapping': 0.3.18 - callsites: 3.1.0 - graceful-fs: 4.2.11 - dev: true - /@jest/source-map@29.4.3: - resolution: {integrity: sha512-qyt/mb6rLyd9j1jUts4EQncvS6Yy3PM9HghnNv86QBlV+zdL2inCdK1tuVlL+J+lpiw2BI67qXOrX3UurBqQ1w==} + '@jest/source-map@29.6.3': + resolution: {integrity: sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jridgewell/trace-mapping': 0.3.18 - callsites: 3.1.0 - graceful-fs: 4.2.11 - dev: true - /@jest/test-result@27.5.1: + '@jest/test-result@27.5.1': resolution: {integrity: sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - dependencies: - '@jest/console': 27.5.1 - '@jest/types': 27.5.1 - '@types/istanbul-lib-coverage': 2.0.4 - collect-v8-coverage: 1.0.1 - dev: false - /@jest/test-result@28.1.3: + '@jest/test-result@28.1.3': resolution: {integrity: sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dependencies: - '@jest/console': 28.1.3 - '@jest/types': 28.1.3 - '@types/istanbul-lib-coverage': 2.0.4 - collect-v8-coverage: 1.0.1 - /@jest/test-result@29.5.0: - resolution: {integrity: sha512-fGl4rfitnbfLsrfx1uUpDEESS7zM8JdgZgOCQuxQvL1Sn/I6ijeAVQWGfXI9zb1i9Mzo495cIpVZhA0yr60PkQ==} + '@jest/test-result@29.7.0': + resolution: {integrity: sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/console': 29.5.0 - '@jest/types': 29.5.0 - '@types/istanbul-lib-coverage': 2.0.4 - collect-v8-coverage: 1.0.1 - dev: true - /@jest/test-sequencer@27.5.1: + '@jest/test-sequencer@27.5.1': resolution: {integrity: sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - dependencies: - '@jest/test-result': 27.5.1 - graceful-fs: 4.2.11 - jest-haste-map: 27.5.1 - jest-runtime: 27.5.1 - transitivePeerDependencies: - - supports-color - dev: false - /@jest/test-sequencer@28.1.3: + '@jest/test-sequencer@28.1.3': resolution: {integrity: sha512-NIMPEqqa59MWnDi1kvXXpYbqsfQmSJsIbnd85mdVGkiDfQ9WQQTXOLsvISUfonmnBT+w85WEgneCigEEdHDFxw==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dependencies: - '@jest/test-result': 28.1.3 - graceful-fs: 4.2.11 - jest-haste-map: 28.1.3 - slash: 3.0.0 - dev: true - /@jest/test-sequencer@29.5.0: - resolution: {integrity: sha512-yPafQEcKjkSfDXyvtgiV4pevSeyuA6MQr6ZIdVkWJly9vkqjnFfcfhRQqpD5whjoU8EORki752xQmjaqoFjzMQ==} + '@jest/test-sequencer@29.7.0': + resolution: {integrity: sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/test-result': 29.5.0 - graceful-fs: 4.2.11 - jest-haste-map: 29.5.0 - slash: 3.0.0 - dev: true - /@jest/transform@27.5.1: + '@jest/transform@27.5.1': resolution: {integrity: sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - dependencies: - '@babel/core': 7.21.4 - '@jest/types': 27.5.1 - babel-plugin-istanbul: 6.1.1 - chalk: 4.1.2 - convert-source-map: 1.9.0 - fast-json-stable-stringify: 2.1.0 - graceful-fs: 4.2.11 - jest-haste-map: 27.5.1 - jest-regex-util: 27.5.1 - jest-util: 27.5.1 - micromatch: 4.0.5 - pirates: 4.0.5 - slash: 3.0.0 - source-map: 0.6.1 - write-file-atomic: 3.0.3 - transitivePeerDependencies: - - supports-color - dev: false - /@jest/transform@28.1.3: + '@jest/transform@28.1.3': resolution: {integrity: sha512-u5dT5di+oFI6hfcLOHGTAfmUxFRrjK+vnaP0kkVow9Md/M7V/MxqQMOz/VV25UZO8pzeA9PjfTpOu6BDuwSPQA==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dependencies: - '@babel/core': 7.21.4 - '@jest/types': 28.1.3 - '@jridgewell/trace-mapping': 0.3.18 - babel-plugin-istanbul: 6.1.1 - chalk: 4.1.2 - convert-source-map: 1.9.0 - fast-json-stable-stringify: 2.1.0 - graceful-fs: 4.2.11 - jest-haste-map: 28.1.3 - jest-regex-util: 28.0.2 - jest-util: 28.1.3 - micromatch: 4.0.5 - pirates: 4.0.5 - slash: 3.0.0 - write-file-atomic: 4.0.2 - transitivePeerDependencies: - - supports-color - dev: true - /@jest/transform@29.5.0: - resolution: {integrity: sha512-8vbeZWqLJOvHaDfeMuoHITGKSz5qWc9u04lnWrQE3VyuSw604PzQM824ZeX9XSjUCeDiE3GuxZe5UKa8J61NQw==} + '@jest/transform@29.7.0': + resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@babel/core': 7.21.4 - '@jest/types': 29.5.0 - '@jridgewell/trace-mapping': 0.3.18 - babel-plugin-istanbul: 6.1.1 - chalk: 4.1.2 - convert-source-map: 2.0.0 - fast-json-stable-stringify: 2.1.0 - graceful-fs: 4.2.11 - jest-haste-map: 29.5.0 - jest-regex-util: 29.4.3 - jest-util: 29.5.0 - micromatch: 4.0.5 - pirates: 4.0.5 - slash: 3.0.0 - write-file-atomic: 4.0.2 - transitivePeerDependencies: - - supports-color - dev: true - /@jest/types@27.5.1: + '@jest/types@27.5.1': resolution: {integrity: sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - dependencies: - '@types/istanbul-lib-coverage': 2.0.4 - '@types/istanbul-reports': 3.0.1 - '@types/node': 18.15.11 - '@types/yargs': 16.0.5 - chalk: 4.1.2 - dev: false - /@jest/types@28.1.3: + '@jest/types@28.1.3': resolution: {integrity: sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dependencies: - '@jest/schemas': 28.1.3 - '@types/istanbul-lib-coverage': 2.0.4 - '@types/istanbul-reports': 3.0.1 - '@types/node': 18.15.11 - '@types/yargs': 17.0.24 - chalk: 4.1.2 - /@jest/types@29.5.0: - resolution: {integrity: sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==} + '@jest/types@29.6.3': + resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/schemas': 29.4.3 - '@types/istanbul-lib-coverage': 2.0.4 - '@types/istanbul-reports': 3.0.1 - '@types/node': 18.15.11 - '@types/yargs': 17.0.24 - chalk: 4.1.2 - dev: true - /@jitsu/sdk-js@3.1.5: + '@jitsu/sdk-js@3.1.5': resolution: {integrity: sha512-e7P8uvGBNwWCC864MItcYSv0IT2+nwYiX8QXZ0lfyG8hutEXXX9Yt2/+MynXuGxMIoIrJ+j0bWVu20k9rrhxSg==} - dev: true + deprecated: This package is for legacy Jitsu Classic version. For latest version of Jitsu please use @jitsu/js - /@jridgewell/gen-mapping@0.3.3: - resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} - engines: {node: '>=6.0.0'} - dependencies: - '@jridgewell/set-array': 1.1.2 - '@jridgewell/sourcemap-codec': 1.4.15 - '@jridgewell/trace-mapping': 0.3.18 + '@jridgewell/gen-mapping@0.3.12': + resolution: {integrity: sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==} - /@jridgewell/resolve-uri@3.1.0: - resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} + '@jridgewell/gen-mapping@0.3.5': + resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} engines: {node: '>=6.0.0'} - /@jridgewell/resolve-uri@3.1.1: - resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} - /@jridgewell/set-array@1.1.2: - resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} + '@jridgewell/set-array@1.2.1': + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} engines: {node: '>=6.0.0'} - /@jridgewell/source-map@0.3.3: - resolution: {integrity: sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==} - dependencies: - '@jridgewell/gen-mapping': 0.3.3 - '@jridgewell/trace-mapping': 0.3.18 + '@jridgewell/source-map@0.3.6': + resolution: {integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==} - /@jridgewell/sourcemap-codec@1.4.14: - resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} - /@jridgewell/sourcemap-codec@1.4.15: - resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + '@jridgewell/trace-mapping@0.3.25': + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} - /@jridgewell/trace-mapping@0.3.18: - resolution: {integrity: sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==} - dependencies: - '@jridgewell/resolve-uri': 3.1.0 - '@jridgewell/sourcemap-codec': 1.4.14 + '@jridgewell/trace-mapping@0.3.29': + resolution: {integrity: sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==} - /@jridgewell/trace-mapping@0.3.9: + '@jridgewell/trace-mapping@0.3.9': resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} - dependencies: - '@jridgewell/resolve-uri': 3.1.1 - '@jridgewell/sourcemap-codec': 1.4.15 - /@jsdoc/salty@0.2.5: - resolution: {integrity: sha512-TfRP53RqunNe2HBobVBJ0VLhK1HbfvBYeTC1ahnN64PWvyYyGebmMiPkuwvD9fpw2ZbkoPb8Q7mwy0aR8Z9rvw==} - engines: {node: '>=v12.0.0'} - dependencies: - lodash: 4.17.21 - dev: false - optional: true + '@js-sdsl/ordered-map@4.4.2': + resolution: {integrity: sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==} + + '@kurkle/color@0.3.2': + resolution: {integrity: sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==} + + '@kwsites/file-exists@1.1.1': + resolution: {integrity: sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==} - /@leichtgewicht/ip-codec@2.0.4: - resolution: {integrity: sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==} - dev: false + '@kwsites/promise-deferred@1.1.1': + resolution: {integrity: sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==} - /@loadable/component@5.15.3(react@18.2.0): - resolution: {integrity: sha512-VOgYgCABn6+/7aGIpg7m0Ruj34tGetaJzt4bQ345FwEovDQZ+dua+NWLmuJKv8rWZyxOUSfoJkmGnzyDXH2BAQ==} + '@leichtgewicht/ip-codec@2.0.5': + resolution: {integrity: sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==} + + '@loadable/component@5.16.4': + resolution: {integrity: sha512-fJWxx9b5WHX90QKmizo9B+es2so8DnBthI1mbflwCoOyvzEwxiZ/SVDCTtXEnHG72/kGBdzr297SSIekYtzSOQ==} engines: {node: '>=8'} peerDependencies: react: ^16.3.0 || ^17.0.0 || ^18.0.0 - dependencies: - '@babel/runtime': 7.21.0 - hoist-non-react-statics: 3.3.2 - react: 18.2.0 - react-is: 16.13.1 - dev: false - /@lukeed/csprng@1.1.0: + '@lottiefiles/dotlottie-react@0.13.3': + resolution: {integrity: sha512-V4FfdYlqzjBUX7f0KV6vfQOOI0Cp+3XeG/ZqSDFSEVg5P7fpROpDv5/I9aTM8sOCESK1SWT96Fem+QVUnBV1wQ==} + peerDependencies: + react: ^17 || ^18 || ^19 + + '@lottiefiles/dotlottie-web@0.42.0': + resolution: {integrity: sha512-Zr2LCaOAoPCsdAQgeLyCSiQ1+xrAJtRCyuEYDj0qR5heUwpc+Pxbb88JyTVumcXFfKOBMOMmrlsTScLz2mrvQQ==} + + '@lukeed/csprng@1.1.0': resolution: {integrity: sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==} engines: {node: '>=8'} - dev: true - /@lukeed/uuid@2.0.1: + '@lukeed/uuid@2.0.1': resolution: {integrity: sha512-qC72D4+CDdjGqJvkFMMEAtancHUQ7/d/tAiHf64z8MopFDmcrtbcJuerDtFceuAfQJ2pDSfCKCtbqoGBNnwg0w==} engines: {node: '>=8'} - dependencies: - '@lukeed/csprng': 1.1.0 - dev: true - /@mapbox/node-pre-gyp@1.0.10: - resolution: {integrity: sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA==} + '@mapbox/node-pre-gyp@1.0.11': + resolution: {integrity: sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==} hasBin: true - dependencies: - detect-libc: 2.0.1 - https-proxy-agent: 5.0.1 - make-dir: 3.1.0 - node-fetch: 2.6.9 - nopt: 5.0.0 - npmlog: 5.0.1 - rimraf: 3.0.2 - semver: 7.5.0 - tar: 6.1.13 - transitivePeerDependencies: - - encoding - - supports-color - dev: true - /@monaco-editor/loader@1.3.3(monaco-editor@0.37.1): - resolution: {integrity: sha512-6KKF4CTzcJiS8BJwtxtfyYt9shBiEv32ateQ9T4UVogwn4HM/uPo9iJd2Dmbkpz8CM6Y0PDUpjnZzCwC+eYo2Q==} + '@maxmind/geoip2-node@5.0.0': + resolution: {integrity: sha512-ki+q5//oU4tZ3BAhegZJcB5czoZyic5JSTEKbrUAQB/BzAoAiGyLW0immEmQvVVyy2SMlvBTJ3zqyRj8K9BbwQ==} + + '@monaco-editor/loader@1.4.0': + resolution: {integrity: sha512-00ioBig0x642hytVspPl7DbQyaSWRaolYie/UFNjoTdvoKPzo6xrXLhTk9ixgIKcLH5b5vDOjVNiGyY+uDCUlg==} peerDependencies: monaco-editor: '>= 0.21.0 < 1' - dependencies: - monaco-editor: 0.37.1 - state-local: 1.0.7 - dev: false - /@monaco-editor/react@4.5.0(monaco-editor@0.37.1)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-VJMkp5Fe1+w8pLEq8tZPHZKu8zDXQIA1FtiDTSNccg1D3wg1YIZaH2es2Qpvop1k62g3c/YySRb3bnGXu2XwYQ==} + '@monaco-editor/react@4.6.0': + resolution: {integrity: sha512-RFkU9/i7cN2bsq/iTkurMWOEErmYcY6JiQI3Jn+WeR/FGISH8JbHERjpS9oRuSOPvDMJI0Z8nJeKkbOs9sBYQw==} peerDependencies: monaco-editor: '>= 0.25.0 < 1' react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - dependencies: - '@monaco-editor/loader': 1.3.3(monaco-editor@0.37.1) - monaco-editor: 0.37.1 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - dev: false - - /@nangohq/frontend@0.21.11: - resolution: {integrity: sha512-oNOqM8gZhRaaHt63zyaWxsWi3pip4a78S9IzstEBVvUUzEFOU3+ys0r3TmyQCbA1veaAZjzeMyq+LtZem/Nt9g==} - dev: false - - /@next/env@13.0.0: - resolution: {integrity: sha512-65v9BVuah2Mplohm4+efsKEnoEuhmlGm8B2w6vD1geeEP2wXtlSJCvR/cCRJ3fD8wzCQBV41VcMBQeYET6MRkg==} - dev: false - /@next/env@13.3.0: - resolution: {integrity: sha512-AjppRV4uG3No7L1plinoTQETH+j2F10TEnrMfzbTUYwze5sBUPveeeBAPZPm8OkJZ1epq9OyYKhZrvbD6/9HCQ==} - dev: false + '@mongodb-js/saslprep@1.1.9': + resolution: {integrity: sha512-tVkljjeEaAhCqTzajSdgbQ6gE6f3oneVwa3iXR6csiEwXXOFsiC6Uh9iAjAhXPtqa/XMDHWjjeNH/77m/Yq2dw==} - /@next/eslint-plugin-next@13.0.0: - resolution: {integrity: sha512-z+gnX4Zizatqatc6f4CQrcC9oN8Us3Vrq/OLyc98h7K/eWctrnV91zFZodmJHUjx0cITY8uYM7LXD7IdYkg3kg==} - dependencies: - glob: 7.1.7 - dev: false + '@mongodb-js/zstd@2.0.1': + resolution: {integrity: sha512-hbQKltFj0hMrhe+Udh9gjkzswIJJVOo55vEHgfHbb6wjPpo4Oc3kng2bao/XnzLPCdd5Q1PXbWTC91LYPQrCtA==} + engines: {node: '>= 16.20.1'} - /@next/eslint-plugin-next@13.3.0: - resolution: {integrity: sha512-wuGN5qSEjSgcq9fVkH0Y/qIPFjnZtW3ZPwfjJOn7l/rrf6y8J24h/lo61kwqunTyzZJm/ETGfGVU9PUs8cnzEA==} - dependencies: - glob: 7.1.7 - dev: true + '@nangohq/frontend@0.21.17': + resolution: {integrity: sha512-utnO6JkRIQVqZBo472Yynf0nBl5Pc3QCMlC9L5LsIWApCD1pSkqZCA7cZKIJiX2p3tiB8Hzav31o0lAsnTZ1AQ==} - /@next/swc-android-arm-eabi@13.0.0: - resolution: {integrity: sha512-+DUQkYF93gxFjWY+CYWE1QDX6gTgnUiWf+W4UqZjM1Jcef8U97fS6xYh+i+8rH4MM0AXHm7OSakvfOMzmjU6VA==} - engines: {node: '>= 10'} - cpu: [arm] - os: [android] - requiresBuild: true - dev: false - optional: true + '@next/bundle-analyzer@15.5.4': + resolution: {integrity: sha512-wMtpIjEHi+B/wC34ZbEcacGIPgQTwTFjjp0+F742s9TxC6QwT0MwB/O0QEgalMe8s3SH/K09DO0gmTvUSJrLRA==} - /@next/swc-android-arm64@13.0.0: - resolution: {integrity: sha512-RW9Uy3bMSc0zVGCa11klFuwfP/jdcdkhdruqnrJ7v+7XHm6OFKkSRzX6ee7yGR1rdDZvTnP4GZSRSpzjLv/N0g==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [android] - requiresBuild: true - dev: false - optional: true + '@next/env@15.5.4': + resolution: {integrity: sha512-27SQhYp5QryzIT5uO8hq99C69eLQ7qkzkDPsk3N+GuS2XgOgoYEeOav7Pf8Tn4drECOVDsDg8oj+/DVy8qQL2A==} - /@next/swc-darwin-arm64@13.0.0: - resolution: {integrity: sha512-APA26nps1j4qyhOIzkclW/OmgotVHj1jBxebSpMCPw2rXfiNvKNY9FA0TcuwPmUCNqaTnm703h6oW4dvp73A4Q==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: false - optional: true + '@next/eslint-plugin-next@15.5.4': + resolution: {integrity: sha512-SR1vhXNNg16T4zffhJ4TS7Xn7eq4NfKfcOsRwea7RIAHrjRpI9ALYbamqIJqkAhowLlERffiwk0FMvTLNdnVtw==} - /@next/swc-darwin-arm64@13.3.0: - resolution: {integrity: sha512-DmIQCNq6JtccLPPBzf0dgh2vzMWt5wjxbP71pCi5EWpWYE3MsP6FcRXi4MlAmFNDQOfcFXR2r7kBeG1LpZUh1w==} + '@next/swc-darwin-arm64@15.5.4': + resolution: {integrity: sha512-nopqz+Ov6uvorej8ndRX6HlxCYWCO3AHLfKK2TYvxoSB2scETOcfm/HSS3piPqc3A+MUgyHoqE6je4wnkjfrOA==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - requiresBuild: true - dev: false - optional: true - - /@next/swc-darwin-x64@13.0.0: - resolution: {integrity: sha512-qsUhUdoFuRJiaJ7LnvTQ6GZv1QnMDcRXCIjxaN0FNVXwrjkq++U7KjBUaxXkRzLV4C7u0NHLNOp0iZwNNE7ypw==} - engines: {node: '>= 10'} - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: false - optional: true - /@next/swc-darwin-x64@13.3.0: - resolution: {integrity: sha512-oQoqFa88OGgwnYlnAGHVct618FRI/749se0N3S8t9Bzdv5CRbscnO0RcX901+YnNK4Q6yeiizfgO3b7kogtsZg==} + '@next/swc-darwin-x64@15.5.4': + resolution: {integrity: sha512-QOTCFq8b09ghfjRJKfb68kU9k2K+2wsC4A67psOiMn849K9ZXgCSRQr0oVHfmKnoqCbEmQWG1f2h1T2vtJJ9mA==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - requiresBuild: true - dev: false - optional: true - - /@next/swc-freebsd-x64@13.0.0: - resolution: {integrity: sha512-sCdyCbboS7CwdnevKH9J6hkJI76LUw1jVWt4eV7kISuLiPba3JmehZSWm80oa4ADChRVAwzhLAo2zJaYRrInbg==} - engines: {node: '>= 10'} - cpu: [x64] - os: [freebsd] - requiresBuild: true - dev: false - optional: true - - /@next/swc-linux-arm-gnueabihf@13.0.0: - resolution: {integrity: sha512-/X/VxfFA41C9jrEv+sUsPLQ5vbDPVIgG0CJrzKvrcc+b+4zIgPgtfsaWq9ockjHFQi3ycvlZK4TALOXO8ovQ6Q==} - engines: {node: '>= 10'} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: false - optional: true - - /@next/swc-linux-arm64-gnu@13.0.0: - resolution: {integrity: sha512-x6Oxr1GIi0ZtNiT6jbw+JVcbEi3UQgF7mMmkrgfL4mfchOwXtWSHKTSSPnwoJWJfXYa0Vy1n8NElWNTGAqoWFw==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: false - optional: true - /@next/swc-linux-arm64-gnu@13.3.0: - resolution: {integrity: sha512-Wzz2p/WqAJUqTVoLo6H18WMeAXo3i+9DkPDae4oQG8LMloJ3if4NEZTnOnTUlro6cq+S/W4pTGa97nWTrOjbGw==} + '@next/swc-linux-arm64-gnu@15.5.4': + resolution: {integrity: sha512-eRD5zkts6jS3VfE/J0Kt1VxdFqTnMc3QgO5lFE5GKN3KDI/uUpSyK3CjQHmfEkYR4wCOl0R0XrsjpxfWEA++XA==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - requiresBuild: true - dev: false - optional: true - - /@next/swc-linux-arm64-musl@13.0.0: - resolution: {integrity: sha512-SnMH9ngI+ipGh3kqQ8+mDtWunirwmhQnQeZkEq9e/9Xsgjf04OetqrqRHKM1HmJtG2qMUJbyXFJ0F81TPuT+3g==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: false - optional: true - /@next/swc-linux-arm64-musl@13.3.0: - resolution: {integrity: sha512-xPVrIQOQo9WXJYgmoTlMnAD/HlR/1e1ZIWGbwIzEirXBVBqMARUulBEIKdC19zuvoJ477qZJgBDCKtKEykCpyQ==} + '@next/swc-linux-arm64-musl@15.5.4': + resolution: {integrity: sha512-TOK7iTxmXFc45UrtKqWdZ1shfxuL4tnVAOuuJK4S88rX3oyVV4ZkLjtMT85wQkfBrOOvU55aLty+MV8xmcJR8A==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - requiresBuild: true - dev: false - optional: true - - /@next/swc-linux-x64-gnu@13.0.0: - resolution: {integrity: sha512-VSQwTX9EmdbotArtA1J67X8964oQfe0xHb32x4tu+JqTR+wOHyG6wGzPMdXH2oKAp6rdd7BzqxUXXf0J+ypHlw==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: false - optional: true - - /@next/swc-linux-x64-gnu@13.3.0: - resolution: {integrity: sha512-jOFlpGuPD7W2tuXVJP4wt9a3cpNxWAPcloq5EfMJRiXsBBOjLVFZA7boXYxEBzSVgUiVVr1V9T0HFM7pULJ1qA==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: false - optional: true - /@next/swc-linux-x64-musl@13.0.0: - resolution: {integrity: sha512-xBCP0nnpO0q4tsytXkvIwWFINtbFRyVY5gxa1zB0vlFtqYR9lNhrOwH3CBrks3kkeaePOXd611+8sjdUtrLnXA==} + '@next/swc-linux-x64-gnu@15.5.4': + resolution: {integrity: sha512-7HKolaj+481FSW/5lL0BcTkA4Ueam9SPYWyN/ib/WGAFZf0DGAN8frNpNZYFHtM4ZstrHZS3LY3vrwlIQfsiMA==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - requiresBuild: true - dev: false - optional: true - /@next/swc-linux-x64-musl@13.3.0: - resolution: {integrity: sha512-2OwKlzaBgmuet9XYHc3KwsEilzb04F540rlRXkAcjMHL7eCxB7uZIGtsVvKOnQLvC/elrUegwSw1+5f7WmfyOw==} + '@next/swc-linux-x64-musl@15.5.4': + resolution: {integrity: sha512-nlQQ6nfgN0nCO/KuyEUwwOdwQIGjOs4WNMjEUtpIQJPR2NUfmGpW2wkJln1d4nJ7oUzd1g4GivH5GoEPBgfsdw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - requiresBuild: true - dev: false - optional: true - - /@next/swc-win32-arm64-msvc@13.0.0: - resolution: {integrity: sha512-NutwDafqhGxqPj/eiUixJq9ImS/0sgx6gqlD7jRndCvQ2Q8AvDdu1+xKcGWGNnhcDsNM/n1avf1e62OG1GaqJg==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [win32] - requiresBuild: true - dev: false - optional: true - /@next/swc-win32-arm64-msvc@13.3.0: - resolution: {integrity: sha512-OeHiA6YEvndxT46g+rzFK/MQTfftKxJmzslERMu9LDdC6Kez0bdrgEYed5eXFK2Z1viKZJCGRlhd06rBusyztA==} + '@next/swc-win32-arm64-msvc@15.5.4': + resolution: {integrity: sha512-PcR2bN7FlM32XM6eumklmyWLLbu2vs+D7nJX8OAIoWy69Kef8mfiN4e8TUv2KohprwifdpFKPzIP1njuCjD0YA==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - requiresBuild: true - dev: false - optional: true - - /@next/swc-win32-ia32-msvc@13.0.0: - resolution: {integrity: sha512-zNaxaO+Kl/xNz02E9QlcVz0pT4MjkXGDLb25qxtAzyJL15aU0+VjjbIZAYWctG59dvggNIUNDWgoBeVTKB9xLg==} - engines: {node: '>= 10'} - cpu: [ia32] - os: [win32] - requiresBuild: true - dev: false - optional: true - - /@next/swc-win32-ia32-msvc@13.3.0: - resolution: {integrity: sha512-4aB7K9mcVK1lYEzpOpqWrXHEZympU3oK65fnNcY1Qc4HLJFLJj8AViuqQd4jjjPNuV4sl8jAwTz3gN5VNGWB7w==} - engines: {node: '>= 10'} - cpu: [ia32] - os: [win32] - requiresBuild: true - dev: false - optional: true - - /@next/swc-win32-x64-msvc@13.0.0: - resolution: {integrity: sha512-FFOGGWwTCRMu9W7MF496Urefxtuo2lttxF1vwS+1rIRsKvuLrWhVaVTj3T8sf2EBL6gtJbmh4TYlizS+obnGKA==} - engines: {node: '>= 10'} - cpu: [x64] - os: [win32] - requiresBuild: true - dev: false - optional: true - /@next/swc-win32-x64-msvc@13.3.0: - resolution: {integrity: sha512-Reer6rkLLcoOvB0dd66+Y7WrWVFH7sEEkF/4bJCIfsSKnTStTYaHtwIJAwbqnt9I392Tqvku0KkoqZOryWV9LQ==} + '@next/swc-win32-x64-msvc@15.5.4': + resolution: {integrity: sha512-1ur2tSHZj8Px/KMAthmuI9FMp/YFusMMGoRNJaRZMOlSkgvLjzosSdQI0cJAKogdHl3qXUQKL9MGaYvKwA7DXg==} engines: {node: '>= 10'} cpu: [x64] os: [win32] - requiresBuild: true - dev: false - optional: true - /@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1: + '@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1': resolution: {integrity: sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==} - dependencies: - eslint-scope: 5.1.1 - dev: false - /@nodelib/fs.scandir@2.1.5: + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} - dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 - /@nodelib/fs.stat@2.0.5: + '@nodelib/fs.stat@2.0.5': resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} engines: {node: '>= 8'} - /@nodelib/fs.walk@1.2.8: + '@nodelib/fs.walk@1.2.8': resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.15.0 - /@npmcli/arborist@4.3.1: + '@nolyfill/is-core-module@1.0.39': + resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==} + engines: {node: '>=12.4.0'} + + '@npmcli/arborist@4.3.1': resolution: {integrity: sha512-yMRgZVDpwWjplorzt9SFSaakWx6QIK248Nw4ZFgkrAy/GvJaFRaSZzE6nD7JBK5r8g/+PTxFq5Wj/sfciE7x+A==} engines: {node: ^12.13.0 || ^14.15.0 || >=16} hasBin: true - dependencies: - '@isaacs/string-locale-compare': 1.1.0 - '@npmcli/installed-package-contents': 1.0.7 - '@npmcli/map-workspaces': 2.0.4 - '@npmcli/metavuln-calculator': 2.0.0 - '@npmcli/move-file': 1.1.2 - '@npmcli/name-from-folder': 1.0.1 - '@npmcli/node-gyp': 1.0.3 - '@npmcli/package-json': 1.0.1 - '@npmcli/run-script': 2.0.0 - bin-links: 3.0.3 - cacache: 15.3.0 - common-ancestor-path: 1.0.1 - json-parse-even-better-errors: 2.3.1 - json-stringify-nice: 1.1.4 - mkdirp: 1.0.4 - mkdirp-infer-owner: 2.0.0 - npm-install-checks: 4.0.0 - npm-package-arg: 8.1.5 - npm-pick-manifest: 6.1.1 - npm-registry-fetch: 12.0.2 - pacote: 12.0.3 - parse-conflict-json: 2.0.2 - proc-log: 1.0.0 - promise-all-reject-late: 1.0.1 - promise-call-limit: 1.0.2 - read-package-json-fast: 2.0.3 - readdir-scoped-modules: 1.1.0 - rimraf: 3.0.2 - semver: 7.5.0 - ssri: 8.0.1 - treeverse: 1.0.4 - walk-up-path: 1.0.0 - transitivePeerDependencies: - - bluebird - - supports-color - /@npmcli/fs@1.1.1: + '@npmcli/fs@1.1.1': resolution: {integrity: sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==} - dependencies: - '@gar/promisify': 1.1.3 - semver: 7.5.0 - /@npmcli/fs@2.1.2: + '@npmcli/fs@2.1.2': resolution: {integrity: sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - dependencies: - '@gar/promisify': 1.1.3 - semver: 7.5.0 - /@npmcli/git@2.1.0: + '@npmcli/fs@3.1.1': + resolution: {integrity: sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + '@npmcli/git@2.1.0': resolution: {integrity: sha512-/hBFX/QG1b+N7PZBFs0bi+evgRZcK9nWBxQKZkGoXUT5hJSwl5c4d7y8/hm+NQZRPhQ67RzFaj5UM9YeyKoryw==} - dependencies: - '@npmcli/promise-spawn': 1.3.2 - lru-cache: 6.0.0 - mkdirp: 1.0.4 - npm-pick-manifest: 6.1.1 - promise-inflight: 1.0.1 - promise-retry: 2.0.1 - semver: 7.5.0 - which: 2.0.2 - transitivePeerDependencies: - - bluebird - /@npmcli/installed-package-contents@1.0.7: + '@npmcli/git@4.1.0': + resolution: {integrity: sha512-9hwoB3gStVfa0N31ymBmrX+GuDGdVA/QWShZVqE0HK2Af+7QGGrCTbZia/SW0ImUTjTne7SP91qxDmtXvDHRPQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + '@npmcli/installed-package-contents@1.0.7': resolution: {integrity: sha512-9rufe0wnJusCQoLpV9ZPKIVP55itrM5BxOXs10DmdbRfgWtHy1LDyskbwRnBghuB0PrF7pNPOqREVtpz4HqzKw==} engines: {node: '>= 10'} hasBin: true - dependencies: - npm-bundled: 1.1.2 - npm-normalize-package-bin: 1.0.1 - /@npmcli/map-workspaces@2.0.4: + '@npmcli/installed-package-contents@2.1.0': + resolution: {integrity: sha512-c8UuGLeZpm69BryRykLuKRyKFZYJsZSCT4aVY5ds4omyZqJ172ApzgfKJ5eV/r3HgLdUYgFVe54KSFVjKoe27w==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + hasBin: true + + '@npmcli/map-workspaces@2.0.4': resolution: {integrity: sha512-bMo0aAfwhVwqoVM5UzX1DJnlvVvzDCHae821jv48L1EsrYwfOZChlqWYXEtto/+BkBXetPbEWgau++/brh4oVg==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - dependencies: - '@npmcli/name-from-folder': 1.0.1 - glob: 8.1.0 - minimatch: 5.1.6 - read-package-json-fast: 2.0.3 - /@npmcli/metavuln-calculator@2.0.0: + '@npmcli/metavuln-calculator@2.0.0': resolution: {integrity: sha512-VVW+JhWCKRwCTE+0xvD6p3uV4WpqocNYYtzyvenqL/u1Q3Xx6fGTJ+6UoIoii07fbuEO9U3IIyuGY0CYHDv1sg==} engines: {node: ^12.13.0 || ^14.15.0 || >=16} - dependencies: - cacache: 15.3.0 - json-parse-even-better-errors: 2.3.1 - pacote: 12.0.3 - semver: 7.5.0 - transitivePeerDependencies: - - bluebird - - supports-color - /@npmcli/move-file@1.1.2: + '@npmcli/move-file@1.1.2': resolution: {integrity: sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==} engines: {node: '>=10'} deprecated: This functionality has been moved to @npmcli/fs - dependencies: - mkdirp: 1.0.4 - rimraf: 3.0.2 - /@npmcli/move-file@2.0.1: + '@npmcli/move-file@2.0.1': resolution: {integrity: sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} deprecated: This functionality has been moved to @npmcli/fs - dependencies: - mkdirp: 1.0.4 - rimraf: 3.0.2 - /@npmcli/name-from-folder@1.0.1: + '@npmcli/name-from-folder@1.0.1': resolution: {integrity: sha512-qq3oEfcLFwNfEYOQ8HLimRGKlD8WSeGEdtUa7hmzpR8Sa7haL1KVQrvgO6wqMjhWFFVjgtrh1gIxDz+P8sjUaA==} - /@npmcli/node-gyp@1.0.3: + '@npmcli/node-gyp@1.0.3': resolution: {integrity: sha512-fnkhw+fmX65kiLqk6E3BFLXNC26rUhK90zVwe2yncPliVT/Qos3xjhTLE59Df8KnPlcwIERXKVlU1bXoUQ+liA==} - /@npmcli/package-json@1.0.1: - resolution: {integrity: sha512-y6jnu76E9C23osz8gEMBayZmaZ69vFOIk8vR1FJL/wbEJ54+9aVG9rLTjQKSXfgYZEr50nw1txBBFfBZZe+bYg==} - dependencies: - json-parse-even-better-errors: 2.3.1 + '@npmcli/node-gyp@3.0.0': + resolution: {integrity: sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - /@npmcli/package-json@2.0.0: - resolution: {integrity: sha512-42jnZ6yl16GzjWSH7vtrmWyJDGVa/LXPdpN2rcUWolFjc9ON2N3uz0qdBbQACfmhuJZ2lbKYtmK5qx68ZPLHMA==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - dependencies: - json-parse-even-better-errors: 2.3.1 - dev: true + '@npmcli/package-json@1.0.1': + resolution: {integrity: sha512-y6jnu76E9C23osz8gEMBayZmaZ69vFOIk8vR1FJL/wbEJ54+9aVG9rLTjQKSXfgYZEr50nw1txBBFfBZZe+bYg==} - /@npmcli/promise-spawn@1.3.2: + '@npmcli/promise-spawn@1.3.2': resolution: {integrity: sha512-QyAGYo/Fbj4MXeGdJcFzZ+FkDkomfRBrPM+9QYJSg+PxgAUL+LU3FneQk37rKR2/zjqkCV1BLHccX98wRXG3Sg==} - dependencies: - infer-owner: 1.0.4 - /@npmcli/run-script@2.0.0: + '@npmcli/promise-spawn@6.0.2': + resolution: {integrity: sha512-gGq0NJkIGSwdbUt4yhdF8ZrmkGKVz9vAdVzpOfnom+V8PLSmSOVhZwbNvZZS1EYcJN5hzzKBxmmVVAInM6HQLg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + '@npmcli/run-script@2.0.0': resolution: {integrity: sha512-fSan/Pu11xS/TdaTpTB0MRn9guwGU8dye+x56mEVgBEd/QsybBbYcAL0phPXi8SGWFEChkQd6M9qL4y6VOpFig==} - dependencies: - '@npmcli/node-gyp': 1.0.3 - '@npmcli/promise-spawn': 1.3.2 - node-gyp: 8.4.1 - read-package-json-fast: 2.0.3 - transitivePeerDependencies: - - bluebird - - supports-color - /@octokit/auth-token@2.5.0: + '@npmcli/run-script@6.0.2': + resolution: {integrity: sha512-NCcr1uQo1k5U+SYlnIrbAh3cxy+OQT1VtqiAbxdymSlptbzBb62AjH2xXgjNCoP073hoa1CfCAcwoZ8k96C4nA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + '@octokit/auth-token@2.5.0': resolution: {integrity: sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==} - dependencies: - '@octokit/types': 6.41.0 - /@octokit/core@3.6.0: + '@octokit/core@3.6.0': resolution: {integrity: sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==} - dependencies: - '@octokit/auth-token': 2.5.0 - '@octokit/graphql': 4.8.0 - '@octokit/request': 5.6.3 - '@octokit/request-error': 2.1.0 - '@octokit/types': 6.41.0 - before-after-hook: 2.2.3 - universal-user-agent: 6.0.0 - transitivePeerDependencies: - - encoding - /@octokit/endpoint@6.0.12: + '@octokit/endpoint@6.0.12': resolution: {integrity: sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==} - dependencies: - '@octokit/types': 6.41.0 - is-plain-object: 5.0.0 - universal-user-agent: 6.0.0 - /@octokit/graphql@4.8.0: + '@octokit/graphql@4.8.0': resolution: {integrity: sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==} - dependencies: - '@octokit/request': 5.6.3 - '@octokit/types': 6.41.0 - universal-user-agent: 6.0.0 - transitivePeerDependencies: - - encoding - /@octokit/openapi-types@12.11.0: + '@octokit/openapi-types@12.11.0': resolution: {integrity: sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==} - /@octokit/plugin-paginate-rest@2.21.3(@octokit/core@3.6.0): + '@octokit/plugin-paginate-rest@2.21.3': resolution: {integrity: sha512-aCZTEf0y2h3OLbrgKkrfFdjRL6eSOo8komneVQJnYecAxIej7Bafor2xhuDJOIFau4pk0i/P28/XgtbyPF0ZHw==} peerDependencies: '@octokit/core': '>=2' - dependencies: - '@octokit/core': 3.6.0 - '@octokit/types': 6.41.0 - /@octokit/plugin-request-log@1.0.4(@octokit/core@3.6.0): + '@octokit/plugin-request-log@1.0.4': resolution: {integrity: sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==} peerDependencies: '@octokit/core': '>=3' - dependencies: - '@octokit/core': 3.6.0 - /@octokit/plugin-rest-endpoint-methods@5.16.2(@octokit/core@3.6.0): + '@octokit/plugin-rest-endpoint-methods@5.16.2': resolution: {integrity: sha512-8QFz29Fg5jDuTPXVtey05BLm7OB+M8fnvE64RNegzX7U+5NUXcOcnpTIK0YfSHBg8gYd0oxIq3IZTe9SfPZiRw==} peerDependencies: '@octokit/core': '>=3' - dependencies: - '@octokit/core': 3.6.0 - '@octokit/types': 6.41.0 - deprecation: 2.3.1 - /@octokit/request-error@2.1.0: + '@octokit/request-error@2.1.0': resolution: {integrity: sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==} - dependencies: - '@octokit/types': 6.41.0 - deprecation: 2.3.1 - once: 1.4.0 - /@octokit/request@5.6.3: + '@octokit/request@5.6.3': resolution: {integrity: sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==} - dependencies: - '@octokit/endpoint': 6.0.12 - '@octokit/request-error': 2.1.0 - '@octokit/types': 6.41.0 - is-plain-object: 5.0.0 - node-fetch: 2.6.9 - universal-user-agent: 6.0.0 - transitivePeerDependencies: - - encoding - /@octokit/rest@18.12.0: + '@octokit/rest@18.12.0': resolution: {integrity: sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q==} - dependencies: - '@octokit/core': 3.6.0 - '@octokit/plugin-paginate-rest': 2.21.3(@octokit/core@3.6.0) - '@octokit/plugin-request-log': 1.0.4(@octokit/core@3.6.0) - '@octokit/plugin-rest-endpoint-methods': 5.16.2(@octokit/core@3.6.0) - transitivePeerDependencies: - - encoding - /@octokit/types@6.41.0: + '@octokit/types@6.41.0': resolution: {integrity: sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==} - dependencies: - '@octokit/openapi-types': 12.11.0 - /@panva/hkdf@1.0.4: - resolution: {integrity: sha512-003xWiCuvePbLaPHT+CRuaV4GlyCAVm6XYSbBZDHoWZGn1mNkVKFaDbGJjjxmEFvizUwlCoM6O18FCBMMky2zQ==} - dev: false + '@one-ini/wasm@0.1.1': + resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==} - /@pkgr/utils@2.3.1: - resolution: {integrity: sha512-wfzX8kc1PMyUILA+1Z/EqoE4UCXGy0iRGMhPwdfae1+f0OXlLqCk+By+aMzgJBzR9AzS4CDizioG6Ss1gvAFJw==} - engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} - dependencies: - cross-spawn: 7.0.3 - is-glob: 4.0.3 - open: 8.4.2 - picocolors: 1.0.0 - tiny-glob: 0.2.9 - tslib: 2.5.0 - dev: true + '@opentelemetry/api@1.9.0': + resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} + engines: {node: '>=8.0.0'} + + '@panva/hkdf@1.2.1': + resolution: {integrity: sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw==} - /@playwright/test@1.31.2: - resolution: {integrity: sha512-BYVutxDI4JeZKV1+ups6dt5WiqKhjBtIYowyZIJ3kBDmJgsuPKsqqKNIMFbUePLSCmp2cZu+BDL427RcNKTRYw==} + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} + + '@playwright/test@1.39.0': + resolution: {integrity: sha512-3u1iFqgzl7zr004bGPYiN/5EZpRUSFddQBra8Rqll5N0/vfpqlP9I9EXqAoGacuAbX6c9Ulg/Cjqglp5VkK6UQ==} + engines: {node: '>=16'} + deprecated: Please update to the latest version of Playwright to test up-to-date browsers. hasBin: true - dependencies: - '@types/node': 18.15.11 - playwright-core: 1.31.2 - optionalDependencies: - fsevents: 2.3.2 - dev: true - /@pmmmwh/react-refresh-webpack-plugin@0.5.10(react-refresh@0.11.0)(webpack-dev-server@4.13.2)(webpack@5.78.0): - resolution: {integrity: sha512-j0Ya0hCFZPd4x40qLzbhGsh9TMtdb+CJQiso+WxLOPNasohq9cc5SNUcwsZaRH6++Xh91Xkm/xHCkuIiIu0LUA==} + '@pmmmwh/react-refresh-webpack-plugin@0.5.15': + resolution: {integrity: sha512-LFWllMA55pzB9D34w/wXUCf8+c+IYKuJDgxiZ3qMhl64KRMBHYM1I3VdGaD2BV5FNPV2/S2596bppxHbv2ZydQ==} engines: {node: '>= 10.13'} peerDependencies: '@types/webpack': 4.x || 5.x react-refresh: '>=0.10.0 <1.0.0' sockjs-client: ^1.4.0 - type-fest: '>=0.17.0 <4.0.0' + type-fest: '>=0.17.0 <5.0.0' webpack: '>=4.43.0 <6.0.0' - webpack-dev-server: 3.x || 4.x + webpack-dev-server: 3.x || 4.x || 5.x webpack-hot-middleware: 2.x webpack-plugin-serve: 0.x || 1.x peerDependenciesMeta: @@ -5696,1969 +4035,1329 @@ packages: optional: true webpack-plugin-serve: optional: true - dependencies: - ansi-html-community: 0.0.8 - common-path-prefix: 3.0.0 - core-js-pure: 3.30.0 - error-stack-parser: 2.1.4 - find-up: 5.0.0 - html-entities: 2.3.3 - loader-utils: 2.0.4 - react-refresh: 0.11.0 - schema-utils: 3.1.1 - source-map: 0.7.4 - webpack: 5.78.0(esbuild@0.16.3)(webpack-cli@5.0.1) - webpack-dev-server: 4.13.2(webpack@5.78.0) - dev: false - /@prisma/client@4.12.0(prisma@4.12.0): - resolution: {integrity: sha512-j9/ighfWwux97J2dS15nqhl60tYoH8V0IuSsgZDb6bCFcQD3fXbXmxjYC8GHhIgOk3lB7Pq+8CwElz2MiDpsSg==} - engines: {node: '>=14.17'} - requiresBuild: true + '@polka/url@1.0.0-next.28': + resolution: {integrity: sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==} + + '@prisma/client@6.5.0': + resolution: {integrity: sha512-M6w1Ql/BeiGoZmhMdAZUXHu5sz5HubyVcKukbLs3l0ELcQb8hTUJxtGEChhv4SVJ0QJlwtLnwOLgIRQhpsm9dw==} + engines: {node: '>=18.18'} peerDependencies: prisma: '*' + typescript: '>=5.1.0' peerDependenciesMeta: prisma: optional: true - dependencies: - '@prisma/engines-version': 4.12.0-67.659ef412370fa3b41cd7bf6e94587c1dfb7f67e7 - prisma: 4.12.0 - dev: false + typescript: + optional: true + + '@prisma/config@6.5.0': + resolution: {integrity: sha512-sOH/2Go9Zer67DNFLZk6pYOHj+rumSb0VILgltkoxOjYnlLqUpHPAN826vnx8HigqnOCxj9LRhT6U7uLiIIWgw==} - /@prisma/debug@3.8.1: + '@prisma/debug@3.8.1': resolution: {integrity: sha512-ft4VPTYME1UBJ7trfrBuF2w9jX1ipDy786T9fAEskNGb+y26gPDqz5fiEWc2kgHNeVdz/qTI/V3wXILRyEcgxQ==} - dependencies: - '@types/debug': 4.1.7 - ms: 2.1.3 - strip-ansi: 6.0.1 - dev: true - /@prisma/engines-version@4.12.0-67.659ef412370fa3b41cd7bf6e94587c1dfb7f67e7: - resolution: {integrity: sha512-JIHNj5jlXb9mcaJwakM0vpgRYJIAurxTUqM0iX0tfEQA5XLZ9ONkIckkhuAKdAzocZ+80GYg7QSsfpjg7OxbOA==} - dev: false + '@prisma/debug@6.5.0': + resolution: {integrity: sha512-fc/nusYBlJMzDmDepdUtH9aBsJrda2JNErP9AzuHbgUEQY0/9zQYZdNlXmKoIWENtio+qarPNe/+DQtrX5kMcQ==} - /@prisma/engines@4.12.0: - resolution: {integrity: sha512-0alKtnxhNB5hYU+ymESBlGI4b9XrGGSdv7Ud+8TE/fBNOEhIud0XQsAR+TrvUZgS4na5czubiMsODw0TUrgkIA==} - requiresBuild: true + '@prisma/engines-version@6.5.0-73.173f8d54f8d52e692c7e27e72a88314ec7aeff60': + resolution: {integrity: sha512-iK3EmiVGFDCmXjSpdsKGNqy9hOdLnvYBrJB61far/oP03hlIxrb04OWmDjNTwtmZ3UZdA5MCvI+f+3k2jPTflQ==} - /@prisma/generator-helper@3.8.1: + '@prisma/engines@6.5.0': + resolution: {integrity: sha512-FVPQYHgOllJklN9DUyujXvh3hFJCY0NX86sDmBErLvoZjy2OXGiZ5FNf3J/C4/RZZmCypZBYpBKEhx7b7rEsdw==} + + '@prisma/fetch-engine@6.5.0': + resolution: {integrity: sha512-3LhYA+FXP6pqY8FLHCjewyE8pGXXJ7BxZw2rhPq+CZAhvflVzq4K8Qly3OrmOkn6wGlz79nyLQdknyCG2HBTuA==} + + '@prisma/generator-helper@3.8.1': resolution: {integrity: sha512-3zSy+XTEjmjLj6NO+/YPN1Cu7or3xA11TOoOnLRJ9G4pTT67RJXjK0L9Xy5n+3I0Xlb7xrWCgo8MvQQLMWzxPA==} - dependencies: - '@prisma/debug': 3.8.1 - '@types/cross-spawn': 6.0.2 - chalk: 4.1.2 - cross-spawn: 7.0.3 - dev: true - /@protobufjs/aspromise@1.1.2: + '@prisma/get-platform@6.5.0': + resolution: {integrity: sha512-xYcvyJwNMg2eDptBYFqFLUCfgi+wZLcj6HDMsj0Qw0irvauG4IKmkbywnqwok0B+k+W+p+jThM2DKTSmoPCkzw==} + + '@protobufjs/aspromise@1.1.2': resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} - /@protobufjs/base64@1.1.2: + '@protobufjs/base64@1.1.2': resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} - /@protobufjs/codegen@2.0.4: + '@protobufjs/codegen@2.0.4': resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==} - /@protobufjs/eventemitter@1.1.0: + '@protobufjs/eventemitter@1.1.0': resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==} - /@protobufjs/fetch@1.1.0: + '@protobufjs/fetch@1.1.0': resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==} - dependencies: - '@protobufjs/aspromise': 1.1.2 - '@protobufjs/inquire': 1.1.0 - /@protobufjs/float@1.0.2: + '@protobufjs/float@1.0.2': resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} - /@protobufjs/inquire@1.1.0: + '@protobufjs/inquire@1.1.0': resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==} - /@protobufjs/path@1.1.2: + '@protobufjs/path@1.1.2': resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} - /@protobufjs/pool@1.1.0: + '@protobufjs/pool@1.1.0': resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} - /@protobufjs/utf8@1.1.0: + '@protobufjs/utf8@1.1.0': resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} - /@rc-component/context@1.3.0(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-6QdaCJ7Wn5UZLJs15IEfqy4Ru3OaL5ctqpQYWd5rlfV9wwzrzdt6+kgAQZV/qdB0MUPN4nhyBfRembQCIvBf+w==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - dependencies: - '@babel/runtime': 7.21.0 - rc-util: 5.29.3(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + '@radix-ui/colors@3.0.0': + resolution: {integrity: sha512-FUOsGBkHrYJwCSEtWRCIfQbZG7q1e6DgxCIOe1SUQzDe/7rXXeA47s8yCn6fuTNQAj1Zq4oTFi9Yjp3wzElcxg==} - /@rc-component/mini-decimal@1.0.1: - resolution: {integrity: sha512-9N8nRk0oKj1qJzANKl+n9eNSMUGsZtjwNuDCiZ/KA+dt1fE3zq5x2XxclRcAbOIXnZcJ53ozP2Pa60gyELXagA==} - engines: {node: '>=8.x'} - dependencies: - '@babel/runtime': 7.21.0 + '@radix-ui/primitive@1.1.2': + resolution: {integrity: sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==} - /@rc-component/mutate-observer@1.0.0(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-okqRJSfNisXdI6CUeOLZC5ukBW/8kir2Ii4PJiKpUt+3+uS7dxwJUMxsUZquxA1rQuL8YcEmKVp/TCnR+yUdZA==} - engines: {node: '>=8.x'} + '@radix-ui/react-arrow@1.1.4': + resolution: {integrity: sha512-qz+fxrqgNxG0dYew5l7qR3c7wdgRu1XVUHGnGYX7rg5HM4p9SWaRmJwfgR3J0SgyUKayLmzQIun+N6rWRgiRKw==} peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - dependencies: - '@babel/runtime': 7.21.0 - classnames: 2.3.2 - rc-util: 5.29.3(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true - /@rc-component/portal@1.1.1(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-m8w3dFXX0H6UkJ4wtfrSwhe2/6M08uz24HHrF8pWfAXPwA9hwCuTE5per/C86KwNLouRpwFGcr7LfpHaa1F38g==} - engines: {node: '>=8.x'} + '@radix-ui/react-collapsible@1.1.7': + resolution: {integrity: sha512-zGFsPcFJNdQa/UNd6MOgF40BS054FIGj32oOWBllixz42f+AkQg3QJ1YT9pw7vs+Ai+EgWkh839h69GEK8oH2A==} peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - dependencies: - '@babel/runtime': 7.21.0 - classnames: 2.3.2 - rc-util: 5.29.3(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true - /@rc-component/tour@1.8.0(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-rrRGioHTLQlGca27G2+lw7QpRb3uuMYCUIJjj31/B44VCJS0P2tqYhOgtzvWQmaLMlWH3ZlpzotkKX13NT4XEA==} - engines: {node: '>=8.x'} + '@radix-ui/react-collection@1.1.4': + resolution: {integrity: sha512-cv4vSf7HttqXilDnAnvINd53OTl1/bjUYVZrkFnA7nwmY9Ob2POUy0WY0sfqBAe1s5FyKsyceQlqiEGPYNTadg==} peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - dependencies: - '@babel/runtime': 7.21.0 - '@rc-component/portal': 1.1.1(react-dom@18.2.0)(react@18.2.0) - '@rc-component/trigger': 1.8.0(react-dom@18.2.0)(react@18.2.0) - classnames: 2.3.2 - rc-util: 5.29.3(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true - /@rc-component/trigger@1.8.0(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-O9d4Tlg/FiCUlkQ+aAUUO5KmrBbj4XYq6qYfZE/hvNHzIepHqwLGx8H/d+1fG13dVPq70nGDf5ha9PQ96YRMVg==} - engines: {node: '>=8.x'} + '@radix-ui/react-compose-refs@1.1.2': + resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==} peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' - dependencies: - '@babel/runtime': 7.21.0 - '@rc-component/portal': 1.1.1(react-dom@18.2.0)(react@18.2.0) - classnames: 2.3.2 - rc-align: 4.0.15(react-dom@18.2.0)(react@18.2.0) - rc-motion: 2.6.3(react-dom@18.2.0)(react@18.2.0) - rc-resize-observer: 1.3.1(react-dom@18.2.0)(react@18.2.0) - rc-util: 5.29.3(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - - /@remix-run/router@1.5.0: - resolution: {integrity: sha512-bkUDCp8o1MvFO+qxkODcbhSqRa6P2GXgrGZVpt0dCXNW2HCSCqYI0ZoAqEOSAjRWmmlKcYgFvN4B4S+zo/f8kg==} - engines: {node: '>=14'} + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true - /@remix-run/server-runtime@1.15.0: - resolution: {integrity: sha512-DL9xjHfYYrEcOq5VbhYtrjJUWo/nFQAT7Y+Np/oC55HokyU6cb2jGhl52nx96aAxKwaFCse5N90GeodFsRzX7w==} - engines: {node: '>=14'} - dependencies: - '@remix-run/router': 1.5.0 - '@types/cookie': 0.4.1 - '@types/react': 18.0.28 - '@web3-storage/multipart-parser': 1.0.0 - cookie: 0.4.2 - set-cookie-parser: 2.6.0 - source-map: 0.7.4 - dev: true + '@radix-ui/react-context@1.1.2': + resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true - /@rjsf/antd@5.3.1(@ant-design/icons@5.0.1)(@rjsf/core@5.3.1)(@rjsf/utils@5.3.1)(antd@5.4.2)(dayjs@1.11.7)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-Cu2n6XMeNQUpYLqWwzSFwHeaPclzueeV3Wqf2zQcvG5UlOBZP5FhLMNlkY1PG/AeshK7rsMiTJa0AN60k0AsKg==} - engines: {node: '>=14'} + '@radix-ui/react-direction@1.1.1': + resolution: {integrity: sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==} peerDependencies: - '@ant-design/icons': ^4.0.0 - '@rjsf/core': ^5.0.0 - '@rjsf/utils': ^5.0.0 - antd: ^4.0.0 - dayjs: ^1.8.0 - react: ^16.14.0 || >=17 - dependencies: - '@ant-design/icons': 5.0.1(react-dom@18.2.0)(react@18.2.0) - '@rjsf/core': 5.3.1(@rjsf/utils@5.3.1)(react@18.2.0) - '@rjsf/utils': 5.3.1(react@18.2.0) - antd: 5.4.2(react-dom@18.2.0)(react@18.2.0) - classnames: 2.3.2 - dayjs: 1.11.7 - lodash: 4.17.21 - lodash-es: 4.17.21 - rc-picker: 2.7.0(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - transitivePeerDependencies: - - react-dom - dev: false + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true - /@rjsf/core@5.3.1(@rjsf/utils@5.3.1)(react@18.2.0): - resolution: {integrity: sha512-AejBNI5XBmfirubZ2L/f7Hx9hga4E3w777UB4ZvzAPwNygxKS3p/4nUKuP4Ho53aktsArdva0mdmgdBNn8phCw==} - engines: {node: '>=14'} + '@radix-ui/react-dismissable-layer@1.1.7': + resolution: {integrity: sha512-j5+WBUdhccJsmH5/H0K6RncjDtoALSEr6jbkaZu+bjw6hOPOhHycr6vEUujl+HBK8kjUfWcoCJXxP6e4lUlMZw==} peerDependencies: - '@rjsf/utils': ^5.0.0 - react: ^16.14.0 || >=17 - dependencies: - '@rjsf/utils': 5.3.1(react@18.2.0) - lodash: 4.17.21 - lodash-es: 4.17.21 - markdown-to-jsx: 7.2.0(react@18.2.0) - nanoid: 3.3.6 - prop-types: 15.8.1 - react: 18.2.0 - dev: false + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true - /@rjsf/utils@5.3.1(react@18.2.0): - resolution: {integrity: sha512-bpqq1c8nivNaynGgQa/ZOTxZaD8RYo1YYF/cHpmTqGGO+qwNhimJdFCxu6uOJ5dRP59nsJ3nYbhzK1cLtxFXfQ==} - engines: {node: '>=14'} + '@radix-ui/react-dropdown-menu@2.1.10': + resolution: {integrity: sha512-8qnILty92BmXbxKugWX3jgEeFeMoxtdggeCCxb/aB7l34QFAKB23IhJfnwyVMbRnAUJiT5LOay4kUS22+AWuRg==} peerDependencies: - react: ^16.14.0 || >=17 - dependencies: - json-schema-merge-allof: 0.8.1 - jsonpointer: 5.0.1 - lodash: 4.17.21 - lodash-es: 4.17.21 - react: 18.2.0 - react-is: 18.2.0 - dev: false + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true - /@rjsf/validator-ajv6@5.3.1(@rjsf/utils@5.3.1): - resolution: {integrity: sha512-HzlBZlrZKx+3tgzBT0cZALfblzBUFZCbWZtBLBH13DTIZ/6fj1CrnBs2JqtnrSpoWFRSGPyRm9g6+G/BJkFJcQ==} - engines: {node: '>=14'} + '@radix-ui/react-focus-guards@1.1.2': + resolution: {integrity: sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA==} peerDependencies: - '@rjsf/utils': ^5.0.0 - dependencies: - '@rjsf/utils': 5.3.1(react@18.2.0) - ajv: 6.12.6 - lodash: 4.17.21 - lodash-es: 4.17.21 - dev: false + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true - /@rollup/plugin-alias@3.1.9(rollup@1.32.1): - resolution: {integrity: sha512-QI5fsEvm9bDzt32k39wpOwZhVzRcL5ydcffUHMyLVaVaLeC70I8TJZ17F1z1eMoLu4E/UOcH9BWVkKpIKdrfiw==} - engines: {node: '>=8.0.0'} + '@radix-ui/react-focus-scope@1.1.4': + resolution: {integrity: sha512-r2annK27lIW5w9Ho5NyQgqs0MmgZSTIKXWpVCJaLC1q2kZrZkcqnmHkCHMEmv8XLvsLlurKMPT+kbKkRkm/xVA==} peerDependencies: - rollup: ^1.20.0||^2.0.0 - dependencies: - rollup: 1.32.1 - slash: 3.0.0 - dev: true + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true - /@rollup/plugin-babel@5.3.1(@babel/core@7.21.4)(rollup@2.79.1): - resolution: {integrity: sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==} - engines: {node: '>= 10.0.0'} + '@radix-ui/react-id@1.1.1': + resolution: {integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==} peerDependencies: - '@babel/core': ^7.0.0 - '@types/babel__core': ^7.1.9 - rollup: ^1.20.0||^2.0.0 + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: - '@types/babel__core': + '@types/react': optional: true - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-module-imports': 7.21.4 - '@rollup/pluginutils': 3.1.0(rollup@2.79.1) - rollup: 2.79.1 - dev: false - /@rollup/plugin-babel@6.0.3(@babel/core@7.21.4)(rollup@3.20.2): - resolution: {integrity: sha512-fKImZKppa1A/gX73eg4JGo+8kQr/q1HBQaCGKECZ0v4YBBv3lFqi14+7xyApECzvkLTHCifx+7ntcrvtBIRcpg==} - engines: {node: '>=14.0.0'} + '@radix-ui/react-menu@2.1.10': + resolution: {integrity: sha512-OupA+1PrVf2H0K4jIwkDyA+rsJ7vF1y/VxLEO43dmZ68GtCjvx9K1/B/QscPZM3jIeFNK/wPd0HmiLjT36hVcA==} peerDependencies: - '@babel/core': ^7.0.0 - '@types/babel__core': ^7.1.9 - rollup: ^1.20.0||^2.0.0||^3.0.0 + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: - '@types/babel__core': + '@types/react': optional: true - rollup: + '@types/react-dom': optional: true - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-module-imports': 7.21.4 - '@rollup/pluginutils': 5.0.2(rollup@3.20.2) - rollup: 3.20.2 - dev: true - - /@rollup/plugin-commonjs@11.1.0(rollup@1.32.1): - resolution: {integrity: sha512-Ycr12N3ZPN96Fw2STurD21jMqzKwL9QuFhms3SD7KKRK7oaXUsBU9Zt0jL/rOPHiPYisI21/rXGO3jr9BnLHUA==} - engines: {node: '>= 8.0.0'} - peerDependencies: - rollup: ^1.20.0||^2.0.0 - dependencies: - '@rollup/pluginutils': 3.1.0(rollup@1.32.1) - commondir: 1.0.1 - estree-walker: 1.0.1 - glob: 7.2.3 - is-reference: 1.2.1 - magic-string: 0.25.9 - resolve: 1.22.2 - rollup: 1.32.1 - dev: true - /@rollup/plugin-commonjs@23.0.7(rollup@3.20.2): - resolution: {integrity: sha512-hsSD5Qzyuat/swzrExGG5l7EuIlPhwTsT7KwKbSCQzIcJWjRxiimi/0tyMYY2bByitNb3i1p+6JWEDGa0NvT0Q==} - engines: {node: '>=14.0.0'} + '@radix-ui/react-popover@1.1.10': + resolution: {integrity: sha512-IZN7b3sXqajiPsOzKuNJBSP9obF4MX5/5UhTgWNofw4r1H+eATWb0SyMlaxPD/kzA4vadFgy1s7Z1AEJ6WMyHQ==} peerDependencies: - rollup: ^2.68.0||^3.0.0 + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: - rollup: + '@types/react': + optional: true + '@types/react-dom': optional: true - dependencies: - '@rollup/pluginutils': 5.0.2(rollup@3.20.2) - commondir: 1.0.1 - estree-walker: 2.0.2 - glob: 8.1.0 - is-reference: 1.2.1 - magic-string: 0.27.0 - rollup: 3.20.2 - dev: true - /@rollup/plugin-json@4.1.0(rollup@1.32.1): - resolution: {integrity: sha512-yfLbTdNS6amI/2OpmbiBoW12vngr5NW2jCJVZSBEz+H5KfUJZ2M7sDjk0U6GOOdCWFVScShte29o9NezJ53TPw==} + '@radix-ui/react-popper@1.2.4': + resolution: {integrity: sha512-3p2Rgm/a1cK0r/UVkx5F/K9v/EplfjAeIFCGOPYPO4lZ0jtg4iSQXt/YGTSLWaf4x7NG6Z4+uKFcylcTZjeqDA==} peerDependencies: - rollup: ^1.20.0 || ^2.0.0 - dependencies: - '@rollup/pluginutils': 3.1.0(rollup@1.32.1) - rollup: 1.32.1 - dev: true + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true - /@rollup/plugin-json@5.0.2(rollup@3.20.2): - resolution: {integrity: sha512-D1CoOT2wPvadWLhVcmpkDnesTzjhNIQRWLsc3fA49IFOP2Y84cFOOJ+nKGYedvXHKUsPeq07HR4hXpBBr+CHlA==} - engines: {node: '>=14.0.0'} + '@radix-ui/react-portal@1.1.6': + resolution: {integrity: sha512-XmsIl2z1n/TsYFLIdYam2rmFwf9OC/Sh2avkbmVMDuBZIe7hSpM0cYnWPAo7nHOVx8zTuwDZGByfcqLdnzp3Vw==} peerDependencies: - rollup: ^1.20.0||^2.0.0||^3.0.0 + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: - rollup: + '@types/react': + optional: true + '@types/react-dom': optional: true - dependencies: - '@rollup/pluginutils': 5.0.2(rollup@3.20.2) - rollup: 3.20.2 - dev: true - /@rollup/plugin-multi-entry@6.0.0(rollup@3.20.2): - resolution: {integrity: sha512-msBgVncGQwh/ahxeP/rc8MXVZNBOjoVCsBuDk6uqyFzDv/SZN7jksfAsu6DJ2w4r5PaBX3/OXOjVPeCxya2waA==} - engines: {node: '>=14.0.0'} + '@radix-ui/react-presence@1.1.3': + resolution: {integrity: sha512-IrVLIhskYhH3nLvtcBLQFZr61tBG7wx7O3kEmdzcYwRGAEBmBicGGL7ATzNgruYJ3xBTbuzEEq9OXJM3PAX3tA==} peerDependencies: - rollup: ^1.20.0||^2.0.0||^3.0.0 + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: - rollup: + '@types/react': + optional: true + '@types/react-dom': optional: true - dependencies: - '@rollup/plugin-virtual': 3.0.1(rollup@3.20.2) - matched: 5.0.1 - rollup: 3.20.2 - dev: true - /@rollup/plugin-node-resolve@11.2.1(rollup@2.79.1): - resolution: {integrity: sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==} - engines: {node: '>= 10.0.0'} + '@radix-ui/react-primitive@2.1.0': + resolution: {integrity: sha512-/J/FhLdK0zVcILOwt5g+dH4KnkonCtkVJsa2G6JmvbbtZfBEI1gMsO3QMjseL4F/SwfAMt1Vc/0XKYKq+xJ1sw==} peerDependencies: - rollup: ^1.20.0||^2.0.0 - dependencies: - '@rollup/pluginutils': 3.1.0(rollup@2.79.1) - '@types/resolve': 1.17.1 - builtin-modules: 3.3.0 - deepmerge: 4.3.1 - is-module: 1.0.0 - resolve: 1.22.2 - rollup: 2.79.1 - dev: false + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true - /@rollup/plugin-node-resolve@15.0.2(rollup@3.20.2): - resolution: {integrity: sha512-Y35fRGUjC3FaurG722uhUuG8YHOJRJQbI6/CkbRkdPotSpDj9NtIN85z1zrcyDcCQIW4qp5mgG72U+gJ0TAFEg==} - engines: {node: '>=14.0.0'} + '@radix-ui/react-roving-focus@1.1.6': + resolution: {integrity: sha512-D2ReXCuIueKf5L2f1ks/wTj3bWck1SvK1pjLmEHPbwksS1nOHBsvgY0b9Hypt81FczqBqSyLHQxn/vbsQ0gDHw==} peerDependencies: - rollup: ^2.78.0||^3.0.0 + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: - rollup: + '@types/react': + optional: true + '@types/react-dom': optional: true - dependencies: - '@rollup/pluginutils': 5.0.2(rollup@3.20.2) - '@types/resolve': 1.20.2 - deepmerge: 4.3.1 - is-builtin-module: 3.2.1 - is-module: 1.0.0 - resolve: 1.22.2 - rollup: 3.20.2 - dev: true - /@rollup/plugin-node-resolve@6.1.0(rollup@1.32.1): - resolution: {integrity: sha512-Cv7PDIvxdE40SWilY5WgZpqfIUEaDxFxs89zCAHjqyRwlTSuql4M5hjIuc5QYJkOH0/vyiyNXKD72O+LhRipGA==} - engines: {node: '>= 8.0.0'} + '@radix-ui/react-slot@1.2.0': + resolution: {integrity: sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==} peerDependencies: - rollup: ^1.20.0 - dependencies: - '@rollup/pluginutils': 3.1.0(rollup@1.32.1) - '@types/resolve': 0.0.8 - builtin-modules: 3.3.0 - is-module: 1.0.0 - resolve: 1.22.2 - rollup: 1.32.1 - dev: true + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true - /@rollup/plugin-replace@2.4.2(rollup@2.79.1): - resolution: {integrity: sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==} + '@radix-ui/react-tabs@1.1.7': + resolution: {integrity: sha512-sawt4HkD+6haVGjYOC3BMIiCumBpqTK6o407n6zN/6yReed2EN7bXyykNrpqg+xCfudpBUZg7Y2cJBd/x/iybA==} peerDependencies: - rollup: ^1.20.0 || ^2.0.0 - dependencies: - '@rollup/pluginutils': 3.1.0(rollup@2.79.1) - magic-string: 0.25.9 - rollup: 2.79.1 - dev: false + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true - /@rollup/plugin-replace@5.0.2(rollup@3.20.2): - resolution: {integrity: sha512-M9YXNekv/C/iHHK+cvORzfRYfPbq0RDD8r0G+bMiTXjNGKulPnCT9O3Ss46WfhI6ZOCgApOP7xAdmCQJ+U2LAA==} - engines: {node: '>=14.0.0'} + '@radix-ui/react-toggle-group@1.1.6': + resolution: {integrity: sha512-XOBq9VqC+mIn5hzjGdJLhQbvQeiOpV5ExNE6qMQQPvFsCT44QUcxFzYytTWVoyWg9XKfgrleKmTeEyu6aoTPhg==} peerDependencies: - rollup: ^1.20.0||^2.0.0||^3.0.0 + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: - rollup: + '@types/react': + optional: true + '@types/react-dom': optional: true - dependencies: - '@rollup/pluginutils': 5.0.2(rollup@3.20.2) - magic-string: 0.27.0 - rollup: 3.20.2 - dev: true - /@rollup/plugin-terser@0.1.0(rollup@3.20.2): - resolution: {integrity: sha512-N2KK+qUfHX2hBzVzM41UWGLrEmcjVC37spC8R3c9mt3oEDFKh3N2e12/lLp9aVSt86veR0TQiCNQXrm8C6aiUQ==} - engines: {node: '>=14.0.0'} + '@radix-ui/react-toggle@1.1.6': + resolution: {integrity: sha512-3SeJxKeO3TO1zVw1Nl++Cp0krYk6zHDHMCUXXVkosIzl6Nxcvb07EerQpyD2wXQSJ5RZajrYAmPaydU8Hk1IyQ==} peerDependencies: - rollup: ^2.x || ^3.x + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: - rollup: + '@types/react': + optional: true + '@types/react-dom': optional: true - dependencies: - rollup: 3.20.2 - terser: 5.16.9 - dev: true - /@rollup/plugin-typescript@9.0.2(rollup@3.20.2)(tslib@2.5.0)(typescript@4.9.5): - resolution: {integrity: sha512-/sS93vmHUMjzDUsl5scNQr1mUlNE1QjBBvOhmRwJCH8k2RRhDIm3c977B3wdu3t3Ap17W6dDeXP3hj1P1Un1bA==} - engines: {node: '>=14.0.0'} + '@radix-ui/react-tooltip@1.2.3': + resolution: {integrity: sha512-0KX7jUYFA02np01Y11NWkk6Ip6TqMNmD4ijLelYAzeIndl2aVeltjJFJ2gwjNa1P8U/dgjQ+8cr9Y3Ni+ZNoRA==} peerDependencies: - rollup: ^2.14.0||^3.0.0 - tslib: '*' - typescript: '>=3.7.0' + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: - rollup: + '@types/react': optional: true - tslib: + '@types/react-dom': optional: true - dependencies: - '@rollup/pluginutils': 5.0.2(rollup@3.20.2) - resolve: 1.22.2 - rollup: 3.20.2 - tslib: 2.5.0 - typescript: 4.9.5 - dev: true - /@rollup/plugin-virtual@3.0.1(rollup@3.20.2): - resolution: {integrity: sha512-fK8O0IL5+q+GrsMLuACVNk2x21g3yaw+sG2qn16SnUd3IlBsQyvWxLMGHmCmXRMecPjGRSZ/1LmZB4rjQm68og==} - engines: {node: '>=14.0.0'} + '@radix-ui/react-use-callback-ref@1.1.1': + resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==} peerDependencies: - rollup: ^1.20.0||^2.0.0||^3.0.0 + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: - rollup: + '@types/react': optional: true - dependencies: - rollup: 3.20.2 - dev: true - /@rollup/pluginutils@3.1.0(rollup@1.32.1): - resolution: {integrity: sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==} - engines: {node: '>= 8.0.0'} + '@radix-ui/react-use-controllable-state@1.2.2': + resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==} peerDependencies: - rollup: ^1.20.0||^2.0.0 - dependencies: - '@types/estree': 0.0.39 - estree-walker: 1.0.1 - picomatch: 2.3.1 - rollup: 1.32.1 - dev: true + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true - /@rollup/pluginutils@3.1.0(rollup@2.79.1): - resolution: {integrity: sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==} - engines: {node: '>= 8.0.0'} + '@radix-ui/react-use-effect-event@0.0.2': + resolution: {integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==} peerDependencies: - rollup: ^1.20.0||^2.0.0 - dependencies: - '@types/estree': 0.0.39 - estree-walker: 1.0.1 - picomatch: 2.3.1 - rollup: 2.79.1 - dev: false + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true - /@rollup/pluginutils@4.2.1: - resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==} - engines: {node: '>= 8.0.0'} - dependencies: - estree-walker: 2.0.2 - picomatch: 2.3.1 - dev: true + '@radix-ui/react-use-escape-keydown@1.1.1': + resolution: {integrity: sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true - /@rollup/pluginutils@5.0.2(rollup@3.20.2): - resolution: {integrity: sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==} - engines: {node: '>=14.0.0'} + '@radix-ui/react-use-layout-effect@1.1.1': + resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==} peerDependencies: - rollup: ^1.20.0||^2.0.0||^3.0.0 + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: - rollup: + '@types/react': optional: true - dependencies: - '@types/estree': 1.0.0 - estree-walker: 2.0.2 - picomatch: 2.3.1 - rollup: 3.20.2 - dev: true - /@rushstack/eslint-patch@1.2.0: - resolution: {integrity: sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==} + '@radix-ui/react-use-rect@1.1.1': + resolution: {integrity: sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true - /@segment/analytics-core@1.2.3: - resolution: {integrity: sha512-/B4f4Hxmwd9WpEba/ChYkUwhILz5cPhG4Sto03IlLc8vbV7gAOCGH021EKvU3Wv70WlRK6EgJkuDLPnRl2a2aA==} - dependencies: - '@lukeed/uuid': 2.0.1 - dset: 3.1.2 - tslib: 2.5.0 - dev: true + '@radix-ui/react-use-size@1.1.1': + resolution: {integrity: sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true - /@segment/analytics-next@1.51.3: - resolution: {integrity: sha512-c22GDz6rrhliIsgtLQjEcRiZdqb70+0hEyfTI6YpRXZzEXBwdJybO5ZCD7NRlVFHf/qXp1qcjHuQ5xyOGr2lJg==} - dependencies: - '@lukeed/uuid': 2.0.1 - '@segment/analytics-core': 1.2.3 - '@segment/analytics.js-video-plugins': 0.2.1 - '@segment/facade': 3.4.10 - '@segment/tsub': 1.0.1 - dset: 3.1.2 - js-cookie: 3.0.1 - node-fetch: 2.6.9 - spark-md5: 3.0.2 - tslib: 2.5.0 - unfetch: 4.2.0 - transitivePeerDependencies: - - encoding - - supports-color - dev: true + '@radix-ui/react-visually-hidden@1.2.0': + resolution: {integrity: sha512-rQj0aAWOpCdCMRbI6pLQm8r7S2BM3YhTa0SzOYD55k+hJA8oo9J+H+9wLM9oMlZWOX/wJWPTzfDfmZkf7LvCfg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true - /@segment/analytics.js-video-plugins@0.2.1: - resolution: {integrity: sha512-lZwCyEXT4aaHBLNK433okEKdxGAuyrVmop4BpQqQSJuRz0DglPZgd9B/XjiiWs1UyOankg2aNYMN3VcS8t4eSQ==} - dependencies: - unfetch: 3.1.2 - dev: true + '@radix-ui/rect@1.1.1': + resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==} - /@segment/facade@3.4.10: - resolution: {integrity: sha512-xVQBbB/lNvk/u8+ey0kC/+g8pT3l0gCT8O2y9Z+StMMn3KAFAQ9w8xfgef67tJybktOKKU7pQGRPolRM1i1pdA==} - dependencies: - '@segment/isodate-traverse': 1.1.1 - inherits: 2.0.4 - new-date: 1.0.3 - obj-case: 0.2.1 - dev: true + '@rc-component/async-validator@5.0.4': + resolution: {integrity: sha512-qgGdcVIF604M9EqjNF0hbUTz42bz/RDtxWdWuU5EQe3hi7M8ob54B6B35rOsvX5eSvIHIzT9iH1R3n+hk3CGfg==} + engines: {node: '>=14.x'} - /@segment/isodate-traverse@1.1.1: - resolution: {integrity: sha512-+G6e1SgAUkcq0EDMi+SRLfT48TNlLPF3QnSgFGVs0V9F3o3fq/woQ2rHFlW20W0yy5NnCUH0QGU3Am2rZy/E3w==} - dependencies: - '@segment/isodate': 1.0.3 - dev: true + '@rc-component/color-picker@2.0.1': + resolution: {integrity: sha512-WcZYwAThV/b2GISQ8F+7650r5ZZJ043E57aVBFkQ+kSY4C6wdofXgB0hBx+GPGpIU0Z81eETNoDUJMr7oy/P8Q==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' - /@segment/isodate@1.0.3: - resolution: {integrity: sha512-BtanDuvJqnACFkeeYje7pWULVv8RgZaqKHWwGFnL/g/TH/CcZjkIVTfGDp/MAxmilYHUkrX70SqwnYSTNEaN7A==} - dev: true + '@rc-component/context@1.4.0': + resolution: {integrity: sha512-kFcNxg9oLRMoL3qki0OMxK+7g5mypjgaaJp/pkOis/6rVxma9nJBF/8kCIuTYHUQNr0ii7MxqE33wirPZLJQ2w==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' - /@segment/tsub@1.0.1: - resolution: {integrity: sha512-rUpvlj/rRfOolk5rjwyrsbl0qzGLsaYgFNEiOSrwrWDryDPq1ZGdo+3Eb+E8+EC0yZOAO4F1DjJfLtaSifpx7w==} - hasBin: true - dependencies: - '@stdlib/math-base-special-ldexp': 0.0.5 - dlv: 1.1.3 - dset: 3.1.2 - tiny-hashes: 1.0.1 - transitivePeerDependencies: - - supports-color - dev: true + '@rc-component/mini-decimal@1.1.0': + resolution: {integrity: sha512-jS4E7T9Li2GuYwI6PyiVXmxTiM6b07rlD9Ge8uGZSCz3WlzcG5ZK7g5bbuKNeZ9pgUuPK/5guV781ujdVpm4HQ==} + engines: {node: '>=8.x'} - /@sensejs/kafkajs-zstd-support@0.9.2(kafkajs@2.2.4): - resolution: {integrity: sha512-mzwpWC93EpNUjGYG/0whqTa+wYeMaY/OJ8Z/8FhP/YS0czwZZH8dNNKGYPIXo6YzkHuucULo1F3pNIE3L0HxXQ==} + '@rc-component/mutate-observer@1.1.0': + resolution: {integrity: sha512-QjrOsDXQusNwGZPf4/qRQasg7UFEj06XiCJ8iuiq/Io7CrHrgVi6Uuetw60WAMG1799v+aM8kyc+1L/GBbHSlw==} + engines: {node: '>=8.x'} peerDependencies: - kafkajs: ^1.16.0 || ^2.0.2 - dependencies: - kafkajs: 2.2.4 - tslib: 2.5.0 - zstd-napi: 0.0.6 - dev: false + react: '>=16.9.0' + react-dom: '>=16.9.0' - /@sinclair/typebox@0.24.51: - resolution: {integrity: sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==} - - /@sinclair/typebox@0.25.24: - resolution: {integrity: sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==} - dev: true + '@rc-component/portal@1.1.2': + resolution: {integrity: sha512-6f813C0IsasTZms08kfA8kPAGxbbkYToa8ALaiDIGGECU4i9hj8Plgbx0sNJDrey3EtHO30hmdaxtT0138xZcg==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' - /@sindresorhus/is@4.6.0: - resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} - engines: {node: '>=10'} - dev: true + '@rc-component/qrcode@1.0.0': + resolution: {integrity: sha512-L+rZ4HXP2sJ1gHMGHjsg9jlYBX/SLN2D6OxP9Zn3qgtpMWtO2vUfxVFwiogHpAIqs54FnALxraUy/BCO1yRIgg==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' - /@sinonjs/commons@1.8.6: - resolution: {integrity: sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==} - dependencies: - type-detect: 4.0.8 + '@rc-component/tour@1.15.1': + resolution: {integrity: sha512-Tr2t7J1DKZUpfJuDZWHxyxWpfmj8EZrqSgyMZ+BCdvKZ6r1UDsfU46M/iWAAFBy961Ssfom2kv5f3UcjIL2CmQ==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' - /@sinonjs/commons@2.0.0: - resolution: {integrity: sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==} - dependencies: - type-detect: 4.0.8 - dev: true + '@rc-component/trigger@2.2.5': + resolution: {integrity: sha512-F1EJ4KjFpGAHAjuKvOyZB/6IZDkVx0bHl0M4fQM5wXcmm7lgTgVSSnR3bXwdmS6jOJGHOqfDxIJW3WUvwMIXhQ==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' - /@sinonjs/fake-timers@10.0.2: - resolution: {integrity: sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw==} - dependencies: - '@sinonjs/commons': 2.0.0 - dev: true + '@react-email/body@0.0.11': + resolution: {integrity: sha512-ZSD2SxVSgUjHGrB0Wi+4tu3MEpB4fYSbezsFNEJk2xCWDBkFiOeEsjTmR5dvi+CxTK691hQTQlHv0XWuP7ENTg==} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc - /@sinonjs/fake-timers@8.1.0: - resolution: {integrity: sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==} - dependencies: - '@sinonjs/commons': 1.8.6 - dev: false + '@react-email/button@0.2.0': + resolution: {integrity: sha512-8i+v6cMxr2emz4ihCrRiYJPp2/sdYsNNsBzXStlcA+/B9Umpm5Jj3WJKYpgTPM+aeyiqlG/MMI1AucnBm4f1oQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc - /@sinonjs/fake-timers@9.1.2: - resolution: {integrity: sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==} - dependencies: - '@sinonjs/commons': 1.8.6 - dev: true + '@react-email/code-block@0.1.0': + resolution: {integrity: sha512-jSpHFsgqnQXxDIssE4gvmdtFncaFQz5D6e22BnVjcCPk/udK+0A9jRwGFEG8JD2si9ZXBmU4WsuqQEczuZn4ww==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc - /@smithy/protocol-http@1.0.1: - resolution: {integrity: sha512-9OrEn0WfOVtBNYJUjUAn9AOiJ4lzERCJJ/JeZs8E6yajTGxBaFRxUnNBHiNqoDJVg076hY36UmEnPx7xXrvUSg==} - engines: {node: '>=14.0.0'} - dependencies: - '@smithy/types': 1.0.0 - tslib: 2.5.0 - dev: false + '@react-email/code-inline@0.0.5': + resolution: {integrity: sha512-MmAsOzdJpzsnY2cZoPHFPk6uDO/Ncpb4Kh1hAt9UZc1xOW3fIzpe1Pi9y9p6wwUmpaeeDalJxAxH6/fnTquinA==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc - /@smithy/types@1.0.0: - resolution: {integrity: sha512-kc1m5wPBHQCTixwuaOh9vnak/iJm21DrSf9UK6yDE5S3mQQ4u11pqAUiKWnlrZnYkeLfAI9UEHj9OaMT1v5Umg==} - engines: {node: '>=14.0.0'} - dependencies: - tslib: 2.5.0 - dev: false + '@react-email/column@0.0.13': + resolution: {integrity: sha512-Lqq17l7ShzJG/d3b1w/+lVO+gp2FM05ZUo/nW0rjxB8xBICXOVv6PqjDnn3FXKssvhO5qAV20lHM6S+spRhEwQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc - /@stdlib/array-float32@0.0.6: - resolution: {integrity: sha512-QgKT5UaE92Rv7cxfn7wBKZAlwFFHPla8eXsMFsTGt5BiL4yUy36lwinPUh4hzybZ11rw1vifS3VAPuk6JP413Q==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/assert-has-float32array-support': 0.0.8 - transitivePeerDependencies: - - supports-color - dev: true + '@react-email/components@0.3.1': + resolution: {integrity: sha512-FqcyGaUpJJu8zfYGSS+qaSy7Zc2BFAswBc/LvHeSV4iTQMZMD8Dy7aS/NvP1SQMg5vjsO1aMpGFdrD4NBY58dw==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc - /@stdlib/array-float64@0.0.6: - resolution: {integrity: sha512-oE8y4a84LyBF1goX5//sU1mOjet8gLI0/6wucZcjg+j/yMmNV1xFu84Az9GOGmFSE6Ze6lirGOhfBeEWNNNaJg==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/assert-has-float64array-support': 0.0.8 - dev: true + '@react-email/container@0.0.15': + resolution: {integrity: sha512-Qo2IQo0ru2kZq47REmHW3iXjAQaKu4tpeq/M8m1zHIVwKduL2vYOBQWbC2oDnMtWPmkBjej6XxgtZByxM6cCFg==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc - /@stdlib/array-uint16@0.0.6: - resolution: {integrity: sha512-/A8Tr0CqJ4XScIDRYQawosko8ha1Uy+50wsTgJhjUtXDpPRp7aUjmxvYkbe7Rm+ImYYbDQVix/uCiPAFQ8ed4Q==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/assert-has-uint16array-support': 0.0.8 - dev: true + '@react-email/font@0.0.9': + resolution: {integrity: sha512-4zjq23oT9APXkerqeslPH3OZWuh5X4crHK6nx82mVHV2SrLba8+8dPEnWbaACWTNjOCbcLIzaC9unk7Wq2MIXw==} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc - /@stdlib/array-uint32@0.0.6: - resolution: {integrity: sha512-2hFPK1Fg7obYPZWlGDjW9keiIB6lXaM9dKmJubg/ergLQCsJQJZpYsG6mMAfTJi4NT1UF4jTmgvyKD+yf0D9cA==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/assert-has-uint32array-support': 0.0.8 - dev: true + '@react-email/head@0.0.12': + resolution: {integrity: sha512-X2Ii6dDFMF+D4niNwMAHbTkeCjlYYnMsd7edXOsi0JByxt9wNyZ9EnhFiBoQdqkE+SMDcu8TlNNttMrf5sJeMA==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc - /@stdlib/array-uint8@0.0.7: - resolution: {integrity: sha512-qYJQQfGKIcky6TzHFIGczZYTuVlut7oO+V8qUBs7BJC9TwikVnnOmb3hY3jToY4xaoi5p9OvgdJKPInhyIhzFg==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/assert-has-uint8array-support': 0.0.8 - dev: true + '@react-email/heading@0.0.15': + resolution: {integrity: sha512-xF2GqsvBrp/HbRHWEfOgSfRFX+Q8I5KBEIG5+Lv3Vb2R/NYr0s8A5JhHHGf2pWBMJdbP4B2WHgj/VUrhy8dkIg==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc - /@stdlib/assert-has-float32array-support@0.0.8: - resolution: {integrity: sha512-Yrg7K6rBqwCzDWZ5bN0VWLS5dNUWcoSfUeU49vTERdUmZID06J069CDc07UUl8vfQWhFgBWGocH3rrpKm1hi9w==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - hasBin: true - dependencies: - '@stdlib/assert-is-float32array': 0.0.8 - '@stdlib/cli-ctor': 0.0.3 - '@stdlib/constants-float64-pinf': 0.0.8 - '@stdlib/fs-read-file': 0.0.8 - transitivePeerDependencies: - - supports-color - dev: true + '@react-email/hr@0.0.11': + resolution: {integrity: sha512-S1gZHVhwOsd1Iad5IFhpfICwNPMGPJidG/Uysy1AwmspyoAP5a4Iw3OWEpINFdgh9MHladbxcLKO2AJO+cA9Lw==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc - /@stdlib/assert-has-float64array-support@0.0.8: - resolution: {integrity: sha512-UVQcoeWqgMw9b8PnAmm/sgzFnuWkZcNhJoi7xyMjbiDV/SP1qLCrvi06mq86cqS3QOCma1fEayJdwgteoXyyuw==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - hasBin: true - dependencies: - '@stdlib/assert-is-float64array': 0.0.8 - '@stdlib/cli-ctor': 0.0.3 - '@stdlib/fs-read-file': 0.0.8 - dev: true + '@react-email/html@0.0.11': + resolution: {integrity: sha512-qJhbOQy5VW5qzU74AimjAR9FRFQfrMa7dn4gkEXKMB/S9xZN8e1yC1uA9C15jkXI/PzmJ0muDIWmFwatm5/+VA==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc - /@stdlib/assert-has-node-buffer-support@0.0.8: - resolution: {integrity: sha512-fgI+hW4Yg4ciiv4xVKH+1rzdV7e5+6UKgMnFbc1XDXHcxLub3vOr8+H6eDECdAIfgYNA7X0Dxa/DgvX9dwDTAQ==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - hasBin: true - dependencies: - '@stdlib/assert-is-buffer': 0.0.8 - '@stdlib/cli-ctor': 0.0.3 - '@stdlib/fs-read-file': 0.0.8 - dev: true - - /@stdlib/assert-has-own-property@0.0.7: - resolution: {integrity: sha512-3YHwSWiUqGlTLSwxAWxrqaD1PkgcJniGyotJeIt5X0tSNmSW0/c9RWroCImTUUB3zBkyBJ79MyU9Nf4Qgm59fQ==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dev: true - - /@stdlib/assert-has-symbol-support@0.0.8: - resolution: {integrity: sha512-PoQ9rk8DgDCuBEkOIzGGQmSnjtcdagnUIviaP5YskB45/TJHXseh4NASWME8FV77WFW9v/Wt1MzKFKMzpDFu4Q==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - hasBin: true - dependencies: - '@stdlib/cli-ctor': 0.0.3 - '@stdlib/fs-read-file': 0.0.8 - dev: true + '@react-email/img@0.0.11': + resolution: {integrity: sha512-aGc8Y6U5C3igoMaqAJKsCpkbm1XjguQ09Acd+YcTKwjnC2+0w3yGUJkjWB2vTx4tN8dCqQCXO8FmdJpMfOA9EQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc - /@stdlib/assert-has-tostringtag-support@0.0.9: - resolution: {integrity: sha512-UTsqdkrnQ7eufuH5BeyWOJL3ska3u5nvDWKqw3onNNZ2mvdgkfoFD7wHutVGzAA2rkTsSJAMBHVwWLsm5SbKgw==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - hasBin: true - dependencies: - '@stdlib/assert-has-symbol-support': 0.0.8 - '@stdlib/cli-ctor': 0.0.3 - '@stdlib/fs-read-file': 0.0.8 - dev: true + '@react-email/link@0.0.12': + resolution: {integrity: sha512-vF+xxQk2fGS1CN7UPQDbzvcBGfffr+GjTPNiWM38fhBfsLv6A/YUfaqxWlmL7zLzVmo0K2cvvV9wxlSyNba1aQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc - /@stdlib/assert-has-uint16array-support@0.0.8: - resolution: {integrity: sha512-vqFDn30YrtzD+BWnVqFhB130g3cUl2w5AdOxhIkRkXCDYAM5v7YwdNMJEON+D4jI8YB4D5pEYjqKweYaCq4nyg==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - hasBin: true - dependencies: - '@stdlib/assert-is-uint16array': 0.0.8 - '@stdlib/cli-ctor': 0.0.3 - '@stdlib/constants-uint16-max': 0.0.7 - '@stdlib/fs-read-file': 0.0.8 - dev: true + '@react-email/markdown@0.0.15': + resolution: {integrity: sha512-UQA9pVm5sbflgtg3EX3FquUP4aMBzmLReLbGJ6DZQZnAskBF36aI56cRykDq1o+1jT+CKIK1CducPYziaXliag==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc - /@stdlib/assert-has-uint32array-support@0.0.8: - resolution: {integrity: sha512-tJtKuiFKwFSQQUfRXEReOVGXtfdo6+xlshSfwwNWXL1WPP2LrceoiUoQk7zMCMT6VdbXgGH92LDjVcPmSbH4Xw==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - hasBin: true - dependencies: - '@stdlib/assert-is-uint32array': 0.0.8 - '@stdlib/cli-ctor': 0.0.3 - '@stdlib/constants-uint32-max': 0.0.7 - '@stdlib/fs-read-file': 0.0.8 - dev: true + '@react-email/preview-server@4.1.3': + resolution: {integrity: sha512-BsxIKYvEeH8CyysHKj1D5t0XLd8vVoz7E2pdD7xaQNZytpuzZEqbnwgtWoaVRHj4dDimtMrb42jYC+9nCjZsOg==} - /@stdlib/assert-has-uint8array-support@0.0.8: - resolution: {integrity: sha512-ie4vGTbAS/5Py+LLjoSQi0nwtYBp+WKk20cMYCzilT0rCsBI/oez0RqHrkYYpmt4WaJL4eJqC+/vfQ5NsI7F5w==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - hasBin: true - dependencies: - '@stdlib/assert-is-uint8array': 0.0.8 - '@stdlib/cli-ctor': 0.0.3 - '@stdlib/constants-uint8-max': 0.0.7 - '@stdlib/fs-read-file': 0.0.8 - dev: true + '@react-email/preview@0.0.13': + resolution: {integrity: sha512-F7j9FJ0JN/A4d7yr+aw28p4uX7VLWs7hTHtLo7WRyw4G+Lit6Zucq4UWKRxJC8lpsUdzVmG7aBJnKOT+urqs/w==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc - /@stdlib/assert-is-array@0.0.7: - resolution: {integrity: sha512-/o6KclsGkNcZ5hiROarsD9XUs6xQMb4lTwF6O71UHbKWTtomEF/jD0rxLvlvj0BiCxfKrReddEYd2CnhUyskMA==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/utils-native-class': 0.0.8 - dev: true + '@react-email/render@0.0.17': + resolution: {integrity: sha512-xBQ+/73+WsGuXKY7r1U73zMBNV28xdV0cp9cFjhNYipBReDHhV97IpA6v7Hl0dDtDzt+yS/72dY5vYXrF1v8NA==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.2.0 + react-dom: ^18.2.0 - /@stdlib/assert-is-big-endian@0.0.7: - resolution: {integrity: sha512-BvutsX84F76YxaSIeS5ZQTl536lz+f+P7ew68T1jlFqxBhr4v7JVYFmuf24U040YuK1jwZ2sAq+bPh6T09apwQ==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - hasBin: true - dependencies: - '@stdlib/array-uint16': 0.0.6 - '@stdlib/array-uint8': 0.0.7 - '@stdlib/cli-ctor': 0.0.3 - '@stdlib/fs-read-file': 0.0.8 - dev: true - - /@stdlib/assert-is-boolean@0.0.8: - resolution: {integrity: sha512-PRCpslMXSYqFMz1Yh4dG2K/WzqxTCtlKbgJQD2cIkAtXux4JbYiXCtepuoV7l4Wv1rm0a1eU8EqNPgnOmWajGw==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/assert-has-tostringtag-support': 0.0.9 - '@stdlib/utils-define-nonenumerable-read-only-property': 0.0.7 - '@stdlib/utils-native-class': 0.0.8 - dev: true - - /@stdlib/assert-is-buffer@0.0.8: - resolution: {integrity: sha512-SYmGwOXkzZVidqUyY1IIx6V6QnSL36v3Lcwj8Rvne/fuW0bU2OomsEBzYCFMvcNgtY71vOvgZ9VfH3OppvV6eA==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/assert-is-object-like': 0.0.8 - dev: true - - /@stdlib/assert-is-float32array@0.0.8: - resolution: {integrity: sha512-Phk0Ze7Vj2/WLv5Wy8Oo7poZIDMSTiTrEnc1t4lBn3Svz2vfBXlvCufi/i5d93vc4IgpkdrOEwfry6nldABjNQ==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/utils-native-class': 0.0.8 - dev: true - - /@stdlib/assert-is-float64array@0.0.8: - resolution: {integrity: sha512-UC0Av36EEYIgqBbCIz1lj9g7qXxL5MqU1UrWun+n91lmxgdJ+Z77fHy75efJbJlXBf6HXhcYXECIsc0u3SzyDQ==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/utils-native-class': 0.0.8 - dev: true - - /@stdlib/assert-is-function@0.0.8: - resolution: {integrity: sha512-M55Dt2njp5tnY8oePdbkKBRIypny+LpCMFZhEjJIxjLE4rA6zSlHs1yRMqD4PmW+Wl9WTeEM1GYO4AQHl1HAjA==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/utils-type-of': 0.0.8 - dev: true - - /@stdlib/assert-is-little-endian@0.0.7: - resolution: {integrity: sha512-SPObC73xXfDXY0dOewXR0LDGN3p18HGzm+4K8azTj6wug0vpRV12eB3hbT28ybzRCa6TAKUjwM/xY7Am5QzIlA==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - hasBin: true - dependencies: - '@stdlib/array-uint16': 0.0.6 - '@stdlib/array-uint8': 0.0.7 - '@stdlib/cli-ctor': 0.0.3 - '@stdlib/fs-read-file': 0.0.8 - dev: true - - /@stdlib/assert-is-number@0.0.7: - resolution: {integrity: sha512-mNV4boY1cUOmoWWfA2CkdEJfXA6YvhcTvwKC0Fzq+HoFFOuTK/scpTd9HanUyN6AGBlWA8IW+cQ1ZwOT3XMqag==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/assert-has-tostringtag-support': 0.0.9 - '@stdlib/number-ctor': 0.0.7 - '@stdlib/utils-define-nonenumerable-read-only-property': 0.0.7 - '@stdlib/utils-native-class': 0.0.8 - dev: true - - /@stdlib/assert-is-object-like@0.0.8: - resolution: {integrity: sha512-pe9selDPYAu/lYTFV5Rj4BStepgbzQCr36b/eC8EGSJh6gMgRXgHVv0R+EbdJ69KNkHvKKRjnWj0A/EmCwW+OA==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/assert-tools-array-function': 0.0.7 - '@stdlib/utils-define-nonenumerable-read-only-property': 0.0.7 - dev: true - - /@stdlib/assert-is-object@0.0.8: - resolution: {integrity: sha512-ooPfXDp9c7w+GSqD2NBaZ/Du1JRJlctv+Abj2vRJDcDPyrnRTb1jmw+AuPgcW7Ca7op39JTbArI+RVHm/FPK+Q==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/assert-is-array': 0.0.7 - dev: true - - /@stdlib/assert-is-plain-object@0.0.7: - resolution: {integrity: sha512-t/CEq2a083ajAgXgSa5tsH8l3kSoEqKRu1qUwniVLFYL4RGv3615CrpJUDQKVtEX5S/OKww5q0Byu3JidJ4C5w==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/assert-has-own-property': 0.0.7 - '@stdlib/assert-is-function': 0.0.8 - '@stdlib/assert-is-object': 0.0.8 - '@stdlib/utils-get-prototype-of': 0.0.7 - '@stdlib/utils-native-class': 0.0.8 - dev: true - - /@stdlib/assert-is-regexp-string@0.0.9: - resolution: {integrity: sha512-FYRJJtH7XwXEf//X6UByUC0Eqd0ZYK5AC8or5g5m5efQrgr2lOaONHyDQ3Scj1A2D6QLIJKZc9XBM4uq5nOPXA==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - hasBin: true - dependencies: - '@stdlib/assert-is-string': 0.0.8 - '@stdlib/cli-ctor': 0.0.3 - '@stdlib/fs-read-file': 0.0.8 - '@stdlib/process-read-stdin': 0.0.7 - '@stdlib/regexp-eol': 0.0.7 - '@stdlib/regexp-regexp': 0.0.8 - '@stdlib/streams-node-stdin': 0.0.7 - dev: true - - /@stdlib/assert-is-regexp@0.0.7: - resolution: {integrity: sha512-ty5qvLiqkDq6AibHlNJe0ZxDJ9Mg896qolmcHb69mzp64vrsORnPPOTzVapAq0bEUZbXoypeijypLPs9sCGBSQ==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/assert-has-tostringtag-support': 0.0.9 - '@stdlib/utils-native-class': 0.0.8 - dev: true - - /@stdlib/assert-is-string@0.0.8: - resolution: {integrity: sha512-Uk+bR4cglGBbY0q7O7HimEJiW/DWnO1tSzr4iAGMxYgf+VM2PMYgI5e0TLy9jOSOzWon3YS39lc63eR3a9KqeQ==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/assert-has-tostringtag-support': 0.0.9 - '@stdlib/utils-define-nonenumerable-read-only-property': 0.0.7 - '@stdlib/utils-native-class': 0.0.8 - dev: true - - /@stdlib/assert-is-uint16array@0.0.8: - resolution: {integrity: sha512-M+qw7au+qglRXcXHjvoUZVLlGt1mPjuKudrVRto6KL4+tDsP2j+A89NDP3Fz8/XIUD+5jhj+65EOKHSMvDYnng==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/utils-native-class': 0.0.8 - dev: true - - /@stdlib/assert-is-uint32array@0.0.8: - resolution: {integrity: sha512-cnZi2DicYcplMnkJ3dBxBVKsRNFjzoGpmG9A6jXq4KH5rFl52SezGAXSVY9o5ZV7bQGaF5JLyCLp6n9Y74hFGg==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/utils-native-class': 0.0.8 - dev: true - - /@stdlib/assert-is-uint8array@0.0.8: - resolution: {integrity: sha512-8cqpDQtjnJAuVtRkNAktn45ixq0JHaGJxVsSiK79k7GRggvMI6QsbzO6OvcLnZ/LimD42FmgbLd13Yc2esDmZw==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/utils-native-class': 0.0.8 - dev: true - - /@stdlib/assert-tools-array-function@0.0.7: - resolution: {integrity: sha512-3lqkaCIBMSJ/IBHHk4NcCnk2NYU52tmwTYbbqhAmv7vim8rZPNmGfj3oWkzrCsyCsyTF7ooD+In2x+qTmUbCtQ==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/assert-is-array': 0.0.7 - dev: true - - /@stdlib/buffer-ctor@0.0.7: - resolution: {integrity: sha512-4IyTSGijKUQ8+DYRaKnepf9spvKLZ+nrmZ+JrRcB3FrdTX/l9JDpggcUcC/Fe+A4KIZOnClfxLn6zfIlkCZHNA==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/assert-has-node-buffer-support': 0.0.8 - dev: true - - /@stdlib/buffer-from-string@0.0.8: - resolution: {integrity: sha512-Dws5ZbK2M9l4Bkn/ODHFm3lNZ8tWko+NYXqGS/UH/RIQv3PGp+1tXFUSvjwjDneM6ppjQVExzVedUH1ftABs9A==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/assert-is-function': 0.0.8 - '@stdlib/assert-is-string': 0.0.8 - '@stdlib/buffer-ctor': 0.0.7 - '@stdlib/string-format': 0.0.3 - dev: true - - /@stdlib/cli-ctor@0.0.3: - resolution: {integrity: sha512-0zCuZnzFyxj66GoF8AyIOhTX5/mgGczFvr6T9h4mXwegMZp8jBC/ZkOGMwmp+ODLBTvlcnnDNpNFkDDyR6/c2g==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/utils-define-nonenumerable-read-only-property': 0.0.7 - '@stdlib/utils-noop': 0.0.14 - minimist: 1.2.8 - dev: true + '@react-email/render@1.1.3': + resolution: {integrity: sha512-TjjF1tdTmOqYEIWWg9wMx5q9JbQRbWmnG7owQbSGEHkNfc/c/vBu7hjfrki907lgQEAkYac9KPTyIjOKhvhJCg==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^18.0 || ^19.0 || ^19.0.0-rc - /@stdlib/complex-float32@0.0.7: - resolution: {integrity: sha512-POCtQcBZnPm4IrFmTujSaprR1fcOFr/MRw2Mt7INF4oed6b1nzeG647K+2tk1m4mMrMPiuXCdvwJod4kJ0SXxQ==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/assert-is-number': 0.0.7 - '@stdlib/number-float64-base-to-float32': 0.0.7 - '@stdlib/utils-define-nonenumerable-read-only-property': 0.0.7 - '@stdlib/utils-define-property': 0.0.9 - '@stdlib/utils-library-manifest': 0.0.8 - transitivePeerDependencies: - - supports-color - dev: true + '@react-email/row@0.0.12': + resolution: {integrity: sha512-HkCdnEjvK3o+n0y0tZKXYhIXUNPDx+2vq1dJTmqappVHXS5tXS6W5JOPZr5j+eoZ8gY3PShI2LWj5rWF7ZEtIQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc - /@stdlib/complex-float64@0.0.8: - resolution: {integrity: sha512-lUJwsXtGEziOWAqCcnKnZT4fcVoRsl6t6ECaCJX45Z7lAc70yJLiwUieLWS5UXmyoADHuZyUXkxtI4oClfpnaw==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/assert-is-number': 0.0.7 - '@stdlib/complex-float32': 0.0.7 - '@stdlib/utils-define-nonenumerable-read-only-property': 0.0.7 - '@stdlib/utils-define-property': 0.0.9 - '@stdlib/utils-library-manifest': 0.0.8 - transitivePeerDependencies: - - supports-color - dev: true + '@react-email/section@0.0.16': + resolution: {integrity: sha512-FjqF9xQ8FoeUZYKSdt8sMIKvoT9XF8BrzhT3xiFKdEMwYNbsDflcjfErJe3jb7Wj/es/lKTbV5QR1dnLzGpL3w==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc - /@stdlib/complex-reim@0.0.6: - resolution: {integrity: sha512-28WXfPSIFMtHb0YgdatkGS4yxX5sPYea5MiNgqPv3E78+tFcg8JJG52NQ/MviWP2wsN9aBQAoCPeu8kXxSPdzA==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/array-float64': 0.0.6 - '@stdlib/complex-float64': 0.0.8 - '@stdlib/types': 0.0.14 - '@stdlib/utils-library-manifest': 0.0.8 - transitivePeerDependencies: - - supports-color - dev: true + '@react-email/tailwind@1.2.1': + resolution: {integrity: sha512-SmVyDuNQLJwO3wHEe/snSTaRhf/Exldy5DQU/RyPjcSPC0EuXXYwFlBr16br8jJSxkZA/fL91AxKL7HbbWp0Rw==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc - /@stdlib/complex-reimf@0.0.1: - resolution: {integrity: sha512-P9zu05ZW2i68Oppp3oHelP7Tk0D7tGBL0hGl1skJppr2vY9LltuNbeYI3C96tQe/7Enw/5GyAWgxoQI4cWccQA==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/array-float32': 0.0.6 - '@stdlib/complex-float32': 0.0.7 - '@stdlib/types': 0.0.14 - '@stdlib/utils-library-manifest': 0.0.8 - transitivePeerDependencies: - - supports-color - dev: true + '@react-email/text@0.1.5': + resolution: {integrity: sha512-o5PNHFSE085VMXayxH+SJ1LSOtGsTv+RpNKnTiJDrJUwoBu77G3PlKOsZZQHCNyD28WsQpl9v2WcJLbQudqwPg==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc - /@stdlib/constants-float64-exponent-bias@0.0.8: - resolution: {integrity: sha512-IzBJQw9hYgWCki7VoC/zJxEA76Nmf8hmY+VkOWnJ8IyfgTXClgY8tfDGS1cc4l/hCOEllxGp9FRvVdn24A5tKQ==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/utils-library-manifest': 0.0.8 - transitivePeerDependencies: - - supports-color - dev: true + '@remix-run/router@1.20.0': + resolution: {integrity: sha512-mUnk8rPJBI9loFDZ+YzPGdeniYK+FTmRD1TMCz7ev2SNIozyKKpnGgsxO34u6Z4z/t0ITuu7voi/AshfsGsgFg==} + engines: {node: '>=14.0.0'} - /@stdlib/constants-float64-high-word-abs-mask@0.0.1: - resolution: {integrity: sha512-1vy8SUyMHFBwqUUVaZFA7r4/E3cMMRKSwsaa/EZ15w7Kmc01W/ZmaaTLevRcIdACcNgK+8i8813c8H7LScXNcQ==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/utils-library-manifest': 0.0.8 - transitivePeerDependencies: - - supports-color - dev: true + '@rjsf/antd@5.22.4': + resolution: {integrity: sha512-+8y8Z0WdKQBNFI2dk/St6rS7kLIGn45YcJTd54UXSUcGwPTDVGW4OU+nhVn+NZns8JnZax3qGi2kbOiva8UiMg==} + engines: {node: '>=14'} + peerDependencies: + '@ant-design/icons': ^4.0.0 || ^5.0.0 + '@rjsf/core': ^5.22.x + '@rjsf/utils': ^5.22.x + antd: ^4.24.0 || ^5.8.5 + dayjs: ^1.8.0 + react: ^16.14.0 || >=17 - /@stdlib/constants-float64-high-word-exponent-mask@0.0.8: - resolution: {integrity: sha512-z28/EQERc0VG7N36bqdvtrRWjFc8600PKkwvl/nqx6TpKAzMXNw55BS1xT4C28Sa9Z7uBWeUj3UbIFedbkoyMw==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/utils-library-manifest': 0.0.8 - transitivePeerDependencies: - - supports-color - dev: true + '@rjsf/core@5.22.4': + resolution: {integrity: sha512-0QjAVPXDi/19jR/E44ULDzOkvC4Px5zcZhpGtBFNWNWWmb9UgyjPuvJYga2obzHU46P+5maLvUQEZVAeFwDuqQ==} + engines: {node: '>=14'} + peerDependencies: + '@rjsf/utils': ^5.22.x + react: ^16.14.0 || >=17 - /@stdlib/constants-float64-high-word-sign-mask@0.0.1: - resolution: {integrity: sha512-hmTr5caK1lh1m0eyaQqt2Vt3y+eEdAx57ndbADEbXhxC9qSGd0b4bLSzt/Xp4MYBYdQkHAE/BlkgUiRThswhCg==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/utils-library-manifest': 0.0.8 - transitivePeerDependencies: - - supports-color - dev: true + '@rjsf/utils@5.22.4': + resolution: {integrity: sha512-yQTdz5ryiYy258xCVthVPQ3DeaMzrRNrFcO8xvGHorp0/bLUxdTZ0iidXop49m3y8SaxxTZd398ZKWg2cqxiIA==} + engines: {node: '>=14'} + peerDependencies: + react: ^16.14.0 || >=17 - /@stdlib/constants-float64-max-base2-exponent-subnormal@0.0.8: - resolution: {integrity: sha512-YGBZykSiXFebznnJfWFDwhho2Q9xhUWOL+X0lZJ4ItfTTo40W6VHAyNYz98tT/gJECFype0seNzzo1nUxCE7jQ==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/utils-library-manifest': 0.0.8 - transitivePeerDependencies: - - supports-color - dev: true + '@rjsf/validator-ajv8@5.22.4': + resolution: {integrity: sha512-HIMmJpSY9iyN325W/3/l/2Pbt5BAPd9pf0nO+KZuW5dxE0WUThwjIsa104gFJUgig5M3RuKbelX565Qmvfa87A==} + engines: {node: '>=14'} + peerDependencies: + '@rjsf/utils': ^5.22.x - /@stdlib/constants-float64-max-base2-exponent@0.0.8: - resolution: {integrity: sha512-xBAOtso1eiy27GnTut2difuSdpsGxI8dJhXupw0UukGgvy/3CSsyNm+a1Suz/dhqK4tPOTe5QboIdNMw5IgXKQ==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/utils-library-manifest': 0.0.8 - transitivePeerDependencies: - - supports-color - dev: true + '@rollup/plugin-alias@3.1.9': + resolution: {integrity: sha512-QI5fsEvm9bDzt32k39wpOwZhVzRcL5ydcffUHMyLVaVaLeC70I8TJZ17F1z1eMoLu4E/UOcH9BWVkKpIKdrfiw==} + engines: {node: '>=8.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0 - /@stdlib/constants-float64-min-base2-exponent-subnormal@0.0.8: - resolution: {integrity: sha512-bt81nBus/91aEqGRQBenEFCyWNsf8uaxn4LN1NjgkvY92S1yVxXFlC65fJHsj9FTqvyZ+uj690/gdMKUDV3NjQ==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/utils-library-manifest': 0.0.8 - transitivePeerDependencies: - - supports-color - dev: true + '@rollup/plugin-babel@5.3.1': + resolution: {integrity: sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==} + engines: {node: '>= 10.0.0'} + peerDependencies: + '@babel/core': ^7.0.0 + '@types/babel__core': ^7.1.9 + rollup: ^1.20.0||^2.0.0 + peerDependenciesMeta: + '@types/babel__core': + optional: true - /@stdlib/constants-float64-ninf@0.0.8: - resolution: {integrity: sha512-bn/uuzCne35OSLsQZJlNrkvU1/40spGTm22g1+ZI1LL19J8XJi/o4iupIHRXuLSTLFDBqMoJlUNphZlWQ4l8zw==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/number-ctor': 0.0.7 - '@stdlib/utils-library-manifest': 0.0.8 - transitivePeerDependencies: - - supports-color - dev: true + '@rollup/plugin-commonjs@17.1.0': + resolution: {integrity: sha512-PoMdXCw0ZyvjpCMT5aV4nkL0QywxP29sODQsSGeDpr/oI49Qq9tRtAsb/LbYbDzFlOydVEqHmmZWFtXJEAX9ew==} + engines: {node: '>= 8.0.0'} + peerDependencies: + rollup: ^2.30.0 - /@stdlib/constants-float64-pinf@0.0.8: - resolution: {integrity: sha512-I3R4rm2cemoMuiDph07eo5oWZ4ucUtpuK73qBJiJPDQKz8fSjSe4wJBAigq2AmWYdd7yJHsl5NJd8AgC6mP5Qw==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/utils-library-manifest': 0.0.8 - transitivePeerDependencies: - - supports-color - dev: true + '@rollup/plugin-commonjs@28.0.2': + resolution: {integrity: sha512-BEFI2EDqzl+vA1rl97IDRZ61AIwGH093d9nz8+dThxJNH8oSoB7MjWvPCX3dkaK1/RCJ/1v/R1XB15FuSs0fQw==} + engines: {node: '>=16.0.0 || 14 >= 14.17'} + peerDependencies: + rollup: ^2.68.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true - /@stdlib/constants-float64-smallest-normal@0.0.8: - resolution: {integrity: sha512-Qwxpn5NA3RXf+mQcffCWRcsHSPTUQkalsz0+JDpblDszuz2XROcXkOdDr5LKgTAUPIXsjOgZzTsuRONENhsSEg==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/utils-library-manifest': 0.0.8 - transitivePeerDependencies: - - supports-color - dev: true + '@rollup/plugin-json@4.1.0': + resolution: {integrity: sha512-yfLbTdNS6amI/2OpmbiBoW12vngr5NW2jCJVZSBEz+H5KfUJZ2M7sDjk0U6GOOdCWFVScShte29o9NezJ53TPw==} + peerDependencies: + rollup: ^1.20.0 || ^2.0.0 - /@stdlib/constants-uint16-max@0.0.7: - resolution: {integrity: sha512-7TPoku7SlskA67mAm7mykIAjeEnkQJemw1cnKZur0mT5W4ryvDR6iFfL9xBiByVnWYq/+ei7DHbOv6/2b2jizw==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dev: true + '@rollup/plugin-json@5.0.2': + resolution: {integrity: sha512-D1CoOT2wPvadWLhVcmpkDnesTzjhNIQRWLsc3fA49IFOP2Y84cFOOJ+nKGYedvXHKUsPeq07HR4hXpBBr+CHlA==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0 + peerDependenciesMeta: + rollup: + optional: true - /@stdlib/constants-uint32-max@0.0.7: - resolution: {integrity: sha512-8+NK0ewqc1vnEZNqzwFJgFSy3S543Eft7i8WyW/ygkofiqEiLAsujvYMHzPAB8/3D+PYvjTSe37StSwRwvQ6uw==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dev: true + '@rollup/plugin-json@6.1.0': + resolution: {integrity: sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true - /@stdlib/constants-uint8-max@0.0.7: - resolution: {integrity: sha512-fqV+xds4jgwFxwWu08b8xDuIoW6/D4/1dtEjZ1sXVeWR7nf0pjj1cHERq4kdkYxsvOGu+rjoR3MbjzpFc4fvSw==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dev: true + '@rollup/plugin-multi-entry@6.0.1': + resolution: {integrity: sha512-AXm6toPyTSfbYZWghQGbom1Uh7dHXlrGa+HoiYNhQtDUE3Q7LqoUYdVQx9E1579QWS1uOiu+cZRSE4okO7ySgw==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true - /@stdlib/fs-exists@0.0.8: - resolution: {integrity: sha512-mZktcCxiLmycCJefm1+jbMTYkmhK6Jk1ShFmUVqJvs+Ps9/2EEQXfPbdEniLoVz4HeHLlcX90JWobUEghOOnAQ==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - hasBin: true - dependencies: - '@stdlib/cli-ctor': 0.0.3 - '@stdlib/fs-read-file': 0.0.8 - '@stdlib/process-cwd': 0.0.8 - '@stdlib/utils-define-nonenumerable-read-only-property': 0.0.7 - dev: true + '@rollup/plugin-node-resolve@11.2.1': + resolution: {integrity: sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==} + engines: {node: '>= 10.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0 - /@stdlib/fs-read-file@0.0.8: - resolution: {integrity: sha512-pIZID/G91+q7ep4x9ECNC45+JT2j0+jdz/ZQVjCHiEwXCwshZPEvxcPQWb9bXo6coOY+zJyX5TwBIpXBxomWFg==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - hasBin: true - dependencies: - '@stdlib/cli-ctor': 0.0.3 - '@stdlib/utils-define-nonenumerable-read-only-property': 0.0.7 - dev: true + '@rollup/plugin-node-resolve@16.0.0': + resolution: {integrity: sha512-0FPvAeVUT/zdWoO0jnb/V5BlBsUSNfkIOtFHzMO4H9MOklrmQFY6FduVHKucNb/aTFxvnGhj4MNj/T1oNdDfNg==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^2.78.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true - /@stdlib/fs-resolve-parent-path@0.0.8: - resolution: {integrity: sha512-ok1bTWsAziChibQE3u7EoXwbCQUDkFjjRAHSxh7WWE5JEYVJQg1F0o3bbjRr4D/wfYYPWLAt8AFIKBUDmWghpg==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - hasBin: true - dependencies: - '@stdlib/assert-has-own-property': 0.0.7 - '@stdlib/assert-is-function': 0.0.8 - '@stdlib/assert-is-plain-object': 0.0.7 - '@stdlib/assert-is-string': 0.0.8 - '@stdlib/cli-ctor': 0.0.3 - '@stdlib/fs-exists': 0.0.8 - '@stdlib/fs-read-file': 0.0.8 - '@stdlib/process-cwd': 0.0.8 - '@stdlib/utils-define-nonenumerable-read-only-property': 0.0.7 - dev: true + '@rollup/plugin-replace@2.4.2': + resolution: {integrity: sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==} + peerDependencies: + rollup: ^1.20.0 || ^2.0.0 - /@stdlib/math-base-assert-is-infinite@0.0.9: - resolution: {integrity: sha512-JuPDdmxd+AtPWPHu9uaLvTsnEPaZODZk+zpagziNbDKy8DRiU1cy+t+QEjB5WizZt0A5MkuxDTjZ/8/sG5GaYQ==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/constants-float64-ninf': 0.0.8 - '@stdlib/constants-float64-pinf': 0.0.8 - '@stdlib/utils-library-manifest': 0.0.8 - transitivePeerDependencies: - - supports-color - dev: true + '@rollup/plugin-replace@5.0.7': + resolution: {integrity: sha512-PqxSfuorkHz/SPpyngLyg5GCEkOcee9M1bkxiVDr41Pd61mqP1PLOoDPbpl44SB2mQGKwV/In74gqQmGITOhEQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true - /@stdlib/math-base-assert-is-nan@0.0.8: - resolution: {integrity: sha512-m+gCVBxLFW8ZdAfdkATetYMvM7sPFoMKboacHjb1pe21jHQqVb+/4bhRSDg6S7HGX7/8/bSzEUm9zuF7vqK5rQ==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/utils-library-manifest': 0.0.8 - transitivePeerDependencies: - - supports-color - dev: true + '@rollup/plugin-terser@0.1.0': + resolution: {integrity: sha512-N2KK+qUfHX2hBzVzM41UWGLrEmcjVC37spC8R3c9mt3oEDFKh3N2e12/lLp9aVSt86veR0TQiCNQXrm8C6aiUQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^2.x || ^3.x + peerDependenciesMeta: + rollup: + optional: true - /@stdlib/math-base-napi-binary@0.0.8: - resolution: {integrity: sha512-B8d0HBPhfXefbdl/h0h5c+lM2sE+/U7Fb7hY/huVeoQtBtEx0Jbx/qKvPSVxMjmWCKfWlbPpbgKpN5GbFgLiAg==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/complex-float32': 0.0.7 - '@stdlib/complex-float64': 0.0.8 - '@stdlib/complex-reim': 0.0.6 - '@stdlib/complex-reimf': 0.0.1 - '@stdlib/utils-library-manifest': 0.0.8 - transitivePeerDependencies: - - supports-color - dev: true + '@rollup/plugin-terser@0.4.4': + resolution: {integrity: sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true - /@stdlib/math-base-napi-unary@0.0.9: - resolution: {integrity: sha512-2WNKhjCygkGMp0RgjaD7wAHJTqPZmuVW7yPOc62Tnz2U+Ad8q/tcOcN+uvq2dtKsAGr1HDMIQxZ/XrrThMePyA==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/complex-float32': 0.0.7 - '@stdlib/complex-float64': 0.0.8 - '@stdlib/complex-reim': 0.0.6 - '@stdlib/complex-reimf': 0.0.1 - '@stdlib/utils-library-manifest': 0.0.8 - transitivePeerDependencies: - - supports-color - dev: true + '@rollup/plugin-typescript@11.1.6': + resolution: {integrity: sha512-R92yOmIACgYdJ7dJ97p4K69I8gg6IEHt8M7dUBxN3W6nrO8uUxX5ixl0yU/N3aZTi8WhPuICvOHXQvF6FaykAA==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^2.14.0||^3.0.0||^4.0.0 + tslib: '*' + typescript: '>=3.7.0' + peerDependenciesMeta: + rollup: + optional: true + tslib: + optional: true - /@stdlib/math-base-special-abs@0.0.6: - resolution: {integrity: sha512-FaaMUnYs2qIVN3kI5m/qNlBhDnjszhDOzEhxGEoQWR/k0XnxbCsTyjNesR2DkpiKuoAXAr9ojoDe2qBYdirWoQ==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/math-base-napi-unary': 0.0.9 - '@stdlib/number-float64-base-to-words': 0.0.7 - '@stdlib/utils-library-manifest': 0.0.8 - transitivePeerDependencies: - - supports-color - dev: true + '@rollup/plugin-virtual@3.0.2': + resolution: {integrity: sha512-10monEYsBp3scM4/ND4LNH5Rxvh3e/cVeL3jWTgZ2SrQ+BmUoQcopVQvnaMcOnykb1VkxUFuDAN+0FnpTFRy2A==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true - /@stdlib/math-base-special-copysign@0.0.7: - resolution: {integrity: sha512-7Br7oeuVJSBKG8BiSk/AIRFTBd2sbvHdV3HaqRj8tTZHX8BQomZ3Vj4Qsiz3kPyO4d6PpBLBTYlGTkSDlGOZJA==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/constants-float64-high-word-abs-mask': 0.0.1 - '@stdlib/constants-float64-high-word-sign-mask': 0.0.1 - '@stdlib/math-base-napi-binary': 0.0.8 - '@stdlib/number-float64-base-from-words': 0.0.6 - '@stdlib/number-float64-base-get-high-word': 0.0.6 - '@stdlib/number-float64-base-to-words': 0.0.7 - '@stdlib/utils-library-manifest': 0.0.8 - transitivePeerDependencies: - - supports-color - dev: true + '@rollup/pluginutils@3.1.0': + resolution: {integrity: sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==} + engines: {node: '>= 8.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0 - /@stdlib/math-base-special-ldexp@0.0.5: - resolution: {integrity: sha512-RLRsPpCdcJZMhwb4l4B/FsmGfEPEWAsik6KYUkUSSHb7ok/gZWt8LgVScxGMpJMpl5IV0v9qG4ZINVONKjX5KA==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/constants-float64-exponent-bias': 0.0.8 - '@stdlib/constants-float64-max-base2-exponent': 0.0.8 - '@stdlib/constants-float64-max-base2-exponent-subnormal': 0.0.8 - '@stdlib/constants-float64-min-base2-exponent-subnormal': 0.0.8 - '@stdlib/constants-float64-ninf': 0.0.8 - '@stdlib/constants-float64-pinf': 0.0.8 - '@stdlib/math-base-assert-is-infinite': 0.0.9 - '@stdlib/math-base-assert-is-nan': 0.0.8 - '@stdlib/math-base-special-copysign': 0.0.7 - '@stdlib/number-float64-base-exponent': 0.0.6 - '@stdlib/number-float64-base-from-words': 0.0.6 - '@stdlib/number-float64-base-normalize': 0.0.9 - '@stdlib/number-float64-base-to-words': 0.0.7 - transitivePeerDependencies: - - supports-color - dev: true + '@rollup/pluginutils@4.2.1': + resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==} + engines: {node: '>= 8.0.0'} - /@stdlib/number-ctor@0.0.7: - resolution: {integrity: sha512-kXNwKIfnb10Ro3RTclhAYqbE3DtIXax+qpu0z1/tZpI2vkmTfYDQLno2QJrzJsZZgdeFtXIws+edONN9kM34ow==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dev: true + '@rollup/pluginutils@5.1.3': + resolution: {integrity: sha512-Pnsb6f32CD2W3uCaLZIzDmeFyQ2b8UWMFI7xtwUezpcGBDVDW6y9XgAWIlARiGAo6eNF5FK5aQTr0LFyNyqq5A==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true - /@stdlib/number-float64-base-exponent@0.0.6: - resolution: {integrity: sha512-wLXsG+cvynmapoffmj5hVNDH7BuHIGspBcTCdjPaD+tnqPDIm03qV5Z9YBhDh91BdOCuPZQ8Ovu2WBpX+ySeGg==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/constants-float64-exponent-bias': 0.0.8 - '@stdlib/constants-float64-high-word-exponent-mask': 0.0.8 - '@stdlib/number-float64-base-get-high-word': 0.0.6 - transitivePeerDependencies: - - supports-color - dev: true + '@rollup/rollup-android-arm-eabi@4.24.3': + resolution: {integrity: sha512-ufb2CH2KfBWPJok95frEZZ82LtDl0A6QKTa8MoM+cWwDZvVGl5/jNb79pIhRvAalUu+7LD91VYR0nwRD799HkQ==} + cpu: [arm] + os: [android] - /@stdlib/number-float64-base-from-words@0.0.6: - resolution: {integrity: sha512-r0elnekypCN831aw9Gp8+08br8HHAqvqtc5uXaxEh3QYIgBD/QM5qSb3b7WSAQ0ZxJJKdoykupODWWBkWQTijg==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/array-float64': 0.0.6 - '@stdlib/array-uint32': 0.0.6 - '@stdlib/assert-is-little-endian': 0.0.7 - '@stdlib/number-float64-base-to-words': 0.0.7 - '@stdlib/utils-library-manifest': 0.0.8 - transitivePeerDependencies: - - supports-color - dev: true + '@rollup/rollup-android-arm64@4.24.3': + resolution: {integrity: sha512-iAHpft/eQk9vkWIV5t22V77d90CRofgR2006UiCjHcHJFVI1E0oBkQIAbz+pLtthFw3hWEmVB4ilxGyBf48i2Q==} + cpu: [arm64] + os: [android] - /@stdlib/number-float64-base-get-high-word@0.0.6: - resolution: {integrity: sha512-jSFSYkgiG/IzDurbwrDKtWiaZeSEJK8iJIsNtbPG1vOIdQMRyw+t0bf3Kf3vuJu/+bnSTvYZLqpCO6wzT/ve9g==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/array-float64': 0.0.6 - '@stdlib/array-uint32': 0.0.6 - '@stdlib/assert-is-little-endian': 0.0.7 - '@stdlib/number-float64-base-to-words': 0.0.7 - '@stdlib/utils-library-manifest': 0.0.8 - transitivePeerDependencies: - - supports-color - dev: true + '@rollup/rollup-darwin-arm64@4.24.3': + resolution: {integrity: sha512-QPW2YmkWLlvqmOa2OwrfqLJqkHm7kJCIMq9kOz40Zo9Ipi40kf9ONG5Sz76zszrmIZZ4hgRIkez69YnTHgEz1w==} + cpu: [arm64] + os: [darwin] - /@stdlib/number-float64-base-normalize@0.0.9: - resolution: {integrity: sha512-+rm7RQJEj8zHkqYFE2a6DgNQSB5oKE/IydHAajgZl40YB91BoYRYf/ozs5/tTwfy2Fc04+tIpSfFtzDr4ZY19Q==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/constants-float64-smallest-normal': 0.0.8 - '@stdlib/math-base-assert-is-infinite': 0.0.9 - '@stdlib/math-base-assert-is-nan': 0.0.8 - '@stdlib/math-base-special-abs': 0.0.6 - '@stdlib/types': 0.0.14 - '@stdlib/utils-define-nonenumerable-read-only-property': 0.0.7 - '@stdlib/utils-library-manifest': 0.0.8 - transitivePeerDependencies: - - supports-color - dev: true + '@rollup/rollup-darwin-x64@4.24.3': + resolution: {integrity: sha512-KO0pN5x3+uZm1ZXeIfDqwcvnQ9UEGN8JX5ufhmgH5Lz4ujjZMAnxQygZAVGemFWn+ZZC0FQopruV4lqmGMshow==} + cpu: [x64] + os: [darwin] - /@stdlib/number-float64-base-to-float32@0.0.7: - resolution: {integrity: sha512-PNUSi6+cqfFiu4vgFljUKMFY2O9PxI6+T+vqtIoh8cflf+PjSGj3v4QIlstK9+6qU40eGR5SHZyLTWdzmNqLTQ==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/array-float32': 0.0.6 - transitivePeerDependencies: - - supports-color - dev: true + '@rollup/rollup-freebsd-arm64@4.24.3': + resolution: {integrity: sha512-CsC+ZdIiZCZbBI+aRlWpYJMSWvVssPuWqrDy/zi9YfnatKKSLFCe6fjna1grHuo/nVaHG+kiglpRhyBQYRTK4A==} + cpu: [arm64] + os: [freebsd] - /@stdlib/number-float64-base-to-words@0.0.7: - resolution: {integrity: sha512-7wsYuq+2MGp9rAkTnQ985rah7EJI9TfgHrYSSd4UIu4qIjoYmWIKEhIDgu7/69PfGrls18C3PxKg1pD/v7DQTg==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/array-float64': 0.0.6 - '@stdlib/array-uint32': 0.0.6 - '@stdlib/assert-is-little-endian': 0.0.7 - '@stdlib/os-byte-order': 0.0.7 - '@stdlib/os-float-word-order': 0.0.7 - '@stdlib/types': 0.0.14 - '@stdlib/utils-define-nonenumerable-read-only-property': 0.0.7 - '@stdlib/utils-library-manifest': 0.0.8 - transitivePeerDependencies: - - supports-color - dev: true + '@rollup/rollup-freebsd-x64@4.24.3': + resolution: {integrity: sha512-F0nqiLThcfKvRQhZEzMIXOQG4EeX61im61VYL1jo4eBxv4aZRmpin6crnBJQ/nWnCsjH5F6J3W6Stdm0mBNqBg==} + cpu: [x64] + os: [freebsd] - /@stdlib/os-byte-order@0.0.7: - resolution: {integrity: sha512-rRJWjFM9lOSBiIX4zcay7BZsqYBLoE32Oz/Qfim8cv1cN1viS5D4d3DskRJcffw7zXDnG3oZAOw5yZS0FnlyUg==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - hasBin: true - dependencies: - '@stdlib/assert-is-big-endian': 0.0.7 - '@stdlib/assert-is-little-endian': 0.0.7 - '@stdlib/cli-ctor': 0.0.3 - '@stdlib/fs-read-file': 0.0.8 - '@stdlib/utils-library-manifest': 0.0.8 - transitivePeerDependencies: - - supports-color - dev: true + '@rollup/rollup-linux-arm-gnueabihf@4.24.3': + resolution: {integrity: sha512-KRSFHyE/RdxQ1CSeOIBVIAxStFC/hnBgVcaiCkQaVC+EYDtTe4X7z5tBkFyRoBgUGtB6Xg6t9t2kulnX6wJc6A==} + cpu: [arm] + os: [linux] - /@stdlib/os-float-word-order@0.0.7: - resolution: {integrity: sha512-gXIcIZf+ENKP7E41bKflfXmPi+AIfjXW/oU+m8NbP3DQasqHaZa0z5758qvnbO8L1lRJb/MzLOkIY8Bx/0cWEA==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - hasBin: true - dependencies: - '@stdlib/cli-ctor': 0.0.3 - '@stdlib/fs-read-file': 0.0.8 - '@stdlib/os-byte-order': 0.0.7 - '@stdlib/utils-library-manifest': 0.0.8 - transitivePeerDependencies: - - supports-color - dev: true + '@rollup/rollup-linux-arm-musleabihf@4.24.3': + resolution: {integrity: sha512-h6Q8MT+e05zP5BxEKz0vi0DhthLdrNEnspdLzkoFqGwnmOzakEHSlXfVyA4HJ322QtFy7biUAVFPvIDEDQa6rw==} + cpu: [arm] + os: [linux] - /@stdlib/process-cwd@0.0.8: - resolution: {integrity: sha512-GHINpJgSlKEo9ODDWTHp0/Zc/9C/qL92h5Mc0QlIFBXAoUjy6xT4FB2U16wCNZMG3eVOzt5+SjmCwvGH0Wbg3Q==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - hasBin: true - dependencies: - '@stdlib/cli-ctor': 0.0.3 - '@stdlib/fs-read-file': 0.0.8 - dev: true - - /@stdlib/process-read-stdin@0.0.7: - resolution: {integrity: sha512-nep9QZ5iDGrRtrZM2+pYAvyCiYG4HfO0/9+19BiLJepjgYq4GKeumPAQo22+1xawYDL7Zu62uWzYszaVZcXuyw==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/assert-is-function': 0.0.8 - '@stdlib/assert-is-string': 0.0.8 - '@stdlib/buffer-ctor': 0.0.7 - '@stdlib/buffer-from-string': 0.0.8 - '@stdlib/streams-node-stdin': 0.0.7 - '@stdlib/utils-next-tick': 0.0.8 - dev: true - - /@stdlib/regexp-eol@0.0.7: - resolution: {integrity: sha512-BTMpRWrmlnf1XCdTxOrb8o6caO2lmu/c80XSyhYCi1DoizVIZnqxOaN5yUJNCr50g28vQ47PpsT3Yo7J3SdlRA==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/assert-has-own-property': 0.0.7 - '@stdlib/assert-is-boolean': 0.0.8 - '@stdlib/assert-is-plain-object': 0.0.7 - '@stdlib/assert-is-string': 0.0.8 - '@stdlib/utils-define-nonenumerable-read-only-property': 0.0.7 - dev: true - - /@stdlib/regexp-extended-length-path@0.0.7: - resolution: {integrity: sha512-z6uqzMWq3WPDKbl4MIZJoNA5ZsYLQI9G3j2TIvhU8X2hnhlku8p4mvK9F+QmoVvgPxKliwNnx/DAl7ltutSDKw==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/utils-define-nonenumerable-read-only-property': 0.0.7 - dev: true - - /@stdlib/regexp-function-name@0.0.7: - resolution: {integrity: sha512-MaiyFUUqkAUpUoz/9F6AMBuMQQfA9ssQfK16PugehLQh4ZtOXV1LhdY8e5Md7SuYl9IrvFVg1gSAVDysrv5ZMg==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/utils-define-nonenumerable-read-only-property': 0.0.7 - dev: true - - /@stdlib/regexp-regexp@0.0.8: - resolution: {integrity: sha512-S5PZICPd/XRcn1dncVojxIDzJsHtEleuJHHD7ji3o981uPHR7zI2Iy9a1eV2u7+ABeUswbI1Yuix6fXJfcwV1w==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/utils-define-nonenumerable-read-only-property': 0.0.7 - dev: true - - /@stdlib/streams-node-stdin@0.0.7: - resolution: {integrity: sha512-gg4lgrjuoG3V/L29wNs32uADMCqepIcmoOFHJCTAhVe0GtHDLybUVnLljaPfdvmpPZmTvmusPQtIcscbyWvAyg==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dev: true - - /@stdlib/string-base-format-interpolate@0.0.4: - resolution: {integrity: sha512-8FC8+/ey+P5hf1B50oXpXzRzoAgKI1rikpyKZ98Xmjd5rcbSq3NWYi8TqOF8mUHm9hVZ2CXWoNCtEe2wvMQPMg==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dev: true - - /@stdlib/string-base-format-tokenize@0.0.4: - resolution: {integrity: sha512-+vMIkheqAhDeT/iF5hIQo95IMkt5IzC68eR3CxW1fhc48NMkKFE2UfN73ET8fmLuOanLo/5pO2E90c2G7PExow==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dev: true - - /@stdlib/string-format@0.0.3: - resolution: {integrity: sha512-1jiElUQXlI/tTkgRuzJi9jUz/EjrO9kzS8VWHD3g7gdc3ZpxlA5G9JrIiPXGw/qmZTi0H1pXl6KmX+xWQEQJAg==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/string-base-format-interpolate': 0.0.4 - '@stdlib/string-base-format-tokenize': 0.0.4 - dev: true - - /@stdlib/string-lowercase@0.0.9: - resolution: {integrity: sha512-tXFFjbhIlDak4jbQyV1DhYiSTO8b1ozS2g/LELnsKUjIXECDKxGFyWYcz10KuyAWmFotHnCJdIm8/blm2CfDIA==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - hasBin: true - dependencies: - '@stdlib/assert-is-string': 0.0.8 - '@stdlib/cli-ctor': 0.0.3 - '@stdlib/fs-read-file': 0.0.8 - '@stdlib/process-read-stdin': 0.0.7 - '@stdlib/streams-node-stdin': 0.0.7 - '@stdlib/string-format': 0.0.3 - dev: true - - /@stdlib/string-replace@0.0.11: - resolution: {integrity: sha512-F0MY4f9mRE5MSKpAUfL4HLbJMCbG6iUTtHAWnNeAXIvUX1XYIw/eItkA58R9kNvnr1l5B08bavnjrgTJGIKFFQ==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - hasBin: true - dependencies: - '@stdlib/assert-is-function': 0.0.8 - '@stdlib/assert-is-regexp': 0.0.7 - '@stdlib/assert-is-regexp-string': 0.0.9 - '@stdlib/assert-is-string': 0.0.8 - '@stdlib/cli-ctor': 0.0.3 - '@stdlib/fs-read-file': 0.0.8 - '@stdlib/process-read-stdin': 0.0.7 - '@stdlib/regexp-eol': 0.0.7 - '@stdlib/streams-node-stdin': 0.0.7 - '@stdlib/string-format': 0.0.3 - '@stdlib/utils-escape-regexp-string': 0.0.9 - '@stdlib/utils-regexp-from-string': 0.0.9 - dev: true - - /@stdlib/types@0.0.14: - resolution: {integrity: sha512-AP3EI9/il/xkwUazcoY+SbjtxHRrheXgSbWZdEGD+rWpEgj6n2i63hp6hTOpAB5NipE0tJwinQlDGOuQ1lCaCw==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dev: true - - /@stdlib/utils-constructor-name@0.0.8: - resolution: {integrity: sha512-GXpyNZwjN8u3tyYjL2GgGfrsxwvfogUC3gg7L7NRZ1i86B6xmgfnJUYHYOUnSfB+R531ET7NUZlK52GxL7P82Q==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/assert-is-buffer': 0.0.8 - '@stdlib/regexp-function-name': 0.0.7 - '@stdlib/utils-native-class': 0.0.8 - dev: true - - /@stdlib/utils-convert-path@0.0.8: - resolution: {integrity: sha512-GNd8uIswrcJCctljMbmjtE4P4oOjhoUIfMvdkqfSrRLRY+ZqPB2xM+yI0MQFfUq/0Rnk/xtESlGSVLz9ZDtXfA==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - hasBin: true - dependencies: - '@stdlib/assert-is-string': 0.0.8 - '@stdlib/cli-ctor': 0.0.3 - '@stdlib/fs-read-file': 0.0.8 - '@stdlib/process-read-stdin': 0.0.7 - '@stdlib/regexp-eol': 0.0.7 - '@stdlib/regexp-extended-length-path': 0.0.7 - '@stdlib/streams-node-stdin': 0.0.7 - '@stdlib/string-lowercase': 0.0.9 - '@stdlib/string-replace': 0.0.11 - dev: true - - /@stdlib/utils-define-nonenumerable-read-only-property@0.0.7: - resolution: {integrity: sha512-c7dnHDYuS4Xn3XBRWIQBPcROTtP/4lkcFyq0FrQzjXUjimfMgHF7cuFIIob6qUTnU8SOzY9p0ydRR2QJreWE6g==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/types': 0.0.14 - '@stdlib/utils-define-property': 0.0.9 - dev: true - - /@stdlib/utils-define-property@0.0.9: - resolution: {integrity: sha512-pIzVvHJvVfU/Lt45WwUAcodlvSPDDSD4pIPc9WmIYi4vnEBA9U7yHtiNz2aTvfGmBMTaLYTVVFIXwkFp+QotMA==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/types': 0.0.14 - dev: true - - /@stdlib/utils-escape-regexp-string@0.0.9: - resolution: {integrity: sha512-E+9+UDzf2mlMLgb+zYrrPy2FpzbXh189dzBJY6OG+XZqEJAXcjWs7DURO5oGffkG39EG5KXeaQwDXUavcMDCIw==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/assert-is-string': 0.0.8 - '@stdlib/string-format': 0.0.3 - dev: true - - /@stdlib/utils-get-prototype-of@0.0.7: - resolution: {integrity: sha512-fCUk9lrBO2ELrq+/OPJws1/hquI4FtwG0SzVRH6UJmJfwb1zoEFnjcwyDAy+HWNVmo3xeRLsrz6XjHrJwer9pg==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/assert-is-function': 0.0.8 - '@stdlib/utils-native-class': 0.0.8 - dev: true - - /@stdlib/utils-global@0.0.7: - resolution: {integrity: sha512-BBNYBdDUz1X8Lhfw9nnnXczMv9GztzGpQ88J/6hnY7PHJ71av5d41YlijWeM9dhvWjnH9I7HNE3LL7R07yw0kA==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/assert-is-boolean': 0.0.8 - dev: true - - /@stdlib/utils-library-manifest@0.0.8: - resolution: {integrity: sha512-IOQSp8skSRQn9wOyMRUX9Hi0j/P5v5TvD8DJWTqtE8Lhr8kVVluMBjHfvheoeKHxfWAbNHSVpkpFY/Bdh/SHgQ==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - hasBin: true - dependencies: - '@stdlib/cli-ctor': 0.0.3 - '@stdlib/fs-resolve-parent-path': 0.0.8 - '@stdlib/utils-convert-path': 0.0.8 - debug: 2.6.9 - resolve: 1.22.2 - transitivePeerDependencies: - - supports-color - dev: true + '@rollup/rollup-linux-arm64-gnu@4.24.3': + resolution: {integrity: sha512-fKElSyXhXIJ9pqiYRqisfirIo2Z5pTTve5K438URf08fsypXrEkVmShkSfM8GJ1aUyvjakT+fn2W7Czlpd/0FQ==} + cpu: [arm64] + os: [linux] - /@stdlib/utils-native-class@0.0.8: - resolution: {integrity: sha512-0Zl9me2V9rSrBw/N8o8/9XjmPUy8zEeoMM0sJmH3N6C9StDsYTjXIAMPGzYhMEWaWHvGeYyNteFK2yDOVGtC3w==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/assert-has-own-property': 0.0.7 - '@stdlib/assert-has-tostringtag-support': 0.0.9 - dev: true + '@rollup/rollup-linux-arm64-musl@4.24.3': + resolution: {integrity: sha512-YlddZSUk8G0px9/+V9PVilVDC6ydMz7WquxozToozSnfFK6wa6ne1ATUjUvjin09jp34p84milxlY5ikueoenw==} + cpu: [arm64] + os: [linux] - /@stdlib/utils-next-tick@0.0.8: - resolution: {integrity: sha512-l+hPl7+CgLPxk/gcWOXRxX/lNyfqcFCqhzzV/ZMvFCYLY/wI9lcWO4xTQNMALY2rp+kiV+qiAiO9zcO+hewwUg==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dev: true + '@rollup/rollup-linux-powerpc64le-gnu@4.24.3': + resolution: {integrity: sha512-yNaWw+GAO8JjVx3s3cMeG5Esz1cKVzz8PkTJSfYzE5u7A+NvGmbVFEHP+BikTIyYWuz0+DX9kaA3pH9Sqxp69g==} + cpu: [ppc64] + os: [linux] - /@stdlib/utils-noop@0.0.14: - resolution: {integrity: sha512-A5faFEUfszMgd93RCyB+aWb62hQxgP+dZ/l9rIOwNWbIrCYNwSuL4z50lNJuatnwwU4BQ4EjQr+AmBsnvuLcyQ==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dev: true + '@rollup/rollup-linux-riscv64-gnu@4.24.3': + resolution: {integrity: sha512-lWKNQfsbpv14ZCtM/HkjCTm4oWTKTfxPmr7iPfp3AHSqyoTz5AgLemYkWLwOBWc+XxBbrU9SCokZP0WlBZM9lA==} + cpu: [riscv64] + os: [linux] - /@stdlib/utils-regexp-from-string@0.0.9: - resolution: {integrity: sha512-3rN0Mcyiarl7V6dXRjFAUMacRwe0/sYX7ThKYurf0mZkMW9tjTP+ygak9xmL9AL0QQZtbrFFwWBrDO+38Vnavw==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/assert-is-string': 0.0.8 - '@stdlib/regexp-regexp': 0.0.8 - '@stdlib/string-format': 0.0.3 - dev: true + '@rollup/rollup-linux-s390x-gnu@4.24.3': + resolution: {integrity: sha512-HoojGXTC2CgCcq0Woc/dn12wQUlkNyfH0I1ABK4Ni9YXyFQa86Fkt2Q0nqgLfbhkyfQ6003i3qQk9pLh/SpAYw==} + cpu: [s390x] + os: [linux] - /@stdlib/utils-type-of@0.0.8: - resolution: {integrity: sha512-b4xqdy3AnnB7NdmBBpoiI67X4vIRxvirjg3a8BfhM5jPr2k0njby1jAbG9dUxJvgAV6o32S4kjUgfIdjEYpTNQ==} - engines: {node: '>=0.10.0', npm: '>2.7.0'} - os: [aix, darwin, freebsd, linux, macos, openbsd, sunos, win32, windows] - dependencies: - '@stdlib/utils-constructor-name': 0.0.8 - '@stdlib/utils-global': 0.0.7 - dev: true + '@rollup/rollup-linux-x64-gnu@4.24.3': + resolution: {integrity: sha512-mnEOh4iE4USSccBOtcrjF5nj+5/zm6NcNhbSEfR3Ot0pxBwvEn5QVUXcuOwwPkapDtGZ6pT02xLoPaNv06w7KQ==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.24.3': + resolution: {integrity: sha512-rMTzawBPimBQkG9NKpNHvquIUTQPzrnPxPbCY1Xt+mFkW7pshvyIS5kYgcf74goxXOQk0CP3EoOC1zcEezKXhw==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-win32-arm64-msvc@4.24.3': + resolution: {integrity: sha512-2lg1CE305xNvnH3SyiKwPVsTVLCg4TmNCF1z7PSHX2uZY2VbUpdkgAllVoISD7JO7zu+YynpWNSKAtOrX3AiuA==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.24.3': + resolution: {integrity: sha512-9SjYp1sPyxJsPWuhOCX6F4jUMXGbVVd5obVpoVEi8ClZqo52ViZewA6eFz85y8ezuOA+uJMP5A5zo6Oz4S5rVQ==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.24.3': + resolution: {integrity: sha512-HGZgRFFYrMrP3TJlq58nR1xy8zHKId25vhmm5S9jETEfDf6xybPxsavFTJaufe2zgOGYJBskGlj49CwtEuFhWQ==} + cpu: [x64] + os: [win32] + + '@rtsao/scc@1.1.0': + resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} + + '@rushstack/eslint-patch@1.10.4': + resolution: {integrity: sha512-WJgX9nzTqknM393q1QJDJmoW28kUfEnybeTfVNcNAPnIx210RXm2DiXiHzfNPJNIUUb1tJnz/l4QGtJ30PgWmA==} + + '@segment/analytics-core@1.8.0': + resolution: {integrity: sha512-6CrccsYRY33I3mONN2ZW8SdBpbLtu1Ict3xR+n0FemYF5RB/jG7pW6jOvDXULR8kuYMzMmGOP4HvlyUmf3qLpg==} + + '@segment/analytics-generic-utils@1.2.0': + resolution: {integrity: sha512-DfnW6mW3YQOLlDQQdR89k4EqfHb0g/3XvBXkovH1FstUN93eL1kfW9CsDcVQyH3bAC5ZsFyjA/o/1Q2j0QeoWw==} + + '@segment/analytics-next@1.75.0': + resolution: {integrity: sha512-IWtcLqYMOdoa0KbLf2y0EMuZmOhRysYxECgKkf0VD6SPAmioQjSvTwa6YGxRWNrE/eATqhdfhjQkRF3J25Ti5w==} + + '@segment/analytics.js-video-plugins@0.2.1': + resolution: {integrity: sha512-lZwCyEXT4aaHBLNK433okEKdxGAuyrVmop4BpQqQSJuRz0DglPZgd9B/XjiiWs1UyOankg2aNYMN3VcS8t4eSQ==} + + '@segment/facade@3.4.10': + resolution: {integrity: sha512-xVQBbB/lNvk/u8+ey0kC/+g8pT3l0gCT8O2y9Z+StMMn3KAFAQ9w8xfgef67tJybktOKKU7pQGRPolRM1i1pdA==} + + '@segment/isodate-traverse@1.1.1': + resolution: {integrity: sha512-+G6e1SgAUkcq0EDMi+SRLfT48TNlLPF3QnSgFGVs0V9F3o3fq/woQ2rHFlW20W0yy5NnCUH0QGU3Am2rZy/E3w==} + + '@segment/isodate@1.0.3': + resolution: {integrity: sha512-BtanDuvJqnACFkeeYje7pWULVv8RgZaqKHWwGFnL/g/TH/CcZjkIVTfGDp/MAxmilYHUkrX70SqwnYSTNEaN7A==} + + '@selderee/plugin-htmlparser2@0.11.0': + resolution: {integrity: sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==} + + '@sigstore/bundle@1.1.0': + resolution: {integrity: sha512-PFutXEy0SmQxYI4texPw3dd2KewuNqv7OuK1ZFtY2fM754yhvG2KdgwIhRnoEE2uHdtdGNQ8s0lb94dW9sELog==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + '@sigstore/protobuf-specs@0.2.1': + resolution: {integrity: sha512-XTWVxnWJu+c1oCshMLwnKvz8ZQJJDVOlciMfgpJBQbThVjKTCG8dwyhgLngBD2KN0ap9F/gOV8rFDEx8uh7R2A==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + '@sigstore/sign@1.0.0': + resolution: {integrity: sha512-INxFVNQteLtcfGmcoldzV6Je0sbbfh9I16DM4yJPw3j5+TFP8X6uIiA18mvpEa9yyeycAKgPmOA3X9hVdVTPUA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + '@sigstore/tuf@1.0.3': + resolution: {integrity: sha512-2bRovzs0nJZFlCN3rXirE4gwxCn97JNjMmwpecqlbgV9WcxX7WRuIrgzx/X7Ib7MYRbyUTpBYE0s2x6AmZXnlg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + '@sinclair/typebox@0.24.51': + resolution: {integrity: sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==} + + '@sinclair/typebox@0.27.8': + resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + + '@sindresorhus/merge-streams@2.3.0': + resolution: {integrity: sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==} + engines: {node: '>=18'} + + '@sinonjs/commons@1.8.6': + resolution: {integrity: sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==} + + '@sinonjs/commons@3.0.1': + resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} + + '@sinonjs/fake-timers@10.3.0': + resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} + + '@sinonjs/fake-timers@8.1.0': + resolution: {integrity: sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==} + + '@sinonjs/fake-timers@9.1.2': + resolution: {integrity: sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==} + + '@smithy/abort-controller@3.1.6': + resolution: {integrity: sha512-0XuhuHQlEqbNQZp7QxxrFTdVWdwxch4vjxYgfInF91hZFkPxf9QDrdQka0KfxFMPqLNzSw0b95uGTrLliQUavQ==} + engines: {node: '>=16.0.0'} + + '@smithy/chunked-blob-reader-native@3.0.1': + resolution: {integrity: sha512-VEYtPvh5rs/xlyqpm5NRnfYLZn+q0SRPELbvBV+C/G7IQ+ouTuo+NKKa3ShG5OaFR8NYVMXls9hPYLTvIKKDrQ==} + + '@smithy/chunked-blob-reader@4.0.0': + resolution: {integrity: sha512-jSqRnZvkT4egkq/7b6/QRCNXmmYVcHwnJldqJ3IhVpQE2atObVJ137xmGeuGFhjFUr8gCEVAOKwSY79OvpbDaQ==} + + '@smithy/config-resolver@3.0.10': + resolution: {integrity: sha512-Uh0Sz9gdUuz538nvkPiyv1DZRX9+D15EKDtnQP5rYVAzM/dnYk3P8cg73jcxyOitPgT3mE3OVj7ky7sibzHWkw==} + engines: {node: '>=16.0.0'} + + '@smithy/core@2.5.1': + resolution: {integrity: sha512-DujtuDA7BGEKExJ05W5OdxCoyekcKT3Rhg1ZGeiUWaz2BJIWXjZmsG/DIP4W48GHno7AQwRsaCb8NcBgH3QZpg==} + engines: {node: '>=16.0.0'} + + '@smithy/credential-provider-imds@3.2.5': + resolution: {integrity: sha512-4FTQGAsuwqTzVMmiRVTn0RR9GrbRfkP0wfu/tXWVHd2LgNpTY0uglQpIScXK4NaEyXbB3JmZt8gfVqO50lP8wg==} + engines: {node: '>=16.0.0'} - /@surma/rollup-plugin-off-main-thread@2.2.3: + '@smithy/eventstream-codec@3.1.7': + resolution: {integrity: sha512-kVSXScIiRN7q+s1x7BrQtZ1Aa9hvvP9FeCqCdBxv37GimIHgBCOnZ5Ip80HLt0DhnAKpiobFdGqTFgbaJNrazA==} + + '@smithy/eventstream-serde-browser@3.0.11': + resolution: {integrity: sha512-Pd1Wnq3CQ/v2SxRifDUihvpXzirJYbbtXfEnnLV/z0OGCTx/btVX74P86IgrZkjOydOASBGXdPpupYQI+iO/6A==} + engines: {node: '>=16.0.0'} + + '@smithy/eventstream-serde-config-resolver@3.0.8': + resolution: {integrity: sha512-zkFIG2i1BLbfoGQnf1qEeMqX0h5qAznzaZmMVNnvPZz9J5AWBPkOMckZWPedGUPcVITacwIdQXoPcdIQq5FRcg==} + engines: {node: '>=16.0.0'} + + '@smithy/eventstream-serde-node@3.0.10': + resolution: {integrity: sha512-hjpU1tIsJ9qpcoZq9zGHBJPBOeBGYt+n8vfhDwnITPhEre6APrvqq/y3XMDEGUT2cWQ4ramNqBPRbx3qn55rhw==} + engines: {node: '>=16.0.0'} + + '@smithy/eventstream-serde-universal@3.0.10': + resolution: {integrity: sha512-ewG1GHbbqsFZ4asaq40KmxCmXO+AFSM1b+DcO2C03dyJj/ZH71CiTg853FSE/3SHK9q3jiYQIFjlGSwfxQ9kww==} + engines: {node: '>=16.0.0'} + + '@smithy/fetch-http-handler@3.2.9': + resolution: {integrity: sha512-hYNVQOqhFQ6vOpenifFME546f0GfJn2OiQ3M0FDmuUu8V/Uiwy2wej7ZXxFBNqdx0R5DZAqWM1l6VRhGz8oE6A==} + + '@smithy/fetch-http-handler@4.0.0': + resolution: {integrity: sha512-MLb1f5tbBO2X6K4lMEKJvxeLooyg7guq48C2zKr4qM7F2Gpkz4dc+hdSgu77pCJ76jVqFBjZczHYAs6dp15N+g==} + + '@smithy/hash-blob-browser@3.1.7': + resolution: {integrity: sha512-4yNlxVNJifPM5ThaA5HKnHkn7JhctFUHvcaz6YXxHlYOSIrzI6VKQPTN8Gs1iN5nqq9iFcwIR9THqchUCouIfg==} + + '@smithy/hash-node@3.0.8': + resolution: {integrity: sha512-tlNQYbfpWXHimHqrvgo14DrMAgUBua/cNoz9fMYcDmYej7MAmUcjav/QKQbFc3NrcPxeJ7QClER4tWZmfwoPng==} + engines: {node: '>=16.0.0'} + + '@smithy/hash-stream-node@3.1.7': + resolution: {integrity: sha512-xMAsvJ3hLG63lsBVi1Hl6BBSfhd8/Qnp8fC06kjOpJvyyCEXdwHITa5Kvdsk6gaAXLhbZMhQMIGvgUbfnJDP6Q==} + engines: {node: '>=16.0.0'} + + '@smithy/invalid-dependency@3.0.8': + resolution: {integrity: sha512-7Qynk6NWtTQhnGTTZwks++nJhQ1O54Mzi7fz4PqZOiYXb4Z1Flpb2yRvdALoggTS8xjtohWUM+RygOtB30YL3Q==} + + '@smithy/is-array-buffer@2.2.0': + resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==} + engines: {node: '>=14.0.0'} + + '@smithy/is-array-buffer@3.0.0': + resolution: {integrity: sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==} + engines: {node: '>=16.0.0'} + + '@smithy/md5-js@3.0.8': + resolution: {integrity: sha512-LwApfTK0OJ/tCyNUXqnWCKoE2b4rDSr4BJlDAVCkiWYeHESr+y+d5zlAanuLW6fnitVJRD/7d9/kN/ZM9Su4mA==} + + '@smithy/middleware-content-length@3.0.10': + resolution: {integrity: sha512-T4dIdCs1d/+/qMpwhJ1DzOhxCZjZHbHazEPJWdB4GDi2HjIZllVzeBEcdJUN0fomV8DURsgOyrbEUzg3vzTaOg==} + engines: {node: '>=16.0.0'} + + '@smithy/middleware-endpoint@3.2.1': + resolution: {integrity: sha512-wWO3xYmFm6WRW8VsEJ5oU6h7aosFXfszlz3Dj176pTij6o21oZnzkCLzShfmRaaCHDkBXWBdO0c4sQAvLFP6zA==} + engines: {node: '>=16.0.0'} + + '@smithy/middleware-retry@3.0.25': + resolution: {integrity: sha512-m1F70cPaMBML4HiTgCw5I+jFNtjgz5z5UdGnUbG37vw6kh4UvizFYjqJGHvicfgKMkDL6mXwyPp5mhZg02g5sg==} + engines: {node: '>=16.0.0'} + + '@smithy/middleware-serde@3.0.8': + resolution: {integrity: sha512-Xg2jK9Wc/1g/MBMP/EUn2DLspN8LNt+GMe7cgF+Ty3vl+Zvu+VeZU5nmhveU+H8pxyTsjrAkci8NqY6OuvZnjA==} + engines: {node: '>=16.0.0'} + + '@smithy/middleware-stack@3.0.8': + resolution: {integrity: sha512-d7ZuwvYgp1+3682Nx0MD3D/HtkmZd49N3JUndYWQXfRZrYEnCWYc8BHcNmVsPAp9gKvlurdg/mubE6b/rPS9MA==} + engines: {node: '>=16.0.0'} + + '@smithy/node-config-provider@3.1.9': + resolution: {integrity: sha512-qRHoah49QJ71eemjuS/WhUXB+mpNtwHRWQr77J/m40ewBVVwvo52kYAmb7iuaECgGTTcYxHS4Wmewfwy++ueew==} + engines: {node: '>=16.0.0'} + + '@smithy/node-http-handler@3.2.5': + resolution: {integrity: sha512-PkOwPNeKdvX/jCpn0A8n9/TyoxjGZB8WVoJmm9YzsnAgggTj4CrjpRHlTQw7dlLZ320n1mY1y+nTRUDViKi/3w==} + engines: {node: '>=16.0.0'} + + '@smithy/property-provider@3.1.8': + resolution: {integrity: sha512-ukNUyo6rHmusG64lmkjFeXemwYuKge1BJ8CtpVKmrxQxc6rhUX0vebcptFA9MmrGsnLhwnnqeH83VTU9hwOpjA==} + engines: {node: '>=16.0.0'} + + '@smithy/protocol-http@4.1.5': + resolution: {integrity: sha512-hsjtwpIemmCkm3ZV5fd/T0bPIugW1gJXwZ/hpuVubt2hEUApIoUTrf6qIdh9MAWlw0vjMrA1ztJLAwtNaZogvg==} + engines: {node: '>=16.0.0'} + + '@smithy/querystring-builder@3.0.8': + resolution: {integrity: sha512-btYxGVqFUARbUrN6VhL9c3dnSviIwBYD9Rz1jHuN1hgh28Fpv2xjU1HeCeDJX68xctz7r4l1PBnFhGg1WBBPuA==} + engines: {node: '>=16.0.0'} + + '@smithy/querystring-parser@3.0.8': + resolution: {integrity: sha512-BtEk3FG7Ks64GAbt+JnKqwuobJNX8VmFLBsKIwWr1D60T426fGrV2L3YS5siOcUhhp6/Y6yhBw1PSPxA5p7qGg==} + engines: {node: '>=16.0.0'} + + '@smithy/service-error-classification@3.0.8': + resolution: {integrity: sha512-uEC/kCCFto83bz5ZzapcrgGqHOh/0r69sZ2ZuHlgoD5kYgXJEThCoTuw/y1Ub3cE7aaKdznb+jD9xRPIfIwD7g==} + engines: {node: '>=16.0.0'} + + '@smithy/shared-ini-file-loader@3.1.9': + resolution: {integrity: sha512-/+OsJRNtoRbtsX0UpSgWVxFZLsJHo/4sTr+kBg/J78sr7iC+tHeOvOJrS5hCpVQ6sWBbhWLp1UNiuMyZhE6pmA==} + engines: {node: '>=16.0.0'} + + '@smithy/signature-v4@4.2.1': + resolution: {integrity: sha512-NsV1jF4EvmO5wqmaSzlnTVetemBS3FZHdyc5CExbDljcyJCEEkJr8ANu2JvtNbVg/9MvKAWV44kTrGS+Pi4INg==} + engines: {node: '>=16.0.0'} + + '@smithy/smithy-client@3.4.2': + resolution: {integrity: sha512-dxw1BDxJiY9/zI3cBqfVrInij6ShjpV4fmGHesGZZUiP9OSE/EVfdwdRz0PgvkEvrZHpsj2htRaHJfftE8giBA==} + engines: {node: '>=16.0.0'} + + '@smithy/types@3.6.0': + resolution: {integrity: sha512-8VXK/KzOHefoC65yRgCn5vG1cysPJjHnOVt9d0ybFQSmJgQj152vMn4EkYhGuaOmnnZvCPav/KnYyE6/KsNZ2w==} + engines: {node: '>=16.0.0'} + + '@smithy/url-parser@3.0.8': + resolution: {integrity: sha512-4FdOhwpTW7jtSFWm7SpfLGKIBC9ZaTKG5nBF0wK24aoQKQyDIKUw3+KFWCQ9maMzrgTJIuOvOnsV2lLGW5XjTg==} + + '@smithy/util-base64@3.0.0': + resolution: {integrity: sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ==} + engines: {node: '>=16.0.0'} + + '@smithy/util-body-length-browser@3.0.0': + resolution: {integrity: sha512-cbjJs2A1mLYmqmyVl80uoLTJhAcfzMOyPgjwAYusWKMdLeNtzmMz9YxNl3/jRLoxSS3wkqkf0jwNdtXWtyEBaQ==} + + '@smithy/util-body-length-node@3.0.0': + resolution: {integrity: sha512-Tj7pZ4bUloNUP6PzwhN7K386tmSmEET9QtQg0TgdNOnxhZvCssHji+oZTUIuzxECRfG8rdm2PMw2WCFs6eIYkA==} + engines: {node: '>=16.0.0'} + + '@smithy/util-buffer-from@2.2.0': + resolution: {integrity: sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==} + engines: {node: '>=14.0.0'} + + '@smithy/util-buffer-from@3.0.0': + resolution: {integrity: sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==} + engines: {node: '>=16.0.0'} + + '@smithy/util-config-provider@3.0.0': + resolution: {integrity: sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ==} + engines: {node: '>=16.0.0'} + + '@smithy/util-defaults-mode-browser@3.0.25': + resolution: {integrity: sha512-fRw7zymjIDt6XxIsLwfJfYUfbGoO9CmCJk6rjJ/X5cd20+d2Is7xjU5Kt/AiDt6hX8DAf5dztmfP5O82gR9emA==} + engines: {node: '>= 10.0.0'} + + '@smithy/util-defaults-mode-node@3.0.25': + resolution: {integrity: sha512-H3BSZdBDiVZGzt8TG51Pd2FvFO0PAx/A0mJ0EH8a13KJ6iUCdYnw/Dk/MdC1kTd0eUuUGisDFaxXVXo4HHFL1g==} + engines: {node: '>= 10.0.0'} + + '@smithy/util-endpoints@2.1.4': + resolution: {integrity: sha512-kPt8j4emm7rdMWQyL0F89o92q10gvCUa6sBkBtDJ7nV2+P7wpXczzOfoDJ49CKXe5CCqb8dc1W+ZdLlrKzSAnQ==} + engines: {node: '>=16.0.0'} + + '@smithy/util-hex-encoding@3.0.0': + resolution: {integrity: sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ==} + engines: {node: '>=16.0.0'} + + '@smithy/util-middleware@3.0.8': + resolution: {integrity: sha512-p7iYAPaQjoeM+AKABpYWeDdtwQNxasr4aXQEA/OmbOaug9V0odRVDy3Wx4ci8soljE/JXQo+abV0qZpW8NX0yA==} + engines: {node: '>=16.0.0'} + + '@smithy/util-retry@3.0.8': + resolution: {integrity: sha512-TCEhLnY581YJ+g1x0hapPz13JFqzmh/pMWL2KEFASC51qCfw3+Y47MrTmea4bUE5vsdxQ4F6/KFbUeSz22Q1ow==} + engines: {node: '>=16.0.0'} + + '@smithy/util-stream@3.2.1': + resolution: {integrity: sha512-R3ufuzJRxSJbE58K9AEnL/uSZyVdHzud9wLS8tIbXclxKzoe09CRohj2xV8wpx5tj7ZbiJaKYcutMm1eYgz/0A==} + engines: {node: '>=16.0.0'} + + '@smithy/util-uri-escape@3.0.0': + resolution: {integrity: sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg==} + engines: {node: '>=16.0.0'} + + '@smithy/util-utf8@2.3.0': + resolution: {integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==} + engines: {node: '>=14.0.0'} + + '@smithy/util-utf8@3.0.0': + resolution: {integrity: sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==} + engines: {node: '>=16.0.0'} + + '@smithy/util-waiter@3.1.7': + resolution: {integrity: sha512-d5yGlQtmN/z5eoTtIYgkvOw27US2Ous4VycnXatyoImIF9tzlcpnKqQ/V7qhvJmb2p6xZne1NopCLakdTnkBBQ==} + engines: {node: '>=16.0.0'} + + '@socket.io/component-emitter@3.1.2': + resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==} + + '@surma/rollup-plugin-off-main-thread@2.2.3': resolution: {integrity: sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==} - dependencies: - ejs: 3.1.9 - json5: 2.2.3 - magic-string: 0.25.9 - string.prototype.matchall: 4.0.8 - dev: false - /@svgr/babel-plugin-add-jsx-attribute@5.4.0: + '@svgr/babel-plugin-add-jsx-attribute@5.4.0': resolution: {integrity: sha512-ZFf2gs/8/6B8PnSofI0inYXr2SDNTDScPXhN7k5EqD4aZ3gi6u+rbmZHVB8IM3wDyx8ntKACZbtXSm7oZGRqVg==} engines: {node: '>=10'} - /@svgr/babel-plugin-remove-jsx-attribute@5.4.0: + '@svgr/babel-plugin-remove-jsx-attribute@5.4.0': resolution: {integrity: sha512-yaS4o2PgUtwLFGTKbsiAy6D0o3ugcUhWK0Z45umJ66EPWunAz9fuFw2gJuje6wqQvQWOTJvIahUwndOXb7QCPg==} engines: {node: '>=10'} - /@svgr/babel-plugin-remove-jsx-empty-expression@5.0.1: + '@svgr/babel-plugin-remove-jsx-empty-expression@5.0.1': resolution: {integrity: sha512-LA72+88A11ND/yFIMzyuLRSMJ+tRKeYKeQ+mR3DcAZ5I4h5CPWN9AHyUzJbWSYp/u2u0xhmgOe0+E41+GjEueA==} engines: {node: '>=10'} - /@svgr/babel-plugin-replace-jsx-attribute-value@5.0.1: + '@svgr/babel-plugin-replace-jsx-attribute-value@5.0.1': resolution: {integrity: sha512-PoiE6ZD2Eiy5mK+fjHqwGOS+IXX0wq/YDtNyIgOrc6ejFnxN4b13pRpiIPbtPwHEc+NT2KCjteAcq33/F1Y9KQ==} engines: {node: '>=10'} - /@svgr/babel-plugin-svg-dynamic-title@5.4.0: + '@svgr/babel-plugin-svg-dynamic-title@5.4.0': resolution: {integrity: sha512-zSOZH8PdZOpuG1ZVx/cLVePB2ibo3WPpqo7gFIjLV9a0QsuQAzJiwwqmuEdTaW2pegyBE17Uu15mOgOcgabQZg==} engines: {node: '>=10'} - /@svgr/babel-plugin-svg-em-dimensions@5.4.0: + '@svgr/babel-plugin-svg-em-dimensions@5.4.0': resolution: {integrity: sha512-cPzDbDA5oT/sPXDCUYoVXEmm3VIoAWAPT6mSPTJNbQaBNUuEKVKyGH93oDY4e42PYHRW67N5alJx/eEol20abw==} engines: {node: '>=10'} - /@svgr/babel-plugin-transform-react-native-svg@5.4.0: + '@svgr/babel-plugin-transform-react-native-svg@5.4.0': resolution: {integrity: sha512-3eYP/SaopZ41GHwXma7Rmxcv9uRslRDTY1estspeB1w1ueZWd/tPlMfEOoccYpEMZU3jD4OU7YitnXcF5hLW2Q==} engines: {node: '>=10'} - /@svgr/babel-plugin-transform-svg-component@5.5.0: + '@svgr/babel-plugin-transform-svg-component@5.5.0': resolution: {integrity: sha512-q4jSH1UUvbrsOtlo/tKcgSeiCHRSBdXoIoqX1pgcKK/aU3JD27wmMKwGtpB8qRYUYoyXvfGxUVKchLuR5pB3rQ==} engines: {node: '>=10'} - /@svgr/babel-preset@5.5.0: + '@svgr/babel-preset@5.5.0': resolution: {integrity: sha512-4FiXBjvQ+z2j7yASeGPEi8VD/5rrGQk4Xrq3EdJmoZgz/tpqChpo5hgXDvmEauwtvOc52q8ghhZK4Oy7qph4ig==} engines: {node: '>=10'} - dependencies: - '@svgr/babel-plugin-add-jsx-attribute': 5.4.0 - '@svgr/babel-plugin-remove-jsx-attribute': 5.4.0 - '@svgr/babel-plugin-remove-jsx-empty-expression': 5.0.1 - '@svgr/babel-plugin-replace-jsx-attribute-value': 5.0.1 - '@svgr/babel-plugin-svg-dynamic-title': 5.4.0 - '@svgr/babel-plugin-svg-em-dimensions': 5.4.0 - '@svgr/babel-plugin-transform-react-native-svg': 5.4.0 - '@svgr/babel-plugin-transform-svg-component': 5.5.0 - /@svgr/core@5.5.0: + '@svgr/core@5.5.0': resolution: {integrity: sha512-q52VOcsJPvV3jO1wkPtzTuKlvX7Y3xIcWRpCMtBF3MrteZJtBfQw/+u0B1BHy5ColpQc1/YVTrPEtSYIMNZlrQ==} engines: {node: '>=10'} - dependencies: - '@svgr/plugin-jsx': 5.5.0 - camelcase: 6.3.0 - cosmiconfig: 7.1.0 - transitivePeerDependencies: - - supports-color - /@svgr/hast-util-to-babel-ast@5.5.0: + '@svgr/hast-util-to-babel-ast@5.5.0': resolution: {integrity: sha512-cAaR/CAiZRB8GP32N+1jocovUtvlj0+e65TB50/6Lcime+EA49m/8l+P2ko+XPJ4dw3xaPS3jOL4F2X4KWxoeQ==} engines: {node: '>=10'} - dependencies: - '@babel/types': 7.21.4 - /@svgr/plugin-jsx@5.5.0: + '@svgr/plugin-jsx@5.5.0': resolution: {integrity: sha512-V/wVh33j12hGh05IDg8GpIUXbjAPnTdPTKuP4VNLggnwaHMPNQNae2pRnyTAILWCQdz5GyMqtO488g7CKM8CBA==} engines: {node: '>=10'} - dependencies: - '@babel/core': 7.21.4 - '@svgr/babel-preset': 5.5.0 - '@svgr/hast-util-to-babel-ast': 5.5.0 - svg-parser: 2.0.4 - transitivePeerDependencies: - - supports-color - /@svgr/plugin-svgo@5.5.0: + '@svgr/plugin-svgo@5.5.0': resolution: {integrity: sha512-r5swKk46GuQl4RrVejVwpeeJaydoxkdwkM1mBKOgJLBUJPGaLci6ylg/IjhrRsREKDkr4kbMWdgOtbXEh0fyLQ==} engines: {node: '>=10'} - dependencies: - cosmiconfig: 7.1.0 - deepmerge: 4.3.1 - svgo: 1.3.2 - - /@svgr/rollup@5.5.0: - resolution: {integrity: sha512-EiZmH2VTr+Xzyb6Ga8XtGa9MEbiU3WQnB5vHmqhwAUqibU3uwuwr7MN+QwIh/gtBk1ucMim8BCfcRTlLVREM8A==} - engines: {node: '>=10'} - dependencies: - '@babel/core': 7.21.4 - '@babel/plugin-transform-react-constant-elements': 7.21.3(@babel/core@7.21.4) - '@babel/preset-env': 7.21.4(@babel/core@7.21.4) - '@babel/preset-react': 7.18.6(@babel/core@7.21.4) - '@svgr/core': 5.5.0 - '@svgr/plugin-jsx': 5.5.0 - '@svgr/plugin-svgo': 5.5.0 - rollup-pluginutils: 2.8.2 - transitivePeerDependencies: - - supports-color - dev: true - /@svgr/webpack@5.5.0: + '@svgr/webpack@5.5.0': resolution: {integrity: sha512-DOBOK255wfQxguUta2INKkzPj6AIS6iafZYiYmHn6W3pHlycSRRlvWKCfLDG10fXfLWqE3DJHgRUOyJYmARa7g==} engines: {node: '>=10'} - dependencies: - '@babel/core': 7.21.4 - '@babel/plugin-transform-react-constant-elements': 7.21.3(@babel/core@7.21.4) - '@babel/preset-env': 7.21.4(@babel/core@7.21.4) - '@babel/preset-react': 7.18.6(@babel/core@7.21.4) - '@svgr/core': 5.5.0 - '@svgr/plugin-jsx': 5.5.0 - '@svgr/plugin-svgo': 5.5.0 - loader-utils: 2.0.4 - transitivePeerDependencies: - - supports-color - dev: false - /@swc/core-darwin-arm64@1.3.49: - resolution: {integrity: sha512-g7aIfXh6uPHmhLXdjXQq5t3HAyS/EdvujasW1DIS5k8UqOBaSoCcSGtLIjzcLv3KujqNfYcm118E+12H0nY6fQ==} + '@swc/core-darwin-arm64@1.11.10': + resolution: {integrity: sha512-FWwYyhUu+xRXldXHw4CBP6M0rXQs9gnE5/qodsb+cyOJaTHI8kU6FJtwaC0PiOVxjREdg/DoTrXS4sZUiL881A==} engines: {node: '>=10'} cpu: [arm64] os: [darwin] - requiresBuild: true - dev: false - optional: true - /@swc/core-darwin-x64@1.3.49: - resolution: {integrity: sha512-eSIxVX0YDw40Bre5sAx2BV3DzdIGzmQvCf2yiBvLqiiL6GC0mmuDeWbUCAzdUX6fJ6FUVEBMUVqNOc9oJ2/d5w==} + '@swc/core-darwin-x64@1.11.10': + resolution: {integrity: sha512-NKQ62w81TGR5YAidV3KF7CDY0nu62OWmz6Hl/mB/i8Cd9xPc+MnLwdY1cJOU/DsrU4YnRFSaOfBF4Fx4mKLWxA==} engines: {node: '>=10'} cpu: [x64] os: [darwin] - requiresBuild: true - dev: false - optional: true - /@swc/core-linux-arm-gnueabihf@1.3.49: - resolution: {integrity: sha512-8mj3IcRVr/OJY0mVITz6Z5osNAMJK5GiKDaZ+3QejPLbl6aiu4sH4GmTHDRN14RnaVXOpecsGcUoQmNoNa3u3w==} + '@swc/core-linux-arm-gnueabihf@1.11.10': + resolution: {integrity: sha512-1Vu+ZjoR7M8ShIf0Koi+B1OJ6DsU7jd4Py743KCgKlabvLFrv/uahp5fPJ1kyAUTxFE5d37qWqWLl5NkYDmDtQ==} engines: {node: '>=10'} cpu: [arm] os: [linux] - requiresBuild: true - dev: false - optional: true - /@swc/core-linux-arm64-gnu@1.3.49: - resolution: {integrity: sha512-Rmg9xw6tmpOpf6GKKjpHQGmjfHzqSths5ebI2ahrHlhekzZF2HYmPkVw4bHda8Bja6mbaw8FVBgBHjPU8mMeDA==} + '@swc/core-linux-arm64-gnu@1.11.10': + resolution: {integrity: sha512-mP26821Auyqa+Dce8gFlH4GxxbJ8xJU8H5/iIU8ObK12ulmK75G2VdILoc3gFDKfx3K7IqQkfokW3PAGI9X2Vg==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - requiresBuild: true - dev: false - optional: true - /@swc/core-linux-arm64-musl@1.3.49: - resolution: {integrity: sha512-nlKPYMogAI3Aak6Mlkag8/2AlHAZ/DpH7RjhfMazsaGhD/sQOmYdyY9Al69ejpa419YJuREeeeLoojFlSsd30g==} + '@swc/core-linux-arm64-musl@1.11.10': + resolution: {integrity: sha512-XZ61quwNgTqvbMqpFAa6/ZqoErabocHUHMWQHyShxbqM2nkP1sBe6EgODX6mNSzLn0u+KDVRyQUy9ratt+xbFw==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - requiresBuild: true - dev: false - optional: true - /@swc/core-linux-x64-gnu@1.3.49: - resolution: {integrity: sha512-QOyeJQ6NVi73SJcizbwvIZTiGA/N+BxX9liRrvibumaQmRh8fWjJiLNsv3ODSHeuonak7E8Bf7a7NnSTyu48Mw==} + '@swc/core-linux-x64-gnu@1.11.10': + resolution: {integrity: sha512-BwohorC2nkak8YQuS6IH/70XkhBjqmPbL7KT0NKmr4sstRe52I3F5Vbo30xBckpvT8ZRPvjmjK3FvJ2Rf3PRmw==} engines: {node: '>=10'} cpu: [x64] os: [linux] - requiresBuild: true - dev: false - optional: true - /@swc/core-linux-x64-musl@1.3.49: - resolution: {integrity: sha512-WlDMz+SOpYC9O/ZBUw1oiyWI7HyUCMlf/HS8Fy/kRI3eGoGCUxVTCJ1mP57GdQr4Wg32Y/ZpO2KSNQFWnT8mAw==} + '@swc/core-linux-x64-musl@1.11.10': + resolution: {integrity: sha512-bCaEJVB1+5KscAolNfL6qd3I1bVovhNDShutrTlNXNvjqNavWrX8z8ZfSJ3oK6CvrBzFR6fjCSqkoD+ckKBYBA==} engines: {node: '>=10'} cpu: [x64] os: [linux] - requiresBuild: true - dev: false - optional: true - /@swc/core-win32-arm64-msvc@1.3.49: - resolution: {integrity: sha512-41LZOeI94Za3twib8KOIjnHYAZ+nkBFmboaREsFR1760S7jiMVywqWX8nFZvn/CXj15Fjjgdgyuig+zMREwXwQ==} + '@swc/core-win32-arm64-msvc@1.11.10': + resolution: {integrity: sha512-Gq4svadhEVP7xClzsV8W2/8R/kfEUbJJKIS2fj8hb9lM6/AVs/PVmDiLGye6cYfVpQzkdDsJLm8r4yhSAIFsFQ==} engines: {node: '>=10'} cpu: [arm64] os: [win32] - requiresBuild: true - dev: false - optional: true - /@swc/core-win32-ia32-msvc@1.3.49: - resolution: {integrity: sha512-IdqLPoMKssyAoOCZdNXmnAd6/uyx+Hb9KSfZUHepZaNfwMy6J5XXrOsbYs3v53FH8MtekUUdV+mMX4me9bcv9w==} + '@swc/core-win32-ia32-msvc@1.11.10': + resolution: {integrity: sha512-RkZYTY0pQiHgcoFJwZoFZiEWw4WB/XVLp+y90l4Ar1nnoQQNmfb4FyvWYZbDQgrMGP0Wj5WhZuMXzW12/qI5Kg==} engines: {node: '>=10'} cpu: [ia32] os: [win32] - requiresBuild: true - dev: false - optional: true - /@swc/core-win32-x64-msvc@1.3.49: - resolution: {integrity: sha512-7Fqjo5pS3uIohhSbYSaR0+e/bJdxmQb4oG97FIh5qvlCCGQaQ9UiaEeYy4uK0Ad+Menum1IXCAEiG7RHcl6Eyw==} + '@swc/core-win32-x64-msvc@1.11.10': + resolution: {integrity: sha512-clDl+oAl6YLsqZiGb8NzpEXTdIzCTPCJSRFCeHIldjLlsAs+qsqItry2r2xSAKU1pFv4D7j9WgJmVVxOPgs/Jg==} engines: {node: '>=10'} cpu: [x64] os: [win32] - requiresBuild: true - dev: false - optional: true - /@swc/core@1.3.49: - resolution: {integrity: sha512-br44ZHOfE9YyRGcORSLkHFQHTvhwRcaithBJ1Q5y5iMGpLbH0Wai3GN49L60RvmGwxNJfWzT+E7+rNNR7ewKgA==} + '@swc/core@1.11.10': + resolution: {integrity: sha512-Si27CiYwqJSF3K0HgxugQnjHNfH7YqqD89V+fLpyRHr81uTmCQpF0bnVdRMQ2SGAkCFJACYETRiBSrZOQ660+Q==} engines: {node: '>=10'} - requiresBuild: true peerDependencies: - '@swc/helpers': ^0.5.0 + '@swc/helpers': '*' peerDependenciesMeta: '@swc/helpers': optional: true - optionalDependencies: - '@swc/core-darwin-arm64': 1.3.49 - '@swc/core-darwin-x64': 1.3.49 - '@swc/core-linux-arm-gnueabihf': 1.3.49 - '@swc/core-linux-arm64-gnu': 1.3.49 - '@swc/core-linux-arm64-musl': 1.3.49 - '@swc/core-linux-x64-gnu': 1.3.49 - '@swc/core-linux-x64-musl': 1.3.49 - '@swc/core-win32-arm64-msvc': 1.3.49 - '@swc/core-win32-ia32-msvc': 1.3.49 - '@swc/core-win32-x64-msvc': 1.3.49 - dev: false - - /@swc/helpers@0.4.11: - resolution: {integrity: sha512-rEUrBSGIoSFuYxwBYtlUFMlE2CwGhmW+w9355/5oduSw8e5h2+Tj4UrAGNNgP9915++wj5vkQo0UuOBqOAq4nw==} - dependencies: - tslib: 2.5.0 - dev: false - - /@swc/helpers@0.4.14: - resolution: {integrity: sha512-4C7nX/dvpzB7za4Ql9K81xK3HPxCpHMgwTZVyf+9JQ6VUbn9jjZVN7/Nkdz/Ugzs2CSjqnL/UPXroiVBVHUWUw==} - dependencies: - tslib: 2.5.0 - dev: false - - /@swc/wasm@1.3.49: - resolution: {integrity: sha512-OMWybhrtnJrJR3POwvO6X/RA4HksVKPOsDyGwl5JaCxty8Mu6jlizzmjEhQDMaHF9VNY5kqMMeHypQdxS1Tc+w==} - dev: false - - /@szmarczak/http-timer@4.0.6: - resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==} - engines: {node: '>=10'} - dependencies: - defer-to-connect: 2.0.1 - dev: true - /@tailwindcss/typography@0.5.9(tailwindcss@3.3.1): - resolution: {integrity: sha512-t8Sg3DyynFysV9f4JDOVISGsjazNb48AeIYQwcL+Bsq5uf4RYL75C1giZ43KISjeDGBaTN3Kxh7Xj/vRSMJUUg==} + '@swc/counter@0.1.3': + resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} + + '@swc/helpers@0.5.15': + resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} + + '@swc/types@0.1.19': + resolution: {integrity: sha512-WkAZaAfj44kh/UFdAQcrMP1I0nwRqpt27u+08LMBYMqmQfwwMofYoMh/48NGkMMRfC4ynpfwRbJuu8ErfNloeA==} + + '@tailwindcss/typography@0.5.15': + resolution: {integrity: sha512-AqhlCXl+8grUz8uqExv5OTtgpjuVIwFTSXTrh8y9/pw6q2ek7fJ+Y8ZEVw7EB2DCcuCOtEjf9w3+J3rzts01uA==} peerDependencies: - tailwindcss: '>=3.0.0 || insiders' - dependencies: - lodash.castarray: 4.4.0 - lodash.isplainobject: 4.0.6 - lodash.merge: 4.6.2 - postcss-selector-parser: 6.0.10 - tailwindcss: 3.3.1(postcss@8.4.21)(ts-node@10.8.2) - dev: true + tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20' - /@tanstack/query-core@4.29.1: - resolution: {integrity: sha512-vkPewLEG8ua0efo3SsVT0BcBtkq5RZX8oPhDAyKL+k/rdOYSQTEocfGEXSaBwIwsXeOGBUpfKqI+UmHvNqdWXg==} - dev: false + '@tanstack/query-core@4.36.1': + resolution: {integrity: sha512-DJSilV5+ytBP1FbFcEJovv4rnnm/CokuVvrBEtW/Va9DvuJ3HksbXUJEpI0aV1KtuL4ZoO9AVE6PyNLzF7tLeA==} - /@tanstack/react-query@4.29.1(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-/crv1v+OeuGG6EOvaQmyeo9GCKtH4jbmuhZkvk9ulufRiHcTr/A9+YP9GevEAZzUTdzXMwenpTbyxBGvG2xXvw==} + '@tanstack/react-query@4.36.1': + resolution: {integrity: sha512-y7ySVHFyyQblPl3J3eQBWpXZkliroki3ARnBKsdJchlgt7yJLRDUcf4B8soufgiYt3pEQIkBWBx1N9/ZPIeUWw==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 @@ -7668,707 +5367,419 @@ packages: optional: true react-native: optional: true - dependencies: - '@tanstack/query-core': 4.29.1 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - use-sync-external-store: 1.2.0(react@18.2.0) - dev: false - /@testing-library/dom@8.20.0: - resolution: {integrity: sha512-d9ULIT+a4EXLX3UU8FBjauG9NnsZHkHztXoIcTsOKoOw030fyjheN9svkTULjJxtYag9DZz5Jz5qkWZDPxTFwA==} + '@testing-library/dom@10.4.0': + resolution: {integrity: sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==} + engines: {node: '>=18'} + + '@testing-library/dom@8.20.1': + resolution: {integrity: sha512-/DiOQ5xBxgdYRC8LNk7U+RWat0S3qRLeIw3ZIkMQ9kkVlRmwD/Eg8k8CqIpD6GW7u20JIUOfMKbxtiLutpjQ4g==} engines: {node: '>=12'} - dependencies: - '@babel/code-frame': 7.21.4 - '@babel/runtime': 7.21.0 - '@types/aria-query': 5.0.1 - aria-query: 5.1.3 - chalk: 4.1.2 - dom-accessibility-api: 0.5.16 - lz-string: 1.5.0 - pretty-format: 27.5.1 - dev: true - /@testing-library/dom@9.2.0: - resolution: {integrity: sha512-xTEnpUKiV/bMyEsE5bT4oYA0x0Z/colMtxzUY8bKyPXBNLn/e0V4ZjBZkEhms0xE4pv9QsPfSRu9AWS4y5wGvA==} - engines: {node: '>=14'} - dependencies: - '@babel/code-frame': 7.21.4 - '@babel/runtime': 7.21.0 - '@types/aria-query': 5.0.1 - aria-query: 5.1.3 - chalk: 4.1.2 - dom-accessibility-api: 0.5.16 - lz-string: 1.5.0 - pretty-format: 27.5.1 - dev: true - - /@testing-library/jest-dom@5.16.5: - resolution: {integrity: sha512-N5ixQ2qKpi5OLYfwQmUb/5mSV9LneAcaUfp32pn4yCnpb8r/Yz0pXFPck21dIicKmi+ta5WRAknkZCfA8refMA==} - engines: {node: '>=8', npm: '>=6', yarn: '>=1'} - dependencies: - '@adobe/css-tools': 4.2.0 - '@babel/runtime': 7.21.0 - '@types/testing-library__jest-dom': 5.14.5 - aria-query: 5.1.3 - chalk: 3.0.0 - css.escape: 1.5.1 - dom-accessibility-api: 0.5.16 - lodash: 4.17.21 - redent: 3.0.0 - dev: true + '@testing-library/jest-dom@6.6.2': + resolution: {integrity: sha512-P6GJD4yqc9jZLbe98j/EkyQDTPgqftohZF5FBkHY5BUERZmcf4HeO2k0XaefEg329ux2p21i1A1DmyQ1kKw2Jw==} + engines: {node: '>=14', npm: '>=6', yarn: '>=1'} - /@testing-library/react@13.4.0(react-dom@18.2.0)(react@18.2.0): + '@testing-library/react@13.4.0': resolution: {integrity: sha512-sXOGON+WNTh3MLE9rve97ftaZukN3oNf2KjDy7YTx6hcTO2uuLHuCGynMDhFwGw/jYf4OJ2Qk0i4i79qMNNkyw==} engines: {node: '>=12'} peerDependencies: react: ^18.0.0 react-dom: ^18.0.0 - dependencies: - '@babel/runtime': 7.21.0 - '@testing-library/dom': 8.20.0 - '@types/react-dom': 18.0.11 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - dev: true - /@testing-library/user-event@14.4.3(@testing-library/dom@9.2.0): - resolution: {integrity: sha512-kCUc5MEwaEMakkO5x7aoD+DLi02ehmEM2QCGWvNqAS1dV/fAvORWEjnjsEIvml59M7Y5kCkWN6fCCyPOe8OL6Q==} + '@testing-library/user-event@14.5.2': + resolution: {integrity: sha512-YAh82Wh4TIrxYLmfGcixwD18oIjyC1pFQC2Y01F2lzV2HTMiYrI0nze0FD0ocB//CKS/7jIUgae+adPqxK5yCQ==} engines: {node: '>=12', npm: '>=6'} peerDependencies: '@testing-library/dom': '>=7.21.4' - dependencies: - '@testing-library/dom': 9.2.0 - dev: true - /@tootallnate/once@1.1.2: + '@tootallnate/once@1.1.2': resolution: {integrity: sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==} engines: {node: '>= 6'} - /@tootallnate/once@2.0.0: + '@tootallnate/once@2.0.0': resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} engines: {node: '>= 10'} - /@trysound/sax@0.2.0: + '@trysound/sax@0.2.0': resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==} engines: {node: '>=10.13.0'} - dev: false - - /@ts-morph/common@0.11.1: - resolution: {integrity: sha512-7hWZS0NRpEsNV8vWJzg7FEz6V8MaLNeJOmwmghqUXTpzk16V1LLZhdo+4QvE/+zv4cVci0OviuJFnqhEfoV3+g==} - dependencies: - fast-glob: 3.2.12 - minimatch: 3.1.2 - mkdirp: 1.0.4 - path-browserify: 1.0.1 - dev: true - /@ts-morph/common@0.12.3: + '@ts-morph/common@0.12.3': resolution: {integrity: sha512-4tUmeLyXJnJWvTFOKtcNJ1yh0a3SsTLi2MUoyj8iUNznFRN1ZquaNe7Oukqrnki2FzZkm0J9adCNLDZxUzvj+w==} - dependencies: - fast-glob: 3.2.12 - minimatch: 3.1.2 - mkdirp: 1.0.4 - path-browserify: 1.0.1 - dev: true - /@tsconfig/node10@1.0.9: - resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==} + '@tsconfig/node10@1.0.11': + resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} - /@tsconfig/node12@1.0.11: + '@tsconfig/node12@1.0.11': resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} - /@tsconfig/node14@1.0.3: + '@tsconfig/node14@1.0.3': resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} - /@tsconfig/node16@1.0.3: - resolution: {integrity: sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==} + '@tsconfig/node16@1.0.4': + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} - /@types/acorn@4.0.6: - resolution: {integrity: sha512-veQTnWP+1D/xbxVrPC3zHnCZRjSrKfhbMUlEA43iMZLu7EsnTtkJklIuwrCPbOi8YkvDQAiW05VQQFvvz9oieQ==} - dependencies: - '@types/estree': 1.0.1 - dev: true + '@tufjs/canonical-json@1.0.0': + resolution: {integrity: sha512-QTnf++uxunWvG2z3UFNzAoQPHxnSXOwtaI3iJ+AohhV+5vONuArPjJE7aPXPVXfXJsqrVbZBu9b81AJoSd09IQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - /@types/archiver@5.3.2: - resolution: {integrity: sha512-IctHreBuWE5dvBDz/0WeKtyVKVRs4h75IblxOACL92wU66v+HGAfEYAOyXkOFphvRJMhuXdI9huDXpX0FC6lCw==} - dependencies: - '@types/readdir-glob': 1.1.1 - dev: true + '@tufjs/models@1.0.4': + resolution: {integrity: sha512-qaGV9ltJP0EO25YfFUPhxRVK0evXFIAGicsVXuRim4Ed9cjPxYhNnNJ49SFmbeLgtxpslIkX317IgpfcHPVj/A==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - /@types/aria-query@5.0.1: - resolution: {integrity: sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q==} - dev: true + '@types/archiver@5.3.4': + resolution: {integrity: sha512-Lj7fLBIMwYFgViVVZHEdExZC3lVYsl+QL0VmdNdIzGZH544jHveYWij6qdnBgJQDnR7pMKliN9z2cPZFEbhyPw==} - /@types/babel__core@7.20.0: - resolution: {integrity: sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==} - dependencies: - '@babel/parser': 7.21.4 - '@babel/types': 7.21.4 - '@types/babel__generator': 7.6.4 - '@types/babel__template': 7.4.1 - '@types/babel__traverse': 7.18.3 + '@types/aria-query@5.0.4': + resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} - /@types/babel__generator@7.6.4: - resolution: {integrity: sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==} - dependencies: - '@babel/types': 7.21.4 + '@types/babel__core@7.20.5': + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} - /@types/babel__template@7.4.1: - resolution: {integrity: sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==} - dependencies: - '@babel/parser': 7.21.4 - '@babel/types': 7.21.4 + '@types/babel__generator@7.6.8': + resolution: {integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==} - /@types/babel__traverse@7.18.3: - resolution: {integrity: sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==} - dependencies: - '@babel/types': 7.21.4 + '@types/babel__template@7.4.4': + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} - /@types/body-parser@1.19.2: - resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==} - dependencies: - '@types/connect': 3.4.35 - '@types/node': 18.15.11 - dev: false + '@types/babel__traverse@7.20.6': + resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==} - /@types/bonjour@3.5.10: - resolution: {integrity: sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==} - dependencies: - '@types/node': 18.15.11 - dev: false + '@types/body-parser@1.19.5': + resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} - /@types/cacheable-request@6.0.3: - resolution: {integrity: sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==} - dependencies: - '@types/http-cache-semantics': 4.0.1 - '@types/keyv': 3.1.4 - '@types/node': 18.15.11 - '@types/responselike': 1.0.0 - dev: true + '@types/bonjour@3.5.13': + resolution: {integrity: sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==} - /@types/chalk@2.2.0: - resolution: {integrity: sha512-1zzPV9FDe1I/WHhRkf9SNgqtRJWZqrBWgu7JGveuHmmyR9CnAPCie2N/x+iHrgnpYBIcCJWHBoMRv2TRWktsvw==} - deprecated: This is a stub types definition for chalk (https://github.com/chalk/chalk). chalk provides its own type definitions, so you don't need @types/chalk installed! - dependencies: - chalk: 5.2.0 - dev: true + '@types/caseless@0.12.5': + resolution: {integrity: sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==} - /@types/cli-progress@3.11.0: - resolution: {integrity: sha512-XhXhBv1R/q2ahF3BM7qT5HLzJNlIL0wbcGyZVjqOTqAybAnsLisd7gy1UCyIqpL+5Iv6XhlSyzjLCnI2sIdbCg==} - dependencies: - '@types/node': 18.15.11 - dev: true + '@types/chalk@2.2.4': + resolution: {integrity: sha512-pb/QoGqtCpH2famSp72qEsXkNzcErlVmiXlQ/ww+5AddD8TmmYS7EWg5T20YiNCAiTgs8pMf2G8SJG5h/ER1ZQ==} + deprecated: This is a stub types definition. chalk provides its own type definitions, so you do not need this installed. - /@types/connect-history-api-fallback@1.3.5: - resolution: {integrity: sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==} - dependencies: - '@types/express-serve-static-core': 4.17.33 - '@types/node': 18.15.11 - dev: false + '@types/cli-progress@3.11.6': + resolution: {integrity: sha512-cE3+jb9WRlu+uOSAugewNpITJDt1VF8dHOopPO4IABFc3SXYL5WE/+PTz/FCdZRRfIujiWW3n3aMbv1eIGVRWA==} - /@types/connect@3.4.35: - resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==} - dependencies: - '@types/node': 18.15.11 - dev: false + '@types/commander@2.12.5': + resolution: {integrity: sha512-YXGZ/rz+s57VbzcvEV9fUoXeJlBt5HaKu5iUheiIWNsJs23bz6AnRuRiZBRVBLYyPnixNvVnuzM5pSaxr8Yp/g==} + deprecated: This is a stub types definition. commander provides its own type definitions, so you do not need this installed. - /@types/cookie@0.4.1: - resolution: {integrity: sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==} - dev: true + '@types/connect-history-api-fallback@1.5.4': + resolution: {integrity: sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==} - /@types/cookie@0.5.1: - resolution: {integrity: sha512-COUnqfB2+ckwXXSFInsFdOAWQzCCx+a5hq2ruyj+Vjund94RJQd4LG2u9hnvJrTgunKAaax7ancBYlDrNYxA0g==} - dev: false + '@types/connect@3.4.38': + resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} - /@types/cross-spawn@6.0.2: - resolution: {integrity: sha512-KuwNhp3eza+Rhu8IFI5HUXRP0LIhqH5cAjubUvGXXthh4YYBuP2ntwEX+Cz8GJoZUHlKo247wPWOfA9LYEq4cw==} - dependencies: - '@types/node': 18.15.11 - dev: true + '@types/cookie@1.0.0': + resolution: {integrity: sha512-mGFXbkDQJ6kAXByHS7QAggRXgols0mAdP4MuXgloGY1tXokvzaFFM4SMqWvf7AH0oafI7zlFJwoGWzmhDqTZ9w==} + deprecated: This is a stub types definition. cookie provides its own type definitions, so you do not need this installed. - /@types/crypto-js@4.1.1: - resolution: {integrity: sha512-BG7fQKZ689HIoc5h+6D2Dgq1fABRa0RbBWKBd9SP/MVRVXROflpm5fhwyATX5duFmbStzyzyycPB8qUYKDH3NA==} - dev: true + '@types/cors@2.8.17': + resolution: {integrity: sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==} - /@types/debug@4.1.7: - resolution: {integrity: sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==} - dependencies: - '@types/ms': 0.7.31 - dev: true + '@types/cross-spawn@6.0.2': + resolution: {integrity: sha512-KuwNhp3eza+Rhu8IFI5HUXRP0LIhqH5cAjubUvGXXthh4YYBuP2ntwEX+Cz8GJoZUHlKo247wPWOfA9LYEq4cw==} - /@types/dlv@1.1.2: - resolution: {integrity: sha512-OyiZ3jEKu7RtGO1yp9oOdK0cTwZ/10oE9PDJ6fyN3r9T5wkyOcvr6awdugjYdqF6KVO5eUvt7jx7rk2Eylufow==} - dev: false + '@types/crypto-js@4.2.2': + resolution: {integrity: sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ==} - /@types/docker-modem@3.0.2: - resolution: {integrity: sha512-qC7prjoEYR2QEe6SmCVfB1x3rfcQtUr1n4x89+3e0wSTMQ/KYCyf+/RAA9n2tllkkNc6//JMUZePdFRiGIWfaQ==} - dependencies: - '@types/node': 18.15.11 - '@types/ssh2': 1.11.11 - dev: true + '@types/d3-array@3.2.1': + resolution: {integrity: sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==} - /@types/dockerode@3.3.16: - resolution: {integrity: sha512-ZAX2VrkTjwjk1808T8m6vMr+CFXSLiDD+tkEkLThI+v83AfzlYQZEWfZKwFyk1PWopSXkdDunmIhrF7sxt+zWg==} - dependencies: - '@types/docker-modem': 3.0.2 - '@types/node': 18.15.11 - dev: true + '@types/d3-color@3.1.3': + resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==} - /@types/eslint-scope@3.7.4: - resolution: {integrity: sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==} - dependencies: - '@types/eslint': 8.37.0 - '@types/estree': 0.0.51 + '@types/d3-ease@3.0.2': + resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==} - /@types/eslint@8.37.0: - resolution: {integrity: sha512-Piet7dG2JBuDIfohBngQ3rCt7MgO9xCO4xIMKxBThCq5PNRB91IjlJ10eJVwfoNtvTErmxLzwBZ7rHZtbOMmFQ==} - dependencies: - '@types/estree': 1.0.0 - '@types/json-schema': 7.0.11 + '@types/d3-interpolate@3.0.4': + resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==} - /@types/estree-jsx@0.0.1: - resolution: {integrity: sha512-gcLAYiMfQklDCPjQegGn0TBAn9it05ISEsEhlKQUddIk7o2XDokOcTN7HBO8tznM0D9dGezvHEfRZBfZf6me0A==} - dependencies: - '@types/estree': 1.0.1 - dev: true + '@types/d3-path@3.1.0': + resolution: {integrity: sha512-P2dlU/q51fkOc/Gfl3Ul9kicV7l+ra934qBFXCFhrZMOL6du1TM0pm1ThYvENukyOn5h9v+yMJ9Fn5JK4QozrQ==} - /@types/estree-jsx@1.0.0: - resolution: {integrity: sha512-3qvGd0z8F2ENTGr/GG1yViqfiKmRfrXVx5sJyHGFu3z7m5g5utCQtGp/g29JnjflhtQJBv1WDQukHiT58xPcYQ==} - dependencies: - '@types/estree': 1.0.1 - dev: true + '@types/d3-scale@4.0.8': + resolution: {integrity: sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==} - /@types/estree@0.0.39: - resolution: {integrity: sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==} + '@types/d3-shape@3.1.6': + resolution: {integrity: sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA==} - /@types/estree@0.0.51: - resolution: {integrity: sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==} + '@types/d3-time@3.0.3': + resolution: {integrity: sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw==} - /@types/estree@1.0.0: - resolution: {integrity: sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==} + '@types/d3-timer@3.0.2': + resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==} - /@types/estree@1.0.1: - resolution: {integrity: sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==} - dev: true + '@types/debug@4.1.7': + resolution: {integrity: sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==} - /@types/expect@1.20.4: - resolution: {integrity: sha512-Q5Vn3yjTDyCMV50TB6VRIbQNxSE4OmZR86VSbGaNpfUolm0iePBB4KdEEHmxoY5sT2+2DIvXW0rvMDP2nHZ4Mg==} + '@types/dlv@1.1.4': + resolution: {integrity: sha512-m8KmImw4Jt+4rIgupwfivrWEOnj1LzkmKkqbh075uG13eTQ1ZxHWT6T0vIdSQhLIjQCiR0n0lZdtyDOPO1x2Mw==} - /@types/express-serve-static-core@4.17.33: - resolution: {integrity: sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==} - dependencies: - '@types/node': 18.15.11 - '@types/qs': 6.9.7 - '@types/range-parser': 1.2.4 - dev: false + '@types/docker-modem@3.0.6': + resolution: {integrity: sha512-yKpAGEuKRSS8wwx0joknWxsmLha78wNMe9R2S3UNsVOkZded8UqOrV8KoeDXoXsjndxwyF3eIhyClGbO1SEhEg==} - /@types/express@4.17.17: - resolution: {integrity: sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==} - dependencies: - '@types/body-parser': 1.19.2 - '@types/express-serve-static-core': 4.17.33 - '@types/qs': 6.9.7 - '@types/serve-static': 1.15.1 - dev: false + '@types/dockerode@3.3.31': + resolution: {integrity: sha512-42R9eoVqJDSvVspV89g7RwRqfNExgievLNWoHkg7NoWIqAmavIbgQBb4oc0qRtHkxE+I3Xxvqv7qVXFABKPBTg==} - /@types/firebase@3.2.1: - resolution: {integrity: sha512-G8XgHMu2jHlElfc2xVNaYP50F0qrqeTCjgeG1v5b4SRwWG4XKC4fCuEdVZuZaMRmVygcnbRZBAo9O7RsDvmkGQ==} - deprecated: This is a stub types definition for Firebase API (https://www.firebase.com/docs/javascript/firebase). Firebase API provides its own type definitions, so you don't need @types/firebase installed! - dependencies: - firebase: 9.19.1 - transitivePeerDependencies: - - encoding - dev: true + '@types/eslint-scope@3.7.7': + resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} - /@types/glob@7.2.0: - resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==} - dependencies: - '@types/minimatch': 5.1.2 - '@types/node': 18.15.11 - dev: true + '@types/eslint@8.56.12': + resolution: {integrity: sha512-03ruubjWyOHlmljCVoxSuNDdmfZDzsrrz0P2LeJsOXr+ZwFQ+0yQIwNCwt/GYhV7Z31fgtXJTAEs+FYlEL851g==} - /@types/glob@8.1.0: - resolution: {integrity: sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w==} - dependencies: - '@types/minimatch': 5.1.2 - '@types/node': 16.11.6 + '@types/estree@0.0.39': + resolution: {integrity: sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==} - /@types/graceful-fs@4.1.6: - resolution: {integrity: sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==} - dependencies: - '@types/node': 18.15.11 + '@types/estree@1.0.6': + resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} - /@types/hast@2.3.4: - resolution: {integrity: sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g==} - dependencies: - '@types/unist': 2.0.6 - dev: true + '@types/expect@1.20.4': + resolution: {integrity: sha512-Q5Vn3yjTDyCMV50TB6VRIbQNxSE4OmZR86VSbGaNpfUolm0iePBB4KdEEHmxoY5sT2+2DIvXW0rvMDP2nHZ4Mg==} - /@types/html-minifier-terser@6.1.0: - resolution: {integrity: sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==} - dev: false + '@types/express-serve-static-core@4.19.6': + resolution: {integrity: sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==} - /@types/http-cache-semantics@4.0.1: - resolution: {integrity: sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==} - dev: true + '@types/express-serve-static-core@5.0.1': + resolution: {integrity: sha512-CRICJIl0N5cXDONAdlTv5ShATZ4HEwk6kDDIW2/w9qOWKg+NU/5F8wYRWCrONad0/UKkloNSmmyN/wX4rtpbVA==} - /@types/http-proxy@1.17.10: - resolution: {integrity: sha512-Qs5aULi+zV1bwKAg5z1PWnDXWmsn+LxIvUGv6E2+OOMYhclZMO+OXd9pYVf2gLykf2I7IV2u7oTHwChPNsvJ7g==} - dependencies: - '@types/node': 18.15.11 - dev: false + '@types/express@4.17.21': + resolution: {integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==} - /@types/istanbul-lib-coverage@2.0.4: - resolution: {integrity: sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==} + '@types/glob@7.2.0': + resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==} - /@types/istanbul-lib-report@3.0.0: - resolution: {integrity: sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==} - dependencies: - '@types/istanbul-lib-coverage': 2.0.4 + '@types/graceful-fs@4.1.9': + resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} - /@types/istanbul-reports@3.0.1: - resolution: {integrity: sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==} - dependencies: - '@types/istanbul-lib-report': 3.0.0 + '@types/html-minifier-terser@6.1.0': + resolution: {integrity: sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==} - /@types/jest@29.5.0: - resolution: {integrity: sha512-3Emr5VOl/aoBwnWcH/EFQvlSAmjV+XtV9GGu5mwdYew5vhQh0IUZx/60x0TzHDu09Bi7HMx10t/namdJw5QIcg==} - dependencies: - expect: 29.5.0 - pretty-format: 29.5.0 - dev: true + '@types/http-errors@2.0.4': + resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==} - /@types/js-cookie@2.2.7: - resolution: {integrity: sha512-aLkWa0C0vO5b4Sr798E26QgOkss68Un0bLjs7u9qxzPT5CG+8DuNTffWES58YzJs3hrVAOs1wonycqEBqNJubA==} - dev: false + '@types/http-proxy@1.17.15': + resolution: {integrity: sha512-25g5atgiVNTIv0LBDTg1H74Hvayx0ajtJPLLcYE3whFv75J0pWNtOBzaXJQgDTmrX1bx5U9YC2w/n65BN1HwRQ==} - /@types/js-yaml@4.0.5: - resolution: {integrity: sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==} - dev: true + '@types/inquirer@9.0.7': + resolution: {integrity: sha512-Q0zyBupO6NxGRZut/JdmqYKOnN95Eg5V8Csg3PGKkP+FnvsUZx1jAyK7fztIszxxMuoBA6E3KXWvdZVXIpx60g==} - /@types/json-schema@7.0.11: - resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==} + '@types/istanbul-lib-coverage@2.0.6': + resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} - /@types/json5@0.0.29: - resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + '@types/istanbul-lib-report@3.0.3': + resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==} - /@types/jsonwebtoken@8.5.9: - resolution: {integrity: sha512-272FMnFGzAVMGtu9tkr29hRL6bZj4Zs1KZNeHLnKqAvp06tAIcarTMwOh8/8bz4FmKRcMxZhZNeUAQsNLoiPhg==} - dependencies: - '@types/node': 18.11.12 - dev: true + '@types/istanbul-reports@3.0.4': + resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} - /@types/jsonwebtoken@9.0.1: - resolution: {integrity: sha512-c5ltxazpWabia/4UzhIoaDcIza4KViOQhdbjRlfcIGVnsE3c3brkz9Z+F/EeJIECOQP7W7US2hNE930cWWkPiw==} - dependencies: - '@types/node': 18.15.11 - dev: false + '@types/jest@29.5.14': + resolution: {integrity: sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==} - /@types/keyv@3.1.4: - resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} - dependencies: - '@types/node': 18.15.11 - dev: true + '@types/js-cookie@2.2.7': + resolution: {integrity: sha512-aLkWa0C0vO5b4Sr798E26QgOkss68Un0bLjs7u9qxzPT5CG+8DuNTffWES58YzJs3hrVAOs1wonycqEBqNJubA==} - /@types/linkify-it@3.0.2: - resolution: {integrity: sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA==} - dev: false - optional: true + '@types/js-yaml@4.0.9': + resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==} - /@types/lodash@4.14.192: - resolution: {integrity: sha512-km+Vyn3BYm5ytMO13k9KTp27O75rbQ0NFw+U//g+PX7VZyjCioXaRFisqSIJRECljcTv73G3i6BpglNGHgUQ5A==} - dev: true + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - /@types/long@4.0.2: - resolution: {integrity: sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==} + '@types/json5@0.0.29': + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} - /@types/markdown-it@12.2.3: - resolution: {integrity: sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==} - dependencies: - '@types/linkify-it': 3.0.2 - '@types/mdurl': 1.0.2 - dev: false - optional: true + '@types/jsonwebtoken@9.0.7': + resolution: {integrity: sha512-ugo316mmTYBl2g81zDFnZ7cfxlut3o+/EQdaP7J8QN2kY6lJ22hmQYCK5EHcJHbrW+dkCGSCPgbG8JtYj6qSrg==} - /@types/mdast@3.0.11: - resolution: {integrity: sha512-Y/uImid8aAwrEA24/1tcRZwpxX3pIFTSilcNDKSPn+Y2iDywSEachzRuvgAYYLR3wpGXAsMbv5lvKLDZLeYPAw==} - dependencies: - '@types/unist': 2.0.6 - dev: true + '@types/lodash@4.17.13': + resolution: {integrity: sha512-lfx+dftrEZcdBPczf9d0Qv0x+j/rfNCMuC6OcfXmO8gkfeNAY88PgKUbvG56whcN23gc27yenwF6oJZXGFpYxg==} - /@types/mdurl@1.0.2: - resolution: {integrity: sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==} + '@types/long@4.0.2': + resolution: {integrity: sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==} - /@types/mime@3.0.1: - resolution: {integrity: sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==} - dev: false + '@types/mime@1.3.5': + resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} - /@types/minimatch@3.0.5: + '@types/minimatch@3.0.5': resolution: {integrity: sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==} - /@types/minimatch@5.1.2: + '@types/minimatch@5.1.2': resolution: {integrity: sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==} - /@types/minimist@1.2.2: - resolution: {integrity: sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==} - dev: true + '@types/ms@0.7.34': + resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==} - /@types/ms@0.7.31: - resolution: {integrity: sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==} - dev: true + '@types/node-fetch@2.6.11': + resolution: {integrity: sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==} - /@types/node@14.18.33: - resolution: {integrity: sha512-qelS/Ra6sacc4loe/3MSjXNL1dNQ/GjxNHVzuChwMfmk7HuycRLVQN2qNY3XahK+fZc5E2szqQSKUyAF0E+2bg==} - dev: true + '@types/node-forge@1.3.11': + resolution: {integrity: sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==} - /@types/node@15.14.9: + '@types/node@15.14.9': resolution: {integrity: sha512-qjd88DrCxupx/kJD5yQgZdcYKZKSIGBVDIBE1/LTGcNm3d2Np/jxojkdePDdfnBHJc5W7vSMpbJ1aB7p/Py69A==} - /@types/node@16.11.6: - resolution: {integrity: sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w==} + '@types/node@18.19.61': + resolution: {integrity: sha512-z8fH66NcVkDzBItOao+Nyh0fiy7CYdxIyxnNCcZ60aY0I+EA/y4TSi/S/W9i8DIQvwVo7a0pgzAxmDeNnqrpkw==} - /@types/node@16.18.23: - resolution: {integrity: sha512-XAMpaw1s1+6zM+jn2tmw8MyaRDIJfXxqmIQIS0HfoGYPuf7dUWeiUKopwq13KFX9lEp1+THGtlaaYx39Nxr58g==} + '@types/node@22.13.10': + resolution: {integrity: sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==} - /@types/node@18.11.12: - resolution: {integrity: sha512-FgD3NtTAKvyMmD44T07zz2fEf+OKwutgBCEVM8GcvMGVGaDktiLNTDvPwC/LUe3PinMW+X6CuLOF2Ui1mAlSXg==} + '@types/node@22.14.1': + resolution: {integrity: sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw==} - /@types/node@18.15.11: - resolution: {integrity: sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==} + '@types/nodemailer@6.4.17': + resolution: {integrity: sha512-I9CCaIp6DTldEg7vyUTZi8+9Vo0hi1/T8gv3C89yk1rSAAzoKQ8H8ki/jBYJSFoH/BisgLP8tkZMlQ91CIquww==} - /@types/nodemailer@6.4.7: - resolution: {integrity: sha512-f5qCBGAn/f0qtRcd4SEn88c8Fp3Swct1731X4ryPKqS61/A3LmmzN8zaEz7hneJvpjFbUUgY7lru/B/7ODTazg==} - dependencies: - '@types/node': 18.15.11 - dev: true + '@types/normalize-package-data@2.4.4': + resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} - /@types/normalize-package-data@2.4.1: - resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} + '@types/normalize-path@3.0.2': + resolution: {integrity: sha512-DO++toKYPaFn0Z8hQ7Tx+3iT9t77IJo/nDiqTXilgEP+kPNIYdpS9kh3fXuc53ugqwp9pxC1PVjCpV1tQDyqMA==} - /@types/parse-json@4.0.0: - resolution: {integrity: sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==} + '@types/parse-json@4.0.2': + resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} - /@types/pg-promise@5.4.3: + '@types/pegjs@0.10.6': + resolution: {integrity: sha512-eLYXDbZWXh2uxf+w8sXS8d6KSoXTswfps6fvCUuVAGN8eRpfe7h9eSRydxiSJvo9Bf+GzifsDOr9TMQlmJdmkw==} + + '@types/pg-cursor@2.7.2': + resolution: {integrity: sha512-m3xT8bVFCvx98LuzbvXyuCdT/Hjdd/v8ml4jL4K1QF70Y8clOfCFdgoaEB1FWdcSwcpoFYZTJQaMD9/GQ27efQ==} + + '@types/pg-promise@5.4.3': resolution: {integrity: sha512-ABgy5hzmcyRZxLNG6HUafpxbpmXGeVJ5dopKwssEsFeA7rpRakdoodt75HJNIjQB4He+s3PTfG1AoWQWm7OuCg==} deprecated: This is a stub types definition for pg-promise (https://github.com/vitaly-t/pg-promise). pg-promise provides its own type definitions, so you don't need @types/pg-promise installed! - dependencies: - pg-promise: 11.4.3 - transitivePeerDependencies: - - pg-native - dev: false - /@types/pg-query-stream@3.4.0(pg@8.10.0): + '@types/pg-query-stream@3.4.0': resolution: {integrity: sha512-oqGAu+5CMPAVNqemN9xYT3LBDy2qXBke15cstEuVYIvJL6lcKSNKeG61fONlVrxTYIFi9sPuGVtIweanrEZcJw==} deprecated: This is a stub types definition. pg-query-stream provides its own type definitions, so you do not need this installed. - dependencies: - pg-query-stream: 4.4.0(pg@8.10.0) - transitivePeerDependencies: - - pg - dev: true - /@types/pg@8.6.6: - resolution: {integrity: sha512-O2xNmXebtwVekJDD+02udOncjVcMZQuTEQEMpKJ0ZRf5E7/9JJX3izhKUcUifBkyKpljyUM6BTgy2trmviKlpw==} - dependencies: - '@types/node': 18.15.11 - pg-protocol: 1.6.0 - pg-types: 2.2.0 - dev: true + '@types/pg@8.11.15': + resolution: {integrity: sha512-vtxIjQ/s9HxJAkGrYha6nQEeBZn+P4q0KUAOlUmXX4nJ9niX5hSE3x3wEigLJhklEfM6jmPh3wWPOR3zot8v3g==} - /@types/prettier@2.7.2: - resolution: {integrity: sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==} + '@types/prettier@2.7.3': + resolution: {integrity: sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==} - /@types/prop-types@15.7.5: - resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==} + '@types/prismjs@1.26.5': + resolution: {integrity: sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ==} - /@types/q@1.5.5: - resolution: {integrity: sha512-L28j2FcJfSZOnL1WBjDYp2vUHCeIFlyYI/53EwD/rKUBQ7MtUUfbQWiyKJGpcnv4/WgrhWsFKrcPstcAt/J0tQ==} + '@types/prop-types@15.7.13': + resolution: {integrity: sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==} - /@types/qs@6.9.7: - resolution: {integrity: sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==} - dev: false + '@types/q@1.5.8': + resolution: {integrity: sha512-hroOstUScF6zhIi+5+x0dzqrHA1EJi+Irri6b1fxolMTqqHIV/Cg77EtnQcZqZCu8hR3mX2BzIxN4/GzI68Kfw==} - /@types/range-parser@1.2.4: - resolution: {integrity: sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==} - dev: false + '@types/qs@6.9.16': + resolution: {integrity: sha512-7i+zxXdPD0T4cKDuxCUXJ4wHcsJLwENa6Z3dCu8cfCK743OGy5Nu1RmAGqDPsoTDINVEcdXKRvR/zre+P2Ku1A==} - /@types/react-dom@18.0.11: - resolution: {integrity: sha512-O38bPbI2CWtgw/OoQoY+BRelw7uysmXbWvw3nLWO21H1HSh+GOlqPuXshJfjmpNlKiiSDG9cc1JZAaMmVdcTlw==} - dependencies: - '@types/react': 18.0.28 + '@types/range-parser@1.2.7': + resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} - /@types/react@18.0.28: - resolution: {integrity: sha512-RD0ivG1kEztNBdoAK7lekI9M+azSnitIn85h4iOiaLjaTrMjzslhaqCGaI4IyCJ1RljWiLCEu4jyrLLgqxBTew==} - dependencies: - '@types/prop-types': 15.7.5 - '@types/scheduler': 0.16.3 - csstype: 3.1.2 + '@types/react-dom@18.3.0': + resolution: {integrity: sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==} - /@types/readdir-glob@1.1.1: - resolution: {integrity: sha512-ImM6TmoF8bgOwvehGviEj3tRdRBbQujr1N+0ypaln/GWjaerOB26jb93vsRHmdMtvVQZQebOlqt2HROark87mQ==} - dependencies: - '@types/node': 18.15.11 - dev: true + '@types/react-dom@19.0.4': + resolution: {integrity: sha512-4fSQ8vWFkg+TGhePfUzVmat3eC14TXYSsiiDSLI0dVLsrm9gZFABjPy/Qu6TKgl1tq1Bu1yDsuQgY3A3DOjCcg==} + peerDependencies: + '@types/react': ^19.0.0 - /@types/resolve@0.0.8: - resolution: {integrity: sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ==} - dependencies: - '@types/node': 18.15.11 - dev: true + '@types/react@18.3.3': + resolution: {integrity: sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==} + + '@types/react@19.0.10': + resolution: {integrity: sha512-JuRQ9KXLEjaUNjTWpzuR231Z2WpIwczOkBEIvbHNCzQefFIT0L8IqE6NV6ULLyC1SI/i234JnDoMkfg+RjQj2g==} - /@types/resolve@1.17.1: + '@types/readdir-glob@1.1.5': + resolution: {integrity: sha512-raiuEPUYqXu+nvtY2Pe8s8FEmZ3x5yAH4VkLdihcPdalvsHltomrRC9BzuStrJ9yk06470hS0Crw0f1pXqD+Hg==} + + '@types/request@2.48.12': + resolution: {integrity: sha512-G3sY+NpsA9jnwm0ixhAFQSJ3Q9JkpLZpJbI3GMv0mIAT0y3mRabYeINzal5WOChIiaTEGQYlHOKgkaM9EisWHw==} + + '@types/resolve@1.17.1': resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==} - dependencies: - '@types/node': 18.15.11 - dev: false - /@types/resolve@1.20.2: + '@types/resolve@1.20.2': resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} - dev: true - - /@types/responselike@1.0.0: - resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==} - dependencies: - '@types/node': 18.15.11 - dev: true - /@types/retry@0.12.0: + '@types/retry@0.12.0': resolution: {integrity: sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==} - dev: false - - /@types/rimraf@3.0.2: - resolution: {integrity: sha512-F3OznnSLAUxFrCEu/L5PY8+ny8DtcFRjx7fZZ9bycvXRi3KPTRS9HOitGZwvPg0juRhXFWIeKX58cnX5YqLohQ==} - dependencies: - '@types/glob': 8.1.0 - '@types/node': 18.15.11 - dev: false - optional: true - /@types/scheduler@0.16.3: - resolution: {integrity: sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==} + '@types/semver@7.5.8': + resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} - /@types/semver@7.3.13: - resolution: {integrity: sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==} - dev: false + '@types/send@0.17.4': + resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==} - /@types/serve-index@1.9.1: - resolution: {integrity: sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==} - dependencies: - '@types/express': 4.17.17 - dev: false + '@types/serve-index@1.9.4': + resolution: {integrity: sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==} - /@types/serve-static@1.15.1: - resolution: {integrity: sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==} - dependencies: - '@types/mime': 3.0.1 - '@types/node': 18.15.11 - dev: false + '@types/serve-static@1.15.7': + resolution: {integrity: sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==} - /@types/sockjs@0.3.33: - resolution: {integrity: sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==} - dependencies: - '@types/node': 18.15.11 - dev: false + '@types/sockjs@0.3.36': + resolution: {integrity: sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==} - /@types/ssh2-streams@0.1.9: - resolution: {integrity: sha512-I2J9jKqfmvXLR5GomDiCoHrEJ58hAOmFrekfFqmCFd+A6gaEStvWnPykoWUwld1PNg4G5ag1LwdA+Lz1doRJqg==} - dependencies: - '@types/node': 18.15.11 - dev: true + '@types/ssh2-streams@0.1.12': + resolution: {integrity: sha512-Sy8tpEmCce4Tq0oSOYdfqaBpA3hDM8SoxoFh5vzFsu2oL+znzGz8oVWW7xb4K920yYMUY+PIG31qZnFMfPWNCg==} - /@types/ssh2@0.5.52: + '@types/ssh2@0.5.52': resolution: {integrity: sha512-lbLLlXxdCZOSJMCInKH2+9V/77ET2J6NPQHpFI0kda61Dd1KglJs+fPQBchizmzYSOJBgdTajhPqBO1xxLywvg==} - dependencies: - '@types/node': 18.15.11 - '@types/ssh2-streams': 0.1.9 - dev: true - /@types/ssh2@1.11.11: - resolution: {integrity: sha512-LdnE7UBpvHCgUznvn2fwLt2hkaENcKPFqOyXGkvyTLfxCXBN6roc1RmECNYuzzbHePzD3PaAov5rri9hehzx9Q==} - dependencies: - '@types/node': 18.15.11 - dev: true + '@types/ssh2@1.15.1': + resolution: {integrity: sha512-ZIbEqKAsi5gj35y4P4vkJYly642wIbY6PqoN0xiyQGshKUGXR9WQjF/iF9mXBQ8uBKy3ezfsCkcoHKhd0BzuDA==} + + '@types/stack-utils@2.0.3': + resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} + + '@types/strip-bom@3.0.0': + resolution: {integrity: sha512-xevGOReSYGM7g/kUBZzPqCrR/KYAo+F0yiPc85WFTJa0MSLtyFTVTU6cJu/aV4mid7IffDIWqo69THF2o4JiEQ==} - /@types/stack-utils@2.0.1: - resolution: {integrity: sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==} + '@types/strip-json-comments@0.0.30': + resolution: {integrity: sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==} - /@types/stripe@8.0.417: + '@types/stripe@8.0.417': resolution: {integrity: sha512-PTuqskh9YKNENnOHGVJBm4sM0zE8B1jZw1JIskuGAPkMB+OH236QeN8scclhYGPA4nG6zTtPXgwpXdp+HPDTVw==} deprecated: This is a stub types definition. stripe provides its own type definitions, so you do not need this installed. - dependencies: - stripe: 11.18.0 - dev: true - /@types/testing-library__jest-dom@5.14.5: - resolution: {integrity: sha512-SBwbxYoyPIvxHbeHxTZX2Pe/74F/tX2/D3mMvzabdeJ25bBojfW0TyB8BHrbq/9zaaKICJZjLP+8r6AeZMFCuQ==} - dependencies: - '@types/jest': 29.5.0 - dev: true + '@types/through@0.0.33': + resolution: {integrity: sha512-HsJ+z3QuETzP3cswwtzt2vEIiHBk/dCcHGhbmG5X3ecnwFD/lPrMpliGXxSCg03L9AhrdwA4Oz/qfspkDW+xGQ==} - /@types/trusted-types@2.0.3: - resolution: {integrity: sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g==} - dev: false + '@types/tough-cookie@4.0.5': + resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==} - /@types/unist@2.0.6: - resolution: {integrity: sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==} - dev: true + '@types/trusted-types@2.0.7': + resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} - /@types/vinyl@2.0.7: - resolution: {integrity: sha512-4UqPv+2567NhMQuMLdKAyK4yzrfCqwaTt6bLhHEs8PFcxbHILsrxaY63n4wgE/BRLDWDQeI+WcTmkXKExh9hQg==} - dependencies: - '@types/expect': 1.20.4 - '@types/node': 18.15.11 + '@types/vinyl@2.0.12': + resolution: {integrity: sha512-Sr2fYMBUVGYq8kj3UthXFAu5UN6ZW+rYr4NACjZQJvHvj+c8lYv0CahmZ2P/r7iUkN44gGUBwqxZkrKXYPb7cw==} - /@types/web@0.0.69: - resolution: {integrity: sha512-0CqMxeJ+ETljejsVQ5U6gV75C9zz9nmCBXHYqdfnVmWp54VfoJSxUgeO6NI+ezgBl0JQsSiZCs2pkCCT9ERhbA==} - dev: true + '@types/web@0.0.152': + resolution: {integrity: sha512-Iu1OJ3ahq6/mwSK6jRKj2revI+avfPRWZOADcKeuv1AGCtkwbDl12AkWAl4KjiW2l4s2rkST7WagdhWOVGx9qg==} - /@types/webidl-conversions@7.0.0: - resolution: {integrity: sha512-xTE1E+YF4aWPJJeUzaZI5DRntlkY3+BCVJi0axFptnjGmAoWxkyREIh/XMrfxVLejwQxMCfDXdICo0VLxThrog==} - dev: false + '@types/webidl-conversions@7.0.3': + resolution: {integrity: sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==} - /@types/webpack@5.28.1(esbuild@0.16.3)(webpack-cli@5.0.1): - resolution: {integrity: sha512-qw1MqGZclCoBrpiSe/hokSgQM/su8Ocpl3L/YHE0L6moyaypg4+5F7Uzq7NgaPKPxUxUbQ4fLPLpDWdR27bCZw==} - dependencies: - '@types/node': 16.11.6 - tapable: 2.2.1 - webpack: 5.78.0(esbuild@0.16.3)(webpack-cli@5.0.1) - transitivePeerDependencies: - - '@swc/core' - - esbuild - - uglify-js - - webpack-cli - dev: true + '@types/webpack@5.28.5': + resolution: {integrity: sha512-wR87cgvxj3p6D0Crt1r5avwqffqPXUkNlnQ1mjU93G7gCuFjufZR4I6j8cz5g1F1tTYpfOOFvly+cmIQwL9wvw==} - /@types/whatwg-url@8.2.2: - resolution: {integrity: sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==} - dependencies: - '@types/node': 18.15.11 - '@types/webidl-conversions': 7.0.0 - dev: false + '@types/whatwg-url@11.0.5': + resolution: {integrity: sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==} - /@types/ws@8.5.4: - resolution: {integrity: sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==} - dependencies: - '@types/node': 18.15.11 - dev: false + '@types/ws@8.5.12': + resolution: {integrity: sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==} - /@types/yargs-parser@21.0.0: - resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==} + '@types/yargs-parser@21.0.3': + resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} - /@types/yargs@16.0.5: - resolution: {integrity: sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==} - dependencies: - '@types/yargs-parser': 21.0.0 - dev: false + '@types/yargs@16.0.9': + resolution: {integrity: sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==} - /@types/yargs@17.0.24: - resolution: {integrity: sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==} - dependencies: - '@types/yargs-parser': 21.0.0 + '@types/yargs@17.0.33': + resolution: {integrity: sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==} - /@typescript-eslint/eslint-plugin@5.58.0(@typescript-eslint/parser@5.58.0)(eslint@8.38.0)(typescript@4.9.5): - resolution: {integrity: sha512-vxHvLhH0qgBd3/tW6/VccptSfc8FxPQIkmNTVLWcCOVqSBvqpnKkBTYrhcGlXfSnd78azwe+PsjYFj0X34/njA==} + '@typescript-eslint/eslint-plugin@5.62.0': + resolution: {integrity: sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: '@typescript-eslint/parser': ^5.0.0 @@ -8377,39 +5788,26 @@ packages: peerDependenciesMeta: typescript: optional: true - dependencies: - '@eslint-community/regexpp': 4.5.0 - '@typescript-eslint/parser': 5.58.0(eslint@8.38.0)(typescript@4.9.5) - '@typescript-eslint/scope-manager': 5.58.0 - '@typescript-eslint/type-utils': 5.58.0(eslint@8.38.0)(typescript@4.9.5) - '@typescript-eslint/utils': 5.58.0(eslint@8.38.0)(typescript@4.9.5) - debug: 4.3.4 - eslint: 8.38.0 - grapheme-splitter: 1.0.4 - ignore: 5.2.4 - natural-compare-lite: 1.4.0 - semver: 7.5.0 - tsutils: 3.21.0(typescript@4.9.5) - typescript: 4.9.5 - transitivePeerDependencies: - - supports-color - dev: false - /@typescript-eslint/experimental-utils@5.58.0(eslint@8.38.0)(typescript@4.9.5): - resolution: {integrity: sha512-LA/sRPaynZlrlYxdefrZbMx8dqs/1Kc0yNG+XOk5CwwZx7tTv263ix3AJNioF0YBVt7hADpAUR20owl6pv4MIQ==} + '@typescript-eslint/eslint-plugin@8.12.2': + resolution: {integrity: sha512-gQxbxM8mcxBwaEmWdtLCIGLfixBMHhQjBqR8sVWNTPpcj45WlYL2IObS/DNMLH1DBP0n8qz+aiiLTGfopPEebw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/experimental-utils@5.62.0': + resolution: {integrity: sha512-RTXpeB3eMkpoclG3ZHft6vG/Z30azNHuqY6wKPBHlVMZFuEvrtlEDe8gMqDb+SO+9hjC/pLekeSCryf9vMZlCw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - dependencies: - '@typescript-eslint/utils': 5.58.0(eslint@8.38.0)(typescript@4.9.5) - eslint: 8.38.0 - transitivePeerDependencies: - - supports-color - - typescript - dev: false - /@typescript-eslint/parser@5.58.0(eslint@8.24.0)(typescript@4.9.5): - resolution: {integrity: sha512-ixaM3gRtlfrKzP8N6lRhBbjTow1t6ztfBvQNGuRM8qH1bjFFXIJ35XY+FC0RRBKn3C6cT+7VW1y8tNm7DwPHDQ==} + '@typescript-eslint/parser@5.62.0': + resolution: {integrity: sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -8417,549 +5815,199 @@ packages: peerDependenciesMeta: typescript: optional: true - dependencies: - '@typescript-eslint/scope-manager': 5.58.0 - '@typescript-eslint/types': 5.58.0 - '@typescript-eslint/typescript-estree': 5.58.0(typescript@4.9.5) - debug: 4.3.4 - eslint: 8.24.0 - typescript: 4.9.5 - transitivePeerDependencies: - - supports-color - dev: true - /@typescript-eslint/parser@5.58.0(eslint@8.29.0)(typescript@4.9.4): - resolution: {integrity: sha512-ixaM3gRtlfrKzP8N6lRhBbjTow1t6ztfBvQNGuRM8qH1bjFFXIJ35XY+FC0RRBKn3C6cT+7VW1y8tNm7DwPHDQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@typescript-eslint/parser@8.12.2': + resolution: {integrity: sha512-MrvlXNfGPLH3Z+r7Tk+Z5moZAc0dzdVjTgUgwsdGweH7lydysQsnSww3nAmsq8blFuRD5VRlAr9YdEFw3e6PBw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + eslint: ^8.57.0 || ^9.0.0 typescript: '*' peerDependenciesMeta: typescript: optional: true - dependencies: - '@typescript-eslint/scope-manager': 5.58.0 - '@typescript-eslint/types': 5.58.0 - '@typescript-eslint/typescript-estree': 5.58.0(typescript@4.9.4) - debug: 4.3.4 - eslint: 8.29.0 - typescript: 4.9.4 - transitivePeerDependencies: - - supports-color - /@typescript-eslint/parser@5.58.0(eslint@8.38.0)(typescript@4.9.5): - resolution: {integrity: sha512-ixaM3gRtlfrKzP8N6lRhBbjTow1t6ztfBvQNGuRM8qH1bjFFXIJ35XY+FC0RRBKn3C6cT+7VW1y8tNm7DwPHDQ==} + '@typescript-eslint/scope-manager@5.62.0': + resolution: {integrity: sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@typescript-eslint/scope-manager@8.12.2': + resolution: {integrity: sha512-gPLpLtrj9aMHOvxJkSbDBmbRuYdtiEbnvO25bCMza3DhMjTQw0u7Y1M+YR5JPbMsXXnSPuCf5hfq0nEkQDL/JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/type-utils@5.62.0': + resolution: {integrity: sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + eslint: '*' typescript: '*' peerDependenciesMeta: typescript: optional: true - dependencies: - '@typescript-eslint/scope-manager': 5.58.0 - '@typescript-eslint/types': 5.58.0 - '@typescript-eslint/typescript-estree': 5.58.0(typescript@4.9.5) - debug: 4.3.4 - eslint: 8.38.0 - typescript: 4.9.5 - transitivePeerDependencies: - - supports-color - dev: false - - /@typescript-eslint/scope-manager@5.58.0: - resolution: {integrity: sha512-b+w8ypN5CFvrXWQb9Ow9T4/6LC2MikNf1viLkYTiTbkQl46CnR69w7lajz1icW0TBsYmlpg+mRzFJ4LEJ8X9NA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - '@typescript-eslint/types': 5.58.0 - '@typescript-eslint/visitor-keys': 5.58.0 - /@typescript-eslint/type-utils@5.58.0(eslint@8.38.0)(typescript@4.9.5): - resolution: {integrity: sha512-FF5vP/SKAFJ+LmR9PENql7fQVVgGDOS+dq3j+cKl9iW/9VuZC/8CFmzIP0DLKXfWKpRHawJiG70rVH+xZZbp8w==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@typescript-eslint/type-utils@8.12.2': + resolution: {integrity: sha512-bwuU4TAogPI+1q/IJSKuD4shBLc/d2vGcRT588q+jzayQyjVK2X6v/fbR4InY2U2sgf8MEvVCqEWUzYzgBNcGQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: '*' typescript: '*' peerDependenciesMeta: typescript: optional: true - dependencies: - '@typescript-eslint/typescript-estree': 5.58.0(typescript@4.9.5) - '@typescript-eslint/utils': 5.58.0(eslint@8.38.0)(typescript@4.9.5) - debug: 4.3.4 - eslint: 8.38.0 - tsutils: 3.21.0(typescript@4.9.5) - typescript: 4.9.5 - transitivePeerDependencies: - - supports-color - dev: false - /@typescript-eslint/types@5.58.0: - resolution: {integrity: sha512-JYV4eITHPzVQMnHZcYJXl2ZloC7thuUHrcUmxtzvItyKPvQ50kb9QXBkgNAt90OYMqwaodQh2kHutWZl1fc+1g==} + '@typescript-eslint/types@5.62.0': + resolution: {integrity: sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - /@typescript-eslint/typescript-estree@5.58.0(typescript@4.9.4): - resolution: {integrity: sha512-cRACvGTodA+UxnYM2uwA2KCwRL7VAzo45syNysqlMyNyjw0Z35Icc9ihPJZjIYuA5bXJYiJ2YGUB59BqlOZT1Q==} + '@typescript-eslint/types@8.12.2': + resolution: {integrity: sha512-VwDwMF1SZ7wPBUZwmMdnDJ6sIFk4K4s+ALKLP6aIQsISkPv8jhiw65sAK6SuWODN/ix+m+HgbYDkH+zLjrzvOA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@5.62.0': + resolution: {integrity: sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: typescript: '*' peerDependenciesMeta: typescript: optional: true - dependencies: - '@typescript-eslint/types': 5.58.0 - '@typescript-eslint/visitor-keys': 5.58.0 - debug: 4.3.4 - globby: 11.1.0 - is-glob: 4.0.3 - semver: 7.5.0 - tsutils: 3.21.0(typescript@4.9.4) - typescript: 4.9.4 - transitivePeerDependencies: - - supports-color - /@typescript-eslint/typescript-estree@5.58.0(typescript@4.9.5): - resolution: {integrity: sha512-cRACvGTodA+UxnYM2uwA2KCwRL7VAzo45syNysqlMyNyjw0Z35Icc9ihPJZjIYuA5bXJYiJ2YGUB59BqlOZT1Q==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@typescript-eslint/typescript-estree@8.12.2': + resolution: {integrity: sha512-mME5MDwGe30Pq9zKPvyduyU86PH7aixwqYR2grTglAdB+AN8xXQ1vFGpYaUSJ5o5P/5znsSBeNcs5g5/2aQwow==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '*' peerDependenciesMeta: typescript: optional: true - dependencies: - '@typescript-eslint/types': 5.58.0 - '@typescript-eslint/visitor-keys': 5.58.0 - debug: 4.3.4 - globby: 11.1.0 - is-glob: 4.0.3 - semver: 7.5.0 - tsutils: 3.21.0(typescript@4.9.5) - typescript: 4.9.5 - transitivePeerDependencies: - - supports-color - /@typescript-eslint/utils@5.58.0(eslint@8.38.0)(typescript@4.9.5): - resolution: {integrity: sha512-gAmLOTFXMXOC+zP1fsqm3VceKSBQJNzV385Ok3+yzlavNHZoedajjS4UyS21gabJYcobuigQPs/z71A9MdJFqQ==} + '@typescript-eslint/utils@5.62.0': + resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.38.0) - '@types/json-schema': 7.0.11 - '@types/semver': 7.3.13 - '@typescript-eslint/scope-manager': 5.58.0 - '@typescript-eslint/types': 5.58.0 - '@typescript-eslint/typescript-estree': 5.58.0(typescript@4.9.5) - eslint: 8.38.0 - eslint-scope: 5.1.1 - semver: 7.5.0 - transitivePeerDependencies: - - supports-color - - typescript - dev: false - /@typescript-eslint/visitor-keys@5.58.0: - resolution: {integrity: sha512-/fBraTlPj0jwdyTwLyrRTxv/3lnU2H96pNTVM6z3esTWLtA5MZ9ghSMJ7Rb+TtUAdtEw9EyJzJ0EydIMKxQ9gA==} + '@typescript-eslint/utils@8.12.2': + resolution: {integrity: sha512-UTTuDIX3fkfAz6iSVa5rTuSfWIYZ6ATtEocQ/umkRSyC9O919lbZ8dcH7mysshrCdrAM03skJOEYaBugxN+M6A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + + '@typescript-eslint/visitor-keys@5.62.0': + resolution: {integrity: sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - '@typescript-eslint/types': 5.58.0 - eslint-visitor-keys: 3.4.0 - /@vanilla-extract/babel-plugin-debug-ids@1.0.2: - resolution: {integrity: sha512-LjnbQWGeMwaydmovx8jWUR8BxLtLiPyq0xz5C8G5OvFhsuJxvavLdrBHNNizvr1dq7/3qZGlPv0znsvU4P44YA==} - dependencies: - '@babel/core': 7.21.4 - transitivePeerDependencies: - - supports-color - dev: true + '@typescript-eslint/visitor-keys@8.12.2': + resolution: {integrity: sha512-PChz8UaKQAVNHghsHcPyx1OMHoFRUEA7rJSK/mDhdq85bk+PLsUHUBqTQTFt18VJZbmxBovM65fezlheQRsSDA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - /@vanilla-extract/css@1.11.0: - resolution: {integrity: sha512-uohj+8cGWbnrVzTfrjlJeXqdGjH3d3TcscdQxKe3h5bb5QQXTpPSq+c+SeWADIGiZybzcW0CBvZV8jsy1ywY9w==} - dependencies: - '@emotion/hash': 0.9.0 - '@vanilla-extract/private': 1.0.3 - ahocorasick: 1.0.2 - chalk: 4.1.2 - css-what: 5.1.0 - cssesc: 3.0.0 - csstype: 3.1.2 - deep-object-diff: 1.1.9 - deepmerge: 4.3.1 - media-query-parser: 2.0.2 - outdent: 0.8.0 - dev: true - - /@vanilla-extract/integration@6.2.1(@types/node@18.15.11): - resolution: {integrity: sha512-+xYJz07G7TFAMZGrOqArOsURG+xcYvqctujEkANjw2McCBvGEK505RxQqOuNiA9Mi9hgGdNp2JedSa94f3eoLg==} - dependencies: - '@babel/core': 7.21.4 - '@babel/plugin-syntax-typescript': 7.21.4(@babel/core@7.21.4) - '@vanilla-extract/babel-plugin-debug-ids': 1.0.2 - '@vanilla-extract/css': 1.11.0 - esbuild: 0.17.6 - eval: 0.1.6 - find-up: 5.0.0 - javascript-stringify: 2.1.0 - lodash: 4.17.21 - mlly: 1.2.0 - outdent: 0.8.0 - vite: 4.2.2(@types/node@18.15.11) - vite-node: 0.28.5(@types/node@18.15.11) - transitivePeerDependencies: - - '@types/node' - - less - - sass - - stylus - - sugarss - - supports-color - - terser - dev: true + '@ungap/structured-clone@1.2.0': + resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} - /@vanilla-extract/private@1.0.3: - resolution: {integrity: sha512-17kVyLq3ePTKOkveHxXuIJZtGYs+cSoev7BlP+Lf4916qfDhk/HBjvlYDe8egrea7LNPHKwSZJK/bzZC+Q6AwQ==} - dev: true + '@webassemblyjs/ast@1.12.1': + resolution: {integrity: sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==} - /@vercel/build-utils@6.7.1: - resolution: {integrity: sha512-Ecc9oQBSVwk1suENcRcj1L6gQrUt4+0XA9oPFxrUpoFEk04lP/ZV3qAQPk+ex08N+vfUulYdqb+cmVTnwqsmqw==} - dev: true + '@webassemblyjs/ast@1.14.1': + resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==} - /@vercel/gatsby-plugin-vercel-analytics@1.0.9: - resolution: {integrity: sha512-JEzoLxjAVFZT1569dEZKKrhq9UvMjJZ13ACMAsGx33hf2QeH/okkVnIeAiy1yGQKFFRKyhFQ6N29JSdV7zD/Zg==} - dependencies: - '@babel/runtime': 7.12.1 - web-vitals: 0.2.4 - dev: true + '@webassemblyjs/floating-point-hex-parser@1.11.6': + resolution: {integrity: sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==} - /@vercel/gatsby-plugin-vercel-builder@1.2.8: - resolution: {integrity: sha512-Z2HDeD/XzGxLIgaLEKBCwGKhI0aorzmY1gYccq8yaC2pXMKBJblZfU87QQwgQgXI+BWigXM0xmtIca6ijbyALg==} - dependencies: - '@sinclair/typebox': 0.25.24 - '@vercel/build-utils': 6.7.1 - '@vercel/node': 2.10.3 - '@vercel/routing-utils': 2.2.0 - esbuild: 0.14.47 - etag: 1.8.1 - fs-extra: 11.1.0 - transitivePeerDependencies: - - '@swc/core' - - '@swc/wasm' - - encoding - dev: true + '@webassemblyjs/floating-point-hex-parser@1.13.2': + resolution: {integrity: sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==} - /@vercel/go@2.4.4: - resolution: {integrity: sha512-JXez+dQ6nXv68SnBor6FJqYy3X71FKnFc+OL4nxrsEsTzhCbqjLS/9hm4ztB0KQslWJvDp+2aRr++zLtyiCeQQ==} - dev: true + '@webassemblyjs/helper-api-error@1.11.6': + resolution: {integrity: sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==} - /@vercel/hydrogen@0.0.62: - resolution: {integrity: sha512-A1Ttgc4pBrLEK5CpeLuf4TQp0IsXjISDMZVnRrpWhu10hKUV/KfJ8r8df1w+dEq5Qd9LmJAwvVXlpX5vcJLDRQ==} - dev: true + '@webassemblyjs/helper-api-error@1.13.2': + resolution: {integrity: sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==} - /@vercel/next@3.7.4: - resolution: {integrity: sha512-ADM6ZvAOldy0QZCmKP4pMad145bcO3bZK+pAS7SQ/68jFun50Boz7Ophb5J/CYvRyCybIraJ1TxDEf+dyo9GeA==} - dev: true + '@webassemblyjs/helper-buffer@1.12.1': + resolution: {integrity: sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==} - /@vercel/nft@0.22.5: - resolution: {integrity: sha512-mug57Wd1BL7GMj9gXMgMeKUjdqO0e4u+0QLPYMFE1rwdJ+55oPy6lp3nIBCS8gOvigT62UI4QKUL2sGqcoW4Hw==} - engines: {node: '>=14'} - hasBin: true - dependencies: - '@mapbox/node-pre-gyp': 1.0.10 - '@rollup/pluginutils': 4.2.1 - acorn: 8.8.2 - async-sema: 3.1.1 - bindings: 1.5.0 - estree-walker: 2.0.2 - glob: 7.2.3 - graceful-fs: 4.2.11 - micromatch: 4.0.5 - node-gyp-build: 4.6.0 - resolve-from: 5.0.0 - transitivePeerDependencies: - - encoding - - supports-color - dev: true + '@webassemblyjs/helper-buffer@1.14.1': + resolution: {integrity: sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==} - /@vercel/node-bridge@4.0.1: - resolution: {integrity: sha512-XEfKfnLGzlIBpad7eGNPql1HnMhoSTv9q3uDNC4axdaAC/kI5yvl8kXjuCPAXYvpbJnVQPpcSUC5/r5ap8F3jA==} - dev: true + '@webassemblyjs/helper-numbers@1.11.6': + resolution: {integrity: sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==} - /@vercel/node@2.10.3: - resolution: {integrity: sha512-R6YwD7YTV4OPEjXnthTP2Zn96ZF2TAjmBhGKfYC9ZuqlmFzSxqyuHn+RUSkknkKBO46b4OzaNdi5XVnAdJizLA==} - dependencies: - '@edge-runtime/vm': 2.0.0 - '@types/node': 14.18.33 - '@vercel/build-utils': 6.7.1 - '@vercel/node-bridge': 4.0.1 - '@vercel/static-config': 2.0.15 - edge-runtime: 2.0.0 - esbuild: 0.14.47 - exit-hook: 2.2.1 - node-fetch: 2.6.7 - ts-node: 10.9.1(@types/node@14.18.33)(typescript@4.3.4) - typescript: 4.3.4 - transitivePeerDependencies: - - '@swc/core' - - '@swc/wasm' - - encoding - dev: true + '@webassemblyjs/helper-numbers@1.13.2': + resolution: {integrity: sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==} - /@vercel/python@3.1.58: - resolution: {integrity: sha512-RZJkNLyxZMuNcBwJMU589noyuNAq2pNxwAqrMs7bdBucWkNRTURiEh3/rWjUSADOq3eUx4vhfQDzwvaeZyRxoA==} - dev: true + '@webassemblyjs/helper-wasm-bytecode@1.11.6': + resolution: {integrity: sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==} - /@vercel/redwood@1.1.14: - resolution: {integrity: sha512-QFIhLegvfVp2OLdv96krTyz6C5/cUncUg4CEEfx3U48+l31hWaWcnjI6+MhgN4PZC4YN+s21vKZNz/UWnGnTiA==} - dependencies: - '@vercel/nft': 0.22.5 - '@vercel/routing-utils': 2.2.0 - semver: 6.1.1 - transitivePeerDependencies: - - encoding - - supports-color - dev: true + '@webassemblyjs/helper-wasm-bytecode@1.13.2': + resolution: {integrity: sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==} - /@vercel/remix-builder@1.8.4(@types/node@18.15.11)(ts-node@10.8.2): - resolution: {integrity: sha512-bexJo+z0kCZf2nhlne5TBcxWHQ4/xGT2JgmOJ5SaJOhhuZXt0BUZ3B38+2MPScUnItNWPSmn86xzp/VMWNuzuw==} - dependencies: - '@remix-run/dev': /@vercel/remix-run-dev@1.15.0(@types/node@18.15.11)(ts-node@10.8.2) - '@vercel/build-utils': 6.7.1 - '@vercel/nft': 0.22.5 - '@vercel/static-config': 2.0.15 - path-to-regexp: 6.2.1 - semver: 7.3.8 - ts-morph: 12.0.0 - transitivePeerDependencies: - - '@remix-run/serve' - - '@types/node' - - bluebird - - bufferutil - - encoding - - less - - sass - - stylus - - sugarss - - supports-color - - terser - - ts-node - - utf-8-validate - dev: true + '@webassemblyjs/helper-wasm-section@1.12.1': + resolution: {integrity: sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==} - /@vercel/remix-run-dev@1.15.0(@types/node@18.15.11)(ts-node@10.8.2): - resolution: {integrity: sha512-pQTM5WmOzrvhpPSHFDShwqX71YnLaTUxffhnly4MxVNKJ2WKV9zqx8bGQ/7cLfpEu9JfY2c+pVjYYb3wAMBt+Q==} - engines: {node: '>=14'} - hasBin: true - peerDependencies: - '@remix-run/serve': ^1.15.0 - peerDependenciesMeta: - '@remix-run/serve': - optional: true - dependencies: - '@babel/core': 7.21.4 - '@babel/generator': 7.21.4 - '@babel/parser': 7.21.4 - '@babel/plugin-syntax-jsx': 7.21.4(@babel/core@7.21.4) - '@babel/plugin-syntax-typescript': 7.21.4(@babel/core@7.21.4) - '@babel/preset-env': 7.21.4(@babel/core@7.21.4) - '@babel/preset-typescript': 7.21.4(@babel/core@7.21.4) - '@babel/traverse': 7.21.4 - '@babel/types': 7.21.4 - '@esbuild-plugins/node-modules-polyfill': 0.1.4(esbuild@0.16.3) - '@npmcli/package-json': 2.0.0 - '@remix-run/server-runtime': 1.15.0 - '@vanilla-extract/integration': 6.2.1(@types/node@18.15.11) - arg: 5.0.2 - cacache: 15.3.0 - chalk: 4.1.2 - chokidar: 3.5.3 - dotenv: 16.0.3 - esbuild: 0.16.3 - execa: 5.1.1 - exit-hook: 2.2.1 - express: 4.18.2 - fast-glob: 3.2.11 - fs-extra: 10.1.0 - get-port: 5.1.1 - glob-to-regexp: 0.4.1 - gunzip-maybe: 1.4.2 - inquirer: 8.2.5 - jsesc: 3.0.2 - json5: 2.2.3 - lodash: 4.17.21 - lodash.debounce: 4.0.8 - lru-cache: 7.18.3 - minimatch: 3.1.2 - node-fetch: 2.6.9 - ora: 5.4.1 - postcss: 8.4.22 - postcss-discard-duplicates: 5.1.0(postcss@8.4.22) - postcss-load-config: 4.0.1(postcss@8.4.22)(ts-node@10.8.2) - postcss-modules: 6.0.0(postcss@8.4.22) - prettier: 2.7.1 - pretty-ms: 7.0.1 - proxy-agent: 5.0.0 - react-refresh: 0.14.0 - recast: 0.21.5 - remark-frontmatter: 4.0.1 - remark-mdx-frontmatter: 1.1.1 - semver: 7.5.0 - sort-package-json: 1.57.0 - tar-fs: 2.1.1 - tsconfig-paths: 4.2.0 - ws: 7.5.9 - xdm: 2.1.0 - transitivePeerDependencies: - - '@types/node' - - bluebird - - bufferutil - - encoding - - less - - sass - - stylus - - sugarss - - supports-color - - terser - - ts-node - - utf-8-validate - dev: true - - /@vercel/routing-utils@2.2.0: - resolution: {integrity: sha512-Ro90s1mStpbgu2HV8I4LFEKNG8GVxkWm238ebD/23BCO9/DxIJ3+wCzga8j8BMmG57x4etVlaHNV25bbzW5r2g==} - dependencies: - path-to-regexp: 6.1.0 - optionalDependencies: - ajv: 6.12.6 - dev: true + '@webassemblyjs/helper-wasm-section@1.14.1': + resolution: {integrity: sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==} - /@vercel/ruby@1.3.75: - resolution: {integrity: sha512-sUmzJnd9O1N7StFEpKG9JvHJvHmJjgfrmhgQsQLEQ7OOQJkO9DYoLomlrIDW9qNdu7dNOeyj7gQY5B8y8RMntw==} - dev: true + '@webassemblyjs/ieee754@1.11.6': + resolution: {integrity: sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==} - /@vercel/static-build@1.3.23: - resolution: {integrity: sha512-SjlOOqZRoe/10Af3iSWKEo2+pFpTwroZj6pwF1iP7I9Y3G5IDObzOfpxgdpsKCWSlmUIr1sGOjf1fgoPctbdLw==} - dependencies: - '@vercel/gatsby-plugin-vercel-analytics': 1.0.9 - '@vercel/gatsby-plugin-vercel-builder': 1.2.8 - transitivePeerDependencies: - - '@swc/core' - - '@swc/wasm' - - encoding - dev: true - - /@vercel/static-config@2.0.15: - resolution: {integrity: sha512-A/N3ZGiOOMql9JArwBTIfhFngFtmVC7ndKQKp0FoFq8MO79AS5qBBtdpILS5QA71M5v+9CPjVkHxN6QweU55Xg==} - dependencies: - ajv: 8.6.3 - json-schema-to-ts: 1.6.4 - ts-morph: 12.0.0 - dev: true - - /@web3-storage/multipart-parser@1.0.0: - resolution: {integrity: sha512-BEO6al7BYqcnfX15W2cnGR+Q566ACXAT9UQykORCWW80lmkpWsnEob6zJS1ZVBKsSJC8+7vJkHwlp+lXG1UCdw==} - dev: true + '@webassemblyjs/ieee754@1.13.2': + resolution: {integrity: sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==} - /@webassemblyjs/ast@1.11.1: - resolution: {integrity: sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==} - dependencies: - '@webassemblyjs/helper-numbers': 1.11.1 - '@webassemblyjs/helper-wasm-bytecode': 1.11.1 - - /@webassemblyjs/floating-point-hex-parser@1.11.1: - resolution: {integrity: sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==} + '@webassemblyjs/leb128@1.11.6': + resolution: {integrity: sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==} - /@webassemblyjs/helper-api-error@1.11.1: - resolution: {integrity: sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==} + '@webassemblyjs/leb128@1.13.2': + resolution: {integrity: sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==} - /@webassemblyjs/helper-buffer@1.11.1: - resolution: {integrity: sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==} + '@webassemblyjs/utf8@1.11.6': + resolution: {integrity: sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==} - /@webassemblyjs/helper-numbers@1.11.1: - resolution: {integrity: sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==} - dependencies: - '@webassemblyjs/floating-point-hex-parser': 1.11.1 - '@webassemblyjs/helper-api-error': 1.11.1 - '@xtuc/long': 4.2.2 + '@webassemblyjs/utf8@1.13.2': + resolution: {integrity: sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==} - /@webassemblyjs/helper-wasm-bytecode@1.11.1: - resolution: {integrity: sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==} + '@webassemblyjs/wasm-edit@1.12.1': + resolution: {integrity: sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==} - /@webassemblyjs/helper-wasm-section@1.11.1: - resolution: {integrity: sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==} - dependencies: - '@webassemblyjs/ast': 1.11.1 - '@webassemblyjs/helper-buffer': 1.11.1 - '@webassemblyjs/helper-wasm-bytecode': 1.11.1 - '@webassemblyjs/wasm-gen': 1.11.1 + '@webassemblyjs/wasm-edit@1.14.1': + resolution: {integrity: sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==} - /@webassemblyjs/ieee754@1.11.1: - resolution: {integrity: sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==} - dependencies: - '@xtuc/ieee754': 1.2.0 + '@webassemblyjs/wasm-gen@1.12.1': + resolution: {integrity: sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==} - /@webassemblyjs/leb128@1.11.1: - resolution: {integrity: sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==} - dependencies: - '@xtuc/long': 4.2.2 + '@webassemblyjs/wasm-gen@1.14.1': + resolution: {integrity: sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==} - /@webassemblyjs/utf8@1.11.1: - resolution: {integrity: sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==} + '@webassemblyjs/wasm-opt@1.12.1': + resolution: {integrity: sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==} - /@webassemblyjs/wasm-edit@1.11.1: - resolution: {integrity: sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==} - dependencies: - '@webassemblyjs/ast': 1.11.1 - '@webassemblyjs/helper-buffer': 1.11.1 - '@webassemblyjs/helper-wasm-bytecode': 1.11.1 - '@webassemblyjs/helper-wasm-section': 1.11.1 - '@webassemblyjs/wasm-gen': 1.11.1 - '@webassemblyjs/wasm-opt': 1.11.1 - '@webassemblyjs/wasm-parser': 1.11.1 - '@webassemblyjs/wast-printer': 1.11.1 + '@webassemblyjs/wasm-opt@1.14.1': + resolution: {integrity: sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==} - /@webassemblyjs/wasm-gen@1.11.1: - resolution: {integrity: sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==} - dependencies: - '@webassemblyjs/ast': 1.11.1 - '@webassemblyjs/helper-wasm-bytecode': 1.11.1 - '@webassemblyjs/ieee754': 1.11.1 - '@webassemblyjs/leb128': 1.11.1 - '@webassemblyjs/utf8': 1.11.1 + '@webassemblyjs/wasm-parser@1.12.1': + resolution: {integrity: sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==} - /@webassemblyjs/wasm-opt@1.11.1: - resolution: {integrity: sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==} - dependencies: - '@webassemblyjs/ast': 1.11.1 - '@webassemblyjs/helper-buffer': 1.11.1 - '@webassemblyjs/wasm-gen': 1.11.1 - '@webassemblyjs/wasm-parser': 1.11.1 + '@webassemblyjs/wasm-parser@1.14.1': + resolution: {integrity: sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==} - /@webassemblyjs/wasm-parser@1.11.1: - resolution: {integrity: sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==} - dependencies: - '@webassemblyjs/ast': 1.11.1 - '@webassemblyjs/helper-api-error': 1.11.1 - '@webassemblyjs/helper-wasm-bytecode': 1.11.1 - '@webassemblyjs/ieee754': 1.11.1 - '@webassemblyjs/leb128': 1.11.1 - '@webassemblyjs/utf8': 1.11.1 + '@webassemblyjs/wast-printer@1.12.1': + resolution: {integrity: sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==} - /@webassemblyjs/wast-printer@1.11.1: - resolution: {integrity: sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==} - dependencies: - '@webassemblyjs/ast': 1.11.1 - '@xtuc/long': 4.2.2 + '@webassemblyjs/wast-printer@1.14.1': + resolution: {integrity: sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==} - /@webpack-cli/configtest@2.0.1(webpack-cli@5.0.1)(webpack@5.78.0): - resolution: {integrity: sha512-njsdJXJSiS2iNbQVS0eT8A/KPnmyH4pv1APj2K0d1wrZcBLw+yppxOy4CGqa0OxDJkzfL/XELDhD8rocnIwB5A==} + '@webpack-cli/configtest@2.1.1': + resolution: {integrity: sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==} engines: {node: '>=14.15.0'} peerDependencies: webpack: 5.x.x webpack-cli: 5.x.x - dependencies: - webpack: 5.78.0(esbuild@0.16.3)(webpack-cli@5.0.1) - webpack-cli: 5.0.1(@webpack-cli/generators@3.0.1)(webpack@5.78.0) - /@webpack-cli/generators@3.0.1(mem-fs-editor@9.7.0)(mem-fs@2.3.0)(prettier@2.8.7)(webpack-cli@5.0.1)(webpack@5.78.0): - resolution: {integrity: sha512-kgtqwN13udxC2wV2LfEmJQ/yGz6+j8cgy17jE9ybyHrmT0PJcwjSpsvj1fDWYUb7brSOB4B03s1mEPjZTRfwCQ==} + '@webpack-cli/configtest@3.0.1': + resolution: {integrity: sha512-u8d0pJ5YFgneF/GuvEiDA61Tf1VDomHHYMjv/wc9XzYj7nopltpG96nXN5dJRstxZhcNpV1g+nT6CydO7pHbjA==} + engines: {node: '>=18.12.0'} + peerDependencies: + webpack: ^5.82.0 + webpack-cli: 6.x.x + + '@webpack-cli/generators@3.0.7': + resolution: {integrity: sha512-H4dlEX8CzO5EHBYYZQop9x4w6lG9FenSF/1spLRlvRAULDgTs0VfmwOuwp03tTLml9jpMsouuVw6vEN8KpwE/w==} engines: {node: '>=14.15.0'} peerDependencies: prettier: '*' @@ -8968,31 +6016,23 @@ packages: peerDependenciesMeta: prettier: optional: true - dependencies: - prettier: 2.8.7 - webpack: 5.78.0(esbuild@0.16.3)(webpack-cli@5.0.1) - webpack-cli: 5.0.1(@webpack-cli/generators@3.0.1)(webpack@5.78.0) - yeoman-environment: 3.15.1(mem-fs-editor@9.7.0)(mem-fs@2.3.0) - yeoman-generator: 5.8.0(mem-fs@2.3.0)(yeoman-environment@3.15.1) - transitivePeerDependencies: - - bluebird - - encoding - - mem-fs - - mem-fs-editor - - supports-color - /@webpack-cli/info@2.0.1(webpack-cli@5.0.1)(webpack@5.78.0): - resolution: {integrity: sha512-fE1UEWTwsAxRhrJNikE7v4EotYflkEhBL7EbajfkPlf6E37/2QshOy/D48Mw8G5XMFlQtS6YV42vtbG9zBpIQA==} + '@webpack-cli/info@2.0.2': + resolution: {integrity: sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==} engines: {node: '>=14.15.0'} peerDependencies: webpack: 5.x.x webpack-cli: 5.x.x - dependencies: - webpack: 5.78.0(esbuild@0.16.3)(webpack-cli@5.0.1) - webpack-cli: 5.0.1(@webpack-cli/generators@3.0.1)(webpack@5.78.0) - /@webpack-cli/serve@2.0.1(webpack-cli@5.0.1)(webpack@5.78.0): - resolution: {integrity: sha512-0G7tNyS+yW8TdgHwZKlDWYXFA6OJQnoLCQvYKkQP0Q2X205PSQ6RNUj0M+1OB/9gRQaUZ/ccYfaxd0nhaWKfjw==} + '@webpack-cli/info@3.0.1': + resolution: {integrity: sha512-coEmDzc2u/ffMvuW9aCjoRzNSPDl/XLuhPdlFRpT9tZHmJ/039az33CE7uH+8s0uL1j5ZNtfdv0HkfaKRBGJsQ==} + engines: {node: '>=18.12.0'} + peerDependencies: + webpack: ^5.82.0 + webpack-cli: 6.x.x + + '@webpack-cli/serve@2.0.5': + resolution: {integrity: sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==} engines: {node: '>=14.15.0'} peerDependencies: webpack: 5.x.x @@ -9001,1980 +6041,1198 @@ packages: peerDependenciesMeta: webpack-dev-server: optional: true - dependencies: - webpack: 5.78.0(esbuild@0.16.3)(webpack-cli@5.0.1) - webpack-cli: 5.0.1(@webpack-cli/generators@3.0.1)(webpack@5.78.0) - /@xobotyi/scrollbar-width@1.9.5: + '@webpack-cli/serve@3.0.1': + resolution: {integrity: sha512-sbgw03xQaCLiT6gcY/6u3qBDn01CWw/nbaXl3gTdTFuJJ75Gffv3E3DBpgvY2fkkrdS1fpjaXNOmJlnbtKauKg==} + engines: {node: '>=18.12.0'} + peerDependencies: + webpack: ^5.82.0 + webpack-cli: 6.x.x + webpack-dev-server: '*' + peerDependenciesMeta: + webpack-dev-server: + optional: true + + '@xobotyi/scrollbar-width@1.9.5': resolution: {integrity: sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ==} - dev: false - /@xtuc/ieee754@1.2.0: + '@xtuc/ieee754@1.2.0': resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} - /@xtuc/long@4.2.2: + '@xtuc/long@4.2.2': resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} - /abab@2.0.6: + abab@2.0.6: resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==} - dev: false + deprecated: Use your platform's native atob() and btoa() methods instead - /abbrev@1.1.1: + abbrev@1.1.1: resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} - /abort-controller@3.0.0: + abbrev@2.0.0: + resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + abort-controller@3.0.0: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} engines: {node: '>=6.5'} - dependencies: - event-target-shim: 5.0.1 - dev: false - /accepts@1.3.8: + accepts@1.3.8: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} engines: {node: '>= 0.6'} - dependencies: - mime-types: 2.1.35 - negotiator: 0.6.3 - /acorn-globals@6.0.0: + acorn-globals@6.0.0: resolution: {integrity: sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==} - dependencies: - acorn: 7.4.1 - acorn-walk: 7.2.0 - dev: false - /acorn-import-assertions@1.8.0(acorn@8.8.2): - resolution: {integrity: sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==} + acorn-import-attributes@1.9.5: + resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} peerDependencies: acorn: ^8 - dependencies: - acorn: 8.8.2 - /acorn-jsx@5.3.2(acorn@8.8.2): + acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - dependencies: - acorn: 8.8.2 - /acorn-walk@7.2.0: + acorn-walk@7.2.0: resolution: {integrity: sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==} engines: {node: '>=0.4.0'} - dev: false - /acorn-walk@8.2.0: - resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} + acorn-walk@8.3.4: + resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==} engines: {node: '>=0.4.0'} - /acorn@7.4.1: + acorn@7.4.1: resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==} engines: {node: '>=0.4.0'} hasBin: true - /acorn@8.8.2: - resolution: {integrity: sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==} + acorn@8.14.0: + resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} engines: {node: '>=0.4.0'} hasBin: true - /address@1.2.2: + address@1.2.2: resolution: {integrity: sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==} engines: {node: '>= 10.0.0'} - dev: false - /adjust-sourcemap-loader@4.0.0: + adjust-sourcemap-loader@4.0.0: resolution: {integrity: sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==} engines: {node: '>=8.9'} - dependencies: - loader-utils: 2.0.4 - regex-parser: 2.2.11 - dev: false - /agent-base@6.0.2: + agent-base@6.0.2: resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} engines: {node: '>= 6.0.0'} - dependencies: - debug: 4.3.4 - transitivePeerDependencies: - - supports-color - /agentkeepalive@4.3.0: + agent-base@7.1.1: + resolution: {integrity: sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==} + engines: {node: '>= 14'} + + agentkeepalive@4.3.0: resolution: {integrity: sha512-7Epl1Blf4Sy37j4v9f9FjICCh4+KAQOyXgHEwlyBiAQLbhKdq/i2QQU3amQalS/wPhdPzDXPL5DMR5bkn+YeWg==} engines: {node: '>= 8.0.0'} - dependencies: - debug: 4.3.4 - depd: 2.0.0 - humanize-ms: 1.2.1 - transitivePeerDependencies: - - supports-color - /aggregate-error@3.1.0: + aggregate-error@3.1.0: resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} engines: {node: '>=8'} - dependencies: - clean-stack: 2.2.0 - indent-string: 4.0.0 - /ahocorasick@1.0.2: - resolution: {integrity: sha512-hCOfMzbFx5IDutmWLAt6MZwOUjIfSM9G9FyVxytmE4Rs/5YDPWQrD/+IR1w+FweD9H2oOZEnv36TmkjhNURBVA==} - dev: true - - /ajv-formats@2.1.1(ajv@8.12.0): + ajv-formats@2.1.1: resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} peerDependencies: ajv: ^8.0.0 peerDependenciesMeta: ajv: optional: true - dependencies: - ajv: 8.12.0 - /ajv-keywords@3.5.2(ajv@6.12.6): + ajv-keywords@3.5.2: resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==} peerDependencies: ajv: ^6.9.1 - dependencies: - ajv: 6.12.6 - /ajv-keywords@5.1.0(ajv@8.12.0): + ajv-keywords@5.1.0: resolution: {integrity: sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==} peerDependencies: ajv: ^8.8.2 - dependencies: - ajv: 8.12.0 - fast-deep-equal: 3.1.3 - /ajv@6.12.6: + ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} - requiresBuild: true - dependencies: - fast-deep-equal: 3.1.3 - fast-json-stable-stringify: 2.1.0 - json-schema-traverse: 0.4.1 - uri-js: 4.4.1 - - /ajv@8.12.0: - resolution: {integrity: sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==} - dependencies: - fast-deep-equal: 3.1.3 - json-schema-traverse: 1.0.0 - require-from-string: 2.0.2 - uri-js: 4.4.1 - - /ajv@8.6.3: - resolution: {integrity: sha512-SMJOdDP6LqTkD0Uq8qLi+gMwSt0imXLSV080qFVwJCpH9U6Mb+SUGHAXM0KNbcBPguytWyvFxcHgMLe2D2XSpw==} - dependencies: - fast-deep-equal: 3.1.3 - json-schema-traverse: 1.0.0 - require-from-string: 2.0.2 - uri-js: 4.4.1 - dev: true - /alphanum-sort@1.0.2: - resolution: {integrity: sha512-0FcBfdcmaumGPQ0qPn7Q5qTgz/ooXgIyp1rf8ik5bGX8mpE2YHjC0P/eyQvxu1GURYQgq9ozf2mteQ5ZD9YiyQ==} - dev: true + ajv@8.17.1: + resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} - /analytics-utils@1.0.10(@types/dlv@1.1.2): - resolution: {integrity: sha512-ZKYKhip7Sf09qE85l4vZMBPR3fz6ISUTlwzkWSwJHbzLLzP5qrWQtcQlBcP9Pah7BMNSq8pqho+PX4ZKB014Yg==} + analytics-utils@1.0.12: + resolution: {integrity: sha512-WvV2YWgsnXLxaY0QYux0crpBAg/0JA763NmbMVz22jKhMPo7dpTBet8G2IlF7ixTjLDzGlkHk1ZaKqqQmjJ+4w==} peerDependencies: '@types/dlv': ^1.0.0 - dependencies: - '@analytics/type-utils': 0.6.0 - '@types/dlv': 1.1.2 - dlv: 1.1.3 - dev: false - /analytics@0.8.1(@types/dlv@1.1.2): - resolution: {integrity: sha512-mXOe8zTGDfiYqw9MZsgul8HrOBmHsIwk/0xbrkGZr75yvWqAcyKfZA0WjOalwI9tzIKv8WNfHV5yhnrtQcXJpw==} - dependencies: - '@analytics/core': 0.11.1(@types/dlv@1.1.2) - '@analytics/storage-utils': 0.4.0 - transitivePeerDependencies: - - '@types/dlv' - dev: false + analytics@0.8.9: + resolution: {integrity: sha512-oTbUzQpncMTslakqfK70GgB6bopk5hY+uuekwnadMkDyqNLgcD02KRzteTnO7q5Ko6wDECVtT8xi/6OuAMZykA==} - /ansi-colors@4.1.3: + ansi-align@3.0.1: + resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} + + ansi-colors@4.1.3: resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} engines: {node: '>=6'} - dev: false - /ansi-escapes@4.3.2: + ansi-escapes@4.3.2: resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} engines: {node: '>=8'} - dependencies: - type-fest: 0.21.3 - /ansi-html-community@0.0.8: + ansi-html-community@0.0.8: resolution: {integrity: sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==} engines: {'0': node >= 0.8.0} hasBin: true - dev: false - /ansi-regex@2.1.1: + ansi-html@0.0.9: + resolution: {integrity: sha512-ozbS3LuenHVxNRh/wdnN16QapUHzauqSomAl1jwwJRRsGwFwtj644lIhxfWu0Fy0acCij2+AEgHvjscq3dlVXg==} + engines: {'0': node >= 0.8.0} + hasBin: true + + ansi-regex@2.1.1: resolution: {integrity: sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==} engines: {node: '>=0.10.0'} - /ansi-regex@5.0.1: + ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} - /ansi-regex@6.0.1: - resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} + ansi-regex@6.1.0: + resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} engines: {node: '>=12'} - dev: false - /ansi-styles@2.2.1: + ansi-styles@2.2.1: resolution: {integrity: sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==} engines: {node: '>=0.10.0'} - dev: true - /ansi-styles@3.2.1: + ansi-styles@3.2.1: resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} engines: {node: '>=4'} - dependencies: - color-convert: 1.9.3 - /ansi-styles@4.3.0: + ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} - dependencies: - color-convert: 2.0.1 - /ansi-styles@5.2.0: + ansi-styles@5.2.0: resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} engines: {node: '>=10'} - /antd@5.4.2(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-OxXZ7joFf6Um4zeXm07tyJ9WV6eMwUw1KUmewfM/BDceUFVtJVf7YbBTBfX3JTl+jOuSpMSb4naFhOCgVwtyFw==} + ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + + ansi-to-html@0.7.2: + resolution: {integrity: sha512-v6MqmEpNlxF+POuyhKkidusCHWWkaLcGRURzivcU3I9tv7k4JVhFcnukrM5Rlk2rUywdZuzYAZ+kbZqWCnfN3g==} + engines: {node: '>=8.0.0'} + hasBin: true + + antd@5.22.0: + resolution: {integrity: sha512-hZE8riK8+LWsXUnPpbluvBxjnwXRh34s1yIND7g5WUV/AVZtPjt81jOoXCbKw5SJ8ORIx2o7nlaa4PJ3Luwisg==} peerDependencies: react: '>=16.9.0' react-dom: '>=16.9.0' - dependencies: - '@ant-design/colors': 7.0.0 - '@ant-design/cssinjs': 1.8.1(react-dom@18.2.0)(react@18.2.0) - '@ant-design/icons': 5.0.1(react-dom@18.2.0)(react@18.2.0) - '@ant-design/react-slick': 1.0.0(react@18.2.0) - '@babel/runtime': 7.21.0 - '@ctrl/tinycolor': 3.6.0 - '@rc-component/mutate-observer': 1.0.0(react-dom@18.2.0)(react@18.2.0) - '@rc-component/tour': 1.8.0(react-dom@18.2.0)(react@18.2.0) - '@rc-component/trigger': 1.8.0(react-dom@18.2.0)(react@18.2.0) - classnames: 2.3.2 - copy-to-clipboard: 3.3.3 - dayjs: 1.11.7 - qrcode.react: 3.1.0(react@18.2.0) - rc-cascader: 3.10.1(react-dom@18.2.0)(react@18.2.0) - rc-checkbox: 3.0.0(react-dom@18.2.0)(react@18.2.0) - rc-collapse: 3.5.2(react-dom@18.2.0)(react@18.2.0) - rc-dialog: 9.1.0(react-dom@18.2.0)(react@18.2.0) - rc-drawer: 6.1.5(react-dom@18.2.0)(react@18.2.0) - rc-dropdown: 4.0.1(react-dom@18.2.0)(react@18.2.0) - rc-field-form: 1.29.2(react-dom@18.2.0)(react@18.2.0) - rc-image: 5.16.0(react-dom@18.2.0)(react@18.2.0) - rc-input: 1.0.4(react-dom@18.2.0)(react@18.2.0) - rc-input-number: 7.4.2(react-dom@18.2.0)(react@18.2.0) - rc-mentions: 2.2.0(react-dom@18.2.0)(react@18.2.0) - rc-menu: 9.8.4(react-dom@18.2.0)(react@18.2.0) - rc-motion: 2.6.3(react-dom@18.2.0)(react@18.2.0) - rc-notification: 5.0.3(react-dom@18.2.0)(react@18.2.0) - rc-pagination: 3.3.1(react-dom@18.2.0)(react@18.2.0) - rc-picker: 3.6.2(dayjs@1.11.7)(react-dom@18.2.0)(react@18.2.0) - rc-progress: 3.4.1(react-dom@18.2.0)(react@18.2.0) - rc-rate: 2.10.0(react-dom@18.2.0)(react@18.2.0) - rc-resize-observer: 1.3.1(react-dom@18.2.0)(react@18.2.0) - rc-segmented: 2.1.2(react-dom@18.2.0)(react@18.2.0) - rc-select: 14.4.3(react-dom@18.2.0)(react@18.2.0) - rc-slider: 10.1.1(react-dom@18.2.0)(react@18.2.0) - rc-steps: 6.0.0(react-dom@18.2.0)(react@18.2.0) - rc-switch: 4.0.0(react-dom@18.2.0)(react@18.2.0) - rc-table: 7.31.1(react-dom@18.2.0)(react@18.2.0) - rc-tabs: 12.5.10(react-dom@18.2.0)(react@18.2.0) - rc-textarea: 1.2.2(react-dom@18.2.0)(react@18.2.0) - rc-tooltip: 6.0.1(react-dom@18.2.0)(react@18.2.0) - rc-tree: 5.7.3(react-dom@18.2.0)(react@18.2.0) - rc-tree-select: 5.8.0(react-dom@18.2.0)(react@18.2.0) - rc-trigger: 5.3.4(react-dom@18.2.0)(react@18.2.0) - rc-upload: 4.3.4(react-dom@18.2.0)(react@18.2.0) - rc-util: 5.29.3(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - scroll-into-view-if-needed: 3.0.10 - throttle-debounce: 5.0.0 - transitivePeerDependencies: - - date-fns - - luxon - - moment - /any-promise@1.3.0: + any-promise@1.3.0: resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} - /anymatch@3.1.3: + anymatch@3.1.3: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} - dependencies: - normalize-path: 3.0.0 - picomatch: 2.3.1 - /aproba@1.2.0: - resolution: {integrity: sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==} - dev: false - - /aproba@2.0.0: + aproba@2.0.0: resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==} - /archiver-utils@2.1.0: + archiver-utils@2.1.0: resolution: {integrity: sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==} engines: {node: '>= 6'} - dependencies: - glob: 7.2.3 - graceful-fs: 4.2.11 - lazystream: 1.0.1 - lodash.defaults: 4.2.0 - lodash.difference: 4.5.0 - lodash.flatten: 4.4.0 - lodash.isplainobject: 4.0.6 - lodash.union: 4.6.0 - normalize-path: 3.0.0 - readable-stream: 2.3.8 - dev: true - /archiver@5.3.1: - resolution: {integrity: sha512-8KyabkmbYrH+9ibcTScQ1xCJC/CGcugdVIwB+53f5sZziXgwUh3iXlAlANMxcZyDEfTHMe6+Z5FofV8nopXP7w==} + archiver-utils@3.0.4: + resolution: {integrity: sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==} engines: {node: '>= 10'} - dependencies: - archiver-utils: 2.1.0 - async: 3.2.4 - buffer-crc32: 0.2.13 - readable-stream: 3.6.2 - readdir-glob: 1.1.3 - tar-stream: 2.2.0 - zip-stream: 4.1.0 - dev: true - /are-we-there-yet@1.1.7: - resolution: {integrity: sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==} - dependencies: - delegates: 1.0.0 - readable-stream: 2.3.8 - dev: false + archiver@5.3.2: + resolution: {integrity: sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==} + engines: {node: '>= 10'} - /are-we-there-yet@2.0.0: + are-we-there-yet@2.0.0: resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==} engines: {node: '>=10'} - dependencies: - delegates: 1.0.0 - readable-stream: 3.6.2 + deprecated: This package is no longer supported. - /are-we-there-yet@3.0.1: + are-we-there-yet@3.0.1: resolution: {integrity: sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - dependencies: - delegates: 1.0.0 - readable-stream: 3.6.2 + deprecated: This package is no longer supported. - /arg@4.1.3: + arg@4.1.3: resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} - /arg@5.0.2: + arg@5.0.2: resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} - /argparse@1.0.10: + argparse@1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} - dependencies: - sprintf-js: 1.0.3 - /argparse@2.0.1: + argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - /aria-query@5.1.3: + aria-hidden@1.2.6: + resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} + engines: {node: '>=10'} + + aria-query@5.1.3: resolution: {integrity: sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==} - dependencies: - deep-equal: 2.2.0 - /array-buffer-byte-length@1.0.0: - resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==} - dependencies: - call-bind: 1.0.2 - is-array-buffer: 3.0.2 + aria-query@5.3.0: + resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + + aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} - /array-differ@3.0.0: + array-buffer-byte-length@1.0.1: + resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==} + engines: {node: '>= 0.4'} + + array-differ@3.0.0: resolution: {integrity: sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg==} engines: {node: '>=8'} - /array-flatten@1.1.1: + array-flatten@1.1.1: resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} - /array-flatten@2.1.2: - resolution: {integrity: sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==} - dev: false - - /array-includes@3.1.6: - resolution: {integrity: sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==} + array-includes@3.1.8: + resolution: {integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==} engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 - get-intrinsic: 1.2.0 - is-string: 1.0.7 - - /array-tree-filter@2.1.0: - resolution: {integrity: sha512-4ROwICNlNw/Hqa9v+rk5h22KjmzB1JGTMVKP2AKJBOCgb0yL0ASf0+YvCcLNNwquOHNX48jkeZIJ3a+oOQqKcw==} - /array-union@1.0.2: + array-union@1.0.2: resolution: {integrity: sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==} engines: {node: '>=0.10.0'} - dependencies: - array-uniq: 1.0.3 - dev: true - /array-union@2.1.0: + array-union@2.1.0: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} - /array-uniq@1.0.3: + array-uniq@1.0.3: resolution: {integrity: sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==} engines: {node: '>=0.10.0'} - dev: true - /array.prototype.flat@1.3.1: - resolution: {integrity: sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==} + array.prototype.findlast@1.2.5: + resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 - es-shim-unscopables: 1.0.0 - /array.prototype.flatmap@1.3.1: - resolution: {integrity: sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==} + array.prototype.findlastindex@1.2.5: + resolution: {integrity: sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==} engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 - es-shim-unscopables: 1.0.0 - /array.prototype.reduce@1.0.5: - resolution: {integrity: sha512-kDdugMl7id9COE8R7MHF5jWk7Dqt/fs4Pv+JXoICnYwqpjjjbUurz6w5fT5IG6brLdJhv6/VoHB0H7oyIBXd+Q==} + array.prototype.flat@1.3.2: + resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 - es-array-method-boxes-properly: 1.0.0 - is-string: 1.0.7 - /array.prototype.tosorted@1.1.1: - resolution: {integrity: sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ==} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 - es-shim-unscopables: 1.0.0 - get-intrinsic: 1.2.0 + array.prototype.flatmap@1.3.2: + resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} + engines: {node: '>= 0.4'} + + array.prototype.reduce@1.0.7: + resolution: {integrity: sha512-mzmiUCVwtiD4lgxYP8g7IYy8El8p2CSMePvIbTS7gchKir/L1fgJrk0yDKmAX6mnRQFKNADYIk8nNlTris5H1Q==} + engines: {node: '>= 0.4'} + + array.prototype.tosorted@1.1.4: + resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==} + engines: {node: '>= 0.4'} + + arraybuffer.prototype.slice@1.0.3: + resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==} + engines: {node: '>= 0.4'} - /arrify@2.0.1: + arrify@2.0.1: resolution: {integrity: sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==} engines: {node: '>=8'} - /asap@2.0.6: + asap@2.0.6: resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} - /asn1@0.2.6: + asn1@0.2.6: resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==} - dependencies: - safer-buffer: 2.1.2 - dev: true - /assert-options@0.8.1: - resolution: {integrity: sha512-5lNGRB5g5i2bGIzb+J1QQE1iKU/WEMVBReFIc5pPDWjcPj23otPL0eI6PB2v7QPi0qU6Mhym5D3y0ZiSIOf3GA==} + assert-options@0.8.2: + resolution: {integrity: sha512-XaXoMxY0zuwAb0YuZjxIm8FeWvNq0aWNIbrzHhFjme8Smxw4JlPoyrAKQ6808k5UvQdhvnWqHZCphq5mXd4TDA==} engines: {node: '>=10.0.0'} - dev: false - - /ast-types-flow@0.0.7: - resolution: {integrity: sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==} - /ast-types@0.13.4: - resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==} - engines: {node: '>=4'} - dependencies: - tslib: 2.5.0 - dev: true - - /ast-types@0.15.2: - resolution: {integrity: sha512-c27loCv9QkZinsa5ProX751khO9DJl/AcB5c2KNtA6NRvHKS0PgLfcftz72KVq504vB0Gku5s2kUZzDBvQWvHg==} - engines: {node: '>=4'} - dependencies: - tslib: 2.5.0 - dev: true + assert-plus@1.0.0: + resolution: {integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==} + engines: {node: '>=0.8'} - /astring@1.8.4: - resolution: {integrity: sha512-97a+l2LBU3Op3bBQEff79i/E4jMD2ZLFD8rHx9B6mXyB2uQwhJQYfiDqUwtfjF4QA1F2qs//N6Cw8LetMbQjcw==} - hasBin: true - dev: true + ast-types-flow@0.0.8: + resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} - /async-lock@1.4.0: - resolution: {integrity: sha512-coglx5yIWuetakm3/1dsX9hxCNox22h7+V80RQOu2XUUMidtArxKoZoOtHUPuR84SycKTXzgGzAUR5hJxujyJQ==} - dev: true + async-lock@1.4.1: + resolution: {integrity: sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==} - /async-retry@1.3.3: + async-retry@1.3.3: resolution: {integrity: sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==} - dependencies: - retry: 0.13.1 - dev: false - optional: true - - /async-sema@3.1.1: - resolution: {integrity: sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg==} - dev: true - - /async-validator@4.2.5: - resolution: {integrity: sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==} - /async@3.2.4: - resolution: {integrity: sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==} + async@3.2.6: + resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} - /asynckit@0.4.0: + asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - dev: false - /asyncro@3.0.0: + asyncro@3.0.0: resolution: {integrity: sha512-nEnWYfrBmA3taTiuiOoZYmgJ/CNrSoQLeLs29SeLcPu60yaw/mHDBHV0iOZ051fTvsTHxpCY+gXibqT9wbQYfg==} - dev: true - /at-least-node@1.0.0: + at-least-node@1.0.0: resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} engines: {node: '>= 4.0.0'} - dev: false - /autoprefixer@10.4.14(postcss@8.4.21): - resolution: {integrity: sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==} + autoprefixer@10.4.20: + resolution: {integrity: sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==} engines: {node: ^10 || ^12 || >=14} hasBin: true peerDependencies: - postcss: ^8.1.0 - dependencies: - browserslist: 4.21.5 - caniuse-lite: 1.0.30001478 - fraction.js: 4.2.0 - normalize-range: 0.1.2 - picocolors: 1.0.0 - postcss: 8.4.21 - postcss-value-parser: 4.2.0 + postcss: ^8.4.47 - /autoprefixer@9.8.8: - resolution: {integrity: sha512-eM9d/swFopRt5gdJ7jrpCwgvEMIayITpojhkkSMRsFHYuH5bkSQ4p/9qTEHtmNudUZh22Tehu7I6CxAW0IXTKA==} + autoprefixer@10.4.21: + resolution: {integrity: sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==} + engines: {node: ^10 || ^12 || >=14} hasBin: true - dependencies: - browserslist: 4.21.5 - caniuse-lite: 1.0.30001478 - normalize-range: 0.1.2 - num2fraction: 1.2.2 - picocolors: 0.2.1 - postcss: 7.0.39 - postcss-value-parser: 4.2.0 - dev: true + peerDependencies: + postcss: ^8.4.47 - /available-typed-arrays@1.0.5: - resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} - /axe-core@4.6.3: - resolution: {integrity: sha512-/BQzOX780JhsxDnPpH4ZiyrJAzcd8AfzFPkv+89veFSr1rcMjuq2JDCwypKaPeB6ljHp9KjXhPpjgCvQlWYuqg==} + axe-core@4.10.2: + resolution: {integrity: sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w==} engines: {node: '>=4'} - /axios@0.27.2: - resolution: {integrity: sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==} - dependencies: - follow-redirects: 1.15.2 - form-data: 4.0.0 - transitivePeerDependencies: - - debug - dev: false + axios@1.8.2: + resolution: {integrity: sha512-ls4GYBm5aig9vWx8AWDSGLpnpDQRtWAfrjU+EuytuODrFBkqesN2RkOQCBzrA1RQNHw1SmRMSDDDSwzNAYQ6Rg==} - /axobject-query@3.1.1: - resolution: {integrity: sha512-goKlv8DZrK9hUh975fnHzhNIO4jUnFCfv/dszV5VwUGDFjI6vQ2VwoyjYjYNEbBE8AH87TduWP5uyDR1D+Iteg==} - dependencies: - deep-equal: 2.2.0 + axobject-query@4.1.0: + resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} + engines: {node: '>= 0.4'} - /babel-jest@27.5.1(@babel/core@7.21.4): + babel-jest@27.5.1: resolution: {integrity: sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} peerDependencies: '@babel/core': ^7.8.0 - dependencies: - '@babel/core': 7.21.4 - '@jest/transform': 27.5.1 - '@jest/types': 27.5.1 - '@types/babel__core': 7.20.0 - babel-plugin-istanbul: 6.1.1 - babel-preset-jest: 27.5.1(@babel/core@7.21.4) - chalk: 4.1.2 - graceful-fs: 4.2.11 - slash: 3.0.0 - transitivePeerDependencies: - - supports-color - dev: false - /babel-jest@28.1.3(@babel/core@7.21.4): + babel-jest@28.1.3: resolution: {integrity: sha512-epUaPOEWMk3cWX0M/sPvCHHCe9fMFAa/9hXEgKP8nFfNl/jlGkE9ucq9NqkZGXLDduCJYS0UvSlPUwC0S+rH6Q==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} peerDependencies: '@babel/core': ^7.8.0 - dependencies: - '@babel/core': 7.21.4 - '@jest/transform': 28.1.3 - '@types/babel__core': 7.20.0 - babel-plugin-istanbul: 6.1.1 - babel-preset-jest: 28.1.3(@babel/core@7.21.4) - chalk: 4.1.2 - graceful-fs: 4.2.11 - slash: 3.0.0 - transitivePeerDependencies: - - supports-color - dev: true - /babel-jest@29.5.0(@babel/core@7.21.4): - resolution: {integrity: sha512-mA4eCDh5mSo2EcA9xQjVTpmbbNk32Zb3Q3QFQsNhaK56Q+yoXowzFodLux30HRgyOho5rsQ6B0P9QpMkvvnJ0Q==} + babel-jest@29.7.0: + resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: '@babel/core': ^7.8.0 - dependencies: - '@babel/core': 7.21.4 - '@jest/transform': 29.5.0 - '@types/babel__core': 7.20.0 - babel-plugin-istanbul: 6.1.1 - babel-preset-jest: 29.5.0(@babel/core@7.21.4) - chalk: 4.1.2 - graceful-fs: 4.2.11 - slash: 3.0.0 - transitivePeerDependencies: - - supports-color - dev: true - /babel-loader@8.3.0(@babel/core@7.21.4)(webpack@5.78.0): - resolution: {integrity: sha512-H8SvsMF+m9t15HNLMipppzkC+Y2Yq+v3SonZyU70RBL/h1gxPkH08Ot8pEE9Z4Kd+czyWJClmFS8qzIP9OZ04Q==} + babel-loader@8.4.1: + resolution: {integrity: sha512-nXzRChX+Z1GoE6yWavBQg6jDslyFF3SDjl2paADuoQtQW10JqShJt62R6eJQ5m/pjJFDT8xgKIWSP85OY8eXeA==} engines: {node: '>= 8.9'} peerDependencies: '@babel/core': ^7.0.0 webpack: '>=2' - dependencies: - '@babel/core': 7.21.4 - find-cache-dir: 3.3.2 - loader-utils: 2.0.4 - make-dir: 3.1.0 - schema-utils: 2.7.1 - webpack: 5.78.0(esbuild@0.16.3)(webpack-cli@5.0.1) - dev: false - /babel-loader@9.1.2(@babel/core@7.21.4)(webpack@5.78.0): - resolution: {integrity: sha512-mN14niXW43tddohGl8HPu5yfQq70iUThvFL/4QzESA7GcZoC0eVOhvWdQ8+3UlSjaDE9MVtsW9mxDY07W7VpVA==} + babel-loader@9.2.1: + resolution: {integrity: sha512-fqe8naHt46e0yIdkjUZYqddSXfej3AHajX+CSO5X7oy0EmPc6o5Xh+RClNoHjnieWz9AW4kZxW9yyFMhVB1QLA==} engines: {node: '>= 14.15.0'} peerDependencies: '@babel/core': ^7.12.0 webpack: '>=5' - dependencies: - '@babel/core': 7.21.4 - find-cache-dir: 3.3.2 - schema-utils: 4.0.0 - webpack: 5.78.0(esbuild@0.16.3)(webpack-cli@5.0.1) - dev: true - /babel-plugin-istanbul@6.1.1: + babel-plugin-istanbul@6.1.1: resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} engines: {node: '>=8'} - dependencies: - '@babel/helper-plugin-utils': 7.20.2 - '@istanbuljs/load-nyc-config': 1.1.0 - '@istanbuljs/schema': 0.1.3 - istanbul-lib-instrument: 5.2.1 - test-exclude: 6.0.0 - transitivePeerDependencies: - - supports-color - /babel-plugin-jest-hoist@27.5.1: + babel-plugin-jest-hoist@27.5.1: resolution: {integrity: sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - dependencies: - '@babel/template': 7.20.7 - '@babel/types': 7.21.4 - '@types/babel__core': 7.20.0 - '@types/babel__traverse': 7.18.3 - dev: false - /babel-plugin-jest-hoist@28.1.3: + babel-plugin-jest-hoist@28.1.3: resolution: {integrity: sha512-Ys3tUKAmfnkRUpPdpa98eYrAR0nV+sSFUZZEGuQ2EbFd1y4SOLtD5QDNHAq+bb9a+bbXvYQC4b+ID/THIMcU6Q==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dependencies: - '@babel/template': 7.20.7 - '@babel/types': 7.21.4 - '@types/babel__core': 7.20.0 - '@types/babel__traverse': 7.18.3 - dev: true - /babel-plugin-jest-hoist@29.5.0: - resolution: {integrity: sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w==} + babel-plugin-jest-hoist@29.6.3: + resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@babel/template': 7.20.7 - '@babel/types': 7.21.4 - '@types/babel__core': 7.20.0 - '@types/babel__traverse': 7.18.3 - dev: true - - /babel-plugin-macros@2.8.0: - resolution: {integrity: sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==} - dependencies: - '@babel/runtime': 7.21.0 - cosmiconfig: 6.0.0 - resolve: 1.22.2 - dev: true - /babel-plugin-macros@3.1.0: + babel-plugin-macros@3.1.0: resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} engines: {node: '>=10', npm: '>=6'} - dependencies: - '@babel/runtime': 7.21.0 - cosmiconfig: 7.1.0 - resolve: 1.22.2 - dev: false - /babel-plugin-named-asset-import@0.4.0-next.14(@babel/core@7.21.4): - resolution: {integrity: sha512-MazwHExK09ffj7qGS++lmcPNXG7dB2BDF8vLmO1dgkK3p10fV4NNE9hljmwoiDM52vgeMd9hJW9GBOCEnEpL/Q==} + babel-plugin-named-asset-import@0.3.8: + resolution: {integrity: sha512-WXiAc++qo7XcJ1ZnTYGtLxmBCVbddAml3CEXgWaBzNzLNoxtQ8AiGEFDMOhot9XjTCQbvP5E77Fj9Gk924f00Q==} peerDependencies: '@babel/core': ^7.1.0 - dependencies: - '@babel/core': 7.21.4 - dev: false - /babel-plugin-polyfill-corejs2@0.3.3(@babel/core@7.21.4): - resolution: {integrity: sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==} + babel-plugin-polyfill-corejs2@0.4.11: + resolution: {integrity: sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q==} peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/compat-data': 7.21.4 - '@babel/core': 7.21.4 - '@babel/helper-define-polyfill-provider': 0.3.3(@babel/core@7.21.4) - semver: 6.3.0 - transitivePeerDependencies: - - supports-color + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 - /babel-plugin-polyfill-corejs3@0.6.0(@babel/core@7.21.4): - resolution: {integrity: sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==} + babel-plugin-polyfill-corejs3@0.10.6: + resolution: {integrity: sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==} peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-define-polyfill-provider': 0.3.3(@babel/core@7.21.4) - core-js-compat: 3.30.1 - transitivePeerDependencies: - - supports-color + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 - /babel-plugin-polyfill-regenerator@0.4.1(@babel/core@7.21.4): - resolution: {integrity: sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==} + babel-plugin-polyfill-regenerator@0.6.2: + resolution: {integrity: sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg==} peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/helper-define-polyfill-provider': 0.3.3(@babel/core@7.21.4) - transitivePeerDependencies: - - supports-color + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 - /babel-plugin-transform-async-to-promises@0.8.18: + babel-plugin-transform-async-to-promises@0.8.18: resolution: {integrity: sha512-WpOrF76nUHijnNn10eBGOHZmXQC8JYRME9rOLxStOga7Av2VO53ehVFvVNImMksVtQuL2/7ZNxEgxnx7oo/3Hw==} - dev: true - /babel-plugin-transform-react-remove-prop-types@0.4.24: + babel-plugin-transform-react-remove-prop-types@0.4.24: resolution: {integrity: sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA==} - dev: false - /babel-plugin-transform-replace-expressions@0.2.0(@babel/core@7.21.4): + babel-plugin-transform-replace-expressions@0.2.0: resolution: {integrity: sha512-Eh1rRd9hWEYgkgoA3D0kGp7xJ/wgVshgsqmq60iC4HVWD+Lux+fNHSHBa2v1Hsv+dHflShC71qKhiH40OiPtDA==} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.4 - '@babel/parser': 7.21.4 - dev: true - /babel-preset-current-node-syntax@1.0.1(@babel/core@7.21.4): - resolution: {integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==} + babel-preset-current-node-syntax@1.1.0: + resolution: {integrity: sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==} peerDependencies: '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.21.4 - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.21.4) - '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.21.4) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.21.4) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.21.4) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.21.4) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.21.4) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.21.4) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.21.4) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.21.4) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.21.4) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.21.4) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.21.4) - - /babel-preset-jest@27.5.1(@babel/core@7.21.4): + + babel-preset-jest@27.5.1: resolution: {integrity: sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} peerDependencies: '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.21.4 - babel-plugin-jest-hoist: 27.5.1 - babel-preset-current-node-syntax: 1.0.1(@babel/core@7.21.4) - dev: false - /babel-preset-jest@28.1.3(@babel/core@7.21.4): + babel-preset-jest@28.1.3: resolution: {integrity: sha512-L+fupJvlWAHbQfn74coNX3zf60LXMJsezNvvx8eIh7iOR1luJ1poxYgQk1F8PYtNq/6QODDHCqsSnTFSWC491A==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} peerDependencies: '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.21.4 - babel-plugin-jest-hoist: 28.1.3 - babel-preset-current-node-syntax: 1.0.1(@babel/core@7.21.4) - dev: true - /babel-preset-jest@29.5.0(@babel/core@7.21.4): - resolution: {integrity: sha512-JOMloxOqdiBSxMAzjRaH023/vvcaSaec49zvg+2LmNsktC7ei39LTJGw02J+9uUtTZUq6xbLyJ4dxe9sSmIuAg==} + babel-preset-jest@29.6.3: + resolution: {integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.21.4 - babel-plugin-jest-hoist: 29.5.0 - babel-preset-current-node-syntax: 1.0.1(@babel/core@7.21.4) - dev: true - - /babel-preset-react-app@10.1.0-next.14: - resolution: {integrity: sha512-/xYDXmFaTFqny9vVvsaNigvt0zzFRjjgi/3oTCeg3UzPKcWW1O6WG9JyeAV3d47Sewyr7i5B57nL5or32Vnn6A==} - dependencies: - '@babel/core': 7.21.4 - '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-proposal-decorators': 7.21.0(@babel/core@7.21.4) - '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-proposal-numeric-separator': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.21.4) - '@babel/plugin-proposal-private-methods': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-transform-flow-strip-types': 7.21.0(@babel/core@7.21.4) - '@babel/plugin-transform-react-display-name': 7.18.6(@babel/core@7.21.4) - '@babel/plugin-transform-runtime': 7.21.4(@babel/core@7.21.4) - '@babel/preset-env': 7.21.4(@babel/core@7.21.4) - '@babel/preset-react': 7.18.6(@babel/core@7.21.4) - '@babel/preset-typescript': 7.21.4(@babel/core@7.21.4) - '@babel/runtime': 7.21.0 - babel-plugin-macros: 3.1.0 - babel-plugin-transform-react-remove-prop-types: 0.4.24 - transitivePeerDependencies: - - supports-color - dev: false - /babel-runtime@6.25.0: - resolution: {integrity: sha512-zeCYxDePWYAT/DfmQWIHsMSFW2vv45UIwIAMjGvQVsTd47RwsiRH0uK1yzyWZ7LDBKdhnGDPM6NYEO5CZyhPrg==} - dependencies: - core-js: 2.6.12 - regenerator-runtime: 0.10.5 - dev: false + babel-preset-react-app@10.0.1: + resolution: {integrity: sha512-b0D9IZ1WhhCWkrTXyFuIIgqGzSkRIH5D5AmB0bXbzYAB1OBAwHcUeyWW2LorutLWF5btNo/N7r/cIdmvvKJlYg==} - /bail@2.0.2: - resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} - dev: true + babel-runtime@6.25.0: + resolution: {integrity: sha512-zeCYxDePWYAT/DfmQWIHsMSFW2vv45UIwIAMjGvQVsTd47RwsiRH0uK1yzyWZ7LDBKdhnGDPM6NYEO5CZyhPrg==} - /balanced-match@1.0.2: + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - /base16@1.0.0: + base16@1.0.0: resolution: {integrity: sha512-pNdYkNPiJUnEhnfXV56+sQy8+AaPcG3POZAUnwr4EeqCUZFz4u2PePbo3e5Gj4ziYPCWGUZT9RHisvJKnwFuBQ==} - dev: false - /base64-js@1.5.1: + base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - /batch@0.6.1: + base64id@2.0.0: + resolution: {integrity: sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==} + engines: {node: ^4.5.0 || >= 5.9} + + batch@0.6.1: resolution: {integrity: sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==} - dev: false - /bcrypt-pbkdf@1.0.2: + bcrypt-pbkdf@1.0.2: resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==} - dependencies: - tweetnacl: 0.14.5 - dev: true - /before-after-hook@2.2.3: + before-after-hook@2.2.3: resolution: {integrity: sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==} - /bfj@7.0.2: - resolution: {integrity: sha512-+e/UqUzwmzJamNF50tBV6tZPTORow7gQ96iFow+8b562OdMpEK0BcJEq2OSPEDmAbSMBQ7PKZ87ubFkgxpYWgw==} + bfj@7.1.0: + resolution: {integrity: sha512-I6MMLkn+anzNdCUp9hMRyui1HaNEUCco50lxbvNS4+EyXg8lN3nJ48PjPWtbH8UVS9CuMoaKE9U2V3l29DaRQw==} engines: {node: '>= 8.0.0'} - dependencies: - bluebird: 3.7.2 - check-types: 11.2.2 - hoopy: 0.1.4 - tryer: 1.0.1 - dev: false - /big-integer@1.6.51: - resolution: {integrity: sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==} + big-integer@1.6.52: + resolution: {integrity: sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==} engines: {node: '>=0.6'} - dev: false - /big.js@5.2.2: + big.js@5.2.2: resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==} - /big.js@6.2.1: - resolution: {integrity: sha512-bCtHMwL9LeDIozFn+oNhhFoq+yQ3BNdnsLSASUxLciOb1vgvpHsIO1dsENiGMgbb4SkP5TrzWzRiLddn8ahVOQ==} - dev: true + bignumber.js@9.1.2: + resolution: {integrity: sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==} - /bignumber.js@9.1.1: - resolution: {integrity: sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig==} - dev: false - - /bin-links@3.0.3: + bin-links@3.0.3: resolution: {integrity: sha512-zKdnMPWEdh4F5INR07/eBrodC7QrF5JKvqskjz/ZZRXg5YSAZIbn8zGhbhUrElzHBZ2fvEQdOU59RHcTG3GiwA==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - dependencies: - cmd-shim: 5.0.0 - mkdirp-infer-owner: 2.0.0 - npm-normalize-package-bin: 2.0.0 - read-cmd-shim: 3.0.1 - rimraf: 3.0.2 - write-file-atomic: 4.0.2 - /binary-extensions@2.2.0: - resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} - /binaryextensions@4.18.0: - resolution: {integrity: sha512-PQu3Kyv9dM4FnwB7XGj1+HucW+ShvJzJqjuw1JkKVs1mWdwOKVcRjOi+pV9X52A0tNvrPCsPkbFFQb+wE1EAXw==} + binaryextensions@4.19.0: + resolution: {integrity: sha512-DRxnVbOi/1OgA5pA9EDiRT8gvVYeqfuN7TmPfLyt6cyho3KbHCi3EtDQf39TTmGDrR5dZ9CspdXhPkL/j/WGbg==} engines: {node: '>=0.8'} - /bindings@1.5.0: + bindings@1.5.0: resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} - dependencies: - file-uri-to-path: 1.0.0 - dev: true - /bl@4.1.0: + bintrees@1.0.2: + resolution: {integrity: sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw==} + + bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} - dependencies: - buffer: 5.7.1 - inherits: 2.0.4 - readable-stream: 3.6.2 - /bluebird@3.7.2: + bluebird@3.7.2: resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==} - dev: false - /body-parser@1.20.1: - resolution: {integrity: sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==} + bn.js@4.12.0: + resolution: {integrity: sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==} + + body-parser@1.20.3: + resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} - dependencies: - bytes: 3.1.2 - content-type: 1.0.5 - debug: 2.6.9 - depd: 2.0.0 - destroy: 1.2.0 - http-errors: 2.0.0 - iconv-lite: 0.4.24 - on-finished: 2.4.1 - qs: 6.11.0 - raw-body: 2.5.1 - type-is: 1.6.18 - unpipe: 1.0.0 - transitivePeerDependencies: - - supports-color - /bonjour-service@1.1.1: - resolution: {integrity: sha512-Z/5lQRMOG9k7W+FkeGTNjh7htqn/2LMnfOvBZ8pynNZCM9MwkQkI3zeI4oz09uWdcgmgHugVvBqxGg4VQJ5PCg==} - dependencies: - array-flatten: 2.1.2 - dns-equal: 1.0.0 - fast-deep-equal: 3.1.3 - multicast-dns: 7.2.5 - dev: false + bonjour-service@1.2.1: + resolution: {integrity: sha512-oSzCS2zV14bh2kji6vNe7vrpJYCHGvcZnlffFQ1MEoX/WOeQ/teD8SYWKR942OI3INjq8OMNJlbPK5LLLUxFDw==} - /boolbase@1.0.0: + boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} - /bowser@2.11.0: + bottleneck@2.19.5: + resolution: {integrity: sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==} + + bowser@2.11.0: resolution: {integrity: sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==} - dev: false - /brace-expansion@1.1.11: + boxen@7.1.1: + resolution: {integrity: sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog==} + engines: {node: '>=14.16'} + + brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} - dependencies: - balanced-match: 1.0.2 - concat-map: 0.0.1 - /brace-expansion@2.0.1: + brace-expansion@2.0.1: resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} - dependencies: - balanced-match: 1.0.2 - /braces@3.0.2: - resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - dependencies: - fill-range: 7.0.1 - /brotli-size@4.0.0: + brorand@1.1.0: + resolution: {integrity: sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==} + + brotli-size@4.0.0: resolution: {integrity: sha512-uA9fOtlTRC0iqKfzff1W34DXUA3GyVqbUaeo3Rw3d4gd1eavKVCETXrn3NzO74W+UVkG3UHu8WxUi+XvKI/huA==} engines: {node: '>= 10.16.0'} - dependencies: - duplexer: 0.1.1 - dev: true - /browser-process-hrtime@1.0.0: + browser-process-hrtime@1.0.0: resolution: {integrity: sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==} - dev: false - /browserify-zlib@0.1.4: - resolution: {integrity: sha512-19OEpq7vWgsH6WkvkBJQDFvJS1uPcbFOQ4v9CU839dO+ZZXUZO6XpE6hNCqvlIIj+4fZvRiJ6DsAQ382GwiyTQ==} - dependencies: - pako: 0.2.9 - dev: true + browserslist@4.24.2: + resolution: {integrity: sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true - /browserslist@4.21.5: - resolution: {integrity: sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==} + browserslist@4.25.1: + resolution: {integrity: sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true - dependencies: - caniuse-lite: 1.0.30001480 - electron-to-chromium: 1.4.368 - node-releases: 2.0.10 - update-browserslist-db: 1.0.11(browserslist@4.21.5) - /bs-logger@0.2.6: + bs-logger@0.2.6: resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==} engines: {node: '>= 6'} - dependencies: - fast-json-stable-stringify: 2.1.0 - dev: true - /bser@2.1.1: + bser@2.1.1: resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} - dependencies: - node-int64: 0.4.0 - /bson@5.2.0: - resolution: {integrity: sha512-HevkSpDbpUfsrHWmWiAsNavANKYIErV2ePXllp1bwq5CDreAaFVj6RVlZpJnxK4WWDCJ/5jMUpaY6G526q3Hjg==} - engines: {node: '>=14.20.1'} - dev: false + bson@6.10.3: + resolution: {integrity: sha512-MTxGsqgYTwfshYWTRdmZRC+M7FnG1b4y7RO7p2k3X24Wq0yv1m77Wsj0BzlPzd/IowgESfsruQCUToa7vbOpPQ==} + engines: {node: '>=16.20.1'} - /buffer-crc32@0.2.13: + buffer-crc32@0.2.13: resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} - dev: true - /buffer-equal-constant-time@1.0.1: + buffer-equal-constant-time@1.0.1: resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} - dev: false - /buffer-from@1.1.2: + buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - /buffer-writer@2.0.0: - resolution: {integrity: sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==} - engines: {node: '>=4'} - - /buffer@5.7.1: + buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - /buildcheck@0.0.5: - resolution: {integrity: sha512-jYWpRy8eedl/JZqkOeq0X0bNcaK04hXKhIi4gYsDKZUJWRjJJWViYfsMXO0BJQ40zSLcdLoa+iqe48Kz2PtQag==} + buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + + buildcheck@0.0.6: + resolution: {integrity: sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A==} engines: {node: '>=10.0.0'} - dev: true - optional: true - /builtin-modules@3.3.0: + builtin-modules@3.3.0: resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} engines: {node: '>=6'} - /builtins@1.0.3: + builtins@1.0.3: resolution: {integrity: sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==} - /busboy@1.6.0: - resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} - engines: {node: '>=10.16.0'} - dependencies: - streamsearch: 1.1.0 - dev: false - - /byline@5.0.0: + byline@5.0.0: resolution: {integrity: sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q==} engines: {node: '>=0.10.0'} - dev: true - /bytes@3.0.0: + bytes@3.0.0: resolution: {integrity: sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==} engines: {node: '>= 0.8'} - dev: false - /bytes@3.1.2: + bytes@3.1.2: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} - /cac@6.7.14: - resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} - engines: {node: '>=8'} - dev: true - - /cacache@15.3.0: + cacache@15.3.0: resolution: {integrity: sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==} engines: {node: '>= 10'} - dependencies: - '@npmcli/fs': 1.1.1 - '@npmcli/move-file': 1.1.2 - chownr: 2.0.0 - fs-minipass: 2.1.0 - glob: 7.2.3 - infer-owner: 1.0.4 - lru-cache: 6.0.0 - minipass: 3.3.6 - minipass-collect: 1.0.2 - minipass-flush: 1.0.5 - minipass-pipeline: 1.2.4 - mkdirp: 1.0.4 - p-map: 4.0.0 - promise-inflight: 1.0.1 - rimraf: 3.0.2 - ssri: 8.0.1 - tar: 6.1.13 - unique-filename: 1.1.1 - transitivePeerDependencies: - - bluebird - /cacache@16.1.3: + cacache@16.1.3: resolution: {integrity: sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - dependencies: - '@npmcli/fs': 2.1.2 - '@npmcli/move-file': 2.0.1 - chownr: 2.0.0 - fs-minipass: 2.1.0 - glob: 8.1.0 - infer-owner: 1.0.4 - lru-cache: 7.18.3 - minipass: 3.3.6 - minipass-collect: 1.0.2 - minipass-flush: 1.0.5 - minipass-pipeline: 1.2.4 - mkdirp: 1.0.4 - p-map: 4.0.0 - promise-inflight: 1.0.1 - rimraf: 3.0.2 - ssri: 9.0.1 - tar: 6.1.13 - unique-filename: 2.0.1 - transitivePeerDependencies: - - bluebird - - /cacheable-lookup@5.0.4: - resolution: {integrity: sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==} - engines: {node: '>=10.6.0'} - dev: true - - /cacheable-request@7.0.2: - resolution: {integrity: sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==} - engines: {node: '>=8'} - dependencies: - clone-response: 1.0.3 - get-stream: 5.2.0 - http-cache-semantics: 4.1.1 - keyv: 4.5.2 - lowercase-keys: 2.0.0 - normalize-url: 6.1.0 - responselike: 2.0.1 - dev: true - - /call-bind@1.0.2: - resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} - dependencies: - function-bind: 1.1.1 - get-intrinsic: 1.2.0 - /call-me-maybe@1.0.2: - resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==} - dev: false + cacache@17.1.4: + resolution: {integrity: sha512-/aJwG2l3ZMJ1xNAnqbMpA40of9dj/pIH3QfiuQSqjfPJF747VR0J/bHn+/KdNnHKc6XQcWt/AfRSBft82W1d2A==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - /caller-callsite@2.0.0: - resolution: {integrity: sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ==} - engines: {node: '>=4'} - dependencies: - callsites: 2.0.0 - dev: true - - /caller-path@2.0.0: - resolution: {integrity: sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A==} - engines: {node: '>=4'} - dependencies: - caller-callsite: 2.0.0 - dev: true + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} - /callsites@2.0.0: - resolution: {integrity: sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ==} - engines: {node: '>=4'} - dev: true + call-bind@1.0.7: + resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} + engines: {node: '>= 0.4'} - /callsites@3.1.0: + callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} - /camel-case@3.0.0: - resolution: {integrity: sha512-+MbKztAYHXPr1jNTSKQF52VpcFjwY5RkR7fxksV8Doo4KAYc5Fl4UJRgthBbTmEx8C54DqahhbLJkDwjI3PI/w==} - dependencies: - no-case: 2.3.2 - upper-case: 1.1.3 - dev: false - - /camel-case@4.1.2: + camel-case@4.1.2: resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==} - dependencies: - pascal-case: 3.1.2 - tslib: 2.5.0 - dev: false - /camelcase-css@2.0.1: + camelcase-css@2.0.1: resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} engines: {node: '>= 6'} - /camelcase@5.3.1: + camelcase@5.3.1: resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} engines: {node: '>=6'} - /camelcase@6.3.0: + camelcase@6.3.0: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} - /caniuse-api@3.0.0: + camelcase@7.0.1: + resolution: {integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==} + engines: {node: '>=14.16'} + + caniuse-api@3.0.0: resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} - dependencies: - browserslist: 4.21.5 - caniuse-lite: 1.0.30001480 - lodash.memoize: 4.1.2 - lodash.uniq: 4.5.0 - /caniuse-lite@1.0.30001478: - resolution: {integrity: sha512-gMhDyXGItTHipJj2ApIvR+iVB5hd0KP3svMWWXDvZOmjzJJassGLMfxRkQCSYgGd2gtdL/ReeiyvMSFD1Ss6Mw==} + caniuse-lite@1.0.30001675: + resolution: {integrity: sha512-/wV1bQwPrkLiQMjaJF5yUMVM/VdRPOCU8QZ+PmG6uW6DvYSrNY1bpwHI/3mOcUosLaJCzYDi5o91IQB51ft6cg==} - /caniuse-lite@1.0.30001480: - resolution: {integrity: sha512-q7cpoPPvZYgtyC4VaBSN0Bt+PJ4c4EYRf0DrduInOz2SkFpHD5p3LnvEpqBp7UnJn+8x1Ogl1s38saUxe+ihQQ==} + caniuse-lite@1.0.30001727: + resolution: {integrity: sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==} - /case-sensitive-paths-webpack-plugin@2.4.0: + case-sensitive-paths-webpack-plugin@2.4.0: resolution: {integrity: sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==} engines: {node: '>=4'} - dev: false - - /catharsis@0.9.0: - resolution: {integrity: sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==} - engines: {node: '>= 10'} - dependencies: - lodash: 4.17.21 - dev: false - optional: true - /chalk@1.1.3: + chalk@1.1.3: resolution: {integrity: sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==} engines: {node: '>=0.10.0'} - dependencies: - ansi-styles: 2.2.1 - escape-string-regexp: 1.0.5 - has-ansi: 2.0.0 - strip-ansi: 3.0.1 - supports-color: 2.0.0 - dev: true - /chalk@2.4.2: + chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} - dependencies: - ansi-styles: 3.2.1 - escape-string-regexp: 1.0.5 - supports-color: 5.5.0 - /chalk@3.0.0: + chalk@3.0.0: resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==} engines: {node: '>=8'} - dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 - dev: true - /chalk@4.1.2: + chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} - dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 - /chalk@5.2.0: - resolution: {integrity: sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==} + chalk@5.3.0: + resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - /char-regex@1.0.2: + char-regex@1.0.2: resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} engines: {node: '>=10'} - /char-regex@2.0.1: + char-regex@2.0.1: resolution: {integrity: sha512-oSvEeo6ZUD7NepqAat3RqoucZ5SeqLJgOvVIwkafu6IP3V0pO38s/ypdVUmDDK6qIIHNlYHJAKX9E7R7HoKElw==} engines: {node: '>=12.20'} - dev: false - - /character-entities-html4@2.1.0: - resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} - dev: true - - /character-entities-legacy@3.0.0: - resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} - dev: true - /character-entities@2.0.2: - resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} - dev: true - - /character-reference-invalid@2.0.1: - resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} - dev: true - - /chardet@0.7.0: + chardet@0.7.0: resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} - /check-types@11.2.2: - resolution: {integrity: sha512-HBiYvXvn9Z70Z88XKjz3AEKd4HJhBXsa3j7xFnITAzoS8+q6eIGi8qDB8FKPBAjtuxjI/zFpwuiCb8oDtKOYrA==} - dev: false + chart.js@4.4.6: + resolution: {integrity: sha512-8Y406zevUPbbIBA/HRk33khEmQPk5+cxeflWE/2rx1NJsjVWMPw/9mSP9rxHP5eqi6LNoPBVMfZHxbwLSgldYA==} + engines: {pnpm: '>=8'} - /cheerio-select@2.1.0: + check-types@11.2.3: + resolution: {integrity: sha512-+67P1GkJRaxQD6PKK0Et9DhwQB+vGg3PM5+aavopCpZT1lj9jeqfvpgTLAWErNj8qApkkmXlu/Ug74kmhagkXg==} + + cheerio-select@2.1.0: resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==} - dependencies: - boolbase: 1.0.0 - css-select: 5.1.0 - css-what: 6.1.0 - domelementtype: 2.3.0 - domhandler: 5.0.3 - domutils: 3.0.1 - dev: false - /cheerio@1.0.0-rc.12: + cheerio@1.0.0: + resolution: {integrity: sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww==} + engines: {node: '>=18.17'} + + cheerio@1.0.0-rc.12: resolution: {integrity: sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==} engines: {node: '>= 6'} - dependencies: - cheerio-select: 2.1.0 - dom-serializer: 2.0.0 - domhandler: 5.0.3 - domutils: 3.0.1 - htmlparser2: 8.0.2 - parse5: 7.1.2 - parse5-htmlparser2-tree-adapter: 7.0.0 - dev: false - /chokidar@3.5.3: - resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} - dependencies: - anymatch: 3.1.3 - braces: 3.0.2 - glob-parent: 5.1.2 - is-binary-path: 2.1.0 - is-glob: 4.0.3 - normalize-path: 3.0.0 - readdirp: 3.6.0 - optionalDependencies: - fsevents: 2.3.2 - /chownr@1.1.4: + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} + + chownr@1.1.4: resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} - /chownr@2.0.0: + chownr@2.0.0: resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} engines: {node: '>=10'} - /chrome-trace-event@1.0.3: - resolution: {integrity: sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==} + chownr@3.0.0: + resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} + engines: {node: '>=18'} + + chrome-trace-event@1.0.4: + resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==} engines: {node: '>=6.0'} - /ci-info@3.8.0: - resolution: {integrity: sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==} + ci-info@3.9.0: + resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} engines: {node: '>=8'} - /cjs-module-lexer@1.2.2: - resolution: {integrity: sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==} + circ-json@1.0.4: + resolution: {integrity: sha512-/2NljkKlOCvNgs8jsIlJcCFhUfF6C1JJKM8PdJQ6HqIrqhJBouMcWFdeDJLNLZAadeTj0K9FMziH74hpEtAr1g==} - /classnames@2.3.2: - resolution: {integrity: sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==} + citty@0.1.6: + resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==} - /clean-css@4.2.4: - resolution: {integrity: sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A==} - engines: {node: '>= 4.0'} - dependencies: - source-map: 0.6.1 - dev: false + cjs-module-lexer@1.4.1: + resolution: {integrity: sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA==} + + classnames@2.5.1: + resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} - /clean-css@5.3.2: - resolution: {integrity: sha512-JVJbM+f3d3Q704rF4bqQ5UUyTtuJ0JRKNbTKVEeujCCBoMdkEi+V+e8oktO9qGQNSvHrFTM6JZRXrUvGR1czww==} + clean-css@5.3.3: + resolution: {integrity: sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==} engines: {node: '>= 10.0'} - dependencies: - source-map: 0.6.1 - dev: false - /clean-stack@2.2.0: + clean-stack@2.2.0: resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} engines: {node: '>=6'} - /clean-webpack-plugin@4.0.0(webpack@5.78.0): + clean-webpack-plugin@4.0.0: resolution: {integrity: sha512-WuWE1nyTNAyW5T7oNyys2EN0cfP2fdRxhxnIQWiAp0bMabPdHhoGxM8A6YL2GhqwgrPnnaemVE7nv5XJ2Fhh2w==} engines: {node: '>=10.0.0'} peerDependencies: webpack: '>=4.0.0 <6.0.0' - dependencies: - del: 4.1.1 - webpack: 5.78.0(esbuild@0.16.3)(webpack-cli@5.0.1) - dev: true - /cli-cursor@3.1.0: + cli-boxes@3.0.0: + resolution: {integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==} + engines: {node: '>=10'} + + cli-cursor@3.1.0: resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} engines: {node: '>=8'} - dependencies: - restore-cursor: 3.1.0 - /cli-spinners@2.8.0: - resolution: {integrity: sha512-/eG5sJcvEIwxcdYM86k5tPwn0MUzkX5YY3eImTGpJOZgVe4SdTMY14vQpcxgBzJ0wXwAYrS8E+c3uHeK4JNyzQ==} + cli-cursor@5.0.0: + resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} + engines: {node: '>=18'} + + cli-spinners@2.9.2: + resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} engines: {node: '>=6'} - /cli-table@0.3.11: + cli-table@0.3.11: resolution: {integrity: sha512-IqLQi4lO0nIB4tcdTpN4LCB9FI3uqrJZK7RC515EnhZ6qBaglkIgICb1wjeAqpdoOabm1+SuQtkXIPdYC93jhQ==} engines: {node: '>= 0.2.0'} - dependencies: - colors: 1.0.3 - /cli-width@3.0.0: + cli-width@3.0.0: resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==} engines: {node: '>= 10'} - /client-only@0.0.1: + cli-width@4.1.0: + resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} + engines: {node: '>= 12'} + + client-only@0.0.1: resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} - dev: false - /cliui@7.0.4: + cliui@7.0.4: resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 7.0.0 - /cliui@8.0.1: + cliui@8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 7.0.0 - dev: true - /clone-buffer@1.0.0: + clone-buffer@1.0.0: resolution: {integrity: sha512-KLLTJWrvwIP+OPfMn0x2PheDEP20RPUcGXj/ERegTgdmPEZylALQldygiqrPPu8P45uNuPs7ckmReLY6v/iA5g==} engines: {node: '>= 0.10'} - /clone-deep@4.0.1: + clone-deep@4.0.1: resolution: {integrity: sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==} engines: {node: '>=6'} - dependencies: - is-plain-object: 2.0.4 - kind-of: 6.0.3 - shallow-clone: 3.0.1 - - /clone-response@1.0.3: - resolution: {integrity: sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==} - dependencies: - mimic-response: 1.0.1 - dev: true - /clone-stats@1.0.0: + clone-stats@1.0.0: resolution: {integrity: sha512-au6ydSpg6nsrigcZ4m8Bc9hxjeW+GJ8xh5G3BJCMt4WXe1H10UNaVOamqQTmrx1kjVuxAHIQSNU6hY4Nsn9/ag==} - /clone@1.0.4: + clone@1.0.4: resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} engines: {node: '>=0.8'} - /clone@2.1.2: + clone@2.1.2: resolution: {integrity: sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==} engines: {node: '>=0.8'} - /cloneable-readable@1.1.3: + cloneable-readable@1.1.3: resolution: {integrity: sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==} - dependencies: - inherits: 2.0.4 - process-nextick-args: 2.0.1 - readable-stream: 2.3.8 - /cluster-key-slot@1.1.2: + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + cluster-key-slot@1.1.2: resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==} engines: {node: '>=0.10.0'} - /cmd-shim@5.0.0: + cmd-shim@5.0.0: resolution: {integrity: sha512-qkCtZ59BidfEwHltnJwkyVZn+XQojdAySM1D1gSeh11Z4pW1Kpolkyo53L5noc0nrxmIvyFwTmJRo4xs7FFLPw==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - dependencies: - mkdirp-infer-owner: 2.0.0 - /co@4.6.0: + co@4.6.0: resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} - /coa@2.0.2: + coa@2.0.2: resolution: {integrity: sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==} engines: {node: '>= 4.0'} - dependencies: - '@types/q': 1.5.5 - chalk: 2.4.2 - q: 1.5.1 - /code-block-writer@10.1.1: - resolution: {integrity: sha512-67ueh2IRGst/51p0n6FvPrnRjAGHY5F8xdjkgrYE7DDzpJe6qA07RYQ9VcoUeo5ATOjSOiWpSL3SWBRRbempMw==} - dev: true - - /code-block-writer@11.0.3: + code-block-writer@11.0.3: resolution: {integrity: sha512-NiujjUFB4SwScJq2bwbYUtXbZhBSlY6vYzm++3Q6oC+U+injTqfPYFK8wS9COOmb2lueqp0ZRB4nK1VYeHgNyw==} - dev: true - - /code-point-at@1.1.0: - resolution: {integrity: sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==} - engines: {node: '>=0.10.0'} - dev: false - /collect-v8-coverage@1.0.1: - resolution: {integrity: sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==} + collect-v8-coverage@1.0.2: + resolution: {integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==} - /color-convert@1.9.3: + color-convert@1.9.3: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} - dependencies: - color-name: 1.1.3 - /color-convert@2.0.1: + color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} - dependencies: - color-name: 1.1.4 - /color-name@1.1.3: + color-name@1.1.3: resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} - /color-name@1.1.4: + color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - /color-string@1.9.1: + color-string@1.9.1: resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} - dependencies: - color-name: 1.1.4 - simple-swizzle: 0.2.2 - /color-support@1.1.3: + color-support@1.1.3: resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} hasBin: true - /color@3.2.1: + color@3.2.1: resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==} - dependencies: - color-convert: 1.9.3 - color-string: 1.9.1 - /colord@2.9.3: + color@4.2.3: + resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} + engines: {node: '>=12.5.0'} + + colord@2.9.3: resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==} - dev: false - /colorette@2.0.19: - resolution: {integrity: sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==} + colorette@2.0.20: + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} - /colors@1.0.3: + colors@1.0.3: resolution: {integrity: sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw==} engines: {node: '>=0.1.90'} - /combined-stream@1.0.8: + combined-stream@1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} - dependencies: - delayed-stream: 1.0.0 - dev: false - /comma-separated-tokens@2.0.3: - resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} - dev: true + commander@10.0.1: + resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} + engines: {node: '>=14'} + + commander@11.1.0: + resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} + engines: {node: '>=16'} + + commander@12.1.0: + resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} + engines: {node: '>=18'} + + commander@13.1.0: + resolution: {integrity: sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==} + engines: {node: '>=18'} - /commander@2.20.3: + commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} - /commander@4.1.1: + commander@4.1.1: resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} engines: {node: '>= 6'} - /commander@6.2.1: - resolution: {integrity: sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==} - engines: {node: '>= 6'} - dev: false - - /commander@7.1.0: + commander@7.1.0: resolution: {integrity: sha512-pRxBna3MJe6HKnBGsDyMv8ETbptw3axEdYHoqNh7gu5oDcew8fs0xnivZGm06Ogk8zGAJ9VX+OPEr2GXEQK4dg==} engines: {node: '>= 10'} - /commander@7.2.0: + commander@7.2.0: resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} engines: {node: '>= 10'} - dev: false - /commander@8.3.0: + commander@8.3.0: resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} engines: {node: '>= 12'} - dev: false - /commander@9.5.0: - resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==} - engines: {node: ^12.20.0 || >=14} - - /common-ancestor-path@1.0.1: + common-ancestor-path@1.0.1: resolution: {integrity: sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==} - /common-path-prefix@3.0.0: + common-path-prefix@3.0.0: resolution: {integrity: sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==} - dev: false - /common-tags@1.8.2: + common-tags@1.8.2: resolution: {integrity: sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==} engines: {node: '>=4.0.0'} - dev: false - /commondir@1.0.1: + commondir@1.0.1: resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} - /compress-commons@4.1.1: - resolution: {integrity: sha512-QLdDLCKNV2dtoTorqgxngQCMA+gWXkM/Nwu7FpeBhk/RdkzimqC3jueb/FDmaZeXh+uby1jkBqE3xArsLBE5wQ==} + compress-commons@4.1.2: + resolution: {integrity: sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==} engines: {node: '>= 10'} - dependencies: - buffer-crc32: 0.2.13 - crc32-stream: 4.0.2 - normalize-path: 3.0.0 - readable-stream: 3.6.2 - dev: true - /compressible@2.0.18: + compressible@2.0.18: resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==} engines: {node: '>= 0.6'} - dependencies: - mime-db: 1.52.0 - dev: false - /compression@1.7.4: + compression@1.7.4: resolution: {integrity: sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==} engines: {node: '>= 0.8.0'} - dependencies: - accepts: 1.3.8 - bytes: 3.0.0 - compressible: 2.0.18 - debug: 2.6.9 - on-headers: 1.0.2 - safe-buffer: 5.1.2 - vary: 1.1.2 - transitivePeerDependencies: - - supports-color - dev: false - /compute-gcd@1.2.1: + compute-gcd@1.2.1: resolution: {integrity: sha512-TwMbxBNz0l71+8Sc4czv13h4kEqnchV9igQZBi6QUaz09dnz13juGnnaWWJTRsP3brxOoxeB4SA2WELLw1hCtg==} - dependencies: - validate.io-array: 1.0.6 - validate.io-function: 1.0.2 - validate.io-integer-array: 1.0.0 - dev: false - /compute-lcm@1.1.2: + compute-lcm@1.1.2: resolution: {integrity: sha512-OFNPdQAXnQhDSKioX8/XYT6sdUlXwpeMjfd6ApxMJfyZ4GxmLR1xvMERctlYhlHwIiz6CSpBc2+qYKjHGZw4TQ==} - dependencies: - compute-gcd: 1.2.1 - validate.io-array: 1.0.6 - validate.io-function: 1.0.2 - validate.io-integer-array: 1.0.0 - dev: false - /compute-scroll-into-view@3.0.3: - resolution: {integrity: sha512-nadqwNxghAGTamwIqQSG433W6OADZx2vCo3UXHNrzTRHK/htu+7+L0zhjEoaeaQVNAi3YgqWDv8+tzf0hRfR+A==} + compute-scroll-into-view@3.1.0: + resolution: {integrity: sha512-rj8l8pD4bJ1nx+dAkMhV1xB5RuZEyVysfxJqB1pRchh1KVvwOv9b7CGB8ZfjTImVv2oF+sYMUkMZq6Na5Ftmbg==} - /concat-map@0.0.1: + concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - /concat-with-sourcemaps@1.1.0: + concat-with-sourcemaps@1.1.0: resolution: {integrity: sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg==} - dependencies: - source-map: 0.6.1 - dev: true - /config-chain@1.1.13: + confbox@0.2.2: + resolution: {integrity: sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==} + + config-chain@1.1.13: resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} - dependencies: - ini: 1.3.8 - proto-list: 1.2.4 - dev: false - /confusing-browser-globals@1.1.0-next.14: - resolution: {integrity: sha512-shpUugMlZFU47qCZ4BuoAoAlUKOjiO7Nhn1aVFx1RADeAbKdgJ1iFc18b9KPY5GRr4EnU22boZrMSlo/NfQzwQ==} - dev: false + confusing-browser-globals@1.0.11: + resolution: {integrity: sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==} - /connect-history-api-fallback@2.0.0: + connect-history-api-fallback@2.0.0: resolution: {integrity: sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==} engines: {node: '>=0.8'} - dev: false - /console-control-strings@1.1.0: + consola@3.4.2: + resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} + engines: {node: ^14.18.0 || >=16.10.0} + + console-control-strings@1.1.0: resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} - /content-disposition@0.5.4: + content-disposition@0.5.4: resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} engines: {node: '>= 0.6'} - dependencies: - safe-buffer: 5.2.1 - /content-type@1.0.5: + content-type@1.0.5: resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} engines: {node: '>= 0.6'} - /convert-hrtime@3.0.0: - resolution: {integrity: sha512-7V+KqSvMiHp8yWDuwfww06XleMWVVB9b9tURBx+G7UTADuo5hYPuowKloz4OzOqbPezxgo+fdQ1522WzPG4OeA==} - engines: {node: '>=8'} - dev: true - - /convert-source-map@1.9.0: + convert-source-map@1.9.0: resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} - /convert-source-map@2.0.0: + convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - dev: true - /cookie-parser@1.4.6: - resolution: {integrity: sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==} + cookie-parser@1.4.7: + resolution: {integrity: sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==} engines: {node: '>= 0.8.0'} - dependencies: - cookie: 0.4.1 - cookie-signature: 1.0.6 - dev: true - /cookie-signature@1.0.6: + cookie-signature@1.0.6: resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} - /cookie@0.4.1: - resolution: {integrity: sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==} + cookie@0.7.1: + resolution: {integrity: sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==} engines: {node: '>= 0.6'} - dev: true - /cookie@0.4.2: - resolution: {integrity: sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==} + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} engines: {node: '>= 0.6'} - dev: true - /cookie@0.5.0: - resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} - engines: {node: '>= 0.6'} + cookie@1.0.1: + resolution: {integrity: sha512-Xd8lFX4LM9QEEwxQpF9J9NTUh8pmdJO0cyRJhFiDoLTk2eH8FXlRv2IFGYVadZpqI3j8fhNrSdKCeYPxiAhLXw==} + engines: {node: '>=18'} - /copy-anything@2.0.6: + copy-anything@2.0.6: resolution: {integrity: sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==} - dependencies: - is-what: 3.14.1 - dev: true - /copy-to-clipboard@3.3.3: + copy-to-clipboard@3.3.3: resolution: {integrity: sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==} - dependencies: - toggle-selection: 1.0.6 - /copy-webpack-plugin@11.0.0(webpack@5.78.0): - resolution: {integrity: sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==} - engines: {node: '>= 14.15.0'} + copy-webpack-plugin@12.0.2: + resolution: {integrity: sha512-SNwdBeHyII+rWvee/bTnAYyO8vfVdcSTud4EIb6jcZ8inLeWucJE0DnxXQBjlQ5zlteuuvooGQy3LIyGxhvlOA==} + engines: {node: '>= 18.12.0'} peerDependencies: webpack: ^5.1.0 - dependencies: - fast-glob: 3.2.12 - glob-parent: 6.0.2 - globby: 13.1.4 - normalize-path: 3.0.0 - schema-utils: 4.0.0 - serialize-javascript: 6.0.1 - webpack: 5.78.0(esbuild@0.16.3)(webpack-cli@5.0.1) - dev: true - /core-js-compat@3.30.1: - resolution: {integrity: sha512-d690npR7MC6P0gq4npTl5n2VQeNAmUrJ90n+MHiKS7W2+xno4o3F5GDEuylSdi6EJ3VssibSGXOa1r3YXD3Mhw==} - dependencies: - browserslist: 4.21.5 + core-js-compat@3.38.1: + resolution: {integrity: sha512-JRH6gfXxGmrzF3tZ57lFx97YARxCXPaMzPo6jELZhv88pBH5VXpQ+y0znKGlFnzuaihqhLbefxSJxWJMPtfDzw==} - /core-js-pure@3.30.0: - resolution: {integrity: sha512-+2KbMFGeBU0ln/csoPqTe0i/yfHbrd2EUhNMObsGtXMKS/RTtlkYyi+/3twLcevbgNR0yM/r0Psa3TEoQRpFMQ==} - requiresBuild: true - dev: false + core-js-pure@3.38.1: + resolution: {integrity: sha512-BY8Etc1FZqdw1glX0XNOq2FDwfrg/VGqoZOZCdaL+UmdaqDwQwYXkMJT4t6In+zfEfOJDcM9T0KdbBeJg8KKCQ==} - /core-js@2.6.12: + core-js@2.6.12: resolution: {integrity: sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==} deprecated: core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js. - requiresBuild: true - dev: false - /core-js@3.30.0: - resolution: {integrity: sha512-hQotSSARoNh1mYPi9O2YaWeiq/cEB95kOrFb4NCrO4RIFt1qqNpKsaE+vy/L3oiqvND5cThqXzUU3r9F7Efztg==} - requiresBuild: true - dev: false + core-js@3.38.1: + resolution: {integrity: sha512-OP35aUorbU3Zvlx7pjsFdu1rGNnD4pgw/CWoYzRY3t2EzoVT7shKHY1dlAy3f41cGIO7ZDPQimhGFTlEYkG/Hw==} + + core-util-is@1.0.2: + resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==} - /core-util-is@1.0.3: + core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} - /cosmiconfig@5.2.1: - resolution: {integrity: sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==} - engines: {node: '>=4'} - dependencies: - import-fresh: 2.0.0 - is-directory: 0.3.1 - js-yaml: 3.14.1 - parse-json: 4.0.0 - dev: true + cors@2.8.5: + resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} + engines: {node: '>= 0.10'} - /cosmiconfig@6.0.0: + cosmiconfig@6.0.0: resolution: {integrity: sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==} engines: {node: '>=8'} - dependencies: - '@types/parse-json': 4.0.0 - import-fresh: 3.3.0 - parse-json: 5.2.0 - path-type: 4.0.0 - yaml: 1.10.2 - /cosmiconfig@7.1.0: + cosmiconfig@7.1.0: resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} engines: {node: '>=10'} - dependencies: - '@types/parse-json': 4.0.0 - import-fresh: 3.3.0 - parse-json: 5.2.0 - path-type: 4.0.0 - yaml: 1.10.2 - /cpu-features@0.0.6: - resolution: {integrity: sha512-Rj33pk//oVNh25YjsBaKtOkXNW7IARYxejWJosJkXqVPpbK+FrdpThPk6f4m3d+CEh2qMlkGx/SFt2Y1XSWN6g==} + cosmiconfig@9.0.0: + resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + + cpu-features@0.0.10: + resolution: {integrity: sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==} engines: {node: '>=10.0.0'} - requiresBuild: true - dependencies: - buildcheck: 0.0.5 - nan: 2.17.0 - dev: true - optional: true - /crc-32@1.2.2: + crc-32@1.2.2: resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} engines: {node: '>=0.8'} hasBin: true - dev: true - /crc32-stream@4.0.2: - resolution: {integrity: sha512-DxFZ/Hk473b/muq1VJ///PMNLj0ZMnzye9thBpmjpJKCc5eMgB95aK8zCGrGfQ90cWo561Te6HK9D+j4KPdM6w==} + crc32-stream@4.0.3: + resolution: {integrity: sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==} engines: {node: '>= 10'} - dependencies: - crc-32: 1.2.2 - readable-stream: 3.6.2 - dev: true - /create-require@1.1.1: + create-jest@29.7.0: + resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + + create-require@1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} - /cross-fetch@3.1.5: - resolution: {integrity: sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==} - dependencies: - node-fetch: 2.6.7 - transitivePeerDependencies: - - encoding - dev: false + cross-fetch@3.1.8: + resolution: {integrity: sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==} + + cross-fetch@4.0.0: + resolution: {integrity: sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==} - /cross-spawn@7.0.3: - resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} - dependencies: - path-key: 3.1.1 - shebang-command: 2.0.0 - which: 2.0.2 - /crypto-js@4.1.1: - resolution: {integrity: sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==} - dev: false + crypto-js@4.2.0: + resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==} - /crypto-random-string@2.0.0: + crypto-random-string@2.0.0: resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==} engines: {node: '>=8'} - dev: false - /css-blank-pseudo@3.0.3(postcss@8.4.21): + css-blank-pseudo@3.0.3: resolution: {integrity: sha512-VS90XWtsHGqoM0t4KpH053c4ehxZ2E6HtGI7x68YFV0pTo/QmkV/YFA+NnlvK8guxZVNWGQhVNJGC39Q8XF4OQ==} engines: {node: ^12 || ^14 || >=16} hasBin: true peerDependencies: - postcss: ^8.4 - dependencies: - postcss: 8.4.21 - postcss-selector-parser: 6.0.11 - dev: false - - /css-color-names@0.0.4: - resolution: {integrity: sha512-zj5D7X1U2h2zsXOAM8EyUREBnnts6H+Jm+d1M2DbiQQcUtnqgQsMrdo8JW9R80YFUmIdBZeMu5wvYM7hcgWP/Q==} - dev: true - - /css-declaration-sorter@4.0.1: - resolution: {integrity: sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA==} - engines: {node: '>4'} - dependencies: - postcss: 7.0.39 - timsort: 0.3.0 - dev: true + postcss: ^8.4.47 - /css-declaration-sorter@6.4.0(postcss@8.4.21): - resolution: {integrity: sha512-jDfsatwWMWN0MODAFuHszfjphEXfNw9JUAhmY4pLu3TyTU+ohUpsbVtbU+1MZn4a47D9kqh03i4eyOm+74+zew==} + css-declaration-sorter@6.4.1: + resolution: {integrity: sha512-rtdthzxKuyq6IzqX6jEcIzQF/YqccluefyCYheovBOLhFT/drQA9zj/UbRAa9J7C0o6EG6u3E6g+vKkay7/k3g==} engines: {node: ^10 || ^12 || >=14} peerDependencies: - postcss: ^8.0.9 - dependencies: - postcss: 8.4.21 - dev: false + postcss: ^8.4.47 + + css-declaration-sorter@7.2.0: + resolution: {integrity: sha512-h70rUM+3PNFuaBDTLe8wF/cdWu+dOZmb7pJt8Z2sedYbAcQVQV/tEchueg3GWxwqS0cxtbxmaHEdkNACqcvsow==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4.47 - /css-has-pseudo@3.0.4(postcss@8.4.21): + css-has-pseudo@3.0.4: resolution: {integrity: sha512-Vse0xpR1K9MNlp2j5w1pgWIJtm1a8qS0JwS9goFYcImjlHEmywP9VUF05aGBXzGpDJF86QXk4L0ypBmwPhGArw==} engines: {node: ^12 || ^14 || >=16} hasBin: true peerDependencies: - postcss: ^8.4 - dependencies: - postcss: 8.4.21 - postcss-selector-parser: 6.0.11 - dev: false + postcss: ^8.4.47 - /css-in-js-utils@3.1.0: + css-in-js-utils@3.1.0: resolution: {integrity: sha512-fJAcud6B3rRu+KHYk+Bwf+WFL2MDCJJ1XG9x137tJQ0xYxor7XziQtuGFbWNdqrvF4Tk26O3H73nfVqXt/fW1A==} - dependencies: - hyphenate-style-name: 1.0.4 - dev: false - /css-loader@6.7.3(webpack@5.78.0): - resolution: {integrity: sha512-qhOH1KlBMnZP8FzRO6YCH9UHXQhVMcEGLyNdb7Hv2cpcmJbW0YrddO+tG1ab5nT41KpHIYGsbeHqxB9xPu1pKQ==} + css-loader@6.11.0: + resolution: {integrity: sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g==} engines: {node: '>= 12.13.0'} peerDependencies: + '@rspack/core': 0.x || 1.x webpack: ^5.0.0 - dependencies: - icss-utils: 5.1.0(postcss@8.4.21) - postcss: 8.4.21 - postcss-modules-extract-imports: 3.0.0(postcss@8.4.21) - postcss-modules-local-by-default: 4.0.0(postcss@8.4.21) - postcss-modules-scope: 3.0.0(postcss@8.4.21) - postcss-modules-values: 4.0.0(postcss@8.4.21) - postcss-value-parser: 4.2.0 - semver: 7.5.0 - webpack: 5.78.0(esbuild@0.16.3)(webpack-cli@5.0.1) - dev: false + peerDependenciesMeta: + '@rspack/core': + optional: true + webpack: + optional: true - /css-minimizer-webpack-plugin@3.4.1(esbuild@0.16.3)(webpack@5.78.0): + css-minimizer-webpack-plugin@3.4.1: resolution: {integrity: sha512-1u6D71zeIfgngN2XNRJefc/hY7Ybsxd74Jm4qngIXyUEk7fss3VUzuHxLAq/R8NAba4QU9OUSaMZlbpRc7bM4Q==} engines: {node: '>= 12.13.0'} peerDependencies: @@ -10992,1516 +7250,787 @@ packages: optional: true esbuild: optional: true - dependencies: - cssnano: 5.1.15(postcss@8.4.21) - esbuild: 0.16.3 - jest-worker: 27.5.1 - postcss: 8.4.21 - schema-utils: 4.0.0 - serialize-javascript: 6.0.1 - source-map: 0.6.1 - webpack: 5.78.0(esbuild@0.16.3)(webpack-cli@5.0.1) - dev: false - /css-modules-loader-core@1.1.0: - resolution: {integrity: sha512-XWOBwgy5nwBn76aA+6ybUGL/3JBnCtBX9Ay9/OWIpzKYWlVHMazvJ+WtHumfi+xxdPF440cWK7JCYtt8xDifew==} - dependencies: - icss-replace-symbols: 1.1.0 - postcss: 6.0.1 - postcss-modules-extract-imports: 1.1.0 - postcss-modules-local-by-default: 1.2.0 - postcss-modules-scope: 1.1.0 - postcss-modules-values: 1.3.0 - dev: true - - /css-prefers-color-scheme@6.0.3(postcss@8.4.21): + css-prefers-color-scheme@6.0.3: resolution: {integrity: sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==} engines: {node: ^12 || ^14 || >=16} hasBin: true peerDependencies: - postcss: ^8.4 - dependencies: - postcss: 8.4.21 - dev: false + postcss: ^8.4.47 - /css-select-base-adapter@0.1.1: + css-select-base-adapter@0.1.1: resolution: {integrity: sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==} - /css-select@2.1.0: + css-select@2.1.0: resolution: {integrity: sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==} - dependencies: - boolbase: 1.0.0 - css-what: 3.4.2 - domutils: 1.7.0 - nth-check: 1.0.2 - /css-select@4.3.0: + css-select@4.3.0: resolution: {integrity: sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==} - dependencies: - boolbase: 1.0.0 - css-what: 6.1.0 - domhandler: 4.3.1 - domutils: 2.8.0 - nth-check: 2.1.1 - dev: false - /css-select@5.1.0: + css-select@5.1.0: resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==} - dependencies: - boolbase: 1.0.0 - css-what: 6.1.0 - domhandler: 5.0.3 - domutils: 3.0.1 - nth-check: 2.1.1 - dev: false - - /css-selector-tokenizer@0.7.3: - resolution: {integrity: sha512-jWQv3oCEL5kMErj4wRnK/OPoBi0D+P1FR2cDCKYPaMeD2eW3/mttav8HT4hT1CKopiJI/psEULjkClhvJo4Lvg==} - dependencies: - cssesc: 3.0.0 - fastparse: 1.1.2 - dev: true - /css-tree@1.0.0-alpha.37: + css-tree@1.0.0-alpha.37: resolution: {integrity: sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==} engines: {node: '>=8.0.0'} - dependencies: - mdn-data: 2.0.4 - source-map: 0.6.1 - /css-tree@1.1.3: + css-tree@1.1.3: resolution: {integrity: sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==} engines: {node: '>=8.0.0'} - dependencies: - mdn-data: 2.0.14 - source-map: 0.6.1 - /css-what@3.4.2: - resolution: {integrity: sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==} - engines: {node: '>= 6'} + css-tree@2.2.1: + resolution: {integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} + + css-tree@2.3.1: + resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} - /css-what@5.1.0: - resolution: {integrity: sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw==} + css-what@3.4.2: + resolution: {integrity: sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==} engines: {node: '>= 6'} - dev: true - /css-what@6.1.0: + css-what@6.1.0: resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} engines: {node: '>= 6'} - dev: false - /css.escape@1.5.1: + css.escape@1.5.1: resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} - dev: true - /cssdb@7.5.4: - resolution: {integrity: sha512-fGD+J6Jlq+aurfE1VDXlLS4Pt0VtNlu2+YgfGOdMxRyl/HQ9bDiHTwSck1Yz8A97Dt/82izSK6Bp/4nVqacOsg==} - dev: false + cssdb@7.11.2: + resolution: {integrity: sha512-lhQ32TFkc1X4eTefGfYPvgovRSzIMofHkigfH8nWtyRL4XJLsRhJFreRvEgKzept7x1rjBuy3J/MurXLaFxW/A==} - /cssesc@3.0.0: + cssesc@3.0.0: resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} engines: {node: '>=4'} hasBin: true - /cssnano-preset-default@4.0.8: - resolution: {integrity: sha512-LdAyHuq+VRyeVREFmuxUZR1TXjQm8QQU/ktoo/x7bz+SdOge1YKc5eMN6pRW7YWBmyq59CqYba1dJ5cUukEjLQ==} - engines: {node: '>=6.9.0'} - dependencies: - css-declaration-sorter: 4.0.1 - cssnano-util-raw-cache: 4.0.1 - postcss: 7.0.39 - postcss-calc: 7.0.5 - postcss-colormin: 4.0.3 - postcss-convert-values: 4.0.1 - postcss-discard-comments: 4.0.2 - postcss-discard-duplicates: 4.0.2 - postcss-discard-empty: 4.0.1 - postcss-discard-overridden: 4.0.1 - postcss-merge-longhand: 4.0.11 - postcss-merge-rules: 4.0.3 - postcss-minify-font-values: 4.0.2 - postcss-minify-gradients: 4.0.2 - postcss-minify-params: 4.0.2 - postcss-minify-selectors: 4.0.2 - postcss-normalize-charset: 4.0.1 - postcss-normalize-display-values: 4.0.2 - postcss-normalize-positions: 4.0.2 - postcss-normalize-repeat-style: 4.0.2 - postcss-normalize-string: 4.0.2 - postcss-normalize-timing-functions: 4.0.2 - postcss-normalize-unicode: 4.0.1 - postcss-normalize-url: 4.0.1 - postcss-normalize-whitespace: 4.0.2 - postcss-ordered-values: 4.1.2 - postcss-reduce-initial: 4.0.3 - postcss-reduce-transforms: 4.0.2 - postcss-svgo: 4.0.3 - postcss-unique-selectors: 4.0.1 - dev: true - - /cssnano-preset-default@5.2.14(postcss@8.4.21): + cssnano-preset-default@5.2.14: resolution: {integrity: sha512-t0SFesj/ZV2OTylqQVOrFgEh5uanxbO6ZAdeCrNsUQ6fVuXwYTxJPNAGvGTxHbD68ldIJNec7PyYZDBrfDQ+6A==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: - postcss: ^8.2.15 - dependencies: - css-declaration-sorter: 6.4.0(postcss@8.4.21) - cssnano-utils: 3.1.0(postcss@8.4.21) - postcss: 8.4.21 - postcss-calc: 8.2.4(postcss@8.4.21) - postcss-colormin: 5.3.1(postcss@8.4.21) - postcss-convert-values: 5.1.3(postcss@8.4.21) - postcss-discard-comments: 5.1.2(postcss@8.4.21) - postcss-discard-duplicates: 5.1.0(postcss@8.4.21) - postcss-discard-empty: 5.1.1(postcss@8.4.21) - postcss-discard-overridden: 5.1.0(postcss@8.4.21) - postcss-merge-longhand: 5.1.7(postcss@8.4.21) - postcss-merge-rules: 5.1.4(postcss@8.4.21) - postcss-minify-font-values: 5.1.0(postcss@8.4.21) - postcss-minify-gradients: 5.1.1(postcss@8.4.21) - postcss-minify-params: 5.1.4(postcss@8.4.21) - postcss-minify-selectors: 5.2.1(postcss@8.4.21) - postcss-normalize-charset: 5.1.0(postcss@8.4.21) - postcss-normalize-display-values: 5.1.0(postcss@8.4.21) - postcss-normalize-positions: 5.1.1(postcss@8.4.21) - postcss-normalize-repeat-style: 5.1.1(postcss@8.4.21) - postcss-normalize-string: 5.1.0(postcss@8.4.21) - postcss-normalize-timing-functions: 5.1.0(postcss@8.4.21) - postcss-normalize-unicode: 5.1.1(postcss@8.4.21) - postcss-normalize-url: 5.1.0(postcss@8.4.21) - postcss-normalize-whitespace: 5.1.1(postcss@8.4.21) - postcss-ordered-values: 5.1.3(postcss@8.4.21) - postcss-reduce-initial: 5.1.2(postcss@8.4.21) - postcss-reduce-transforms: 5.1.0(postcss@8.4.21) - postcss-svgo: 5.1.0(postcss@8.4.21) - postcss-unique-selectors: 5.1.1(postcss@8.4.21) - dev: false - - /cssnano-util-get-arguments@4.0.0: - resolution: {integrity: sha512-6RIcwmV3/cBMG8Aj5gucQRsJb4vv4I4rn6YjPbVWd5+Pn/fuG+YseGvXGk00XLkoZkaj31QOD7vMUpNPC4FIuw==} - engines: {node: '>=6.9.0'} - dev: true - - /cssnano-util-get-match@4.0.0: - resolution: {integrity: sha512-JPMZ1TSMRUPVIqEalIBNoBtAYbi8okvcFns4O0YIhcdGebeYZK7dMyHJiQ6GqNBA9kE0Hym4Aqym5rPdsV/4Cw==} - engines: {node: '>=6.9.0'} - dev: true + postcss: ^8.4.47 - /cssnano-util-raw-cache@4.0.1: - resolution: {integrity: sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA==} - engines: {node: '>=6.9.0'} - dependencies: - postcss: 7.0.39 - dev: true - - /cssnano-util-same-parent@4.0.1: - resolution: {integrity: sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q==} - engines: {node: '>=6.9.0'} - dev: true + cssnano-preset-default@7.0.6: + resolution: {integrity: sha512-ZzrgYupYxEvdGGuqL+JKOY70s7+saoNlHSCK/OGn1vB2pQK8KSET8jvenzItcY+kA7NoWvfbb/YhlzuzNKjOhQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.47 - /cssnano-utils@3.1.0(postcss@8.4.21): + cssnano-utils@3.1.0: resolution: {integrity: sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: - postcss: ^8.2.15 - dependencies: - postcss: 8.4.21 - dev: false + postcss: ^8.4.47 - /cssnano@4.1.11: - resolution: {integrity: sha512-6gZm2htn7xIPJOHY824ERgj8cNPgPxyCSnkXc4v7YvNW+TdVfzgngHcEhy/8D11kUWRUMbke+tC+AUcUsnMz2g==} - engines: {node: '>=6.9.0'} - dependencies: - cosmiconfig: 5.2.1 - cssnano-preset-default: 4.0.8 - is-resolvable: 1.1.0 - postcss: 7.0.39 - dev: true + cssnano-utils@5.0.0: + resolution: {integrity: sha512-Uij0Xdxc24L6SirFr25MlwC2rCFX6scyUmuKpzI+JQ7cyqDEwD42fJ0xfB3yLfOnRDU5LKGgjQ9FA6LYh76GWQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.47 - /cssnano@5.1.15(postcss@8.4.21): + cssnano@5.1.15: resolution: {integrity: sha512-j+BKgDcLDQA+eDifLx0EO4XSA56b7uut3BQFH+wbSaSTuGLuiyTa/wbRYthUXX8LC9mLg+WWKe8h+qJuwTAbHw==} engines: {node: ^10 || ^12 || >=14.0} peerDependencies: - postcss: ^8.2.15 - dependencies: - cssnano-preset-default: 5.2.14(postcss@8.4.21) - lilconfig: 2.1.0 - postcss: 8.4.21 - yaml: 1.10.2 - dev: false + postcss: ^8.4.47 - /csso@4.2.0: + cssnano@7.0.6: + resolution: {integrity: sha512-54woqx8SCbp8HwvNZYn68ZFAepuouZW4lTwiMVnBErM3VkO7/Sd4oTOt3Zz3bPx3kxQ36aISppyXj2Md4lg8bw==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.47 + + csso@4.2.0: resolution: {integrity: sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==} engines: {node: '>=8.0.0'} - dependencies: - css-tree: 1.1.3 - /cssom@0.3.8: + csso@5.0.5: + resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} + + cssom@0.3.8: resolution: {integrity: sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==} - dev: false - /cssom@0.4.4: + cssom@0.4.4: resolution: {integrity: sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==} - dev: false - /cssstyle@2.3.0: + cssstyle@2.3.0: resolution: {integrity: sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==} engines: {node: '>=8'} - dependencies: - cssom: 0.3.8 - dev: false - /csstype@3.1.2: - resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==} + csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} - /cuid@2.1.8: + cuid@2.1.8: resolution: {integrity: sha512-xiEMER6E7TlTPnDxrM4eRiC6TRgjNX9xzEZ5U/Se2YJKr7Mq4pJn/2XEHjl3STcSh96GmkHPcBXLES8M29wyyg==} deprecated: Cuid and other k-sortable and non-cryptographic ids (Ulid, ObjectId, KSUID, all UUIDs) are all insecure. Use @paralleldrive/cuid2 instead. - dev: false - /damerau-levenshtein@1.0.8: + d3-array@3.2.4: + resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} + engines: {node: '>=12'} + + d3-color@3.1.0: + resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} + engines: {node: '>=12'} + + d3-ease@3.0.1: + resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==} + engines: {node: '>=12'} + + d3-format@3.1.0: + resolution: {integrity: sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==} + engines: {node: '>=12'} + + d3-interpolate@3.0.1: + resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} + engines: {node: '>=12'} + + d3-path@3.1.0: + resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==} + engines: {node: '>=12'} + + d3-scale@4.0.2: + resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==} + engines: {node: '>=12'} + + d3-shape@3.2.0: + resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==} + engines: {node: '>=12'} + + d3-time-format@4.1.0: + resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==} + engines: {node: '>=12'} + + d3-time@3.1.0: + resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==} + engines: {node: '>=12'} + + d3-timer@3.0.1: + resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} + engines: {node: '>=12'} + + damerau-levenshtein@1.0.8: resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} - /dargs@7.0.0: + dargs@7.0.0: resolution: {integrity: sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==} engines: {node: '>=8'} - /data-uri-to-buffer@3.0.1: - resolution: {integrity: sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==} - engines: {node: '>= 6'} - dev: true + data-uri-to-buffer@4.0.1: + resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} + engines: {node: '>= 12'} - /data-urls@2.0.0: + data-urls@2.0.0: resolution: {integrity: sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==} engines: {node: '>=10'} - dependencies: - abab: 2.0.6 - whatwg-mimetype: 2.3.0 - whatwg-url: 8.7.0 - dev: false - /date-fns@2.29.3: - resolution: {integrity: sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==} + data-view-buffer@1.0.1: + resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==} + engines: {node: '>= 0.4'} + + data-view-byte-length@1.0.1: + resolution: {integrity: sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==} + engines: {node: '>= 0.4'} + + data-view-byte-offset@1.0.0: + resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==} + engines: {node: '>= 0.4'} + + date-fns@2.30.0: + resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} engines: {node: '>=0.11'} - dev: false - /dateformat@4.6.3: + dateformat@4.6.3: resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==} - /dayjs@1.11.7: - resolution: {integrity: sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ==} + dayjs@1.11.13: + resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==} - /deasync@0.1.28: - resolution: {integrity: sha512-QqLF6inIDwiATrfROIyQtwOQxjZuek13WRYZ7donU5wJPLoP67MnYxA6QtqdvdBy2mMqv5m3UefBVdJjvevOYg==} - engines: {node: '>=0.11.0'} - requiresBuild: true - dependencies: - bindings: 1.5.0 - node-addon-api: 1.7.2 - dev: true - optional: true + debounce@1.2.1: + resolution: {integrity: sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==} - /debug@2.6.9: + debounce@2.0.0: + resolution: {integrity: sha512-xRetU6gL1VJbs85Mc4FoEGSjQxzpdxRyFhe3lmWFyy2EzydIcD4xzUvRJMD+NPDfMwKNhxa3PvsIOU32luIWeA==} + engines: {node: '>=18'} + + debug@2.6.9: resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} peerDependencies: supports-color: '*' peerDependenciesMeta: supports-color: optional: true - dependencies: - ms: 2.0.0 - /debug@3.2.7(supports-color@5.5.0): + debug@3.2.7: resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} peerDependencies: supports-color: '*' peerDependenciesMeta: supports-color: optional: true - dependencies: - ms: 2.1.3 - supports-color: 5.5.0 - /debug@4.3.4: - resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + debug@4.3.7: + resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} engines: {node: '>=6.0'} peerDependencies: supports-color: '*' peerDependenciesMeta: supports-color: optional: true - dependencies: - ms: 2.1.2 - /debuglog@1.0.1: + debuglog@1.0.1: resolution: {integrity: sha512-syBZ+rnAK3EgMsH2aYEOLUW7mZSY9Gb+0wUMCFsZvcmiz+HigA0LOcq/HoQqVuGG+EKykunc7QG2bzrponfaSw==} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. + + decimal.js-light@2.5.1: + resolution: {integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==} - /decimal.js@10.4.3: + decimal.js@10.4.3: resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} - dev: false - /declaration-bundler-webpack-plugin@1.0.3: + declaration-bundler-webpack-plugin@1.0.3: resolution: {integrity: sha512-bgeoSOZYTOOdiNUZd/U8K6Z+6IrM/X+DgUcm3/VI1l130lzOBeL+ObetjIkKksxcj0zUJbLaFRFumFGYDOQ9fg==} - dev: true - /decode-named-character-reference@1.0.2: - resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==} - dependencies: - character-entities: 2.0.2 - dev: true - - /decompress-response@4.2.1: - resolution: {integrity: sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==} - engines: {node: '>=8'} - dependencies: - mimic-response: 2.1.0 - dev: false - - /decompress-response@6.0.0: + decompress-response@6.0.0: resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} engines: {node: '>=10'} - dependencies: - mimic-response: 3.1.0 - dev: true - /dedent@0.7.0: + dedent@0.7.0: resolution: {integrity: sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==} - /deep-equal@2.2.0: - resolution: {integrity: sha512-RdpzE0Hv4lhowpIUKKMJfeH6C1pXdtT1/it80ubgWqwI3qpuxUBpC1S4hnHg+zjnuOoDkzUtUCEEkG+XG5l3Mw==} - dependencies: - call-bind: 1.0.2 - es-get-iterator: 1.1.3 - get-intrinsic: 1.2.0 - is-arguments: 1.1.1 - is-array-buffer: 3.0.2 - is-date-object: 1.0.5 - is-regex: 1.1.4 - is-shared-array-buffer: 1.0.2 - isarray: 2.0.5 - object-is: 1.1.5 - object-keys: 1.1.1 - object.assign: 4.1.4 - regexp.prototype.flags: 1.4.3 - side-channel: 1.0.4 - which-boxed-primitive: 1.0.2 - which-collection: 1.0.1 - which-typed-array: 1.1.9 + dedent@1.5.3: + resolution: {integrity: sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==} + peerDependencies: + babel-plugin-macros: ^3.1.0 + peerDependenciesMeta: + babel-plugin-macros: + optional: true + + deep-equal@2.2.3: + resolution: {integrity: sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==} + engines: {node: '>= 0.4'} - /deep-extend@0.6.0: + deep-extend@0.6.0: resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} engines: {node: '>=4.0.0'} - /deep-is@0.1.4: + deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} - /deep-object-diff@1.1.9: - resolution: {integrity: sha512-Rn+RuwkmkDwCi2/oXOFS9Gsr5lJZu/yTGpK7wAaAIE75CC+LCGEZHpY6VQJa/RoJcrmaA/docWJZvYohlNkWPA==} - dev: true - - /deepmerge@4.3.1: + deepmerge@4.3.1: resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} engines: {node: '>=0.10.0'} - /default-gateway@6.0.3: + default-gateway@6.0.3: resolution: {integrity: sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==} engines: {node: '>= 10'} - dependencies: - execa: 5.1.1 - dev: false - /defaults@1.0.4: + defaults@1.0.4: resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} - dependencies: - clone: 1.0.4 - /defer-to-connect@2.0.1: - resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} - engines: {node: '>=10'} - dev: true + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} - /define-lazy-prop@2.0.0: + define-lazy-prop@2.0.0: resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} engines: {node: '>=8'} - /define-properties@1.2.0: - resolution: {integrity: sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==} + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} - dependencies: - has-property-descriptors: 1.0.0 - object-keys: 1.1.1 - - /degenerator@3.0.3: - resolution: {integrity: sha512-FTq/qYMeBJACu1gHcXJvzsRBTK6aw5zWCYbEnIOyamOt5UJufWJRQ5XfDb6OuayfJWvmWAHgcZyt43vm/hbj7g==} - engines: {node: '>= 6'} - dependencies: - ast-types: 0.13.4 - escodegen: 1.14.3 - esprima: 4.0.1 - vm2: 3.9.17 - dev: true - /del@4.1.1: + del@4.1.1: resolution: {integrity: sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==} engines: {node: '>=6'} - dependencies: - '@types/glob': 7.2.0 - globby: 6.1.0 - is-path-cwd: 2.2.0 - is-path-in-cwd: 2.1.0 - p-map: 2.1.0 - pify: 4.0.1 - rimraf: 2.7.1 - dev: true - /delayed-stream@1.0.0: + delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} - dev: false - /delegates@1.0.0: + delegates@1.0.0: resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} - /denque@2.1.0: + denque@2.1.0: resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==} engines: {node: '>=0.10'} - /depd@1.1.2: + depd@1.1.2: resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==} engines: {node: '>= 0.6'} - dev: false - /depd@2.0.0: + depd@2.0.0: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} - /deprecation@2.3.1: + deprecation@2.3.1: resolution: {integrity: sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==} - /dequal@2.0.3: + dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} - dev: true - /destroy@1.2.0: + destroy@1.2.0: resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} - /detect-indent@6.1.0: - resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} + detect-libc@2.0.3: + resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} engines: {node: '>=8'} - dev: true - - /detect-libc@1.0.3: - resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==} - engines: {node: '>=0.10'} - hasBin: true - dev: false - /detect-libc@2.0.1: - resolution: {integrity: sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==} + detect-libc@2.0.4: + resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==} engines: {node: '>=8'} - dev: true - /detect-newline@3.1.0: + detect-newline@3.1.0: resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} engines: {node: '>=8'} - /detect-node@2.0.4: - resolution: {integrity: sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==} - dev: false + detect-node-es@1.1.0: + resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} - /detect-node@2.1.0: + detect-node@2.1.0: resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==} - dev: false - /detect-port-alt@1.1.6: + detect-port-alt@1.1.6: resolution: {integrity: sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==} engines: {node: '>= 4.2.1'} hasBin: true - dependencies: - address: 1.2.2 - debug: 2.6.9 - transitivePeerDependencies: - - supports-color - dev: false - /dezalgo@1.0.4: + dezalgo@1.0.4: resolution: {integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==} - dependencies: - asap: 2.0.6 - wrappy: 1.0.2 - /didyoumean@1.2.2: + didyoumean@1.2.2: resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} - /diff-sequences@27.5.1: + diff-match-patch@1.0.5: + resolution: {integrity: sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==} + + diff-sequences@27.5.1: resolution: {integrity: sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - dev: false - /diff-sequences@28.1.1: + diff-sequences@28.1.1: resolution: {integrity: sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dev: true - /diff-sequences@29.4.3: - resolution: {integrity: sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==} + diff-sequences@29.6.3: + resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dev: true - /diff@4.0.2: + diff@4.0.2: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} - /diff@5.1.0: - resolution: {integrity: sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==} + diff@5.2.0: + resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==} engines: {node: '>=0.3.1'} - /dir-glob@3.0.1: + dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} - dependencies: - path-type: 4.0.0 - /dlv@1.1.3: + dlv@1.1.3: resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} - /dns-equal@1.0.0: - resolution: {integrity: sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==} - dev: false - - /dns-packet@5.5.0: - resolution: {integrity: sha512-USawdAUzRkV6xrqTjiAEp6M9YagZEzWcSUaZTcIFAiyQWW1SoI6KyId8y2+/71wbgHKQAKd+iupLv4YvEwYWvA==} + dns-packet@5.6.1: + resolution: {integrity: sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==} engines: {node: '>=6'} - dependencies: - '@leichtgewicht/ip-codec': 2.0.4 - dev: false - /docker-compose@0.23.19: - resolution: {integrity: sha512-v5vNLIdUqwj4my80wxFDkNH+4S85zsRuH29SO7dCWVWPCMt/ohZBsGN6g6KXWifT0pzQ7uOxqEKCYCDPJ8Vz4g==} + docker-compose@0.24.8: + resolution: {integrity: sha512-plizRs/Vf15H+GCVxq2EUvyPK7ei9b/cVesHvjnX4xaXjM9spHe2Ytq0BitndFgvTJ3E3NljPNUEl7BAN43iZw==} engines: {node: '>= 6.0.0'} - dependencies: - yaml: 1.10.2 - dev: true - /docker-modem@3.0.8: + docker-modem@3.0.8: resolution: {integrity: sha512-f0ReSURdM3pcKPNS30mxOHSbaFLcknGmQjwSfmbcdOw1XWKXVhukM3NJHhr7NpY9BIyyWQb0EBo3KQvvuU5egQ==} engines: {node: '>= 8.0'} - dependencies: - debug: 4.3.4 - readable-stream: 3.6.2 - split-ca: 1.0.1 - ssh2: 1.11.0 - transitivePeerDependencies: - - supports-color - dev: true - /dockerode@3.3.5: + dockerode@3.3.5: resolution: {integrity: sha512-/0YNa3ZDNeLr/tSckmD69+Gq+qVNhvKfAHNeZJBnp7EOP6RGKV8ORrJHkUn20So5wU+xxT7+1n5u8PjHbfjbSA==} engines: {node: '>= 8.0'} - dependencies: - '@balena/dockerignore': 1.0.2 - docker-modem: 3.0.8 - tar-fs: 2.0.1 - transitivePeerDependencies: - - supports-color - dev: true - /doctrine@2.1.0: + doctrine@2.1.0: resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} engines: {node: '>=0.10.0'} - dependencies: - esutils: 2.0.3 - /doctrine@3.0.0: + doctrine@3.0.0: resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} engines: {node: '>=6.0.0'} - dependencies: - esutils: 2.0.3 - /dom-accessibility-api@0.5.16: + dom-accessibility-api@0.5.16: resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} - dev: true - /dom-align@1.12.4: + dom-accessibility-api@0.6.3: + resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} + + dom-align@1.12.4: resolution: {integrity: sha512-R8LUSEay/68zE5c8/3BDxiTEvgb4xZTF0RKmAHfiEVN3klfIpXfi2/QCoiWPccVQ0J/ZGdz9OjzL4uJEP/MRAw==} - /dom-converter@0.2.0: + dom-converter@0.2.0: resolution: {integrity: sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==} - dependencies: - utila: 0.4.0 - dev: false - /dom-serializer@0.2.2: + dom-helpers@5.2.1: + resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} + + dom-serializer@0.2.2: resolution: {integrity: sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==} - dependencies: - domelementtype: 2.3.0 - entities: 2.2.0 - /dom-serializer@1.4.1: + dom-serializer@1.4.1: resolution: {integrity: sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==} - dependencies: - domelementtype: 2.3.0 - domhandler: 4.3.1 - entities: 2.2.0 - dev: false - /dom-serializer@2.0.0: + dom-serializer@2.0.0: resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} - dependencies: - domelementtype: 2.3.0 - domhandler: 5.0.3 - entities: 4.4.0 - dev: false - /domelementtype@1.3.1: + domelementtype@1.3.1: resolution: {integrity: sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==} - /domelementtype@2.3.0: + domelementtype@2.3.0: resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} - /domexception@2.0.1: + domexception@2.0.1: resolution: {integrity: sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==} engines: {node: '>=8'} - dependencies: - webidl-conversions: 5.0.0 - dev: false + deprecated: Use your platform's native DOMException instead - /domhandler@3.3.0: + domhandler@3.3.0: resolution: {integrity: sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA==} engines: {node: '>= 4'} - dependencies: - domelementtype: 2.3.0 - dev: false - /domhandler@4.3.1: + domhandler@4.3.1: resolution: {integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==} engines: {node: '>= 4'} - dependencies: - domelementtype: 2.3.0 - dev: false - /domhandler@5.0.3: + domhandler@5.0.3: resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} engines: {node: '>= 4'} - dependencies: - domelementtype: 2.3.0 - dev: false - /domutils@1.7.0: + domutils@1.7.0: resolution: {integrity: sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==} - dependencies: - dom-serializer: 0.2.2 - domelementtype: 1.3.1 - /domutils@2.8.0: + domutils@2.8.0: resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==} - dependencies: - dom-serializer: 1.4.1 - domelementtype: 2.3.0 - domhandler: 4.3.1 - dev: false - /domutils@3.0.1: - resolution: {integrity: sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==} - dependencies: - dom-serializer: 2.0.0 - domelementtype: 2.3.0 - domhandler: 5.0.3 - dev: false + domutils@3.1.0: + resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==} - /dot-case@3.0.4: + dot-case@3.0.4: resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} - dependencies: - no-case: 3.0.4 - tslib: 2.5.0 - dev: false - - /dot-prop@5.3.0: - resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} - engines: {node: '>=8'} - dependencies: - is-obj: 2.0.0 - dev: true - /dotenv-cli@6.0.0: + dotenv-cli@6.0.0: resolution: {integrity: sha512-qXlCOi3UMDhCWFKe0yq5sg3X+pJAz+RQDiFN38AMSbUrnY3uZshSfDJUAge951OS7J9gwLZGfsBlWRSOYz/TRg==} hasBin: true - dependencies: - cross-spawn: 7.0.3 - dotenv: 16.0.3 - dotenv-expand: 8.0.3 - minimist: 1.2.8 - dev: true - /dotenv-expand@5.1.0: + dotenv-cli@7.4.2: + resolution: {integrity: sha512-SbUj8l61zIbzyhIbg0FwPJq6+wjbzdn9oEtozQpZ6kW2ihCcapKVZj49oCT3oPM+mgQm+itgvUQcG5szxVrZTA==} + hasBin: true + + dotenv-expand@10.0.0: + resolution: {integrity: sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==} + engines: {node: '>=12'} + + dotenv-expand@5.1.0: resolution: {integrity: sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==} - dev: false - /dotenv-expand@8.0.3: + dotenv-expand@8.0.3: resolution: {integrity: sha512-SErOMvge0ZUyWd5B0NXMQlDkN+8r+HhVUsxgOO7IoPDOdDRD2JjExpN6y3KnFR66jsJMwSn1pqIivhU5rcJiNg==} engines: {node: '>=12'} - dev: true - /dotenv@10.0.0: + dotenv@10.0.0: resolution: {integrity: sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==} engines: {node: '>=10'} - dev: false - /dotenv@16.0.3: - resolution: {integrity: sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==} + dotenv@16.4.5: + resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} engines: {node: '>=12'} - dev: true - /dset@3.1.2: - resolution: {integrity: sha512-g/M9sqy3oHe477Ar4voQxWtaPIFw1jTdKZuomOjhCcBx9nHUNn0pu6NopuFFrTh/TRZIKEj+76vLWFu9BNKk+Q==} + dset@3.1.4: + resolution: {integrity: sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==} engines: {node: '>=4'} - dev: true - /duplexer@0.1.1: + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + duplexer@0.1.1: resolution: {integrity: sha512-sxNZ+ljy+RA1maXoUReeqBBpBC6RLKmg5ewzV+x+mSETmWNoKdZN6vcQjpFROemza23hGFskJtFNoUWUaQ+R4Q==} - dev: true - /duplexer@0.1.2: + duplexer@0.1.2: resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} - /duplexify@3.7.1: - resolution: {integrity: sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==} - dependencies: - end-of-stream: 1.4.4 - inherits: 2.0.4 - readable-stream: 2.3.8 - stream-shift: 1.0.1 - dev: true + duplexify@4.1.3: + resolution: {integrity: sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==} - /duplexify@4.1.2: - resolution: {integrity: sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw==} - dependencies: - end-of-stream: 1.4.4 - inherits: 2.0.4 - readable-stream: 3.6.2 - stream-shift: 1.0.1 - dev: false - optional: true + dynamic-dedupe@0.3.0: + resolution: {integrity: sha512-ssuANeD+z97meYOqd50e04Ze5qp4bPqo8cCkI4TRjZkzAUgIDTrXV1R8QCdINpiI+hw14+rYazvTRdQrz0/rFQ==} - /ecdsa-sig-formatter@1.0.11: - resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} - dependencies: - safe-buffer: 5.2.1 - dev: false + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - /edge-runtime@2.0.0: - resolution: {integrity: sha512-TmRJhKi4mlM1e+zgF4CSzVU5gJ1sWj7ia+XhVgZ8PYyYUxk4PPjJU8qScpSLsAbdSxoBghLxdMuwuCzdYLd1sQ==} - hasBin: true - dependencies: - '@edge-runtime/format': 1.1.0 - '@edge-runtime/vm': 2.0.0 - exit-hook: 2.2.1 - http-status: 1.5.3 - mri: 1.2.0 - picocolors: 1.0.0 - pretty-bytes: 5.6.0 - pretty-ms: 7.0.1 - time-span: 4.0.0 - dev: true + ecdsa-sig-formatter@1.0.11: + resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} - /editorconfig@0.15.3: - resolution: {integrity: sha512-M9wIMFx96vq0R4F+gRpY3o2exzb8hEj/n9S8unZtHSvYjibBp/iMufSzvmOcV/laG0ZtuTVGtiJggPOSW2r93g==} + editorconfig@1.0.4: + resolution: {integrity: sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==} + engines: {node: '>=14'} hasBin: true - dependencies: - commander: 2.20.3 - lru-cache: 4.1.5 - semver: 5.7.1 - sigmund: 1.0.1 - dev: false - /ee-first@1.1.1: + ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - /ejs@3.1.9: - resolution: {integrity: sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==} + ejs@3.1.10: + resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==} engines: {node: '>=0.10.0'} hasBin: true - dependencies: - jake: 10.8.5 - /electron-to-chromium@1.4.368: - resolution: {integrity: sha512-e2aeCAixCj9M7nJxdB/wDjO6mbYX+lJJxSJCXDzlr5YPGYVofuJwGN9nKg2o6wWInjX6XmxRinn3AeJMK81ltw==} + electron-to-chromium@1.5.183: + resolution: {integrity: sha512-vCrDBYjQCAEefWGjlK3EpoSKfKbT10pR4XXPdn65q7snuNOZnthoVpBfZPykmDapOKfoD+MMIPG8ZjKyyc9oHA==} - /emittery@0.10.2: + electron-to-chromium@1.5.49: + resolution: {integrity: sha512-ZXfs1Of8fDb6z7WEYZjXpgIRF6MEu8JdeGA0A40aZq6OQbS+eJpnnV49epZRna2DU/YsEjSQuGtQPPtvt6J65A==} + + elliptic@6.6.1: + resolution: {integrity: sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==} + + emittery@0.10.2: resolution: {integrity: sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==} engines: {node: '>=12'} - /emittery@0.13.1: + emittery@0.13.1: resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} engines: {node: '>=12'} - dev: true - /emittery@0.8.1: + emittery@0.8.1: resolution: {integrity: sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==} engines: {node: '>=10'} - dev: false - /emoji-regex@8.0.0: + emoji-regex@10.4.0: + resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==} + + emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - /emoji-regex@9.2.2: + emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - /emojis-list@3.0.0: + emojis-list@3.0.0: resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==} engines: {node: '>= 4'} - /encodeurl@1.0.2: + encodeurl@1.0.2: resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} engines: {node: '>= 0.8'} - /encoding@0.1.13: + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + + encoding-sniffer@0.2.0: + resolution: {integrity: sha512-ju7Wq1kg04I3HtiYIOrUrdfdDvkyO9s5XM8QAj/bN61Yo/Vb4vgJxy5vi4Yxk01gWHbrofpPtpxM8bKger9jhg==} + + encoding@0.1.13: resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} - requiresBuild: true - dependencies: - iconv-lite: 0.6.3 - optional: true - /end-of-stream@1.4.4: + end-of-stream@1.4.4: resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} - dependencies: - once: 1.4.0 - /enhanced-resolve@5.12.0: - resolution: {integrity: sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==} - engines: {node: '>=10.13.0'} - dependencies: - graceful-fs: 4.2.11 - tapable: 2.2.1 + engine.io-client@6.6.3: + resolution: {integrity: sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==} - /enhanced-resolve@5.13.0: - resolution: {integrity: sha512-eyV8f0y1+bzyfh8xAwW/WTSZpLbjhqc4ne9eGSH4Zo2ejdyiNG9pU6mf9DG8a7+Auk6MFTlNOT4Y2y/9k8GKVg==} - engines: {node: '>=10.13.0'} - dependencies: - graceful-fs: 4.2.11 - tapable: 2.2.1 - dev: true + engine.io-parser@5.2.3: + resolution: {integrity: sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==} + engines: {node: '>=10.0.0'} - /ent@2.2.0: - resolution: {integrity: sha512-GHrMyVZQWvTIdDtpiEXdHZnFQKzeO09apj8Cbl4pKWy4i0Oprcq17usfDt5aO63swf0JOeMWjWQE/LzgSRuWpA==} - dev: false - optional: true + engine.io@6.6.4: + resolution: {integrity: sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==} + engines: {node: '>=10.2.0'} - /entities@2.1.0: - resolution: {integrity: sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==} - dev: false - optional: true + enhanced-resolve@5.17.1: + resolution: {integrity: sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==} + engines: {node: '>=10.13.0'} - /entities@2.2.0: + entities@2.2.0: resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==} - /entities@4.4.0: - resolution: {integrity: sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==} + entities@3.0.1: + resolution: {integrity: sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==} + engines: {node: '>=0.12'} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} - dev: false - /env-paths@2.2.1: + env-paths@2.2.1: resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} engines: {node: '>=6'} - /envinfo@7.8.1: - resolution: {integrity: sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==} + envinfo@7.14.0: + resolution: {integrity: sha512-CO40UI41xDQzhLB1hWyqUKgFhs250pNcGbyGKe1l/e4FSaI/+YE4IMG76GDt0In67WLPACIITC+sOi08x4wIvg==} engines: {node: '>=4'} hasBin: true - /err-code@2.0.3: + err-code@2.0.3: resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} - /errno@0.1.8: + errno@0.1.8: resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==} hasBin: true - requiresBuild: true - dependencies: - prr: 1.0.1 - dev: true - optional: true - /error-ex@1.3.2: + error-ex@1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} - dependencies: - is-arrayish: 0.2.1 - /error-stack-parser@2.1.4: + error-stack-parser@2.1.4: resolution: {integrity: sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==} - dependencies: - stackframe: 1.3.4 - dev: false - /error@10.4.0: + error@10.4.0: resolution: {integrity: sha512-YxIFEJuhgcICugOUvRx5th0UM+ActZ9sjY0QJmeVwsQdvosZ7kYzc9QqS0Da3R5iUmgU5meGIxh0xBeZpMVeLw==} - /es-abstract@1.21.2: - resolution: {integrity: sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==} + es-abstract@1.23.3: + resolution: {integrity: sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==} engines: {node: '>= 0.4'} - dependencies: - array-buffer-byte-length: 1.0.0 - available-typed-arrays: 1.0.5 - call-bind: 1.0.2 - es-set-tostringtag: 2.0.1 - es-to-primitive: 1.2.1 - function.prototype.name: 1.1.5 - get-intrinsic: 1.2.0 - get-symbol-description: 1.0.0 - globalthis: 1.0.3 - gopd: 1.0.1 - has: 1.0.3 - has-property-descriptors: 1.0.0 - has-proto: 1.0.1 - has-symbols: 1.0.3 - internal-slot: 1.0.5 - is-array-buffer: 3.0.2 - is-callable: 1.2.7 - is-negative-zero: 2.0.2 - is-regex: 1.1.4 - is-shared-array-buffer: 1.0.2 - is-string: 1.0.7 - is-typed-array: 1.1.10 - is-weakref: 1.0.2 - object-inspect: 1.12.3 - object-keys: 1.1.1 - object.assign: 4.1.4 - regexp.prototype.flags: 1.4.3 - safe-regex-test: 1.0.0 - string.prototype.trim: 1.2.7 - string.prototype.trimend: 1.0.6 - string.prototype.trimstart: 1.0.6 - typed-array-length: 1.0.4 - unbox-primitive: 1.0.2 - which-typed-array: 1.1.9 - /es-array-method-boxes-properly@1.0.0: + es-array-method-boxes-properly@1.0.0: resolution: {integrity: sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==} - /es-get-iterator@1.1.3: - resolution: {integrity: sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==} - dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.0 - has-symbols: 1.0.3 - is-arguments: 1.1.1 - is-map: 2.0.2 - is-set: 2.0.2 - is-string: 1.0.7 - isarray: 2.0.5 - stop-iteration-iterator: 1.0.0 + es-define-property@1.0.0: + resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} + engines: {node: '>= 0.4'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} - /es-module-lexer@0.9.3: - resolution: {integrity: sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==} + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} - /es-module-lexer@1.2.1: - resolution: {integrity: sha512-9978wrXM50Y4rTMmW5kXIC09ZdXQZqkE4mxhwkd8VbzsGkXGPgV4zWuqQJgCEzYngdo2dYDa0l8xhX4fkSwJSg==} - dev: true + es-get-iterator@1.1.3: + resolution: {integrity: sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==} - /es-set-tostringtag@2.0.1: - resolution: {integrity: sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==} + es-iterator-helpers@1.1.0: + resolution: {integrity: sha512-/SurEfycdyssORP/E+bj4sEu1CWw4EmLDsHynHwSXQ7utgbrMRWW195pTrCjFgFCddf/UkYm3oqKPRq5i8bJbw==} engines: {node: '>= 0.4'} - dependencies: - get-intrinsic: 1.2.0 - has: 1.0.3 - has-tostringtag: 1.0.0 - /es-shim-unscopables@1.0.0: - resolution: {integrity: sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==} - dependencies: - has: 1.0.3 + es-module-lexer@1.5.4: + resolution: {integrity: sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==} - /es-to-primitive@1.2.1: - resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} + es-object-atoms@1.0.0: + resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==} engines: {node: '>= 0.4'} - dependencies: - is-callable: 1.2.7 - is-date-object: 1.0.5 - is-symbol: 1.0.4 - /es6-promisify@6.1.1: - resolution: {integrity: sha512-HBL8I3mIki5C1Cc9QjKUenHtnG0A5/xA8Q/AllRcfiwl2CZFXGK7ddBiCoRwAix4i2KxcQfjtIVcrVbB3vbmwg==} - dev: true + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} - /esbuild-android-64@0.14.47: - resolution: {integrity: sha512-R13Bd9+tqLVFndncMHssZrPWe6/0Kpv2/dt4aA69soX4PRxlzsVpCvoJeFE8sOEoeVEiBkI0myjlkDodXlHa0g==} - engines: {node: '>=12'} - cpu: [x64] - os: [android] - requiresBuild: true - dev: true - optional: true + es-set-tostringtag@2.0.3: + resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==} + engines: {node: '>= 0.4'} - /esbuild-android-arm64@0.14.47: - resolution: {integrity: sha512-OkwOjj7ts4lBp/TL6hdd8HftIzOy/pdtbrNA4+0oVWgGG64HrdVzAF5gxtJufAPOsEjkyh1oIYvKAUinKKQRSQ==} - engines: {node: '>=12'} - cpu: [arm64] - os: [android] - requiresBuild: true - dev: true - optional: true + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} - /esbuild-darwin-64@0.14.47: - resolution: {integrity: sha512-R6oaW0y5/u6Eccti/TS6c/2c1xYTb1izwK3gajJwi4vIfNs1s8B1dQzI1UiC9T61YovOQVuePDcfqHLT3mUZJA==} - engines: {node: '>=12'} - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: true - optional: true + es-shim-unscopables@1.0.2: + resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==} - /esbuild-darwin-arm64@0.14.47: - resolution: {integrity: sha512-seCmearlQyvdvM/noz1L9+qblC5vcBrhUaOoLEDDoLInF/VQ9IkobGiLlyTPYP5dW1YD4LXhtBgOyevoIHGGnw==} - engines: {node: '>=12'} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: true - optional: true + es-to-primitive@1.2.1: + resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} + engines: {node: '>= 0.4'} - /esbuild-freebsd-64@0.14.47: - resolution: {integrity: sha512-ZH8K2Q8/Ux5kXXvQMDsJcxvkIwut69KVrYQhza/ptkW50DC089bCVrJZZ3sKzIoOx+YPTrmsZvqeZERjyYrlvQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [freebsd] - requiresBuild: true - dev: true - optional: true + es6-promise@4.2.8: + resolution: {integrity: sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==} - /esbuild-freebsd-arm64@0.14.47: - resolution: {integrity: sha512-ZJMQAJQsIOhn3XTm7MPQfCzEu5b9STNC+s90zMWe2afy9EwnHV7Ov7ohEMv2lyWlc2pjqLW8QJnz2r0KZmeAEQ==} - engines: {node: '>=12'} - cpu: [arm64] - os: [freebsd] - requiresBuild: true - dev: true - optional: true + esbuild-register@3.6.0: + resolution: {integrity: sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==} + peerDependencies: + esbuild: '>=0.12 <1' - /esbuild-linux-32@0.14.47: - resolution: {integrity: sha512-FxZOCKoEDPRYvq300lsWCTv1kcHgiiZfNrPtEhFAiqD7QZaXrad8LxyJ8fXGcWzIFzRiYZVtB3ttvITBvAFhKw==} - engines: {node: '>=12'} - cpu: [ia32] - os: [linux] - requiresBuild: true - dev: true - optional: true + esbuild@0.23.1: + resolution: {integrity: sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==} + engines: {node: '>=18'} + hasBin: true - /esbuild-linux-64@0.14.47: - resolution: {integrity: sha512-nFNOk9vWVfvWYF9YNYksZptgQAdstnDCMtR6m42l5Wfugbzu11VpMCY9XrD4yFxvPo9zmzcoUL/88y0lfJZJJw==} - engines: {node: '>=12'} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true - optional: true + esbuild@0.25.6: + resolution: {integrity: sha512-GVuzuUwtdsghE3ocJ9Bs8PNoF13HNQ5TXbEi2AhvVb8xU1Iwt9Fos9FEamfoee+u/TOsn7GUWc04lz46n2bbTg==} + engines: {node: '>=18'} + hasBin: true - /esbuild-linux-arm64@0.14.47: - resolution: {integrity: sha512-ywfme6HVrhWcevzmsufjd4iT3PxTfCX9HOdxA7Hd+/ZM23Y9nXeb+vG6AyA6jgq/JovkcqRHcL9XwRNpWG6XRw==} - engines: {node: '>=12'} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true - optional: true + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} - /esbuild-linux-arm@0.14.47: - resolution: {integrity: sha512-ZGE1Bqg/gPRXrBpgpvH81tQHpiaGxa8c9Rx/XOylkIl2ypLuOcawXEAo8ls+5DFCcRGt/o3sV+PzpAFZobOsmA==} - engines: {node: '>=12'} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: true - optional: true + escape-goat@3.0.0: + resolution: {integrity: sha512-w3PwNZJwRxlp47QGzhuEBldEqVHHhh8/tIPcl6ecf2Bou99cdAt0knihBV0Ecc7CGxYduXVBDheH1K2oADRlvw==} + engines: {node: '>=10'} - /esbuild-linux-mips64le@0.14.47: - resolution: {integrity: sha512-mg3D8YndZ1LvUiEdDYR3OsmeyAew4MA/dvaEJxvyygahWmpv1SlEEnhEZlhPokjsUMfRagzsEF/d/2XF+kTQGg==} - engines: {node: '>=12'} - cpu: [mips64el] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /esbuild-linux-ppc64le@0.14.47: - resolution: {integrity: sha512-WER+f3+szmnZiWoK6AsrTKGoJoErG2LlauSmk73LEZFQ/iWC+KhhDsOkn1xBUpzXWsxN9THmQFltLoaFEH8F8w==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /esbuild-linux-riscv64@0.14.47: - resolution: {integrity: sha512-1fI6bP3A3rvI9BsaaXbMoaOjLE3lVkJtLxsgLHqlBhLlBVY7UqffWBvkrX/9zfPhhVMd9ZRFiaqXnB1T7BsL2g==} - engines: {node: '>=12'} - cpu: [riscv64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /esbuild-linux-s390x@0.14.47: - resolution: {integrity: sha512-eZrWzy0xFAhki1CWRGnhsHVz7IlSKX6yT2tj2Eg8lhAwlRE5E96Hsb0M1mPSE1dHGpt1QVwwVivXIAacF/G6mw==} - engines: {node: '>=12'} - cpu: [s390x] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /esbuild-netbsd-64@0.14.47: - resolution: {integrity: sha512-Qjdjr+KQQVH5Q2Q1r6HBYswFTToPpss3gqCiSw2Fpq/ua8+eXSQyAMG+UvULPqXceOwpnPo4smyZyHdlkcPppQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [netbsd] - requiresBuild: true - dev: true - optional: true - - /esbuild-openbsd-64@0.14.47: - resolution: {integrity: sha512-QpgN8ofL7B9z8g5zZqJE+eFvD1LehRlxr25PBkjyyasakm4599iroUpaj96rdqRlO2ShuyqwJdr+oNqWwTUmQw==} - engines: {node: '>=12'} - cpu: [x64] - os: [openbsd] - requiresBuild: true - dev: true - optional: true - - /esbuild-sunos-64@0.14.47: - resolution: {integrity: sha512-uOeSgLUwukLioAJOiGYm3kNl+1wJjgJA8R671GYgcPgCx7QR73zfvYqXFFcIO93/nBdIbt5hd8RItqbbf3HtAQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [sunos] - requiresBuild: true - dev: true - optional: true - - /esbuild-windows-32@0.14.47: - resolution: {integrity: sha512-H0fWsLTp2WBfKLBgwYT4OTfFly4Im/8B5f3ojDv1Kx//kiubVY0IQunP2Koc/fr/0wI7hj3IiBDbSrmKlrNgLQ==} - engines: {node: '>=12'} - cpu: [ia32] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /esbuild-windows-64@0.14.47: - resolution: {integrity: sha512-/Pk5jIEH34T68r8PweKRi77W49KwanZ8X6lr3vDAtOlH5EumPE4pBHqkCUdELanvsT14yMXLQ/C/8XPi1pAtkQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /esbuild-windows-arm64@0.14.47: - resolution: {integrity: sha512-HFSW2lnp62fl86/qPQlqw6asIwCnEsEoNIL1h2uVMgakddf+vUuMcCbtUY1i8sst7KkgHrVKCJQB33YhhOweCQ==} - engines: {node: '>=12'} - cpu: [arm64] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /esbuild@0.14.47: - resolution: {integrity: sha512-wI4ZiIfFxpkuxB8ju4MHrGwGLyp1+awEHAHVpx6w7a+1pmYIq8T9FGEVVwFo0iFierDoMj++Xq69GXWYn2EiwA==} - engines: {node: '>=12'} - hasBin: true - requiresBuild: true - optionalDependencies: - esbuild-android-64: 0.14.47 - esbuild-android-arm64: 0.14.47 - esbuild-darwin-64: 0.14.47 - esbuild-darwin-arm64: 0.14.47 - esbuild-freebsd-64: 0.14.47 - esbuild-freebsd-arm64: 0.14.47 - esbuild-linux-32: 0.14.47 - esbuild-linux-64: 0.14.47 - esbuild-linux-arm: 0.14.47 - esbuild-linux-arm64: 0.14.47 - esbuild-linux-mips64le: 0.14.47 - esbuild-linux-ppc64le: 0.14.47 - esbuild-linux-riscv64: 0.14.47 - esbuild-linux-s390x: 0.14.47 - esbuild-netbsd-64: 0.14.47 - esbuild-openbsd-64: 0.14.47 - esbuild-sunos-64: 0.14.47 - esbuild-windows-32: 0.14.47 - esbuild-windows-64: 0.14.47 - esbuild-windows-arm64: 0.14.47 - dev: true - - /esbuild@0.16.3: - resolution: {integrity: sha512-71f7EjPWTiSguen8X/kxEpkAS7BFHwtQKisCDDV3Y4GLGWBaoSCyD5uXkaUew6JDzA9FEN1W23mdnSwW9kqCeg==} - engines: {node: '>=12'} - hasBin: true - requiresBuild: true - optionalDependencies: - '@esbuild/android-arm': 0.16.3 - '@esbuild/android-arm64': 0.16.3 - '@esbuild/android-x64': 0.16.3 - '@esbuild/darwin-arm64': 0.16.3 - '@esbuild/darwin-x64': 0.16.3 - '@esbuild/freebsd-arm64': 0.16.3 - '@esbuild/freebsd-x64': 0.16.3 - '@esbuild/linux-arm': 0.16.3 - '@esbuild/linux-arm64': 0.16.3 - '@esbuild/linux-ia32': 0.16.3 - '@esbuild/linux-loong64': 0.16.3 - '@esbuild/linux-mips64el': 0.16.3 - '@esbuild/linux-ppc64': 0.16.3 - '@esbuild/linux-riscv64': 0.16.3 - '@esbuild/linux-s390x': 0.16.3 - '@esbuild/linux-x64': 0.16.3 - '@esbuild/netbsd-x64': 0.16.3 - '@esbuild/openbsd-x64': 0.16.3 - '@esbuild/sunos-x64': 0.16.3 - '@esbuild/win32-arm64': 0.16.3 - '@esbuild/win32-ia32': 0.16.3 - '@esbuild/win32-x64': 0.16.3 - - /esbuild@0.17.6: - resolution: {integrity: sha512-TKFRp9TxrJDdRWfSsSERKEovm6v30iHnrjlcGhLBOtReE28Yp1VSBRfO3GTaOFMoxsNerx4TjrhzSuma9ha83Q==} - engines: {node: '>=12'} - hasBin: true - requiresBuild: true - optionalDependencies: - '@esbuild/android-arm': 0.17.6 - '@esbuild/android-arm64': 0.17.6 - '@esbuild/android-x64': 0.17.6 - '@esbuild/darwin-arm64': 0.17.6 - '@esbuild/darwin-x64': 0.17.6 - '@esbuild/freebsd-arm64': 0.17.6 - '@esbuild/freebsd-x64': 0.17.6 - '@esbuild/linux-arm': 0.17.6 - '@esbuild/linux-arm64': 0.17.6 - '@esbuild/linux-ia32': 0.17.6 - '@esbuild/linux-loong64': 0.17.6 - '@esbuild/linux-mips64el': 0.17.6 - '@esbuild/linux-ppc64': 0.17.6 - '@esbuild/linux-riscv64': 0.17.6 - '@esbuild/linux-s390x': 0.17.6 - '@esbuild/linux-x64': 0.17.6 - '@esbuild/netbsd-x64': 0.17.6 - '@esbuild/openbsd-x64': 0.17.6 - '@esbuild/sunos-x64': 0.17.6 - '@esbuild/win32-arm64': 0.17.6 - '@esbuild/win32-ia32': 0.17.6 - '@esbuild/win32-x64': 0.17.6 - dev: true - - /escalade@3.1.1: - resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} - engines: {node: '>=6'} - - /escape-goat@3.0.0: - resolution: {integrity: sha512-w3PwNZJwRxlp47QGzhuEBldEqVHHhh8/tIPcl6ecf2Bou99cdAt0knihBV0Ecc7CGxYduXVBDheH1K2oADRlvw==} - engines: {node: '>=10'} - dev: false - - /escape-html@1.0.3: + escape-html@1.0.3: resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} - /escape-string-regexp@1.0.5: + escape-string-regexp@1.0.5: resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} engines: {node: '>=0.8.0'} - /escape-string-regexp@2.0.0: + escape-string-regexp@2.0.0: resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} engines: {node: '>=8'} - /escape-string-regexp@4.0.0: + escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} - /escodegen@1.14.3: + escodegen@1.14.3: resolution: {integrity: sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==} engines: {node: '>=4.0'} hasBin: true - dependencies: - esprima: 4.0.1 - estraverse: 4.3.0 - esutils: 2.0.3 - optionator: 0.8.3 - optionalDependencies: - source-map: 0.6.1 - /escodegen@2.0.0: - resolution: {integrity: sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==} + escodegen@2.1.0: + resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} engines: {node: '>=6.0'} hasBin: true - dependencies: - esprima: 4.0.1 - estraverse: 5.3.0 - esutils: 2.0.3 - optionator: 0.8.3 - optionalDependencies: - source-map: 0.6.1 - dev: false - /eslint-config-next@13.0.0(eslint@8.29.0)(typescript@4.9.4): - resolution: {integrity: sha512-y2nqWS2tycWySdVhb+rhp6CuDmDazGySqkzzQZf3UTyfHyC7og1m5m/AtMFwCo5mtvDqvw1BENin52kV9733lg==} + eslint-config-next@15.5.4: + resolution: {integrity: sha512-BzgVVuT3kfJes8i2GHenC1SRJ+W3BTML11lAOYFOOPzrk2xp66jBOAGEFRw+3LkYCln5UzvFsLhojrshb5Zfaw==} peerDependencies: - eslint: ^7.23.0 || ^8.0.0 + eslint: ^7.23.0 || ^8.0.0 || ^9.0.0 typescript: '>=3.3.1' peerDependenciesMeta: typescript: optional: true - dependencies: - '@next/eslint-plugin-next': 13.0.0 - '@rushstack/eslint-patch': 1.2.0 - '@typescript-eslint/parser': 5.58.0(eslint@8.29.0)(typescript@4.9.4) - eslint: 8.29.0 - eslint-import-resolver-node: 0.3.7 - eslint-import-resolver-typescript: 2.7.1(eslint-plugin-import@2.27.5)(eslint@8.29.0) - eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.58.0)(eslint-import-resolver-typescript@2.7.1)(eslint@8.29.0) - eslint-plugin-jsx-a11y: 6.7.1(eslint@8.29.0) - eslint-plugin-react: 7.32.2(eslint@8.29.0) - eslint-plugin-react-hooks: 4.6.0(eslint@8.29.0) - typescript: 4.9.4 - transitivePeerDependencies: - - eslint-import-resolver-webpack - - supports-color - dev: false - /eslint-config-next@13.3.0(eslint@8.24.0)(typescript@4.9.5): - resolution: {integrity: sha512-6YEwmFBX0VjBd3ODGW9df0Is0FLaRFdMN8eAahQG9CN6LjQ28J8AFr19ngxqMSg7Qv6Uca/3VeeBosJh1bzu0w==} + eslint-config-prettier@9.1.0: + resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==} + hasBin: true peerDependencies: - eslint: ^7.23.0 || ^8.0.0 - typescript: '>=3.3.1' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@next/eslint-plugin-next': 13.3.0 - '@rushstack/eslint-patch': 1.2.0 - '@typescript-eslint/parser': 5.58.0(eslint@8.24.0)(typescript@4.9.5) - eslint: 8.24.0 - eslint-import-resolver-node: 0.3.7 - eslint-import-resolver-typescript: 3.5.5(@typescript-eslint/parser@5.58.0)(eslint-import-resolver-node@0.3.7)(eslint-plugin-import@2.27.5)(eslint@8.24.0) - eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.58.0)(eslint-import-resolver-typescript@2.7.1)(eslint@8.29.0) - eslint-plugin-jsx-a11y: 6.7.1(eslint@8.24.0) - eslint-plugin-react: 7.32.2(eslint@8.24.0) - eslint-plugin-react-hooks: 4.6.0(eslint@8.24.0) - typescript: 4.9.5 - transitivePeerDependencies: - - eslint-import-resolver-webpack - - supports-color - dev: true + eslint: '>=7.0.0' - /eslint-config-react-app@7.1.0-next.14(@babel/plugin-syntax-flow@7.21.4)(@babel/plugin-transform-react-jsx@7.21.0)(eslint@8.38.0)(jest@27.5.1)(typescript@4.9.5): - resolution: {integrity: sha512-E7nP1tWYDe2y3bS6MVIt/Iw3R3fYqhRqcQXGeMf066ohUaYoIZMbPpi69MjCxwjaqQOJUoYTKNO5oPwwc5Wpkg==} + eslint-config-react-app@7.0.1: + resolution: {integrity: sha512-K6rNzvkIeHaTd8m/QEh1Zko0KI7BACWkkneSs6s9cKZC/J27X3eZR6Upt1jkmZ/4FK+XUOPPxMEN7+lbUXfSlA==} engines: {node: '>=14.0.0'} peerDependencies: eslint: ^8.0.0 - dependencies: - '@babel/core': 7.21.4 - '@babel/eslint-parser': 7.21.3(@babel/core@7.21.4)(eslint@8.38.0) - '@rushstack/eslint-patch': 1.2.0 - '@typescript-eslint/eslint-plugin': 5.58.0(@typescript-eslint/parser@5.58.0)(eslint@8.38.0)(typescript@4.9.5) - '@typescript-eslint/parser': 5.58.0(eslint@8.38.0)(typescript@4.9.5) - babel-preset-react-app: 10.1.0-next.14 - confusing-browser-globals: 1.1.0-next.14 - eslint: 8.38.0 - eslint-plugin-flowtype: 8.0.3(@babel/plugin-syntax-flow@7.21.4)(@babel/plugin-transform-react-jsx@7.21.0)(eslint@8.38.0) - eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.58.0)(eslint-import-resolver-typescript@2.7.1)(eslint@8.29.0) - eslint-plugin-jest: 25.7.0(@typescript-eslint/eslint-plugin@5.58.0)(eslint@8.38.0)(jest@27.5.1)(typescript@4.9.5) - eslint-plugin-jsx-a11y: 6.7.1(eslint@8.38.0) - eslint-plugin-react: 7.32.2(eslint@8.38.0) - eslint-plugin-react-hooks: 4.6.0(eslint@8.38.0) - eslint-plugin-testing-library: 5.10.2(eslint@8.38.0)(typescript@4.9.5) - transitivePeerDependencies: - - '@babel/plugin-syntax-flow' - - '@babel/plugin-transform-react-jsx' - - eslint-import-resolver-typescript - - eslint-import-resolver-webpack - - jest - - supports-color - - typescript - dev: false - - /eslint-import-resolver-node@0.3.7: - resolution: {integrity: sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==} - dependencies: - debug: 3.2.7(supports-color@5.5.0) - is-core-module: 2.12.0 - resolve: 1.22.2 - transitivePeerDependencies: - - supports-color + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true - /eslint-import-resolver-typescript@2.7.1(eslint-plugin-import@2.27.5)(eslint@8.29.0): - resolution: {integrity: sha512-00UbgGwV8bSgUv34igBDbTOtKhqoRMy9bFjNehT40bXg6585PNIct8HhXZ0SybqB9rWtXj9crcku8ndDn/gIqQ==} - engines: {node: '>=4'} - peerDependencies: - eslint: '*' - eslint-plugin-import: '*' - dependencies: - debug: 4.3.4 - eslint: 8.29.0 - eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.58.0)(eslint-import-resolver-typescript@2.7.1)(eslint@8.29.0) - glob: 7.2.3 - is-glob: 4.0.3 - resolve: 1.22.2 - tsconfig-paths: 3.14.2 - transitivePeerDependencies: - - supports-color + eslint-import-resolver-node@0.3.9: + resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} - /eslint-import-resolver-typescript@3.5.5(@typescript-eslint/parser@5.58.0)(eslint-import-resolver-node@0.3.7)(eslint-plugin-import@2.27.5)(eslint@8.24.0): - resolution: {integrity: sha512-TdJqPHs2lW5J9Zpe17DZNQuDnox4xo2o+0tE7Pggain9Rbc19ik8kFtXdxZ250FVx2kF4vlt2RSf4qlUpG7bhw==} + eslint-import-resolver-typescript@3.6.3: + resolution: {integrity: sha512-ud9aw4szY9cCT1EWWdGv1L1XR6hh2PaRWif0j2QjQ0pgTY/69iw+W0Z4qZv5wHahOl8isEr+k/JnyAqNQkLkIA==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: eslint: '*' eslint-plugin-import: '*' - dependencies: - debug: 4.3.4 - enhanced-resolve: 5.12.0 - eslint: 8.24.0 - eslint-module-utils: 2.7.4(@typescript-eslint/parser@5.58.0)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@3.5.5)(eslint@8.24.0) - eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.58.0)(eslint-import-resolver-typescript@2.7.1)(eslint@8.29.0) - get-tsconfig: 4.5.0 - globby: 13.1.4 - is-core-module: 2.12.0 - is-glob: 4.0.3 - synckit: 0.8.5 - transitivePeerDependencies: - - '@typescript-eslint/parser' - - eslint-import-resolver-node - - eslint-import-resolver-webpack - - supports-color - dev: true - - /eslint-module-utils@2.7.4(@typescript-eslint/parser@5.58.0)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@2.7.1)(eslint@8.29.0): - resolution: {integrity: sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==} - engines: {node: '>=4'} - peerDependencies: - '@typescript-eslint/parser': '*' - eslint: '*' - eslint-import-resolver-node: '*' - eslint-import-resolver-typescript: '*' - eslint-import-resolver-webpack: '*' + eslint-plugin-import-x: '*' peerDependenciesMeta: - '@typescript-eslint/parser': - optional: true - eslint: + eslint-plugin-import: optional: true - eslint-import-resolver-node: - optional: true - eslint-import-resolver-typescript: - optional: true - eslint-import-resolver-webpack: + eslint-plugin-import-x: optional: true - dependencies: - '@typescript-eslint/parser': 5.58.0(eslint@8.29.0)(typescript@4.9.4) - debug: 3.2.7(supports-color@5.5.0) - eslint: 8.29.0 - eslint-import-resolver-node: 0.3.7 - eslint-import-resolver-typescript: 2.7.1(eslint-plugin-import@2.27.5)(eslint@8.29.0) - transitivePeerDependencies: - - supports-color - /eslint-module-utils@2.7.4(@typescript-eslint/parser@5.58.0)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@3.5.5)(eslint@8.24.0): - resolution: {integrity: sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==} + eslint-module-utils@2.12.0: + resolution: {integrity: sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==} engines: {node: '>=4'} peerDependencies: '@typescript-eslint/parser': '*' @@ -12520,64 +8049,26 @@ packages: optional: true eslint-import-resolver-webpack: optional: true - dependencies: - '@typescript-eslint/parser': 5.58.0(eslint@8.24.0)(typescript@4.9.5) - debug: 3.2.7(supports-color@5.5.0) - eslint: 8.24.0 - eslint-import-resolver-node: 0.3.7 - eslint-import-resolver-typescript: 3.5.5(@typescript-eslint/parser@5.58.0)(eslint-import-resolver-node@0.3.7)(eslint-plugin-import@2.27.5)(eslint@8.24.0) - transitivePeerDependencies: - - supports-color - dev: true - /eslint-plugin-flowtype@8.0.3(@babel/plugin-syntax-flow@7.21.4)(@babel/plugin-transform-react-jsx@7.21.0)(eslint@8.38.0): + eslint-plugin-flowtype@8.0.3: resolution: {integrity: sha512-dX8l6qUL6O+fYPtpNRideCFSpmWOUVx5QcaGLVqe/vlDiBSe4vYljDWDETwnyFzpl7By/WVIu6rcrniCgH9BqQ==} engines: {node: '>=12.0.0'} peerDependencies: '@babel/plugin-syntax-flow': ^7.14.5 '@babel/plugin-transform-react-jsx': ^7.14.9 eslint: ^8.1.0 - dependencies: - '@babel/plugin-syntax-flow': 7.21.4(@babel/core@7.21.4) - '@babel/plugin-transform-react-jsx': 7.21.0(@babel/core@7.21.4) - eslint: 8.38.0 - lodash: 4.17.21 - string-natural-compare: 3.0.1 - dev: false - /eslint-plugin-import@2.27.5(@typescript-eslint/parser@5.58.0)(eslint-import-resolver-typescript@2.7.1)(eslint@8.29.0): - resolution: {integrity: sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==} + eslint-plugin-import@2.31.0: + resolution: {integrity: sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==} engines: {node: '>=4'} peerDependencies: '@typescript-eslint/parser': '*' - eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9 peerDependenciesMeta: '@typescript-eslint/parser': optional: true - dependencies: - '@typescript-eslint/parser': 5.58.0(eslint@8.29.0)(typescript@4.9.4) - array-includes: 3.1.6 - array.prototype.flat: 1.3.1 - array.prototype.flatmap: 1.3.1 - debug: 3.2.7(supports-color@5.5.0) - doctrine: 2.1.0 - eslint: 8.29.0 - eslint-import-resolver-node: 0.3.7 - eslint-module-utils: 2.7.4(@typescript-eslint/parser@5.58.0)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@2.7.1)(eslint@8.29.0) - has: 1.0.3 - is-core-module: 2.12.0 - is-glob: 4.0.3 - minimatch: 3.1.2 - object.values: 1.1.6 - resolve: 1.22.2 - semver: 6.3.0 - tsconfig-paths: 3.14.2 - transitivePeerDependencies: - - eslint-import-resolver-typescript - - eslint-import-resolver-webpack - - supports-color - /eslint-plugin-jest@25.7.0(@typescript-eslint/eslint-plugin@5.58.0)(eslint@8.38.0)(jest@27.5.1)(typescript@4.9.5): + eslint-plugin-jest@25.7.0: resolution: {integrity: sha512-PWLUEXeeF7C9QGKqvdSbzLOiLTx+bno7/HC9eefePfEb257QFHg7ye3dh80AZVkaa/RQsBB1Q/ORQvg2X7F0NQ==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} peerDependencies: @@ -12589,971 +8080,384 @@ packages: optional: true jest: optional: true - dependencies: - '@typescript-eslint/eslint-plugin': 5.58.0(@typescript-eslint/parser@5.58.0)(eslint@8.38.0)(typescript@4.9.5) - '@typescript-eslint/experimental-utils': 5.58.0(eslint@8.38.0)(typescript@4.9.5) - eslint: 8.38.0 - jest: 27.5.1(ts-node@10.8.2) - transitivePeerDependencies: - - supports-color - - typescript - dev: false - - /eslint-plugin-jsx-a11y@6.7.1(eslint@8.24.0): - resolution: {integrity: sha512-63Bog4iIethyo8smBklORknVjB0T2dwB8Mr/hIC+fBS0uyHdYYpzM/Ed+YC8VxTjlXHEWFOdmgwcDn1U2L9VCA==} - engines: {node: '>=4.0'} - peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 - dependencies: - '@babel/runtime': 7.21.0 - aria-query: 5.1.3 - array-includes: 3.1.6 - array.prototype.flatmap: 1.3.1 - ast-types-flow: 0.0.7 - axe-core: 4.6.3 - axobject-query: 3.1.1 - damerau-levenshtein: 1.0.8 - emoji-regex: 9.2.2 - eslint: 8.24.0 - has: 1.0.3 - jsx-ast-utils: 3.3.3 - language-tags: 1.0.5 - minimatch: 3.1.2 - object.entries: 1.1.6 - object.fromentries: 2.0.6 - semver: 6.3.0 - dev: true - - /eslint-plugin-jsx-a11y@6.7.1(eslint@8.29.0): - resolution: {integrity: sha512-63Bog4iIethyo8smBklORknVjB0T2dwB8Mr/hIC+fBS0uyHdYYpzM/Ed+YC8VxTjlXHEWFOdmgwcDn1U2L9VCA==} - engines: {node: '>=4.0'} - peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 - dependencies: - '@babel/runtime': 7.21.0 - aria-query: 5.1.3 - array-includes: 3.1.6 - array.prototype.flatmap: 1.3.1 - ast-types-flow: 0.0.7 - axe-core: 4.6.3 - axobject-query: 3.1.1 - damerau-levenshtein: 1.0.8 - emoji-regex: 9.2.2 - eslint: 8.29.0 - has: 1.0.3 - jsx-ast-utils: 3.3.3 - language-tags: 1.0.5 - minimatch: 3.1.2 - object.entries: 1.1.6 - object.fromentries: 2.0.6 - semver: 6.3.0 - dev: false - /eslint-plugin-jsx-a11y@6.7.1(eslint@8.38.0): - resolution: {integrity: sha512-63Bog4iIethyo8smBklORknVjB0T2dwB8Mr/hIC+fBS0uyHdYYpzM/Ed+YC8VxTjlXHEWFOdmgwcDn1U2L9VCA==} + eslint-plugin-jsx-a11y@6.10.2: + resolution: {integrity: sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==} engines: {node: '>=4.0'} peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 - dependencies: - '@babel/runtime': 7.21.0 - aria-query: 5.1.3 - array-includes: 3.1.6 - array.prototype.flatmap: 1.3.1 - ast-types-flow: 0.0.7 - axe-core: 4.6.3 - axobject-query: 3.1.1 - damerau-levenshtein: 1.0.8 - emoji-regex: 9.2.2 - eslint: 8.38.0 - has: 1.0.3 - jsx-ast-utils: 3.3.3 - language-tags: 1.0.5 - minimatch: 3.1.2 - object.entries: 1.1.6 - object.fromentries: 2.0.6 - semver: 6.3.0 - dev: false - - /eslint-plugin-react-hooks@4.6.0(eslint@8.24.0): - resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==} - engines: {node: '>=10'} - peerDependencies: - eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 - dependencies: - eslint: 8.24.0 - dev: true + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9 - /eslint-plugin-react-hooks@4.6.0(eslint@8.29.0): - resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==} + eslint-plugin-react-hooks@4.6.2: + resolution: {integrity: sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==} engines: {node: '>=10'} peerDependencies: eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 - dependencies: - eslint: 8.29.0 - dev: false - /eslint-plugin-react-hooks@4.6.0(eslint@8.38.0): - resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==} + eslint-plugin-react-hooks@5.0.0: + resolution: {integrity: sha512-hIOwI+5hYGpJEc4uPRmz2ulCjAGD/N13Lukkh8cLV0i2IRk/bdZDYjgLVHj+U9Z704kLIdIO6iueGvxNur0sgw==} engines: {node: '>=10'} peerDependencies: - eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 - dependencies: - eslint: 8.38.0 - dev: false - - /eslint-plugin-react@7.32.2(eslint@8.24.0): - resolution: {integrity: sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg==} - engines: {node: '>=4'} - peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 - dependencies: - array-includes: 3.1.6 - array.prototype.flatmap: 1.3.1 - array.prototype.tosorted: 1.1.1 - doctrine: 2.1.0 - eslint: 8.24.0 - estraverse: 5.3.0 - jsx-ast-utils: 3.3.3 - minimatch: 3.1.2 - object.entries: 1.1.6 - object.fromentries: 2.0.6 - object.hasown: 1.1.2 - object.values: 1.1.6 - prop-types: 15.8.1 - resolve: 2.0.0-next.4 - semver: 6.3.0 - string.prototype.matchall: 4.0.8 - dev: true - - /eslint-plugin-react@7.32.2(eslint@8.29.0): - resolution: {integrity: sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg==} - engines: {node: '>=4'} - peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 - dependencies: - array-includes: 3.1.6 - array.prototype.flatmap: 1.3.1 - array.prototype.tosorted: 1.1.1 - doctrine: 2.1.0 - eslint: 8.29.0 - estraverse: 5.3.0 - jsx-ast-utils: 3.3.3 - minimatch: 3.1.2 - object.entries: 1.1.6 - object.fromentries: 2.0.6 - object.hasown: 1.1.2 - object.values: 1.1.6 - prop-types: 15.8.1 - resolve: 2.0.0-next.4 - semver: 6.3.0 - string.prototype.matchall: 4.0.8 - dev: false + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 - /eslint-plugin-react@7.32.2(eslint@8.38.0): - resolution: {integrity: sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg==} + eslint-plugin-react@7.37.2: + resolution: {integrity: sha512-EsTAnj9fLVr/GZleBLFbj/sSuXeWmp1eXIN60ceYnZveqEaUCyW4X+Vh4WTdUhCkW4xutXYqTXCUSyqD4rB75w==} engines: {node: '>=4'} peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 - dependencies: - array-includes: 3.1.6 - array.prototype.flatmap: 1.3.1 - array.prototype.tosorted: 1.1.1 - doctrine: 2.1.0 - eslint: 8.38.0 - estraverse: 5.3.0 - jsx-ast-utils: 3.3.3 - minimatch: 3.1.2 - object.entries: 1.1.6 - object.fromentries: 2.0.6 - object.hasown: 1.1.2 - object.values: 1.1.6 - prop-types: 15.8.1 - resolve: 2.0.0-next.4 - semver: 6.3.0 - string.prototype.matchall: 4.0.8 - dev: false + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 - /eslint-plugin-testing-library@5.10.2(eslint@8.38.0)(typescript@4.9.5): - resolution: {integrity: sha512-f1DmDWcz5SDM+IpCkEX0lbFqrrTs8HRsEElzDEqN/EBI0hpRj8Cns5+IVANXswE8/LeybIJqPAOQIFu2j5Y5sw==} + eslint-plugin-testing-library@5.11.1: + resolution: {integrity: sha512-5eX9e1Kc2PqVRed3taaLnAAqPZGEX75C+M/rXzUAI3wIg/ZxzUm1OVAwfe/O+vE+6YXOLetSe9g5GKD2ecXipw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0, npm: '>=6'} peerDependencies: eslint: ^7.5.0 || ^8.0.0 - dependencies: - '@typescript-eslint/utils': 5.58.0(eslint@8.38.0)(typescript@4.9.5) - eslint: 8.38.0 - transitivePeerDependencies: - - supports-color - - typescript - dev: false - /eslint-plugin-unused-imports@2.0.0(eslint@8.24.0): - resolution: {integrity: sha512-3APeS/tQlTrFa167ThtP0Zm0vctjr4M44HMpeg1P4bK6wItarumq0Ma82xorMKdFsWpphQBlRPzw/pxiVELX1A==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + eslint-plugin-unused-imports@4.1.4: + resolution: {integrity: sha512-YptD6IzQjDardkl0POxnnRBhU1OEePMV0nd6siHaRBbd+lyh6NAhFEobiznKU7kTsSsDeSD62Pe7kAM1b7dAZQ==} peerDependencies: - '@typescript-eslint/eslint-plugin': ^5.0.0 - eslint: ^8.0.0 + '@typescript-eslint/eslint-plugin': ^8.0.0-0 || ^7.0.0 || ^6.0.0 || ^5.0.0 + eslint: ^9.0.0 || ^8.0.0 peerDependenciesMeta: '@typescript-eslint/eslint-plugin': optional: true - dependencies: - eslint: 8.24.0 - eslint-rule-composer: 0.3.0 - dev: true - - /eslint-rule-composer@0.3.0: - resolution: {integrity: sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg==} - engines: {node: '>=4.0.0'} - dev: true - /eslint-scope@5.1.1: + eslint-scope@5.1.1: resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} engines: {node: '>=8.0.0'} - dependencies: - esrecurse: 4.3.0 - estraverse: 4.3.0 - /eslint-scope@7.1.1: - resolution: {integrity: sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==} + eslint-scope@7.2.2: + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - esrecurse: 4.3.0 - estraverse: 5.3.0 - - /eslint-utils@3.0.0(eslint@8.24.0): - resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} - engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} - peerDependencies: - eslint: '>=5' - dependencies: - eslint: 8.24.0 - eslint-visitor-keys: 2.1.0 - dev: true - - /eslint-utils@3.0.0(eslint@8.29.0): - resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} - engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} - peerDependencies: - eslint: '>=5' - dependencies: - eslint: 8.29.0 - eslint-visitor-keys: 2.1.0 - /eslint-visitor-keys@2.1.0: + eslint-visitor-keys@2.1.0: resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==} engines: {node: '>=10'} - /eslint-visitor-keys@3.4.0: - resolution: {integrity: sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ==} + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - /eslint-webpack-plugin@3.2.0(eslint@8.38.0)(webpack@5.78.0): + eslint-webpack-plugin@3.2.0: resolution: {integrity: sha512-avrKcGncpPbPSUHX6B3stNGzkKFto3eL+DKM4+VyMrVnhPc3vRczVlCq3uhuFOdRvDHTVXuzwk1ZKUrqDQHQ9w==} engines: {node: '>= 12.13.0'} peerDependencies: eslint: ^7.0.0 || ^8.0.0 webpack: ^5.0.0 - dependencies: - '@types/eslint': 8.37.0 - eslint: 8.38.0 - jest-worker: 28.1.3 - micromatch: 4.0.5 - normalize-path: 3.0.0 - schema-utils: 4.0.0 - webpack: 5.78.0(esbuild@0.16.3)(webpack-cli@5.0.1) - dev: false - /eslint@8.24.0: - resolution: {integrity: sha512-dWFaPhGhTAiPcCgm3f6LI2MBWbogMnTJzFBbhXVRQDJPkr9pGZvVjlVfXd+vyDcWPA2Ic9L2AXPIQM0+vk/cSQ==} + eslint@8.57.1: + resolution: {integrity: sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. hasBin: true - dependencies: - '@eslint/eslintrc': 1.4.1 - '@humanwhocodes/config-array': 0.10.7 - '@humanwhocodes/gitignore-to-minimatch': 1.0.2 - '@humanwhocodes/module-importer': 1.0.1 - ajv: 6.12.6 - chalk: 4.1.2 - cross-spawn: 7.0.3 - debug: 4.3.4 - doctrine: 3.0.0 - escape-string-regexp: 4.0.0 - eslint-scope: 7.1.1 - eslint-utils: 3.0.0(eslint@8.24.0) - eslint-visitor-keys: 3.4.0 - espree: 9.5.1 - esquery: 1.5.0 - esutils: 2.0.3 - fast-deep-equal: 3.1.3 - file-entry-cache: 6.0.1 - find-up: 5.0.0 - glob-parent: 6.0.2 - globals: 13.20.0 - globby: 11.1.0 - grapheme-splitter: 1.0.4 - ignore: 5.2.4 - import-fresh: 3.3.0 - imurmurhash: 0.1.4 - is-glob: 4.0.3 - js-sdsl: 4.4.0 - js-yaml: 4.1.0 - json-stable-stringify-without-jsonify: 1.0.1 - levn: 0.4.1 - lodash.merge: 4.6.2 - minimatch: 3.1.2 - natural-compare: 1.4.0 - optionator: 0.9.1 - regexpp: 3.2.0 - strip-ansi: 6.0.1 - strip-json-comments: 3.1.1 - text-table: 0.2.0 - transitivePeerDependencies: - - supports-color - dev: true - /eslint@8.29.0: - resolution: {integrity: sha512-isQ4EEiyUjZFbEKvEGJKKGBwXtvXX+zJbkVKCgTuB9t/+jUBcy8avhkEwWJecI15BkRkOYmvIM5ynbhRjEkoeg==} + espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - hasBin: true - dependencies: - '@eslint/eslintrc': 1.4.1 - '@humanwhocodes/config-array': 0.11.8 - '@humanwhocodes/module-importer': 1.0.1 - '@nodelib/fs.walk': 1.2.8 - ajv: 6.12.6 - chalk: 4.1.2 - cross-spawn: 7.0.3 - debug: 4.3.4 - doctrine: 3.0.0 - escape-string-regexp: 4.0.0 - eslint-scope: 7.1.1 - eslint-utils: 3.0.0(eslint@8.29.0) - eslint-visitor-keys: 3.4.0 - espree: 9.5.1 - esquery: 1.5.0 - esutils: 2.0.3 - fast-deep-equal: 3.1.3 - file-entry-cache: 6.0.1 - find-up: 5.0.0 - glob-parent: 6.0.2 - globals: 13.20.0 - grapheme-splitter: 1.0.4 - ignore: 5.2.4 - import-fresh: 3.3.0 - imurmurhash: 0.1.4 - is-glob: 4.0.3 - is-path-inside: 3.0.3 - js-sdsl: 4.4.0 - js-yaml: 4.1.0 - json-stable-stringify-without-jsonify: 1.0.1 - levn: 0.4.1 - lodash.merge: 4.6.2 - minimatch: 3.1.2 - natural-compare: 1.4.0 - optionator: 0.9.1 - regexpp: 3.2.0 - strip-ansi: 6.0.1 - strip-json-comments: 3.1.1 - text-table: 0.2.0 - transitivePeerDependencies: - - supports-color - /eslint@8.38.0: - resolution: {integrity: sha512-pIdsD2jwlUGf/U38Jv97t8lq6HpaU/G9NKbYmpWpZGw3LdTNhZLbJePqxOXGB5+JEKfOPU/XLxYxFh03nr1KTg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + esprima@1.2.2: + resolution: {integrity: sha512-+JpPZam9w5DuJ3Q67SqsMGtiHKENSMRVoxvArfJZK01/BfLEObtZ6orJa/MtoGNR/rfMgp5837T41PAmTwAv/A==} + engines: {node: '>=0.4.0'} hasBin: true - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.38.0) - '@eslint-community/regexpp': 4.5.0 - '@eslint/eslintrc': 2.0.2 - '@eslint/js': 8.38.0 - '@humanwhocodes/config-array': 0.11.8 - '@humanwhocodes/module-importer': 1.0.1 - '@nodelib/fs.walk': 1.2.8 - ajv: 6.12.6 - chalk: 4.1.2 - cross-spawn: 7.0.3 - debug: 4.3.4 - doctrine: 3.0.0 - escape-string-regexp: 4.0.0 - eslint-scope: 7.1.1 - eslint-visitor-keys: 3.4.0 - espree: 9.5.1 - esquery: 1.5.0 - esutils: 2.0.3 - fast-deep-equal: 3.1.3 - file-entry-cache: 6.0.1 - find-up: 5.0.0 - glob-parent: 6.0.2 - globals: 13.20.0 - grapheme-splitter: 1.0.4 - ignore: 5.2.4 - import-fresh: 3.3.0 - imurmurhash: 0.1.4 - is-glob: 4.0.3 - is-path-inside: 3.0.3 - js-sdsl: 4.4.0 - js-yaml: 4.1.0 - json-stable-stringify-without-jsonify: 1.0.1 - levn: 0.4.1 - lodash.merge: 4.6.2 - minimatch: 3.1.2 - natural-compare: 1.4.0 - optionator: 0.9.1 - strip-ansi: 6.0.1 - strip-json-comments: 3.1.1 - text-table: 0.2.0 - transitivePeerDependencies: - - supports-color - dev: false - - /espree@9.5.1: - resolution: {integrity: sha512-5yxtHSZXRSW5pvv3hAlXM5+/Oswi1AUFqBmbibKb5s6bp3rGIDkyXU6xCoyuuLhijr4SFwPrXRoZjz0AZDN9tg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - acorn: 8.8.2 - acorn-jsx: 5.3.2(acorn@8.8.2) - eslint-visitor-keys: 3.4.0 - /esprima@4.0.1: + esprima@4.0.1: resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} engines: {node: '>=4'} hasBin: true - /esquery@1.5.0: - resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} + esquery@1.6.0: + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} engines: {node: '>=0.10'} - dependencies: - estraverse: 5.3.0 - /esrecurse@4.3.0: + esrecurse@4.3.0: resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} engines: {node: '>=4.0'} - dependencies: - estraverse: 5.3.0 - /estraverse@4.3.0: + estraverse@4.3.0: resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} engines: {node: '>=4.0'} - /estraverse@5.3.0: + estraverse@5.3.0: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} - /estree-util-attach-comments@2.1.1: - resolution: {integrity: sha512-+5Ba/xGGS6mnwFbXIuQiDPTbuTxuMCooq3arVv7gPZtYpjp+VXH/NkHAP35OOefPhNG/UGqU3vt/LTABwcHX0w==} - dependencies: - '@types/estree': 1.0.1 - dev: true - - /estree-util-build-jsx@2.2.2: - resolution: {integrity: sha512-m56vOXcOBuaF+Igpb9OPAy7f9w9OIkb5yhjsZuaPm7HoGi4oTOQi0h2+yZ+AtKklYFZ+rPC4n0wYCJCEU1ONqg==} - dependencies: - '@types/estree-jsx': 1.0.0 - estree-util-is-identifier-name: 2.1.0 - estree-walker: 3.0.3 - dev: true - - /estree-util-is-identifier-name@1.1.0: - resolution: {integrity: sha512-OVJZ3fGGt9By77Ix9NhaRbzfbDV/2rx9EP7YIDJTmsZSEc5kYn2vWcNccYyahJL2uAQZK2a5Or2i0wtIKTPoRQ==} - dev: true - - /estree-util-is-identifier-name@2.1.0: - resolution: {integrity: sha512-bEN9VHRyXAUOjkKVQVvArFym08BTWB0aJPppZZr0UNyAqWsLaVfAqP7hbaTJjzHifmB5ebnR8Wm7r7yGN/HonQ==} - dev: true - - /estree-util-value-to-estree@1.3.0: - resolution: {integrity: sha512-Y+ughcF9jSUJvncXwqRageavjrNPAI+1M/L3BI3PyLp1nmgYTGUXU6t5z1Y7OWuThoDdhPME07bQU+d5LxdJqw==} - engines: {node: '>=12.0.0'} - dependencies: - is-plain-obj: 3.0.0 - dev: true - - /estree-util-visit@1.2.1: - resolution: {integrity: sha512-xbgqcrkIVbIG+lI/gzbvd9SGTJL4zqJKBFttUl5pP27KhAjtMKbX/mQXJ7qgyXpMgVy/zvpm0xoQQaGL8OloOw==} - dependencies: - '@types/estree-jsx': 1.0.0 - '@types/unist': 2.0.6 - dev: true - - /estree-walker@0.6.1: + estree-walker@0.6.1: resolution: {integrity: sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==} - dev: true - /estree-walker@1.0.1: + estree-walker@1.0.1: resolution: {integrity: sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==} - /estree-walker@2.0.2: + estree-walker@2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} - dev: true - /estree-walker@3.0.3: - resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} - dependencies: - '@types/estree': 1.0.1 - dev: true - - /esutils@2.0.3: + esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} - /etag@1.8.1: + etag@1.8.1: resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} engines: {node: '>= 0.6'} - /eval@0.1.6: - resolution: {integrity: sha512-o0XUw+5OGkXw4pJZzQoXUk+H87DHuC+7ZE//oSrRGtatTmr12oTnLfg6QOq9DyTt0c/p4TwzgmkKrBzWTSizyQ==} - engines: {node: '>= 0.8'} - dependencies: - require-like: 0.1.2 - dev: true - - /event-target-shim@5.0.1: + event-target-shim@5.0.1: resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} engines: {node: '>=6'} - dev: false - /eventemitter3@4.0.7: + eventemitter3@4.0.7: resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} - /events@3.3.0: + events@3.3.0: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} - /execa@5.1.1: + execa@5.1.1: resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} engines: {node: '>=10'} - dependencies: - cross-spawn: 7.0.3 - get-stream: 6.0.1 - human-signals: 2.1.0 - is-stream: 2.0.1 - merge-stream: 2.0.0 - npm-run-path: 4.0.1 - onetime: 5.1.2 - signal-exit: 3.0.7 - strip-final-newline: 2.0.0 - - /exit-hook@2.2.1: - resolution: {integrity: sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw==} - engines: {node: '>=6'} - dev: true - /exit@0.1.2: + exit@0.1.2: resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} engines: {node: '>= 0.8.0'} - /expand-template@2.0.3: + expand-template@2.0.3: resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} engines: {node: '>=6'} - dev: false - /expect@27.5.1: + expect@27.5.1: resolution: {integrity: sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - dependencies: - '@jest/types': 27.5.1 - jest-get-type: 27.5.1 - jest-matcher-utils: 27.5.1 - jest-message-util: 27.5.1 - dev: false - /expect@28.1.3: + expect@28.1.3: resolution: {integrity: sha512-eEh0xn8HlsuOBxFgIss+2mX85VAS4Qy3OSkjV7rlBWljtA4oWH37glVGyOZSZvErDT/yBywZdPGwCXuTvSG85g==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dependencies: - '@jest/expect-utils': 28.1.3 - jest-get-type: 28.0.2 - jest-matcher-utils: 28.1.3 - jest-message-util: 28.1.3 - jest-util: 28.1.3 - dev: true - /expect@29.5.0: - resolution: {integrity: sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg==} + expect@29.7.0: + resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/expect-utils': 29.5.0 - jest-get-type: 29.4.3 - jest-matcher-utils: 29.5.0 - jest-message-util: 29.5.0 - jest-util: 29.5.0 - dev: true - /express@4.18.2: - resolution: {integrity: sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==} + exponential-backoff@3.1.1: + resolution: {integrity: sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==} + + express@4.21.2: + resolution: {integrity: sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==} engines: {node: '>= 0.10.0'} - dependencies: - accepts: 1.3.8 - array-flatten: 1.1.1 - body-parser: 1.20.1 - content-disposition: 0.5.4 - content-type: 1.0.5 - cookie: 0.5.0 - cookie-signature: 1.0.6 - debug: 2.6.9 - depd: 2.0.0 - encodeurl: 1.0.2 - escape-html: 1.0.3 - etag: 1.8.1 - finalhandler: 1.2.0 - fresh: 0.5.2 - http-errors: 2.0.0 - merge-descriptors: 1.0.1 - methods: 1.1.2 - on-finished: 2.4.1 - parseurl: 1.3.3 - path-to-regexp: 0.1.7 - proxy-addr: 2.0.7 - qs: 6.11.0 - range-parser: 1.2.1 - safe-buffer: 5.2.1 - send: 0.18.0 - serve-static: 1.15.0 - setprototypeof: 1.2.0 - statuses: 2.0.1 - type-is: 1.6.18 - utils-merge: 1.0.1 - vary: 1.1.2 - transitivePeerDependencies: - - supports-color - /extend@3.0.2: + exsolve@1.0.7: + resolution: {integrity: sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==} + + extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} - /external-editor@3.1.0: + external-editor@3.1.0: resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} engines: {node: '>=4'} - dependencies: - chardet: 0.7.0 - iconv-lite: 0.4.24 - tmp: 0.0.33 - /fast-deep-equal@3.1.3: + extsprintf@1.3.0: + resolution: {integrity: sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==} + engines: {'0': node >=0.6.0} + + farmhash-modern@1.1.0: + resolution: {integrity: sha512-6ypT4XfgqJk/F3Yuv4SX26I3doUjt0GTG4a+JgWxXQpxXzTBq8fPUeGHfcYMMDPHJHm3yPOSjaeBwBGAHWXCdA==} + engines: {node: '>=18.0.0'} + + fast-deep-equal@2.0.1: + resolution: {integrity: sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w==} + + fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - /fast-glob@3.2.11: - resolution: {integrity: sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==} + fast-equals@5.0.1: + resolution: {integrity: sha512-WF1Wi8PwwSY7/6Kx0vKXtw8RwuSGoM1bvDaJbu7MxDlR1vovZjIAKrnzyrThgAjm6JDTu0fVgWXDlMGspodfoQ==} + engines: {node: '>=6.0.0'} + + fast-glob@3.3.1: + resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==} engines: {node: '>=8.6.0'} - dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.5 - dev: true - /fast-glob@3.2.12: - resolution: {integrity: sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==} + fast-glob@3.3.2: + resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} engines: {node: '>=8.6.0'} - dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.5 - /fast-json-stable-stringify@2.1.0: + fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} - /fast-levenshtein@2.0.6: + fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - /fast-loops@1.1.3: - resolution: {integrity: sha512-8EZzEP0eKkEEVX+drtd9mtuQ+/QrlfW/5MlwcwK5Nds6EkZ/tRzEexkzUY2mIssnAyVLT+TKHuRXmFNNXYUd6g==} - dev: false - - /fast-shallow-equal@1.0.0: + fast-shallow-equal@1.0.0: resolution: {integrity: sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw==} - dev: false - /fast-text-encoding@1.0.6: + fast-text-encoding@1.0.6: resolution: {integrity: sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w==} - dev: false - /fast-xml-parser@4.1.2: - resolution: {integrity: sha512-CDYeykkle1LiA/uqQyNwYpFbyF6Axec6YapmpUP+/RHWIoR1zKjocdvNaTsxCxZzQ6v9MLXaSYm9Qq0thv0DHg==} + fast-uri@3.0.3: + resolution: {integrity: sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==} + + fast-xml-parser@4.4.1: + resolution: {integrity: sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==} + hasBin: true + + fast-xml-parser@4.5.0: + resolution: {integrity: sha512-/PlTQCI96+fZMAOLMZK4CWG1ItCbfZ/0jx7UIJFChPNrx7tcEgerUgWbeieCM9MfHInUDyK8DWYZ+YrywDJuTg==} hasBin: true - dependencies: - strnum: 1.0.5 - dev: false - /fastest-levenshtein@1.0.16: + fastest-levenshtein@1.0.16: resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==} engines: {node: '>= 4.9.1'} - /fastest-stable-stringify@2.0.2: + fastest-stable-stringify@2.0.2: resolution: {integrity: sha512-bijHueCGd0LqqNK9b5oCMHc0MluJAx0cwqASgbWMvkO01lCYgIhacVRLcaDz3QnyYIRNJRDwMb41VuT6pHJ91Q==} - dev: false - - /fastparse@1.1.2: - resolution: {integrity: sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==} - dev: true - - /fastq@1.15.0: - resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} - dependencies: - reusify: 1.0.4 - /fault@2.0.1: - resolution: {integrity: sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==} - dependencies: - format: 0.2.2 - dev: true + fastq@1.17.1: + resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} - /faye-websocket@0.11.4: + faye-websocket@0.11.4: resolution: {integrity: sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==} engines: {node: '>=0.8.0'} - dependencies: - websocket-driver: 0.7.4 - /fb-watchman@2.0.2: + fb-watchman@2.0.2: resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} - dependencies: - bser: 2.1.1 - /fbemitter@3.0.0: + fbemitter@3.0.0: resolution: {integrity: sha512-KWKaceCwKQU0+HPoop6gn4eOHk50bBv/VxjJtGMfwmJt3D29JpN4H4eisCtIPA+a8GVBam+ldMMpMjJUvpDyHw==} - dependencies: - fbjs: 3.0.4 - transitivePeerDependencies: - - encoding - dev: false - /fbjs-css-vars@1.0.2: + fbjs-css-vars@1.0.2: resolution: {integrity: sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==} - dev: false - /fbjs@3.0.4: - resolution: {integrity: sha512-ucV0tDODnGV3JCnnkmoszb5lf4bNpzjv80K41wd4k798Etq+UYD0y0TIfalLjZoKgjive6/adkRnszwapiDgBQ==} - dependencies: - cross-fetch: 3.1.5 - fbjs-css-vars: 1.0.2 - loose-envify: 1.4.0 - object-assign: 4.1.1 - promise: 7.3.1 - setimmediate: 1.0.5 - ua-parser-js: 0.7.35 - transitivePeerDependencies: - - encoding - dev: false + fbjs@3.0.5: + resolution: {integrity: sha512-ztsSx77JBtkuMrEypfhgc3cI0+0h+svqeie7xHbh1k/IKdcydnvadp/mUaGgjAOXQmQSxsqgaRhS3q9fy+1kxg==} + + fdir@6.4.2: + resolution: {integrity: sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true - /fetch-blob@3.2.0: + fetch-blob@3.2.0: resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} engines: {node: ^12.20 || >= 14.13} - dependencies: - node-domexception: 1.0.0 - web-streams-polyfill: 3.2.1 - /figures@1.7.0: + figlet@1.8.0: + resolution: {integrity: sha512-chzvGjd+Sp7KUvPHZv6EXV5Ir3Q7kYNpCr4aHrRW79qFtTefmQZNny+W1pW9kf5zeE6dikku2W50W/wAH2xWgw==} + engines: {node: '>= 0.4.0'} + hasBin: true + + figures@1.7.0: resolution: {integrity: sha512-UxKlfCRuCBxSXU4C6t9scbDyWZ4VlaFFdojKtzJuSkuOBQ5CNFum+zZXFwHjo+CxBC1t6zlYPgHIgFjL8ggoEQ==} engines: {node: '>=0.10.0'} - dependencies: - escape-string-regexp: 1.0.5 - object-assign: 4.1.1 - dev: true - /figures@3.2.0: + figures@3.2.0: resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} engines: {node: '>=8'} - dependencies: - escape-string-regexp: 1.0.5 - /file-entry-cache@6.0.1: + file-entry-cache@6.0.1: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} engines: {node: ^10.12.0 || >=12.0.0} - dependencies: - flat-cache: 3.0.4 - /file-loader@6.2.0(webpack@5.78.0): + file-loader@6.2.0: resolution: {integrity: sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==} engines: {node: '>= 10.13.0'} peerDependencies: webpack: ^4.0.0 || ^5.0.0 - dependencies: - loader-utils: 2.0.4 - schema-utils: 3.1.1 - webpack: 5.78.0(esbuild@0.16.3)(webpack-cli@5.0.1) - dev: false - /file-uri-to-path@1.0.0: + file-uri-to-path@1.0.0: resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} - dev: true - - /file-uri-to-path@2.0.0: - resolution: {integrity: sha512-hjPFI8oE/2iQPVe4gbrJ73Pp+Xfub2+WI2LlXDbsaJBwT5wuMh35WNWVYYTpnz895shtwfyutMFLFywpQAFdLg==} - engines: {node: '>= 6'} - dev: true - /filelist@1.0.4: + filelist@1.0.4: resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} - dependencies: - minimatch: 5.1.6 - /filesize@6.4.0: + filesize@6.4.0: resolution: {integrity: sha512-mjFIpOHC4jbfcTfoh4rkWpI31mF7viw9ikj/JyLoKzqlwG/YsefKfvYlYhdYdg/9mtK2z1AzgN/0LvVQ3zdlSQ==} engines: {node: '>= 0.4.0'} - dev: true - /filesize@8.0.7: + filesize@8.0.7: resolution: {integrity: sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==} engines: {node: '>= 0.4.0'} - dev: false - /fill-range@7.0.1: - resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} - dependencies: - to-regex-range: 5.0.1 - /finalhandler@1.2.0: - resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==} + finalhandler@1.3.1: + resolution: {integrity: sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==} engines: {node: '>= 0.8'} - dependencies: - debug: 2.6.9 - encodeurl: 1.0.2 - escape-html: 1.0.3 - on-finished: 2.4.1 - parseurl: 1.3.3 - statuses: 2.0.1 - unpipe: 1.0.0 - transitivePeerDependencies: - - supports-color - /find-cache-dir@3.3.2: + find-cache-dir@3.3.2: resolution: {integrity: sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==} engines: {node: '>=8'} - dependencies: - commondir: 1.0.1 - make-dir: 3.1.0 - pkg-dir: 4.2.0 - /find-free-port@2.0.0: + find-cache-dir@4.0.0: + resolution: {integrity: sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==} + engines: {node: '>=14.16'} + + find-free-port@2.0.0: resolution: {integrity: sha512-J1j8gfEVf5FN4PR5w5wrZZ7NYs2IvqsHcd03cAeQx3Ec/mo+lKceaVNhpsRKoZpZKbId88o8qh+dwUwzBV6WCg==} - dev: true - /find-up@3.0.0: + find-up@3.0.0: resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==} engines: {node: '>=6'} - dependencies: - locate-path: 3.0.0 - dev: false - /find-up@4.1.0: + find-up@4.1.0: resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} engines: {node: '>=8'} - dependencies: - locate-path: 5.0.0 - path-exists: 4.0.0 - /find-up@5.0.0: + find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} - dependencies: - locate-path: 6.0.0 - path-exists: 4.0.0 - /find-yarn-workspace-root2@1.2.16: + find-up@6.3.0: + resolution: {integrity: sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + find-yarn-workspace-root2@1.2.16: resolution: {integrity: sha512-hr6hb1w8ePMpPVUK39S4RlwJzi+xPLuVuG8XlwXU3KD5Yn3qgBWVfy3AzNlDhWvE1EORCE65/Qm26rFQt3VLVA==} - dependencies: - micromatch: 4.0.5 - pkg-dir: 4.2.0 - /firebase-admin@11.6.0: - resolution: {integrity: sha512-Kvjs+u/EHs+8B4pNOehbXkazEVMNmTQjkz+B5vUIvWWWR44GOiZj4lBf/hM5An4k3rFkBfO6G4s0kWi7QMOm3g==} - engines: {node: '>=14'} - dependencies: - '@fastify/busboy': 1.2.1 - '@firebase/database-compat': 0.3.4 - '@firebase/database-types': 0.10.4 - '@types/node': 18.15.11 - jsonwebtoken: 9.0.0 - jwks-rsa: 3.0.1 - node-forge: 1.3.1 - uuid: 9.0.0 - optionalDependencies: - '@google-cloud/firestore': 6.5.0 - '@google-cloud/storage': 6.9.5 - transitivePeerDependencies: - - encoding - - supports-color - dev: false - - /firebase@9.19.1: - resolution: {integrity: sha512-MeukV4NIk6usV1ZbnoA36gumK62JzxOZmF6OZyqkFwJ7edpAEyYNZXvExQlFsvnx2ery0UwNIvu4pKIZS3HiEQ==} - dependencies: - '@firebase/analytics': 0.9.5(@firebase/app@0.9.7) - '@firebase/analytics-compat': 0.2.5(@firebase/app-compat@0.2.7)(@firebase/app@0.9.7) - '@firebase/app': 0.9.7 - '@firebase/app-check': 0.6.4(@firebase/app@0.9.7) - '@firebase/app-check-compat': 0.3.4(@firebase/app-compat@0.2.7)(@firebase/app@0.9.7) - '@firebase/app-compat': 0.2.7 - '@firebase/app-types': 0.9.0 - '@firebase/auth': 0.22.0(@firebase/app@0.9.7) - '@firebase/auth-compat': 0.3.7(@firebase/app-compat@0.2.7)(@firebase/app-types@0.9.0)(@firebase/app@0.9.7) - '@firebase/database': 0.14.4 - '@firebase/database-compat': 0.3.4 - '@firebase/firestore': 3.10.0(@firebase/app@0.9.7) - '@firebase/firestore-compat': 0.3.6(@firebase/app-compat@0.2.7)(@firebase/app-types@0.9.0)(@firebase/app@0.9.7) - '@firebase/functions': 0.9.4(@firebase/app@0.9.7) - '@firebase/functions-compat': 0.3.4(@firebase/app-compat@0.2.7)(@firebase/app@0.9.7) - '@firebase/installations': 0.6.4(@firebase/app@0.9.7) - '@firebase/installations-compat': 0.2.4(@firebase/app-compat@0.2.7)(@firebase/app-types@0.9.0)(@firebase/app@0.9.7) - '@firebase/messaging': 0.12.4(@firebase/app@0.9.7) - '@firebase/messaging-compat': 0.2.4(@firebase/app-compat@0.2.7)(@firebase/app@0.9.7) - '@firebase/performance': 0.6.4(@firebase/app@0.9.7) - '@firebase/performance-compat': 0.2.4(@firebase/app-compat@0.2.7)(@firebase/app@0.9.7) - '@firebase/remote-config': 0.4.4(@firebase/app@0.9.7) - '@firebase/remote-config-compat': 0.2.4(@firebase/app-compat@0.2.7)(@firebase/app@0.9.7) - '@firebase/storage': 0.11.2(@firebase/app@0.9.7) - '@firebase/storage-compat': 0.3.2(@firebase/app-compat@0.2.7)(@firebase/app-types@0.9.0)(@firebase/app@0.9.7) - '@firebase/util': 1.9.3 - transitivePeerDependencies: - - encoding + firebase-admin@13.2.0: + resolution: {integrity: sha512-qQBTKo0QWCDaWwISry989pr8YfZSSk00rNCKaucjOgltEm3cCYzEe4rODqBd1uUwma+Iu5jtAzg89Nfsjr3fGg==} + engines: {node: '>=18'} + + firebase@11.4.0: + resolution: {integrity: sha512-Z6kwhWIPDgIm0+NUEQxwjH14hMP7t42WSFnf/78R0Vh59VovLYTOCTM3MIdY3jlSZ9uKz56FhXrvsNXNhAn/Xg==} - /first-chunk-stream@2.0.0: + first-chunk-stream@2.0.0: resolution: {integrity: sha512-X8Z+b/0L4lToKYq+lwnKqi9X/Zek0NibLpsJgVsSxpoYq7JtiCtRb5HqKVEjEw/qAb/4AKKRLOwwKHlWNpm2Eg==} engines: {node: '>=0.10.0'} - dependencies: - readable-stream: 2.3.8 - /flat-cache@3.0.4: - resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==} + flat-cache@3.2.0: + resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} engines: {node: ^10.12.0 || >=12.0.0} - dependencies: - flatted: 3.2.7 - rimraf: 3.0.2 - /flatted@3.2.7: - resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==} + flat@5.0.2: + resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} + hasBin: true + + flatted@3.3.1: + resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} - /flux@4.0.4(react@18.2.0): + flux@4.0.4: resolution: {integrity: sha512-NCj3XlayA2UsapRpM7va6wU1+9rE5FIL7qoMcmxWHRzbp0yujihMBm9BBHZ1MDIk5h5o2Bl6eGiCe8rYELAmYw==} peerDependencies: react: ^15.0.2 || ^16.0.0 || ^17.0.0 - dependencies: - fbemitter: 3.0.0 - fbjs: 3.0.4 - react: 18.2.0 - transitivePeerDependencies: - - encoding - dev: false - /follow-redirects@1.15.2: - resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==} + follow-redirects@1.15.9: + resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} engines: {node: '>=4.0'} peerDependencies: debug: '*' peerDependenciesMeta: debug: optional: true - dev: false - /for-each@0.3.3: + for-each@0.3.3: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} - dependencies: - is-callable: 1.2.7 - /fork-ts-checker-webpack-plugin@6.5.3(eslint@8.38.0)(typescript@4.9.5)(webpack@5.78.0): + foreground-child@3.3.0: + resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} + engines: {node: '>=14'} + + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + engines: {node: '>=14'} + + fork-ts-checker-webpack-plugin@6.5.3: resolution: {integrity: sha512-SbH/l9ikmMWycd5puHJKTkZJKddF4iRLyW3DeZ08HTI7NGyLS38MXd/KGgeWumQO7YNQbW2u/NtPT2YowbPaGQ==} engines: {node: '>=10', yarn: '>=1.0.0'} peerDependencies: @@ -13566,1789 +8470,991 @@ packages: optional: true vue-template-compiler: optional: true - dependencies: - '@babel/code-frame': 7.21.4 - '@types/json-schema': 7.0.11 - chalk: 4.1.2 - chokidar: 3.5.3 - cosmiconfig: 6.0.0 - deepmerge: 4.3.1 - eslint: 8.38.0 - fs-extra: 9.1.0 - glob: 7.2.3 - memfs: 3.5.0 - minimatch: 3.1.2 - schema-utils: 2.7.0 - semver: 7.5.0 - tapable: 1.1.3 - typescript: 4.9.5 - webpack: 5.78.0(esbuild@0.16.3)(webpack-cli@5.0.1) - dev: false - - /form-data@3.0.1: - resolution: {integrity: sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==} - engines: {node: '>= 6'} - dependencies: - asynckit: 0.4.0 - combined-stream: 1.0.8 - mime-types: 2.1.35 - dev: false - /form-data@4.0.0: - resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} + form-data@4.0.4: + resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} engines: {node: '>= 6'} - dependencies: - asynckit: 0.4.0 - combined-stream: 1.0.8 - mime-types: 2.1.35 - dev: false - - /format-util@1.0.5: - resolution: {integrity: sha512-varLbTj0e0yVyRpqQhuWV+8hlePAgaoFRhNFj50BNjEIrw1/DphHSObtqwskVCPWNgzwPoQrZAbfa/SBiicNeg==} - dev: false - /format@0.2.2: - resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==} - engines: {node: '>=0.4.x'} - dev: true - - /formdata-polyfill@4.0.10: + formdata-polyfill@4.0.10: resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} engines: {node: '>=12.20.0'} - dependencies: - fetch-blob: 3.2.0 - /forwarded@0.2.0: + forwarded@0.2.0: resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} engines: {node: '>= 0.6'} - /fraction.js@4.2.0: - resolution: {integrity: sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==} + fraction.js@4.3.7: + resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} + + framer-motion@12.7.5: + resolution: {integrity: sha512-iD+vBOLn8E8bwBAFUQ1DYXjivm+cGGPgQUQ4Doleq7YP/zHdozUVwAMBJwOOfCTbtM8uOooMi77noD261Kxiyw==} + peerDependencies: + '@emotion/is-prop-valid': '*' + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/is-prop-valid': + optional: true + react: + optional: true + react-dom: + optional: true - /fresh@0.5.2: + fresh@0.5.2: resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} engines: {node: '>= 0.6'} - /fs-constants@1.0.0: + fs-constants@1.0.0: resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} - /fs-extra@10.1.0: + fs-extra@10.1.0: resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} engines: {node: '>=12'} - dependencies: - graceful-fs: 4.2.11 - jsonfile: 6.1.0 - universalify: 2.0.0 - - /fs-extra@11.1.0: - resolution: {integrity: sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw==} - engines: {node: '>=14.14'} - dependencies: - graceful-fs: 4.2.11 - jsonfile: 6.1.0 - universalify: 2.0.0 - dev: true - - /fs-extra@8.1.0: - resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} - engines: {node: '>=6 <7 || >=8'} - dependencies: - graceful-fs: 4.2.11 - jsonfile: 4.0.0 - universalify: 0.1.2 - dev: true - /fs-extra@9.1.0: + fs-extra@9.1.0: resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==} engines: {node: '>=10'} - dependencies: - at-least-node: 1.0.0 - graceful-fs: 4.2.11 - jsonfile: 6.1.0 - universalify: 2.0.0 - dev: false - /fs-minipass@2.1.0: + fs-minipass@2.1.0: resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} engines: {node: '>= 8'} - dependencies: - minipass: 3.3.6 - /fs-monkey@1.0.3: - resolution: {integrity: sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==} - dev: false + fs-minipass@3.0.3: + resolution: {integrity: sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - /fs.realpath@1.0.0: + fs-monkey@1.0.6: + resolution: {integrity: sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg==} + + fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - /fsevents@2.3.2: + fsevents@2.3.2: resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] - requiresBuild: true - optional: true - /ftp@0.3.10: - resolution: {integrity: sha512-faFVML1aBx2UoDStmLwv2Wptt4vw5x03xxX172nhA5Y5HBshW5JweqQ2W4xL4dezQTG8inJsuYcpPHHU3X5OTQ==} - engines: {node: '>=0.8.0'} - dependencies: - readable-stream: 1.1.14 - xregexp: 2.0.0 - dev: true + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] - /function-bind@1.1.1: - resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - /function.prototype.name@1.1.5: - resolution: {integrity: sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==} + function.prototype.name@1.1.6: + resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 - functions-have-names: 1.2.3 - /functional-red-black-tree@1.0.1: + functional-red-black-tree@1.0.1: resolution: {integrity: sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==} - dev: false - optional: true - /functions-have-names@1.2.3: + functions-have-names@1.2.3: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} - /gauge@2.7.4: - resolution: {integrity: sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg==} - dependencies: - aproba: 1.2.0 - console-control-strings: 1.1.0 - has-unicode: 2.0.1 - object-assign: 4.1.1 - signal-exit: 3.0.7 - string-width: 1.0.2 - strip-ansi: 3.0.1 - wide-align: 1.1.5 - dev: false - - /gauge@3.0.2: + gauge@3.0.2: resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==} engines: {node: '>=10'} - dependencies: - aproba: 2.0.0 - color-support: 1.1.3 - console-control-strings: 1.1.0 - has-unicode: 2.0.1 - object-assign: 4.1.1 - signal-exit: 3.0.7 - string-width: 4.2.3 - strip-ansi: 6.0.1 - wide-align: 1.1.5 + deprecated: This package is no longer supported. - /gauge@4.0.4: + gauge@4.0.4: resolution: {integrity: sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - dependencies: - aproba: 2.0.0 - color-support: 1.1.3 - console-control-strings: 1.1.0 - has-unicode: 2.0.1 - signal-exit: 3.0.7 - string-width: 4.2.3 - strip-ansi: 6.0.1 - wide-align: 1.1.5 + deprecated: This package is no longer supported. - /gaxios@4.3.3: + gaxios@4.3.3: resolution: {integrity: sha512-gSaYYIO1Y3wUtdfHmjDUZ8LWaxJQpiavzbF5Kq53akSzvmVg0RfyOcFDbO1KJ/KCGRFz2qG+lS81F0nkr7cRJA==} engines: {node: '>=10'} - dependencies: - abort-controller: 3.0.0 - extend: 3.0.2 - https-proxy-agent: 5.0.1 - is-stream: 2.0.1 - node-fetch: 2.6.9 - transitivePeerDependencies: - - encoding - - supports-color - dev: false - /gaxios@5.1.0: - resolution: {integrity: sha512-aezGIjb+/VfsJtIcHGcBSerNEDdfdHeMros+RbYbGpmonKWQCOVOes0LVZhn1lDtIgq55qq0HaxymIoae3Fl/A==} - engines: {node: '>=12'} - dependencies: - extend: 3.0.2 - https-proxy-agent: 5.0.1 - is-stream: 2.0.1 - node-fetch: 2.6.9 - transitivePeerDependencies: - - encoding - - supports-color - dev: false - optional: true + gaxios@6.7.1: + resolution: {integrity: sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==} + engines: {node: '>=14'} - /gcp-metadata@4.3.1: + gcp-metadata@4.3.1: resolution: {integrity: sha512-x850LS5N7V1F3UcV7PoupzGsyD6iVwTVvsh3tbXfkctZnBnjW5yu5z1/3k3SehF7TyoTIe78rJs02GMMy+LF+A==} engines: {node: '>=10'} - dependencies: - gaxios: 4.3.3 - json-bigint: 1.0.0 - transitivePeerDependencies: - - encoding - - supports-color - dev: false - - /gcp-metadata@5.2.0: - resolution: {integrity: sha512-aFhhvvNycky2QyhG+dcfEdHBF0FRbYcf39s6WNHUDysKSrbJ5vuFbjydxBcmewtXeV248GP8dWT3ByPNxsyHCw==} - engines: {node: '>=12'} - dependencies: - gaxios: 5.1.0 - json-bigint: 1.0.0 - transitivePeerDependencies: - - encoding - - supports-color - dev: false - optional: true - /generic-names@2.0.1: - resolution: {integrity: sha512-kPCHWa1m9wGG/OwQpeweTwM/PYiQLrUIxXbt/P4Nic3LbGjCP0YwrALHW1uNLKZ0LIMg+RF+XRlj2ekT9ZlZAQ==} - dependencies: - loader-utils: 1.4.2 - dev: true + gcp-metadata@6.1.0: + resolution: {integrity: sha512-Jh/AIwwgaxan+7ZUUmRLCjtchyDiqh4KjBJ5tW3plBZb5iL/BPcso8A5DlzeD9qlw0duCamnNdpFjxwaT0KyKg==} + engines: {node: '>=14'} - /generic-names@4.0.0: + generic-names@4.0.0: resolution: {integrity: sha512-ySFolZQfw9FoDb3ed9d80Cm9f0+r7qj+HJkWjeD9RBfpxEVTlVhol+gvaQB/78WbwYfbnNh8nWHHBSlg072y6A==} - dependencies: - loader-utils: 3.2.1 - dev: true - /gensync@1.0.0-beta.2: + gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} - /get-caller-file@2.0.5: + get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} - /get-intrinsic@1.2.0: - resolution: {integrity: sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==} - dependencies: - function-bind: 1.1.1 - has: 1.0.3 - has-symbols: 1.0.3 + get-east-asian-width@1.3.0: + resolution: {integrity: sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==} + engines: {node: '>=18'} + + get-intrinsic@1.2.4: + resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} + engines: {node: '>= 0.4'} - /get-own-enumerable-property-symbols@3.0.2: + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-nonce@1.0.1: + resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} + engines: {node: '>=6'} + + get-own-enumerable-property-symbols@3.0.2: resolution: {integrity: sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==} - dev: false - /get-package-type@0.1.0: + get-package-type@0.1.0: resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} engines: {node: '>=8.0.0'} - /get-port@5.1.1: + get-port@5.1.1: resolution: {integrity: sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==} engines: {node: '>=8'} - dev: true - /get-stream@5.2.0: - resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} - engines: {node: '>=8'} - dependencies: - pump: 3.0.0 - dev: true + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} - /get-stream@6.0.1: + get-stream@6.0.1: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} engines: {node: '>=10'} - /get-symbol-description@1.0.0: - resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==} + get-symbol-description@1.0.2: + resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.0 - /get-tsconfig@4.5.0: - resolution: {integrity: sha512-MjhiaIWCJ1sAU4pIQ5i5OfOuHHxVo1oYeNsWTON7jxYkod8pHocXeh+SSbmu5OZZZK73B6cbJ2XADzXehLyovQ==} - dev: true + get-tsconfig@4.8.1: + resolution: {integrity: sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==} - /get-uri@3.0.2: - resolution: {integrity: sha512-+5s0SJbGoyiJTZZ2JTpFPLMPSch72KEqGOTvQsBqg0RBWvwhWUSYZFAtz3TPW0GXJuLBJPts1E241iHg+VRfhg==} - engines: {node: '>= 6'} - dependencies: - '@tootallnate/once': 1.1.2 - data-uri-to-buffer: 3.0.1 - debug: 4.3.4 - file-uri-to-path: 2.0.0 - fs-extra: 8.1.0 - ftp: 0.3.10 - transitivePeerDependencies: - - supports-color - dev: true - - /git-hooks-list@1.0.3: - resolution: {integrity: sha512-Y7wLWcrLUXwk2noSka166byGCvhMtDRpgHdzCno1UQv/n/Hegp++a2xBWJL1lJarnKD3SWaljD+0z1ztqxuKyQ==} - dev: true - - /github-from-package@0.0.0: + github-from-package@0.0.0: resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} - dev: false - /github-username@6.0.0: + github-username@6.0.0: resolution: {integrity: sha512-7TTrRjxblSI5l6adk9zd+cV5d6i1OrJSo3Vr9xdGqFLBQo0mz5P9eIfKCDJ7eekVGGFLbce0qbPSnktXV2BjDQ==} engines: {node: '>=10'} - dependencies: - '@octokit/rest': 18.12.0 - transitivePeerDependencies: - - encoding - /glob-parent@5.1.2: + glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} - dependencies: - is-glob: 4.0.3 - /glob-parent@6.0.2: + glob-parent@6.0.2: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} - dependencies: - is-glob: 4.0.3 - /glob-to-regexp@0.4.1: + glob-to-regexp@0.4.1: resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} - /glob@7.1.6: - resolution: {integrity: sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==} - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.2 - once: 1.4.0 - path-is-absolute: 1.0.1 + glob@10.4.5: + resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + hasBin: true - /glob@7.1.7: - resolution: {integrity: sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==} - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.2 - once: 1.4.0 - path-is-absolute: 1.0.1 + glob@11.0.3: + resolution: {integrity: sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==} + engines: {node: 20 || >=22} + hasBin: true - /glob@7.2.3: + glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.2 - once: 1.4.0 - path-is-absolute: 1.0.1 + deprecated: Glob versions prior to v9 are no longer supported - /glob@8.1.0: + glob@8.1.0: resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} engines: {node: '>=12'} - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 5.1.6 - once: 1.4.0 + deprecated: Glob versions prior to v9 are no longer supported - /global-modules@2.0.0: + global-modules@2.0.0: resolution: {integrity: sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==} engines: {node: '>=6'} - dependencies: - global-prefix: 3.0.0 - dev: false - /global-prefix@3.0.0: + global-prefix@3.0.0: resolution: {integrity: sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==} engines: {node: '>=6'} - dependencies: - ini: 1.3.8 - kind-of: 6.0.3 - which: 1.3.1 - dev: false - /globals@11.12.0: + globals@11.12.0: resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} engines: {node: '>=4'} - /globals@13.20.0: - resolution: {integrity: sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==} + globals@13.24.0: + resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} engines: {node: '>=8'} - dependencies: - type-fest: 0.20.2 - /globalthis@1.0.3: - resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==} + globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} engines: {node: '>= 0.4'} - dependencies: - define-properties: 1.2.0 - /globalyzer@0.1.0: + globalyzer@0.1.0: resolution: {integrity: sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==} - dev: true - - /globby@10.0.0: - resolution: {integrity: sha512-3LifW9M4joGZasyYPz2A1U74zbC/45fvpXUvO/9KbSa+VV0aGZarWkfdgKyR9sExNP0t0x0ss/UMJpNpcaTspw==} - engines: {node: '>=8'} - dependencies: - '@types/glob': 7.2.0 - array-union: 2.1.0 - dir-glob: 3.0.1 - fast-glob: 3.2.12 - glob: 7.2.3 - ignore: 5.2.4 - merge2: 1.4.1 - slash: 3.0.0 - dev: true - /globby@11.1.0: + globby@11.1.0: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} engines: {node: '>=10'} - dependencies: - array-union: 2.1.0 - dir-glob: 3.0.1 - fast-glob: 3.2.12 - ignore: 5.2.4 - merge2: 1.4.1 - slash: 3.0.0 - /globby@13.1.4: - resolution: {integrity: sha512-iui/IiiW+QrJ1X1hKH5qwlMQyv34wJAYwH1vrf8b9kBA4sNiif3gKsMHa+BrdnOpEudWjpotfa7LrTzB1ERS/g==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dependencies: - dir-glob: 3.0.1 - fast-glob: 3.2.12 - ignore: 5.2.4 - merge2: 1.4.1 - slash: 4.0.0 - dev: true + globby@14.0.2: + resolution: {integrity: sha512-s3Fq41ZVh7vbbe2PN3nrW7yC7U7MFVc5c98/iTl9c2GawNMKx/J648KQRW6WKkuU8GIbbh2IXfIRQjOZnXcTnw==} + engines: {node: '>=18'} - /globby@6.1.0: + globby@6.1.0: resolution: {integrity: sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==} engines: {node: '>=0.10.0'} - dependencies: - array-union: 1.0.2 - glob: 7.2.3 - object-assign: 4.1.1 - pify: 2.3.0 - pinkie-promise: 2.0.1 - dev: true - /globrex@0.1.2: + globrex@0.1.2: resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} - dev: true - /google-auth-library@7.14.1: + google-ads-api@17.1.0-rest-beta: + resolution: {integrity: sha512-SgM8nECNkjUUVBtzpIySEsi1Ckl8MjiwAg9sh16UJgtnFeWX3318GSb1ThtSAWaXMjIhESai5LtaD8Dj03l2bw==} + + google-ads-node@14.0.0: + resolution: {integrity: sha512-rbgZkcaRSCQKnwRki4Jl2GY6Csd12a3ouX45xcK1ttFbJqtKrm8GPkctAIVnwAx0yVi8VMgpFWaEls8/i0SGJg==} + + google-auth-library@7.14.1: resolution: {integrity: sha512-5Rk7iLNDFhFeBYc3s8l1CqzbEBcdhwR193RlD4vSNFajIcINKI8W8P0JLmBpwymHqqWbX34pJDQu39cSy/6RsA==} engines: {node: '>=10'} - dependencies: - arrify: 2.0.1 - base64-js: 1.5.1 - ecdsa-sig-formatter: 1.0.11 - fast-text-encoding: 1.0.6 - gaxios: 4.3.3 - gcp-metadata: 4.3.1 - gtoken: 5.3.2 - jws: 4.0.0 - lru-cache: 6.0.0 - transitivePeerDependencies: - - encoding - - supports-color - dev: false - /google-auth-library@8.7.0: - resolution: {integrity: sha512-1M0NG5VDIvJZEnstHbRdckLZESoJwguinwN8Dhae0j2ZKIQFIV63zxm6Fo6nM4xkgqUr2bbMtV5Dgo+Hy6oo0Q==} - engines: {node: '>=12'} - dependencies: - arrify: 2.0.1 - base64-js: 1.5.1 - ecdsa-sig-formatter: 1.0.11 - fast-text-encoding: 1.0.6 - gaxios: 5.1.0 - gcp-metadata: 5.2.0 - gtoken: 6.1.2 - jws: 4.0.0 - lru-cache: 6.0.0 - transitivePeerDependencies: - - encoding - - supports-color - dev: false - optional: true + google-auth-library@9.14.2: + resolution: {integrity: sha512-R+FRIfk1GBo3RdlRYWPdwk8nmtVUOn6+BkDomAC46KoU8kzXzE1HLmOasSCbWUByMMAGkknVF0G5kQ69Vj7dlA==} + engines: {node: '>=14'} - /google-gax@3.6.0: - resolution: {integrity: sha512-2fyb61vWxUonHiArRNJQmE4tx5oY1ni8VPo08fzII409vDSCWG7apDX4qNOQ2GXXT82gLBn3d3P1Dydh7pWjyw==} - engines: {node: '>=12'} - hasBin: true - dependencies: - '@grpc/grpc-js': 1.8.13 - '@grpc/proto-loader': 0.7.6 - '@types/long': 4.0.2 - '@types/rimraf': 3.0.2 - abort-controller: 3.0.0 - duplexify: 4.1.2 - fast-text-encoding: 1.0.6 - google-auth-library: 8.7.0 - is-stream-ended: 0.1.4 - node-fetch: 2.6.9 - object-hash: 3.0.0 - proto3-json-serializer: 1.1.0 - protobufjs: 7.2.3 - protobufjs-cli: 1.1.1(protobufjs@7.2.3) - retry-request: 5.0.2 - transitivePeerDependencies: - - encoding - - supports-color - dev: false - optional: true + google-gax@4.4.1: + resolution: {integrity: sha512-Phyp9fMfA00J3sZbJxbbB4jC55b7DBjE3F6poyL3wKMEBVKA79q6BGuHcTiM28yOzVql0NDbRL8MLLh8Iwk9Dg==} + engines: {node: '>=14'} - /google-p12-pem@3.1.4: + google-p12-pem@3.1.4: resolution: {integrity: sha512-HHuHmkLgwjdmVRngf5+gSmpkyaRI6QmOg77J8tkNBHhNEI62sGHyw4/+UkgyZEI7h84NbWprXDJ+sa3xOYFvTg==} engines: {node: '>=10'} + deprecated: Package is no longer maintained hasBin: true - dependencies: - node-forge: 1.3.1 - dev: false - /google-p12-pem@4.0.1: - resolution: {integrity: sha512-WPkN4yGtz05WZ5EhtlxNDWPhC4JIic6G8ePitwUWy4l+XPVYec+a0j0Ts47PDtW59y3RwAhUd9/h9ZZ63px6RQ==} - engines: {node: '>=12.0.0'} - hasBin: true - dependencies: - node-forge: 1.3.1 - dev: false - optional: true - - /googleapis-common@5.1.0: - resolution: {integrity: sha512-RXrif+Gzhq1QAzfjxulbGvAY3FPj8zq/CYcvgjzDbaBNCD6bUl+86I7mUs4DKWHGruuK26ijjR/eDpWIDgNROA==} - engines: {node: '>=10.10.0'} - dependencies: - extend: 3.0.2 - gaxios: 4.3.3 - google-auth-library: 7.14.1 - qs: 6.11.1 - url-template: 2.0.8 - uuid: 8.3.2 - transitivePeerDependencies: - - encoding - - supports-color - dev: false + googleapis-common@7.2.0: + resolution: {integrity: sha512-/fhDZEJZvOV3X5jmD+fKxMqma5q2Q9nZNSF3kn1F18tpxmA86BcTxAGBQdM0N89Z3bEaIs+HVznSmFJEAmMTjA==} + engines: {node: '>=14.0.0'} - /googleapis@100.0.0: - resolution: {integrity: sha512-RToFQGY54B756IDbjdyjb1vWFmn03bYpXHB2lIf0eq2UBYsIbYOLZ0kqSomfJnpclEukwEmMF7Jn6Wsev871ew==} - engines: {node: '>=10'} - dependencies: - google-auth-library: 7.14.1 - googleapis-common: 5.1.0 - transitivePeerDependencies: - - encoding - - supports-color - dev: false + googleapis@126.0.1: + resolution: {integrity: sha512-4N8LLi+hj6ytK3PhE52KcM8iSGhJjtXnCDYB4fp6l+GdLbYz4FoDmx074WqMbl7iYMDN87vqD/8drJkhxW92mQ==} + engines: {node: '>=14.0.0'} - /gopd@1.0.1: + gopd@1.0.1: resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} - dependencies: - get-intrinsic: 1.2.0 - /got@11.8.6: - resolution: {integrity: sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==} - engines: {node: '>=10.19.0'} - dependencies: - '@sindresorhus/is': 4.6.0 - '@szmarczak/http-timer': 4.0.6 - '@types/cacheable-request': 6.0.3 - '@types/responselike': 1.0.0 - cacheable-lookup: 5.0.4 - cacheable-request: 7.0.2 - decompress-response: 6.0.0 - http2-wrapper: 1.0.3 - lowercase-keys: 2.0.0 - p-cancelable: 2.1.1 - responselike: 2.0.1 - dev: true + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} - /graceful-fs@4.2.11: + graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - /grapheme-splitter@1.0.4: - resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==} + graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - /grouped-queue@2.0.0: + grouped-queue@2.0.0: resolution: {integrity: sha512-/PiFUa7WIsl48dUeCvhIHnwNmAAzlI/eHoJl0vu3nsFA366JleY7Ff8EVTplZu5kO0MIdZjKTTnzItL61ahbnw==} engines: {node: '>=8.0.0'} - /gtoken@5.3.2: + gtoken@5.3.2: resolution: {integrity: sha512-gkvEKREW7dXWF8NV8pVrKfW7WqReAmjjkMBh6lNCCGOM4ucS0r0YyXXl0r/9Yj8wcW/32ISkfc8h5mPTDbtifQ==} engines: {node: '>=10'} - dependencies: - gaxios: 4.3.3 - google-p12-pem: 3.1.4 - jws: 4.0.0 - transitivePeerDependencies: - - encoding - - supports-color - dev: false - - /gtoken@6.1.2: - resolution: {integrity: sha512-4ccGpzz7YAr7lxrT2neugmXQ3hP9ho2gcaityLVkiUecAiwiy60Ii8gRbZeOsXV19fYaRjgBSshs8kXw+NKCPQ==} - engines: {node: '>=12.0.0'} - dependencies: - gaxios: 5.1.0 - google-p12-pem: 4.0.1 - jws: 4.0.0 - transitivePeerDependencies: - - encoding - - supports-color - dev: false - optional: true - /gunzip-maybe@1.4.2: - resolution: {integrity: sha512-4haO1M4mLO91PW57BMsDFf75UmwoRX0GkdD+Faw+Lr+r/OZrOCS0pIBwOL1xCKQqnQzbNFGgK2V2CpBUPeFNTw==} - hasBin: true - dependencies: - browserify-zlib: 0.1.4 - is-deflate: 1.0.0 - is-gzip: 1.0.0 - peek-stream: 1.1.3 - pumpify: 1.5.1 - through2: 2.0.5 - dev: true + gtoken@7.1.0: + resolution: {integrity: sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==} + engines: {node: '>=14.0.0'} - /gzip-size@3.0.0: + gzip-size@3.0.0: resolution: {integrity: sha512-6s8trQiK+OMzSaCSVXX+iqIcLV9tC+E73jrJrJTyS4h/AJhlxHvzFKqM1YLDJWRGgHX8uLkBeXkA0njNj39L4w==} engines: {node: '>=0.12.0'} - dependencies: - duplexer: 0.1.2 - dev: true - /gzip-size@5.1.1: - resolution: {integrity: sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA==} - engines: {node: '>=6'} - dependencies: - duplexer: 0.1.2 - pify: 4.0.1 - dev: true - - /gzip-size@6.0.0: + gzip-size@6.0.0: resolution: {integrity: sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==} engines: {node: '>=10'} - dependencies: - duplexer: 0.1.2 - dev: false - /handle-thing@2.0.1: + handle-thing@2.0.1: resolution: {integrity: sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==} - dev: false - /harmony-reflect@1.6.2: + harmony-reflect@1.6.2: resolution: {integrity: sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==} - dev: false - /has-ansi@2.0.0: + has-ansi@2.0.0: resolution: {integrity: sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==} engines: {node: '>=0.10.0'} - dependencies: - ansi-regex: 2.1.1 - dev: true - /has-bigints@1.0.2: + has-bigints@1.0.2: resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} - /has-flag@1.0.0: - resolution: {integrity: sha512-DyYHfIYwAJmjAjSSPKANxI8bFY9YtFrgkAfinBojQ8YJTOuOuav64tMUJv584SES4xl74PmuaevIyaLESHdTAA==} - engines: {node: '>=0.10.0'} - dev: true - - /has-flag@3.0.0: + has-flag@3.0.0: resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} engines: {node: '>=4'} - /has-flag@4.0.0: + has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} - /has-property-descriptors@1.0.0: - resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==} - dependencies: - get-intrinsic: 1.2.0 + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} - /has-proto@1.0.1: - resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==} + has-proto@1.0.3: + resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} engines: {node: '>= 0.4'} - /has-symbols@1.0.3: + has-symbols@1.0.3: resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} engines: {node: '>= 0.4'} - /has-tostringtag@1.0.0: - resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==} + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} engines: {node: '>= 0.4'} - dependencies: - has-symbols: 1.0.3 - /has-unicode@2.0.1: - resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} - /has@1.0.3: - resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} - engines: {node: '>= 0.4.0'} - dependencies: - function-bind: 1.1.1 + has-unicode@2.0.1: + resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} - /hast-util-to-estree@2.3.2: - resolution: {integrity: sha512-YYDwATNdnvZi3Qi84iatPIl1lWpXba1MeNrNbDfJfVzEBZL8uUmtR7mt7bxKBC8kuAuvb0bkojXYZzsNHyHCLg==} - dependencies: - '@types/estree': 1.0.1 - '@types/estree-jsx': 1.0.0 - '@types/hast': 2.3.4 - '@types/unist': 2.0.6 - comma-separated-tokens: 2.0.3 - estree-util-attach-comments: 2.1.1 - estree-util-is-identifier-name: 2.1.0 - hast-util-whitespace: 2.0.1 - mdast-util-mdx-expression: 1.3.2 - mdast-util-mdxjs-esm: 1.3.1 - property-information: 6.2.0 - space-separated-tokens: 2.0.2 - style-to-object: 0.4.1 - unist-util-position: 4.0.4 - zwitch: 2.0.4 - transitivePeerDependencies: - - supports-color - dev: true + hash.js@1.1.7: + resolution: {integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==} - /hast-util-whitespace@2.0.1: - resolution: {integrity: sha512-nAxA0v8+vXSBDt3AnRUNjyRIQ0rD+ntpbAp4LnPkumc5M9yUbSMa4XDU9Q6etY4f1Wp4bNgvc1yjiZtsTTrSng==} - dev: true + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} - /he@1.2.0: + he@1.2.0: resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} hasBin: true - dev: false - - /hex-color-regex@1.1.0: - resolution: {integrity: sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==} - dev: true - /highlight.js@11.7.0: - resolution: {integrity: sha512-1rRqesRFhMO/PRF+G86evnyJkCgaZFOI+Z6kdj15TA18funfoqJXvgPCLSf0SWq3SRfg1j3HlDs8o4s3EGq1oQ==} + highlight.js@11.10.0: + resolution: {integrity: sha512-SYVnVFswQER+zu1laSya563s+F8VDGt7o35d4utbamowvUNLLMovFqwCLSocpZTz3MgaSRA1IbqRWZv97dtErQ==} engines: {node: '>=12.0.0'} - dev: false - /hoist-non-react-statics@3.3.2: + hmac-drbg@1.0.1: + resolution: {integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==} + + hoist-non-react-statics@3.3.2: resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} - dependencies: - react-is: 16.13.1 - dev: false - /hoopy@0.1.4: + hoopy@0.1.4: resolution: {integrity: sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==} engines: {node: '>= 6.0.0'} - dev: false - /hosted-git-info@2.8.9: + hosted-git-info@2.8.9: resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} - /hosted-git-info@4.1.0: + hosted-git-info@4.1.0: resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==} engines: {node: '>=10'} - dependencies: - lru-cache: 6.0.0 - - /hpack.js@2.1.6: - resolution: {integrity: sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==} - dependencies: - inherits: 2.0.4 - obuf: 1.1.2 - readable-stream: 2.3.8 - wbuf: 1.7.3 - dev: false - /hsl-regex@1.0.0: - resolution: {integrity: sha512-M5ezZw4LzXbBKMruP+BNANf0k+19hDQMgpzBIYnya//Al+fjNct9Wf3b1WedLqdEs2hKBvxq/jh+DsHJLj0F9A==} - dev: true + hosted-git-info@6.1.1: + resolution: {integrity: sha512-r0EI+HBMcXadMrugk0GCQ+6BQV39PiWAZVfq7oIckeGiN7sjRGyQxPdft3nQekFTCQbYxLBH+/axZMeH8UX6+w==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - /hsla-regex@1.0.0: - resolution: {integrity: sha512-7Wn5GMLuHBjZCb2bTmnDOycho0p/7UVaAeqXZGbHrBCl6Yd/xDhQJAXe6Ga9AXJH2I5zY1dEdYw2u1UptnSBJA==} - dev: true + hpack.js@2.1.6: + resolution: {integrity: sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==} - /html-encoding-sniffer@2.0.1: + html-encoding-sniffer@2.0.1: resolution: {integrity: sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==} engines: {node: '>=10'} - dependencies: - whatwg-encoding: 1.0.5 - dev: false - /html-entities@2.3.3: - resolution: {integrity: sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==} - dev: false + html-entities@2.5.2: + resolution: {integrity: sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==} - /html-escaper@2.0.2: + html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} - /html-minifier-terser@6.1.0: + html-minifier-terser@6.1.0: resolution: {integrity: sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==} engines: {node: '>=12'} hasBin: true - dependencies: - camel-case: 4.1.2 - clean-css: 5.3.2 - commander: 8.3.0 - he: 1.2.0 - param-case: 3.0.4 - relateurl: 0.2.7 - terser: 5.16.9 - dev: false - /html-minifier@4.0.0: - resolution: {integrity: sha512-aoGxanpFPLg7MkIl/DDFYtb0iWz7jMFGqFhvEDZga6/4QTjneiD8I/NXL1x5aaoCp7FSIT6h/OhykDdPsbtMig==} - engines: {node: '>=6'} - hasBin: true - dependencies: - camel-case: 3.0.0 - clean-css: 4.2.4 - commander: 2.20.3 - he: 1.2.0 - param-case: 2.1.1 - relateurl: 0.2.7 - uglify-js: 3.17.4 - dev: false + html-to-text@9.0.5: + resolution: {integrity: sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg==} + engines: {node: '>=14'} - /html-webpack-plugin@5.5.0(webpack@5.78.0): - resolution: {integrity: sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw==} + html-webpack-plugin@5.6.3: + resolution: {integrity: sha512-QSf1yjtSAsmf7rYBV7XX86uua4W/vkhIt0xNXKbsi2foEeW7vjJQz4bhnpL3xH+l1ryl1680uNv968Z+X6jSYg==} engines: {node: '>=10.13.0'} peerDependencies: + '@rspack/core': 0.x || 1.x webpack: ^5.20.0 - dependencies: - '@types/html-minifier-terser': 6.1.0 - html-minifier-terser: 6.1.0 - lodash: 4.17.21 - pretty-error: 4.0.0 - tapable: 2.2.1 - webpack: 5.78.0(esbuild@0.16.3)(webpack-cli@5.0.1) - dev: false + peerDependenciesMeta: + '@rspack/core': + optional: true + webpack: + optional: true + + htmlencode@0.0.4: + resolution: {integrity: sha512-0uDvNVpzj/E2TfvLLyyXhKBRvF1y84aZsyRxRXFsQobnHaL4pcaXk+Y9cnFlvnxrBLeXDNq/VJBD+ngdBgQG1w==} + + htmlnano@2.1.1: + resolution: {integrity: sha512-kAERyg/LuNZYmdqgCdYvugyLWNFAm8MWXpQMz1pLpetmCbFwoMxvkSoaAMlFrOC4OKTWI4KlZGT/RsNxg4ghOw==} + peerDependencies: + cssnano: ^7.0.0 + postcss: ^8.4.47 + purgecss: ^6.0.0 + relateurl: ^0.2.7 + srcset: 5.0.1 + svgo: ^3.0.2 + terser: ^5.10.0 + uncss: ^0.17.3 + peerDependenciesMeta: + cssnano: + optional: true + postcss: + optional: true + purgecss: + optional: true + relateurl: + optional: true + srcset: + optional: true + svgo: + optional: true + terser: + optional: true + uncss: + optional: true - /htmlparser2@5.0.1: + htmlparser2@5.0.1: resolution: {integrity: sha512-vKZZra6CSe9qsJzh0BjBGXo8dvzNsq/oGvsjfRdOrrryfeD9UOBEEQdeoqCRmKZchF5h2zOBMQ6YuQ0uRUmdbQ==} - dependencies: - domelementtype: 2.3.0 - domhandler: 3.3.0 - domutils: 2.8.0 - entities: 2.2.0 - dev: false - /htmlparser2@6.1.0: + htmlparser2@6.1.0: resolution: {integrity: sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==} - dependencies: - domelementtype: 2.3.0 - domhandler: 4.3.1 - domutils: 2.8.0 - entities: 2.2.0 - dev: false - /htmlparser2@8.0.2: + htmlparser2@7.2.0: + resolution: {integrity: sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==} + + htmlparser2@8.0.2: resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==} - dependencies: - domelementtype: 2.3.0 - domhandler: 5.0.3 - domutils: 3.0.1 - entities: 4.4.0 - dev: false - /http-cache-semantics@4.1.1: + htmlparser2@9.1.0: + resolution: {integrity: sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==} + + http-cache-semantics@4.1.1: resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} - /http-deceiver@1.2.7: + http-deceiver@1.2.7: resolution: {integrity: sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==} - dev: false - /http-errors@1.6.3: + http-errors@1.6.3: resolution: {integrity: sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==} engines: {node: '>= 0.6'} - dependencies: - depd: 1.1.2 - inherits: 2.0.3 - setprototypeof: 1.1.0 - statuses: 1.5.0 - dev: false - /http-errors@2.0.0: + http-errors@2.0.0: resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} engines: {node: '>= 0.8'} - dependencies: - depd: 2.0.0 - inherits: 2.0.4 - setprototypeof: 1.2.0 - statuses: 2.0.1 - toidentifier: 1.0.1 - /http-parser-js@0.5.8: + http-parser-js@0.5.8: resolution: {integrity: sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==} - /http-proxy-agent@4.0.1: + http-proxy-agent@4.0.1: resolution: {integrity: sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==} engines: {node: '>= 6'} - dependencies: - '@tootallnate/once': 1.1.2 - agent-base: 6.0.2 - debug: 4.3.4 - transitivePeerDependencies: - - supports-color - /http-proxy-agent@5.0.0: + http-proxy-agent@5.0.0: resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==} engines: {node: '>= 6'} - dependencies: - '@tootallnate/once': 2.0.0 - agent-base: 6.0.2 - debug: 4.3.4 - transitivePeerDependencies: - - supports-color - /http-proxy-middleware@2.0.6(@types/express@4.17.17): - resolution: {integrity: sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==} + http-proxy-middleware@2.0.7: + resolution: {integrity: sha512-fgVY8AV7qU7z/MmXJ/rxwbrtQH4jBQ9m7kp3llF0liB7glmFeVZFBepQb32T3y8n8k2+AEYuMPCpinYW+/CuRA==} engines: {node: '>=12.0.0'} peerDependencies: '@types/express': ^4.17.13 peerDependenciesMeta: '@types/express': optional: true - dependencies: - '@types/express': 4.17.17 - '@types/http-proxy': 1.17.10 - http-proxy: 1.18.1 - is-glob: 4.0.3 - is-plain-obj: 3.0.0 - micromatch: 4.0.5 - transitivePeerDependencies: - - debug - dev: false - /http-proxy@1.18.1: + http-proxy@1.18.1: resolution: {integrity: sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==} engines: {node: '>=8.0.0'} - dependencies: - eventemitter3: 4.0.7 - follow-redirects: 1.15.2 - requires-port: 1.0.0 - transitivePeerDependencies: - - debug - dev: false - - /http-status@1.5.3: - resolution: {integrity: sha512-jCClqdnnwigYslmtfb28vPplOgoiZ0siP2Z8C5Ua+3UKbx410v+c+jT+jh1bbI4TvcEySuX0vd/CfFZFbDkJeQ==} - engines: {node: '>= 0.4.0'} - dev: true - - /http2-wrapper@1.0.3: - resolution: {integrity: sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==} - engines: {node: '>=10.19.0'} - dependencies: - quick-lru: 5.1.1 - resolve-alpn: 1.2.1 - dev: true - /https-proxy-agent@5.0.1: + https-proxy-agent@5.0.1: resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} engines: {node: '>= 6'} - dependencies: - agent-base: 6.0.2 - debug: 4.3.4 - transitivePeerDependencies: - - supports-color - /human-signals@2.1.0: + https-proxy-agent@7.0.5: + resolution: {integrity: sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==} + engines: {node: '>= 14'} + + human-signals@2.1.0: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} - /humanize-ms@1.2.1: + humanize-ms@1.2.1: resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} - dependencies: - ms: 2.1.3 - /husky@8.0.3: + husky@8.0.3: resolution: {integrity: sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==} engines: {node: '>=14'} hasBin: true - dev: true - /hyphenate-style-name@1.0.4: - resolution: {integrity: sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==} - dev: false + hyphenate-style-name@1.1.0: + resolution: {integrity: sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==} - /iconv-lite@0.4.24: + iconv-lite@0.4.24: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} - dependencies: - safer-buffer: 2.1.2 - /iconv-lite@0.6.3: + iconv-lite@0.6.3: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} - dependencies: - safer-buffer: 2.1.2 - /icss-replace-symbols@1.1.0: + icss-replace-symbols@1.1.0: resolution: {integrity: sha512-chIaY3Vh2mh2Q3RGXttaDIzeiPvaVXJ+C4DAh/w3c37SKZ/U6PGMmuicR2EQQp9bKG8zLMCl7I+PtIoOOPp8Gg==} - dev: true - - /icss-utils@5.1.0(postcss@8.4.21): - resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==} - engines: {node: ^10 || ^12 || >= 14} - peerDependencies: - postcss: ^8.1.0 - dependencies: - postcss: 8.4.21 - dev: false - /icss-utils@5.1.0(postcss@8.4.22): + icss-utils@5.1.0: resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==} engines: {node: ^10 || ^12 || >= 14} peerDependencies: - postcss: ^8.1.0 - dependencies: - postcss: 8.4.22 - dev: true - - /idb@7.0.1: - resolution: {integrity: sha512-UUxlE7vGWK5RfB/fDwEGgRf84DY/ieqNha6msMV99UsEMQhJ1RwbCd8AYBj3QMgnE3VZnfQvm4oKVCJTYlqIgg==} + postcss: ^8.4.47 - /idb@7.1.1: + idb@7.1.1: resolution: {integrity: sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==} - dev: false - /identity-obj-proxy@3.0.0: + identity-obj-proxy@3.0.0: resolution: {integrity: sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA==} engines: {node: '>=4'} - dependencies: - harmony-reflect: 1.6.2 - dev: false - /ieee754@1.2.1: + ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - /ignore-by-default@1.0.1: + ignore-by-default@1.0.1: resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==} - dev: true - /ignore-walk@4.0.1: + ignore-walk@4.0.1: resolution: {integrity: sha512-rzDQLaW4jQbh2YrOFlJdCtX8qgJTehFRYiUB2r1osqTeDzV/3+Jh8fz1oAPzUThf3iku8Ds4IDqawI5d8mUiQw==} engines: {node: '>=10'} - dependencies: - minimatch: 3.1.2 - /ignore@5.2.4: - resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} + ignore-walk@6.0.5: + resolution: {integrity: sha512-VuuG0wCnjhnylG1ABXT3dAuIpTNDs/G8jlpmwXY03fXoXy/8ZK8/T+hMzt8L4WnrLCJgdybqgPagnF/f97cg3A==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} - /image-size@0.5.5: + image-size@0.5.5: resolution: {integrity: sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==} engines: {node: '>=0.10.0'} hasBin: true - requiresBuild: true - dev: true - optional: true - /immer@9.0.21: + immer@9.0.21: resolution: {integrity: sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==} - dev: false - /import-cwd@2.1.0: - resolution: {integrity: sha512-Ew5AZzJQFqrOV5BTW3EIoHAnoie1LojZLXKcCQ/yTRyVZosBhK1x1ViYjHGf5pAFOq8ZyChZp6m/fSN7pJyZtg==} - engines: {node: '>=4'} - dependencies: - import-from: 2.1.0 - dev: true - - /import-cwd@3.0.0: + import-cwd@3.0.0: resolution: {integrity: sha512-4pnzH16plW+hgvRECbDWpQl3cqtvSofHWh44met7ESfZ8UZOWWddm8hEyDTqREJ9RbYHY8gi8DqmaelApoOGMg==} engines: {node: '>=8'} - dependencies: - import-from: 3.0.0 - dev: true - - /import-fresh@2.0.0: - resolution: {integrity: sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==} - engines: {node: '>=4'} - dependencies: - caller-path: 2.0.0 - resolve-from: 3.0.0 - dev: true - /import-fresh@3.3.0: + import-fresh@3.3.0: resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} engines: {node: '>=6'} - dependencies: - parent-module: 1.0.1 - resolve-from: 4.0.0 - - /import-from@2.1.0: - resolution: {integrity: sha512-0vdnLL2wSGnhlRmzHJAg5JHjt1l2vYhzJ7tNLGbeVg0fse56tpGaH0uzH+r9Slej+BSXXEHvBKDEnVSLLE9/+w==} - engines: {node: '>=4'} - dependencies: - resolve-from: 3.0.0 - dev: true - /import-from@3.0.0: + import-from@3.0.0: resolution: {integrity: sha512-CiuXOFFSzkU5x/CR0+z7T91Iht4CXgfCxVOFRhh2Zyhg5wOpWvvDLQUsWl+gcN+QscYBjez8hDCt85O7RLDttQ==} engines: {node: '>=8'} - dependencies: - resolve-from: 5.0.0 - dev: true - /import-local@3.1.0: - resolution: {integrity: sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==} + import-local@3.2.0: + resolution: {integrity: sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==} engines: {node: '>=8'} hasBin: true - dependencies: - pkg-dir: 4.2.0 - resolve-cwd: 3.0.0 - /imurmurhash@0.1.4: + imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} - /indent-string@4.0.0: + indent-string@4.0.0: resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} engines: {node: '>=8'} - /indexes-of@1.0.1: - resolution: {integrity: sha512-bup+4tap3Hympa+JBJUG7XuOsdNQ6fxt0MHyXMKuLBKn0OqsTfvUxkUrroEX1+B2VsSHvCjiIcZVxRtYa4nllA==} - dev: true - - /infer-owner@1.0.4: + infer-owner@1.0.4: resolution: {integrity: sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==} - /inflight@1.0.6: + inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} - dependencies: - once: 1.4.0 - wrappy: 1.0.2 + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. - /inherits@2.0.3: + inherits@2.0.3: resolution: {integrity: sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==} - dev: false - /inherits@2.0.4: + inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - /ini@1.3.8: + ini@1.3.8: resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} - dev: false - - /inline-style-parser@0.1.1: - resolution: {integrity: sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==} - dev: true - /inline-style-prefixer@6.0.4: - resolution: {integrity: sha512-FwXmZC2zbeeS7NzGjJ6pAiqRhXR0ugUShSNb6GApMl6da0/XGc4MOJsoWAywia52EEWbXNSy0pzkwz/+Y+swSg==} - dependencies: - css-in-js-utils: 3.1.0 - fast-loops: 1.1.3 - dev: false + inline-style-prefixer@7.0.1: + resolution: {integrity: sha512-lhYo5qNTQp3EvSSp3sRvXMbVQTLrvGV6DycRMJ5dm2BLMiJ30wpXKdDdgX+GmJZ5uQMucwRKHamXSst3Sj/Giw==} - /inquirer@8.2.5: - resolution: {integrity: sha512-QAgPDQMEgrDssk1XiwwHoOGYF9BAbUcc1+j+FhEvaOt8/cKRqyLn0U5qA6F74fGhTMGxf92pOvPBeh29jQJDTQ==} + inquirer@8.2.6: + resolution: {integrity: sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==} engines: {node: '>=12.0.0'} - dependencies: - ansi-escapes: 4.3.2 - chalk: 4.1.2 - cli-cursor: 3.1.0 - cli-width: 3.0.0 - external-editor: 3.1.0 - figures: 3.2.0 - lodash: 4.17.21 - mute-stream: 0.0.8 - ora: 5.4.1 - run-async: 2.4.1 - rxjs: 7.8.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - through: 2.3.8 - wrap-ansi: 7.0.0 - /internal-slot@1.0.5: - resolution: {integrity: sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==} + inquirer@9.3.7: + resolution: {integrity: sha512-LJKFHCSeIRq9hanN14IlOtPSTe3lNES7TYDTE2xxdAy1LS5rYphajK1qtwvj3YmQXvvk0U2Vbmcni8P9EIQW9w==} + engines: {node: '>=18'} + + intercom-client@5.0.0: + resolution: {integrity: sha512-fEzM9w+apUwK6roDyZPvfXqmI9mrdM5Nz0QmCeDTM/8G2I0464SzJDfLTDRvz+ZkY6EIeTHEaQnK09DmYgqRhA==} + engines: {node: '>= v8.0.0'} + + internal-slot@1.0.7: + resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} engines: {node: '>= 0.4'} - dependencies: - get-intrinsic: 1.2.0 - has: 1.0.3 - side-channel: 1.0.4 - /interpret@1.4.0: + internmap@2.0.3: + resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} + engines: {node: '>=12'} + + interpret@1.4.0: resolution: {integrity: sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==} engines: {node: '>= 0.10'} - /interpret@3.1.1: + interpret@3.1.1: resolution: {integrity: sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==} engines: {node: '>=10.13.0'} - /ioredis@5.3.1: - resolution: {integrity: sha512-C+IBcMysM6v52pTLItYMeV4Hz7uriGtoJdz7SSBDX6u+zwSYGirLdQh3L7t/OItWITcw3gTFMjJReYUwS4zihg==} + ioredis@5.4.1: + resolution: {integrity: sha512-2YZsvl7jopIa1gaePkeMtd9rAcSjOOjPtpcLlOeusyO+XH2SK5ZcT+UCrElPP+WVIInh2TzeI4XW9ENaSLVVHA==} engines: {node: '>=12.22.0'} - dependencies: - '@ioredis/commands': 1.2.0 - cluster-key-slot: 1.1.2 - debug: 4.3.4 - denque: 2.1.0 - lodash.defaults: 4.2.0 - lodash.isarguments: 3.1.0 - redis-errors: 1.2.0 - redis-parser: 3.0.0 - standard-as-callback: 2.1.0 - transitivePeerDependencies: - - supports-color - /ip@1.1.8: - resolution: {integrity: sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==} - dev: true + ip-address@9.0.5: + resolution: {integrity: sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==} + engines: {node: '>= 12'} + + ip6addr@0.2.5: + resolution: {integrity: sha512-9RGGSB6Zc9Ox5DpDGFnJdIeF0AsqXzdH+FspCfPPaU/L/4tI6P+5lIoFUFm9JXs9IrJv1boqAaNCQmoDADTSKQ==} - /ip@2.0.0: - resolution: {integrity: sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==} + ip@2.0.1: + resolution: {integrity: sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ==} - /ipaddr.js@1.9.1: + ipaddr.js@1.9.1: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} - /ipaddr.js@2.0.1: - resolution: {integrity: sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==} + ipaddr.js@2.2.0: + resolution: {integrity: sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==} engines: {node: '>= 10'} - dev: false - - /is-absolute-url@2.1.0: - resolution: {integrity: sha512-vOx7VprsKyllwjSkLV79NIhpyLfr3jAp7VaTCMXOJHu4m0Ew1CZ2fcjASwmV1jI3BWuWHB013M48eyeldk9gYg==} - engines: {node: '>=0.10.0'} - dev: true - - /is-alphabetical@2.0.1: - resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} - dev: true - - /is-alphanumerical@2.0.1: - resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} - dependencies: - is-alphabetical: 2.0.1 - is-decimal: 2.0.1 - dev: true - /is-arguments@1.1.1: + is-arguments@1.1.1: resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - has-tostringtag: 1.0.0 - /is-array-buffer@3.0.2: - resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==} - dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.0 - is-typed-array: 1.1.10 + is-array-buffer@3.0.4: + resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} + engines: {node: '>= 0.4'} - /is-arrayish@0.2.1: + is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - /is-arrayish@0.3.2: + is-arrayish@0.3.2: resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} - /is-bigint@1.0.4: + is-async-function@2.0.0: + resolution: {integrity: sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==} + engines: {node: '>= 0.4'} + + is-bigint@1.0.4: resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} - dependencies: - has-bigints: 1.0.2 - /is-binary-path@2.1.0: + is-binary-path@2.1.0: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} - dependencies: - binary-extensions: 2.2.0 - /is-boolean-object@1.1.2: + is-boolean-object@1.1.2: resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - has-tostringtag: 1.0.0 - - /is-buffer@2.0.5: - resolution: {integrity: sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==} - engines: {node: '>=4'} - dev: true - /is-builtin-module@3.2.1: - resolution: {integrity: sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==} - engines: {node: '>=6'} - dependencies: - builtin-modules: 3.3.0 - dev: true + is-bun-module@1.2.1: + resolution: {integrity: sha512-AmidtEM6D6NmUiLOvvU7+IePxjEjOzra2h0pSrsfSAcXwl/83zLLXDByafUJy9k/rKK0pvXMLdwKwGHlX2Ke6Q==} - /is-callable@1.2.7: + is-callable@1.2.7: resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} engines: {node: '>= 0.4'} - /is-color-stop@1.1.0: - resolution: {integrity: sha512-H1U8Vz0cfXNujrJzEcvvwMDW9Ra+biSYA3ThdQvAnMLJkEHQXn6bWzLkxHtVYJ+Sdbx0b6finn3jZiaVe7MAHA==} - dependencies: - css-color-names: 0.0.4 - hex-color-regex: 1.1.0 - hsl-regex: 1.0.0 - hsla-regex: 1.0.0 - rgb-regex: 1.0.1 - rgba-regex: 1.0.0 - dev: true + is-core-module@2.15.1: + resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==} + engines: {node: '>= 0.4'} - /is-core-module@2.12.0: - resolution: {integrity: sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ==} - dependencies: - has: 1.0.3 + is-data-view@1.0.1: + resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==} + engines: {node: '>= 0.4'} - /is-date-object@1.0.5: + is-date-object@1.0.5: resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} engines: {node: '>= 0.4'} - dependencies: - has-tostringtag: 1.0.0 - - /is-decimal@2.0.1: - resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} - dev: true - /is-deflate@1.0.0: - resolution: {integrity: sha512-YDoFpuZWu1VRXlsnlYMzKyVRITXj7Ej/V9gXQ2/pAe7X1J7M/RNOqaIYi6qUn+B7nGyB9pDXrv02dsB58d2ZAQ==} - dev: true - - /is-directory@0.3.1: - resolution: {integrity: sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw==} - engines: {node: '>=0.10.0'} - dev: true - - /is-docker@2.2.1: + is-docker@2.2.1: resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} engines: {node: '>=8'} hasBin: true - /is-extglob@2.1.1: + is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} - /is-fullwidth-code-point@1.0.0: - resolution: {integrity: sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==} - engines: {node: '>=0.10.0'} - dependencies: - number-is-nan: 1.0.1 - dev: false + is-finalizationregistry@1.0.2: + resolution: {integrity: sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==} - /is-fullwidth-code-point@3.0.0: + is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} - /is-generator-fn@2.1.0: + is-generator-fn@2.1.0: resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} engines: {node: '>=6'} - /is-glob@4.0.3: - resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} - engines: {node: '>=0.10.0'} - dependencies: - is-extglob: 2.1.1 + is-generator-function@1.0.10: + resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} + engines: {node: '>= 0.4'} - /is-gzip@1.0.0: - resolution: {integrity: sha512-rcfALRIb1YewtnksfRIHGcIY93QnK8BIQ/2c9yDYcG/Y6+vRoJuTWBmmSEbyLLYtXm7q35pHOHbZFQBaLrhlWQ==} + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} - dev: true - /is-hexadecimal@2.0.1: - resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} - dev: true - - /is-interactive@1.0.0: + is-interactive@1.0.0: resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} engines: {node: '>=8'} - /is-lambda@1.0.1: + is-interactive@2.0.0: + resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} + engines: {node: '>=12'} + + is-json@2.0.1: + resolution: {integrity: sha512-6BEnpVn1rcf3ngfmViLM6vjUjGErbdrL4rwlv+u1NO1XO8kqT4YGL8+19Q+Z/bas8tY90BTWMk2+fW1g6hQjbA==} + + is-lambda@1.0.1: resolution: {integrity: sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==} - /is-map@2.0.2: - resolution: {integrity: sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==} + is-map@2.0.3: + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} - /is-module@1.0.0: + is-module@1.0.0: resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==} - /is-negative-zero@2.0.2: - resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==} + is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} engines: {node: '>= 0.4'} - /is-number-object@1.0.7: + is-number-object@1.0.7: resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} engines: {node: '>= 0.4'} - dependencies: - has-tostringtag: 1.0.0 - /is-number@7.0.0: + is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} - /is-obj@1.0.1: + is-obj@1.0.1: resolution: {integrity: sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==} engines: {node: '>=0.10.0'} - dev: false - - /is-obj@2.0.0: - resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} - engines: {node: '>=8'} - dev: true - /is-path-cwd@2.2.0: + is-path-cwd@2.2.0: resolution: {integrity: sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==} engines: {node: '>=6'} - dev: true - /is-path-in-cwd@2.1.0: + is-path-in-cwd@2.1.0: resolution: {integrity: sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==} engines: {node: '>=6'} - dependencies: - is-path-inside: 2.1.0 - dev: true - /is-path-inside@2.1.0: + is-path-inside@2.1.0: resolution: {integrity: sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==} engines: {node: '>=6'} - dependencies: - path-is-inside: 1.0.2 - dev: true - /is-path-inside@3.0.3: + is-path-inside@3.0.3: resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} engines: {node: '>=8'} - /is-plain-obj@2.1.0: + is-plain-obj@2.1.0: resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==} engines: {node: '>=8'} - /is-plain-obj@3.0.0: + is-plain-obj@3.0.0: resolution: {integrity: sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==} engines: {node: '>=10'} - /is-plain-obj@4.1.0: - resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} - engines: {node: '>=12'} - dev: true - - /is-plain-object@2.0.4: + is-plain-object@2.0.4: resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==} engines: {node: '>=0.10.0'} - dependencies: - isobject: 3.0.1 - /is-plain-object@5.0.0: + is-plain-object@5.0.0: resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} engines: {node: '>=0.10.0'} - /is-potential-custom-element-name@1.0.1: + is-potential-custom-element-name@1.0.1: resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} - dev: false - /is-reference@1.2.1: + is-reference@1.2.1: resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} - dependencies: - '@types/estree': 1.0.0 - dev: true - - /is-reference@3.0.1: - resolution: {integrity: sha512-baJJdQLiYaJdvFbJqXrcGv3WU3QCzBlUcI5QhbesIm6/xPsvmO+2CDoi/GMOFBQEQm+PXkwOPrp9KK5ozZsp2w==} - dependencies: - '@types/estree': 1.0.1 - dev: true - /is-regex@1.1.4: + is-regex@1.1.4: resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - has-tostringtag: 1.0.0 - /is-regexp@1.0.0: + is-regexp@1.0.0: resolution: {integrity: sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==} engines: {node: '>=0.10.0'} - dev: false - /is-resolvable@1.1.0: - resolution: {integrity: sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==} - dev: true - - /is-root@2.1.0: + is-root@2.1.0: resolution: {integrity: sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==} engines: {node: '>=6'} - dev: false - /is-scoped@2.1.0: + is-scoped@2.1.0: resolution: {integrity: sha512-Cv4OpPTHAK9kHYzkzCrof3VJh7H/PrG2MBUMvvJebaaUMbqhm0YAtXnvh0I3Hnj2tMZWwrRROWLSgfJrKqWmlQ==} engines: {node: '>=8'} - dependencies: - scoped-regex: 2.1.0 - - /is-set@2.0.2: - resolution: {integrity: sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==} - /is-shared-array-buffer@1.0.2: - resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==} - dependencies: - call-bind: 1.0.2 + is-set@2.0.3: + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} - /is-stream-ended@0.1.4: - resolution: {integrity: sha512-xj0XPvmr7bQFTvirqnFr50o0hQIh6ZItDqloxt5aJrR4NQsYeSsyFQERYGCAzfindAcnKjINnwEEgLx4IqVzQw==} - dev: false - optional: true + is-shared-array-buffer@1.0.3: + resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==} + engines: {node: '>= 0.4'} - /is-stream@2.0.1: + is-stream@2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} - /is-string@1.0.7: + is-string@1.0.7: resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} engines: {node: '>= 0.4'} - dependencies: - has-tostringtag: 1.0.0 - /is-symbol@1.0.4: + is-symbol@1.0.4: resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} engines: {node: '>= 0.4'} - dependencies: - has-symbols: 1.0.3 - /is-typed-array@1.1.10: - resolution: {integrity: sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==} + is-typed-array@1.1.13: + resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} engines: {node: '>= 0.4'} - dependencies: - available-typed-arrays: 1.0.5 - call-bind: 1.0.2 - for-each: 0.3.3 - gopd: 1.0.1 - has-tostringtag: 1.0.0 - /is-typedarray@1.0.0: + is-typedarray@1.0.0: resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} - dev: false - /is-unicode-supported@0.1.0: + is-unicode-supported@0.1.0: resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} engines: {node: '>=10'} - /is-utf8@0.2.1: + is-unicode-supported@1.3.0: + resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} + engines: {node: '>=12'} + + is-unicode-supported@2.1.0: + resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} + engines: {node: '>=18'} + + is-utf8@0.2.1: resolution: {integrity: sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==} - /is-weakmap@2.0.1: - resolution: {integrity: sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==} + is-valid-domain@0.1.6: + resolution: {integrity: sha512-ZKtq737eFkZr71At8NxOFcP9O1K89gW3DkdrGMpp1upr/ueWjj+Weh4l9AI4rN0Gt8W2M1w7jrG2b/Yv83Ljpg==} + + is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} - /is-weakref@1.0.2: + is-weakref@1.0.2: resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} - dependencies: - call-bind: 1.0.2 - /is-weakset@2.0.2: - resolution: {integrity: sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==} - dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.0 + is-weakset@2.0.3: + resolution: {integrity: sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==} + engines: {node: '>= 0.4'} - /is-what@3.14.1: + is-what@3.14.1: resolution: {integrity: sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==} - dev: true - /is-wsl@2.2.0: + is-wsl@2.2.0: resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} engines: {node: '>=8'} - dependencies: - is-docker: 2.2.1 - /isarray@0.0.1: - resolution: {integrity: sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==} - dev: true - - /isarray@1.0.0: + isarray@1.0.0: resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} - /isarray@2.0.5: + isarray@2.0.5: resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} - /isbinaryfile@4.0.10: + isbinaryfile@4.0.10: resolution: {integrity: sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==} engines: {node: '>= 8.0.0'} - /isbinaryfile@5.0.0: - resolution: {integrity: sha512-UDdnyGvMajJUWCkib7Cei/dvyJrrvo4FIrsvSFWdPpXSUorzXrDJ0S+X5Q4ZlasfPjca4yqCNNsjbCeiy8FFeg==} - engines: {node: '>= 14.0.0'} + isbinaryfile@5.0.4: + resolution: {integrity: sha512-YKBKVkKhty7s8rxddb40oOkuP0NbaeXrQvLin6QMHL7Ypiy2RW9LwOVrVgZRyOrhQlayMd9t+D8yDy8MKFTSDQ==} + engines: {node: '>= 18.0.0'} - /isexe@2.0.0: + isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - /isobject@3.0.1: + isobject@3.0.1: resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==} engines: {node: '>=0.10.0'} - /istanbul-lib-coverage@3.2.0: - resolution: {integrity: sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==} + isolated-vm@6.0.0: + resolution: {integrity: sha512-MMhgIssDZ2g3eD5tyqiWPx0k8Ps2H5halFOGGE9cUjNymJ1xL24ebU8EV7J6z2gEXiiX51VNcXM7BJp3355/LA==} + engines: {node: '>=24.0.0'} + + istanbul-lib-coverage@3.2.2: + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} engines: {node: '>=8'} - /istanbul-lib-instrument@5.2.1: + istanbul-lib-instrument@5.2.1: resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} engines: {node: '>=8'} - dependencies: - '@babel/core': 7.21.4 - '@babel/parser': 7.21.4 - '@istanbuljs/schema': 0.1.3 - istanbul-lib-coverage: 3.2.0 - semver: 6.3.0 - transitivePeerDependencies: - - supports-color - /istanbul-lib-report@3.0.0: - resolution: {integrity: sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==} - engines: {node: '>=8'} - dependencies: - istanbul-lib-coverage: 3.2.0 - make-dir: 3.1.0 - supports-color: 7.2.0 + istanbul-lib-instrument@6.0.3: + resolution: {integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==} + engines: {node: '>=10'} + + istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} - /istanbul-lib-source-maps@4.0.1: + istanbul-lib-source-maps@4.0.1: resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} engines: {node: '>=10'} - dependencies: - debug: 4.3.4 - istanbul-lib-coverage: 3.2.0 - source-map: 0.6.1 - transitivePeerDependencies: - - supports-color - /istanbul-reports@3.1.5: - resolution: {integrity: sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==} + istanbul-reports@3.1.7: + resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==} engines: {node: '>=8'} - dependencies: - html-escaper: 2.0.2 - istanbul-lib-report: 3.0.0 - /jake@10.8.5: - resolution: {integrity: sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==} + iterator.prototype@1.1.3: + resolution: {integrity: sha512-FW5iMbeQ6rBGm/oKgzq2aW4KvAGpxPzYES8N4g4xNXUKpL1mclMvOe+76AcLDTvD+Ze+sOpVhgdAQEKF4L9iGQ==} + engines: {node: '>= 0.4'} + + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + + jackspeak@4.1.1: + resolution: {integrity: sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==} + engines: {node: 20 || >=22} + + jake@10.9.2: + resolution: {integrity: sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==} engines: {node: '>=10'} hasBin: true - dependencies: - async: 3.2.4 - chalk: 4.1.2 - filelist: 1.0.4 - minimatch: 3.1.2 - - /javascript-stringify@2.1.0: - resolution: {integrity: sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg==} - dev: true - /jest-changed-files@27.5.1: + jest-changed-files@27.5.1: resolution: {integrity: sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - dependencies: - '@jest/types': 27.5.1 - execa: 5.1.1 - throat: 6.0.2 - dev: false - /jest-changed-files@28.1.3: + jest-changed-files@28.1.3: resolution: {integrity: sha512-esaOfUWJXk2nfZt9SPyC8gA1kNfdKLkQWyzsMlqq8msYSlNKfmZxfRgZn4Cd4MGVUF+7v6dBs0d5TOAKa7iIiA==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dependencies: - execa: 5.1.1 - p-limit: 3.1.0 - dev: true - /jest-changed-files@29.5.0: - resolution: {integrity: sha512-IFG34IUMUaNBIxjQXF/iu7g6EcdMrGRRxaUSw92I/2g2YC6vCdTltl4nHvt7Ci5nSJwXIkCu8Ka1DKF+X7Z1Ag==} + jest-changed-files@29.7.0: + resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - execa: 5.1.1 - p-limit: 3.1.0 - dev: true - /jest-circus@27.5.1: + jest-circus@27.5.1: resolution: {integrity: sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - dependencies: - '@jest/environment': 27.5.1 - '@jest/test-result': 27.5.1 - '@jest/types': 27.5.1 - '@types/node': 18.15.11 - chalk: 4.1.2 - co: 4.6.0 - dedent: 0.7.0 - expect: 27.5.1 - is-generator-fn: 2.1.0 - jest-each: 27.5.1 - jest-matcher-utils: 27.5.1 - jest-message-util: 27.5.1 - jest-runtime: 27.5.1 - jest-snapshot: 27.5.1 - jest-util: 27.5.1 - pretty-format: 27.5.1 - slash: 3.0.0 - stack-utils: 2.0.6 - throat: 6.0.2 - transitivePeerDependencies: - - supports-color - dev: false - /jest-circus@28.1.3: + jest-circus@28.1.3: resolution: {integrity: sha512-cZ+eS5zc79MBwt+IhQhiEp0OeBddpc1n8MBo1nMB8A7oPMKEO+Sre+wHaLJexQUj9Ya/8NOBY0RESUgYjB6fow==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dependencies: - '@jest/environment': 28.1.3 - '@jest/expect': 28.1.3 - '@jest/test-result': 28.1.3 - '@jest/types': 28.1.3 - '@types/node': 18.15.11 - chalk: 4.1.2 - co: 4.6.0 - dedent: 0.7.0 - is-generator-fn: 2.1.0 - jest-each: 28.1.3 - jest-matcher-utils: 28.1.3 - jest-message-util: 28.1.3 - jest-runtime: 28.1.3 - jest-snapshot: 28.1.3 - jest-util: 28.1.3 - p-limit: 3.1.0 - pretty-format: 28.1.3 - slash: 3.0.0 - stack-utils: 2.0.6 - transitivePeerDependencies: - - supports-color - dev: true - /jest-circus@29.5.0: - resolution: {integrity: sha512-gq/ongqeQKAplVxqJmbeUOJJKkW3dDNPY8PjhJ5G0lBRvu0e3EWGxGy5cI4LAGA7gV2UHCtWBI4EMXK8c9nQKA==} + jest-circus@29.7.0: + resolution: {integrity: sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/environment': 29.5.0 - '@jest/expect': 29.5.0 - '@jest/test-result': 29.5.0 - '@jest/types': 29.5.0 - '@types/node': 18.15.11 - chalk: 4.1.2 - co: 4.6.0 - dedent: 0.7.0 - is-generator-fn: 2.1.0 - jest-each: 29.5.0 - jest-matcher-utils: 29.5.0 - jest-message-util: 29.5.0 - jest-runtime: 29.5.0 - jest-snapshot: 29.5.0 - jest-util: 29.5.0 - p-limit: 3.1.0 - pretty-format: 29.5.0 - pure-rand: 6.0.1 - slash: 3.0.0 - stack-utils: 2.0.6 - transitivePeerDependencies: - - supports-color - dev: true - /jest-cli@27.5.1(ts-node@10.8.2): + jest-cli@27.5.1: resolution: {integrity: sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} hasBin: true @@ -15357,28 +9463,8 @@ packages: peerDependenciesMeta: node-notifier: optional: true - dependencies: - '@jest/core': 27.5.1(ts-node@10.8.2) - '@jest/test-result': 27.5.1 - '@jest/types': 27.5.1 - chalk: 4.1.2 - exit: 0.1.2 - graceful-fs: 4.2.11 - import-local: 3.1.0 - jest-config: 27.5.1(ts-node@10.8.2) - jest-util: 27.5.1 - jest-validate: 27.5.1 - prompts: 2.4.2 - yargs: 16.2.0 - transitivePeerDependencies: - - bufferutil - - canvas - - supports-color - - ts-node - - utf-8-validate - dev: false - /jest-cli@28.1.3(@types/node@18.15.11)(ts-node@10.8.2): + jest-cli@28.1.3: resolution: {integrity: sha512-roY3kvrv57Azn1yPgdTebPAXvdR2xfezaKKYzVxZ6It/5NCxzJym6tUI5P1zkdWhfUYkxEI9uZWcQdaFLo8mJQ==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} hasBin: true @@ -15387,55 +9473,9 @@ packages: peerDependenciesMeta: node-notifier: optional: true - dependencies: - '@jest/core': 28.1.3(ts-node@10.8.2) - '@jest/test-result': 28.1.3 - '@jest/types': 28.1.3 - chalk: 4.1.2 - exit: 0.1.2 - graceful-fs: 4.2.11 - import-local: 3.1.0 - jest-config: 28.1.3(@types/node@18.15.11)(ts-node@10.8.2) - jest-util: 28.1.3 - jest-validate: 28.1.3 - prompts: 2.4.2 - yargs: 17.7.1 - transitivePeerDependencies: - - '@types/node' - - supports-color - - ts-node - dev: true - - /jest-cli@29.5.0(@types/node@16.18.23)(ts-node@10.8.2): - resolution: {integrity: sha512-L1KcP1l4HtfwdxXNFCL5bmUbLQiKrakMUriBEcc1Vfz6gx31ORKdreuWvmQVBit+1ss9NNR3yxjwfwzZNdQXJw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - hasBin: true - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - dependencies: - '@jest/core': 29.5.0(ts-node@10.8.2) - '@jest/test-result': 29.5.0 - '@jest/types': 29.5.0 - chalk: 4.1.2 - exit: 0.1.2 - graceful-fs: 4.2.11 - import-local: 3.1.0 - jest-config: 29.5.0(@types/node@16.18.23)(ts-node@10.8.2) - jest-util: 29.5.0 - jest-validate: 29.5.0 - prompts: 2.4.2 - yargs: 17.7.1 - transitivePeerDependencies: - - '@types/node' - - supports-color - - ts-node - dev: true - /jest-cli@29.5.0(@types/node@18.15.11)(ts-node@10.8.2): - resolution: {integrity: sha512-L1KcP1l4HtfwdxXNFCL5bmUbLQiKrakMUriBEcc1Vfz6gx31ORKdreuWvmQVBit+1ss9NNR3yxjwfwzZNdQXJw==} + jest-cli@29.7.0: + resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true peerDependencies: @@ -15443,26 +9483,8 @@ packages: peerDependenciesMeta: node-notifier: optional: true - dependencies: - '@jest/core': 29.5.0(ts-node@10.8.2) - '@jest/test-result': 29.5.0 - '@jest/types': 29.5.0 - chalk: 4.1.2 - exit: 0.1.2 - graceful-fs: 4.2.11 - import-local: 3.1.0 - jest-config: 29.5.0(@types/node@18.15.11)(ts-node@10.8.2) - jest-util: 29.5.0 - jest-validate: 29.5.0 - prompts: 2.4.2 - yargs: 17.7.1 - transitivePeerDependencies: - - '@types/node' - - supports-color - - ts-node - dev: true - /jest-config@27.5.1(ts-node@10.8.2): + jest-config@27.5.1: resolution: {integrity: sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} peerDependencies: @@ -15470,40 +9492,8 @@ packages: peerDependenciesMeta: ts-node: optional: true - dependencies: - '@babel/core': 7.21.4 - '@jest/test-sequencer': 27.5.1 - '@jest/types': 27.5.1 - babel-jest: 27.5.1(@babel/core@7.21.4) - chalk: 4.1.2 - ci-info: 3.8.0 - deepmerge: 4.3.1 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-circus: 27.5.1 - jest-environment-jsdom: 27.5.1 - jest-environment-node: 27.5.1 - jest-get-type: 27.5.1 - jest-jasmine2: 27.5.1 - jest-regex-util: 27.5.1 - jest-resolve: 27.5.1 - jest-runner: 27.5.1 - jest-util: 27.5.1 - jest-validate: 27.5.1 - micromatch: 4.0.5 - parse-json: 5.2.0 - pretty-format: 27.5.1 - slash: 3.0.0 - strip-json-comments: 3.1.1 - ts-node: 10.8.2(@types/node@18.15.11)(typescript@4.9.5) - transitivePeerDependencies: - - bufferutil - - canvas - - supports-color - - utf-8-validate - dev: false - /jest-config@28.1.3(@types/node@18.15.11)(ts-node@10.8.2): + jest-config@28.1.3: resolution: {integrity: sha512-MG3INjByJ0J4AsNBm7T3hsuxKQqFIiRo/AUqb1q9LRKI5UU6Aar9JHbr9Ivn1TVwfUD9KirRoM/T6u8XlcQPHQ==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} peerDependencies: @@ -15514,77 +9504,9 @@ packages: optional: true ts-node: optional: true - dependencies: - '@babel/core': 7.21.4 - '@jest/test-sequencer': 28.1.3 - '@jest/types': 28.1.3 - '@types/node': 18.15.11 - babel-jest: 28.1.3(@babel/core@7.21.4) - chalk: 4.1.2 - ci-info: 3.8.0 - deepmerge: 4.3.1 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-circus: 28.1.3 - jest-environment-node: 28.1.3 - jest-get-type: 28.0.2 - jest-regex-util: 28.0.2 - jest-resolve: 28.1.3 - jest-runner: 28.1.3 - jest-util: 28.1.3 - jest-validate: 28.1.3 - micromatch: 4.0.5 - parse-json: 5.2.0 - pretty-format: 28.1.3 - slash: 3.0.0 - strip-json-comments: 3.1.1 - ts-node: 10.8.2(@types/node@18.15.11)(typescript@4.9.5) - transitivePeerDependencies: - - supports-color - dev: true - - /jest-config@29.5.0(@types/node@16.18.23)(ts-node@10.8.2): - resolution: {integrity: sha512-kvDUKBnNJPNBmFFOhDbm59iu1Fii1Q6SxyhXfvylq3UTHbg6o7j/g8k2dZyXWLvfdKB1vAPxNZnMgtKJcmu3kA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - '@types/node': '*' - ts-node: '>=9.0.0' - peerDependenciesMeta: - '@types/node': - optional: true - ts-node: - optional: true - dependencies: - '@babel/core': 7.21.4 - '@jest/test-sequencer': 29.5.0 - '@jest/types': 29.5.0 - '@types/node': 16.18.23 - babel-jest: 29.5.0(@babel/core@7.21.4) - chalk: 4.1.2 - ci-info: 3.8.0 - deepmerge: 4.3.1 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-circus: 29.5.0 - jest-environment-node: 29.5.0 - jest-get-type: 29.4.3 - jest-regex-util: 29.4.3 - jest-resolve: 29.5.0 - jest-runner: 29.5.0 - jest-util: 29.5.0 - jest-validate: 29.5.0 - micromatch: 4.0.5 - parse-json: 5.2.0 - pretty-format: 29.5.0 - slash: 3.0.0 - strip-json-comments: 3.1.1 - ts-node: 10.8.2(@types/node@18.15.11)(typescript@4.9.5) - transitivePeerDependencies: - - supports-color - dev: true - /jest-config@29.5.0(@types/node@18.15.11)(ts-node@10.8.2): - resolution: {integrity: sha512-kvDUKBnNJPNBmFFOhDbm59iu1Fii1Q6SxyhXfvylq3UTHbg6o7j/g8k2dZyXWLvfdKB1vAPxNZnMgtKJcmu3kA==} + jest-config@29.7.0: + resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: '@types/node': '*' @@ -15594,419 +9516,136 @@ packages: optional: true ts-node: optional: true - dependencies: - '@babel/core': 7.21.4 - '@jest/test-sequencer': 29.5.0 - '@jest/types': 29.5.0 - '@types/node': 18.15.11 - babel-jest: 29.5.0(@babel/core@7.21.4) - chalk: 4.1.2 - ci-info: 3.8.0 - deepmerge: 4.3.1 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-circus: 29.5.0 - jest-environment-node: 29.5.0 - jest-get-type: 29.4.3 - jest-regex-util: 29.4.3 - jest-resolve: 29.5.0 - jest-runner: 29.5.0 - jest-util: 29.5.0 - jest-validate: 29.5.0 - micromatch: 4.0.5 - parse-json: 5.2.0 - pretty-format: 29.5.0 - slash: 3.0.0 - strip-json-comments: 3.1.1 - ts-node: 10.8.2(@types/node@18.15.11)(typescript@4.9.5) - transitivePeerDependencies: - - supports-color - dev: true - /jest-diff@27.5.1: + jest-diff@27.5.1: resolution: {integrity: sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - dependencies: - chalk: 4.1.2 - diff-sequences: 27.5.1 - jest-get-type: 27.5.1 - pretty-format: 27.5.1 - dev: false - /jest-diff@28.1.3: + jest-diff@28.1.3: resolution: {integrity: sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dependencies: - chalk: 4.1.2 - diff-sequences: 28.1.1 - jest-get-type: 28.0.2 - pretty-format: 28.1.3 - dev: true - /jest-diff@29.5.0: - resolution: {integrity: sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw==} + jest-diff@29.7.0: + resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - chalk: 4.1.2 - diff-sequences: 29.4.3 - jest-get-type: 29.4.3 - pretty-format: 29.5.0 - dev: true - /jest-docblock@27.5.1: + jest-docblock@27.5.1: resolution: {integrity: sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - dependencies: - detect-newline: 3.1.0 - dev: false - /jest-docblock@28.1.1: + jest-docblock@28.1.1: resolution: {integrity: sha512-3wayBVNiOYx0cwAbl9rwm5kKFP8yHH3d/fkEaL02NPTkDojPtheGB7HZSFY4wzX+DxyrvhXz0KSCVksmCknCuA==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dependencies: - detect-newline: 3.1.0 - dev: true - /jest-docblock@29.4.3: - resolution: {integrity: sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg==} + jest-docblock@29.7.0: + resolution: {integrity: sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - detect-newline: 3.1.0 - dev: true - /jest-each@27.5.1: + jest-each@27.5.1: resolution: {integrity: sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - dependencies: - '@jest/types': 27.5.1 - chalk: 4.1.2 - jest-get-type: 27.5.1 - jest-util: 27.5.1 - pretty-format: 27.5.1 - dev: false - /jest-each@28.1.3: + jest-each@28.1.3: resolution: {integrity: sha512-arT1z4sg2yABU5uogObVPvSlSMQlDA48owx07BDPAiasW0yYpYHYOo4HHLz9q0BVzDVU4hILFjzJw0So9aCL/g==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dependencies: - '@jest/types': 28.1.3 - chalk: 4.1.2 - jest-get-type: 28.0.2 - jest-util: 28.1.3 - pretty-format: 28.1.3 - dev: true - /jest-each@29.5.0: - resolution: {integrity: sha512-HM5kIJ1BTnVt+DQZ2ALp3rzXEl+g726csObrW/jpEGl+CDSSQpOJJX2KE/vEg8cxcMXdyEPu6U4QX5eruQv5hA==} + jest-each@29.7.0: + resolution: {integrity: sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.5.0 - chalk: 4.1.2 - jest-get-type: 29.4.3 - jest-util: 29.5.0 - pretty-format: 29.5.0 - dev: true - /jest-environment-jsdom@27.5.1: + jest-environment-jsdom@27.5.1: resolution: {integrity: sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - dependencies: - '@jest/environment': 27.5.1 - '@jest/fake-timers': 27.5.1 - '@jest/types': 27.5.1 - '@types/node': 18.15.11 - jest-mock: 27.5.1 - jest-util: 27.5.1 - jsdom: 16.7.0 - transitivePeerDependencies: - - bufferutil - - canvas - - supports-color - - utf-8-validate - dev: false - /jest-environment-node@27.5.1: + jest-environment-node@27.5.1: resolution: {integrity: sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - dependencies: - '@jest/environment': 27.5.1 - '@jest/fake-timers': 27.5.1 - '@jest/types': 27.5.1 - '@types/node': 18.15.11 - jest-mock: 27.5.1 - jest-util: 27.5.1 - dev: false - /jest-environment-node@28.1.3: + jest-environment-node@28.1.3: resolution: {integrity: sha512-ugP6XOhEpjAEhGYvp5Xj989ns5cB1K6ZdjBYuS30umT4CQEETaxSiPcZ/E1kFktX4GkrcM4qu07IIlDYX1gp+A==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dependencies: - '@jest/environment': 28.1.3 - '@jest/fake-timers': 28.1.3 - '@jest/types': 28.1.3 - '@types/node': 18.15.11 - jest-mock: 28.1.3 - jest-util: 28.1.3 - dev: true - /jest-environment-node@29.5.0: - resolution: {integrity: sha512-ExxuIK/+yQ+6PRGaHkKewYtg6hto2uGCgvKdb2nfJfKXgZ17DfXjvbZ+jA1Qt9A8EQSfPnt5FKIfnOO3u1h9qw==} + jest-environment-node@29.7.0: + resolution: {integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/environment': 29.5.0 - '@jest/fake-timers': 29.5.0 - '@jest/types': 29.5.0 - '@types/node': 18.15.11 - jest-mock: 29.5.0 - jest-util: 29.5.0 - dev: true - /jest-get-type@27.5.1: + jest-get-type@27.5.1: resolution: {integrity: sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - dev: false - /jest-get-type@28.0.2: + jest-get-type@28.0.2: resolution: {integrity: sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dev: true - /jest-get-type@29.4.3: - resolution: {integrity: sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==} + jest-get-type@29.6.3: + resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dev: true - /jest-haste-map@27.5.1: + jest-haste-map@27.5.1: resolution: {integrity: sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - dependencies: - '@jest/types': 27.5.1 - '@types/graceful-fs': 4.1.6 - '@types/node': 18.15.11 - anymatch: 3.1.3 - fb-watchman: 2.0.2 - graceful-fs: 4.2.11 - jest-regex-util: 27.5.1 - jest-serializer: 27.5.1 - jest-util: 27.5.1 - jest-worker: 27.5.1 - micromatch: 4.0.5 - walker: 1.0.8 - optionalDependencies: - fsevents: 2.3.2 - dev: false - /jest-haste-map@28.1.3: + jest-haste-map@28.1.3: resolution: {integrity: sha512-3S+RQWDXccXDKSWnkHa/dPwt+2qwA8CJzR61w3FoYCvoo3Pn8tvGcysmMF0Bj0EX5RYvAI2EIvC57OmotfdtKA==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dependencies: - '@jest/types': 28.1.3 - '@types/graceful-fs': 4.1.6 - '@types/node': 18.15.11 - anymatch: 3.1.3 - fb-watchman: 2.0.2 - graceful-fs: 4.2.11 - jest-regex-util: 28.0.2 - jest-util: 28.1.3 - jest-worker: 28.1.3 - micromatch: 4.0.5 - walker: 1.0.8 - optionalDependencies: - fsevents: 2.3.2 - dev: true - /jest-haste-map@29.5.0: - resolution: {integrity: sha512-IspOPnnBro8YfVYSw6yDRKh/TiCdRngjxeacCps1cQ9cgVN6+10JUcuJ1EabrgYLOATsIAigxA0rLR9x/YlrSA==} + jest-haste-map@29.7.0: + resolution: {integrity: sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.5.0 - '@types/graceful-fs': 4.1.6 - '@types/node': 18.15.11 - anymatch: 3.1.3 - fb-watchman: 2.0.2 - graceful-fs: 4.2.11 - jest-regex-util: 29.4.3 - jest-util: 29.5.0 - jest-worker: 29.5.0 - micromatch: 4.0.5 - walker: 1.0.8 - optionalDependencies: - fsevents: 2.3.2 - dev: true - /jest-jasmine2@27.5.1: + jest-jasmine2@27.5.1: resolution: {integrity: sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - dependencies: - '@jest/environment': 27.5.1 - '@jest/source-map': 27.5.1 - '@jest/test-result': 27.5.1 - '@jest/types': 27.5.1 - '@types/node': 18.15.11 - chalk: 4.1.2 - co: 4.6.0 - expect: 27.5.1 - is-generator-fn: 2.1.0 - jest-each: 27.5.1 - jest-matcher-utils: 27.5.1 - jest-message-util: 27.5.1 - jest-runtime: 27.5.1 - jest-snapshot: 27.5.1 - jest-util: 27.5.1 - pretty-format: 27.5.1 - throat: 6.0.2 - transitivePeerDependencies: - - supports-color - dev: false - /jest-leak-detector@27.5.1: + jest-leak-detector@27.5.1: resolution: {integrity: sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - dependencies: - jest-get-type: 27.5.1 - pretty-format: 27.5.1 - dev: false - /jest-leak-detector@28.1.3: + jest-leak-detector@28.1.3: resolution: {integrity: sha512-WFVJhnQsiKtDEo5lG2mM0v40QWnBM+zMdHHyJs8AWZ7J0QZJS59MsyKeJHWhpBZBH32S48FOVvGyOFT1h0DlqA==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dependencies: - jest-get-type: 28.0.2 - pretty-format: 28.1.3 - dev: true - /jest-leak-detector@29.5.0: - resolution: {integrity: sha512-u9YdeeVnghBUtpN5mVxjID7KbkKE1QU4f6uUwuxiY0vYRi9BUCLKlPEZfDGR67ofdFmDz9oPAy2G92Ujrntmow==} + jest-leak-detector@29.7.0: + resolution: {integrity: sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - jest-get-type: 29.4.3 - pretty-format: 29.5.0 - dev: true - /jest-matcher-utils@27.5.1: + jest-matcher-utils@27.5.1: resolution: {integrity: sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - dependencies: - chalk: 4.1.2 - jest-diff: 27.5.1 - jest-get-type: 27.5.1 - pretty-format: 27.5.1 - dev: false - /jest-matcher-utils@28.1.3: + jest-matcher-utils@28.1.3: resolution: {integrity: sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dependencies: - chalk: 4.1.2 - jest-diff: 28.1.3 - jest-get-type: 28.0.2 - pretty-format: 28.1.3 - dev: true - /jest-matcher-utils@29.5.0: - resolution: {integrity: sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw==} + jest-matcher-utils@29.7.0: + resolution: {integrity: sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - chalk: 4.1.2 - jest-diff: 29.5.0 - jest-get-type: 29.4.3 - pretty-format: 29.5.0 - dev: true - /jest-message-util@27.5.1: + jest-message-util@27.5.1: resolution: {integrity: sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - dependencies: - '@babel/code-frame': 7.21.4 - '@jest/types': 27.5.1 - '@types/stack-utils': 2.0.1 - chalk: 4.1.2 - graceful-fs: 4.2.11 - micromatch: 4.0.5 - pretty-format: 27.5.1 - slash: 3.0.0 - stack-utils: 2.0.6 - dev: false - /jest-message-util@28.1.3: + jest-message-util@28.1.3: resolution: {integrity: sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dependencies: - '@babel/code-frame': 7.21.4 - '@jest/types': 28.1.3 - '@types/stack-utils': 2.0.1 - chalk: 4.1.2 - graceful-fs: 4.2.11 - micromatch: 4.0.5 - pretty-format: 28.1.3 - slash: 3.0.0 - stack-utils: 2.0.6 - /jest-message-util@29.5.0: - resolution: {integrity: sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA==} + jest-message-util@29.7.0: + resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@babel/code-frame': 7.21.4 - '@jest/types': 29.5.0 - '@types/stack-utils': 2.0.1 - chalk: 4.1.2 - graceful-fs: 4.2.11 - micromatch: 4.0.5 - pretty-format: 29.5.0 - slash: 3.0.0 - stack-utils: 2.0.6 - dev: true - /jest-mock@27.5.1: + jest-mock@27.5.1: resolution: {integrity: sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - dependencies: - '@jest/types': 27.5.1 - '@types/node': 18.15.11 - dev: false - /jest-mock@28.1.3: + jest-mock@28.1.3: resolution: {integrity: sha512-o3J2jr6dMMWYVH4Lh/NKmDXdosrsJgi4AviS8oXLujcjpCMBb1FMsblDnOXKZKfSiHLxYub1eS0IHuRXsio9eA==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dependencies: - '@jest/types': 28.1.3 - '@types/node': 18.15.11 - dev: true - /jest-mock@29.5.0: - resolution: {integrity: sha512-GqOzvdWDE4fAV2bWQLQCkujxYWL7RxjCnj71b5VhDAGOevB3qj3Ovg26A5NI84ZpODxyzaozXLOh2NCgkbvyaw==} + jest-mock@29.7.0: + resolution: {integrity: sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.5.0 - '@types/node': 18.15.11 - jest-util: 29.5.0 - dev: true - - /jest-pnp-resolver@1.2.3(jest-resolve@27.5.1): - resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} - engines: {node: '>=6'} - peerDependencies: - jest-resolve: '*' - peerDependenciesMeta: - jest-resolve: - optional: true - dependencies: - jest-resolve: 27.5.1 - dev: false - - /jest-pnp-resolver@1.2.3(jest-resolve@28.1.3): - resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} - engines: {node: '>=6'} - peerDependencies: - jest-resolve: '*' - peerDependenciesMeta: - jest-resolve: - optional: true - dependencies: - jest-resolve: 28.1.3 - dev: true - /jest-pnp-resolver@1.2.3(jest-resolve@29.5.0): + jest-pnp-resolver@1.2.3: resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} engines: {node: '>=6'} peerDependencies: @@ -16014,552 +9653,142 @@ packages: peerDependenciesMeta: jest-resolve: optional: true - dependencies: - jest-resolve: 29.5.0 - dev: true - /jest-regex-util@27.5.1: + jest-regex-util@27.5.1: resolution: {integrity: sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - dev: false - /jest-regex-util@28.0.2: + jest-regex-util@28.0.2: resolution: {integrity: sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - /jest-regex-util@29.4.3: - resolution: {integrity: sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg==} + jest-regex-util@29.6.3: + resolution: {integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dev: true - /jest-resolve-dependencies@27.5.1: + jest-resolve-dependencies@27.5.1: resolution: {integrity: sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - dependencies: - '@jest/types': 27.5.1 - jest-regex-util: 27.5.1 - jest-snapshot: 27.5.1 - transitivePeerDependencies: - - supports-color - dev: false - /jest-resolve-dependencies@28.1.3: + jest-resolve-dependencies@28.1.3: resolution: {integrity: sha512-qa0QO2Q0XzQoNPouMbCc7Bvtsem8eQgVPNkwn9LnS+R2n8DaVDPL/U1gngC0LTl1RYXJU0uJa2BMC2DbTfFrHA==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dependencies: - jest-regex-util: 28.0.2 - jest-snapshot: 28.1.3 - transitivePeerDependencies: - - supports-color - dev: true - /jest-resolve-dependencies@29.5.0: - resolution: {integrity: sha512-sjV3GFr0hDJMBpYeUuGduP+YeCRbd7S/ck6IvL3kQ9cpySYKqcqhdLLC2rFwrcL7tz5vYibomBrsFYWkIGGjOg==} + jest-resolve-dependencies@29.7.0: + resolution: {integrity: sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - jest-regex-util: 29.4.3 - jest-snapshot: 29.5.0 - transitivePeerDependencies: - - supports-color - dev: true - /jest-resolve@27.5.1: + jest-resolve@27.5.1: resolution: {integrity: sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - dependencies: - '@jest/types': 27.5.1 - chalk: 4.1.2 - graceful-fs: 4.2.11 - jest-haste-map: 27.5.1 - jest-pnp-resolver: 1.2.3(jest-resolve@27.5.1) - jest-util: 27.5.1 - jest-validate: 27.5.1 - resolve: 1.22.2 - resolve.exports: 1.1.1 - slash: 3.0.0 - dev: false - /jest-resolve@28.1.3: + jest-resolve@28.1.3: resolution: {integrity: sha512-Z1W3tTjE6QaNI90qo/BJpfnvpxtaFTFw5CDgwpyE/Kz8U/06N1Hjf4ia9quUhCh39qIGWF1ZuxFiBiJQwSEYKQ==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dependencies: - chalk: 4.1.2 - graceful-fs: 4.2.11 - jest-haste-map: 28.1.3 - jest-pnp-resolver: 1.2.3(jest-resolve@28.1.3) - jest-util: 28.1.3 - jest-validate: 28.1.3 - resolve: 1.22.2 - resolve.exports: 1.1.1 - slash: 3.0.0 - dev: true - /jest-resolve@29.5.0: - resolution: {integrity: sha512-1TzxJ37FQq7J10jPtQjcc+MkCkE3GBpBecsSUWJ0qZNJpmg6m0D9/7II03yJulm3H/fvVjgqLh/k2eYg+ui52w==} + jest-resolve@29.7.0: + resolution: {integrity: sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - chalk: 4.1.2 - graceful-fs: 4.2.11 - jest-haste-map: 29.5.0 - jest-pnp-resolver: 1.2.3(jest-resolve@29.5.0) - jest-util: 29.5.0 - jest-validate: 29.5.0 - resolve: 1.22.2 - resolve.exports: 2.0.2 - slash: 3.0.0 - dev: true - /jest-runner@27.5.1: + jest-runner@27.5.1: resolution: {integrity: sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - dependencies: - '@jest/console': 27.5.1 - '@jest/environment': 27.5.1 - '@jest/test-result': 27.5.1 - '@jest/transform': 27.5.1 - '@jest/types': 27.5.1 - '@types/node': 18.15.11 - chalk: 4.1.2 - emittery: 0.8.1 - graceful-fs: 4.2.11 - jest-docblock: 27.5.1 - jest-environment-jsdom: 27.5.1 - jest-environment-node: 27.5.1 - jest-haste-map: 27.5.1 - jest-leak-detector: 27.5.1 - jest-message-util: 27.5.1 - jest-resolve: 27.5.1 - jest-runtime: 27.5.1 - jest-util: 27.5.1 - jest-worker: 27.5.1 - source-map-support: 0.5.21 - throat: 6.0.2 - transitivePeerDependencies: - - bufferutil - - canvas - - supports-color - - utf-8-validate - dev: false - /jest-runner@28.1.3: + jest-runner@28.1.3: resolution: {integrity: sha512-GkMw4D/0USd62OVO0oEgjn23TM+YJa2U2Wu5zz9xsQB1MxWKDOlrnykPxnMsN0tnJllfLPinHTka61u0QhaxBA==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dependencies: - '@jest/console': 28.1.3 - '@jest/environment': 28.1.3 - '@jest/test-result': 28.1.3 - '@jest/transform': 28.1.3 - '@jest/types': 28.1.3 - '@types/node': 18.15.11 - chalk: 4.1.2 - emittery: 0.10.2 - graceful-fs: 4.2.11 - jest-docblock: 28.1.1 - jest-environment-node: 28.1.3 - jest-haste-map: 28.1.3 - jest-leak-detector: 28.1.3 - jest-message-util: 28.1.3 - jest-resolve: 28.1.3 - jest-runtime: 28.1.3 - jest-util: 28.1.3 - jest-watcher: 28.1.3 - jest-worker: 28.1.3 - p-limit: 3.1.0 - source-map-support: 0.5.13 - transitivePeerDependencies: - - supports-color - dev: true - /jest-runner@29.5.0: - resolution: {integrity: sha512-m7b6ypERhFghJsslMLhydaXBiLf7+jXy8FwGRHO3BGV1mcQpPbwiqiKUR2zU2NJuNeMenJmlFZCsIqzJCTeGLQ==} + jest-runner@29.7.0: + resolution: {integrity: sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/console': 29.5.0 - '@jest/environment': 29.5.0 - '@jest/test-result': 29.5.0 - '@jest/transform': 29.5.0 - '@jest/types': 29.5.0 - '@types/node': 18.15.11 - chalk: 4.1.2 - emittery: 0.13.1 - graceful-fs: 4.2.11 - jest-docblock: 29.4.3 - jest-environment-node: 29.5.0 - jest-haste-map: 29.5.0 - jest-leak-detector: 29.5.0 - jest-message-util: 29.5.0 - jest-resolve: 29.5.0 - jest-runtime: 29.5.0 - jest-util: 29.5.0 - jest-watcher: 29.5.0 - jest-worker: 29.5.0 - p-limit: 3.1.0 - source-map-support: 0.5.13 - transitivePeerDependencies: - - supports-color - dev: true - /jest-runtime@27.5.1: + jest-runtime@27.5.1: resolution: {integrity: sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - dependencies: - '@jest/environment': 27.5.1 - '@jest/fake-timers': 27.5.1 - '@jest/globals': 27.5.1 - '@jest/source-map': 27.5.1 - '@jest/test-result': 27.5.1 - '@jest/transform': 27.5.1 - '@jest/types': 27.5.1 - chalk: 4.1.2 - cjs-module-lexer: 1.2.2 - collect-v8-coverage: 1.0.1 - execa: 5.1.1 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-haste-map: 27.5.1 - jest-message-util: 27.5.1 - jest-mock: 27.5.1 - jest-regex-util: 27.5.1 - jest-resolve: 27.5.1 - jest-snapshot: 27.5.1 - jest-util: 27.5.1 - slash: 3.0.0 - strip-bom: 4.0.0 - transitivePeerDependencies: - - supports-color - dev: false - /jest-runtime@28.1.3: + jest-runtime@28.1.3: resolution: {integrity: sha512-NU+881ScBQQLc1JHG5eJGU7Ui3kLKrmwCPPtYsJtBykixrM2OhVQlpMmFWJjMyDfdkGgBMNjXCGB/ebzsgNGQw==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dependencies: - '@jest/environment': 28.1.3 - '@jest/fake-timers': 28.1.3 - '@jest/globals': 28.1.3 - '@jest/source-map': 28.1.2 - '@jest/test-result': 28.1.3 - '@jest/transform': 28.1.3 - '@jest/types': 28.1.3 - chalk: 4.1.2 - cjs-module-lexer: 1.2.2 - collect-v8-coverage: 1.0.1 - execa: 5.1.1 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-haste-map: 28.1.3 - jest-message-util: 28.1.3 - jest-mock: 28.1.3 - jest-regex-util: 28.0.2 - jest-resolve: 28.1.3 - jest-snapshot: 28.1.3 - jest-util: 28.1.3 - slash: 3.0.0 - strip-bom: 4.0.0 - transitivePeerDependencies: - - supports-color - dev: true - /jest-runtime@29.5.0: - resolution: {integrity: sha512-1Hr6Hh7bAgXQP+pln3homOiEZtCDZFqwmle7Ew2j8OlbkIu6uE3Y/etJQG8MLQs3Zy90xrp2C0BRrtPHG4zryw==} + jest-runtime@29.7.0: + resolution: {integrity: sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/environment': 29.5.0 - '@jest/fake-timers': 29.5.0 - '@jest/globals': 29.5.0 - '@jest/source-map': 29.4.3 - '@jest/test-result': 29.5.0 - '@jest/transform': 29.5.0 - '@jest/types': 29.5.0 - '@types/node': 18.15.11 - chalk: 4.1.2 - cjs-module-lexer: 1.2.2 - collect-v8-coverage: 1.0.1 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-haste-map: 29.5.0 - jest-message-util: 29.5.0 - jest-mock: 29.5.0 - jest-regex-util: 29.4.3 - jest-resolve: 29.5.0 - jest-snapshot: 29.5.0 - jest-util: 29.5.0 - slash: 3.0.0 - strip-bom: 4.0.0 - transitivePeerDependencies: - - supports-color - dev: true - /jest-serializer@27.5.1: + jest-serializer@27.5.1: resolution: {integrity: sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - dependencies: - '@types/node': 18.15.11 - graceful-fs: 4.2.11 - dev: false - /jest-snapshot@27.5.1: + jest-snapshot@27.5.1: resolution: {integrity: sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - dependencies: - '@babel/core': 7.21.4 - '@babel/generator': 7.21.4 - '@babel/plugin-syntax-typescript': 7.21.4(@babel/core@7.21.4) - '@babel/traverse': 7.21.4 - '@babel/types': 7.21.4 - '@jest/transform': 27.5.1 - '@jest/types': 27.5.1 - '@types/babel__traverse': 7.18.3 - '@types/prettier': 2.7.2 - babel-preset-current-node-syntax: 1.0.1(@babel/core@7.21.4) - chalk: 4.1.2 - expect: 27.5.1 - graceful-fs: 4.2.11 - jest-diff: 27.5.1 - jest-get-type: 27.5.1 - jest-haste-map: 27.5.1 - jest-matcher-utils: 27.5.1 - jest-message-util: 27.5.1 - jest-util: 27.5.1 - natural-compare: 1.4.0 - pretty-format: 27.5.1 - semver: 7.5.0 - transitivePeerDependencies: - - supports-color - dev: false - /jest-snapshot@28.1.3: + jest-snapshot@28.1.3: resolution: {integrity: sha512-4lzMgtiNlc3DU/8lZfmqxN3AYD6GGLbl+72rdBpXvcV+whX7mDrREzkPdp2RnmfIiWBg1YbuFSkXduF2JcafJg==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dependencies: - '@babel/core': 7.21.4 - '@babel/generator': 7.21.4 - '@babel/plugin-syntax-typescript': 7.21.4(@babel/core@7.21.4) - '@babel/traverse': 7.21.4 - '@babel/types': 7.21.4 - '@jest/expect-utils': 28.1.3 - '@jest/transform': 28.1.3 - '@jest/types': 28.1.3 - '@types/babel__traverse': 7.18.3 - '@types/prettier': 2.7.2 - babel-preset-current-node-syntax: 1.0.1(@babel/core@7.21.4) - chalk: 4.1.2 - expect: 28.1.3 - graceful-fs: 4.2.11 - jest-diff: 28.1.3 - jest-get-type: 28.0.2 - jest-haste-map: 28.1.3 - jest-matcher-utils: 28.1.3 - jest-message-util: 28.1.3 - jest-util: 28.1.3 - natural-compare: 1.4.0 - pretty-format: 28.1.3 - semver: 7.5.0 - transitivePeerDependencies: - - supports-color - dev: true - /jest-snapshot@29.5.0: - resolution: {integrity: sha512-x7Wolra5V0tt3wRs3/ts3S6ciSQVypgGQlJpz2rsdQYoUKxMxPNaoHMGJN6qAuPJqS+2iQ1ZUn5kl7HCyls84g==} + jest-snapshot@29.7.0: + resolution: {integrity: sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@babel/core': 7.21.4 - '@babel/generator': 7.21.4 - '@babel/plugin-syntax-jsx': 7.21.4(@babel/core@7.21.4) - '@babel/plugin-syntax-typescript': 7.21.4(@babel/core@7.21.4) - '@babel/traverse': 7.21.4 - '@babel/types': 7.21.4 - '@jest/expect-utils': 29.5.0 - '@jest/transform': 29.5.0 - '@jest/types': 29.5.0 - '@types/babel__traverse': 7.18.3 - '@types/prettier': 2.7.2 - babel-preset-current-node-syntax: 1.0.1(@babel/core@7.21.4) - chalk: 4.1.2 - expect: 29.5.0 - graceful-fs: 4.2.11 - jest-diff: 29.5.0 - jest-get-type: 29.4.3 - jest-matcher-utils: 29.5.0 - jest-message-util: 29.5.0 - jest-util: 29.5.0 - natural-compare: 1.4.0 - pretty-format: 29.5.0 - semver: 7.5.0 - transitivePeerDependencies: - - supports-color - dev: true - /jest-util@27.5.1: + jest-util@27.5.1: resolution: {integrity: sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - dependencies: - '@jest/types': 27.5.1 - '@types/node': 18.15.11 - chalk: 4.1.2 - ci-info: 3.8.0 - graceful-fs: 4.2.11 - picomatch: 2.3.1 - dev: false - /jest-util@28.1.3: + jest-util@28.1.3: resolution: {integrity: sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dependencies: - '@jest/types': 28.1.3 - '@types/node': 18.15.11 - chalk: 4.1.2 - ci-info: 3.8.0 - graceful-fs: 4.2.11 - picomatch: 2.3.1 - /jest-util@29.5.0: - resolution: {integrity: sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ==} + jest-util@29.7.0: + resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.5.0 - '@types/node': 18.15.11 - chalk: 4.1.2 - ci-info: 3.8.0 - graceful-fs: 4.2.11 - picomatch: 2.3.1 - dev: true - /jest-validate@27.5.1: + jest-validate@27.5.1: resolution: {integrity: sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - dependencies: - '@jest/types': 27.5.1 - camelcase: 6.3.0 - chalk: 4.1.2 - jest-get-type: 27.5.1 - leven: 3.1.0 - pretty-format: 27.5.1 - dev: false - /jest-validate@28.1.3: + jest-validate@28.1.3: resolution: {integrity: sha512-SZbOGBWEsaTxBGCOpsRWlXlvNkvTkY0XxRfh7zYmvd8uL5Qzyg0CHAXiXKROflh801quA6+/DsT4ODDthOC/OA==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dependencies: - '@jest/types': 28.1.3 - camelcase: 6.3.0 - chalk: 4.1.2 - jest-get-type: 28.0.2 - leven: 3.1.0 - pretty-format: 28.1.3 - dev: true - /jest-validate@29.5.0: - resolution: {integrity: sha512-pC26etNIi+y3HV8A+tUGr/lph9B18GnzSRAkPaaZJIE1eFdiYm6/CewuiJQ8/RlfHd1u/8Ioi8/sJ+CmbA+zAQ==} + jest-validate@29.7.0: + resolution: {integrity: sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.5.0 - camelcase: 6.3.0 - chalk: 4.1.2 - jest-get-type: 29.4.3 - leven: 3.1.0 - pretty-format: 29.5.0 - dev: true - /jest-watch-typeahead@1.1.0(jest@27.5.1): + jest-watch-typeahead@1.1.0: resolution: {integrity: sha512-Va5nLSJTN7YFtC2jd+7wsoe1pNe5K4ShLux/E5iHEwlB9AxaxmggY7to9KUqKojhaJw3aXqt5WAb4jGPOolpEw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: jest: ^27.0.0 || ^28.0.0 - dependencies: - ansi-escapes: 4.3.2 - chalk: 4.1.2 - jest: 27.5.1(ts-node@10.8.2) - jest-regex-util: 28.0.2 - jest-watcher: 28.1.3 - slash: 4.0.0 - string-length: 5.0.1 - strip-ansi: 7.0.1 - dev: false - /jest-watcher@27.5.1: + jest-watcher@27.5.1: resolution: {integrity: sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - dependencies: - '@jest/test-result': 27.5.1 - '@jest/types': 27.5.1 - '@types/node': 18.15.11 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - jest-util: 27.5.1 - string-length: 4.0.2 - dev: false - /jest-watcher@28.1.3: + jest-watcher@28.1.3: resolution: {integrity: sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dependencies: - '@jest/test-result': 28.1.3 - '@jest/types': 28.1.3 - '@types/node': 18.15.11 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - emittery: 0.10.2 - jest-util: 28.1.3 - string-length: 4.0.2 - /jest-watcher@29.5.0: - resolution: {integrity: sha512-KmTojKcapuqYrKDpRwfqcQ3zjMlwu27SYext9pt4GlF5FUgB+7XE1mcCnSm6a4uUpFyQIkb6ZhzZvHl+jiBCiA==} + jest-watcher@29.7.0: + resolution: {integrity: sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/test-result': 29.5.0 - '@jest/types': 29.5.0 - '@types/node': 18.15.11 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - emittery: 0.13.1 - jest-util: 29.5.0 - string-length: 4.0.2 - dev: true - - /jest-worker@24.9.0: - resolution: {integrity: sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==} - engines: {node: '>= 6'} - dependencies: - merge-stream: 2.0.0 - supports-color: 6.1.0 - dev: true - /jest-worker@26.6.2: + jest-worker@26.6.2: resolution: {integrity: sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==} engines: {node: '>= 10.13.0'} - dependencies: - '@types/node': 18.15.11 - merge-stream: 2.0.0 - supports-color: 7.2.0 - dev: false - /jest-worker@27.5.1: + jest-worker@27.5.1: resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} engines: {node: '>= 10.13.0'} - dependencies: - '@types/node': 18.15.11 - merge-stream: 2.0.0 - supports-color: 8.1.1 - /jest-worker@28.1.3: + jest-worker@28.1.3: resolution: {integrity: sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dependencies: - '@types/node': 18.15.11 - merge-stream: 2.0.0 - supports-color: 8.1.1 - /jest-worker@29.5.0: - resolution: {integrity: sha512-NcrQnevGoSp4b5kg+akIpthoAFHxPBcb5P6mYPY0fUNT+sSvmtu6jlkEle3anczUKIKEbMxFimk9oTP/tpIPgA==} + jest-worker@29.7.0: + resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@types/node': 18.15.11 - jest-util: 29.5.0 - merge-stream: 2.0.0 - supports-color: 8.1.1 - dev: true - /jest@27.5.1(ts-node@10.8.2): + jest@27.5.1: resolution: {integrity: sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} hasBin: true @@ -16568,19 +9797,8 @@ packages: peerDependenciesMeta: node-notifier: optional: true - dependencies: - '@jest/core': 27.5.1(ts-node@10.8.2) - import-local: 3.1.0 - jest-cli: 27.5.1(ts-node@10.8.2) - transitivePeerDependencies: - - bufferutil - - canvas - - supports-color - - ts-node - - utf-8-validate - dev: false - /jest@28.1.3(@types/node@18.15.11)(ts-node@10.8.2): + jest@28.1.3: resolution: {integrity: sha512-N4GT5on8UkZgH0O5LUavMRV1EDEhNTL0KEfRmDIeZHSV7p2XgLoY9t9VDUgL6o+yfdgYHVxuz81G8oB9VG5uyA==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} hasBin: true @@ -16589,19 +9807,9 @@ packages: peerDependenciesMeta: node-notifier: optional: true - dependencies: - '@jest/core': 28.1.3(ts-node@10.8.2) - '@jest/types': 28.1.3 - import-local: 3.1.0 - jest-cli: 28.1.3(@types/node@18.15.11)(ts-node@10.8.2) - transitivePeerDependencies: - - '@types/node' - - supports-color - - ts-node - dev: true - /jest@29.5.0(@types/node@16.18.23)(ts-node@10.8.2): - resolution: {integrity: sha512-juMg3he2uru1QoXX078zTa7pO85QyB9xajZc6bU+d9yEGwrKX6+vGmJQ3UdVZsvTEUARIdObzH68QItim6OSSQ==} + jest@29.7.0: + resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true peerDependencies: @@ -16609,115 +9817,49 @@ packages: peerDependenciesMeta: node-notifier: optional: true - dependencies: - '@jest/core': 29.5.0(ts-node@10.8.2) - '@jest/types': 29.5.0 - import-local: 3.1.0 - jest-cli: 29.5.0(@types/node@16.18.23)(ts-node@10.8.2) - transitivePeerDependencies: - - '@types/node' - - supports-color - - ts-node - dev: true - /jest@29.5.0(@types/node@18.15.11)(ts-node@10.8.2): - resolution: {integrity: sha512-juMg3he2uru1QoXX078zTa7pO85QyB9xajZc6bU+d9yEGwrKX6+vGmJQ3UdVZsvTEUARIdObzH68QItim6OSSQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jiti@1.21.6: + resolution: {integrity: sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==} hasBin: true - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - dependencies: - '@jest/core': 29.5.0(ts-node@10.8.2) - '@jest/types': 29.5.0 - import-local: 3.1.0 - jest-cli: 29.5.0(@types/node@18.15.11)(ts-node@10.8.2) - transitivePeerDependencies: - - '@types/node' - - supports-color - - ts-node - dev: true - /jiti@1.18.2: - resolution: {integrity: sha512-QAdOptna2NYiSSpv0O/BwoHBSmz4YhpzJHyi+fnMRTXFjp7B8i/YG5Z8IfusxB1ufjcD2Sre1F3R+nX3fvy7gg==} + jiti@2.4.2: + resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} hasBin: true - /jose@4.13.1: - resolution: {integrity: sha512-MSJQC5vXco5Br38mzaQKiq9mwt7lwj2eXpgpRyQYNHYt2lq1PjkWa7DLXX0WVcQLE9HhMh3jPiufS7fhJf+CLQ==} - dev: false + jose@4.15.9: + resolution: {integrity: sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==} - /js-beautify@1.14.7: - resolution: {integrity: sha512-5SOX1KXPFKx+5f6ZrPsIPEY7NwKeQz47n3jm2i+XeHx9MoRsfQenlOP13FQhWvg8JRS0+XLO6XYUQ2GX+q+T9A==} - engines: {node: '>=10'} + js-beautify@1.15.1: + resolution: {integrity: sha512-ESjNzSlt/sWE8sciZH8kBF8BPlwXPwhR6pWKAw8bw4Bwj+iZcnKW6ONWUutJ7eObuBZQpiIb8S7OYspWrKt7rA==} + engines: {node: '>=14'} hasBin: true - dependencies: - config-chain: 1.1.13 - editorconfig: 0.15.3 - glob: 8.1.0 - nopt: 6.0.0 - dev: false - /js-cookie@2.2.1: + js-cookie@2.2.1: resolution: {integrity: sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==} - dev: false - /js-cookie@3.0.1: + js-cookie@3.0.1: resolution: {integrity: sha512-+0rgsUXZu4ncpPxRL+lNEptWMOWl9etvPHc/koSRp6MPwpRYAhmk0dUG00J4bxVV3r9uUzfo24wW0knS07SKSw==} engines: {node: '>=12'} - dev: true - /js-sdsl@4.4.0: - resolution: {integrity: sha512-FfVSdx6pJ41Oa+CF7RDaFmTnCaFhua+SNYQX74riGOpl96x+2jQCqEfQ2bnXu/5DPCqlRuiqyvTJM0Qjz26IVg==} + js-cookie@3.0.5: + resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==} + engines: {node: '>=14'} - /js-tokens@4.0.0: + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - /js-yaml@3.14.1: + js-yaml@3.14.1: resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} hasBin: true - dependencies: - argparse: 1.0.10 - esprima: 4.0.1 - /js-yaml@4.1.0: + js-yaml@4.1.0: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true - dependencies: - argparse: 2.0.1 - - /js2xmlparser@4.0.2: - resolution: {integrity: sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==} - dependencies: - xmlcreate: 2.0.4 - dev: false - optional: true - /jsdoc@4.0.2: - resolution: {integrity: sha512-e8cIg2z62InH7azBBi3EsSEqrKx+nUtAS5bBcYTSpZFA+vhNPyhv8PTFZ0WsjOPDj04/dOLlm08EDcQJDqaGQg==} - engines: {node: '>=12.0.0'} - hasBin: true - dependencies: - '@babel/parser': 7.21.4 - '@jsdoc/salty': 0.2.5 - '@types/markdown-it': 12.2.3 - bluebird: 3.7.2 - catharsis: 0.9.0 - escape-string-regexp: 2.0.0 - js2xmlparser: 4.0.2 - klaw: 3.0.0 - markdown-it: 12.3.2 - markdown-it-anchor: 8.6.7(@types/markdown-it@12.2.3)(markdown-it@12.3.2) - marked: 4.3.0 - mkdirp: 1.0.4 - requizzle: 0.2.4 - strip-json-comments: 3.1.1 - underscore: 1.13.6 - dev: false - optional: true + jsbn@1.1.0: + resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==} - /jsdom@16.7.0: + jsdom@16.7.0: resolution: {integrity: sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==} engines: {node: '>=10'} peerDependencies: @@ -16725,930 +9867,446 @@ packages: peerDependenciesMeta: canvas: optional: true - dependencies: - abab: 2.0.6 - acorn: 8.8.2 - acorn-globals: 6.0.0 - cssom: 0.4.4 - cssstyle: 2.3.0 - data-urls: 2.0.0 - decimal.js: 10.4.3 - domexception: 2.0.1 - escodegen: 2.0.0 - form-data: 3.0.1 - html-encoding-sniffer: 2.0.1 - http-proxy-agent: 4.0.1 - https-proxy-agent: 5.0.1 - is-potential-custom-element-name: 1.0.1 - nwsapi: 2.2.3 - parse5: 6.0.1 - saxes: 5.0.1 - symbol-tree: 3.2.4 - tough-cookie: 4.1.2 - w3c-hr-time: 1.0.2 - w3c-xmlserializer: 2.0.0 - webidl-conversions: 6.1.0 - whatwg-encoding: 1.0.5 - whatwg-mimetype: 2.3.0 - whatwg-url: 8.7.0 - ws: 7.5.9 - xml-name-validator: 3.0.0 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - dev: false - - /jsesc@0.5.0: - resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==} - hasBin: true - - /jsesc@2.5.2: - resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} - engines: {node: '>=4'} - hasBin: true - /jsesc@3.0.2: + jsesc@3.0.2: resolution: {integrity: sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==} engines: {node: '>=6'} hasBin: true - dev: true - /json-bigint@1.0.0: + json-bigint@1.0.0: resolution: {integrity: sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==} - dependencies: - bignumber.js: 9.1.1 - dev: false - /json-buffer@3.0.1: + json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - dev: true - - /json-grab@0.1.0-alpha.2: - resolution: {integrity: sha512-9aoAuQ+32pyfOhsLbPfAY9cYg0XuUkhTw8FDDX2SS+9gsz+Z0VtnMjE0SLIlarfdJ9uVN+xTPoDeuWLNnWKQxw==} - engines: {node: 16.x} - dev: false - /json-parse-better-errors@1.0.2: - resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==} - dev: true - - /json-parse-even-better-errors@2.3.1: + json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - /json-schema-compare@0.2.2: - resolution: {integrity: sha512-c4WYmDKyJXhs7WWvAWm3uIYnfyWFoIp+JEoX34rctVvEkMYCPGhXtvmFFXiffBbxfZsvQ0RNnV5H7GvDF5HCqQ==} - dependencies: - lodash: 4.17.21 - dev: false + json-parse-even-better-errors@3.0.2: + resolution: {integrity: sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - /json-schema-faker@0.5.3: - resolution: {integrity: sha512-BeIrR0+YSrTbAR9dOMnjbFl1MvHyXnq+Wpdw1FpWZDHWKLzK229hZ5huyPcmzFUfVq1ODwf40WdGVoE266UBUg==} - hasBin: true - dependencies: - json-schema-ref-parser: 6.1.0 - jsonpath-plus: 7.2.0 - dev: false + json-schema-compare@0.2.2: + resolution: {integrity: sha512-c4WYmDKyJXhs7WWvAWm3uIYnfyWFoIp+JEoX34rctVvEkMYCPGhXtvmFFXiffBbxfZsvQ0RNnV5H7GvDF5HCqQ==} - /json-schema-merge-allof@0.8.1: + json-schema-merge-allof@0.8.1: resolution: {integrity: sha512-CTUKmIlPJbsWfzRRnOXz+0MjIqvnleIXwFTzz+t9T86HnYX/Rozria6ZVGLktAU9e+NygNljveP+yxqtQp/Q4w==} engines: {node: '>=12.0.0'} - dependencies: - compute-lcm: 1.1.2 - json-schema-compare: 0.2.2 - lodash: 4.17.21 - dev: false - - /json-schema-ref-parser@6.1.0: - resolution: {integrity: sha512-pXe9H1m6IgIpXmE5JSb8epilNTGsmTb2iPohAXpOdhqGFbQjNeHHsZxU+C8w6T81GZxSPFLeUoqDJmzxx5IGuw==} - deprecated: Please switch to @apidevtools/json-schema-ref-parser - dependencies: - call-me-maybe: 1.0.2 - js-yaml: 3.14.1 - ono: 4.0.11 - dev: false - /json-schema-to-ts@1.6.4: - resolution: {integrity: sha512-pR4yQ9DHz6itqswtHCm26mw45FSNfQ9rEQjosaZErhn5J3J2sIViQiz8rDaezjKAhFGpmsoczYVBgGHzFw/stA==} - dependencies: - '@types/json-schema': 7.0.11 - ts-toolbelt: 6.15.5 - dev: true - - /json-schema-traverse@0.4.1: + json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} - /json-schema-traverse@1.0.0: + json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} - /json-schema@0.4.0: + json-schema@0.4.0: resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} - dev: false - /json-stable-stringify-without-jsonify@1.0.1: + json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} - /json-stringify-nice@1.1.4: + json-stringify-nice@1.1.4: resolution: {integrity: sha512-5Z5RFW63yxReJ7vANgW6eZFGWaQvnPE3WNmZoOJrSkGju2etKA2L5rrOa1sm877TVTFt57A80BH1bArcmlLfPw==} - /json2mq@0.2.0: + json2mq@0.2.0: resolution: {integrity: sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==} - dependencies: - string-convert: 0.2.1 - /json5@1.0.2: + json5@1.0.2: resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} hasBin: true - dependencies: - minimist: 1.2.8 - /json5@2.2.3: + json5@2.2.3: resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} engines: {node: '>=6'} hasBin: true - /jsonc-parser@3.2.0: - resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} - dev: true - - /jsonfile@4.0.0: - resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} - optionalDependencies: - graceful-fs: 4.2.11 - dev: true + jsondiffpatch@0.5.0: + resolution: {integrity: sha512-Quz3MvAwHxVYNXsOByL7xI5EB2WYOeFswqaHIA3qOK3isRWTxiplBEocmmru6XmxDB2L7jDNYtYA4FyimoAFEw==} + engines: {node: '>=8.17.0'} + hasBin: true + bundledDependencies: [] - /jsonfile@6.1.0: + jsonfile@6.1.0: resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} - dependencies: - universalify: 2.0.0 - optionalDependencies: - graceful-fs: 4.2.11 - /jsonparse@1.3.1: + jsonparse@1.3.1: resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} engines: {'0': node >= 0.2.0} - /jsonpath-plus@7.2.0: - resolution: {integrity: sha512-zBfiUPM5nD0YZSBT/o/fbCUlCcepMIdP0CJZxM1+KgA4f2T206f6VAg9e7mX35+KlMaIc5qXW34f3BnwJ3w+RA==} - engines: {node: '>=12.0.0'} - dev: false + jsonpath@1.1.1: + resolution: {integrity: sha512-l6Cg7jRpixfbgoWgkrl77dgEj8RPvND0wMH6TwQmi9Qs4TFfS9u5cUFnbeKTwj5ga5Y3BTGGNI28k117LJ009w==} - /jsonpointer@5.0.1: + jsonpointer@5.0.1: resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==} engines: {node: '>=0.10.0'} - dev: false - - /jsonwebtoken@8.5.1: - resolution: {integrity: sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==} - engines: {node: '>=4', npm: '>=1.4.28'} - dependencies: - jws: 3.2.2 - lodash.includes: 4.3.0 - lodash.isboolean: 3.0.3 - lodash.isinteger: 4.0.4 - lodash.isnumber: 3.0.3 - lodash.isplainobject: 4.0.6 - lodash.isstring: 4.0.1 - lodash.once: 4.1.1 - ms: 2.1.3 - semver: 5.7.1 - dev: false - /jsonwebtoken@9.0.0: - resolution: {integrity: sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw==} + jsonwebtoken@9.0.2: + resolution: {integrity: sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==} engines: {node: '>=12', npm: '>=6'} - dependencies: - jws: 3.2.2 - lodash: 4.17.21 - ms: 2.1.3 - semver: 7.5.0 - dev: false - /jsx-ast-utils@3.3.3: - resolution: {integrity: sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==} + jsprim@2.0.2: + resolution: {integrity: sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==} + engines: {'0': node >=0.6.0} + + jsx-ast-utils@3.3.5: + resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} engines: {node: '>=4.0'} - dependencies: - array-includes: 3.1.6 - object.assign: 4.1.4 - /juice@9.0.0: - resolution: {integrity: sha512-s/IwgQ4caZq3bSnQZlKfdGUqJWy9WzTzB12WSPko9G8uK74H8BJEQvX7GLmFAQ6SLFgAppqC/TUYepKZZaV+JA==} - engines: {node: '>=10.0.0'} + juice@11.0.0: + resolution: {integrity: sha512-sGF8hPz9/Wg+YXbaNDqc1Iuoaw+J/P9lBHNQKXAGc9pPNjCd4fyPai0Zxj7MRtdjMr0lcgk5PjEIkP2b8R9F3w==} + engines: {node: '>=18.17'} hasBin: true - dependencies: - cheerio: 1.0.0-rc.12 - commander: 6.2.1 - mensch: 0.3.4 - slick: 1.12.2 - web-resource-inliner: 6.0.1 - transitivePeerDependencies: - - encoding - dev: false - /just-diff-apply@5.5.0: + just-diff-apply@5.5.0: resolution: {integrity: sha512-OYTthRfSh55WOItVqwpefPtNt2VdKsq5AnAK6apdtR6yCH8pr0CmSr710J0Mf+WdQy7K/OzMy7K2MgAfdQURDw==} - /just-diff@5.2.0: + just-diff@5.2.0: resolution: {integrity: sha512-6ufhP9SHjb7jibNFrNxyFZ6od3g+An6Ai9mhGRvcYe8UJlH0prseN64M+6ZBBUoKYHZsitDP42gAJ8+eVWr3lw==} - /jwa@1.4.1: + jwa@1.4.1: resolution: {integrity: sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==} - dependencies: - buffer-equal-constant-time: 1.0.1 - ecdsa-sig-formatter: 1.0.11 - safe-buffer: 5.2.1 - dev: false - /jwa@2.0.0: + jwa@2.0.0: resolution: {integrity: sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==} - dependencies: - buffer-equal-constant-time: 1.0.1 - ecdsa-sig-formatter: 1.0.11 - safe-buffer: 5.2.1 - dev: false - /jwks-rsa@3.0.1: - resolution: {integrity: sha512-UUOZ0CVReK1QVU3rbi9bC7N5/le8ziUj0A2ef1Q0M7OPD2KvjEYizptqIxGIo6fSLYDkqBrazILS18tYuRc8gw==} + jwks-rsa@3.2.0: + resolution: {integrity: sha512-PwchfHcQK/5PSydeKCs1ylNym0w/SSv8a62DgHJ//7x2ZclCoinlsjAfDxAAbpoTPybOum/Jgy+vkvMmKz89Ww==} engines: {node: '>=14'} - dependencies: - '@types/express': 4.17.17 - '@types/jsonwebtoken': 9.0.1 - debug: 4.3.4 - jose: 4.13.1 - limiter: 1.1.5 - lru-memoizer: 2.2.0 - transitivePeerDependencies: - - supports-color - dev: false - /jws@3.2.2: + jws@3.2.2: resolution: {integrity: sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==} - dependencies: - jwa: 1.4.1 - safe-buffer: 5.2.1 - dev: false - /jws@4.0.0: + jws@4.0.0: resolution: {integrity: sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==} - dependencies: - jwa: 2.0.0 - safe-buffer: 5.2.1 - dev: false - - /kafkajs-snappy@1.1.0: - resolution: {integrity: sha512-M4h2WhlxhXmR64z8nwzfGa1Dhhz78vykRfySRL8tuYQ8e6JSOuclbF2FJ8jeMJP3EZWw3uhjvwHlz7Ucu3UdWA==} - engines: {node: '>=8.0.0'} - dependencies: - snappyjs: 0.6.1 - dev: false - /kafkajs@2.2.4: - resolution: {integrity: sha512-j/YeapB1vfPT2iOIUn/vxdyKEuhuY2PxMBvf5JWux6iSaukAccrMtXEY/Lb7OvavDhOWME589bpLrEdnVHjfjA==} - engines: {node: '>=14.0.0'} - dev: false - - /keyv@4.5.2: - resolution: {integrity: sha512-5MHbFaKn8cNSmVW7BYnijeAVlE4cYA/SVkifVgrh7yotnfhKmjuXpDKjrABLnT0SfHWV21P8ow07OGfRrNDg8g==} - dependencies: - json-buffer: 3.0.1 - dev: true + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - /kind-of@6.0.3: + kind-of@6.0.3: resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} engines: {node: '>=0.10.0'} - /klaw@3.0.0: - resolution: {integrity: sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==} - dependencies: - graceful-fs: 4.2.11 - dev: false - optional: true - - /kleur@3.0.3: + kleur@3.0.3: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} engines: {node: '>=6'} - /kleur@4.1.5: + kleur@4.1.5: resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} engines: {node: '>=6'} - dev: true - /klona@2.0.6: + klona@2.0.6: resolution: {integrity: sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==} engines: {node: '>= 8'} - /language-subtag-registry@0.3.22: - resolution: {integrity: sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==} + language-subtag-registry@0.3.23: + resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==} - /language-tags@1.0.5: - resolution: {integrity: sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ==} - dependencies: - language-subtag-registry: 0.3.22 + language-tags@1.0.9: + resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==} + engines: {node: '>=0.10'} - /launch-editor@2.6.0: - resolution: {integrity: sha512-JpDCcQnyAAzZZaZ7vEiSqL690w7dAEyLao+KC96zBplnYbJS7TYNjvM3M7y3dGz+v7aIsJk3hllWuc0kWAjyRQ==} - dependencies: - picocolors: 1.0.0 - shell-quote: 1.8.1 - dev: false + launch-editor@2.9.1: + resolution: {integrity: sha512-Gcnl4Bd+hRO9P9icCP/RVVT2o8SFlPXofuCxvA2SaZuH45whSvf5p8x5oih5ftLiVhEI4sp5xDY+R+b3zJBh5w==} - /lazystream@1.0.1: + lazystream@1.0.1: resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==} engines: {node: '>= 0.6.3'} - dependencies: - readable-stream: 2.3.8 - dev: true - /less-loader@10.2.0(less@4.1.3)(webpack@5.79.0): + leac@0.6.0: + resolution: {integrity: sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==} + + less-loader@10.2.0: resolution: {integrity: sha512-AV5KHWvCezW27GT90WATaDnfXBv99llDbtaj4bshq6DvAihMdNjaPDcUMa6EXKLRF+P2opFenJp89BXg91XLYg==} engines: {node: '>= 12.13.0'} peerDependencies: less: ^3.5.0 || ^4.0.0 webpack: ^5.0.0 - dependencies: - klona: 2.0.6 - less: 4.1.3 - webpack: 5.79.0(esbuild@0.16.3) - dev: true - /less@4.1.3: - resolution: {integrity: sha512-w16Xk/Ta9Hhyei0Gpz9m7VS8F28nieJaL/VyShID7cYvP6IL5oHeL6p4TXSDJqZE/lNv0oJ2pGVjJsRkfwm5FA==} + less@4.2.0: + resolution: {integrity: sha512-P3b3HJDBtSzsXUl0im2L7gTO5Ubg8mEN6G8qoTS77iXxXX4Hvu4Qj540PZDvQ8V6DmX6iXo98k7Md0Cm1PrLaA==} engines: {node: '>=6'} hasBin: true - dependencies: - copy-anything: 2.0.6 - parse-node-version: 1.0.1 - tslib: 2.5.0 - optionalDependencies: - errno: 0.1.8 - graceful-fs: 4.2.11 - image-size: 0.5.5 - make-dir: 2.1.0 - mime: 1.6.0 - needle: 3.2.0 - source-map: 0.6.1 - transitivePeerDependencies: - - supports-color - dev: true - /leven@3.1.0: + leven@3.1.0: resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} engines: {node: '>=6'} - /levn@0.3.0: + levn@0.3.0: resolution: {integrity: sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==} engines: {node: '>= 0.8.0'} - dependencies: - prelude-ls: 1.1.2 - type-check: 0.3.2 - /levn@0.4.1: + levn@0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} - dependencies: - prelude-ls: 1.2.1 - type-check: 0.4.0 - /lilconfig@2.1.0: + lilconfig@2.1.0: resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} engines: {node: '>=10'} - /limiter@1.1.5: + lilconfig@3.1.2: + resolution: {integrity: sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==} + engines: {node: '>=14'} + + limiter@1.1.5: resolution: {integrity: sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==} - dev: false - /lines-and-columns@1.2.4: + lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - /linkify-it@3.0.3: - resolution: {integrity: sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==} - dependencies: - uc.micro: 1.0.6 - dev: false - optional: true - - /load-yaml-file@0.2.0: + load-yaml-file@0.2.0: resolution: {integrity: sha512-OfCBkGEw4nN6JLtgRidPX6QxjBQGQf72q3si2uvqyFEMbycSFFHwAZeXx6cJgFM9wmLrf9zBwCP3Ivqa+LLZPw==} engines: {node: '>=6'} - dependencies: - graceful-fs: 4.2.11 - js-yaml: 3.14.1 - pify: 4.0.1 - strip-bom: 3.0.0 - /loader-runner@4.3.0: + loader-runner@4.3.0: resolution: {integrity: sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==} engines: {node: '>=6.11.5'} - /loader-utils@1.4.2: - resolution: {integrity: sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==} - engines: {node: '>=4.0.0'} - dependencies: - big.js: 5.2.2 - emojis-list: 3.0.0 - json5: 1.0.2 - dev: true - - /loader-utils@2.0.4: + loader-utils@2.0.4: resolution: {integrity: sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==} engines: {node: '>=8.9.0'} - dependencies: - big.js: 5.2.2 - emojis-list: 3.0.0 - json5: 2.2.3 - /loader-utils@3.2.1: - resolution: {integrity: sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==} + loader-utils@3.3.1: + resolution: {integrity: sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg==} engines: {node: '>= 12.13.0'} - /locate-path@3.0.0: + locate-path@3.0.0: resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==} engines: {node: '>=6'} - dependencies: - p-locate: 3.0.0 - path-exists: 3.0.0 - dev: false - /locate-path@5.0.0: + locate-path@5.0.0: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} engines: {node: '>=8'} - dependencies: - p-locate: 4.1.0 - /locate-path@6.0.0: + locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} - dependencies: - p-locate: 5.0.0 - /lodash-es@4.17.21: + locate-path@7.2.0: + resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + lodash-es@4.17.21: resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} - dev: false - /lodash.camelcase@4.3.0: + lodash.camelcase@4.3.0: resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} - /lodash.castarray@4.4.0: + lodash.castarray@4.4.0: resolution: {integrity: sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==} - dev: true - /lodash.clonedeep@4.5.0: + lodash.clonedeep@4.5.0: resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==} - dev: false - /lodash.curry@4.1.1: + lodash.curry@4.1.1: resolution: {integrity: sha512-/u14pXGviLaweY5JI0IUzgzF2J6Ne8INyzAZjImcryjgkZ+ebruBxy2/JaOOkTqScddcYtakjhSaeemV8lR0tA==} - dev: false - /lodash.debounce@4.0.8: + lodash.debounce@4.0.8: resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} - /lodash.defaults@4.2.0: + lodash.defaults@4.2.0: resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} - /lodash.difference@4.5.0: + lodash.difference@4.5.0: resolution: {integrity: sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==} - dev: true - /lodash.flatten@4.4.0: + lodash.flatten@4.4.0: resolution: {integrity: sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==} - dev: true - /lodash.flow@3.5.0: + lodash.flow@3.5.0: resolution: {integrity: sha512-ff3BX/tSioo+XojX4MOsOMhJw0nZoUEF011LX8g8d3gvjVbxd89cCio4BCXronjxcTUIJUoqKEUA+n4CqvvRPw==} - dev: false - /lodash.includes@4.3.0: + lodash.get@4.4.2: + resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==} + + lodash.includes@4.3.0: resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} - dev: false - /lodash.isarguments@3.1.0: + lodash.isarguments@3.1.0: resolution: {integrity: sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==} - /lodash.isboolean@3.0.3: + lodash.isboolean@3.0.3: resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} - dev: false - /lodash.isinteger@4.0.4: + lodash.isinteger@4.0.4: resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==} - dev: false - /lodash.isnumber@3.0.3: + lodash.isnumber@3.0.3: resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==} - dev: false - /lodash.isplainobject@4.0.6: + lodash.isplainobject@4.0.6: resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} - /lodash.isstring@4.0.1: + lodash.isstring@4.0.1: resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} - dev: false - /lodash.memoize@4.1.2: + lodash.memoize@4.1.2: resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} - /lodash.merge@4.6.2: + lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - /lodash.once@4.1.1: + lodash.once@4.1.1: resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} - dev: false - /lodash.sortby@4.7.0: + lodash.sortby@4.7.0: resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} - dev: false - /lodash.union@4.6.0: + lodash.union@4.6.0: resolution: {integrity: sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==} - dev: true - /lodash.uniq@4.5.0: + lodash.uniq@4.5.0: resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} - /lodash@4.17.21: + lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - /log-symbols@4.1.0: + log-symbols@4.1.0: resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} engines: {node: '>=10'} - dependencies: - chalk: 4.1.2 - is-unicode-supported: 0.1.0 - /long@4.0.0: - resolution: {integrity: sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==} + log-symbols@6.0.0: + resolution: {integrity: sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==} + engines: {node: '>=18'} + + log-symbols@7.0.1: + resolution: {integrity: sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==} + engines: {node: '>=18'} - /long@5.2.1: - resolution: {integrity: sha512-GKSNGeNAtw8IryjjkhZxuKB3JzlcLTwjtiQCHKvqQet81I93kXslhDQruGI/QsddO83mcDToBVy7GqGS/zYf/A==} + long@4.0.0: + resolution: {integrity: sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==} - /longest-streak@3.1.0: - resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} - dev: true + long@5.2.3: + resolution: {integrity: sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==} - /loose-envify@1.4.0: + loose-envify@1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true - dependencies: - js-tokens: 4.0.0 - - /lower-case@1.1.4: - resolution: {integrity: sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==} - dev: false - /lower-case@2.0.2: + lower-case@2.0.2: resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} - dependencies: - tslib: 2.5.0 - dev: false - - /lowercase-keys@2.0.0: - resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==} - engines: {node: '>=8'} - dev: true - /lru-cache@4.0.2: - resolution: {integrity: sha512-uQw9OqphAGiZhkuPlpFGmdTU2tEuhxTourM/19qGJrxBPHAr/f8BT1a0i/lOclESnGatdJG/UCkP9kZB/Lh1iw==} - dependencies: - pseudomap: 1.0.2 - yallist: 2.1.2 - dev: false + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - /lru-cache@4.1.5: - resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} - dependencies: - pseudomap: 1.0.2 - yallist: 2.1.2 - dev: false + lru-cache@11.1.0: + resolution: {integrity: sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==} + engines: {node: 20 || >=22} - /lru-cache@5.1.1: + lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - dependencies: - yallist: 3.1.1 - /lru-cache@6.0.0: + lru-cache@6.0.0: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} engines: {node: '>=10'} - dependencies: - yallist: 4.0.0 - /lru-cache@7.18.3: + lru-cache@7.18.3: resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} engines: {node: '>=12'} - /lru-memoizer@2.2.0: - resolution: {integrity: sha512-QfOZ6jNkxCcM/BkIPnFsqDhtrazLRsghi9mBwFAzol5GCvj4EkFT899Za3+QwikCg5sRX8JstioBDwOxEyzaNw==} - dependencies: - lodash.clonedeep: 4.5.0 - lru-cache: 4.0.2 - dev: false + lru-memoizer@2.3.0: + resolution: {integrity: sha512-GXn7gyHAMhO13WSKrIiNfztwxodVsP8IoZ3XfrJV4yH2x0/OeTO/FIaAHTY5YekdGgW94njfuKmyyt1E0mR6Ug==} - /lucide-react@0.125.0(react@18.2.0): - resolution: {integrity: sha512-tadphtB6TPytEitR9vX75hqu9PQT/uz5RcvXMq976nC190eukAM9+cHMgBxfvfEGDXwIhIT9aFxTUGdAjxw9uQ==} + lucide-react@0.447.0: + resolution: {integrity: sha512-SZ//hQmvi+kDKrNepArVkYK7/jfeZ5uFNEnYmd45RKZcbGD78KLnrcNXmgeg6m+xNHFvTG+CblszXCy4n6DN4w==} peerDependencies: - react: ^16.5.1 || ^17.0.0 || ^18.0.0 - dependencies: - react: 18.2.0 - dev: false + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc - /lz-string@1.5.0: + lz-string@1.5.0: resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} hasBin: true - dev: true - - /magic-string@0.22.5: - resolution: {integrity: sha512-oreip9rJZkzvA8Qzk9HFs8fZGF/u7H/gtrE8EN6RjKJ9kh2HlC+yQ2QezifqTZfGyiuAV0dRv5a+y/8gBb1m9w==} - dependencies: - vlq: 0.2.3 - dev: true - /magic-string@0.25.9: + magic-string@0.25.9: resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==} - dependencies: - sourcemap-codec: 1.4.8 - /magic-string@0.27.0: - resolution: {integrity: sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==} - engines: {node: '>=12'} - dependencies: - '@jridgewell/sourcemap-codec': 1.4.15 - dev: true + magic-string@0.30.12: + resolution: {integrity: sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==} - /make-dir@2.1.0: + magic-string@0.30.17: + resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + + make-dir@2.1.0: resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==} engines: {node: '>=6'} - requiresBuild: true - dependencies: - pify: 4.0.1 - semver: 5.7.1 - dev: true - optional: true - /make-dir@3.1.0: + make-dir@3.1.0: resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} engines: {node: '>=8'} - dependencies: - semver: 6.3.0 - /make-error@1.3.6: + make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + + make-error@1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} - /make-fetch-happen@10.2.1: + make-fetch-happen@10.2.1: resolution: {integrity: sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - dependencies: - agentkeepalive: 4.3.0 - cacache: 16.1.3 - http-cache-semantics: 4.1.1 - http-proxy-agent: 5.0.0 - https-proxy-agent: 5.0.1 - is-lambda: 1.0.1 - lru-cache: 7.18.3 - minipass: 3.3.6 - minipass-collect: 1.0.2 - minipass-fetch: 2.1.2 - minipass-flush: 1.0.5 - minipass-pipeline: 1.2.4 - negotiator: 0.6.3 - promise-retry: 2.0.1 - socks-proxy-agent: 7.0.0 - ssri: 9.0.1 - transitivePeerDependencies: - - bluebird - - supports-color - /make-fetch-happen@9.1.0: + make-fetch-happen@11.1.1: + resolution: {integrity: sha512-rLWS7GCSTcEujjVBs2YqG7Y4643u8ucvCJeSRqiLYhesrDuzeuFIk37xREzAsfQaqzl8b9rNCE4m6J8tvX4Q8w==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + make-fetch-happen@9.1.0: resolution: {integrity: sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==} engines: {node: '>= 10'} - dependencies: - agentkeepalive: 4.3.0 - cacache: 15.3.0 - http-cache-semantics: 4.1.1 - http-proxy-agent: 4.0.1 - https-proxy-agent: 5.0.1 - is-lambda: 1.0.1 - lru-cache: 6.0.0 - minipass: 3.3.6 - minipass-collect: 1.0.2 - minipass-fetch: 1.4.1 - minipass-flush: 1.0.5 - minipass-pipeline: 1.2.4 - negotiator: 0.6.3 - promise-retry: 2.0.1 - socks-proxy-agent: 6.2.1 - ssri: 8.0.1 - transitivePeerDependencies: - - bluebird - - supports-color - /makeerror@1.0.12: + makeerror@1.0.12: resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} - dependencies: - tmpl: 1.0.5 - - /markdown-extensions@1.1.1: - resolution: {integrity: sha512-WWC0ZuMzCyDHYCasEGs4IPvLyTGftYwh6wIEOULOF0HXcqZlhwRzrK0w2VUlxWA98xnvb/jszw4ZSkJ6ADpM6Q==} - engines: {node: '>=0.10.0'} - dev: true - - /markdown-it-anchor@8.6.7(@types/markdown-it@12.2.3)(markdown-it@12.3.2): - resolution: {integrity: sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==} - peerDependencies: - '@types/markdown-it': '*' - markdown-it: '*' - dependencies: - '@types/markdown-it': 12.2.3 - markdown-it: 12.3.2 - dev: false - optional: true - /markdown-it@12.3.2: - resolution: {integrity: sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==} - hasBin: true - dependencies: - argparse: 2.0.1 - entities: 2.1.0 - linkify-it: 3.0.3 - mdurl: 1.0.1 - uc.micro: 1.0.6 - dev: false - optional: true + map-obj@4.3.0: + resolution: {integrity: sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==} + engines: {node: '>=8'} - /markdown-to-jsx@7.2.0(react@18.2.0): - resolution: {integrity: sha512-3l4/Bigjm4bEqjCR6Xr+d4DtM1X6vvtGsMGSjJYyep8RjjIvcWtrXBS8Wbfe1/P+atKNMccpsraESIaWVplzVg==} + markdown-to-jsx@7.5.0: + resolution: {integrity: sha512-RrBNcMHiFPcz/iqIj0n3wclzHXjwS7mzjBNWecKKVhNTIxQepIix6Il/wZCn2Cg5Y1ow2Qi84+eJrryFRWBEWw==} engines: {node: '>= 10'} peerDependencies: react: '>= 0.14.0' - dependencies: - react: 18.2.0 - dev: false - /marked@4.3.0: - resolution: {integrity: sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==} - engines: {node: '>= 12'} + marked@7.0.4: + resolution: {integrity: sha512-t8eP0dXRJMtMvBojtkcsA7n48BkauktUKzfkPSCq85ZMTJ0v76Rke4DYz01omYpPTUh4p/f7HePgRo3ebG8+QQ==} + engines: {node: '>= 16'} hasBin: true - dev: false - optional: true - /matched@5.0.1: + matched@5.0.1: resolution: {integrity: sha512-E1fhSTPRyhAlNaNvGXAgZQlq1hL0bgYMTk/6bktVlIhzUnX/SZs7296ACdVeNJE8xFNGSuvd9IpI7vSnmcqLvw==} engines: {node: '>=10'} - dependencies: - glob: 7.2.3 - picomatch: 2.3.1 - dev: true - /maxmin@2.1.0: + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + maxmin@2.1.0: resolution: {integrity: sha512-NWlApBjW9az9qRPaeg7CX4sQBWwytqz32bIEo1PW9pRW+kBP9KLRfJO3UC+TV31EcQZEUq7eMzikC7zt3zPJcw==} engines: {node: '>=0.12'} - dependencies: - chalk: 1.1.3 - figures: 1.7.0 - gzip-size: 3.0.0 - pretty-bytes: 3.0.1 - dev: true - - /mdast-util-definitions@5.1.2: - resolution: {integrity: sha512-8SVPMuHqlPME/z3gqVwWY4zVXn8lqKv/pAhC57FuJ40ImXyBpmO5ukh98zB2v7Blql2FiHjHv9LVztSIqjY+MA==} - dependencies: - '@types/mdast': 3.0.11 - '@types/unist': 2.0.6 - unist-util-visit: 4.1.2 - dev: true - - /mdast-util-from-markdown@1.3.0: - resolution: {integrity: sha512-HN3W1gRIuN/ZW295c7zi7g9lVBllMgZE40RxCX37wrTPWXCWtpvOZdfnuK+1WNpvZje6XuJeI3Wnb4TJEUem+g==} - dependencies: - '@types/mdast': 3.0.11 - '@types/unist': 2.0.6 - decode-named-character-reference: 1.0.2 - mdast-util-to-string: 3.2.0 - micromark: 3.1.0 - micromark-util-decode-numeric-character-reference: 1.0.0 - micromark-util-decode-string: 1.0.2 - micromark-util-normalize-identifier: 1.0.0 - micromark-util-symbol: 1.0.1 - micromark-util-types: 1.0.2 - unist-util-stringify-position: 3.0.3 - uvu: 0.5.6 - transitivePeerDependencies: - - supports-color - dev: true - - /mdast-util-frontmatter@1.0.1: - resolution: {integrity: sha512-JjA2OjxRqAa8wEG8hloD0uTU0kdn8kbtOWpPP94NBkfAlbxn4S8gCGf/9DwFtEeGPXrDcNXdiDjVaRdUFqYokw==} - dependencies: - '@types/mdast': 3.0.11 - mdast-util-to-markdown: 1.5.0 - micromark-extension-frontmatter: 1.1.0 - dev: true - - /mdast-util-mdx-expression@1.3.2: - resolution: {integrity: sha512-xIPmR5ReJDu/DHH1OoIT1HkuybIfRGYRywC+gJtI7qHjCJp/M9jrmBEJW22O8lskDWm562BX2W8TiAwRTb0rKA==} - dependencies: - '@types/estree-jsx': 1.0.0 - '@types/hast': 2.3.4 - '@types/mdast': 3.0.11 - mdast-util-from-markdown: 1.3.0 - mdast-util-to-markdown: 1.5.0 - transitivePeerDependencies: - - supports-color - dev: true - - /mdast-util-mdx-jsx@1.2.0: - resolution: {integrity: sha512-5+ot/kfxYd3ChgEMwsMUO71oAfYjyRI3pADEK4I7xTmWLGQ8Y7ghm1CG36zUoUvDPxMlIYwQV/9DYHAUWdG4dA==} - dependencies: - '@types/estree-jsx': 0.0.1 - '@types/mdast': 3.0.11 - mdast-util-to-markdown: 1.5.0 - parse-entities: 4.0.1 - stringify-entities: 4.0.3 - unist-util-remove-position: 4.0.2 - unist-util-stringify-position: 3.0.3 - vfile-message: 3.1.4 - dev: true - /mdast-util-mdx@1.1.0: - resolution: {integrity: sha512-leKb9uG7laXdyFlTleYV4ZEaCpsxeU1LlkkR/xp35pgKrfV1Y0fNCuOw9vaRc2a9YDpH22wd145Wt7UY5yzeZw==} - dependencies: - mdast-util-mdx-expression: 1.3.2 - mdast-util-mdx-jsx: 1.2.0 - mdast-util-mdxjs-esm: 1.3.1 - transitivePeerDependencies: - - supports-color - dev: true - - /mdast-util-mdxjs-esm@1.3.1: - resolution: {integrity: sha512-SXqglS0HrEvSdUEfoXFtcg7DRl7S2cwOXc7jkuusG472Mmjag34DUDeOJUZtl+BVnyeO1frIgVpHlNRWc2gk/w==} - dependencies: - '@types/estree-jsx': 1.0.0 - '@types/hast': 2.3.4 - '@types/mdast': 3.0.11 - mdast-util-from-markdown: 1.3.0 - mdast-util-to-markdown: 1.5.0 - transitivePeerDependencies: - - supports-color - dev: true - - /mdast-util-phrasing@3.0.1: - resolution: {integrity: sha512-WmI1gTXUBJo4/ZmSk79Wcb2HcjPJBzM1nlI/OUWA8yk2X9ik3ffNbBGsU+09BFmXaL1IBb9fiuvq6/KMiNycSg==} - dependencies: - '@types/mdast': 3.0.11 - unist-util-is: 5.2.1 - dev: true - - /mdast-util-to-hast@11.3.0: - resolution: {integrity: sha512-4o3Cli3hXPmm1LhB+6rqhfsIUBjnKFlIUZvudaermXB+4/KONdd/W4saWWkC+LBLbPMqhFSSTSRgafHsT5fVJw==} - dependencies: - '@types/hast': 2.3.4 - '@types/mdast': 3.0.11 - '@types/mdurl': 1.0.2 - mdast-util-definitions: 5.1.2 - mdurl: 1.0.1 - unist-builder: 3.0.1 - unist-util-generated: 2.0.1 - unist-util-position: 4.0.4 - unist-util-visit: 4.1.2 - dev: true - - /mdast-util-to-markdown@1.5.0: - resolution: {integrity: sha512-bbv7TPv/WC49thZPg3jXuqzuvI45IL2EVAr/KxF0BSdHsU0ceFHOmwQn6evxAh1GaoK/6GQ1wp4R4oW2+LFL/A==} - dependencies: - '@types/mdast': 3.0.11 - '@types/unist': 2.0.6 - longest-streak: 3.1.0 - mdast-util-phrasing: 3.0.1 - mdast-util-to-string: 3.2.0 - micromark-util-decode-string: 1.0.2 - unist-util-visit: 4.1.2 - zwitch: 2.0.4 - dev: true + maxmind@4.3.22: + resolution: {integrity: sha512-dfLO11mE77ELTEIXNezfW0eslodsFLsZ1lQkLauP+5Zsg1m7kCGtljqRyVOd9E5Ne2RJgvY6UU09qvnVocOZvA==} + engines: {node: '>=12', npm: '>=6'} - /mdast-util-to-string@3.2.0: - resolution: {integrity: sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==} - dependencies: - '@types/mdast': 3.0.11 - dev: true + md-to-react-email@5.0.5: + resolution: {integrity: sha512-OvAXqwq57uOk+WZqFFNCMZz8yDp8BD3WazW1wAKHUrPbbdr89K9DWS6JXY09vd9xNdPNeurI8DU/X4flcfaD8A==} + peerDependencies: + react: ^18.0 || ^19.0 - /mdn-data@2.0.14: + mdn-data@2.0.14: resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==} - /mdn-data@2.0.4: - resolution: {integrity: sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==} + mdn-data@2.0.28: + resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==} - /mdurl@1.0.1: - resolution: {integrity: sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==} + mdn-data@2.0.30: + resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} - /media-query-parser@2.0.2: - resolution: {integrity: sha512-1N4qp+jE0pL5Xv4uEcwVUhIkwdUO3S/9gML90nqKA7v7FcOS5vUtatfzok9S9U1EJU8dHWlcv95WLnKmmxZI9w==} - dependencies: - '@babel/runtime': 7.21.0 - dev: true + mdn-data@2.0.4: + resolution: {integrity: sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==} - /media-typer@0.3.0: + media-typer@0.3.0: resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} engines: {node: '>= 0.6'} - /mem-fs-editor@9.7.0(mem-fs@2.3.0): + mem-fs-editor@9.7.0: resolution: {integrity: sha512-ReB3YD24GNykmu4WeUL/FDIQtkoyGB6zfJv60yfCo3QjKeimNcTqv2FT83bP0ccs6uu+sm5zyoBlspAzigmsdg==} engines: {node: '>=12.10.0'} peerDependencies: @@ -17656,4621 +10314,20340 @@ packages: peerDependenciesMeta: mem-fs: optional: true - dependencies: - binaryextensions: 4.18.0 - commondir: 1.0.1 - deep-extend: 0.6.0 - ejs: 3.1.9 - globby: 11.1.0 - isbinaryfile: 5.0.0 - mem-fs: 2.3.0 - minimatch: 7.4.6 - multimatch: 5.0.0 - normalize-path: 3.0.0 - textextensions: 5.15.0 - /mem-fs@2.3.0: + mem-fs@2.3.0: resolution: {integrity: sha512-GftCCBs6EN8sz3BoWO1bCj8t7YBtT713d8bUgbhg9Iel5kFSqnSvCK06TYIDJAtJ51cSiWkM/YemlT0dfoFycw==} engines: {node: '>=12'} - dependencies: - '@types/node': 15.14.9 - '@types/vinyl': 2.0.7 - vinyl: 2.2.1 - vinyl-file: 3.0.0 - /memfs@3.5.0: - resolution: {integrity: sha512-yK6o8xVJlQerz57kvPROwTMgx5WtGwC2ZxDtOUsnGl49rHjYkfQoPNZPCKH73VdLE1BwBu/+Fx/NL8NYMUw2aA==} + memfs@3.5.3: + resolution: {integrity: sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==} engines: {node: '>= 4.0.0'} - dependencies: - fs-monkey: 1.0.3 - dev: false - /memory-pager@1.5.0: + memory-pager@1.5.0: resolution: {integrity: sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==} - dev: false - optional: true - /mensch@0.3.4: + mensch@0.3.4: resolution: {integrity: sha512-IAeFvcOnV9V0Yk+bFhYR07O3yNina9ANIN5MoXBKYJ/RLYPurd2d0yw14MDhpr9/momp0WofT1bPUh3hkzdi/g==} - dev: false - /merge-descriptors@1.0.1: - resolution: {integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==} + merge-descriptors@1.0.3: + resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} - /merge-stream@2.0.0: + merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - /merge2@1.4.1: + merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} - /methods@1.1.2: + methods@1.1.2: resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} engines: {node: '>= 0.6'} - /microbundle-crl@0.13.11: - resolution: {integrity: sha512-3roSSgtJdIvvIOJdhefWD8BoEjHkbInkJzUoSE+ER6NGcpTuPWXNFyAbXamCL9Qg8rTU4urbYtXusk/d1ZpteA==} + microbundle@0.15.1: + resolution: {integrity: sha512-aAF+nwFbkSIJGfrJk+HyzmJOq3KFaimH6OIFBU6J2DPjQeg1jXIYlIyEv81Gyisb9moUkudn+wj7zLNYMOv75Q==} hasBin: true - dependencies: - '@babel/core': 7.21.4 - '@babel/plugin-proposal-class-properties': 7.7.4(@babel/core@7.21.4) - '@babel/plugin-proposal-decorators': 7.21.0(@babel/core@7.21.4) - '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.21.4) - '@babel/plugin-syntax-jsx': 7.21.4(@babel/core@7.21.4) - '@babel/plugin-transform-flow-strip-types': 7.21.0(@babel/core@7.21.4) - '@babel/plugin-transform-react-jsx': 7.21.0(@babel/core@7.21.4) - '@babel/preset-env': 7.21.4(@babel/core@7.21.4) - '@babel/preset-flow': 7.21.4(@babel/core@7.21.4) - '@rollup/plugin-alias': 3.1.9(rollup@1.32.1) - '@rollup/plugin-commonjs': 11.1.0(rollup@1.32.1) - '@rollup/plugin-json': 4.1.0(rollup@1.32.1) - '@rollup/plugin-node-resolve': 6.1.0(rollup@1.32.1) - '@svgr/rollup': 5.5.0 - asyncro: 3.0.0 - autoprefixer: 9.8.8 - babel-plugin-macros: 2.8.0 - babel-plugin-transform-async-to-promises: 0.8.18 - babel-plugin-transform-replace-expressions: 0.2.0(@babel/core@7.21.4) - brotli-size: 4.0.0 - camelcase: 5.3.1 - cssnano: 4.1.11 - es6-promisify: 6.1.1 - filesize: 6.4.0 - gzip-size: 5.1.1 - kleur: 3.0.3 - lodash.merge: 4.6.2 - module-details-from-path: 1.0.3 - pretty-bytes: 5.6.0 - rollup: 1.32.1 - rollup-plugin-babel: 4.4.0(@babel/core@7.21.4)(rollup@1.32.1) - rollup-plugin-bundle-size: 1.0.3 - rollup-plugin-es3: 1.1.0 - rollup-plugin-postcss: 2.9.0 - rollup-plugin-smart-asset: 2.1.2(rollup@1.32.1) - rollup-plugin-terser: 5.3.1(rollup@1.32.1) - rollup-plugin-typescript2: 0.25.3(rollup@1.32.1)(typescript@3.9.10) - sade: 1.8.1 - tiny-glob: 0.2.9 - tslib: 1.14.1 - typescript: 3.9.10 - transitivePeerDependencies: - - supports-color - dev: true - - /micromark-core-commonmark@1.0.6: - resolution: {integrity: sha512-K+PkJTxqjFfSNkfAhp4GB+cZPfQd6dxtTXnf+RjZOV7T4EEXnvgzOcnp+eSTmpGk9d1S9sL6/lqrgSNn/s0HZA==} - dependencies: - decode-named-character-reference: 1.0.2 - micromark-factory-destination: 1.0.0 - micromark-factory-label: 1.0.2 - micromark-factory-space: 1.0.0 - micromark-factory-title: 1.0.2 - micromark-factory-whitespace: 1.0.0 - micromark-util-character: 1.1.0 - micromark-util-chunked: 1.0.0 - micromark-util-classify-character: 1.0.0 - micromark-util-html-tag-name: 1.1.0 - micromark-util-normalize-identifier: 1.0.0 - micromark-util-resolve-all: 1.0.0 - micromark-util-subtokenize: 1.0.2 - micromark-util-symbol: 1.0.1 - micromark-util-types: 1.0.2 - uvu: 0.5.6 - dev: true - - /micromark-extension-frontmatter@1.1.0: - resolution: {integrity: sha512-0nLelmvXR5aZ+F2IL6/Ed4cDnHLpL/VD/EELKuclsTWHrLI8UgxGHEmeoumeX2FXiM6z2WrBIOEcbKUZR8RYNg==} - dependencies: - fault: 2.0.1 - micromark-util-character: 1.1.0 - micromark-util-symbol: 1.0.1 - micromark-util-types: 1.0.2 - dev: true - - /micromark-extension-mdx-expression@1.0.4: - resolution: {integrity: sha512-TCgLxqW6ReQ3AJgtj1P0P+8ZThBTloLbeb7jNaqr6mCOLDpxUiBFE/9STgooMZttEwOQu5iEcCCa3ZSDhY9FGw==} - dependencies: - micromark-factory-mdx-expression: 1.0.7 - micromark-factory-space: 1.0.0 - micromark-util-character: 1.1.0 - micromark-util-events-to-acorn: 1.2.1 - micromark-util-symbol: 1.0.1 - micromark-util-types: 1.0.2 - uvu: 0.5.6 - dev: true - - /micromark-extension-mdx-jsx@1.0.3: - resolution: {integrity: sha512-VfA369RdqUISF0qGgv2FfV7gGjHDfn9+Qfiv5hEwpyr1xscRj/CiVRkU7rywGFCO7JwJ5L0e7CJz60lY52+qOA==} - dependencies: - '@types/acorn': 4.0.6 - estree-util-is-identifier-name: 2.1.0 - micromark-factory-mdx-expression: 1.0.7 - micromark-factory-space: 1.0.0 - micromark-util-character: 1.1.0 - micromark-util-symbol: 1.0.1 - micromark-util-types: 1.0.2 - uvu: 0.5.6 - vfile-message: 3.1.4 - dev: true - - /micromark-extension-mdx-md@1.0.0: - resolution: {integrity: sha512-xaRAMoSkKdqZXDAoSgp20Azm0aRQKGOl0RrS81yGu8Hr/JhMsBmfs4wR7m9kgVUIO36cMUQjNyiyDKPrsv8gOw==} - dependencies: - micromark-util-types: 1.0.2 - dev: true - - /micromark-extension-mdxjs-esm@1.0.3: - resolution: {integrity: sha512-2N13ol4KMoxb85rdDwTAC6uzs8lMX0zeqpcyx7FhS7PxXomOnLactu8WI8iBNXW8AVyea3KIJd/1CKnUmwrK9A==} - dependencies: - micromark-core-commonmark: 1.0.6 - micromark-util-character: 1.1.0 - micromark-util-events-to-acorn: 1.2.1 - micromark-util-symbol: 1.0.1 - micromark-util-types: 1.0.2 - unist-util-position-from-estree: 1.1.2 - uvu: 0.5.6 - vfile-message: 3.1.4 - dev: true - - /micromark-extension-mdxjs@1.0.0: - resolution: {integrity: sha512-TZZRZgeHvtgm+IhtgC2+uDMR7h8eTKF0QUX9YsgoL9+bADBpBY6SiLvWqnBlLbCEevITmTqmEuY3FoxMKVs1rQ==} - dependencies: - acorn: 8.8.2 - acorn-jsx: 5.3.2(acorn@8.8.2) - micromark-extension-mdx-expression: 1.0.4 - micromark-extension-mdx-jsx: 1.0.3 - micromark-extension-mdx-md: 1.0.0 - micromark-extension-mdxjs-esm: 1.0.3 - micromark-util-combine-extensions: 1.0.0 - micromark-util-types: 1.0.2 - dev: true - - /micromark-factory-destination@1.0.0: - resolution: {integrity: sha512-eUBA7Rs1/xtTVun9TmV3gjfPz2wEwgK5R5xcbIM5ZYAtvGF6JkyaDsj0agx8urXnO31tEO6Ug83iVH3tdedLnw==} - dependencies: - micromark-util-character: 1.1.0 - micromark-util-symbol: 1.0.1 - micromark-util-types: 1.0.2 - dev: true - - /micromark-factory-label@1.0.2: - resolution: {integrity: sha512-CTIwxlOnU7dEshXDQ+dsr2n+yxpP0+fn271pu0bwDIS8uqfFcumXpj5mLn3hSC8iw2MUr6Gx8EcKng1dD7i6hg==} - dependencies: - micromark-util-character: 1.1.0 - micromark-util-symbol: 1.0.1 - micromark-util-types: 1.0.2 - uvu: 0.5.6 - dev: true - - /micromark-factory-mdx-expression@1.0.7: - resolution: {integrity: sha512-QAdFbkQagTZ/eKb8zDGqmjvgevgJH3+aQpvvKrXWxNJp3o8/l2cAbbrBd0E04r0Gx6nssPpqWIjnbHFvZu5qsQ==} - dependencies: - micromark-factory-space: 1.0.0 - micromark-util-character: 1.1.0 - micromark-util-events-to-acorn: 1.2.1 - micromark-util-symbol: 1.0.1 - micromark-util-types: 1.0.2 - unist-util-position-from-estree: 1.1.2 - uvu: 0.5.6 - vfile-message: 3.1.4 - dev: true - - /micromark-factory-space@1.0.0: - resolution: {integrity: sha512-qUmqs4kj9a5yBnk3JMLyjtWYN6Mzfcx8uJfi5XAveBniDevmZasdGBba5b4QsvRcAkmvGo5ACmSUmyGiKTLZew==} - dependencies: - micromark-util-character: 1.1.0 - micromark-util-types: 1.0.2 - dev: true - - /micromark-factory-title@1.0.2: - resolution: {integrity: sha512-zily+Nr4yFqgMGRKLpTVsNl5L4PMu485fGFDOQJQBl2NFpjGte1e86zC0da93wf97jrc4+2G2GQudFMHn3IX+A==} - dependencies: - micromark-factory-space: 1.0.0 - micromark-util-character: 1.1.0 - micromark-util-symbol: 1.0.1 - micromark-util-types: 1.0.2 - uvu: 0.5.6 - dev: true - - /micromark-factory-whitespace@1.0.0: - resolution: {integrity: sha512-Qx7uEyahU1lt1RnsECBiuEbfr9INjQTGa6Err+gF3g0Tx4YEviPbqqGKNv/NrBaE7dVHdn1bVZKM/n5I/Bak7A==} - dependencies: - micromark-factory-space: 1.0.0 - micromark-util-character: 1.1.0 - micromark-util-symbol: 1.0.1 - micromark-util-types: 1.0.2 - dev: true - - /micromark-util-character@1.1.0: - resolution: {integrity: sha512-agJ5B3unGNJ9rJvADMJ5ZiYjBRyDpzKAOk01Kpi1TKhlT1APx3XZk6eN7RtSz1erbWHC2L8T3xLZ81wdtGRZzg==} - dependencies: - micromark-util-symbol: 1.0.1 - micromark-util-types: 1.0.2 - dev: true - - /micromark-util-chunked@1.0.0: - resolution: {integrity: sha512-5e8xTis5tEZKgesfbQMKRCyzvffRRUX+lK/y+DvsMFdabAicPkkZV6gO+FEWi9RfuKKoxxPwNL+dFF0SMImc1g==} - dependencies: - micromark-util-symbol: 1.0.1 - dev: true - - /micromark-util-classify-character@1.0.0: - resolution: {integrity: sha512-F8oW2KKrQRb3vS5ud5HIqBVkCqQi224Nm55o5wYLzY/9PwHGXC01tr3d7+TqHHz6zrKQ72Okwtvm/xQm6OVNZA==} - dependencies: - micromark-util-character: 1.1.0 - micromark-util-symbol: 1.0.1 - micromark-util-types: 1.0.2 - dev: true - - /micromark-util-combine-extensions@1.0.0: - resolution: {integrity: sha512-J8H058vFBdo/6+AsjHp2NF7AJ02SZtWaVUjsayNFeAiydTxUwViQPxN0Hf8dp4FmCQi0UUFovFsEyRSUmFH3MA==} - dependencies: - micromark-util-chunked: 1.0.0 - micromark-util-types: 1.0.2 - dev: true - - /micromark-util-decode-numeric-character-reference@1.0.0: - resolution: {integrity: sha512-OzO9AI5VUtrTD7KSdagf4MWgHMtET17Ua1fIpXTpuhclCqD8egFWo85GxSGvxgkGS74bEahvtM0WP0HjvV0e4w==} - dependencies: - micromark-util-symbol: 1.0.1 - dev: true - - /micromark-util-decode-string@1.0.2: - resolution: {integrity: sha512-DLT5Ho02qr6QWVNYbRZ3RYOSSWWFuH3tJexd3dgN1odEuPNxCngTCXJum7+ViRAd9BbdxCvMToPOD/IvVhzG6Q==} - dependencies: - decode-named-character-reference: 1.0.2 - micromark-util-character: 1.1.0 - micromark-util-decode-numeric-character-reference: 1.0.0 - micromark-util-symbol: 1.0.1 - dev: true - /micromark-util-encode@1.0.1: - resolution: {integrity: sha512-U2s5YdnAYexjKDel31SVMPbfi+eF8y1U4pfiRW/Y8EFVCy/vgxk/2wWTxzcqE71LHtCuCzlBDRU2a5CQ5j+mQA==} - dev: true + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} - /micromark-util-events-to-acorn@1.2.1: - resolution: {integrity: sha512-mkg3BaWlw6ZTkQORrKVBW4o9ICXPxLtGz51vml5mQpKFdo9vqIX68CAx5JhTOdjQyAHH7JFmm4rh8toSPQZUmg==} - dependencies: - '@types/acorn': 4.0.6 - '@types/estree': 1.0.1 - estree-util-visit: 1.2.1 - micromark-util-types: 1.0.2 - uvu: 0.5.6 - vfile-location: 4.1.0 - vfile-message: 3.1.4 - dev: true + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} - /micromark-util-html-tag-name@1.1.0: - resolution: {integrity: sha512-BKlClMmYROy9UiV03SwNmckkjn8QHVaWkqoAqzivabvdGcwNGMMMH/5szAnywmsTBUzDsU57/mFi0sp4BQO6dA==} - dev: true + mime-db@1.53.0: + resolution: {integrity: sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg==} + engines: {node: '>= 0.6'} - /micromark-util-normalize-identifier@1.0.0: - resolution: {integrity: sha512-yg+zrL14bBTFrQ7n35CmByWUTFsgst5JhA4gJYoty4Dqzj4Z4Fr/DHekSS5aLfH9bdlfnSvKAWsAgJhIbogyBg==} - dependencies: - micromark-util-symbol: 1.0.1 - dev: true + mime-db@1.54.0: + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} - /micromark-util-resolve-all@1.0.0: - resolution: {integrity: sha512-CB/AGk98u50k42kvgaMM94wzBqozSzDDaonKU7P7jwQIuH2RU0TeBqGYJz2WY1UdihhjweivStrJ2JdkdEmcfw==} - dependencies: - micromark-util-types: 1.0.2 - dev: true + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mime-types@3.0.1: + resolution: {integrity: sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==} + engines: {node: '>= 0.6'} + + mime@1.6.0: + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} + engines: {node: '>=4'} + hasBin: true + + mime@2.6.0: + resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==} + engines: {node: '>=4.0.0'} + hasBin: true + + mime@3.0.0: + resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==} + engines: {node: '>=10.0.0'} + hasBin: true + + mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + + mimic-function@5.0.1: + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + engines: {node: '>=18'} + + mimic-response@3.1.0: + resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} + engines: {node: '>=10'} + + min-indent@1.0.1: + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} + + mini-css-extract-plugin@2.9.1: + resolution: {integrity: sha512-+Vyi+GCCOHnrJ2VPS+6aPoXN2k2jgUzDRhTFLjjTBn23qyXJXkjUWQgTL+mXpF5/A8ixLdCc6kWsoeOjKGejKQ==} + engines: {node: '>= 12.13.0'} + peerDependencies: + webpack: ^5.0.0 + + minimalistic-assert@1.0.1: + resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} + + minimalistic-crypto-utils@1.0.1: + resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==} + + minimatch@10.0.3: + resolution: {integrity: sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==} + engines: {node: 20 || >=22} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimatch@5.1.6: + resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} + engines: {node: '>=10'} + + minimatch@7.4.6: + resolution: {integrity: sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==} + engines: {node: '>=10'} + + minimatch@9.0.1: + resolution: {integrity: sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==} + engines: {node: '>=16 || 14 >=14.17'} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + minipass-collect@1.0.2: + resolution: {integrity: sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==} + engines: {node: '>= 8'} + + minipass-fetch@1.4.1: + resolution: {integrity: sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==} + engines: {node: '>=8'} + + minipass-fetch@2.1.2: + resolution: {integrity: sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + minipass-fetch@3.0.5: + resolution: {integrity: sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + minipass-flush@1.0.5: + resolution: {integrity: sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==} + engines: {node: '>= 8'} + + minipass-json-stream@1.0.2: + resolution: {integrity: sha512-myxeeTm57lYs8pH2nxPzmEEg8DGIgW+9mv6D4JZD2pa81I/OBjeU7PtICXV6c9eRGTA5JMDsuIPUZRCyBMYNhg==} + + minipass-pipeline@1.2.4: + resolution: {integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==} + engines: {node: '>=8'} + + minipass-sized@1.0.3: + resolution: {integrity: sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==} + engines: {node: '>=8'} + + minipass@3.3.6: + resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} + engines: {node: '>=8'} + + minipass@5.0.0: + resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} + engines: {node: '>=8'} + + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + + minizlib@2.1.2: + resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} + engines: {node: '>= 8'} + + minizlib@3.0.1: + resolution: {integrity: sha512-umcy022ILvb5/3Djuu8LWeqUa8D68JaBzlttKeMWen48SjabqS3iY5w/vzeMzMUNhLDifyhbOwKDSznB1vvrwg==} + engines: {node: '>= 18'} + + mjml-accordion@5.0.0-alpha.6: + resolution: {integrity: sha512-K7FS8HZQsfUNkvwUWMjCmaEEtHRrqEDddyaVoOE6+ir4H6aWAJUZqp1j/USQsYwMI3YAS3UDpqQEOagG4pRDqg==} + + mjml-body@5.0.0-alpha.6: + resolution: {integrity: sha512-otkINnYsBVbKSYqOz/FmhQruOpYzT10w9+UGcOZdBwv+UqDoKHXAD8QeSFZxr7fDSFhc6qeFAKNSY5uciT1ZuQ==} + + mjml-button@5.0.0-alpha.6: + resolution: {integrity: sha512-Z+0J6F2hk7QxKydlABna/vcGtTl7WfMWMLH74x9T1VgNylUAZ2TaWOUAo1AbWICgYBmZfNw1UkDj4EFvb+B1LA==} + + mjml-carousel@5.0.0-alpha.6: + resolution: {integrity: sha512-G2n6D6smGmQEKGByjR52UD14D/0gtVokaEI4wjG7UjisSGYGbDXJW4thfDv2RdwIoozXyGhJD3vRuHsoCPeFnw==} + + mjml-cli@5.0.0-alpha.6: + resolution: {integrity: sha512-mHmw5MCLNImcUUnYwl1yPF3meAd2EApnriMp8DTEN3zCqXsMg1RV8EB1npp4Camwz8HvVMTvEjSoVotwNWxAMg==} + hasBin: true + + mjml-column@5.0.0-alpha.6: + resolution: {integrity: sha512-FWpmyCH1kzV3g0P6po1OMVOLTgQXDTbo6z30ew+CylUYnn1w5Vh30T1ZvSPYQDc2jGqjsX1zuWO/JUjOJ5B2RQ==} + + mjml-core@5.0.0-alpha.6: + resolution: {integrity: sha512-rrGr+xrOCnJ+3V/+LeqA4BCp7jrXiRq0q37FlmSs+etE86yNqMMFMgEFEbeGYTTQO07WuqXlhPSuq85ucAoivg==} + + mjml-divider@5.0.0-alpha.6: + resolution: {integrity: sha512-lw0rQNn2Y4LRcF/ad4JUjVRsyXhYXdaos6yOXPFjRN8aK1T4p7XctZyP5RMl7m8YfXTNxxyN//hN3bQI5DwvgQ==} + + mjml-group@5.0.0-alpha.6: + resolution: {integrity: sha512-No9EeJC9GSpqmxfo0laCmUU0w0xhHb3HaliFJPh7YlzObyRE3nvawHtPYpfNW3lgYNl0U7oYMlN9AQum/DaW2Q==} + + mjml-head-attributes@5.0.0-alpha.6: + resolution: {integrity: sha512-CW/E+IEw3MOLTWkTMbN6egPwoeg22Iup+fICAH9rmITD6SBJ6f37/MFiACSmpkDQIlWdFokBFFKpikbPWFJKQA==} + + mjml-head-breakpoint@5.0.0-alpha.6: + resolution: {integrity: sha512-3RsHM0l3VU+NhG4MAsXevLH50s/uOJqbZznGVKkgEBsodKDhFK2ndlmUWZ5ywDO6H2g/qDrZse4c1K43uGoyzA==} + + mjml-head-font@5.0.0-alpha.6: + resolution: {integrity: sha512-+HZ/Ppd/hfFgob9M27Tz9qio/vWwyTwjo40PsYSI6zXKmIUHC5icBcHaezUKMDGadgd+11aXlPLVfs6zGb0hVg==} + + mjml-head-html-attributes@5.0.0-alpha.6: + resolution: {integrity: sha512-INI3irUFHozLfJCW0pi/499DhlwqpsFEoGQ++NdHlFH87hbQ2XTNlrTHWpgjhSuiJkRJinPYHphRuNyM3KLyVw==} + + mjml-head-preview@5.0.0-alpha.6: + resolution: {integrity: sha512-HGLcYmJ7q4aYD5VNTUKIvk6wvpXUawI5j4yl9m+DEhk2ptKYmayxLpjiAvH+LioNuPh0zTCGt7U9f5Ja0948gQ==} + + mjml-head-style@5.0.0-alpha.6: + resolution: {integrity: sha512-yQcHIvZGH641irNt21r4hzODp5rEI/qKJRvGVJ6vzyGm7poYbaoxhK7Re9cRu+xZxLpkiTRgf1DYFjyZN45SiQ==} + + mjml-head-title@5.0.0-alpha.6: + resolution: {integrity: sha512-m6dGCAItgobZSJ7wlETFPth6rU0+617ZVk1f88Mz5I+YVuOgaAqdNDQWdue5Uj7B19c55NmtdZwsgOKmhHuwow==} + + mjml-head@5.0.0-alpha.6: + resolution: {integrity: sha512-owBfZUcwHV2Wjow32QkRNZClbVHIAthacskvowAODUlOfTG1Xj0czlAM6iCG73cSqmZFuocOD9hy47y0l00Qeg==} + + mjml-hero@5.0.0-alpha.6: + resolution: {integrity: sha512-OLOKTK/VW4fQOH8yQz1e5kKHfyF0heECghPhmxgnEHigJLbaJy/rmb4x4aq0Lr74Bme8hsMjkR+oktGej1c4iQ==} + + mjml-image@5.0.0-alpha.6: + resolution: {integrity: sha512-NENjbEOzobM0iQLlxJUNuqiNlFSP5E1DfiP37bY8smYmpVe4MXi5HC5ajTH5ZBKWsTN4/UVZL7t+ZSqniTnS5w==} + + mjml-navbar@5.0.0-alpha.6: + resolution: {integrity: sha512-bj328dixWJqox9CaA/rvIKsTZ7c5g1mN9B0gL2vuzVMXgX4qnXkzIfN2hw98TdffaKGsKXC6N25iIDVocj1RrQ==} + + mjml-parser-xml@5.0.0-alpha.6: + resolution: {integrity: sha512-2gxzFJXBFq6l8f2/HlDq7MBLtWQja0KPSqePxPBOXmNLjqw6bdSflnQu+OhWVptdywuHm907LNIQVIq0wSo2FQ==} + + mjml-preset-core@5.0.0-alpha.6: + resolution: {integrity: sha512-6K9sPXlxQfe9Vx/8/wAFW2Sy6ImWU1AP37VG08Ge7Mj117cNeKk3S1piagbj6LpyTd7E7AueO8G+xzvmC5YBBw==} + + mjml-raw@5.0.0-alpha.6: + resolution: {integrity: sha512-tY2P+g7bcydVHrk0NPPm2sd+rIB9l5TdCeIdtw+NSOXoHN7vXVezcdr7UYK8TePibzeymh8w9YTWEAIN8fWvLg==} + + mjml-react@2.0.8: + resolution: {integrity: sha512-3wtHZZs1Y7e7Tl+ImojD/+aPE8Z0xshMww7MKSQlD9A1E/92amWQilGZN3T+WjWWaDueKcH2Gc1RJ72PLGSRGA==} + peerDependencies: + mjml: ^4.7.0 + react: ^16.0.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 + + mjml-section@5.0.0-alpha.6: + resolution: {integrity: sha512-gJL+9t9hiAfnXl3gG481Xum1qzURXs6bZ+rZnU47R/070+kinOJTCHt82hFGctjyDi264r/BeM6H6y+m0FIq/w==} + + mjml-social@5.0.0-alpha.6: + resolution: {integrity: sha512-vPRBYHKeEUwEiVwXLrXgQnWBblizdQTlrZ2V45xEH9+3Jqolw3SlTw+uoZzhcFSBNl7+ytdQgwp7gRC7Bn5IYQ==} + + mjml-spacer@5.0.0-alpha.6: + resolution: {integrity: sha512-ZyLcoAElvkWnijny2eW0ulX6RxoDQT8AZwv5pJ8O16s0mnfcPAPMaZGTCQZHxPbACKJcCKsIoZrQllQuShf3XA==} + + mjml-table@5.0.0-alpha.6: + resolution: {integrity: sha512-ze7iNRyT6uX099KxZoV33u1jOlibmfxkUUY/8BXbyfWUIgcwRzb1DfKtIWT/GOqI2WAqh5ZwHd5XZgueimttEA==} + + mjml-text@5.0.0-alpha.6: + resolution: {integrity: sha512-k7/pUgwZo9ZwnoCVvohsGdpOrJuLpScH183ZxjyPYO8+kOruSFpE2yrGhK/jeLaV4UhdXndNk8dz80wbN0fKjw==} + + mjml-validator@5.0.0-alpha.6: + resolution: {integrity: sha512-kL5IJGYXdNN7VunXXJEWyAP40oODRiSNu5NIx5moOXREPHvn7lRqi/XS8MXa48/UBLmKMbltDSQ0DcnESJbodQ==} + + mjml-wrapper@5.0.0-alpha.6: + resolution: {integrity: sha512-qJisqqkQrtq4U74BTgRaNoPjpf2PwYff/QxH5yvkFDXK3fYEoNt011K7lrm/+u5wS9mx4msHZN0WA4TI/EeeOw==} + + mjml@5.0.0-alpha.6: + resolution: {integrity: sha512-unizId6dKTQSHq1nGnRQqe58kpD7VJu9p+vfMsKO4911/+VCrxkFe0oiwS7Q6XA3rc224MVChO2Mvueyeh16jg==} + hasBin: true + + mkdirp-classic@0.5.3: + resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} + + mkdirp-infer-owner@2.0.0: + resolution: {integrity: sha512-sdqtiFt3lkOaYvTXSRIUjkIdPTcxgv5+fgqYE/5qgwdw12cOrAuzzgzvVExIkH/ul1oeHN3bCLOWSG3XOqbKKw==} + engines: {node: '>=10'} + + mkdirp@0.5.6: + resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} + hasBin: true + + mkdirp@1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true + + mkdirp@3.0.1: + resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} + engines: {node: '>=10'} + hasBin: true + + mmdb-lib@2.1.1: + resolution: {integrity: sha512-yx8H/1H5AfnufiLnzzPqPf4yr/dKU9IFT1rPVwSkrKWHsQEeVVd6+X+L0nUbXhlEFTu3y/7hu38CFmEVgzvyeg==} + engines: {node: '>=10', npm: '>=6'} + + moment@2.30.1: + resolution: {integrity: sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==} + + monaco-editor@0.52.0: + resolution: {integrity: sha512-OeWhNpABLCeTqubfqLMXGsqf6OmPU6pHM85kF3dhy6kq5hnhuVS1p3VrEW/XhWHc71P2tHyS5JFySD8mgs1crw==} + + mongodb-connection-string-url@3.0.1: + resolution: {integrity: sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==} + + mongodb@6.16.0: + resolution: {integrity: sha512-D1PNcdT0y4Grhou5Zi/qgipZOYeWrhLEpk33n3nm6LGtz61jvO88WlrWCK/bigMjpnOdAUKKQwsGIl0NtWMyYw==} + 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 + + monorel@0.4.2: + resolution: {integrity: sha512-CoMOQatkftXPegXzAh8Qnjc/+VQuiujhNjU6iWY5YZi0o2N0FKxPckja8LhKYoHBZlMt0jyKA+99EfnxjffjBQ==} + hasBin: true + + motion-dom@12.23.6: + resolution: {integrity: sha512-G2w6Nw7ZOVSzcQmsdLc0doMe64O/Sbuc2bVAbgMz6oP/6/pRStKRiVRV4bQfHp5AHYAKEGhEdVHTM+R3FDgi5w==} + + motion-utils@12.23.6: + resolution: {integrity: sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==} + + mri@1.2.0: + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} + + mrmime@2.0.1: + resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} + engines: {node: '>=10'} + + ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + multicast-dns@7.2.5: + resolution: {integrity: sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==} + hasBin: true + + multimatch@5.0.0: + resolution: {integrity: sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA==} + engines: {node: '>=10'} + + mute-stream@0.0.8: + resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} + + mute-stream@1.0.0: + resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + + nan@2.22.0: + resolution: {integrity: sha512-nbajikzWTMwsW+eSsNm3QwlOs7het9gGJU5dDZzRTQGk03vyBOauxgI4VakDzE0PtsGTmXPsXTbbjVhRwR5mpw==} + + nano-css@5.6.2: + resolution: {integrity: sha512-+6bHaC8dSDGALM1HJjOHVXpuastdu2xFoZlC77Jh4cg+33Zcgm+Gxd+1xsnpZK14eyHObSp82+ll5y3SX75liw==} + peerDependencies: + react: '*' + react-dom: '*' + + nanoid@3.3.8: + resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + napi-build-utils@2.0.0: + resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==} + + natural-compare-lite@1.4.0: + resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + needle@3.3.1: + resolution: {integrity: sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==} + engines: {node: '>= 4.4.x'} + hasBin: true + + negotiator@0.6.3: + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + engines: {node: '>= 0.6'} + + negotiator@0.6.4: + resolution: {integrity: sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==} + engines: {node: '>= 0.6'} + + neo-async@2.6.2: + resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + + new-date@1.0.3: + resolution: {integrity: sha512-0fsVvQPbo2I18DT2zVHpezmeeNYV2JaJSrseiHLc17GNOxJzUdx5mvSigPu8LtIfZSij5i1wXnXFspEs2CD6hA==} + + next-auth@4.24.11: + resolution: {integrity: sha512-pCFXzIDQX7xmHFs4KVH4luCjaCbuPRtZ9oBUjUhOk84mZ9WVPf94n87TxYI4rSRf9HmfHEF8Yep3JrYDVOo3Cw==} + peerDependencies: + '@auth/core': 0.34.2 + next: ^12.2.5 || ^13 || ^14 || ^15 + nodemailer: ^6.6.5 + react: ^17.0.2 || ^18 || ^19 + react-dom: ^17.0.2 || ^18 || ^19 + peerDependenciesMeta: + '@auth/core': + optional: true + nodemailer: + optional: true + + next@15.5.4: + resolution: {integrity: sha512-xH4Yjhb82sFYQfY3vbkJfgSDgXvBB6a8xPs9i35k6oZJRoQRihZH+4s9Yo2qsWpzBmZ3lPXaJ2KPXLfkvW4LnA==} + engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} + hasBin: true + peerDependencies: + '@opentelemetry/api': ^1.1.0 + '@playwright/test': ^1.51.1 + babel-plugin-react-compiler: '*' + react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + sass: ^1.3.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + '@playwright/test': + optional: true + babel-plugin-react-compiler: + optional: true + sass: + optional: true + + no-case@3.0.4: + resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} + + node-abi@3.71.0: + resolution: {integrity: sha512-SZ40vRiy/+wRTf21hxkkEjPJZpARzUMVcJoQse2EF8qkUWbbO2z7vd5oA/H6bVH6SZQ5STGcu0KRDS7biNRfxw==} + engines: {node: '>=10'} + + node-addon-api@4.3.0: + resolution: {integrity: sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==} + + node-cache@5.1.2: + resolution: {integrity: sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg==} + engines: {node: '>= 8.0.0'} + + node-domexception@1.0.0: + resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} + engines: {node: '>=10.5.0'} + + node-fetch-commonjs@3.3.2: + resolution: {integrity: sha512-VBlAiynj3VMLrotgwOS3OyECFxas5y7ltLcK4t41lMUZeaK15Ym4QRkqN0EQKAFL42q9i21EPKjzLUPfltR72A==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + node-fetch@3.3.2: + resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + node-forge@1.3.1: + resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==} + engines: {node: '>= 6.13.0'} + + node-gyp@8.4.1: + resolution: {integrity: sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==} + engines: {node: '>= 10.12.0'} + hasBin: true + + node-gyp@9.4.1: + resolution: {integrity: sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ==} + engines: {node: ^12.13 || ^14.13 || >=16} + hasBin: true + + node-html-parser@7.0.1: + resolution: {integrity: sha512-KGtmPY2kS0thCWGK0VuPyOS+pBKhhe8gXztzA2ilAOhbUbxa9homF1bOyKvhGzMLXUoRds9IOmr/v5lr/lqNmA==} + + node-int64@0.4.0: + resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} + + node-loader@2.0.0: + resolution: {integrity: sha512-I5VN34NO4/5UYJaUBtkrODPWxbobrE4hgDqPrjB25yPkonFhCmZ146vTH+Zg417E9Iwoh1l/MbRs1apc5J295Q==} + engines: {node: '>= 10.13.0'} + peerDependencies: + webpack: ^5.0.0 + + node-postgres@0.6.2: + resolution: {integrity: sha512-wjaW+KutPOFemtBDuogyy6OMMOs2vpSOwuqbUhFUbrDEOTk66K2lmRyvOdO4t26qGtnPyZXQ4bAuF17cBhi5aQ==} + + node-releases@2.0.18: + resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==} + + node-releases@2.0.19: + resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} + + node-sql-parser@5.3.8: + resolution: {integrity: sha512-aqGTzK8kPJAwQ6tqdS0l+vX358LXMmZDw902ePfiPn3PSDsg2HeR2tHFioXNHJW00YHoic6VVYY80waFb/zdxw==} + engines: {node: '>=8'} + + nodemailer@6.10.1: + resolution: {integrity: sha512-Z+iLaBGVaSjbIzQ4pX6XV41HrooLsQ10ZWPUehGmuantvzWoDVBnmsdUcOIDM1t+yPor5pDhVlDESgOMEGxhHA==} + engines: {node: '>=6.0.0'} + + nodemon@3.1.7: + resolution: {integrity: sha512-hLj7fuMow6f0lbB0cD14Lz2xNjwsyruH251Pk4t/yIitCFJbmY1myuLlHm/q06aST4jg6EgAh74PIBBrRqpVAQ==} + engines: {node: '>=10'} + hasBin: true + + nopt@5.0.0: + resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==} + engines: {node: '>=6'} + hasBin: true + + nopt@6.0.0: + resolution: {integrity: sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + hasBin: true + + nopt@7.2.1: + resolution: {integrity: sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + hasBin: true + + normalize-package-data@2.5.0: + resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} + + normalize-package-data@5.0.0: + resolution: {integrity: sha512-h9iPVIfrVZ9wVYQnxFgtw1ugSvGEMOlyPWWtm8BMJhnwyEL/FLbYbTY3V3PpjI/BUK67n9PEWDu6eHzu1fB15Q==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + normalize-range@0.1.2: + resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} + engines: {node: '>=0.10.0'} + + normalize-url@6.1.0: + resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==} + engines: {node: '>=10'} + + npm-bundled@1.1.2: + resolution: {integrity: sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ==} + + npm-bundled@3.0.1: + resolution: {integrity: sha512-+AvaheE/ww1JEwRHOrn4WHNzOxGtVp+adrg2AeZS/7KuxGUYFuBta98wYpfHBbJp6Tg6j1NKSEVHNcfZzJHQwQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + npm-install-checks@4.0.0: + resolution: {integrity: sha512-09OmyDkNLYwqKPOnbI8exiOZU2GVVmQp7tgez2BPi5OZC8M82elDAps7sxC4l//uSUtotWqoEIDwjRvWH4qz8w==} + engines: {node: '>=10'} + + npm-install-checks@6.3.0: + resolution: {integrity: sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + npm-normalize-package-bin@1.0.1: + resolution: {integrity: sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==} + + npm-normalize-package-bin@2.0.0: + resolution: {integrity: sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + npm-normalize-package-bin@3.0.1: + resolution: {integrity: sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + npm-package-arg@10.1.0: + resolution: {integrity: sha512-uFyyCEmgBfZTtrKk/5xDfHp6+MdrqGotX/VoOyEEl3mBwiEE5FlBaePanazJSVMPT7vKepcjYBY2ztg9A3yPIA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + npm-package-arg@8.1.5: + resolution: {integrity: sha512-LhgZrg0n0VgvzVdSm1oiZworPbTxYHUJCgtsJW8mGvlDpxTM1vSJc3m5QZeUkhAHIzbz3VCHd/R4osi1L1Tg/Q==} + engines: {node: '>=10'} + + npm-packlist@3.0.0: + resolution: {integrity: sha512-L/cbzmutAwII5glUcf2DBRNY/d0TFd4e/FnaZigJV6JD85RHZXJFGwCndjMWiiViiWSsWt3tiOLpI3ByTnIdFQ==} + engines: {node: '>=10'} + hasBin: true + + npm-packlist@7.0.4: + resolution: {integrity: sha512-d6RGEuRrNS5/N84iglPivjaJPxhDbZmlbTwTDX2IbcRHG5bZCdtysYMhwiPvcF4GisXHGn7xsxv+GQ7T/02M5Q==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + npm-pick-manifest@6.1.1: + resolution: {integrity: sha512-dBsdBtORT84S8V8UTad1WlUyKIY9iMsAmqxHbLdeEeBNMLQDlDWWra3wYUx9EBEIiG/YwAy0XyNHDd2goAsfuA==} + + npm-pick-manifest@8.0.2: + resolution: {integrity: sha512-1dKY+86/AIiq1tkKVD3l0WI+Gd3vkknVGAggsFeBkTvbhMQ1OND/LKkYv4JtXPKUJ8bOTCyLiqEg2P6QNdK+Gg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + npm-registry-fetch@12.0.2: + resolution: {integrity: sha512-Df5QT3RaJnXYuOwtXBXS9BWs+tHH2olvkCLh6jcR/b/u3DvPMlp3J0TvvYwplPKxHMOwfg287PYih9QqaVFoKA==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16} + + npm-registry-fetch@14.0.5: + resolution: {integrity: sha512-kIDMIo4aBm6xg7jOttupWZamsZRkAqMqwqqbVXnUqstY5+tapvv6bkH/qMR76jdgV+YljEUCyWx3hRYMrJiAgA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + npm-run-path@4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + + npmlog@5.0.1: + resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==} + deprecated: This package is no longer supported. + + npmlog@6.0.2: + resolution: {integrity: sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + deprecated: This package is no longer supported. + + nth-check@2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + + number-is-nan@1.0.1: + resolution: {integrity: sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==} + engines: {node: '>=0.10.0'} + + nwsapi@2.2.13: + resolution: {integrity: sha512-cTGB9ptp9dY9A5VbMSe7fQBcl/tt22Vcqdq8+eN93rblOuE0aCFu4aZ2vMwct/2t+lFnosm8RkQW1I0Omb1UtQ==} + + nypm@0.6.0: + resolution: {integrity: sha512-mn8wBFV9G9+UFHIrq+pZ2r2zL4aPau/by3kJb3cM7+5tQHMt6HGQB8FDIeKFYp8o0D2pnH6nVsO88N4AmUxIWg==} + engines: {node: ^14.16.0 || >=16.10.0} + hasBin: true + + oauth@0.9.15: + resolution: {integrity: sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==} + + obj-case@0.2.1: + resolution: {integrity: sha512-PquYBBTy+Y6Ob/O2574XHhDtHJlV1cJHMCgW+rDRc9J5hhmRelJB3k5dTK/3cVmFVtzvAKuENeuLpoyTzMzkOg==} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-hash@2.2.0: + resolution: {integrity: sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==} + engines: {node: '>= 6'} + + object-hash@3.0.0: + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} + engines: {node: '>= 6'} + + object-inspect@1.13.2: + resolution: {integrity: sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==} + engines: {node: '>= 0.4'} + + object-is@1.1.6: + resolution: {integrity: sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==} + engines: {node: '>= 0.4'} + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + object.assign@4.1.5: + resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} + engines: {node: '>= 0.4'} + + object.entries@1.1.8: + resolution: {integrity: sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==} + engines: {node: '>= 0.4'} + + object.fromentries@2.0.8: + resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} + engines: {node: '>= 0.4'} + + object.getownpropertydescriptors@2.1.8: + resolution: {integrity: sha512-qkHIGe4q0lSYMv0XI4SsBTJz3WaURhLvd0lKSgtVuOsJ2krg4SgMw3PIRQFMp07yi++UR3se2mkcLqsBNpBb/A==} + engines: {node: '>= 0.8'} + + object.groupby@1.0.3: + resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} + engines: {node: '>= 0.4'} + + object.values@1.2.0: + resolution: {integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==} + engines: {node: '>= 0.4'} + + obuf@1.1.2: + resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==} + + oidc-token-hash@5.0.3: + resolution: {integrity: sha512-IF4PcGgzAr6XXSff26Sk/+P4KZFJVuHAJZj3wgO3vX2bMdNVp/QXTP3P7CEm9V1IdG8lDLY3HhiqpsE/nOwpPw==} + engines: {node: ^10.13.0 || >=12.0.0} + + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + + on-headers@1.0.2: + resolution: {integrity: sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==} + engines: {node: '>= 0.8'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + + onetime@7.0.0: + resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} + engines: {node: '>=18'} + + open@8.4.2: + resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} + engines: {node: '>=12'} + + opener@1.5.2: + resolution: {integrity: sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==} + hasBin: true + + openid-client@5.7.0: + resolution: {integrity: sha512-4GCCGZt1i2kTHpwvaC/sCpTpQqDnBzDzuJcJMbH+y1Q5qI8U8RBvoSh28svarXszZHR5BAMXbJPX1PGPRE3VOA==} + + optionator@0.8.3: + resolution: {integrity: sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==} + engines: {node: '>= 0.8.0'} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + ora@5.4.1: + resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} + engines: {node: '>=10'} + + ora@8.2.0: + resolution: {integrity: sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==} + engines: {node: '>=18'} + + os-tmpdir@1.0.2: + resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} + engines: {node: '>=0.10.0'} + + p-finally@1.0.0: + resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} + engines: {node: '>=4'} + + p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-limit@4.0.0: + resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + p-locate@3.0.0: + resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==} + engines: {node: '>=6'} + + p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + p-locate@6.0.0: + resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + p-map@2.1.0: + resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} + engines: {node: '>=6'} + + p-map@4.0.0: + resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} + engines: {node: '>=10'} + + p-queue@6.6.2: + resolution: {integrity: sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==} + engines: {node: '>=8'} + + p-retry@4.6.2: + resolution: {integrity: sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==} + engines: {node: '>=8'} + + p-timeout@3.2.0: + resolution: {integrity: sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==} + engines: {node: '>=8'} + + p-transform@1.3.0: + resolution: {integrity: sha512-UJKdSzgd3KOnXXAtqN5+/eeHcvTn1hBkesEmElVgvO/NAYcxAvmjzIGmnNd3Tb/gRAvMBdNRFD4qAWdHxY6QXg==} + engines: {node: '>=12.10.0'} + + p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + + pacote@12.0.3: + resolution: {integrity: sha512-CdYEl03JDrRO3x18uHjBYA9TyoW8gy+ThVcypcDkxPtKlw76e4ejhYB6i9lJ+/cebbjpqPW/CijjqxwDTts8Ow==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16} + hasBin: true + + pacote@15.2.0: + resolution: {integrity: sha512-rJVZeIwHTUta23sIZgEIM62WYwbmGbThdbnkt81ravBplQv+HjyroqnLRNH2+sLJHcGZmLRmhPwACqhfTcOmnA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + hasBin: true + + param-case@3.0.4: + resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parenthesis@3.1.8: + resolution: {integrity: sha512-KF/U8tk54BgQewkJPvB4s/US3VQY68BRDpH638+7O/n58TpnwiwnOtGIOsT2/i+M78s61BBpeC83STB88d8sqw==} + + parse-conflict-json@2.0.2: + resolution: {integrity: sha512-jDbRGb00TAPFsKWCpZZOT93SxVP9nONOSgES3AevqRq/CHvavEBvKAjxX9p5Y5F0RZLxH9Ufd9+RwtCsa+lFDA==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + parse-duration@1.1.2: + resolution: {integrity: sha512-p8EIONG8L0u7f8GFgfVlL4n8rnChTt8O5FSxgxMz2tjc9FMP199wxVKVB6IbKx11uTbKHACSvaLVIKNnoeNR/A==} + + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + + parse-node-version@1.0.1: + resolution: {integrity: sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==} + engines: {node: '>= 0.10'} + + parse5-htmlparser2-tree-adapter@7.1.0: + resolution: {integrity: sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==} + + parse5-parser-stream@7.1.2: + resolution: {integrity: sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==} + + parse5@6.0.1: + resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==} + + parse5@7.2.1: + resolution: {integrity: sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==} + + parseley@0.12.1: + resolution: {integrity: sha512-e6qHKe3a9HWr0oMRVDTRhKce+bRO8VGQR3NyVwcjwrbhMmFCX9KszEV35+rn4AdilFAq9VPxP/Fe1wC9Qjd2lw==} + + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + + pascal-case@3.1.2: + resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} + + path-browserify@1.0.1: + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + + path-exists@3.0.0: + resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==} + engines: {node: '>=4'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-exists@5.0.0: + resolution: {integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + + path-is-inside@1.0.2: + resolution: {integrity: sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + + path-scurry@2.0.0: + resolution: {integrity: sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==} + engines: {node: 20 || >=22} + + path-to-regexp@0.1.12: + resolution: {integrity: sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + path-type@5.0.0: + resolution: {integrity: sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==} + engines: {node: '>=12'} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + peberminta@0.9.0: + resolution: {integrity: sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==} + + performance-now@2.1.0: + resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==} + + pg-cloudflare@1.1.1: + resolution: {integrity: sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==} + + pg-connection-string@2.7.0: + resolution: {integrity: sha512-PI2W9mv53rXJQEOb8xNR8lH7Hr+EKa6oJa38zsK0S/ky2er16ios1wLKhZyxzD7jUReiWokc9WK5nxSnC7W1TA==} + + pg-cursor@2.10.6: + resolution: {integrity: sha512-yxQGjy1GJl6BROtA4mq62Xd2izqhoGco6NYUgKXY9GMBKXfh7tGSGOhIy5K2ULerqOlJ4+3EKKj4Fog0OEKCyA==} + peerDependencies: + pg: ^8 + + pg-cursor@2.12.1: + resolution: {integrity: sha512-V13tEaA9Oq1w+V6Q3UBIB/blxJrwbbr35/dY54r/86soBJ7xkP236bXaORUTVXUPt9B6Ql2BQu+uwQiuMfRVgg==} + peerDependencies: + pg: ^8 + + pg-int8@1.0.1: + resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} + engines: {node: '>=4.0.0'} + + pg-minify@1.6.5: + resolution: {integrity: sha512-u0UE8veaCnMfJmoklqneeBBopOAPG3/6DHqGVHYAhz8DkJXh9dnjPlz25fRxn4e+6XVzdOp7kau63Rp52fZ3WQ==} + engines: {node: '>=14.0.0'} + + pg-numeric@1.0.2: + resolution: {integrity: sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==} + engines: {node: '>=4'} + + pg-pool@3.7.0: + resolution: {integrity: sha512-ZOBQForurqh4zZWjrgSwwAtzJ7QiRX0ovFkZr2klsen3Nm0aoh33Ls0fzfv3imeH/nw/O27cjdz5kzYJfeGp/g==} + peerDependencies: + pg: '>=8.0' + + pg-promise@11.10.1: + resolution: {integrity: sha512-TceugkypE+VkHTjlklMmLYLN5hUDbM9dIhKZQq2onxN9F//6X6Q5czQ7Ms5sCi0JB5pkbt8fgoC7OLHM2EVI7Q==} + engines: {node: '>=14.0'} + peerDependencies: + pg-query-stream: 4.7.0 + + pg-protocol@1.7.0: + resolution: {integrity: sha512-hTK/mE36i8fDDhgDFjy6xNOG+LCorxLG3WO17tku+ij6sVHXh1jQUJ8hYAnRhNla4QVD2H8er/FOjc/+EgC6yQ==} + + pg-query-stream@4.7.1: + resolution: {integrity: sha512-UMgsgn/pOIYsIifRySp59vwlpTpLADMK9HWJtq5ff0Z3MxBnPMGnCQeaQl5VuL+7ov4F96mSzIRIcz+Duo6OiQ==} + peerDependencies: + pg: ^8 + + pg-types@2.2.0: + resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} + engines: {node: '>=4'} + + pg-types@4.0.2: + resolution: {integrity: sha512-cRL3JpS3lKMGsKaWndugWQoLOCoP+Cic8oseVcbr0qhPzYD5DWXK+RZ9LY9wxRf7RQia4SCwQlXk0q6FCPrVng==} + engines: {node: '>=10'} + + pg@8.11.6: + resolution: {integrity: sha512-6CyL4F0j3vPmakU9rWdeRY8qF5Cjc3OE86y6YpgDI6YtKHhNyCjGEIE8U5ZRfBjKTZikwolKIFWh3I22MeRnoA==} + engines: {node: '>= 8.0.0'} + peerDependencies: + pg-native: '>=3.0.1' + peerDependenciesMeta: + pg-native: + optional: true + + pg@8.13.0: + resolution: {integrity: sha512-34wkUTh3SxTClfoHB3pQ7bIMvw9dpFU1audQQeZG837fmHfHpr14n/AELVDoOYVDW2h5RDWU78tFjkD+erSBsw==} + engines: {node: '>= 8.0.0'} + peerDependencies: + pg-native: '>=3.0.1' + peerDependenciesMeta: + pg-native: + optional: true + + pgpass@1.0.5: + resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + picomatch@4.0.2: + resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + engines: {node: '>=12'} + + pify@2.3.0: + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} + + pify@4.0.1: + resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} + engines: {node: '>=6'} + + pify@5.0.0: + resolution: {integrity: sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA==} + engines: {node: '>=10'} + + pinkie-promise@2.0.1: + resolution: {integrity: sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==} + engines: {node: '>=0.10.0'} + + pinkie@2.0.4: + resolution: {integrity: sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==} + engines: {node: '>=0.10.0'} + + pirates@4.0.6: + resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} + engines: {node: '>= 6'} + + pkg-dir@4.2.0: + resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} + engines: {node: '>=8'} + + pkg-dir@7.0.0: + resolution: {integrity: sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==} + engines: {node: '>=14.16'} + + pkg-types@2.2.0: + resolution: {integrity: sha512-2SM/GZGAEkPp3KWORxQZns4M+WSeXbC2HEvmOIJe3Cmiv6ieAJvdVhDldtHqM5J1Y7MrR1XhkBT/rMlhh9FdqQ==} + + pkg-up@3.1.0: + resolution: {integrity: sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==} + engines: {node: '>=8'} + + playwright-core@1.39.0: + resolution: {integrity: sha512-+k4pdZgs1qiM+OUkSjx96YiKsXsmb59evFoqv8SKO067qBA+Z2s/dCzJij/ZhdQcs2zlTAgRKfeiiLm8PQ2qvw==} + engines: {node: '>=16'} + hasBin: true + + playwright@1.39.0: + resolution: {integrity: sha512-naE5QT11uC/Oiq0BwZ50gDmy8c8WLPRTEWuSSFVG2egBka/1qMoSqYQcROMT9zLwJ86oPofcTH2jBY/5wWOgIw==} + engines: {node: '>=16'} + hasBin: true + + possible-typed-array-names@1.0.0: + resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} + engines: {node: '>= 0.4'} + + postcss-attribute-case-insensitive@5.0.2: + resolution: {integrity: sha512-XIidXV8fDr0kKt28vqki84fRK8VW8eTuIa4PChv2MqKuT6C9UjmSKzen6KaWhWEoYvwxFCa7n/tC1SZ3tyq4SQ==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.4.47 + + postcss-browser-comments@4.0.0: + resolution: {integrity: sha512-X9X9/WN3KIvY9+hNERUqX9gncsgBA25XaeR+jshHz2j8+sYyHktHw1JdKuMjeLpGktXidqDhA7b/qm1mrBDmgg==} + engines: {node: '>=8'} + peerDependencies: + browserslist: '>=4' + postcss: ^8.4.47 + + postcss-calc@10.0.2: + resolution: {integrity: sha512-DT/Wwm6fCKgpYVI7ZEWuPJ4az8hiEHtCUeYjZXqU7Ou4QqYh1Df2yCQ7Ca6N7xqKPFkxN3fhf+u9KSoOCJNAjg==} + engines: {node: ^18.12 || ^20.9 || >=22.0} + peerDependencies: + postcss: ^8.4.47 + + postcss-calc@8.2.4: + resolution: {integrity: sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==} + peerDependencies: + postcss: ^8.4.47 + + postcss-clamp@4.1.0: + resolution: {integrity: sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow==} + engines: {node: '>=7.6.0'} + peerDependencies: + postcss: ^8.4.47 + + postcss-color-functional-notation@4.2.4: + resolution: {integrity: sha512-2yrTAUZUab9s6CpxkxC4rVgFEVaR6/2Pipvi6qcgvnYiVqZcbDHEoBDhrXzyb7Efh2CCfHQNtcqWcIruDTIUeg==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.4.47 + + postcss-color-hex-alpha@8.0.4: + resolution: {integrity: sha512-nLo2DCRC9eE4w2JmuKgVA3fGL3d01kGq752pVALF68qpGLmx2Qrk91QTKkdUqqp45T1K1XV8IhQpcu1hoAQflQ==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.4.47 + + postcss-color-rebeccapurple@7.1.1: + resolution: {integrity: sha512-pGxkuVEInwLHgkNxUc4sdg4g3py7zUeCQ9sMfwyHAT+Ezk8a4OaaVZ8lIY5+oNqA/BXXgLyXv0+5wHP68R79hg==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.4.47 + + postcss-colormin@5.3.1: + resolution: {integrity: sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.4.47 + + postcss-colormin@7.0.2: + resolution: {integrity: sha512-YntRXNngcvEvDbEjTdRWGU606eZvB5prmHG4BF0yLmVpamXbpsRJzevyy6MZVyuecgzI2AWAlvFi8DAeCqwpvA==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.47 + + postcss-convert-values@5.1.3: + resolution: {integrity: sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.4.47 + + postcss-convert-values@7.0.4: + resolution: {integrity: sha512-e2LSXPqEHVW6aoGbjV9RsSSNDO3A0rZLCBxN24zvxF25WknMPpX8Dm9UxxThyEbaytzggRuZxaGXqaOhxQ514Q==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.47 + + postcss-custom-media@8.0.2: + resolution: {integrity: sha512-7yi25vDAoHAkbhAzX9dHx2yc6ntS4jQvejrNcC+csQJAXjj15e7VcWfMgLqBNAbOvqi5uIa9huOVwdHbf+sKqg==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.4.47 + + postcss-custom-properties@12.1.11: + resolution: {integrity: sha512-0IDJYhgU8xDv1KY6+VgUwuQkVtmYzRwu+dMjnmdMafXYv86SWqfxkc7qdDvWS38vsjaEtv8e0vGOUQrAiMBLpQ==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.4.47 + + postcss-custom-selectors@6.0.3: + resolution: {integrity: sha512-fgVkmyiWDwmD3JbpCmB45SvvlCD6z9CG6Ie6Iere22W5aHea6oWa7EM2bpnv2Fj3I94L3VbtvX9KqwSi5aFzSg==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.4.47 + + postcss-dir-pseudo-class@6.0.5: + resolution: {integrity: sha512-eqn4m70P031PF7ZQIvSgy9RSJ5uI2171O/OO/zcRNYpJbvaeKFUlar1aJ7rmgiQtbm0FSPsRewjpdS0Oew7MPA==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.4.47 + + postcss-discard-comments@5.1.2: + resolution: {integrity: sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.4.47 + + postcss-discard-comments@7.0.3: + resolution: {integrity: sha512-q6fjd4WU4afNhWOA2WltHgCbkRhZPgQe7cXF74fuVB/ge4QbM9HEaOIzGSiMvM+g/cOsNAUGdf2JDzqA2F8iLA==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.47 + + postcss-discard-duplicates@5.1.0: + resolution: {integrity: sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.4.47 + + postcss-discard-duplicates@7.0.1: + resolution: {integrity: sha512-oZA+v8Jkpu1ct/xbbrntHRsfLGuzoP+cpt0nJe5ED2FQF8n8bJtn7Bo28jSmBYwqgqnqkuSXJfSUEE7if4nClQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.47 + + postcss-discard-empty@5.1.1: + resolution: {integrity: sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.4.47 + + postcss-discard-empty@7.0.0: + resolution: {integrity: sha512-e+QzoReTZ8IAwhnSdp/++7gBZ/F+nBq9y6PomfwORfP7q9nBpK5AMP64kOt0bA+lShBFbBDcgpJ3X4etHg4lzA==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.47 + + postcss-discard-overridden@5.1.0: + resolution: {integrity: sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.4.47 + + postcss-discard-overridden@7.0.0: + resolution: {integrity: sha512-GmNAzx88u3k2+sBTZrJSDauR0ccpE24omTQCVmaTTZFz1du6AasspjaUPMJ2ud4RslZpoFKyf+6MSPETLojc6w==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.47 + + postcss-double-position-gradients@3.1.2: + resolution: {integrity: sha512-GX+FuE/uBR6eskOK+4vkXgT6pDkexLokPaz/AbJna9s5Kzp/yl488pKPjhy0obB475ovfT1Wv8ho7U/cHNaRgQ==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.4.47 + + postcss-env-function@4.0.6: + resolution: {integrity: sha512-kpA6FsLra+NqcFnL81TnsU+Z7orGtDTxcOhl6pwXeEq1yFPpRMkCDpHhrz8CFQDr/Wfm0jLiNQ1OsGGPjlqPwA==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.4.47 + + postcss-flexbugs-fixes@5.0.2: + resolution: {integrity: sha512-18f9voByak7bTktR2QgDveglpn9DTbBWPUzSOe9g0N4WR/2eSt6Vrcbf0hmspvMI6YWGywz6B9f7jzpFNJJgnQ==} + peerDependencies: + postcss: ^8.4.47 + + postcss-focus-visible@6.0.4: + resolution: {integrity: sha512-QcKuUU/dgNsstIK6HELFRT5Y3lbrMLEOwG+A4s5cA+fx3A3y/JTq3X9LaOj3OC3ALH0XqyrgQIgey/MIZ8Wczw==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.4.47 + + postcss-focus-within@5.0.4: + resolution: {integrity: sha512-vvjDN++C0mu8jz4af5d52CB184ogg/sSxAFS+oUJQq2SuCe7T5U2iIsVJtsCp2d6R4j0jr5+q3rPkBVZkXD9fQ==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.4.47 + + postcss-font-variant@5.0.0: + resolution: {integrity: sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==} + peerDependencies: + postcss: ^8.4.47 + + postcss-gap-properties@3.0.5: + resolution: {integrity: sha512-IuE6gKSdoUNcvkGIqdtjtcMtZIFyXZhmFd5RUlg97iVEvp1BZKV5ngsAjCjrVy+14uhGBQl9tzmi1Qwq4kqVOg==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.4.47 + + postcss-image-set-function@4.0.7: + resolution: {integrity: sha512-9T2r9rsvYzm5ndsBE8WgtrMlIT7VbtTfE7b3BQnudUqnBcBo7L758oc+o+pdj/dUV0l5wjwSdjeOH2DZtfv8qw==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.4.47 + + postcss-import@15.1.0: + resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} + engines: {node: '>=14.0.0'} + peerDependencies: + postcss: ^8.4.47 + + postcss-initial@4.0.1: + resolution: {integrity: sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==} + peerDependencies: + postcss: ^8.4.47 + + postcss-js@4.0.1: + resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} + engines: {node: ^12 || ^14 || >= 16} + peerDependencies: + postcss: ^8.4.47 + + postcss-lab-function@4.2.1: + resolution: {integrity: sha512-xuXll4isR03CrQsmxyz92LJB2xX9n+pZJ5jE9JgcnmsCammLyKdlzrBin+25dy6wIjfhJpKBAN80gsTlCgRk2w==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.4.47 + + postcss-load-config@3.1.4: + resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==} + engines: {node: '>= 10'} + peerDependencies: + postcss: ^8.4.47 + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + + postcss-load-config@4.0.2: + resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} + engines: {node: '>= 14'} + peerDependencies: + postcss: ^8.4.47 + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + + postcss-loader@6.2.1: + resolution: {integrity: sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==} + engines: {node: '>= 12.13.0'} + peerDependencies: + postcss: ^8.4.47 + webpack: ^5.0.0 + + postcss-logical@5.0.4: + resolution: {integrity: sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.4.47 + + postcss-media-minmax@5.0.0: + resolution: {integrity: sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==} + engines: {node: '>=10.0.0'} + peerDependencies: + postcss: ^8.4.47 + + postcss-merge-longhand@5.1.7: + resolution: {integrity: sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.4.47 + + postcss-merge-longhand@7.0.4: + resolution: {integrity: sha512-zer1KoZA54Q8RVHKOY5vMke0cCdNxMP3KBfDerjH/BYHh4nCIh+1Yy0t1pAEQF18ac/4z3OFclO+ZVH8azjR4A==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.47 + + postcss-merge-rules@5.1.4: + resolution: {integrity: sha512-0R2IuYpgU93y9lhVbO/OylTtKMVcHb67zjWIfCiKR9rWL3GUk1677LAqD/BcHizukdZEjT8Ru3oHRoAYoJy44g==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.4.47 + + postcss-merge-rules@7.0.4: + resolution: {integrity: sha512-ZsaamiMVu7uBYsIdGtKJ64PkcQt6Pcpep/uO90EpLS3dxJi6OXamIobTYcImyXGoW0Wpugh7DSD3XzxZS9JCPg==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.47 + + postcss-minify-font-values@5.1.0: + resolution: {integrity: sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.4.47 + + postcss-minify-font-values@7.0.0: + resolution: {integrity: sha512-2ckkZtgT0zG8SMc5aoNwtm5234eUx1GGFJKf2b1bSp8UflqaeFzR50lid4PfqVI9NtGqJ2J4Y7fwvnP/u1cQog==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.47 + + postcss-minify-gradients@5.1.1: + resolution: {integrity: sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.4.47 + + postcss-minify-gradients@7.0.0: + resolution: {integrity: sha512-pdUIIdj/C93ryCHew0UgBnL2DtUS3hfFa5XtERrs4x+hmpMYGhbzo6l/Ir5de41O0GaKVpK1ZbDNXSY6GkXvtg==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.47 + + postcss-minify-params@5.1.4: + resolution: {integrity: sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.4.47 + + postcss-minify-params@7.0.2: + resolution: {integrity: sha512-nyqVLu4MFl9df32zTsdcLqCFfE/z2+f8GE1KHPxWOAmegSo6lpV2GNy5XQvrzwbLmiU7d+fYay4cwto1oNdAaQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.47 + + postcss-minify-selectors@5.2.1: + resolution: {integrity: sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.4.47 + + postcss-minify-selectors@7.0.4: + resolution: {integrity: sha512-JG55VADcNb4xFCf75hXkzc1rNeURhlo7ugf6JjiiKRfMsKlDzN9CXHZDyiG6x/zGchpjQS+UAgb1d4nqXqOpmA==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.47 + + postcss-modules-extract-imports@3.1.0: + resolution: {integrity: sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.4.47 + + postcss-modules-local-by-default@4.0.5: + resolution: {integrity: sha512-6MieY7sIfTK0hYfafw1OMEG+2bg8Q1ocHCpoWLqOKj3JXlKu4G7btkmM/B7lFubYkYWmRSPLZi5chid63ZaZYw==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.4.47 + + postcss-modules-scope@3.2.0: + resolution: {integrity: sha512-oq+g1ssrsZOsx9M96c5w8laRmvEu9C3adDSjI8oTcbfkrTE8hx/zfyobUoWIxaKPO8bt6S62kxpw5GqypEw1QQ==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.4.47 + + postcss-modules-values@4.0.0: + resolution: {integrity: sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.4.47 + + postcss-modules@4.3.1: + resolution: {integrity: sha512-ItUhSUxBBdNamkT3KzIZwYNNRFKmkJrofvC2nWab3CPKhYBQ1f27XXh1PAPE27Psx58jeelPsxWB/+og+KEH0Q==} + peerDependencies: + postcss: ^8.4.47 + + postcss-nested@6.2.0: + resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.4.47 + + postcss-nesting@10.2.0: + resolution: {integrity: sha512-EwMkYchxiDiKUhlJGzWsD9b2zvq/r2SSubcRrgP+jujMXFzqvANLt16lJANC+5uZ6hjI7lpRmI6O8JIl+8l1KA==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.4.47 + + postcss-normalize-charset@5.1.0: + resolution: {integrity: sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.4.47 + + postcss-normalize-charset@7.0.0: + resolution: {integrity: sha512-ABisNUXMeZeDNzCQxPxBCkXexvBrUHV+p7/BXOY+ulxkcjUZO0cp8ekGBwvIh2LbCwnWbyMPNJVtBSdyhM2zYQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.47 + + postcss-normalize-display-values@5.1.0: + resolution: {integrity: sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.4.47 + + postcss-normalize-display-values@7.0.0: + resolution: {integrity: sha512-lnFZzNPeDf5uGMPYgGOw7v0BfB45+irSRz9gHQStdkkhiM0gTfvWkWB5BMxpn0OqgOQuZG/mRlZyJxp0EImr2Q==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.47 + + postcss-normalize-positions@5.1.1: + resolution: {integrity: sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.4.47 + + postcss-normalize-positions@7.0.0: + resolution: {integrity: sha512-I0yt8wX529UKIGs2y/9Ybs2CelSvItfmvg/DBIjTnoUSrPxSV7Z0yZ8ShSVtKNaV/wAY+m7bgtyVQLhB00A1NQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.47 + + postcss-normalize-repeat-style@5.1.1: + resolution: {integrity: sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.4.47 + + postcss-normalize-repeat-style@7.0.0: + resolution: {integrity: sha512-o3uSGYH+2q30ieM3ppu9GTjSXIzOrRdCUn8UOMGNw7Af61bmurHTWI87hRybrP6xDHvOe5WlAj3XzN6vEO8jLw==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.47 + + postcss-normalize-string@5.1.0: + resolution: {integrity: sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.4.47 + + postcss-normalize-string@7.0.0: + resolution: {integrity: sha512-w/qzL212DFVOpMy3UGyxrND+Kb0fvCiBBujiaONIihq7VvtC7bswjWgKQU/w4VcRyDD8gpfqUiBQ4DUOwEJ6Qg==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.47 + + postcss-normalize-timing-functions@5.1.0: + resolution: {integrity: sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.4.47 + + postcss-normalize-timing-functions@7.0.0: + resolution: {integrity: sha512-tNgw3YV0LYoRwg43N3lTe3AEWZ66W7Dh7lVEpJbHoKOuHc1sLrzMLMFjP8SNULHaykzsonUEDbKedv8C+7ej6g==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.47 + + postcss-normalize-unicode@5.1.1: + resolution: {integrity: sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.4.47 + + postcss-normalize-unicode@7.0.2: + resolution: {integrity: sha512-ztisabK5C/+ZWBdYC+Y9JCkp3M9qBv/XFvDtSw0d/XwfT3UaKeW/YTm/MD/QrPNxuecia46vkfEhewjwcYFjkg==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.47 + + postcss-normalize-url@5.1.0: + resolution: {integrity: sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.4.47 + + postcss-normalize-url@7.0.0: + resolution: {integrity: sha512-+d7+PpE+jyPX1hDQZYG+NaFD+Nd2ris6r8fPTBAjE8z/U41n/bib3vze8x7rKs5H1uEw5ppe9IojewouHk0klQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.47 + + postcss-normalize-whitespace@5.1.1: + resolution: {integrity: sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.4.47 + + postcss-normalize-whitespace@7.0.0: + resolution: {integrity: sha512-37/toN4wwZErqohedXYqWgvcHUGlT8O/m2jVkAfAe9Bd4MzRqlBmXrJRePH0e9Wgnz2X7KymTgTOaaFizQe3AQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.47 + + postcss-normalize@10.0.1: + resolution: {integrity: sha512-+5w18/rDev5mqERcG3W5GZNMJa1eoYYNGo8gB7tEwaos0ajk3ZXAI4mHGcNT47NE+ZnZD1pEpUOFLvltIwmeJA==} + engines: {node: '>= 12'} + peerDependencies: + browserslist: '>= 4' + postcss: ^8.4.47 + + postcss-opacity-percentage@1.1.3: + resolution: {integrity: sha512-An6Ba4pHBiDtyVpSLymUUERMo2cU7s+Obz6BTrS+gxkbnSBNKSuD0AVUc+CpBMrpVPKKfoVz0WQCX+Tnst0i4A==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.4.47 + + postcss-ordered-values@5.1.3: + resolution: {integrity: sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.4.47 + + postcss-ordered-values@7.0.1: + resolution: {integrity: sha512-irWScWRL6nRzYmBOXReIKch75RRhNS86UPUAxXdmW/l0FcAsg0lvAXQCby/1lymxn/o0gVa6Rv/0f03eJOwHxw==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.47 + + postcss-overflow-shorthand@3.0.4: + resolution: {integrity: sha512-otYl/ylHK8Y9bcBnPLo3foYFLL6a6Ak+3EQBPOTR7luMYCOsiVTUk1iLvNf6tVPNGXcoL9Hoz37kpfriRIFb4A==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.4.47 + + postcss-page-break@3.0.4: + resolution: {integrity: sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==} + peerDependencies: + postcss: ^8.4.47 + + postcss-place@7.0.5: + resolution: {integrity: sha512-wR8igaZROA6Z4pv0d+bvVrvGY4GVHihBCBQieXFY3kuSuMyOmEnnfFzHl/tQuqHZkfkIVBEbDvYcFfHmpSet9g==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.4.47 + + postcss-preset-env@7.8.3: + resolution: {integrity: sha512-T1LgRm5uEVFSEF83vHZJV2z19lHg4yJuZ6gXZZkqVsqv63nlr6zabMH3l4Pc01FQCyfWVrh2GaUeCVy9Po+Aag==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.4.47 + + postcss-pseudo-class-any-link@7.1.6: + resolution: {integrity: sha512-9sCtZkO6f/5ML9WcTLcIyV1yz9D1rf0tWc+ulKcvV30s0iZKS/ONyETvoWsr6vnrmW+X+KmuK3gV/w5EWnT37w==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.4.47 + + postcss-reduce-initial@5.1.2: + resolution: {integrity: sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.4.47 + + postcss-reduce-initial@7.0.2: + resolution: {integrity: sha512-pOnu9zqQww7dEKf62Nuju6JgsW2V0KRNBHxeKohU+JkHd/GAH5uvoObqFLqkeB2n20mr6yrlWDvo5UBU5GnkfA==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.47 + + postcss-reduce-transforms@5.1.0: + resolution: {integrity: sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.4.47 + + postcss-reduce-transforms@7.0.0: + resolution: {integrity: sha512-pnt1HKKZ07/idH8cpATX/ujMbtOGhUfE+m8gbqwJE05aTaNw8gbo34a2e3if0xc0dlu75sUOiqvwCGY3fzOHew==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.47 + + postcss-replace-overflow-wrap@4.0.0: + resolution: {integrity: sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==} + peerDependencies: + postcss: ^8.4.47 + + postcss-selector-not@6.0.1: + resolution: {integrity: sha512-1i9affjAe9xu/y9uqWH+tD4r6/hDaXJruk8xn2x1vzxC2U3J3LKO3zJW4CyxlNhA56pADJ/djpEwpH1RClI2rQ==} + engines: {node: ^12 || ^14 || >=16} + peerDependencies: + postcss: ^8.4.47 + + postcss-selector-parser@6.0.10: + resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==} + engines: {node: '>=4'} + + postcss-selector-parser@6.1.2: + resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} + engines: {node: '>=4'} + + postcss-svgo@5.1.0: + resolution: {integrity: sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.4.47 + + postcss-svgo@7.0.1: + resolution: {integrity: sha512-0WBUlSL4lhD9rA5k1e5D8EN5wCEyZD6HJk0jIvRxl+FDVOMlJ7DePHYWGGVc5QRqrJ3/06FTXM0bxjmJpmTPSA==} + engines: {node: ^18.12.0 || ^20.9.0 || >= 18} + peerDependencies: + postcss: ^8.4.47 + + postcss-unique-selectors@5.1.1: + resolution: {integrity: sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.4.47 + + postcss-unique-selectors@7.0.3: + resolution: {integrity: sha512-J+58u5Ic5T1QjP/LDV9g3Cx4CNOgB5vz+kM6+OxHHhFACdcDeKhBXjQmB7fnIZM12YSTvsL0Opwco83DmacW2g==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.47 + + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + + postcss@8.4.47: + resolution: {integrity: sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==} + engines: {node: ^10 || ^12 || >=14} + + postgres-array@2.0.0: + resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} + engines: {node: '>=4'} + + postgres-array@3.0.2: + resolution: {integrity: sha512-6faShkdFugNQCLwucjPcY5ARoW1SlbnrZjmGl0IrrqewpvxvhSLHimCVzqeuULCbG0fQv7Dtk1yDbG3xv7Veog==} + engines: {node: '>=12'} + + postgres-bytea@1.0.0: + resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==} + engines: {node: '>=0.10.0'} + + postgres-bytea@3.0.0: + resolution: {integrity: sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==} + engines: {node: '>= 6'} + + postgres-date@1.0.7: + resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==} + engines: {node: '>=0.10.0'} + + postgres-date@2.1.0: + resolution: {integrity: sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA==} + engines: {node: '>=12'} + + postgres-interval@1.2.0: + resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} + engines: {node: '>=0.10.0'} + + postgres-interval@3.0.0: + resolution: {integrity: sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==} + engines: {node: '>=12'} + + postgres-range@1.1.4: + resolution: {integrity: sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w==} + + posthog-node@4.2.1: + resolution: {integrity: sha512-l+fsjYEkTik3m/G0pE7gMr4qBJP84LhK779oQm6MBzhBGpd4By4qieTW+4FUAlNCyzQTynn3Nhsa50c0IELSxQ==} + engines: {node: '>=15.0.0'} + + posthtml-parser@0.11.0: + resolution: {integrity: sha512-QecJtfLekJbWVo/dMAA+OSwY79wpRmbqS5TeXvXSX+f0c6pW4/SE6inzZ2qkU7oAMCPqIDkZDvd/bQsSFUnKyw==} + engines: {node: '>=12'} + + posthtml-render@3.0.0: + resolution: {integrity: sha512-z+16RoxK3fUPgwaIgH9NGnK1HKY9XIDpydky5eQGgAFVXTCSezalv9U2jQuNV+Z9qV1fDWNzldcw4eK0SSbqKA==} + engines: {node: '>=12'} + + posthtml@0.16.6: + resolution: {integrity: sha512-JcEmHlyLK/o0uGAlj65vgg+7LIms0xKXe60lcDOTU7oVX/3LuEuLwrQpW3VJ7de5TaFKiW4kWkaIpJL42FEgxQ==} + engines: {node: '>=12.0.0'} + + preact-render-to-string@5.2.6: + resolution: {integrity: sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==} + peerDependencies: + preact: '>=10' + + preact@10.24.3: + resolution: {integrity: sha512-Z2dPnBnMUfyQfSQ+GBdsGa16hz35YmLmtTLhM169uW944hYL6xzTYkJjC07j+Wosz733pMWx0fgON3JNw1jJQA==} + + prebuild-install@7.1.3: + resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==} + engines: {node: '>=10'} + hasBin: true + + preferred-pm@3.1.4: + resolution: {integrity: sha512-lEHd+yEm22jXdCphDrkvIJQU66EuLojPPtvZkpKIkiD+l0DMThF/niqZKJSoU8Vl7iuvtmzyMhir9LdVy5WMnA==} + engines: {node: '>=10'} + + prelude-ls@1.1.2: + resolution: {integrity: sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==} + engines: {node: '>= 0.8.0'} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier@2.8.8: + resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} + engines: {node: '>=10.13.0'} + hasBin: true + + prettier@3.4.2: + resolution: {integrity: sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==} + engines: {node: '>=14'} + hasBin: true + + prettier@3.6.2: + resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} + engines: {node: '>=14'} + hasBin: true + + pretty-bytes@3.0.1: + resolution: {integrity: sha512-eb7ZAeUTgfh294cElcu51w+OTRp/6ItW758LjwJSK72LDevcuJn0P4eD71PLMDGPwwatXmAmYHTkzvpKlJE3ow==} + engines: {node: '>=0.10.0'} + + pretty-bytes@5.6.0: + resolution: {integrity: sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==} + engines: {node: '>=6'} + + pretty-bytes@6.1.1: + resolution: {integrity: sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==} + engines: {node: ^14.13.1 || >=16.0.0} + + pretty-error@4.0.0: + resolution: {integrity: sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==} + + pretty-format@27.5.1: + resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + pretty-format@28.1.3: + resolution: {integrity: sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + + pretty-format@29.7.0: + resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + pretty-format@3.8.0: + resolution: {integrity: sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==} + + prism-react-renderer@2.4.1: + resolution: {integrity: sha512-ey8Ls/+Di31eqzUxC46h8MksNuGx/n0AAC8uKpwFau4RPDYLuE3EXTp8N8G2vX2N7UC/+IXeNUnlWBGGcAG+Ig==} + peerDependencies: + react: '>=16.0.0' + + prisma@6.5.0: + resolution: {integrity: sha512-yUGXmWqv5F4PByMSNbYFxke/WbnyTLjnJ5bKr8fLkcnY7U5rU9rUTh/+Fja+gOrRxEgtCbCtca94IeITj4j/pg==} + engines: {node: '>=18.18'} + hasBin: true + peerDependencies: + typescript: '>=5.1.0' + peerDependenciesMeta: + typescript: + optional: true + + prismjs@1.30.0: + resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} + engines: {node: '>=6'} + + proc-log@1.0.0: + resolution: {integrity: sha512-aCk8AO51s+4JyuYGg3Q/a6gnrlDO09NpVWePtjp7xwphcoQ04x5WAfCyugcsbLooWcMJ87CLkD4+604IckEdhg==} + + proc-log@3.0.0: + resolution: {integrity: sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + + process@0.11.10: + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} + engines: {node: '>= 0.6.0'} + + prom-client@15.1.3: + resolution: {integrity: sha512-6ZiOBfCywsD4k1BN9IX0uZhF+tJkV8q8llP64G5Hajs4JOeVLPCwpPVcpXy3BwYiUGgyJzsJJQeOIv7+hDSq8g==} + engines: {node: ^16 || ^18 || >=20} + + promise-all-reject-late@1.0.1: + resolution: {integrity: sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw==} + + promise-call-limit@1.0.2: + resolution: {integrity: sha512-1vTUnfI2hzui8AEIixbdAJlFY4LFDXqQswy/2eOlThAscXCY4It8FdVuI0fMJGAB2aWGbdQf/gv0skKYXmdrHA==} + + promise-inflight@1.0.1: + resolution: {integrity: sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==} + peerDependencies: + bluebird: '*' + peerDependenciesMeta: + bluebird: + optional: true + + promise-retry@2.0.1: + resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==} + engines: {node: '>=10'} + + promise.series@0.2.0: + resolution: {integrity: sha512-VWQJyU2bcDTgZw8kpfBpB/ejZASlCrzwz5f2hjb/zlujOEB4oeiAhHygAWq8ubsX2GVkD4kCU5V2dwOTaCY5EQ==} + engines: {node: '>=0.12'} + + promise@7.3.1: + resolution: {integrity: sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==} + + promise@8.3.0: + resolution: {integrity: sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==} + + prompts@2.4.2: + resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} + engines: {node: '>= 6'} + + prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + + proper-lockfile@4.1.2: + resolution: {integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==} + + properties-reader@2.3.0: + resolution: {integrity: sha512-z597WicA7nDZxK12kZqHr2TcvwNU1GCfA5UwfDY/HDp3hXPoPlb5rlEx9bwGTiJnc0OqbBTkU975jDToth8Gxw==} + engines: {node: '>=14'} + + proto-list@1.2.4: + resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} + + proto3-json-serializer@2.0.2: + resolution: {integrity: sha512-SAzp/O4Yh02jGdRc+uIrGoe87dkN/XtwxfZ4ZyafJHymd79ozp5VG5nyZ7ygqPM5+cpLDjjGnYFUkngonyDPOQ==} + engines: {node: '>=14.0.0'} + + protobufjs@7.4.0: + resolution: {integrity: sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==} + engines: {node: '>=12.0.0'} + + proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + + prr@1.0.1: + resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==} + + psl@1.9.0: + resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} + + pstree.remy@1.1.8: + resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==} + + pump@3.0.2: + resolution: {integrity: sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + pure-color@1.3.0: + resolution: {integrity: sha512-QFADYnsVoBMw1srW7OVKEYjG+MbIa49s54w1MA1EDY6r2r/sTcKKYqRX1f4GYvnXP7eN/Pe9HFcX+hwzmrXRHA==} + + pure-rand@6.1.0: + resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} + + q@1.5.1: + resolution: {integrity: sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==} + engines: {node: '>=0.6.0', teleport: '>=0.2.0'} + deprecated: |- + You or someone you depend on is using Q, the JavaScript Promise library that gave JavaScript developers strong feelings about promises. They can almost certainly migrate to the native JavaScript promise now. Thank you literally everyone for joining me in this bet against the odds. Be excellent to each other. + + (For a CapTP with native promises, see @endo/eventual-send and @endo/captp) + + qs@6.13.0: + resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} + engines: {node: '>=0.6'} + + querystringify@2.2.0: + resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + raf@3.4.1: + resolution: {integrity: sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==} + + randombytes@2.1.0: + resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + + raw-body@2.5.2: + resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} + engines: {node: '>= 0.8'} + + raw-loader@4.0.2: + resolution: {integrity: sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA==} + engines: {node: '>= 10.13.0'} + peerDependencies: + webpack: ^4.0.0 || ^5.0.0 + + rc-align@4.0.15: + resolution: {integrity: sha512-wqJtVH60pka/nOX7/IspElA8gjPNQKIx/ZqJ6heATCkXpe1Zg4cPVrMD2vC96wjsFFL8WsmhPbx9tdMo1qqlIA==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-cascader@3.30.0: + resolution: {integrity: sha512-rrzSbk1Bdqbu+pDwiLCLHu72+lwX9BZ28+JKzoi0DWZ4N29QYFeip8Gctl33QVd2Xg3Rf14D3yAOG76ElJw16w==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-checkbox@3.3.0: + resolution: {integrity: sha512-Ih3ZaAcoAiFKJjifzwsGiT/f/quIkxJoklW4yKGho14Olulwn8gN7hOBve0/WGDg5o/l/5mL0w7ff7/YGvefVw==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-collapse@3.9.0: + resolution: {integrity: sha512-swDdz4QZ4dFTo4RAUMLL50qP0EY62N2kvmk2We5xYdRwcRn8WcYtuetCJpwpaCbUfUt5+huLpVxhvmnK+PHrkA==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-dialog@9.6.0: + resolution: {integrity: sha512-ApoVi9Z8PaCQg6FsUzS8yvBEQy0ZL2PkuvAgrmohPkN3okps5WZ5WQWPc1RNuiOKaAYv8B97ACdsFU5LizzCqg==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-drawer@7.2.0: + resolution: {integrity: sha512-9lOQ7kBekEJRdEpScHvtmEtXnAsy+NGDXiRWc2ZVC7QXAazNVbeT4EraQKYwCME8BJLa8Bxqxvs5swwyOepRwg==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-dropdown@4.2.0: + resolution: {integrity: sha512-odM8Ove+gSh0zU27DUj5cG1gNKg7mLWBYzB5E4nNLrLwBmYEgYP43vHKDGOVZcJSVElQBI0+jTQgjnq0NfLjng==} + peerDependencies: + react: '>=16.11.0' + react-dom: '>=16.11.0' + + rc-field-form@2.5.0: + resolution: {integrity: sha512-trTezNK0ThFQkKTy3KmsP6tY1TPMm0O5H/YviB+QLyfpdcMFuYs/aPn1uDbpdQupBd6dE6X73bzITcPS97zhJQ==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-image@7.11.0: + resolution: {integrity: sha512-aZkTEZXqeqfPZtnSdNUnKQA0N/3MbgR7nUnZ+/4MfSFWPFHZau4p5r5ShaI0KPEMnNjv4kijSCFq/9wtJpwykw==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-input-number@9.3.0: + resolution: {integrity: sha512-JQ363ywqRyxwgVxpg2z2kja3CehTpYdqR7emJ/6yJjRdbvo+RvfE83fcpBCIJRq3zLp8SakmEXq60qzWyZ7Usw==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-input@1.6.3: + resolution: {integrity: sha512-wI4NzuqBS8vvKr8cljsvnTUqItMfG1QbJoxovCgL+DX4eVUcHIjVwharwevIxyy7H/jbLryh+K7ysnJr23aWIA==} + peerDependencies: + react: '>=16.0.0' + react-dom: '>=16.0.0' + + rc-mentions@2.17.0: + resolution: {integrity: sha512-sfHy+qLvc+p8jx8GUsujZWXDOIlIimp6YQz7N5ONQ6bHsa2kyG+BLa5k2wuxgebBbH97is33wxiyq5UkiXRpHA==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-menu@9.16.0: + resolution: {integrity: sha512-vAL0yqPkmXWk3+YKRkmIR8TYj3RVdEt3ptG2jCJXWNAvQbT0VJJdRyHZ7kG/l1JsZlB+VJq/VcYOo69VR4oD+w==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-motion@2.9.3: + resolution: {integrity: sha512-rkW47ABVkic7WEB0EKJqzySpvDqwl60/tdkY7hWP7dYnh5pm0SzJpo54oW3TDUGXV5wfxXFmMkxrzRRbotQ0+w==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-notification@5.6.2: + resolution: {integrity: sha512-Id4IYMoii3zzrG0lB0gD6dPgJx4Iu95Xu0BQrhHIbp7ZnAZbLqdqQ73aIWH0d0UFcElxwaKjnzNovTjo7kXz7g==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-overflow@1.3.2: + resolution: {integrity: sha512-nsUm78jkYAoPygDAcGZeC2VwIg/IBGSodtOY3pMof4W3M9qRJgqaDYm03ZayHlde3I6ipliAxbN0RUcGf5KOzw==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-pagination@4.3.0: + resolution: {integrity: sha512-UubEWA0ShnroQ1tDa291Fzw6kj0iOeF26IsUObxYTpimgj4/qPCWVFl18RLZE+0Up1IZg0IK4pMn6nB3mjvB7g==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-picker@2.7.6: + resolution: {integrity: sha512-H9if/BUJUZBOhPfWcPeT15JUI3/ntrG9muzERrXDkSoWmDj4yzmBvumozpxYrHwjcKnjyDGAke68d+whWwvhHA==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-picker@4.7.2: + resolution: {integrity: sha512-KShWVIdrncIKBZ1rm6E2s4Di9jlpcm38EYIZ472skXqKUz8YKlWQgewG5dT+HUjfon+tLs7dp5kmWUe0bW7Gqw==} + engines: {node: '>=8.x'} + peerDependencies: + date-fns: '>= 2.x' + dayjs: '>= 1.x' + luxon: '>= 3.x' + moment: '>= 2.x' + react: '>=16.9.0' + react-dom: '>=16.9.0' + peerDependenciesMeta: + date-fns: + optional: true + dayjs: + optional: true + luxon: + optional: true + moment: + optional: true + + rc-progress@4.0.0: + resolution: {integrity: sha512-oofVMMafOCokIUIBnZLNcOZFsABaUw8PPrf1/y0ZBvKZNpOiu5h4AO9vv11Sw0p4Hb3D0yGWuEattcQGtNJ/aw==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-rate@2.13.0: + resolution: {integrity: sha512-oxvx1Q5k5wD30sjN5tqAyWTvJfLNNJn7Oq3IeS4HxWfAiC4BOXMITNAsw7u/fzdtO4MS8Ki8uRLOzcnEuoQiAw==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-resize-observer@1.4.0: + resolution: {integrity: sha512-PnMVyRid9JLxFavTjeDXEXo65HCRqbmLBw9xX9gfC4BZiSzbLXKzW3jPz+J0P71pLbD5tBMTT+mkstV5gD0c9Q==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-segmented@2.5.0: + resolution: {integrity: sha512-B28Fe3J9iUFOhFJET3RoXAPFJ2u47QvLSYcZWC4tFYNGPEjug5LAxEasZlA/PpAxhdOPqGWsGbSj7ftneukJnw==} + peerDependencies: + react: '>=16.0.0' + react-dom: '>=16.0.0' + + rc-select@14.16.3: + resolution: {integrity: sha512-51+j6s3fJJJXB7E+B6W1hM4Tjzv1B/Decooz9ilgegDBt3ZAth1b/xMwYCTrT5BbG2e53XACQsyDib2+3Ro1fg==} + engines: {node: '>=8.x'} + peerDependencies: + react: '*' + react-dom: '*' + + rc-slider@11.1.7: + resolution: {integrity: sha512-ytYbZei81TX7otdC0QvoYD72XSlxvTihNth5OeZ6PMXyEDq/vHdWFulQmfDGyXK1NwKwSlKgpvINOa88uT5g2A==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-steps@6.0.1: + resolution: {integrity: sha512-lKHL+Sny0SeHkQKKDJlAjV5oZ8DwCdS2hFhAkIjuQt1/pB81M0cA0ErVFdHq9+jmPmFw1vJB2F5NBzFXLJxV+g==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-switch@4.1.0: + resolution: {integrity: sha512-TI8ufP2Az9oEbvyCeVE4+90PDSljGyuwix3fV58p7HV2o4wBnVToEyomJRVyTaZeqNPAp+vqeo4Wnj5u0ZZQBg==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-table@7.48.1: + resolution: {integrity: sha512-Z4mDKjWg+xz/Ezdw6ivWcbqRpaJ0QfCORRoRrlrw65KSGZLK8OcTdacH22/fyGb8L4It/0/9qcMm8VrVAk/WBw==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-tabs@15.4.0: + resolution: {integrity: sha512-llKuyiAVqmXm2z7OrmhX5cNb2ueZaL8ZyA2P4R+6/72NYYcbEgOXibwHiQCFY2RiN3swXl53SIABi2CumUS02g==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-textarea@1.8.2: + resolution: {integrity: sha512-UFAezAqltyR00a8Lf0IPAyTd29Jj9ee8wt8DqXyDMal7r/Cg/nDt3e1OOv3Th4W6mKaZijjgwuPXhAfVNTN8sw==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-tooltip@6.2.1: + resolution: {integrity: sha512-rws0duD/3sHHsD905Nex7FvoUGy2UBQRhTkKxeEvr2FB+r21HsOxcDJI0TzyO8NHhnAA8ILr8pfbSBg5Jj5KBg==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-tree-select@5.24.4: + resolution: {integrity: sha512-MzljkSkk7weKOcE853UtYlXB6uyUEzcEQhhpaCwE6jQPbmBUgGiRURuKWpYUnM/dXrwTTlCK969M6Pgjj35MLA==} + peerDependencies: + react: '*' + react-dom: '*' + + rc-tree@5.10.1: + resolution: {integrity: sha512-FPXb3tT/u39mgjr6JNlHaUTYfHkVGW56XaGDahDpEFLGsnPxGcVLNTjcqoQb/GNbSCycl7tD7EvIymwOTP0+Yw==} + engines: {node: '>=10.x'} + peerDependencies: + react: '*' + react-dom: '*' + + rc-trigger@5.3.4: + resolution: {integrity: sha512-mQv+vas0TwKcjAO2izNPkqR4j86OemLRmvL2nOzdP9OWNWA1ivoTt5hzFqYNW9zACwmTezRiN8bttrC7cZzYSw==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-upload@4.8.1: + resolution: {integrity: sha512-toEAhwl4hjLAI1u8/CgKWt30BR06ulPa4iGQSMvSXoHzO88gPCslxqV/mnn4gJU7PDoltGIC9Eh+wkeudqgHyw==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-util@5.43.0: + resolution: {integrity: sha512-AzC7KKOXFqAdIBqdGWepL9Xn7cm3vnAmjlHqUnoQaTMZYhM4VlXGLkkHHxj/BZ7Td0+SOPKB4RGPboBVKT9htw==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-virtual-list@3.14.8: + resolution: {integrity: sha512-8D0KfzpRYi6YZvlOWIxiOm9BGt4Wf2hQyEaM6RXlDDiY2NhLheuYI+RA+7ZaZj1lq+XQqy3KHlaeeXQfzI5fGg==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc@1.2.8: + resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} + hasBin: true + + react-app-polyfill@3.0.0: + resolution: {integrity: sha512-sZ41cxiU5llIB003yxxQBYrARBqe0repqPTTYBTmMqTz9szeBbE37BehCE891NZsmdZqqP+xWKdT3eo3vOzN8w==} + engines: {node: '>=14'} + + react-base16-styling@0.6.0: + resolution: {integrity: sha512-yvh/7CArceR/jNATXOKDlvTnPKPmGZz7zsenQ3jUwLzHkNUR0CvY3yGYJbWJ/nnxsL8Sgmt5cO3/SILVuPO6TQ==} + + react-dev-utils@12.0.1: + resolution: {integrity: sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=2.7' + webpack: '>=4' + peerDependenciesMeta: + typescript: + optional: true + + react-dom@18.3.1: + resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} + peerDependencies: + react: ^18.3.1 + + react-dom@19.0.0: + resolution: {integrity: sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==} + peerDependencies: + react: ^19.0.0 + + react-email@4.1.3: + resolution: {integrity: sha512-bLiJm20NvgFgOz03o6nCW/4wvJQTnhIY1fI2/qyPCa6V7MGe1RuvSGyrMtaEMJm8yyOu0ylJDX5Dlrcv6xihSQ==} + engines: {node: '>=18.0.0'} + hasBin: true + + react-error-overlay@6.0.11: + resolution: {integrity: sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==} + + react-icons@4.12.0: + resolution: {integrity: sha512-IBaDuHiShdZqmfc/TwHu6+d6k2ltNCf3AszxNmjJc1KUfXdEeRJOKyNvLmAHaarhzGmTSVygNdyu8/opXv2gaw==} + peerDependencies: + react: '*' + + react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + react-is@17.0.2: + resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + + react-is@18.3.1: + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + + react-json-view@1.21.3: + resolution: {integrity: sha512-13p8IREj9/x/Ye4WI/JpjhoIwuzEgUAtgJZNBJckfzJt1qyh24BdTm6UQNGnyTq9dapQdrqvquZTo3dz1X6Cjw==} + peerDependencies: + react: ^17.0.0 || ^16.3.0 || ^15.5.4 + react-dom: ^17.0.0 || ^16.3.0 || ^15.5.4 + + react-lifecycles-compat@3.0.4: + resolution: {integrity: sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==} + + react-promise-suspense@0.3.4: + resolution: {integrity: sha512-I42jl7L3Ze6kZaq+7zXWSunBa3b1on5yfvUW6Eo/3fFOj6dZ5Bqmcd264nJbTK/gn1HjjILAjSwnZbV4RpSaNQ==} + + react-reconciler@0.26.2: + resolution: {integrity: sha512-nK6kgY28HwrMNwDnMui3dvm3rCFjZrcGiuwLc5COUipBK5hWHLOxMJhSnSomirqWwjPBJKV1QcbkI0VJr7Gl1Q==} + engines: {node: '>=0.10.0'} + peerDependencies: + react: ^17.0.2 + + react-refresh@0.11.0: + resolution: {integrity: sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==} + engines: {node: '>=0.10.0'} + + react-remove-scroll-bar@2.3.8: + resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + react-remove-scroll@2.7.1: + resolution: {integrity: sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + react-router-dom@6.27.0: + resolution: {integrity: sha512-+bvtFWMC0DgAFrfKXKG9Fc+BcXWRUO1aJIihbB79xaeq0v5UzfvnM5houGUm1Y461WVRcgAQ+Clh5rdb1eCx4g==} + engines: {node: '>=14.0.0'} + peerDependencies: + react: '>=16.8' + react-dom: '>=16.8' + + react-router@6.27.0: + resolution: {integrity: sha512-YA+HGZXz4jaAkVoYBE98VQl+nVzI+cVI2Oj/06F5ZM+0u3TgedN9Y9kmMRo2mnkSK2nCpNQn0DVob4HCsY/WLw==} + engines: {node: '>=14.0.0'} + peerDependencies: + react: '>=16.8' + + react-scripts@5.0.1: + resolution: {integrity: sha512-8VAmEm/ZAwQzJ+GOMLbBsTdDKOpuZh7RPs0UymvBR2vRk4iZWCskjbFnxqjrzoIvlNNRZ3QJFx6/qDSi6zSnaQ==} + engines: {node: '>=14.0.0'} + hasBin: true + peerDependencies: + eslint: '*' + react: '>= 16' + typescript: ^3.2.1 || ^4 + peerDependenciesMeta: + typescript: + optional: true + + react-smooth@4.0.1: + resolution: {integrity: sha512-OE4hm7XqR0jNOq3Qmk9mFLyd6p2+j6bvbPJ7qlB7+oo0eNcL2l7WQzG6MBnT3EXY6xzkLMUBec3AfewJdA0J8w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + + react-style-singleton@2.2.3: + resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + react-textarea-autosize@8.5.4: + resolution: {integrity: sha512-eSSjVtRLcLfFwFcariT77t9hcbVJHQV76b51QjQGarQIHml2+gM2lms0n3XrhnDmgK5B+/Z7TmQk5OHNzqYm/A==} + engines: {node: '>=10'} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + + react-transition-group@4.4.5: + resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==} + peerDependencies: + react: '>=16.6.0' + react-dom: '>=16.6.0' + + react-universal-interface@0.6.2: + resolution: {integrity: sha512-dg8yXdcQmvgR13RIlZbTRQOoUrDciFVoSBZILwjE2LFISxZZ8loVJKAkuzswl5js8BHda79bIb2b84ehU8IjXw==} + peerDependencies: + react: '*' + tslib: '*' + + react-use@17.5.1: + resolution: {integrity: sha512-LG/uPEVRflLWMwi3j/sZqR00nF6JGqTTDblkXK2nzXsIvij06hXl1V/MZIlwj1OKIQUtlh1l9jK8gLsRyCQxMg==} + peerDependencies: + react: '*' + react-dom: '*' + + react@18.3.1: + resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} + engines: {node: '>=0.10.0'} + + react@19.0.0: + resolution: {integrity: sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==} + engines: {node: '>=0.10.0'} + + read-cache@1.0.0: + resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + + read-cmd-shim@3.0.1: + resolution: {integrity: sha512-kEmDUoYf/CDy8yZbLTmhB1X9kkjf9Q80PCNsDMb7ufrGd6zZSQA1+UyjrO+pZm5K/S4OXCWJeiIt1JA8kAsa6g==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + read-package-json-fast@2.0.3: + resolution: {integrity: sha512-W/BKtbL+dUjTuRL2vziuYhp76s5HZ9qQhd/dKfWIZveD0O40453QNyZhC0e63lqZrAQ4jiOapVoeJ7JrszenQQ==} + engines: {node: '>=10'} + + read-package-json-fast@3.0.2: + resolution: {integrity: sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + read-package-json@6.0.4: + resolution: {integrity: sha512-AEtWXYfopBj2z5N5PbkAOeNHRPUg5q+Nen7QLxV8M2zJq1ym6/lCz3fYNTCXe19puu2d06jfHhrP7v/S2PtMMw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + deprecated: This package is no longer supported. Please use @npmcli/package-json instead. + + read-pkg-up@7.0.1: + resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==} + engines: {node: '>=8'} + + read-pkg@5.2.0: + resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==} + engines: {node: '>=8'} + + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + readable-stream@4.5.2: + resolution: {integrity: sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + readdir-glob@1.1.3: + resolution: {integrity: sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==} + + readdir-scoped-modules@1.1.0: + resolution: {integrity: sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw==} + deprecated: This functionality has been moved to @npmcli/fs + + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} + + recharts-scale@0.4.5: + resolution: {integrity: sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==} + + recharts@2.13.1: + resolution: {integrity: sha512-87LdsmgK/MHLmWQfTC6yDysno2cOigi/+2KRCwy0D8NDu1IOdtTGS8lMovA0VIvJ7kf3zdp1IiwznHZWSPJhYw==} + engines: {node: '>=14'} + peerDependencies: + react: ^16.0.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 + + rechoir@0.6.2: + resolution: {integrity: sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==} + engines: {node: '>= 0.10'} + + rechoir@0.8.0: + resolution: {integrity: sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==} + engines: {node: '>= 10.13.0'} + + recursive-readdir@2.2.3: + resolution: {integrity: sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==} + engines: {node: '>=6.0.0'} + + redent@3.0.0: + resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} + engines: {node: '>=8'} + + redis-errors@1.2.0: + resolution: {integrity: sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==} + engines: {node: '>=4'} + + redis-parser@3.0.0: + resolution: {integrity: sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==} + engines: {node: '>=4'} + + reflect.getprototypeof@1.0.6: + resolution: {integrity: sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==} + engines: {node: '>= 0.4'} + + regenerate-unicode-properties@10.2.0: + resolution: {integrity: sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==} + engines: {node: '>=4'} + + regenerate@1.4.2: + resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==} + + regenerator-runtime@0.10.5: + resolution: {integrity: sha512-02YopEIhAgiBHWeoTiA8aitHDt8z6w+rQqNuIftlM+ZtvSl/brTouaU7DW6GO/cHtvxJvS4Hwv2ibKdxIRi24w==} + + regenerator-runtime@0.13.11: + resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==} + + regenerator-runtime@0.14.1: + resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + + regenerator-transform@0.15.2: + resolution: {integrity: sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==} + + regex-parser@2.3.0: + resolution: {integrity: sha512-TVILVSz2jY5D47F4mA4MppkBrafEaiUWJO/TcZHEIuI13AqoZMkK1WMA4Om1YkYbTx+9Ki1/tSUXbceyr9saRg==} + + regexp.prototype.flags@1.5.3: + resolution: {integrity: sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==} + engines: {node: '>= 0.4'} + + regexpu-core@6.1.1: + resolution: {integrity: sha512-k67Nb9jvwJcJmVpw0jPttR1/zVfnKf8Km0IPatrU/zJ5XeG3+Slx0xLXs9HByJSzXzrlz5EDvN6yLNMDc2qdnw==} + engines: {node: '>=4'} + + regjsgen@0.8.0: + resolution: {integrity: sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==} + + regjsparser@0.11.2: + resolution: {integrity: sha512-3OGZZ4HoLJkkAZx/48mTXJNlmqTGOzc0o9OWQPuWpkOlXXPbyN6OafCcoXUnBqE2D3f/T5L+pWc1kdEmnfnRsA==} + hasBin: true + + relateurl@0.2.7: + resolution: {integrity: sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==} + engines: {node: '>= 0.10'} + + remove-trailing-separator@1.1.0: + resolution: {integrity: sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==} + + renderkid@3.0.0: + resolution: {integrity: sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==} + + replace-ext@1.0.1: + resolution: {integrity: sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==} + engines: {node: '>= 0.10'} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + requires-port@1.0.0: + resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + + resend@4.0.0: + resolution: {integrity: sha512-rDX0rspl/XcmC2JV2V5obQvRX2arzxXUvNFUDMOv5ObBLR68+7kigCOysb7+dlkb0JE3erhQG0nHrbBt/ZCWIg==} + engines: {node: '>=18'} + + resize-observer-polyfill@1.5.1: + resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==} + + resolve-cwd@3.0.0: + resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} + engines: {node: '>=8'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + resolve-url-loader@4.0.0: + resolution: {integrity: sha512-05VEMczVREcbtT7Bz+C+96eUO5HDNvdthIiMB34t7FcF8ehcu4wC0sSgPUubs3XW2Q3CNLJk/BJrCU9wVRymiA==} + engines: {node: '>=8.9'} + peerDependencies: + rework: 1.0.1 + rework-visit: 1.0.0 + peerDependenciesMeta: + rework: + optional: true + rework-visit: + optional: true + + resolve.exports@1.1.1: + resolution: {integrity: sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ==} + engines: {node: '>=10'} + + resolve.exports@2.0.2: + resolution: {integrity: sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==} + engines: {node: '>=10'} + + resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + hasBin: true + + resolve@2.0.0-next.5: + resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} + hasBin: true + + restore-cursor@3.1.0: + resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} + engines: {node: '>=8'} + + restore-cursor@5.1.0: + resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} + engines: {node: '>=18'} + + retry-request@7.0.2: + resolution: {integrity: sha512-dUOvLMJ0/JJYEn8NrpOaGNE7X3vpI5XlZS/u0ANjqtcZVKnIxP7IgCFwrKTxENw29emmwug53awKtaMm4i9g5w==} + engines: {node: '>=14'} + + retry@0.12.0: + resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} + engines: {node: '>= 4'} + + retry@0.13.1: + resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} + engines: {node: '>= 4'} + + reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rimraf@2.7.1: + resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + + rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + + rimraf@5.0.10: + resolution: {integrity: sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==} + hasBin: true + + rollup-plugin-bundle-size@1.0.3: + resolution: {integrity: sha512-aWj0Pvzq90fqbI5vN1IvUrlf4utOqy+AERYxwWjegH1G8PzheMnrRIgQ5tkwKVtQMDP0bHZEACW/zLDF+XgfXQ==} + + rollup-plugin-dts@6.2.1: + resolution: {integrity: sha512-sR3CxYUl7i2CHa0O7bA45mCrgADyAQ0tVtGSqi3yvH28M+eg1+g5d7kQ9hLvEz5dorK3XVsH5L2jwHLQf72DzA==} + engines: {node: '>=16'} + peerDependencies: + rollup: ^3.29.4 || ^4 + typescript: ^4.5 || ^5.0 + + rollup-plugin-postcss@4.0.2: + resolution: {integrity: sha512-05EaY6zvZdmvPUDi3uCcAQoESDcYnv8ogJJQRp6V5kZ6J6P7uAVJlrTZcaaA20wTH527YTnKfkAoPxWI/jPp4w==} + engines: {node: '>=10'} + peerDependencies: + postcss: ^8.4.47 + + rollup-plugin-terser@7.0.2: + resolution: {integrity: sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==} + deprecated: This package has been deprecated and is no longer maintained. Please use @rollup/plugin-terser + peerDependencies: + rollup: ^2.0.0 + + rollup-plugin-typescript2@0.32.1: + resolution: {integrity: sha512-RanO8bp1WbeMv0bVlgcbsFNCn+Y3rX7wF97SQLDxf0fMLsg0B/QFF005t4AsGUcDgF3aKJHoqt4JF2xVaABeKw==} + peerDependencies: + rollup: '>=1.26.3' + typescript: '>=2.4.0' + + rollup-plugin-visualizer@5.12.0: + resolution: {integrity: sha512-8/NU9jXcHRs7Nnj07PF2o4gjxmm9lXIrZ8r175bT9dK8qoLlvKTwRMArRCMgpMGlq8CTLugRvEmyMeMXIU2pNQ==} + engines: {node: '>=14'} + hasBin: true + peerDependencies: + rollup: 2.x || 3.x || 4.x + peerDependenciesMeta: + rollup: + optional: true + + rollup-pluginutils@2.8.2: + resolution: {integrity: sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==} + + rollup@2.79.2: + resolution: {integrity: sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==} + engines: {node: '>=10.0.0'} + hasBin: true + + rollup@3.29.5: + resolution: {integrity: sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==} + engines: {node: '>=14.18.0', npm: '>=8.0.0'} + hasBin: true + + rollup@4.24.3: + resolution: {integrity: sha512-HBW896xR5HGmoksbi3JBDtmVzWiPAYqp7wip50hjQ67JbDz61nyoMPdqu1DvVW9asYb2M65Z20ZHsyJCMqMyDg==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + rtl-css-js@1.16.1: + resolution: {integrity: sha512-lRQgou1mu19e+Ya0LsTvKrVJ5TYUbqCVPAiImX3UfLTenarvPUl1QFdvu5Z3PYmHT9RCcwIfbjRQBntExyj3Zg==} + + run-async@2.4.1: + resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==} + engines: {node: '>=0.12.0'} + + run-async@3.0.0: + resolution: {integrity: sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==} + engines: {node: '>=0.12.0'} + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + rusha@0.8.14: + resolution: {integrity: sha512-cLgakCUf6PedEu15t8kbsjnwIFFR2D4RfL+W3iWFJ4iac7z4B0ZI8fxy4R3J956kAI68HclCFGL8MPoUVC3qVA==} + + rxjs@7.8.1: + resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} + + sade@1.8.1: + resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} + engines: {node: '>=6'} + + safe-array-concat@1.1.2: + resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==} + engines: {node: '>=0.4'} + + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safe-identifier@0.4.2: + resolution: {integrity: sha512-6pNbSMW6OhAi9j+N8V+U715yBQsaWJ7eyEUaOrawX+isg5ZxhUlV1NipNtgaKHmFGiABwt+ZF04Ii+3Xjkg+8w==} + + safe-regex-test@1.0.3: + resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} + engines: {node: '>= 0.4'} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + sanitize.css@13.0.0: + resolution: {integrity: sha512-ZRwKbh/eQ6w9vmTjkuG0Ioi3HBwPFce0O+v//ve+aOq1oeCy7jMV2qzzAlpsNuqpqCBjjriM1lbtZbF/Q8jVyA==} + + sass-loader@12.6.0: + resolution: {integrity: sha512-oLTaH0YCtX4cfnJZxKSLAyglED0naiYfNG1iXfU5w1LNZ+ukoA5DtyDIN5zmKVZwYNJP4KRc5Y3hkWga+7tYfA==} + engines: {node: '>= 12.13.0'} + peerDependencies: + fibers: '>= 3.1.0' + node-sass: ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + sass: ^1.3.0 + sass-embedded: '*' + webpack: ^5.0.0 + peerDependenciesMeta: + fibers: + optional: true + node-sass: + optional: true + sass: + optional: true + sass-embedded: + optional: true + + sax@1.2.4: + resolution: {integrity: sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==} + + sax@1.4.1: + resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==} + + saxes@5.0.1: + resolution: {integrity: sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==} + engines: {node: '>=10'} + + scheduler@0.20.2: + resolution: {integrity: sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==} + + scheduler@0.23.2: + resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} + + scheduler@0.25.0: + resolution: {integrity: sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==} + + schema-utils@2.7.0: + resolution: {integrity: sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==} + engines: {node: '>= 8.9.0'} + + schema-utils@2.7.1: + resolution: {integrity: sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==} + engines: {node: '>= 8.9.0'} + + schema-utils@3.3.0: + resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==} + engines: {node: '>= 10.13.0'} + + schema-utils@4.2.0: + resolution: {integrity: sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==} + engines: {node: '>= 12.13.0'} + + schema-utils@4.3.0: + resolution: {integrity: sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==} + engines: {node: '>= 10.13.0'} + + scoped-regex@2.1.0: + resolution: {integrity: sha512-g3WxHrqSWCZHGHlSrF51VXFdjImhwvH8ZO/pryFH56Qi0cDsZfylQa/t0jCzVQFNbNvM00HfHjkDPEuarKDSWQ==} + engines: {node: '>=8'} + + screenfull@5.2.0: + resolution: {integrity: sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA==} + engines: {node: '>=0.10.0'} + + scroll-into-view-if-needed@3.1.0: + resolution: {integrity: sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==} + + selderee@0.11.0: + resolution: {integrity: sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA==} + + select-hose@2.0.0: + resolution: {integrity: sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==} + + selfsigned@2.4.1: + resolution: {integrity: sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==} + engines: {node: '>=10'} + + semver@5.7.2: + resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} + hasBin: true + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.6.3: + resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} + engines: {node: '>=10'} + hasBin: true + + semver@7.7.1: + resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==} + engines: {node: '>=10'} + hasBin: true + + semver@7.7.2: + resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} + engines: {node: '>=10'} + hasBin: true + + send@0.19.0: + resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==} + engines: {node: '>= 0.8.0'} + + serialize-javascript@4.0.0: + resolution: {integrity: sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==} + + serialize-javascript@6.0.2: + resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} + + serve-index@1.9.1: + resolution: {integrity: sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==} + engines: {node: '>= 0.8.0'} + + serve-static@1.16.2: + resolution: {integrity: sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==} + engines: {node: '>= 0.8.0'} + + set-blocking@2.0.0: + resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + + set-harmonic-interval@1.0.1: + resolution: {integrity: sha512-AhICkFV84tBP1aWqPwLZqFvAwqEoVA9kxNMniGEUvzOlm4vLmOFLiTT3UZ6bziJTy4bOVpzWGTfSCbmaayGx8g==} + engines: {node: '>=6.9'} + + setimmediate@1.0.5: + resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + + setprototypeof@1.1.0: + resolution: {integrity: sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==} + + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + + shallow-clone@3.0.1: + resolution: {integrity: sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==} + engines: {node: '>=8'} + + shallowequal@1.1.0: + resolution: {integrity: sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==} + + sharp@0.34.1: + resolution: {integrity: sha512-1j0w61+eVxu7DawFJtnfYcvSv6qPFvfTaqzTQ2BLknVhHTwGS8sc63ZBF4rzkWMBVKybo4S5OBtDdZahh2A1xg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + + sharp@0.34.3: + resolution: {integrity: sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + shell-quote@1.8.1: + resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==} + + shelljs@0.8.5: + resolution: {integrity: sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==} + engines: {node: '>=4'} + hasBin: true + + side-channel@1.0.6: + resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} + engines: {node: '>= 0.4'} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + sigstore@1.9.0: + resolution: {integrity: sha512-0Zjz0oe37d08VeOtBIuB6cRriqXse2e8w+7yIy2XSXjshRKxbc2KkhXjL229jXSxEm7UbcjS76wcJDGQddVI9A==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + hasBin: true + + simple-concat@1.0.1: + resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} + + simple-get@4.0.1: + resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==} + + simple-git@3.27.0: + resolution: {integrity: sha512-ivHoFS9Yi9GY49ogc6/YAi3Fl9ROnF4VyubNylgCkA+RVqLaKWnDSzXOVzya8csELIaWaYNutsEuAhZrtOjozA==} + + simple-swizzle@0.2.2: + resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + + simple-update-notifier@2.0.0: + resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==} + engines: {node: '>=10'} + + sirv@2.0.4: + resolution: {integrity: sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==} + engines: {node: '>= 10'} + + sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + slash@4.0.0: + resolution: {integrity: sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==} + engines: {node: '>=12'} + + slash@5.1.0: + resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} + engines: {node: '>=14.16'} + + slick@1.12.2: + resolution: {integrity: sha512-4qdtOGcBjral6YIBCWJ0ljFSKNLz9KkhbWtuGvUyRowl1kxfuE1x/Z/aJcaiilpb3do9bl5K7/1h9XC5wWpY/A==} + + smart-buffer@4.2.0: + resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} + engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + + smob@1.5.0: + resolution: {integrity: sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig==} + + socket.io-adapter@2.5.5: + resolution: {integrity: sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==} + + socket.io-client@4.8.1: + resolution: {integrity: sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==} + engines: {node: '>=10.0.0'} + + socket.io-parser@4.2.4: + resolution: {integrity: sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==} + engines: {node: '>=10.0.0'} + + socket.io@4.8.1: + resolution: {integrity: sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==} + engines: {node: '>=10.2.0'} + + sockjs@0.3.24: + resolution: {integrity: sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==} + + socks-proxy-agent@6.2.1: + resolution: {integrity: sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==} + engines: {node: '>= 10'} + + socks-proxy-agent@7.0.0: + resolution: {integrity: sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==} + engines: {node: '>= 10'} + + socks@2.8.3: + resolution: {integrity: sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==} + engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} + + sonner@2.0.3: + resolution: {integrity: sha512-njQ4Hht92m0sMqqHVDL32V2Oun9W1+PHO9NDv9FHfJjT3JT22IG4Jpo3FPQy+mouRKCXFWO+r67v6MrHX2zeIA==} + peerDependencies: + react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc + react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-rc + + sort-keys@4.2.0: + resolution: {integrity: sha512-aUYIEU/UviqPgc8mHR6IW1EGxkAXpeRETYcrzg8cLAvUPZcpAlleSXHV2mY7G12GphSH6Gzv+4MMVSSkbdteHg==} + engines: {node: '>=8'} + + source-list-map@2.0.1: + resolution: {integrity: sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map-loader@3.0.2: + resolution: {integrity: sha512-BokxPoLjyl3iOrgkWaakaxqnelAJSS+0V+De0kKIq6lyWrXuiPgYTGp6z3iHmqljKAaLXwZa+ctD8GccRJeVvg==} + engines: {node: '>= 12.13.0'} + peerDependencies: + webpack: ^5.0.0 + + source-map-support@0.5.13: + resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} + + source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + + source-map@0.5.6: + resolution: {integrity: sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==} + engines: {node: '>=0.10.0'} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + source-map@0.7.4: + resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} + engines: {node: '>= 8'} + + source-map@0.8.0-beta.0: + resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} + engines: {node: '>= 8'} + + sourcemap-codec@1.4.8: + resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} + deprecated: Please use @jridgewell/sourcemap-codec instead + + spamc@0.0.5: + resolution: {integrity: sha512-jYXItuZuiWZyG9fIdvgTUbp2MNRuyhuSwvvhhpPJd4JK/9oSZxkD7zAj53GJtowSlXwCJzLg6sCKAoE9wXsKgg==} + + sparse-bitfield@3.0.3: + resolution: {integrity: sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==} + + spdx-correct@3.2.0: + resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} + + spdx-exceptions@2.5.0: + resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} + + spdx-expression-parse@3.0.1: + resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} + + spdx-license-ids@3.0.20: + resolution: {integrity: sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==} + + spdy-transport@3.0.0: + resolution: {integrity: sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==} + + spdy@4.0.2: + resolution: {integrity: sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==} + engines: {node: '>=6.0.0'} + + spex@3.4.0: + resolution: {integrity: sha512-8JeZJ7QlEBnSj1W1fKXgbB2KUPA8k4BxFMf6lZX/c1ZagU/1b9uZWZK0yD6yjfzqAIuTNG4YlRmtMpQiXuohsg==} + engines: {node: '>=14.0.0'} + + split-ca@1.0.1: + resolution: {integrity: sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ==} + + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + + sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + + sprintf-js@1.1.3: + resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} + + ssh-remote-port-forward@1.0.4: + resolution: {integrity: sha512-x0LV1eVDwjf1gmG7TTnfqIzf+3VPRz7vrNIjX6oYLbeCrf/PeVY6hkT68Mg+q02qXxQhrLjB0jfgvhevoCRmLQ==} + + ssh2@1.16.0: + resolution: {integrity: sha512-r1X4KsBGedJqo7h8F5c4Ybpcr5RjyP+aWIG007uBPRjmdQWfEiVLzSK71Zji1B9sKxwaCvD8y8cwSkYrlLiRRg==} + engines: {node: '>=10.16.0'} + + ssri@10.0.6: + resolution: {integrity: sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + ssri@8.0.1: + resolution: {integrity: sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==} + engines: {node: '>= 8'} + + ssri@9.0.1: + resolution: {integrity: sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + stable-hash@0.0.3: + resolution: {integrity: sha512-c63fvNCQ7ip1wBfPv54MflMA5L6OE5J0p6Fg13IpKft4JAFoNal8+s/jtJ8PibrwqXm4onnbeQsADs8k0NQGUA==} + + stable@0.1.8: + resolution: {integrity: sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==} + deprecated: 'Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility' + + stack-generator@2.0.10: + resolution: {integrity: sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ==} + + stack-utils@2.0.6: + resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} + engines: {node: '>=10'} + + stackframe@1.3.4: + resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==} + + stacktrace-gps@3.1.2: + resolution: {integrity: sha512-GcUgbO4Jsqqg6RxfyTHFiPxdPqF+3LFmQhm7MgCuYQOYuWyqxo5pwRPz5d/u6/WYJdEnWfK4r+jGbyD8TSggXQ==} + + stacktrace-js@2.0.2: + resolution: {integrity: sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg==} + + stacktrace-parser@0.1.11: + resolution: {integrity: sha512-WjlahMgHmCJpqzU8bIBy4qtsZdU9lRlcZE3Lvyej6t4tuOuv1vk57OW3MBrj6hXBFx/nNoC9MPMTcr5YA7NQbg==} + engines: {node: '>=6'} + + standard-as-callback@2.1.0: + resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==} + + state-local@1.0.7: + resolution: {integrity: sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==} + + static-eval@2.0.2: + resolution: {integrity: sha512-N/D219Hcr2bPjLxPiV+TQE++Tsmrady7TqAJugLy7Xk1EumfDWS/f5dtBbkRCGE7wKKXuYockQoj8Rm2/pVKyg==} + + statuses@1.5.0: + resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==} + engines: {node: '>= 0.6'} + + statuses@2.0.1: + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + + stdin-discarder@0.2.2: + resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==} + engines: {node: '>=18'} + + stop-iteration-iterator@1.0.0: + resolution: {integrity: sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==} + engines: {node: '>= 0.4'} + + stopwatch-node@1.1.0: + resolution: {integrity: sha512-TpRTztFjiq/B5wn84oKK0YHHtprgrC8pLOUkqVU+MN+2DgdBMaEIeSyGeAn2n+7mT9zrfJyhM33Yw4j/rRV2Bg==} + + stream-chain@2.2.5: + resolution: {integrity: sha512-1TJmBx6aSWqZ4tx7aTpBDXK0/e2hhcNSTV8+CbFJtDjbb+I1mZ8lHit0Grw9GRT+6JbIrrDd8esncgBi8aBXGA==} + + stream-events@1.0.5: + resolution: {integrity: sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==} + + stream-json@1.9.0: + resolution: {integrity: sha512-TqnfW7hRTKje7UobBzXZJ2qOEDJvdcSVgVIK/fopC03xINFuFqQs8RVjyDT4ry7TmOo2ueAXwpXXXG4tNgtvoQ==} + + stream-shift@1.0.3: + resolution: {integrity: sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==} + + string-convert@0.2.1: + resolution: {integrity: sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==} + + string-hash@1.1.3: + resolution: {integrity: sha512-kJUvRUFK49aub+a7T1nNE66EJbZBMnBgoC1UbCZ5n6bsZKBRga4KgBRTMn/pFkeCZSYtNeSyMxPDM0AXWELk2A==} + + string-length@4.0.2: + resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} + engines: {node: '>=10'} + + string-length@5.0.1: + resolution: {integrity: sha512-9Ep08KAMUn0OadnVaBuRdE2l615CQ508kr0XMadjClfYpdCyvrbFp6Taebo8yyxokQ4viUd/xPPUA4FGgUa0ow==} + engines: {node: '>=12.20'} + + string-natural-compare@3.0.1: + resolution: {integrity: sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + string-width@7.2.0: + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + engines: {node: '>=18'} + + string.prototype.includes@2.0.1: + resolution: {integrity: sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==} + engines: {node: '>= 0.4'} + + string.prototype.matchall@4.0.11: + resolution: {integrity: sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==} + engines: {node: '>= 0.4'} + + string.prototype.repeat@1.0.0: + resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==} + + string.prototype.trim@1.2.9: + resolution: {integrity: sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==} + engines: {node: '>= 0.4'} + + string.prototype.trimend@1.0.8: + resolution: {integrity: sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==} + + string.prototype.trimstart@1.0.8: + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} + engines: {node: '>= 0.4'} + + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + stringify-object@3.3.0: + resolution: {integrity: sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==} + engines: {node: '>=4'} + + strip-ansi@3.0.1: + resolution: {integrity: sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==} + engines: {node: '>=0.10.0'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + + strip-bom-buf@1.0.0: + resolution: {integrity: sha512-1sUIL1jck0T1mhOLP2c696BIznzT525Lkub+n4jjMHjhjhoAQA6Ye659DxdlZBr0aLDMQoTxKIpnlqxgtwjsuQ==} + engines: {node: '>=4'} + + strip-bom-stream@2.0.0: + resolution: {integrity: sha512-yH0+mD8oahBZWnY43vxs4pSinn8SMKAdml/EOGBewoe1Y0Eitd0h2Mg3ZRiXruUW6L4P+lvZiEgbh0NgUGia1w==} + engines: {node: '>=0.10.0'} + + strip-bom@2.0.0: + resolution: {integrity: sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g==} + engines: {node: '>=0.10.0'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-bom@4.0.0: + resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} + engines: {node: '>=8'} + + strip-comments@2.0.1: + resolution: {integrity: sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==} + engines: {node: '>=10'} + + strip-final-newline@2.0.0: + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} + + strip-indent@3.0.0: + resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} + engines: {node: '>=8'} + + strip-json-comments@2.0.1: + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + stripe@11.18.0: + resolution: {integrity: sha512-OUA32uhNoSoM6wOodyFbV+3IBCoO140uzdXmBArQ0S88D4EbH91xl2v+Ml1sKalcFKUBadHLeHfU/p9AbsOfGw==} + engines: {node: '>=12.*'} + + strnum@1.0.5: + resolution: {integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==} + + stubs@3.0.0: + resolution: {integrity: sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==} + + style-inject@0.3.0: + resolution: {integrity: sha512-IezA2qp+vcdlhJaVm5SOdPPTUu0FCEqfNSli2vRuSIBbu5Nq5UvygTk/VzeCqfLz2Atj3dVII5QBKGZRZ0edzw==} + + style-loader@3.3.4: + resolution: {integrity: sha512-0WqXzrsMTyb8yjZJHDqwmnwRJvhALK9LfRtRc6B4UTWe8AijYLZYZ9thuJTZc2VfQWINADW/j+LiJnfy2RoC1w==} + engines: {node: '>= 12.13.0'} + peerDependencies: + webpack: ^5.0.0 + + styled-jsx@5.1.6: + resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@babel/core': '*' + babel-plugin-macros: '*' + react: '>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0' + peerDependenciesMeta: + '@babel/core': + optional: true + babel-plugin-macros: + optional: true + + stylehacks@5.1.1: + resolution: {integrity: sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.4.47 + + stylehacks@7.0.4: + resolution: {integrity: sha512-i4zfNrGMt9SB4xRK9L83rlsFCgdGANfeDAYacO1pkqcE7cRHPdWHwnKZVz7WY17Veq/FvyYsRAU++Ga+qDFIww==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.47 + + stylis@4.3.4: + resolution: {integrity: sha512-osIBl6BGUmSfDkyH2mB7EFvCJntXDrLhKjHTRj/rK6xLH0yuPrHULDRQzKokSOD4VoorhtKpfcfW1GAntu8now==} + + sucrase@3.35.0: + resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + + supports-color@2.0.0: + resolution: {integrity: sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==} + engines: {node: '>=0.8.0'} + + supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + + supports-hyperlinks@2.3.0: + resolution: {integrity: sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==} + engines: {node: '>=8'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + svg-parser@2.0.4: + resolution: {integrity: sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==} + + svgo@1.3.2: + resolution: {integrity: sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==} + engines: {node: '>=4.0.0'} + deprecated: This SVGO version is no longer supported. Upgrade to v2.x.x. + hasBin: true + + svgo@2.8.0: + resolution: {integrity: sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==} + engines: {node: '>=10.13.0'} + hasBin: true + + svgo@3.3.2: + resolution: {integrity: sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==} + engines: {node: '>=14.0.0'} + hasBin: true + + symbol-tree@3.2.4: + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + + tailwind-merge@3.2.0: + resolution: {integrity: sha512-FQT/OVqCD+7edmmJpsgCsY820RTD5AkBryuG5IUqR5YQZSdj5xlH5nLgH7YPths7WsLPSpSBNneJdM8aS8aeFA==} + + tailwindcss@3.4.0: + resolution: {integrity: sha512-VigzymniH77knD1dryXbyxR+ePHihHociZbXnLZHUyzf2MMs2ZVqlUrZ3FvpXP8pno9JzmILt1sZPD19M3IxtA==} + engines: {node: '>=14.0.0'} + hasBin: true + + tailwindcss@3.4.14: + resolution: {integrity: sha512-IcSvOcTRcUtQQ7ILQL5quRDg7Xs93PdJEk1ZLbhhvJc7uj/OAhYOnruEiwnGgBvUtaUAJ8/mhSw1o8L2jCiENA==} + engines: {node: '>=14.0.0'} + hasBin: true + + tapable@1.1.3: + resolution: {integrity: sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==} + engines: {node: '>=6'} + + tapable@2.2.1: + resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} + engines: {node: '>=6'} + + tar-fs@2.0.1: + resolution: {integrity: sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA==} + + tar-fs@2.1.1: + resolution: {integrity: sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==} + + tar-stream@2.2.0: + resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} + engines: {node: '>=6'} + + tar@6.2.1: + resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} + engines: {node: '>=10'} + + tar@7.4.3: + resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==} + engines: {node: '>=18'} + + tdigest@0.1.2: + resolution: {integrity: sha512-+G0LLgjjo9BZX2MfdvPfH+MKLCrxlXSYec5DaPYP1fe6Iyhf0/fSmJ0bFiZ1F8BT6cGXl2LpltQptzjXKWEkKA==} + + teeny-request@9.0.0: + resolution: {integrity: sha512-resvxdc6Mgb7YEThw6G6bExlXKkv6+YbuzGg9xuXxSgxJF7Ozs+o8Y9+2R3sArdWdW8nOokoQb1yrpFB0pQK2g==} + engines: {node: '>=14'} + + temp-dir@2.0.0: + resolution: {integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==} + engines: {node: '>=8'} + + tempy@0.6.0: + resolution: {integrity: sha512-G13vtMYPT/J8A4X2SjdtBTphZlrp1gKv6hZiOjw14RCWg6GbHuQBGtjlx75xLbYV/wEc0D7G5K4rxKP/cXk8Bw==} + engines: {node: '>=10'} + + terminal-link@2.1.1: + resolution: {integrity: sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==} + engines: {node: '>=8'} + + terser-webpack-plugin@5.3.10: + resolution: {integrity: sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==} + engines: {node: '>= 10.13.0'} + peerDependencies: + '@swc/core': '*' + esbuild: '*' + uglify-js: '*' + webpack: ^5.1.0 + peerDependenciesMeta: + '@swc/core': + optional: true + esbuild: + optional: true + uglify-js: + optional: true + + terser-webpack-plugin@5.3.14: + resolution: {integrity: sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==} + engines: {node: '>= 10.13.0'} + peerDependencies: + '@swc/core': '*' + esbuild: '*' + uglify-js: '*' + webpack: ^5.1.0 + peerDependenciesMeta: + '@swc/core': + optional: true + esbuild: + optional: true + uglify-js: + optional: true + + terser@5.36.0: + resolution: {integrity: sha512-IYV9eNMuFAV4THUspIRXkLakHnV6XO7FEdtKjf/mDyrnqUg9LnlOn6/RwRvM9SZjR4GUq8Nk8zj67FzVARr74w==} + engines: {node: '>=10'} + hasBin: true + + test-exclude@6.0.0: + resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} + engines: {node: '>=8'} + + testcontainers@9.12.0: + resolution: {integrity: sha512-zmjLTAUqCiDvhDq7TCwcyhI3m/cXXKGnhyLLJ9pgh53VgG9O+P+opX1pIx28aYTUQ7Yu6b5sJf0xoIuxoiclWg==} + engines: {node: '>= 10.16'} + + text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + + textextensions@5.16.0: + resolution: {integrity: sha512-7D/r3s6uPZyU//MCYrX6I14nzauDwJ5CxazouuRGNuvSCihW87ufN6VLoROLCrHg6FblLuJrT6N2BVaPVzqElw==} + engines: {node: '>=0.8'} + + thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + + thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + + throat@6.0.2: + resolution: {integrity: sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ==} + + throttle-debounce@3.0.1: + resolution: {integrity: sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg==} + engines: {node: '>=10'} + + throttle-debounce@5.0.2: + resolution: {integrity: sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==} + engines: {node: '>=12.22'} + + through@2.3.8: + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + + thunky@1.1.0: + resolution: {integrity: sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==} + + timezones-list@3.0.3: + resolution: {integrity: sha512-C+Vdvvj2c1xB6pu81pOX8geo6mrk/QsudFVlTVQET7QQwu8WAIyhDNeCrK5grU7EMzmbKLWqz7uU6dN8fvQvPQ==} + + timsort@0.3.0: + resolution: {integrity: sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A==} + + tiny-glob@0.2.9: + resolution: {integrity: sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==} + + tiny-invariant@1.3.3: + resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + + tiny-lru@11.2.11: + resolution: {integrity: sha512-27BIW0dIWTYYoWNnqSmoNMKe5WIbkXsc0xaCQHd3/3xT2XMuMJrzHdrO9QBFR14emBz1Bu0dOAs2sCBBrvgPQA==} + engines: {node: '>=12'} + + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + + tmp@0.0.33: + resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} + engines: {node: '>=0.6.0'} + + tmp@0.2.3: + resolution: {integrity: sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==} + engines: {node: '>=14.14'} + + tmpl@1.0.5: + resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + toggle-selection@1.0.6: + resolution: {integrity: sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==} + + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + + totalist@3.0.1: + resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} + engines: {node: '>=6'} + + touch@3.1.1: + resolution: {integrity: sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==} + hasBin: true + + tough-cookie@4.1.4: + resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==} + engines: {node: '>=6'} + + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + tr46@1.0.1: + resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} + + tr46@2.1.0: + resolution: {integrity: sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==} + engines: {node: '>=8'} + + tr46@4.1.1: + resolution: {integrity: sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==} + engines: {node: '>=14'} + + tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + hasBin: true + + treeverse@1.0.4: + resolution: {integrity: sha512-whw60l7r+8ZU8Tu/Uc2yxtc4ZTZbR/PF3u1IPNKGQ6p8EICLb3Z2lAgoqw9bqYd8IkgnsaOcLzYHFckjqNsf0g==} + + tryer@1.0.1: + resolution: {integrity: sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==} + + ts-api-utils@1.3.0: + resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==} + engines: {node: '>=16'} + peerDependencies: + typescript: '>=4.2.0' + + ts-easing@0.2.0: + resolution: {integrity: sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ==} + + ts-interface-checker@0.1.13: + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + + ts-jest@29.0.5: + resolution: {integrity: sha512-PL3UciSgIpQ7f6XjVOmbi96vmDHUqAyqDr8YxzopDqX3kfgYtX1cuNeBjP+L9sFXi6nzsGGA6R3fP3DDDJyrxA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + '@babel/core': '>=7.0.0-beta.0 <8' + '@jest/types': ^29.0.0 + babel-jest: ^29.0.0 + esbuild: '*' + jest: ^29.0.0 + typescript: '>=4.3' + peerDependenciesMeta: + '@babel/core': + optional: true + '@jest/types': + optional: true + babel-jest: + optional: true + esbuild: + optional: true + + ts-jest@29.2.5: + resolution: {integrity: sha512-KD8zB2aAZrcKIdGk4OwpJggeLcH1FgrICqDSROWqlnJXGCXK4Mn6FcdK2B6670Xr73lHMG1kHw8R87A0ecZ+vA==} + engines: {node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@babel/core': '>=7.0.0-beta.0 <8' + '@jest/transform': ^29.0.0 + '@jest/types': ^29.0.0 + babel-jest: ^29.0.0 + esbuild: '*' + jest: ^29.0.0 + typescript: '>=4.3 <6' + peerDependenciesMeta: + '@babel/core': + optional: true + '@jest/transform': + optional: true + '@jest/types': + optional: true + babel-jest: + optional: true + esbuild: + optional: true + + ts-loader@9.5.1: + resolution: {integrity: sha512-rNH3sK9kGZcH9dYzC7CewQm4NtxJTjSEVRJ2DyBZR7f8/wcta+iV44UPCXc5+nzDzivKtlzV6c9P4e+oFhDLYg==} + engines: {node: '>=12.0.0'} + peerDependencies: + typescript: '*' + webpack: ^5.0.0 + + ts-morph@13.0.3: + resolution: {integrity: sha512-pSOfUMx8Ld/WUreoSzvMFQG5i9uEiWIsBYjpU9+TTASOeUa89j5HykomeqVULm1oqWtBdleI3KEFRLrlA3zGIw==} + + ts-node-dev@2.0.0: + resolution: {integrity: sha512-ywMrhCfH6M75yftYvrvNarLEY+SUXtUvU8/0Z6llrHQVBx12GiFk5sStF8UdfE/yfzk9IAq7O5EEbTQsxlBI8w==} + engines: {node: '>=0.8.0'} + hasBin: true + peerDependencies: + node-notifier: '*' + typescript: '*' + peerDependenciesMeta: + node-notifier: + optional: true + + ts-node@10.9.2: + resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + + ts-toolbelt@9.6.0: + resolution: {integrity: sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w==} + + tsconfig-paths@3.15.0: + resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + + tsconfig-paths@4.2.0: + resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} + engines: {node: '>=6'} + + tsconfig@7.0.0: + resolution: {integrity: sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==} + + tslib@1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + + tslib@2.8.0: + resolution: {integrity: sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==} + + tsutils@3.21.0: + resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} + engines: {node: '>= 6'} + peerDependencies: + typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' + + tuf-js@1.1.7: + resolution: {integrity: sha512-i3P9Kgw3ytjELUfpuKVDNBJvk4u5bXL6gskv572mcevPbSKCV3zt3djhmlEQ65yERjIbOSncy7U4cQJaB1CBCg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + tunnel-agent@0.6.0: + resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} + + turbo-darwin-64@1.2.16: + resolution: {integrity: sha512-dyitLQJdH3uLVdlH9jAkP4LqEO/K+wOXjUqOzjTciRLjQPzmsNY60/bmFHODADK4eBBl1nxbtn7tmmoT4vS1qA==} + cpu: [x64] + os: [darwin] + + turbo-darwin-arm64@1.2.16: + resolution: {integrity: sha512-Ex6uM4HU7rGXdhvJMpzNpp6qxglJ98nWeIi5qR/lBXHLjK3UCvSW8BEALArUJYJTXS9FZBq1a5LowFqXYsfDcA==} + cpu: [arm64] + os: [darwin] + + turbo-freebsd-64@1.2.16: + resolution: {integrity: sha512-onRGKMvog8B3XDssSBIAg+FrEq9pcBoAybP7bpi/uYIH1L/WQ7YMmLn88X9JX19ehYuVOVZrjap4jWH2GIkU8A==} + cpu: [x64] + os: [freebsd] + + turbo-freebsd-arm64@1.2.16: + resolution: {integrity: sha512-S0EqPqxwnJuVNNXRgcHB0r8ai8LSrpHdihVJKRM7WYmIR7isccBEf/G9agrt73sCXwjvenxFs4HDR7cSvGt14Q==} + cpu: [arm64] + os: [freebsd] + + turbo-linux-32@1.2.16: + resolution: {integrity: sha512-ecbqmGOxgTWePGrowtwyvZGfvwaLxFWmPK21cU0PS+fzoZBaVmzYmniTdd/2EkGCw7TOPhtiT22v96fWcnRycA==} + cpu: [ia32] + os: [linux] + + turbo-linux-64@1.2.16: + resolution: {integrity: sha512-q6gtdMWCzM0Sktkd73zcaQjNoeM1MjtrbwQBctWN/Sgj0eiPBPnzpIvokvx98x7RLf4qyI99/mlme0Dn5fx21A==} + cpu: [x64] + os: [linux] + + turbo-linux-arm64@1.2.16: + resolution: {integrity: sha512-gUf67tYJ/N09WAZTTmtUWYrqm381tZxiulnRGAIM+iRsaTrweyUKZaYXwJvlPpI/cQOw25wCG9/IyvxLeagL8A==} + cpu: [arm64] + os: [linux] + + turbo-linux-arm@1.2.16: + resolution: {integrity: sha512-du7uvExELNb89V3g7iM0XP21fR1Yl3EoHRcOfQz32oUqnS7idCKvbEowM9LtiluQl1dKcOIJjn1nlvvsqzkhOg==} + cpu: [arm] + os: [linux] + + turbo-linux-mips64le@1.2.16: + resolution: {integrity: sha512-U5BM+Ql3z13uRtwMmKH/8eL+9DdTgyijC2gaX4xP0RTlcN7WfAstg8Fg/Tn2Vw9vtpVDdxwpw7dvX4kw2ghhpA==} + cpu: [mips64el] + os: [linux] + + turbo-linux-ppc64le@1.2.16: + resolution: {integrity: sha512-HQWSCmVZyc5chw7Ie2ZcfZPfmM06mbEEu0Wl11Y5QWh1ZzhPNQHs/TsF4I9r146wHi62XgcrKFjkw4ARZiWsLA==} + cpu: [ppc64] + os: [linux] + + turbo-windows-32@1.2.16: + resolution: {integrity: sha512-0ZtPz5FK2qZjznMG4vvRyaabrhO8BgbN+tBx1wjXSuoICTAjYi5TwRVVRh59c3x7qQmR21Cv33CrhLBPRfeAlg==} + cpu: [ia32] + os: [win32] + + turbo-windows-64@1.2.16: + resolution: {integrity: sha512-j8iAIixq/rGfBpHNbYOosxMasZrGuMzLILEuQGDxZgKNpYgobJ15QFHQlGR9sit1b8qPU5zZX4CtByRtkgH1Bw==} + cpu: [x64] + os: [win32] + + turbo-windows-arm64@1.2.16: + resolution: {integrity: sha512-4GpcJG3B8R9WDhwfT8fu6ZmOOfseCg6Q1cy/G8/zpJQk769yYcSnD8MgQhYgHB58aVFxZcMxBvLL6UA0UrpgWA==} + cpu: [arm64] + os: [win32] + + turbo@1.2.16: + resolution: {integrity: sha512-PPUa2COKgFkyb6N3uF9AnIY3l9FZkF15QQ3U1K2wpI01D3gyGKQO0Q3DUQ4ipmciP0teBfL7H+l/QTrUA9IVvQ==} + hasBin: true + + tweetnacl@0.14.5: + resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==} + + type-check@0.3.2: + resolution: {integrity: sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==} + engines: {node: '>= 0.8.0'} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + type-detect@4.0.8: + resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} + engines: {node: '>=4'} + + type-fest@0.16.0: + resolution: {integrity: sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==} + engines: {node: '>=10'} + + type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + + type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + + type-fest@0.6.0: + resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==} + engines: {node: '>=8'} + + type-fest@0.7.1: + resolution: {integrity: sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==} + engines: {node: '>=8'} + + type-fest@0.8.1: + resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} + engines: {node: '>=8'} + + type-fest@2.19.0: + resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} + engines: {node: '>=12.20'} + + type-fest@3.13.1: + resolution: {integrity: sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==} + engines: {node: '>=14.16'} + + type-is@1.6.18: + resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} + engines: {node: '>= 0.6'} + + typed-array-buffer@1.0.2: + resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==} + engines: {node: '>= 0.4'} + + typed-array-byte-length@1.0.1: + resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==} + engines: {node: '>= 0.4'} + + typed-array-byte-offset@1.0.2: + resolution: {integrity: sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==} + engines: {node: '>= 0.4'} + + typed-array-length@1.0.6: + resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==} + engines: {node: '>= 0.4'} + + typedarray-to-buffer@3.1.5: + resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} + + typescript@4.9.5: + resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} + engines: {node: '>=4.2.0'} + hasBin: true + + typescript@5.6.3: + resolution: {integrity: sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==} + engines: {node: '>=14.17'} + hasBin: true + + ua-parser-js@1.0.39: + resolution: {integrity: sha512-k24RCVWlEcjkdOxYmVJgeD/0a1TiSpqLg+ZalVGV9lsnr4yqu0w7tX/x2xX6G4zpkgQnRf89lxuZ1wsbjXM8lw==} + hasBin: true + + unbox-primitive@1.0.2: + resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} + + undefsafe@2.0.5: + resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==} + + underscore@1.12.1: + resolution: {integrity: sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw==} + + undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + + undici-types@6.20.0: + resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + undici@6.21.2: + resolution: {integrity: sha512-uROZWze0R0itiAKVPsYhFov9LxrPMHLMEQFszeI2gCN6bnIIZ8twzBCJcN2LJrBBLfrP0t1FW0g+JmKVl8Vk1g==} + engines: {node: '>=18.17'} + + undici@7.11.0: + resolution: {integrity: sha512-heTSIac3iLhsmZhUCjyS3JQEkZELateufzZuBaVM5RHXdSBMb1LPMQf5x+FH7qjsZYDP0ttAc3nnVpUB+wYbOg==} + engines: {node: '>=20.18.1'} + + unfetch@3.1.2: + resolution: {integrity: sha512-L0qrK7ZeAudGiKYw6nzFjnJ2D5WHblUBwmHIqtPS6oKUd+Hcpk7/hKsSmcHsTlpd1TbTNsiRBUKRq3bHLNIqIw==} + + unfetch@4.2.0: + resolution: {integrity: sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==} + + unicode-canonical-property-names-ecmascript@2.0.1: + resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==} + engines: {node: '>=4'} + + unicode-match-property-ecmascript@2.0.0: + resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==} + engines: {node: '>=4'} + + unicode-match-property-value-ecmascript@2.2.0: + resolution: {integrity: sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==} + engines: {node: '>=4'} + + unicode-property-aliases-ecmascript@2.1.0: + resolution: {integrity: sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==} + engines: {node: '>=4'} + + unicorn-magic@0.1.0: + resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} + engines: {node: '>=18'} + + unique-filename@1.1.1: + resolution: {integrity: sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==} + + unique-filename@2.0.1: + resolution: {integrity: sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + unique-filename@3.0.0: + resolution: {integrity: sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + unique-slug@2.0.2: + resolution: {integrity: sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==} + + unique-slug@3.0.0: + resolution: {integrity: sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + unique-slug@4.0.0: + resolution: {integrity: sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + unique-string@2.0.0: + resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==} + engines: {node: '>=8'} + + universal-user-agent@6.0.1: + resolution: {integrity: sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==} + + universalify@0.2.0: + resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} + engines: {node: '>= 4.0.0'} + + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + + unquote@1.1.1: + resolution: {integrity: sha512-vRCqFv6UhXpWxZPyGDh/F3ZpNv8/qo7w6iufLpQg9aKnQ71qM4B5KiI7Mia9COcjEhrO9LueHpMYjYzsWH3OIg==} + + untildify@4.0.0: + resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==} + engines: {node: '>=8'} + + upath@1.2.0: + resolution: {integrity: sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==} + engines: {node: '>=4'} + + update-browserslist-db@1.1.1: + resolution: {integrity: sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + update-browserslist-db@1.1.3: + resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + url-parse@1.5.10: + resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} + + url-template@2.0.8: + resolution: {integrity: sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw==} + + use-callback-ref@1.3.3: + resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + use-composed-ref@1.3.0: + resolution: {integrity: sha512-GLMG0Jc/jiKov/3Ulid1wbv3r54K9HlMW29IWcDFPEqFkSO2nS0MuefWgMJpeHQ9YJeXDL3ZUF+P3jdXlZX/cQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + + use-debounce@10.0.4: + resolution: {integrity: sha512-6Cf7Yr7Wk7Kdv77nnJMf6de4HuDE4dTxKij+RqE9rufDsI6zsbjyAxcH5y2ueJCQAnfgKbzXbZHYlkFwmBlWkw==} + engines: {node: '>= 16.0.0'} + peerDependencies: + react: '*' + + use-debounce@10.0.5: + resolution: {integrity: sha512-Q76E3lnIV+4YT9AHcrHEHYmAd9LKwUAbPXDm7FlqVGDHiSOhX3RDjT8dm0AxbJup6WgOb1YEcKyCr11kBJR5KQ==} + engines: {node: '>= 16.0.0'} + peerDependencies: + react: '*' + + use-isomorphic-layout-effect@1.1.2: + resolution: {integrity: sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + use-latest@1.2.1: + resolution: {integrity: sha512-xA+AVm/Wlg3e2P/JiItTziwS7FK92LWrDB0p+hgXloIMuVCeJJ8v6f0eeHyPZaJrM+usM1FkFfbNCrJGs8A/zw==} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + use-sidecar@1.1.3: + resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + use-sync-external-store@1.2.2: + resolution: {integrity: sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + util.promisify@1.0.1: + resolution: {integrity: sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==} + + utila@0.4.0: + resolution: {integrity: sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==} + + utils-merge@1.0.1: + resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} + engines: {node: '>= 0.4.0'} + + uuid@11.1.0: + resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==} + hasBin: true + + uuid@8.3.2: + resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + hasBin: true + + uuid@9.0.1: + resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} + hasBin: true + + v8-compile-cache-lib@3.0.1: + resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + + v8-to-istanbul@8.1.1: + resolution: {integrity: sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w==} + engines: {node: '>=10.12.0'} + + v8-to-istanbul@9.3.0: + resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} + engines: {node: '>=10.12.0'} + + valid-data-url@3.0.1: + resolution: {integrity: sha512-jOWVmzVceKlVVdwjNSenT4PbGghU0SBIizAev8ofZVgivk/TVHXSbNL8LP6M3spZvkR9/QolkyJavGSX5Cs0UA==} + engines: {node: '>=10'} + + validate-npm-package-license@3.0.4: + resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} + + validate-npm-package-name@3.0.0: + resolution: {integrity: sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw==} + + validate-npm-package-name@5.0.1: + resolution: {integrity: sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + validate.io-array@1.0.6: + resolution: {integrity: sha512-DeOy7CnPEziggrOO5CZhVKJw6S3Yi7e9e65R1Nl/RTN1vTQKnzjfvks0/8kQ40FP/dsjRAOd4hxmJ7uLa6vxkg==} + + validate.io-function@1.0.2: + resolution: {integrity: sha512-LlFybRJEriSuBnUhQyG5bwglhh50EpTL2ul23MPIuR1odjO7XaMLFV8vHGwp7AZciFxtYOeiSCT5st+XSPONiQ==} + + validate.io-integer-array@1.0.0: + resolution: {integrity: sha512-mTrMk/1ytQHtCY0oNO3dztafHYyGU88KL+jRxWuzfOmQb+4qqnWmI+gykvGp8usKZOM0H7keJHEbRaFiYA0VrA==} + + validate.io-integer@1.0.5: + resolution: {integrity: sha512-22izsYSLojN/P6bppBqhgUDjCkr5RY2jd+N2a3DCAUey8ydvrZ/OkGvFPR7qfOpwR2LC5p4Ngzxz36g5Vgr/hQ==} + + validate.io-number@1.0.3: + resolution: {integrity: sha512-kRAyotcbNaSYoDnXvb4MHg/0a1egJdLwS6oJ38TJY7aw9n93Fl/3blIXdyYvPOp55CNxywooG/3BcrwNrBpcSg==} + + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + + verror@1.10.0: + resolution: {integrity: sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==} + engines: {'0': node >=0.6.0} + + victory-vendor@36.9.2: + resolution: {integrity: sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==} + + vinyl-file@3.0.0: + resolution: {integrity: sha512-BoJDj+ca3D9xOuPEM6RWVtWQtvEPQiQYn82LvdxhLWplfQsBzBqtgK0yhCP0s1BNTi6dH9BO+dzybvyQIacifg==} + engines: {node: '>=4'} + + vinyl@2.2.1: + resolution: {integrity: sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw==} + engines: {node: '>= 0.10'} + + w3c-hr-time@1.0.2: + resolution: {integrity: sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==} + deprecated: Use your platform's native performance.now() and performance.timeOrigin. + + w3c-xmlserializer@2.0.0: + resolution: {integrity: sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==} + engines: {node: '>=10'} + + walk-up-path@1.0.0: + resolution: {integrity: sha512-hwj/qMDUEjCU5h0xr90KGCf0tg0/LgJbmOWgrWKYlcJZM7XvquvUJZ0G/HMGr7F7OQMOUuPHWP9JpriinkAlkg==} + + walker@1.0.8: + resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} + + watchpack@2.4.2: + resolution: {integrity: sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==} + engines: {node: '>=10.13.0'} + + wbuf@1.7.3: + resolution: {integrity: sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==} + + wcwidth@1.0.1: + resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + + web-resource-inliner@7.0.0: + resolution: {integrity: sha512-NlfnGF8MY9ZUwFjyq3vOUBx7KwF8bmE+ywR781SB0nWB6MoMxN4BA8gtgP1KGTZo/O/AyWJz7HZpR704eaj4mg==} + engines: {node: '>=10.0.0'} + + web-streams-polyfill@3.3.3: + resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} + engines: {node: '>= 8'} + + web-vitals@2.1.4: + resolution: {integrity: sha512-sVWcwhU5mX6crfI5Vd2dC4qchyTqxV8URinzt25XqVh+bHEPGH4C3NPrNionCP7Obx59wrYEbNlw4Z8sjALzZg==} + + web-vitals@4.2.4: + resolution: {integrity: sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==} + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + webidl-conversions@4.0.2: + resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} + + webidl-conversions@5.0.0: + resolution: {integrity: sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==} + engines: {node: '>=8'} + + webidl-conversions@6.1.0: + resolution: {integrity: sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==} + engines: {node: '>=10.4'} + + webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + + webpack-bundle-analyzer@4.10.1: + resolution: {integrity: sha512-s3P7pgexgT/HTUSYgxJyn28A+99mmLq4HsJepMPzu0R8ImJc52QNqaFYW1Z2z2uIb1/J3eYgaAWVpaC+v/1aAQ==} + engines: {node: '>= 10.13.0'} + hasBin: true + + webpack-cli@5.1.4: + resolution: {integrity: sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==} + engines: {node: '>=14.15.0'} + hasBin: true + peerDependencies: + '@webpack-cli/generators': '*' + webpack: 5.x.x + webpack-bundle-analyzer: '*' + webpack-dev-server: '*' + peerDependenciesMeta: + '@webpack-cli/generators': + optional: true + webpack-bundle-analyzer: + optional: true + webpack-dev-server: + optional: true + + webpack-cli@6.0.1: + resolution: {integrity: sha512-MfwFQ6SfwinsUVi0rNJm7rHZ31GyTcpVE5pgVA3hwFRb7COD4TzjUUwhGWKfO50+xdc2MQPuEBBJoqIMGt3JDw==} + engines: {node: '>=18.12.0'} + hasBin: true + peerDependencies: + webpack: ^5.82.0 + webpack-bundle-analyzer: '*' + webpack-dev-server: '*' + peerDependenciesMeta: + webpack-bundle-analyzer: + optional: true + webpack-dev-server: + optional: true + + webpack-dev-middleware@5.3.4: + resolution: {integrity: sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==} + engines: {node: '>= 12.13.0'} + peerDependencies: + webpack: ^4.0.0 || ^5.0.0 + + webpack-dev-server@4.15.2: + resolution: {integrity: sha512-0XavAZbNJ5sDrCbkpWL8mia0o5WPOd2YGtxrEiZkBK9FjLppIUK2TgxK6qGD2P3hUXTJNNPVibrerKcx5WkR1g==} + engines: {node: '>= 12.13.0'} + hasBin: true + peerDependencies: + webpack: ^4.37.0 || ^5.0.0 + webpack-cli: '*' + peerDependenciesMeta: + webpack: + optional: true + webpack-cli: + optional: true + + webpack-hot-middleware@2.26.1: + resolution: {integrity: sha512-khZGfAeJx6I8K9zKohEWWYN6KDlVw2DHownoe+6Vtwj1LP9WFgegXnVMSkZ/dBEBtXFwrkkydsaPFlB7f8wU2A==} + + webpack-manifest-plugin@4.1.1: + resolution: {integrity: sha512-YXUAwxtfKIJIKkhg03MKuiFAD72PlrqCiwdwO4VEXdRO5V0ORCNwaOwAZawPZalCbmH9kBDmXnNeQOw+BIEiow==} + engines: {node: '>=12.22.0'} + peerDependencies: + webpack: ^4.44.2 || ^5.47.0 + + webpack-merge@5.10.0: + resolution: {integrity: sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==} + engines: {node: '>=10.0.0'} + + webpack-merge@6.0.1: + resolution: {integrity: sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==} + engines: {node: '>=18.0.0'} + + webpack-sources@1.4.3: + resolution: {integrity: sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==} + + webpack-sources@2.3.1: + resolution: {integrity: sha512-y9EI9AO42JjEcrTJFOYmVywVZdKVUfOvDUPsJea5GIr1JOEGFVqwlY2K098fFoIjOkDzHn2AjRvM8dsBZu+gCA==} + engines: {node: '>=10.13.0'} + + webpack-sources@3.2.3: + resolution: {integrity: sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==} + engines: {node: '>=10.13.0'} + + webpack@5.95.0: + resolution: {integrity: sha512-2t3XstrKULz41MNMBF+cJ97TyHdyQ8HCt//pqErqDvNjU9YQBnZxIHa11VXsi7F3mb5/aO2tuDxdeTPdU7xu9Q==} + engines: {node: '>=10.13.0'} + hasBin: true + peerDependencies: + webpack-cli: '*' + peerDependenciesMeta: + webpack-cli: + optional: true + + webpack@5.99.5: + resolution: {integrity: sha512-q+vHBa6H9qwBLUlHL4Y7L0L1/LlyBKZtS9FHNCQmtayxjI5RKC9yD8gpvLeqGv5lCQp1Re04yi0MF40pf30Pvg==} + engines: {node: '>=10.13.0'} + hasBin: true + peerDependencies: + webpack-cli: '*' + peerDependenciesMeta: + webpack-cli: + optional: true + + websocket-driver@0.7.4: + resolution: {integrity: sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==} + engines: {node: '>=0.8.0'} + + websocket-extensions@0.1.4: + resolution: {integrity: sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==} + engines: {node: '>=0.8.0'} + + whatwg-encoding@1.0.5: + resolution: {integrity: sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==} + + whatwg-encoding@3.1.1: + resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} + engines: {node: '>=18'} + + whatwg-fetch@3.6.20: + resolution: {integrity: sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==} + + whatwg-mimetype@2.3.0: + resolution: {integrity: sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==} + + whatwg-mimetype@4.0.0: + resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} + engines: {node: '>=18'} + + whatwg-url@13.0.0: + resolution: {integrity: sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==} + engines: {node: '>=16'} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + + whatwg-url@7.1.0: + resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} + + whatwg-url@8.7.0: + resolution: {integrity: sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==} + engines: {node: '>=10'} + + which-boxed-primitive@1.0.2: + resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + + which-builtin-type@1.1.4: + resolution: {integrity: sha512-bppkmBSsHFmIMSl8BO9TbsyzsvGjVoppt8xUiGzwiu/bhDCGxnpOKCxgqj6GuyHE0mINMDecBFPlOm2hzY084w==} + engines: {node: '>= 0.4'} + + which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} + + which-pm@2.2.0: + resolution: {integrity: sha512-MOiaDbA5ZZgUjkeMWM5EkJp4loW5ZRoa5bc3/aeMox/PJelMhE6t7S/mLuiY43DBupyxH+S0U1bTui9kWUlmsw==} + engines: {node: '>=8.15'} + + which-typed-array@1.1.15: + resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==} + engines: {node: '>= 0.4'} + + which@1.3.1: + resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} + hasBin: true + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + which@3.0.1: + resolution: {integrity: sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + hasBin: true + + wide-align@1.1.5: + resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} + + widest-line@4.0.1: + resolution: {integrity: sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==} + engines: {node: '>=12'} + + wildcard@2.0.1: + resolution: {integrity: sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==} + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + workbox-background-sync@6.6.0: + resolution: {integrity: sha512-jkf4ZdgOJxC9u2vztxLuPT/UjlH7m/nWRQ/MgGL0v8BJHoZdVGJd18Kck+a0e55wGXdqyHO+4IQTk0685g4MUw==} + + workbox-broadcast-update@6.6.0: + resolution: {integrity: sha512-nm+v6QmrIFaB/yokJmQ/93qIJ7n72NICxIwQwe5xsZiV2aI93MGGyEyzOzDPVz5THEr5rC3FJSsO3346cId64Q==} + + workbox-build@6.6.0: + resolution: {integrity: sha512-Tjf+gBwOTuGyZwMz2Nk/B13Fuyeo0Q84W++bebbVsfr9iLkDSo6j6PST8tET9HYA58mlRXwlMGpyWO8ETJiXdQ==} + engines: {node: '>=10.0.0'} + + workbox-cacheable-response@6.6.0: + resolution: {integrity: sha512-JfhJUSQDwsF1Xv3EV1vWzSsCOZn4mQ38bWEBR3LdvOxSPgB65gAM6cS2CX8rkkKHRgiLrN7Wxoyu+TuH67kHrw==} + deprecated: workbox-background-sync@6.6.0 + + workbox-core@6.6.0: + resolution: {integrity: sha512-GDtFRF7Yg3DD859PMbPAYPeJyg5gJYXuBQAC+wyrWuuXgpfoOrIQIvFRZnQ7+czTIQjIr1DhLEGFzZanAT/3bQ==} + + workbox-expiration@6.6.0: + resolution: {integrity: sha512-baplYXcDHbe8vAo7GYvyAmlS4f6998Jff513L4XvlzAOxcl8F620O91guoJ5EOf5qeXG4cGdNZHkkVAPouFCpw==} + + workbox-google-analytics@6.6.0: + resolution: {integrity: sha512-p4DJa6OldXWd6M9zRl0H6vB9lkrmqYFkRQ2xEiNdBFp9U0LhsGO7hsBscVEyH9H2/3eZZt8c97NB2FD9U2NJ+Q==} + deprecated: It is not compatible with newer versions of GA starting with v4, as long as you are using GAv3 it should be ok, but the package is not longer being maintained + + workbox-navigation-preload@6.6.0: + resolution: {integrity: sha512-utNEWG+uOfXdaZmvhshrh7KzhDu/1iMHyQOV6Aqup8Mm78D286ugu5k9MFD9SzBT5TcwgwSORVvInaXWbvKz9Q==} + + workbox-precaching@6.6.0: + resolution: {integrity: sha512-eYu/7MqtRZN1IDttl/UQcSZFkHP7dnvr/X3Vn6Iw6OsPMruQHiVjjomDFCNtd8k2RdjLs0xiz9nq+t3YVBcWPw==} + + workbox-range-requests@6.6.0: + resolution: {integrity: sha512-V3aICz5fLGq5DpSYEU8LxeXvsT//mRWzKrfBOIxzIdQnV/Wj7R+LyJVTczi4CQ4NwKhAaBVaSujI1cEjXW+hTw==} + + workbox-recipes@6.6.0: + resolution: {integrity: sha512-TFi3kTgYw73t5tg73yPVqQC8QQjxJSeqjXRO4ouE/CeypmP2O/xqmB/ZFBBQazLTPxILUQ0b8aeh0IuxVn9a6A==} + + workbox-routing@6.6.0: + resolution: {integrity: sha512-x8gdN7VDBiLC03izAZRfU+WKUXJnbqt6PG9Uh0XuPRzJPpZGLKce/FkOX95dWHRpOHWLEq8RXzjW0O+POSkKvw==} + + workbox-strategies@6.6.0: + resolution: {integrity: sha512-eC07XGuINAKUWDnZeIPdRdVja4JQtTuc35TZ8SwMb1ztjp7Ddq2CJ4yqLvWzFWGlYI7CG/YGqaETntTxBGdKgQ==} + + workbox-streams@6.6.0: + resolution: {integrity: sha512-rfMJLVvwuED09CnH1RnIep7L9+mj4ufkTyDPVaXPKlhi9+0czCu+SJggWCIFbPpJaAZmp2iyVGLqS3RUmY3fxg==} + + workbox-sw@6.6.0: + resolution: {integrity: sha512-R2IkwDokbtHUE4Kus8pKO5+VkPHD2oqTgl+XJwh4zbF1HyjAbgNmK/FneZHVU7p03XUt9ICfuGDYISWG9qV/CQ==} + + workbox-webpack-plugin@6.6.0: + resolution: {integrity: sha512-xNZIZHalboZU66Wa7x1YkjIqEy1gTR+zPM+kjrYJzqN7iurYZBctBLISyScjhkJKYuRrZUP0iqViZTh8rS0+3A==} + engines: {node: '>=10.0.0'} + peerDependencies: + webpack: ^4.4.0 || ^5.9.0 + + workbox-window@6.6.0: + resolution: {integrity: sha512-L4N9+vka17d16geaJXXRjENLFldvkWy7JyGxElRD0JvBxvFEd8LOhr+uXCcar/NzAmIBRv9EZ+M+Qr4mOoBITw==} + + wrap-ansi@6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + write-file-atomic@3.0.3: + resolution: {integrity: sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==} + + write-file-atomic@4.0.2: + resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + ws@7.5.10: + resolution: {integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==} + engines: {node: '>=8.3.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + ws@8.17.1: + resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + ws@8.18.0: + resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + xml-name-validator@3.0.0: + resolution: {integrity: sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==} + + xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + + xmlhttprequest-ssl@2.1.2: + resolution: {integrity: sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==} + engines: {node: '>=0.4.0'} + + xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + + yallist@5.0.0: + resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} + engines: {node: '>=18'} + + yaml@1.10.2: + resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} + engines: {node: '>= 6'} + + yaml@2.6.0: + resolution: {integrity: sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ==} + engines: {node: '>= 14'} + hasBin: true + + yargs-parser@20.2.9: + resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} + engines: {node: '>=10'} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@16.2.0: + resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} + engines: {node: '>=10'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + yeoman-environment@3.19.3: + resolution: {integrity: sha512-/+ODrTUHtlDPRH9qIC0JREH8+7nsRcjDl3Bxn2Xo/rvAaVvixH5275jHwg0C85g4QsF4P6M2ojfScPPAl+pLAg==} + engines: {node: '>=12.10.0'} + hasBin: true + + yeoman-generator@5.10.0: + resolution: {integrity: sha512-iDUKykV7L4nDNzeYSedRmSeJ5eMYFucnKDi6KN1WNASXErgPepKqsQw55TgXPHnmpcyOh2Dd/LAZkyc+f0qaAw==} + engines: {node: '>=12.10.0'} + peerDependencies: + yeoman-environment: ^3.2.0 + peerDependenciesMeta: + yeoman-environment: + optional: true + + yn@3.1.1: + resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} + engines: {node: '>=6'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + yocto-queue@1.1.1: + resolution: {integrity: sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==} + engines: {node: '>=12.20'} + + yoctocolors-cjs@2.1.2: + resolution: {integrity: sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==} + engines: {node: '>=18'} + + yoctocolors@2.1.1: + resolution: {integrity: sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==} + engines: {node: '>=18'} + + zip-stream@4.1.1: + resolution: {integrity: sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==} + engines: {node: '>= 10'} + + zod-prisma@0.5.4: + resolution: {integrity: sha512-5Ca4Qd1a1jy1T/NqCEpbr0c+EsbjJfJ/7euEHob3zDvtUK2rTuD1Rc/vfzH8q8PtaR2TZbysD88NHmrLwpv3Xg==} + engines: {node: '>=14'} + hasBin: true + peerDependencies: + decimal.js: ^10.0.0 + prisma: ^3.0.0 + zod: ^3.0.0 + peerDependenciesMeta: + decimal.js: + optional: true + + zod-to-json-schema@3.23.5: + resolution: {integrity: sha512-5wlSS0bXfF/BrL4jPAbz9da5hDlDptdEppYfe+x4eIJ7jioqKG9uUxOwPzqof09u/XeVdrgFu29lZi+8XNDJtA==} + peerDependencies: + zod: ^3.23.3 + + zod@3.23.8: + resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} + + zod@3.24.3: + resolution: {integrity: sha512-HhY1oqzWCQWuUqvBFnsyrtZRhyPeR7SUGv+C4+MsisMuVfSPx8HpwWqH8tRahSlt6M3PiFAcoeFhZAqIXTxoSg==} + +snapshots: + + '@adobe/css-tools@4.4.0': {} + + '@alloc/quick-lru@5.2.0': {} + + '@amplitude/ua-parser-js@0.7.33': {} + + '@ampproject/remapping@2.3.0': + dependencies: + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + + '@analytics/cookie-utils@0.2.12': + dependencies: + '@analytics/global-storage-utils': 0.1.7 + + '@analytics/core@0.12.15(@types/dlv@1.1.4)': + dependencies: + '@analytics/global-storage-utils': 0.1.7 + '@analytics/type-utils': 0.6.2 + analytics-utils: 1.0.12(@types/dlv@1.1.4) + transitivePeerDependencies: + - '@types/dlv' + + '@analytics/global-storage-utils@0.1.7': + dependencies: + '@analytics/type-utils': 0.6.2 + + '@analytics/localstorage-utils@0.1.10': + dependencies: + '@analytics/global-storage-utils': 0.1.7 + + '@analytics/session-storage-utils@0.0.7': + dependencies: + '@analytics/global-storage-utils': 0.1.7 + + '@analytics/storage-utils@0.4.2': + dependencies: + '@analytics/cookie-utils': 0.2.12 + '@analytics/global-storage-utils': 0.1.7 + '@analytics/localstorage-utils': 0.1.10 + '@analytics/session-storage-utils': 0.0.7 + '@analytics/type-utils': 0.6.2 + + '@analytics/type-utils@0.6.2': {} + + '@ant-design/colors@7.1.0': + dependencies: + '@ctrl/tinycolor': 3.6.1 + + '@ant-design/cssinjs-utils@1.1.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@ant-design/cssinjs': 1.21.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@babel/runtime': 7.26.0 + rc-util: 5.43.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@ant-design/cssinjs@1.21.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.26.0 + '@emotion/hash': 0.8.0 + '@emotion/unitless': 0.7.5 + classnames: 2.5.1 + csstype: 3.1.3 + rc-util: 5.43.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + stylis: 4.3.4 + + '@ant-design/fast-color@2.0.6': + dependencies: + '@babel/runtime': 7.26.0 + + '@ant-design/icons-svg@4.4.2': {} + + '@ant-design/icons@5.5.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@ant-design/colors': 7.1.0 + '@ant-design/icons-svg': 4.4.2 + '@babel/runtime': 7.26.0 + classnames: 2.5.1 + rc-util: 5.43.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@ant-design/react-slick@1.1.2(react@18.3.1)': + dependencies: + '@babel/runtime': 7.26.0 + classnames: 2.5.1 + json2mq: 0.2.0 + react: 18.3.1 + resize-observer-polyfill: 1.5.1 + throttle-debounce: 5.0.2 + + '@apideck/better-ajv-errors@0.3.6(ajv@8.17.1)': + dependencies: + ajv: 8.17.1 + json-schema: 0.4.0 + jsonpointer: 5.0.1 + leven: 3.1.0 + + '@aws-crypto/crc32@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.679.0 + tslib: 2.8.0 + + '@aws-crypto/crc32c@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.679.0 + tslib: 2.8.0 + + '@aws-crypto/sha1-browser@5.2.0': + dependencies: + '@aws-crypto/supports-web-crypto': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.679.0 + '@aws-sdk/util-locate-window': 3.679.0 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.0 + + '@aws-crypto/sha256-browser@5.2.0': + dependencies: + '@aws-crypto/sha256-js': 5.2.0 + '@aws-crypto/supports-web-crypto': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.679.0 + '@aws-sdk/util-locate-window': 3.679.0 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.0 + + '@aws-crypto/sha256-js@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.679.0 + tslib: 2.8.0 + + '@aws-crypto/supports-web-crypto@5.2.0': + dependencies: + tslib: 2.8.0 + + '@aws-crypto/util@5.2.0': + dependencies: + '@aws-sdk/types': 3.679.0 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.0 + + '@aws-sdk/client-s3@3.682.0': + dependencies: + '@aws-crypto/sha1-browser': 5.2.0 + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/client-sso-oidc': 3.682.0(@aws-sdk/client-sts@3.682.0) + '@aws-sdk/client-sts': 3.682.0 + '@aws-sdk/core': 3.679.0 + '@aws-sdk/credential-provider-node': 3.682.0(@aws-sdk/client-sso-oidc@3.682.0(@aws-sdk/client-sts@3.682.0))(@aws-sdk/client-sts@3.682.0) + '@aws-sdk/middleware-bucket-endpoint': 3.679.0 + '@aws-sdk/middleware-expect-continue': 3.679.0 + '@aws-sdk/middleware-flexible-checksums': 3.682.0 + '@aws-sdk/middleware-host-header': 3.679.0 + '@aws-sdk/middleware-location-constraint': 3.679.0 + '@aws-sdk/middleware-logger': 3.679.0 + '@aws-sdk/middleware-recursion-detection': 3.679.0 + '@aws-sdk/middleware-sdk-s3': 3.682.0 + '@aws-sdk/middleware-ssec': 3.679.0 + '@aws-sdk/middleware-user-agent': 3.682.0 + '@aws-sdk/region-config-resolver': 3.679.0 + '@aws-sdk/signature-v4-multi-region': 3.682.0 + '@aws-sdk/types': 3.679.0 + '@aws-sdk/util-endpoints': 3.679.0 + '@aws-sdk/util-user-agent-browser': 3.679.0 + '@aws-sdk/util-user-agent-node': 3.682.0 + '@aws-sdk/xml-builder': 3.679.0 + '@smithy/config-resolver': 3.0.10 + '@smithy/core': 2.5.1 + '@smithy/eventstream-serde-browser': 3.0.11 + '@smithy/eventstream-serde-config-resolver': 3.0.8 + '@smithy/eventstream-serde-node': 3.0.10 + '@smithy/fetch-http-handler': 3.2.9 + '@smithy/hash-blob-browser': 3.1.7 + '@smithy/hash-node': 3.0.8 + '@smithy/hash-stream-node': 3.1.7 + '@smithy/invalid-dependency': 3.0.8 + '@smithy/md5-js': 3.0.8 + '@smithy/middleware-content-length': 3.0.10 + '@smithy/middleware-endpoint': 3.2.1 + '@smithy/middleware-retry': 3.0.25 + '@smithy/middleware-serde': 3.0.8 + '@smithy/middleware-stack': 3.0.8 + '@smithy/node-config-provider': 3.1.9 + '@smithy/node-http-handler': 3.2.5 + '@smithy/protocol-http': 4.1.5 + '@smithy/smithy-client': 3.4.2 + '@smithy/types': 3.6.0 + '@smithy/url-parser': 3.0.8 + '@smithy/util-base64': 3.0.0 + '@smithy/util-body-length-browser': 3.0.0 + '@smithy/util-body-length-node': 3.0.0 + '@smithy/util-defaults-mode-browser': 3.0.25 + '@smithy/util-defaults-mode-node': 3.0.25 + '@smithy/util-endpoints': 2.1.4 + '@smithy/util-middleware': 3.0.8 + '@smithy/util-retry': 3.0.8 + '@smithy/util-stream': 3.2.1 + '@smithy/util-utf8': 3.0.0 + '@smithy/util-waiter': 3.1.7 + tslib: 2.8.0 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/client-sso-oidc@3.682.0(@aws-sdk/client-sts@3.682.0)': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/client-sts': 3.682.0 + '@aws-sdk/core': 3.679.0 + '@aws-sdk/credential-provider-node': 3.682.0(@aws-sdk/client-sso-oidc@3.682.0(@aws-sdk/client-sts@3.682.0))(@aws-sdk/client-sts@3.682.0) + '@aws-sdk/middleware-host-header': 3.679.0 + '@aws-sdk/middleware-logger': 3.679.0 + '@aws-sdk/middleware-recursion-detection': 3.679.0 + '@aws-sdk/middleware-user-agent': 3.682.0 + '@aws-sdk/region-config-resolver': 3.679.0 + '@aws-sdk/types': 3.679.0 + '@aws-sdk/util-endpoints': 3.679.0 + '@aws-sdk/util-user-agent-browser': 3.679.0 + '@aws-sdk/util-user-agent-node': 3.682.0 + '@smithy/config-resolver': 3.0.10 + '@smithy/core': 2.5.1 + '@smithy/fetch-http-handler': 3.2.9 + '@smithy/hash-node': 3.0.8 + '@smithy/invalid-dependency': 3.0.8 + '@smithy/middleware-content-length': 3.0.10 + '@smithy/middleware-endpoint': 3.2.1 + '@smithy/middleware-retry': 3.0.25 + '@smithy/middleware-serde': 3.0.8 + '@smithy/middleware-stack': 3.0.8 + '@smithy/node-config-provider': 3.1.9 + '@smithy/node-http-handler': 3.2.5 + '@smithy/protocol-http': 4.1.5 + '@smithy/smithy-client': 3.4.2 + '@smithy/types': 3.6.0 + '@smithy/url-parser': 3.0.8 + '@smithy/util-base64': 3.0.0 + '@smithy/util-body-length-browser': 3.0.0 + '@smithy/util-body-length-node': 3.0.0 + '@smithy/util-defaults-mode-browser': 3.0.25 + '@smithy/util-defaults-mode-node': 3.0.25 + '@smithy/util-endpoints': 2.1.4 + '@smithy/util-middleware': 3.0.8 + '@smithy/util-retry': 3.0.8 + '@smithy/util-utf8': 3.0.0 + tslib: 2.8.0 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/client-sso@3.682.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.679.0 + '@aws-sdk/middleware-host-header': 3.679.0 + '@aws-sdk/middleware-logger': 3.679.0 + '@aws-sdk/middleware-recursion-detection': 3.679.0 + '@aws-sdk/middleware-user-agent': 3.682.0 + '@aws-sdk/region-config-resolver': 3.679.0 + '@aws-sdk/types': 3.679.0 + '@aws-sdk/util-endpoints': 3.679.0 + '@aws-sdk/util-user-agent-browser': 3.679.0 + '@aws-sdk/util-user-agent-node': 3.682.0 + '@smithy/config-resolver': 3.0.10 + '@smithy/core': 2.5.1 + '@smithy/fetch-http-handler': 3.2.9 + '@smithy/hash-node': 3.0.8 + '@smithy/invalid-dependency': 3.0.8 + '@smithy/middleware-content-length': 3.0.10 + '@smithy/middleware-endpoint': 3.2.1 + '@smithy/middleware-retry': 3.0.25 + '@smithy/middleware-serde': 3.0.8 + '@smithy/middleware-stack': 3.0.8 + '@smithy/node-config-provider': 3.1.9 + '@smithy/node-http-handler': 3.2.5 + '@smithy/protocol-http': 4.1.5 + '@smithy/smithy-client': 3.4.2 + '@smithy/types': 3.6.0 + '@smithy/url-parser': 3.0.8 + '@smithy/util-base64': 3.0.0 + '@smithy/util-body-length-browser': 3.0.0 + '@smithy/util-body-length-node': 3.0.0 + '@smithy/util-defaults-mode-browser': 3.0.25 + '@smithy/util-defaults-mode-node': 3.0.25 + '@smithy/util-endpoints': 2.1.4 + '@smithy/util-middleware': 3.0.8 + '@smithy/util-retry': 3.0.8 + '@smithy/util-utf8': 3.0.0 + tslib: 2.8.0 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/client-sts@3.682.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/client-sso-oidc': 3.682.0(@aws-sdk/client-sts@3.682.0) + '@aws-sdk/core': 3.679.0 + '@aws-sdk/credential-provider-node': 3.682.0(@aws-sdk/client-sso-oidc@3.682.0(@aws-sdk/client-sts@3.682.0))(@aws-sdk/client-sts@3.682.0) + '@aws-sdk/middleware-host-header': 3.679.0 + '@aws-sdk/middleware-logger': 3.679.0 + '@aws-sdk/middleware-recursion-detection': 3.679.0 + '@aws-sdk/middleware-user-agent': 3.682.0 + '@aws-sdk/region-config-resolver': 3.679.0 + '@aws-sdk/types': 3.679.0 + '@aws-sdk/util-endpoints': 3.679.0 + '@aws-sdk/util-user-agent-browser': 3.679.0 + '@aws-sdk/util-user-agent-node': 3.682.0 + '@smithy/config-resolver': 3.0.10 + '@smithy/core': 2.5.1 + '@smithy/fetch-http-handler': 3.2.9 + '@smithy/hash-node': 3.0.8 + '@smithy/invalid-dependency': 3.0.8 + '@smithy/middleware-content-length': 3.0.10 + '@smithy/middleware-endpoint': 3.2.1 + '@smithy/middleware-retry': 3.0.25 + '@smithy/middleware-serde': 3.0.8 + '@smithy/middleware-stack': 3.0.8 + '@smithy/node-config-provider': 3.1.9 + '@smithy/node-http-handler': 3.2.5 + '@smithy/protocol-http': 4.1.5 + '@smithy/smithy-client': 3.4.2 + '@smithy/types': 3.6.0 + '@smithy/url-parser': 3.0.8 + '@smithy/util-base64': 3.0.0 + '@smithy/util-body-length-browser': 3.0.0 + '@smithy/util-body-length-node': 3.0.0 + '@smithy/util-defaults-mode-browser': 3.0.25 + '@smithy/util-defaults-mode-node': 3.0.25 + '@smithy/util-endpoints': 2.1.4 + '@smithy/util-middleware': 3.0.8 + '@smithy/util-retry': 3.0.8 + '@smithy/util-utf8': 3.0.0 + tslib: 2.8.0 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/core@3.679.0': + dependencies: + '@aws-sdk/types': 3.679.0 + '@smithy/core': 2.5.1 + '@smithy/node-config-provider': 3.1.9 + '@smithy/property-provider': 3.1.8 + '@smithy/protocol-http': 4.1.5 + '@smithy/signature-v4': 4.2.1 + '@smithy/smithy-client': 3.4.2 + '@smithy/types': 3.6.0 + '@smithy/util-middleware': 3.0.8 + fast-xml-parser: 4.4.1 + tslib: 2.8.0 + + '@aws-sdk/credential-provider-env@3.679.0': + dependencies: + '@aws-sdk/core': 3.679.0 + '@aws-sdk/types': 3.679.0 + '@smithy/property-provider': 3.1.8 + '@smithy/types': 3.6.0 + tslib: 2.8.0 + + '@aws-sdk/credential-provider-http@3.679.0': + dependencies: + '@aws-sdk/core': 3.679.0 + '@aws-sdk/types': 3.679.0 + '@smithy/fetch-http-handler': 3.2.9 + '@smithy/node-http-handler': 3.2.5 + '@smithy/property-provider': 3.1.8 + '@smithy/protocol-http': 4.1.5 + '@smithy/smithy-client': 3.4.2 + '@smithy/types': 3.6.0 + '@smithy/util-stream': 3.2.1 + tslib: 2.8.0 + + '@aws-sdk/credential-provider-ini@3.682.0(@aws-sdk/client-sso-oidc@3.682.0(@aws-sdk/client-sts@3.682.0))(@aws-sdk/client-sts@3.682.0)': + dependencies: + '@aws-sdk/client-sts': 3.682.0 + '@aws-sdk/core': 3.679.0 + '@aws-sdk/credential-provider-env': 3.679.0 + '@aws-sdk/credential-provider-http': 3.679.0 + '@aws-sdk/credential-provider-process': 3.679.0 + '@aws-sdk/credential-provider-sso': 3.682.0(@aws-sdk/client-sso-oidc@3.682.0(@aws-sdk/client-sts@3.682.0)) + '@aws-sdk/credential-provider-web-identity': 3.679.0(@aws-sdk/client-sts@3.682.0) + '@aws-sdk/types': 3.679.0 + '@smithy/credential-provider-imds': 3.2.5 + '@smithy/property-provider': 3.1.8 + '@smithy/shared-ini-file-loader': 3.1.9 + '@smithy/types': 3.6.0 + tslib: 2.8.0 + transitivePeerDependencies: + - '@aws-sdk/client-sso-oidc' + - aws-crt + + '@aws-sdk/credential-provider-node@3.682.0(@aws-sdk/client-sso-oidc@3.682.0(@aws-sdk/client-sts@3.682.0))(@aws-sdk/client-sts@3.682.0)': + dependencies: + '@aws-sdk/credential-provider-env': 3.679.0 + '@aws-sdk/credential-provider-http': 3.679.0 + '@aws-sdk/credential-provider-ini': 3.682.0(@aws-sdk/client-sso-oidc@3.682.0(@aws-sdk/client-sts@3.682.0))(@aws-sdk/client-sts@3.682.0) + '@aws-sdk/credential-provider-process': 3.679.0 + '@aws-sdk/credential-provider-sso': 3.682.0(@aws-sdk/client-sso-oidc@3.682.0(@aws-sdk/client-sts@3.682.0)) + '@aws-sdk/credential-provider-web-identity': 3.679.0(@aws-sdk/client-sts@3.682.0) + '@aws-sdk/types': 3.679.0 + '@smithy/credential-provider-imds': 3.2.5 + '@smithy/property-provider': 3.1.8 + '@smithy/shared-ini-file-loader': 3.1.9 + '@smithy/types': 3.6.0 + tslib: 2.8.0 + transitivePeerDependencies: + - '@aws-sdk/client-sso-oidc' + - '@aws-sdk/client-sts' + - aws-crt + + '@aws-sdk/credential-provider-process@3.679.0': + dependencies: + '@aws-sdk/core': 3.679.0 + '@aws-sdk/types': 3.679.0 + '@smithy/property-provider': 3.1.8 + '@smithy/shared-ini-file-loader': 3.1.9 + '@smithy/types': 3.6.0 + tslib: 2.8.0 + + '@aws-sdk/credential-provider-sso@3.682.0(@aws-sdk/client-sso-oidc@3.682.0(@aws-sdk/client-sts@3.682.0))': + dependencies: + '@aws-sdk/client-sso': 3.682.0 + '@aws-sdk/core': 3.679.0 + '@aws-sdk/token-providers': 3.679.0(@aws-sdk/client-sso-oidc@3.682.0(@aws-sdk/client-sts@3.682.0)) + '@aws-sdk/types': 3.679.0 + '@smithy/property-provider': 3.1.8 + '@smithy/shared-ini-file-loader': 3.1.9 + '@smithy/types': 3.6.0 + tslib: 2.8.0 + transitivePeerDependencies: + - '@aws-sdk/client-sso-oidc' + - aws-crt + + '@aws-sdk/credential-provider-web-identity@3.679.0(@aws-sdk/client-sts@3.682.0)': + dependencies: + '@aws-sdk/client-sts': 3.682.0 + '@aws-sdk/core': 3.679.0 + '@aws-sdk/types': 3.679.0 + '@smithy/property-provider': 3.1.8 + '@smithy/types': 3.6.0 + tslib: 2.8.0 + + '@aws-sdk/middleware-bucket-endpoint@3.679.0': + dependencies: + '@aws-sdk/types': 3.679.0 + '@aws-sdk/util-arn-parser': 3.679.0 + '@smithy/node-config-provider': 3.1.9 + '@smithy/protocol-http': 4.1.5 + '@smithy/types': 3.6.0 + '@smithy/util-config-provider': 3.0.0 + tslib: 2.8.0 + + '@aws-sdk/middleware-expect-continue@3.679.0': + dependencies: + '@aws-sdk/types': 3.679.0 + '@smithy/protocol-http': 4.1.5 + '@smithy/types': 3.6.0 + tslib: 2.8.0 + + '@aws-sdk/middleware-flexible-checksums@3.682.0': + dependencies: + '@aws-crypto/crc32': 5.2.0 + '@aws-crypto/crc32c': 5.2.0 + '@aws-sdk/core': 3.679.0 + '@aws-sdk/types': 3.679.0 + '@smithy/is-array-buffer': 3.0.0 + '@smithy/node-config-provider': 3.1.9 + '@smithy/protocol-http': 4.1.5 + '@smithy/types': 3.6.0 + '@smithy/util-middleware': 3.0.8 + '@smithy/util-utf8': 3.0.0 + tslib: 2.8.0 + + '@aws-sdk/middleware-host-header@3.679.0': + dependencies: + '@aws-sdk/types': 3.679.0 + '@smithy/protocol-http': 4.1.5 + '@smithy/types': 3.6.0 + tslib: 2.8.0 + + '@aws-sdk/middleware-location-constraint@3.679.0': + dependencies: + '@aws-sdk/types': 3.679.0 + '@smithy/types': 3.6.0 + tslib: 2.8.0 + + '@aws-sdk/middleware-logger@3.679.0': + dependencies: + '@aws-sdk/types': 3.679.0 + '@smithy/types': 3.6.0 + tslib: 2.8.0 + + '@aws-sdk/middleware-recursion-detection@3.679.0': + dependencies: + '@aws-sdk/types': 3.679.0 + '@smithy/protocol-http': 4.1.5 + '@smithy/types': 3.6.0 + tslib: 2.8.0 + + '@aws-sdk/middleware-sdk-s3@3.682.0': + dependencies: + '@aws-sdk/core': 3.679.0 + '@aws-sdk/types': 3.679.0 + '@aws-sdk/util-arn-parser': 3.679.0 + '@smithy/core': 2.5.1 + '@smithy/node-config-provider': 3.1.9 + '@smithy/protocol-http': 4.1.5 + '@smithy/signature-v4': 4.2.1 + '@smithy/smithy-client': 3.4.2 + '@smithy/types': 3.6.0 + '@smithy/util-config-provider': 3.0.0 + '@smithy/util-middleware': 3.0.8 + '@smithy/util-stream': 3.2.1 + '@smithy/util-utf8': 3.0.0 + tslib: 2.8.0 + + '@aws-sdk/middleware-ssec@3.679.0': + dependencies: + '@aws-sdk/types': 3.679.0 + '@smithy/types': 3.6.0 + tslib: 2.8.0 + + '@aws-sdk/middleware-user-agent@3.682.0': + dependencies: + '@aws-sdk/core': 3.679.0 + '@aws-sdk/types': 3.679.0 + '@aws-sdk/util-endpoints': 3.679.0 + '@smithy/core': 2.5.1 + '@smithy/protocol-http': 4.1.5 + '@smithy/types': 3.6.0 + tslib: 2.8.0 + + '@aws-sdk/region-config-resolver@3.679.0': + dependencies: + '@aws-sdk/types': 3.679.0 + '@smithy/node-config-provider': 3.1.9 + '@smithy/types': 3.6.0 + '@smithy/util-config-provider': 3.0.0 + '@smithy/util-middleware': 3.0.8 + tslib: 2.8.0 + + '@aws-sdk/signature-v4-multi-region@3.682.0': + dependencies: + '@aws-sdk/middleware-sdk-s3': 3.682.0 + '@aws-sdk/types': 3.679.0 + '@smithy/protocol-http': 4.1.5 + '@smithy/signature-v4': 4.2.1 + '@smithy/types': 3.6.0 + tslib: 2.8.0 + + '@aws-sdk/token-providers@3.679.0(@aws-sdk/client-sso-oidc@3.682.0(@aws-sdk/client-sts@3.682.0))': + dependencies: + '@aws-sdk/client-sso-oidc': 3.682.0(@aws-sdk/client-sts@3.682.0) + '@aws-sdk/types': 3.679.0 + '@smithy/property-provider': 3.1.8 + '@smithy/shared-ini-file-loader': 3.1.9 + '@smithy/types': 3.6.0 + tslib: 2.8.0 + + '@aws-sdk/types@3.679.0': + dependencies: + '@smithy/types': 3.6.0 + tslib: 2.8.0 + + '@aws-sdk/util-arn-parser@3.679.0': + dependencies: + tslib: 2.8.0 + + '@aws-sdk/util-endpoints@3.679.0': + dependencies: + '@aws-sdk/types': 3.679.0 + '@smithy/types': 3.6.0 + '@smithy/util-endpoints': 2.1.4 + tslib: 2.8.0 + + '@aws-sdk/util-locate-window@3.679.0': + dependencies: + tslib: 2.8.0 + + '@aws-sdk/util-user-agent-browser@3.679.0': + dependencies: + '@aws-sdk/types': 3.679.0 + '@smithy/types': 3.6.0 + bowser: 2.11.0 + tslib: 2.8.0 + + '@aws-sdk/util-user-agent-node@3.682.0': + dependencies: + '@aws-sdk/middleware-user-agent': 3.682.0 + '@aws-sdk/types': 3.679.0 + '@smithy/node-config-provider': 3.1.9 + '@smithy/types': 3.6.0 + tslib: 2.8.0 + + '@aws-sdk/xml-builder@3.679.0': + dependencies: + '@smithy/types': 3.6.0 + tslib: 2.8.0 + + '@babel/code-frame@7.26.0': + dependencies: + '@babel/helper-validator-identifier': 7.25.9 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/code-frame@7.26.2': + dependencies: + '@babel/helper-validator-identifier': 7.25.9 + js-tokens: 4.0.0 + picocolors: 1.1.1 + optional: true + + '@babel/code-frame@7.27.1': + dependencies: + '@babel/helper-validator-identifier': 7.27.1 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.26.0': {} + + '@babel/compat-data@7.28.0': {} + + '@babel/core@7.26.0': + dependencies: + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.26.0 + '@babel/generator': 7.26.0 + '@babel/helper-compilation-targets': 7.25.9 + '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) + '@babel/helpers': 7.26.0 + '@babel/parser': 7.26.1 + '@babel/template': 7.25.9 + '@babel/traverse': 7.25.9 + '@babel/types': 7.26.0 + convert-source-map: 2.0.0 + debug: 4.3.7(supports-color@5.5.0) + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/core@7.26.10': + dependencies: + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.0 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.10) + '@babel/helpers': 7.27.6 + '@babel/parser': 7.28.0 + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.0 + '@babel/types': 7.28.1 + convert-source-map: 2.0.0 + debug: 4.3.7(supports-color@5.5.0) + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/eslint-parser@7.25.9(@babel/core@7.26.0)(eslint@8.57.1)': + dependencies: + '@babel/core': 7.26.0 + '@nicolo-ribaudo/eslint-scope-5-internals': 5.1.1-v1 + eslint: 8.57.1 + eslint-visitor-keys: 2.1.0 + semver: 6.3.1 + + '@babel/generator@7.26.0': + dependencies: + '@babel/parser': 7.28.0 + '@babel/types': 7.26.0 + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + jsesc: 3.0.2 + + '@babel/generator@7.28.0': + dependencies: + '@babel/parser': 7.28.0 + '@babel/types': 7.28.1 + '@jridgewell/gen-mapping': 0.3.12 + '@jridgewell/trace-mapping': 0.3.29 + jsesc: 3.0.2 + + '@babel/helper-annotate-as-pure@7.25.9': + dependencies: + '@babel/types': 7.26.0 + + '@babel/helper-builder-binary-assignment-operator-visitor@7.25.9': + dependencies: + '@babel/traverse': 7.25.9 + '@babel/types': 7.26.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-compilation-targets@7.25.9': + dependencies: + '@babel/compat-data': 7.26.0 + '@babel/helper-validator-option': 7.25.9 + browserslist: 4.24.2 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-compilation-targets@7.27.2': + dependencies: + '@babel/compat-data': 7.28.0 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.25.1 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-create-class-features-plugin@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-annotate-as-pure': 7.25.9 + '@babel/helper-member-expression-to-functions': 7.25.9 + '@babel/helper-optimise-call-expression': 7.25.9 + '@babel/helper-replace-supers': 7.25.9(@babel/core@7.26.0) + '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 + '@babel/traverse': 7.25.9 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/helper-create-class-features-plugin@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-annotate-as-pure': 7.25.9 + '@babel/helper-member-expression-to-functions': 7.25.9 + '@babel/helper-optimise-call-expression': 7.25.9 + '@babel/helper-replace-supers': 7.25.9(@babel/core@7.26.10) + '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 + '@babel/traverse': 7.25.9 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/helper-create-regexp-features-plugin@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-annotate-as-pure': 7.25.9 + regexpu-core: 6.1.1 + semver: 6.3.1 + + '@babel/helper-create-regexp-features-plugin@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-annotate-as-pure': 7.25.9 + regexpu-core: 6.1.1 + semver: 6.3.1 + + '@babel/helper-define-polyfill-provider@0.6.2(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-compilation-targets': 7.25.9 + '@babel/helper-plugin-utils': 7.25.9 + debug: 4.3.7(supports-color@5.5.0) + lodash.debounce: 4.0.8 + resolve: 1.22.8 + transitivePeerDependencies: + - supports-color + + '@babel/helper-define-polyfill-provider@0.6.2(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-compilation-targets': 7.25.9 + '@babel/helper-plugin-utils': 7.25.9 + debug: 4.3.7(supports-color@5.5.0) + lodash.debounce: 4.0.8 + resolve: 1.22.8 + transitivePeerDependencies: + - supports-color + + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-member-expression-to-functions@7.25.9': + dependencies: + '@babel/traverse': 7.28.0 + '@babel/types': 7.28.1 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-imports@7.25.9': + dependencies: + '@babel/traverse': 7.25.9 + '@babel/types': 7.26.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.26.0(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-module-imports': 7.25.9 + '@babel/helper-validator-identifier': 7.25.9 + '@babel/traverse': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.26.0(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-module-imports': 7.25.9 + '@babel/helper-validator-identifier': 7.25.9 + '@babel/traverse': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/helper-optimise-call-expression@7.25.9': + dependencies: + '@babel/types': 7.28.1 + + '@babel/helper-plugin-utils@7.25.9': {} + + '@babel/helper-remap-async-to-generator@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-annotate-as-pure': 7.25.9 + '@babel/helper-wrap-function': 7.25.9 + '@babel/traverse': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/helper-remap-async-to-generator@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-annotate-as-pure': 7.25.9 + '@babel/helper-wrap-function': 7.25.9 + '@babel/traverse': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/helper-replace-supers@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-member-expression-to-functions': 7.25.9 + '@babel/helper-optimise-call-expression': 7.25.9 + '@babel/traverse': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/helper-replace-supers@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-member-expression-to-functions': 7.25.9 + '@babel/helper-optimise-call-expression': 7.25.9 + '@babel/traverse': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/helper-simple-access@7.25.9': + dependencies: + '@babel/traverse': 7.25.9 + '@babel/types': 7.26.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-skip-transparent-expression-wrappers@7.25.9': + dependencies: + '@babel/traverse': 7.25.9 + '@babel/types': 7.26.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.25.9': {} + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.25.9': {} + + '@babel/helper-validator-identifier@7.27.1': {} + + '@babel/helper-validator-option@7.25.9': {} + + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helper-wrap-function@7.25.9': + dependencies: + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.0 + '@babel/types': 7.28.1 + transitivePeerDependencies: + - supports-color + + '@babel/helpers@7.26.0': + dependencies: + '@babel/template': 7.25.9 + '@babel/types': 7.26.0 + + '@babel/helpers@7.27.6': + dependencies: + '@babel/template': 7.27.2 + '@babel/types': 7.28.1 + + '@babel/parser@7.26.1': + dependencies: + '@babel/types': 7.26.0 + + '@babel/parser@7.28.0': + dependencies: + '@babel/types': 7.28.1 + + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/traverse': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/traverse': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 + '@babel/plugin-transform-optional-chaining': 7.25.9(@babel/core@7.26.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 + '@babel/plugin-transform-optional-chaining': 7.25.9(@babel/core@7.26.10) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/traverse': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/traverse': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-class-properties@7.12.1(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-class-properties@7.18.6(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-decorators@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.25.9 + '@babel/plugin-syntax-decorators': 7.25.9(@babel/core@7.26.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-nullish-coalescing-operator@7.18.6(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.26.0) + + '@babel/plugin-proposal-numeric-separator@7.18.6(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.26.0) + + '@babel/plugin-proposal-optional-chaining@7.21.0(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.26.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-private-methods@7.18.6(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + + '@babel/plugin-proposal-private-property-in-object@7.21.11(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-annotate-as-pure': 7.25.9 + '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.25.9 + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.26.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + optional: true + + '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + optional: true + + '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + optional: true + + '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + optional: true + + '@babel/plugin-syntax-decorators@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-syntax-flow@7.26.0(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-syntax-import-assertions@7.26.0(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-syntax-import-assertions@7.26.0(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-syntax-import-attributes@7.26.0(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-syntax-import-attributes@7.26.0(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + optional: true + + '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + optional: true + + '@babel/plugin-syntax-jsx@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-syntax-jsx@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + optional: true + + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + optional: true + + '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + optional: true + + '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + optional: true + + '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + optional: true + + '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + optional: true + + '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + optional: true + + '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + optional: true + + '@babel/plugin-syntax-typescript@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-syntax-typescript@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-create-regexp-features-plugin': 7.25.9(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-create-regexp-features-plugin': 7.25.9(@babel/core@7.26.10) + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-arrow-functions@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-arrow-functions@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-async-generator-functions@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-remap-async-to-generator': 7.25.9(@babel/core@7.26.0) + '@babel/traverse': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-async-generator-functions@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-remap-async-to-generator': 7.25.9(@babel/core@7.26.10) + '@babel/traverse': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-async-to-generator@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-module-imports': 7.25.9 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-remap-async-to-generator': 7.25.9(@babel/core@7.26.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-async-to-generator@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-module-imports': 7.25.9 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-remap-async-to-generator': 7.25.9(@babel/core@7.26.10) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-block-scoped-functions@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-block-scoped-functions@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-block-scoping@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-block-scoping@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-class-properties@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-class-properties@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.10) + '@babel/helper-plugin-utils': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-class-static-block@7.26.0(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-class-static-block@7.26.0(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.10) + '@babel/helper-plugin-utils': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-classes@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-annotate-as-pure': 7.25.9 + '@babel/helper-compilation-targets': 7.25.9 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-replace-supers': 7.25.9(@babel/core@7.26.0) + '@babel/traverse': 7.25.9 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-classes@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-annotate-as-pure': 7.25.9 + '@babel/helper-compilation-targets': 7.25.9 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-replace-supers': 7.25.9(@babel/core@7.26.10) + '@babel/traverse': 7.25.9 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-computed-properties@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/template': 7.25.9 + + '@babel/plugin-transform-computed-properties@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/template': 7.25.9 + + '@babel/plugin-transform-destructuring@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-destructuring@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-dotall-regex@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-create-regexp-features-plugin': 7.25.9(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-dotall-regex@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-create-regexp-features-plugin': 7.25.9(@babel/core@7.26.10) + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-duplicate-keys@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-duplicate-keys@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-create-regexp-features-plugin': 7.25.9(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-create-regexp-features-plugin': 7.25.9(@babel/core@7.26.10) + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-dynamic-import@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-dynamic-import@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-exponentiation-operator@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-builder-binary-assignment-operator-visitor': 7.25.9 + '@babel/helper-plugin-utils': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-exponentiation-operator@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-builder-binary-assignment-operator-visitor': 7.25.9 + '@babel/helper-plugin-utils': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-export-namespace-from@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-export-namespace-from@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-flow-strip-types@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/plugin-syntax-flow': 7.26.0(@babel/core@7.26.0) + + '@babel/plugin-transform-for-of@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-for-of@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-function-name@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-compilation-targets': 7.25.9 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/traverse': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-function-name@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-compilation-targets': 7.25.9 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/traverse': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-json-strings@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-json-strings@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-literals@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-literals@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-logical-assignment-operators@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-logical-assignment-operators@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-member-expression-literals@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-member-expression-literals@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-modules-amd@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-amd@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.10) + '@babel/helper-plugin-utils': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-commonjs@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-simple-access': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-commonjs@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.10) + '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-simple-access': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-systemjs@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-validator-identifier': 7.25.9 + '@babel/traverse': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-systemjs@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.10) + '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-validator-identifier': 7.25.9 + '@babel/traverse': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-umd@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-umd@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.10) + '@babel/helper-plugin-utils': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-named-capturing-groups-regex@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-create-regexp-features-plugin': 7.25.9(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-named-capturing-groups-regex@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-create-regexp-features-plugin': 7.25.9(@babel/core@7.26.10) + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-new-target@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-new-target@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-nullish-coalescing-operator@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-nullish-coalescing-operator@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-numeric-separator@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-numeric-separator@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-object-rest-spread@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-compilation-targets': 7.25.9 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/plugin-transform-parameters': 7.25.9(@babel/core@7.26.0) + + '@babel/plugin-transform-object-rest-spread@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-compilation-targets': 7.25.9 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/plugin-transform-parameters': 7.25.9(@babel/core@7.26.10) + + '@babel/plugin-transform-object-super@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-replace-supers': 7.25.9(@babel/core@7.26.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-object-super@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-replace-supers': 7.25.9(@babel/core@7.26.10) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-optional-catch-binding@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-optional-catch-binding@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-optional-chaining@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-optional-chaining@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-parameters@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-parameters@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-private-methods@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-private-methods@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.10) + '@babel/helper-plugin-utils': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-private-property-in-object@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-annotate-as-pure': 7.25.9 + '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-private-property-in-object@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-annotate-as-pure': 7.25.9 + '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.10) + '@babel/helper-plugin-utils': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-property-literals@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-property-literals@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-react-constant-elements@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-react-display-name@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-react-display-name@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-react-jsx-development@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/plugin-transform-react-jsx': 7.25.9(@babel/core@7.26.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-react-jsx-development@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/plugin-transform-react-jsx': 7.25.9(@babel/core@7.26.10) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-react-jsx@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-annotate-as-pure': 7.25.9 + '@babel/helper-module-imports': 7.25.9 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/plugin-syntax-jsx': 7.25.9(@babel/core@7.26.0) + '@babel/types': 7.26.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-react-jsx@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-annotate-as-pure': 7.25.9 + '@babel/helper-module-imports': 7.25.9 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/plugin-syntax-jsx': 7.25.9(@babel/core@7.26.10) + '@babel/types': 7.26.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-react-pure-annotations@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-annotate-as-pure': 7.25.9 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-react-pure-annotations@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-annotate-as-pure': 7.25.9 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-regenerator@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + regenerator-transform: 0.15.2 + + '@babel/plugin-transform-regenerator@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + regenerator-transform: 0.15.2 + + '@babel/plugin-transform-regexp-modifiers@7.26.0(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-create-regexp-features-plugin': 7.25.9(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-regexp-modifiers@7.26.0(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-create-regexp-features-plugin': 7.25.9(@babel/core@7.26.10) + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-reserved-words@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-reserved-words@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-runtime@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-module-imports': 7.25.9 + '@babel/helper-plugin-utils': 7.25.9 + babel-plugin-polyfill-corejs2: 0.4.11(@babel/core@7.26.0) + babel-plugin-polyfill-corejs3: 0.10.6(@babel/core@7.26.0) + babel-plugin-polyfill-regenerator: 0.6.2(@babel/core@7.26.0) + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-shorthand-properties@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-shorthand-properties@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-spread@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-spread@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-sticky-regex@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-sticky-regex@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-template-literals@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-template-literals@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-typeof-symbol@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-typeof-symbol@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-typescript@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-annotate-as-pure': 7.25.9 + '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 + '@babel/plugin-syntax-typescript': 7.25.9(@babel/core@7.26.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-typescript@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-annotate-as-pure': 7.25.9 + '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.10) + '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 + '@babel/plugin-syntax-typescript': 7.25.9(@babel/core@7.26.10) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-unicode-escapes@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-unicode-escapes@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-unicode-property-regex@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-create-regexp-features-plugin': 7.25.9(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-unicode-property-regex@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-create-regexp-features-plugin': 7.25.9(@babel/core@7.26.10) + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-unicode-regex@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-create-regexp-features-plugin': 7.25.9(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-unicode-regex@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-create-regexp-features-plugin': 7.25.9(@babel/core@7.26.10) + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-unicode-sets-regex@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-create-regexp-features-plugin': 7.25.9(@babel/core@7.26.0) + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-transform-unicode-sets-regex@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-create-regexp-features-plugin': 7.25.9(@babel/core@7.26.10) + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/preset-env@7.26.0(@babel/core@7.26.0)': + dependencies: + '@babel/compat-data': 7.26.0 + '@babel/core': 7.26.0 + '@babel/helper-compilation-targets': 7.25.9 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-validator-option': 7.25.9 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.26.0) + '@babel/plugin-syntax-import-assertions': 7.26.0(@babel/core@7.26.0) + '@babel/plugin-syntax-import-attributes': 7.26.0(@babel/core@7.26.0) + '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.26.0) + '@babel/plugin-transform-arrow-functions': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-async-generator-functions': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-async-to-generator': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-block-scoped-functions': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-block-scoping': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-class-properties': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-class-static-block': 7.26.0(@babel/core@7.26.0) + '@babel/plugin-transform-classes': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-computed-properties': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-destructuring': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-dotall-regex': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-duplicate-keys': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-dynamic-import': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-exponentiation-operator': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-export-namespace-from': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-for-of': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-function-name': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-json-strings': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-literals': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-logical-assignment-operators': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-member-expression-literals': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-modules-amd': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-modules-commonjs': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-modules-systemjs': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-modules-umd': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-named-capturing-groups-regex': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-new-target': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-nullish-coalescing-operator': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-numeric-separator': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-object-rest-spread': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-object-super': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-optional-catch-binding': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-optional-chaining': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-parameters': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-private-methods': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-private-property-in-object': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-property-literals': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-regenerator': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-regexp-modifiers': 7.26.0(@babel/core@7.26.0) + '@babel/plugin-transform-reserved-words': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-shorthand-properties': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-spread': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-sticky-regex': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-template-literals': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-typeof-symbol': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-unicode-escapes': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-unicode-property-regex': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-unicode-regex': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-unicode-sets-regex': 7.25.9(@babel/core@7.26.0) + '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.26.0) + babel-plugin-polyfill-corejs2: 0.4.11(@babel/core@7.26.0) + babel-plugin-polyfill-corejs3: 0.10.6(@babel/core@7.26.0) + babel-plugin-polyfill-regenerator: 0.6.2(@babel/core@7.26.0) + core-js-compat: 3.38.1 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/preset-env@7.26.0(@babel/core@7.26.10)': + dependencies: + '@babel/compat-data': 7.26.0 + '@babel/core': 7.26.10 + '@babel/helper-compilation-targets': 7.25.9 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-validator-option': 7.25.9 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.26.10) + '@babel/plugin-syntax-import-assertions': 7.26.0(@babel/core@7.26.10) + '@babel/plugin-syntax-import-attributes': 7.26.0(@babel/core@7.26.10) + '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.26.10) + '@babel/plugin-transform-arrow-functions': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-async-generator-functions': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-async-to-generator': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-block-scoped-functions': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-block-scoping': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-class-properties': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-class-static-block': 7.26.0(@babel/core@7.26.10) + '@babel/plugin-transform-classes': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-computed-properties': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-destructuring': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-dotall-regex': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-duplicate-keys': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-dynamic-import': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-exponentiation-operator': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-export-namespace-from': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-for-of': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-function-name': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-json-strings': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-literals': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-logical-assignment-operators': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-member-expression-literals': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-modules-amd': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-modules-commonjs': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-modules-systemjs': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-modules-umd': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-named-capturing-groups-regex': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-new-target': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-nullish-coalescing-operator': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-numeric-separator': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-object-rest-spread': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-object-super': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-optional-catch-binding': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-optional-chaining': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-parameters': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-private-methods': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-private-property-in-object': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-property-literals': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-regenerator': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-regexp-modifiers': 7.26.0(@babel/core@7.26.10) + '@babel/plugin-transform-reserved-words': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-shorthand-properties': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-spread': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-sticky-regex': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-template-literals': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-typeof-symbol': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-unicode-escapes': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-unicode-property-regex': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-unicode-regex': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-unicode-sets-regex': 7.25.9(@babel/core@7.26.10) + '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.26.10) + babel-plugin-polyfill-corejs2: 0.4.11(@babel/core@7.26.10) + babel-plugin-polyfill-corejs3: 0.10.6(@babel/core@7.26.10) + babel-plugin-polyfill-regenerator: 0.6.2(@babel/core@7.26.10) + core-js-compat: 3.38.1 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/preset-flow@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-validator-option': 7.25.9 + '@babel/plugin-transform-flow-strip-types': 7.25.9(@babel/core@7.26.0) + + '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/types': 7.26.0 + esutils: 2.0.3 + + '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/types': 7.26.0 + esutils: 2.0.3 + + '@babel/preset-react@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-validator-option': 7.25.9 + '@babel/plugin-transform-react-display-name': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-react-jsx': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-react-jsx-development': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-react-pure-annotations': 7.25.9(@babel/core@7.26.0) + transitivePeerDependencies: + - supports-color + + '@babel/preset-react@7.25.9(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-validator-option': 7.25.9 + '@babel/plugin-transform-react-display-name': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-react-jsx': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-react-jsx-development': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-react-pure-annotations': 7.25.9(@babel/core@7.26.10) + transitivePeerDependencies: + - supports-color + + '@babel/preset-typescript@7.26.0(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-validator-option': 7.25.9 + '@babel/plugin-syntax-jsx': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-modules-commonjs': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-typescript': 7.25.9(@babel/core@7.26.0) + transitivePeerDependencies: + - supports-color + + '@babel/preset-typescript@7.26.0(@babel/core@7.26.10)': + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-plugin-utils': 7.25.9 + '@babel/helper-validator-option': 7.25.9 + '@babel/plugin-syntax-jsx': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-modules-commonjs': 7.25.9(@babel/core@7.26.10) + '@babel/plugin-transform-typescript': 7.25.9(@babel/core@7.26.10) + transitivePeerDependencies: + - supports-color + + '@babel/runtime@7.26.0': + dependencies: + regenerator-runtime: 0.14.1 + + '@babel/template@7.25.9': + dependencies: + '@babel/code-frame': 7.26.0 + '@babel/parser': 7.26.1 + '@babel/types': 7.26.0 + + '@babel/template@7.27.2': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/parser': 7.28.0 + '@babel/types': 7.28.1 + + '@babel/traverse@7.25.9': + dependencies: + '@babel/code-frame': 7.26.0 + '@babel/generator': 7.26.0 + '@babel/parser': 7.26.1 + '@babel/template': 7.25.9 + '@babel/types': 7.26.0 + debug: 4.3.7(supports-color@5.5.0) + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + + '@babel/traverse@7.28.0': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.0 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.28.0 + '@babel/template': 7.27.2 + '@babel/types': 7.28.1 + debug: 4.3.7(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + + '@babel/types@7.26.0': + dependencies: + '@babel/helper-string-parser': 7.25.9 + '@babel/helper-validator-identifier': 7.25.9 + + '@babel/types@7.28.1': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + + '@balena/dockerignore@1.0.2': {} + + '@bcoe/v8-coverage@0.2.3': {} + + '@clickhouse/client-common@1.10.1': {} + + '@clickhouse/client@1.10.1': + dependencies: + '@clickhouse/client-common': 1.10.1 + + '@confluentinc/kafka-javascript@1.4.1(encoding@0.1.13)': + dependencies: + '@mapbox/node-pre-gyp': 1.0.11(encoding@0.1.13) + bindings: 1.5.0 + nan: 2.22.0 + transitivePeerDependencies: + - encoding + - supports-color + + '@cspotcode/source-map-support@0.8.1': + dependencies: + '@jridgewell/trace-mapping': 0.3.9 + + '@csstools/normalize.css@12.1.1': {} + + '@csstools/postcss-cascade-layers@1.1.1(postcss@8.4.47)': + dependencies: + '@csstools/selector-specificity': 2.2.0(postcss-selector-parser@6.1.2) + postcss: 8.4.47 + postcss-selector-parser: 6.1.2 + + '@csstools/postcss-color-function@1.1.1(postcss@8.4.47)': + dependencies: + '@csstools/postcss-progressive-custom-properties': 1.3.0(postcss@8.4.47) + postcss: 8.4.47 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-font-format-keywords@1.0.1(postcss@8.4.47)': + dependencies: + postcss: 8.4.47 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-hwb-function@1.0.2(postcss@8.4.47)': + dependencies: + postcss: 8.4.47 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-ic-unit@1.0.1(postcss@8.4.47)': + dependencies: + '@csstools/postcss-progressive-custom-properties': 1.3.0(postcss@8.4.47) + postcss: 8.4.47 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-is-pseudo-class@2.0.7(postcss@8.4.47)': + dependencies: + '@csstools/selector-specificity': 2.2.0(postcss-selector-parser@6.1.2) + postcss: 8.4.47 + postcss-selector-parser: 6.1.2 + + '@csstools/postcss-nested-calc@1.0.0(postcss@8.4.47)': + dependencies: + postcss: 8.4.47 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-normalize-display-values@1.0.1(postcss@8.4.47)': + dependencies: + postcss: 8.4.47 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-oklab-function@1.1.1(postcss@8.4.47)': + dependencies: + '@csstools/postcss-progressive-custom-properties': 1.3.0(postcss@8.4.47) + postcss: 8.4.47 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-progressive-custom-properties@1.3.0(postcss@8.4.47)': + dependencies: + postcss: 8.4.47 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-stepped-value-functions@1.0.1(postcss@8.4.47)': + dependencies: + postcss: 8.4.47 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-text-decoration-shorthand@1.0.0(postcss@8.4.47)': + dependencies: + postcss: 8.4.47 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-trigonometric-functions@1.0.2(postcss@8.4.47)': + dependencies: + postcss: 8.4.47 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-unset-value@1.0.2(postcss@8.4.47)': + dependencies: + postcss: 8.4.47 + + '@csstools/selector-specificity@2.2.0(postcss-selector-parser@6.1.2)': + dependencies: + postcss-selector-parser: 6.1.2 + + '@ctrl/tinycolor@3.6.1': {} + + '@discoveryjs/json-ext@0.5.7': {} + + '@discoveryjs/json-ext@0.6.3': {} + + '@dnd-kit/accessibility@3.1.0(react@18.3.1)': + dependencies: + react: 18.3.1 + tslib: 2.8.0 + + '@dnd-kit/core@6.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@dnd-kit/accessibility': 3.1.0(react@18.3.1) + '@dnd-kit/utilities': 3.2.2(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + tslib: 2.8.0 + + '@dnd-kit/modifiers@6.0.1(@dnd-kit/core@6.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)': + dependencies: + '@dnd-kit/core': 6.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@dnd-kit/utilities': 3.2.2(react@18.3.1) + react: 18.3.1 + tslib: 2.8.0 + + '@dnd-kit/sortable@7.0.2(@dnd-kit/core@6.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)': + dependencies: + '@dnd-kit/core': 6.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@dnd-kit/utilities': 3.2.2(react@18.3.1) + react: 18.3.1 + tslib: 2.8.0 + + '@dnd-kit/utilities@3.2.2(react@18.3.1)': + dependencies: + react: 18.3.1 + tslib: 2.8.0 + + '@emnapi/runtime@1.4.4': + dependencies: + tslib: 2.8.0 + optional: true + + '@emotion/hash@0.8.0': {} + + '@emotion/unitless@0.7.5': {} + + '@esbuild/aix-ppc64@0.23.1': + optional: true + + '@esbuild/aix-ppc64@0.25.6': + optional: true + + '@esbuild/android-arm64@0.23.1': + optional: true + + '@esbuild/android-arm64@0.25.6': + optional: true + + '@esbuild/android-arm@0.23.1': + optional: true + + '@esbuild/android-arm@0.25.6': + optional: true + + '@esbuild/android-x64@0.23.1': + optional: true + + '@esbuild/android-x64@0.25.6': + optional: true + + '@esbuild/darwin-arm64@0.23.1': + optional: true + + '@esbuild/darwin-arm64@0.25.6': + optional: true + + '@esbuild/darwin-x64@0.23.1': + optional: true + + '@esbuild/darwin-x64@0.25.6': + optional: true + + '@esbuild/freebsd-arm64@0.23.1': + optional: true + + '@esbuild/freebsd-arm64@0.25.6': + optional: true + + '@esbuild/freebsd-x64@0.23.1': + optional: true + + '@esbuild/freebsd-x64@0.25.6': + optional: true + + '@esbuild/linux-arm64@0.23.1': + optional: true + + '@esbuild/linux-arm64@0.25.6': + optional: true + + '@esbuild/linux-arm@0.23.1': + optional: true + + '@esbuild/linux-arm@0.25.6': + optional: true + + '@esbuild/linux-ia32@0.23.1': + optional: true + + '@esbuild/linux-ia32@0.25.6': + optional: true + + '@esbuild/linux-loong64@0.23.1': + optional: true + + '@esbuild/linux-loong64@0.25.6': + optional: true + + '@esbuild/linux-mips64el@0.23.1': + optional: true + + '@esbuild/linux-mips64el@0.25.6': + optional: true + + '@esbuild/linux-ppc64@0.23.1': + optional: true + + '@esbuild/linux-ppc64@0.25.6': + optional: true + + '@esbuild/linux-riscv64@0.23.1': + optional: true + + '@esbuild/linux-riscv64@0.25.6': + optional: true + + '@esbuild/linux-s390x@0.23.1': + optional: true + + '@esbuild/linux-s390x@0.25.6': + optional: true + + '@esbuild/linux-x64@0.23.1': + optional: true + + '@esbuild/linux-x64@0.25.6': + optional: true + + '@esbuild/netbsd-arm64@0.25.6': + optional: true + + '@esbuild/netbsd-x64@0.23.1': + optional: true + + '@esbuild/netbsd-x64@0.25.6': + optional: true + + '@esbuild/openbsd-arm64@0.23.1': + optional: true + + '@esbuild/openbsd-arm64@0.25.6': + optional: true + + '@esbuild/openbsd-x64@0.23.1': + optional: true + + '@esbuild/openbsd-x64@0.25.6': + optional: true + + '@esbuild/openharmony-arm64@0.25.6': + optional: true + + '@esbuild/sunos-x64@0.23.1': + optional: true + + '@esbuild/sunos-x64@0.25.6': + optional: true + + '@esbuild/win32-arm64@0.23.1': + optional: true + + '@esbuild/win32-arm64@0.25.6': + optional: true + + '@esbuild/win32-ia32@0.23.1': + optional: true + + '@esbuild/win32-ia32@0.25.6': + optional: true + + '@esbuild/win32-x64@0.23.1': + optional: true + + '@esbuild/win32-x64@0.25.6': + optional: true + + '@eslint-community/eslint-utils@4.4.1(eslint@8.57.1)': + dependencies: + eslint: 8.57.1 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.1': {} + + '@eslint/eslintrc@2.1.4': + dependencies: + ajv: 6.12.6 + debug: 4.3.7(supports-color@5.5.0) + espree: 9.6.1 + globals: 13.24.0 + ignore: 5.3.2 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@8.57.1': {} + + '@fastify/busboy@3.0.0': {} + + '@firebase/analytics-compat@0.2.18(@firebase/app-compat@0.2.51)(@firebase/app@0.11.2)': + dependencies: + '@firebase/analytics': 0.10.12(@firebase/app@0.11.2) + '@firebase/analytics-types': 0.8.3 + '@firebase/app-compat': 0.2.51 + '@firebase/component': 0.6.13 + '@firebase/util': 1.11.0 + tslib: 2.8.0 + transitivePeerDependencies: + - '@firebase/app' + + '@firebase/analytics-types@0.8.3': {} + + '@firebase/analytics@0.10.12(@firebase/app@0.11.2)': + dependencies: + '@firebase/app': 0.11.2 + '@firebase/component': 0.6.13 + '@firebase/installations': 0.6.13(@firebase/app@0.11.2) + '@firebase/logger': 0.4.4 + '@firebase/util': 1.11.0 + tslib: 2.8.0 + + '@firebase/app-check-compat@0.3.19(@firebase/app-compat@0.2.51)(@firebase/app@0.11.2)': + dependencies: + '@firebase/app-check': 0.8.12(@firebase/app@0.11.2) + '@firebase/app-check-types': 0.5.3 + '@firebase/app-compat': 0.2.51 + '@firebase/component': 0.6.13 + '@firebase/logger': 0.4.4 + '@firebase/util': 1.11.0 + tslib: 2.8.0 + transitivePeerDependencies: + - '@firebase/app' + + '@firebase/app-check-interop-types@0.3.3': {} + + '@firebase/app-check-types@0.5.3': {} + + '@firebase/app-check@0.8.12(@firebase/app@0.11.2)': + dependencies: + '@firebase/app': 0.11.2 + '@firebase/component': 0.6.13 + '@firebase/logger': 0.4.4 + '@firebase/util': 1.11.0 + tslib: 2.8.0 + + '@firebase/app-compat@0.2.51': + dependencies: + '@firebase/app': 0.11.2 + '@firebase/component': 0.6.13 + '@firebase/logger': 0.4.4 + '@firebase/util': 1.11.0 + tslib: 2.8.0 + + '@firebase/app-types@0.9.3': {} + + '@firebase/app@0.11.2': + dependencies: + '@firebase/component': 0.6.13 + '@firebase/logger': 0.4.4 + '@firebase/util': 1.11.0 + idb: 7.1.1 + tslib: 2.8.0 + + '@firebase/auth-compat@0.5.19(@firebase/app-compat@0.2.51)(@firebase/app-types@0.9.3)(@firebase/app@0.11.2)': + dependencies: + '@firebase/app-compat': 0.2.51 + '@firebase/auth': 1.9.1(@firebase/app@0.11.2) + '@firebase/auth-types': 0.13.0(@firebase/app-types@0.9.3)(@firebase/util@1.11.0) + '@firebase/component': 0.6.13 + '@firebase/util': 1.11.0 + tslib: 2.8.0 + transitivePeerDependencies: + - '@firebase/app' + - '@firebase/app-types' + - '@react-native-async-storage/async-storage' + + '@firebase/auth-interop-types@0.2.4': {} + + '@firebase/auth-types@0.13.0(@firebase/app-types@0.9.3)(@firebase/util@1.11.0)': + dependencies: + '@firebase/app-types': 0.9.3 + '@firebase/util': 1.11.0 + + '@firebase/auth@1.9.1(@firebase/app@0.11.2)': + dependencies: + '@firebase/app': 0.11.2 + '@firebase/component': 0.6.13 + '@firebase/logger': 0.4.4 + '@firebase/util': 1.11.0 + tslib: 2.8.0 + + '@firebase/component@0.6.13': + dependencies: + '@firebase/util': 1.11.0 + tslib: 2.8.0 + + '@firebase/data-connect@0.3.1(@firebase/app@0.11.2)': + dependencies: + '@firebase/app': 0.11.2 + '@firebase/auth-interop-types': 0.2.4 + '@firebase/component': 0.6.13 + '@firebase/logger': 0.4.4 + '@firebase/util': 1.11.0 + tslib: 2.8.0 + + '@firebase/database-compat@2.0.4': + dependencies: + '@firebase/component': 0.6.13 + '@firebase/database': 1.0.13 + '@firebase/database-types': 1.0.9 + '@firebase/logger': 0.4.4 + '@firebase/util': 1.11.0 + tslib: 2.8.0 + + '@firebase/database-types@1.0.9': + dependencies: + '@firebase/app-types': 0.9.3 + '@firebase/util': 1.11.0 + + '@firebase/database@1.0.13': + dependencies: + '@firebase/app-check-interop-types': 0.3.3 + '@firebase/auth-interop-types': 0.2.4 + '@firebase/component': 0.6.13 + '@firebase/logger': 0.4.4 + '@firebase/util': 1.11.0 + faye-websocket: 0.11.4 + tslib: 2.8.0 + + '@firebase/firestore-compat@0.3.44(@firebase/app-compat@0.2.51)(@firebase/app-types@0.9.3)(@firebase/app@0.11.2)': + dependencies: + '@firebase/app-compat': 0.2.51 + '@firebase/component': 0.6.13 + '@firebase/firestore': 4.7.9(@firebase/app@0.11.2) + '@firebase/firestore-types': 3.0.3(@firebase/app-types@0.9.3)(@firebase/util@1.11.0) + '@firebase/util': 1.11.0 + tslib: 2.8.0 + transitivePeerDependencies: + - '@firebase/app' + - '@firebase/app-types' + + '@firebase/firestore-types@3.0.3(@firebase/app-types@0.9.3)(@firebase/util@1.11.0)': + dependencies: + '@firebase/app-types': 0.9.3 + '@firebase/util': 1.11.0 + + '@firebase/firestore@4.7.9(@firebase/app@0.11.2)': + dependencies: + '@firebase/app': 0.11.2 + '@firebase/component': 0.6.13 + '@firebase/logger': 0.4.4 + '@firebase/util': 1.11.0 + '@firebase/webchannel-wrapper': 1.0.3 + '@grpc/grpc-js': 1.9.15 + '@grpc/proto-loader': 0.7.13 + tslib: 2.8.0 + + '@firebase/functions-compat@0.3.20(@firebase/app-compat@0.2.51)(@firebase/app@0.11.2)': + dependencies: + '@firebase/app-compat': 0.2.51 + '@firebase/component': 0.6.13 + '@firebase/functions': 0.12.3(@firebase/app@0.11.2) + '@firebase/functions-types': 0.6.3 + '@firebase/util': 1.11.0 + tslib: 2.8.0 + transitivePeerDependencies: + - '@firebase/app' + + '@firebase/functions-types@0.6.3': {} + + '@firebase/functions@0.12.3(@firebase/app@0.11.2)': + dependencies: + '@firebase/app': 0.11.2 + '@firebase/app-check-interop-types': 0.3.3 + '@firebase/auth-interop-types': 0.2.4 + '@firebase/component': 0.6.13 + '@firebase/messaging-interop-types': 0.2.3 + '@firebase/util': 1.11.0 + tslib: 2.8.0 + + '@firebase/installations-compat@0.2.13(@firebase/app-compat@0.2.51)(@firebase/app-types@0.9.3)(@firebase/app@0.11.2)': + dependencies: + '@firebase/app-compat': 0.2.51 + '@firebase/component': 0.6.13 + '@firebase/installations': 0.6.13(@firebase/app@0.11.2) + '@firebase/installations-types': 0.5.3(@firebase/app-types@0.9.3) + '@firebase/util': 1.11.0 + tslib: 2.8.0 + transitivePeerDependencies: + - '@firebase/app' + - '@firebase/app-types' + + '@firebase/installations-types@0.5.3(@firebase/app-types@0.9.3)': + dependencies: + '@firebase/app-types': 0.9.3 + + '@firebase/installations@0.6.13(@firebase/app@0.11.2)': + dependencies: + '@firebase/app': 0.11.2 + '@firebase/component': 0.6.13 + '@firebase/util': 1.11.0 + idb: 7.1.1 + tslib: 2.8.0 + + '@firebase/logger@0.4.4': + dependencies: + tslib: 2.8.0 + + '@firebase/messaging-compat@0.2.17(@firebase/app-compat@0.2.51)(@firebase/app@0.11.2)': + dependencies: + '@firebase/app-compat': 0.2.51 + '@firebase/component': 0.6.13 + '@firebase/messaging': 0.12.17(@firebase/app@0.11.2) + '@firebase/util': 1.11.0 + tslib: 2.8.0 + transitivePeerDependencies: + - '@firebase/app' + + '@firebase/messaging-interop-types@0.2.3': {} + + '@firebase/messaging@0.12.17(@firebase/app@0.11.2)': + dependencies: + '@firebase/app': 0.11.2 + '@firebase/component': 0.6.13 + '@firebase/installations': 0.6.13(@firebase/app@0.11.2) + '@firebase/messaging-interop-types': 0.2.3 + '@firebase/util': 1.11.0 + idb: 7.1.1 + tslib: 2.8.0 + + '@firebase/performance-compat@0.2.14(@firebase/app-compat@0.2.51)(@firebase/app@0.11.2)': + dependencies: + '@firebase/app-compat': 0.2.51 + '@firebase/component': 0.6.13 + '@firebase/logger': 0.4.4 + '@firebase/performance': 0.7.1(@firebase/app@0.11.2) + '@firebase/performance-types': 0.2.3 + '@firebase/util': 1.11.0 + tslib: 2.8.0 + transitivePeerDependencies: + - '@firebase/app' + + '@firebase/performance-types@0.2.3': {} + + '@firebase/performance@0.7.1(@firebase/app@0.11.2)': + dependencies: + '@firebase/app': 0.11.2 + '@firebase/component': 0.6.13 + '@firebase/installations': 0.6.13(@firebase/app@0.11.2) + '@firebase/logger': 0.4.4 + '@firebase/util': 1.11.0 + tslib: 2.8.0 + web-vitals: 4.2.4 + + '@firebase/remote-config-compat@0.2.13(@firebase/app-compat@0.2.51)(@firebase/app@0.11.2)': + dependencies: + '@firebase/app-compat': 0.2.51 + '@firebase/component': 0.6.13 + '@firebase/logger': 0.4.4 + '@firebase/remote-config': 0.6.0(@firebase/app@0.11.2) + '@firebase/remote-config-types': 0.4.0 + '@firebase/util': 1.11.0 + tslib: 2.8.0 + transitivePeerDependencies: + - '@firebase/app' + + '@firebase/remote-config-types@0.4.0': {} + + '@firebase/remote-config@0.6.0(@firebase/app@0.11.2)': + dependencies: + '@firebase/app': 0.11.2 + '@firebase/component': 0.6.13 + '@firebase/installations': 0.6.13(@firebase/app@0.11.2) + '@firebase/logger': 0.4.4 + '@firebase/util': 1.11.0 + tslib: 2.8.0 + + '@firebase/storage-compat@0.3.17(@firebase/app-compat@0.2.51)(@firebase/app-types@0.9.3)(@firebase/app@0.11.2)': + dependencies: + '@firebase/app-compat': 0.2.51 + '@firebase/component': 0.6.13 + '@firebase/storage': 0.13.7(@firebase/app@0.11.2) + '@firebase/storage-types': 0.8.3(@firebase/app-types@0.9.3)(@firebase/util@1.11.0) + '@firebase/util': 1.11.0 + tslib: 2.8.0 + transitivePeerDependencies: + - '@firebase/app' + - '@firebase/app-types' + + '@firebase/storage-types@0.8.3(@firebase/app-types@0.9.3)(@firebase/util@1.11.0)': + dependencies: + '@firebase/app-types': 0.9.3 + '@firebase/util': 1.11.0 + + '@firebase/storage@0.13.7(@firebase/app@0.11.2)': + dependencies: + '@firebase/app': 0.11.2 + '@firebase/component': 0.6.13 + '@firebase/util': 1.11.0 + tslib: 2.8.0 + + '@firebase/util@1.11.0': + dependencies: + tslib: 2.8.0 + + '@firebase/vertexai@1.1.0(@firebase/app-types@0.9.3)(@firebase/app@0.11.2)': + dependencies: + '@firebase/app': 0.11.2 + '@firebase/app-check-interop-types': 0.3.3 + '@firebase/app-types': 0.9.3 + '@firebase/component': 0.6.13 + '@firebase/logger': 0.4.4 + '@firebase/util': 1.11.0 + tslib: 2.8.0 + + '@firebase/webchannel-wrapper@1.0.3': {} + + '@floating-ui/core@1.7.2': + dependencies: + '@floating-ui/utils': 0.2.10 + + '@floating-ui/dom@1.7.2': + dependencies: + '@floating-ui/core': 1.7.2 + '@floating-ui/utils': 0.2.10 + + '@floating-ui/react-dom@2.1.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@floating-ui/dom': 1.7.2 + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + + '@floating-ui/utils@0.2.10': {} + + '@fontsource/inter@4.5.15': {} + + '@fontsource/roboto-mono@4.5.10': {} + + '@gar/promisify@1.1.3': {} + + '@google-cloud/firestore@7.11.0(encoding@0.1.13)': + dependencies: + '@opentelemetry/api': 1.9.0 + fast-deep-equal: 3.1.3 + functional-red-black-tree: 1.0.1 + google-gax: 4.4.1(encoding@0.1.13) + protobufjs: 7.4.0 + transitivePeerDependencies: + - encoding + - supports-color + optional: true + + '@google-cloud/paginator@5.0.2': + dependencies: + arrify: 2.0.1 + extend: 3.0.2 + optional: true + + '@google-cloud/projectify@4.0.0': + optional: true + + '@google-cloud/promisify@4.0.0': + optional: true + + '@google-cloud/scheduler@4.3.0(encoding@0.1.13)': + dependencies: + google-gax: 4.4.1(encoding@0.1.13) + transitivePeerDependencies: + - encoding + - supports-color + + '@google-cloud/storage@7.15.2(encoding@0.1.13)': + dependencies: + '@google-cloud/paginator': 5.0.2 + '@google-cloud/projectify': 4.0.0 + '@google-cloud/promisify': 4.0.0 + abort-controller: 3.0.0 + async-retry: 1.3.3 + duplexify: 4.1.3 + fast-xml-parser: 4.5.0 + gaxios: 6.7.1(encoding@0.1.13) + google-auth-library: 9.14.2(encoding@0.1.13) + html-entities: 2.5.2 + mime: 3.0.0 + p-limit: 3.1.0 + retry-request: 7.0.2(encoding@0.1.13) + teeny-request: 9.0.0(encoding@0.1.13) + uuid: 8.3.2 + transitivePeerDependencies: + - encoding + - supports-color + optional: true + + '@grpc/grpc-js@1.12.2': + dependencies: + '@grpc/proto-loader': 0.7.13 + '@js-sdsl/ordered-map': 4.4.2 + + '@grpc/grpc-js@1.9.15': + dependencies: + '@grpc/proto-loader': 0.7.13 + '@types/node': 18.19.61 + + '@grpc/proto-loader@0.7.13': + dependencies: + lodash.camelcase: 4.3.0 + long: 5.2.3 + protobufjs: 7.4.0 + yargs: 17.7.2 + + '@hubspot/api-client@11.2.0(encoding@0.1.13)': + dependencies: + '@types/node-fetch': 2.6.11 + bottleneck: 2.19.5 + es6-promise: 4.2.8 + form-data: 4.0.4 + lodash.get: 4.4.2 + lodash.merge: 4.6.2 + node-fetch: 2.7.0(encoding@0.1.13) + url-parse: 1.5.10 + transitivePeerDependencies: + - encoding + + '@humanwhocodes/config-array@0.13.0': + dependencies: + '@humanwhocodes/object-schema': 2.0.3 + debug: 4.3.7(supports-color@5.5.0) + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/object-schema@2.0.3': {} + + '@img/sharp-darwin-arm64@0.34.1': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.1.0 + optional: true + + '@img/sharp-darwin-arm64@0.34.3': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.2.0 + optional: true + + '@img/sharp-darwin-x64@0.34.1': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.1.0 + optional: true + + '@img/sharp-darwin-x64@0.34.3': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.2.0 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.1.0': + optional: true + + '@img/sharp-libvips-darwin-arm64@1.2.0': + optional: true + + '@img/sharp-libvips-darwin-x64@1.1.0': + optional: true + + '@img/sharp-libvips-darwin-x64@1.2.0': + optional: true + + '@img/sharp-libvips-linux-arm64@1.1.0': + optional: true + + '@img/sharp-libvips-linux-arm64@1.2.0': + optional: true + + '@img/sharp-libvips-linux-arm@1.1.0': + optional: true + + '@img/sharp-libvips-linux-arm@1.2.0': + optional: true + + '@img/sharp-libvips-linux-ppc64@1.1.0': + optional: true + + '@img/sharp-libvips-linux-ppc64@1.2.0': + optional: true + + '@img/sharp-libvips-linux-s390x@1.1.0': + optional: true + + '@img/sharp-libvips-linux-s390x@1.2.0': + optional: true + + '@img/sharp-libvips-linux-x64@1.1.0': + optional: true + + '@img/sharp-libvips-linux-x64@1.2.0': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.1.0': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.2.0': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.1.0': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.2.0': + optional: true + + '@img/sharp-linux-arm64@0.34.1': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.1.0 + optional: true + + '@img/sharp-linux-arm64@0.34.3': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.2.0 + optional: true + + '@img/sharp-linux-arm@0.34.1': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.1.0 + optional: true + + '@img/sharp-linux-arm@0.34.3': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.2.0 + optional: true + + '@img/sharp-linux-ppc64@0.34.3': + optionalDependencies: + '@img/sharp-libvips-linux-ppc64': 1.2.0 + optional: true + + '@img/sharp-linux-s390x@0.34.1': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.1.0 + optional: true + + '@img/sharp-linux-s390x@0.34.3': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.2.0 + optional: true + + '@img/sharp-linux-x64@0.34.1': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.1.0 + optional: true + + '@img/sharp-linux-x64@0.34.3': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.2.0 + optional: true + + '@img/sharp-linuxmusl-arm64@0.34.1': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.1.0 + optional: true + + '@img/sharp-linuxmusl-arm64@0.34.3': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.2.0 + optional: true + + '@img/sharp-linuxmusl-x64@0.34.1': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.1.0 + optional: true + + '@img/sharp-linuxmusl-x64@0.34.3': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.2.0 + optional: true + + '@img/sharp-wasm32@0.34.1': + dependencies: + '@emnapi/runtime': 1.4.4 + optional: true + + '@img/sharp-wasm32@0.34.3': + dependencies: + '@emnapi/runtime': 1.4.4 + optional: true + + '@img/sharp-win32-arm64@0.34.3': + optional: true + + '@img/sharp-win32-ia32@0.34.1': + optional: true + + '@img/sharp-win32-ia32@0.34.3': + optional: true + + '@img/sharp-win32-x64@0.34.1': + optional: true + + '@img/sharp-win32-x64@0.34.3': + optional: true + + '@inquirer/figures@1.0.7': {} + + '@ioredis/commands@1.2.0': {} + + '@isaacs/balanced-match@4.0.1': {} + + '@isaacs/brace-expansion@5.0.0': + dependencies: + '@isaacs/balanced-match': 4.0.1 + + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.0 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + '@isaacs/fs-minipass@4.0.1': + dependencies: + minipass: 7.1.2 + + '@isaacs/string-locale-compare@1.1.0': {} + + '@isaacs/ttlcache@1.4.1': {} + + '@istanbuljs/load-nyc-config@1.1.0': + dependencies: + camelcase: 5.3.1 + find-up: 4.1.0 + get-package-type: 0.1.0 + js-yaml: 3.14.1 + resolve-from: 5.0.0 + + '@istanbuljs/schema@0.1.3': {} + + '@jest/console@27.5.1': + dependencies: + '@jest/types': 27.5.1 + '@types/node': 18.19.61 + chalk: 4.1.2 + jest-message-util: 27.5.1 + jest-util: 27.5.1 + slash: 3.0.0 + + '@jest/console@28.1.3': + dependencies: + '@jest/types': 28.1.3 + '@types/node': 18.19.61 + chalk: 4.1.2 + jest-message-util: 28.1.3 + jest-util: 28.1.3 + slash: 3.0.0 + + '@jest/console@29.7.0': + dependencies: + '@jest/types': 29.6.3 + '@types/node': 18.19.61 + chalk: 4.1.2 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + slash: 3.0.0 + + '@jest/core@27.5.1(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3))': + dependencies: + '@jest/console': 27.5.1 + '@jest/reporters': 27.5.1 + '@jest/test-result': 27.5.1 + '@jest/transform': 27.5.1 + '@jest/types': 27.5.1 + '@types/node': 18.19.61 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + emittery: 0.8.1 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-changed-files: 27.5.1 + jest-config: 27.5.1(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)) + jest-haste-map: 27.5.1 + jest-message-util: 27.5.1 + jest-regex-util: 27.5.1 + jest-resolve: 27.5.1 + jest-resolve-dependencies: 27.5.1 + jest-runner: 27.5.1 + jest-runtime: 27.5.1 + jest-snapshot: 27.5.1 + jest-util: 27.5.1 + jest-validate: 27.5.1 + jest-watcher: 27.5.1 + micromatch: 4.0.8 + rimraf: 3.0.2 + slash: 3.0.0 + strip-ansi: 6.0.1 + transitivePeerDependencies: + - bufferutil + - canvas + - supports-color + - ts-node + - utf-8-validate + + '@jest/core@28.1.3(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3))': + dependencies: + '@jest/console': 28.1.3 + '@jest/reporters': 28.1.3 + '@jest/test-result': 28.1.3 + '@jest/transform': 28.1.3 + '@jest/types': 28.1.3 + '@types/node': 18.19.61 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + ci-info: 3.9.0 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-changed-files: 28.1.3 + jest-config: 28.1.3(@types/node@18.19.61)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)) + jest-haste-map: 28.1.3 + jest-message-util: 28.1.3 + jest-regex-util: 28.0.2 + jest-resolve: 28.1.3 + jest-resolve-dependencies: 28.1.3 + jest-runner: 28.1.3 + jest-runtime: 28.1.3 + jest-snapshot: 28.1.3 + jest-util: 28.1.3 + jest-validate: 28.1.3 + jest-watcher: 28.1.3 + micromatch: 4.0.8 + pretty-format: 28.1.3 + rimraf: 3.0.2 + slash: 3.0.0 + strip-ansi: 6.0.1 + transitivePeerDependencies: + - supports-color + - ts-node + + '@jest/core@29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3))': + dependencies: + '@jest/console': 29.7.0 + '@jest/reporters': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 18.19.61 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + ci-info: 3.9.0 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-changed-files: 29.7.0 + jest-config: 29.7.0(@types/node@18.19.61)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)) + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-resolve-dependencies: 29.7.0 + jest-runner: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + jest-watcher: 29.7.0 + micromatch: 4.0.8 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-ansi: 6.0.1 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + - ts-node + + '@jest/core@29.7.0(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3))': + dependencies: + '@jest/console': 29.7.0 + '@jest/reporters': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 18.19.61 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + ci-info: 3.9.0 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-changed-files: 29.7.0 + jest-config: 29.7.0(@types/node@18.19.61)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)) + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-resolve-dependencies: 29.7.0 + jest-runner: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + jest-watcher: 29.7.0 + micromatch: 4.0.8 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-ansi: 6.0.1 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + - ts-node + + '@jest/core@29.7.0(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@22.14.1)(typescript@5.6.3))': + dependencies: + '@jest/console': 29.7.0 + '@jest/reporters': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 18.19.61 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + ci-info: 3.9.0 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-changed-files: 29.7.0 + jest-config: 29.7.0(@types/node@18.19.61)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@22.14.1)(typescript@5.6.3)) + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-resolve-dependencies: 29.7.0 + jest-runner: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + jest-watcher: 29.7.0 + micromatch: 4.0.8 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-ansi: 6.0.1 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + - ts-node + + '@jest/environment@27.5.1': + dependencies: + '@jest/fake-timers': 27.5.1 + '@jest/types': 27.5.1 + '@types/node': 18.19.61 + jest-mock: 27.5.1 + + '@jest/environment@28.1.3': + dependencies: + '@jest/fake-timers': 28.1.3 + '@jest/types': 28.1.3 + '@types/node': 18.19.61 + jest-mock: 28.1.3 + + '@jest/environment@29.7.0': + dependencies: + '@jest/fake-timers': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 18.19.61 + jest-mock: 29.7.0 + + '@jest/expect-utils@28.1.3': + dependencies: + jest-get-type: 28.0.2 + + '@jest/expect-utils@29.7.0': + dependencies: + jest-get-type: 29.6.3 + + '@jest/expect@28.1.3': + dependencies: + expect: 28.1.3 + jest-snapshot: 28.1.3 + transitivePeerDependencies: + - supports-color + + '@jest/expect@29.7.0': + dependencies: + expect: 29.7.0 + jest-snapshot: 29.7.0 + transitivePeerDependencies: + - supports-color + + '@jest/fake-timers@27.5.1': + dependencies: + '@jest/types': 27.5.1 + '@sinonjs/fake-timers': 8.1.0 + '@types/node': 18.19.61 + jest-message-util: 27.5.1 + jest-mock: 27.5.1 + jest-util: 27.5.1 + + '@jest/fake-timers@28.1.3': + dependencies: + '@jest/types': 28.1.3 + '@sinonjs/fake-timers': 9.1.2 + '@types/node': 18.19.61 + jest-message-util: 28.1.3 + jest-mock: 28.1.3 + jest-util: 28.1.3 + + '@jest/fake-timers@29.7.0': + dependencies: + '@jest/types': 29.6.3 + '@sinonjs/fake-timers': 10.3.0 + '@types/node': 18.19.61 + jest-message-util: 29.7.0 + jest-mock: 29.7.0 + jest-util: 29.7.0 + + '@jest/globals@27.5.1': + dependencies: + '@jest/environment': 27.5.1 + '@jest/types': 27.5.1 + expect: 27.5.1 + + '@jest/globals@28.1.3': + dependencies: + '@jest/environment': 28.1.3 + '@jest/expect': 28.1.3 + '@jest/types': 28.1.3 + transitivePeerDependencies: + - supports-color + + '@jest/globals@29.7.0': + dependencies: + '@jest/environment': 29.7.0 + '@jest/expect': 29.7.0 + '@jest/types': 29.6.3 + jest-mock: 29.7.0 + transitivePeerDependencies: + - supports-color + + '@jest/reporters@27.5.1': + dependencies: + '@bcoe/v8-coverage': 0.2.3 + '@jest/console': 27.5.1 + '@jest/test-result': 27.5.1 + '@jest/transform': 27.5.1 + '@jest/types': 27.5.1 + '@types/node': 18.19.61 + chalk: 4.1.2 + collect-v8-coverage: 1.0.2 + exit: 0.1.2 + glob: 7.2.3 + graceful-fs: 4.2.11 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-instrument: 5.2.1 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 4.0.1 + istanbul-reports: 3.1.7 + jest-haste-map: 27.5.1 + jest-resolve: 27.5.1 + jest-util: 27.5.1 + jest-worker: 27.5.1 + slash: 3.0.0 + source-map: 0.6.1 + string-length: 4.0.2 + terminal-link: 2.1.1 + v8-to-istanbul: 8.1.1 + transitivePeerDependencies: + - supports-color + + '@jest/reporters@28.1.3': + dependencies: + '@bcoe/v8-coverage': 0.2.3 + '@jest/console': 28.1.3 + '@jest/test-result': 28.1.3 + '@jest/transform': 28.1.3 + '@jest/types': 28.1.3 + '@jridgewell/trace-mapping': 0.3.25 + '@types/node': 18.19.61 + chalk: 4.1.2 + collect-v8-coverage: 1.0.2 + exit: 0.1.2 + glob: 7.2.3 + graceful-fs: 4.2.11 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-instrument: 5.2.1 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 4.0.1 + istanbul-reports: 3.1.7 + jest-message-util: 28.1.3 + jest-util: 28.1.3 + jest-worker: 28.1.3 + slash: 3.0.0 + string-length: 4.0.2 + strip-ansi: 6.0.1 + terminal-link: 2.1.1 + v8-to-istanbul: 9.3.0 + transitivePeerDependencies: + - supports-color + + '@jest/reporters@29.7.0': + dependencies: + '@bcoe/v8-coverage': 0.2.3 + '@jest/console': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@jridgewell/trace-mapping': 0.3.25 + '@types/node': 18.19.61 + chalk: 4.1.2 + collect-v8-coverage: 1.0.2 + exit: 0.1.2 + glob: 7.2.3 + graceful-fs: 4.2.11 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-instrument: 6.0.3 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 4.0.1 + istanbul-reports: 3.1.7 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + jest-worker: 29.7.0 + slash: 3.0.0 + string-length: 4.0.2 + strip-ansi: 6.0.1 + v8-to-istanbul: 9.3.0 + transitivePeerDependencies: + - supports-color + + '@jest/schemas@28.1.3': + dependencies: + '@sinclair/typebox': 0.24.51 + + '@jest/schemas@29.6.3': + dependencies: + '@sinclair/typebox': 0.27.8 + + '@jest/source-map@27.5.1': + dependencies: + callsites: 3.1.0 + graceful-fs: 4.2.11 + source-map: 0.6.1 + + '@jest/source-map@28.1.2': + dependencies: + '@jridgewell/trace-mapping': 0.3.25 + callsites: 3.1.0 + graceful-fs: 4.2.11 + + '@jest/source-map@29.6.3': + dependencies: + '@jridgewell/trace-mapping': 0.3.25 + callsites: 3.1.0 + graceful-fs: 4.2.11 + + '@jest/test-result@27.5.1': + dependencies: + '@jest/console': 27.5.1 + '@jest/types': 27.5.1 + '@types/istanbul-lib-coverage': 2.0.6 + collect-v8-coverage: 1.0.2 + + '@jest/test-result@28.1.3': + dependencies: + '@jest/console': 28.1.3 + '@jest/types': 28.1.3 + '@types/istanbul-lib-coverage': 2.0.6 + collect-v8-coverage: 1.0.2 + + '@jest/test-result@29.7.0': + dependencies: + '@jest/console': 29.7.0 + '@jest/types': 29.6.3 + '@types/istanbul-lib-coverage': 2.0.6 + collect-v8-coverage: 1.0.2 + + '@jest/test-sequencer@27.5.1': + dependencies: + '@jest/test-result': 27.5.1 + graceful-fs: 4.2.11 + jest-haste-map: 27.5.1 + jest-runtime: 27.5.1 + transitivePeerDependencies: + - supports-color + + '@jest/test-sequencer@28.1.3': + dependencies: + '@jest/test-result': 28.1.3 + graceful-fs: 4.2.11 + jest-haste-map: 28.1.3 + slash: 3.0.0 + + '@jest/test-sequencer@29.7.0': + dependencies: + '@jest/test-result': 29.7.0 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + slash: 3.0.0 + + '@jest/transform@27.5.1': + dependencies: + '@babel/core': 7.26.0 + '@jest/types': 27.5.1 + babel-plugin-istanbul: 6.1.1 + chalk: 4.1.2 + convert-source-map: 1.9.0 + fast-json-stable-stringify: 2.1.0 + graceful-fs: 4.2.11 + jest-haste-map: 27.5.1 + jest-regex-util: 27.5.1 + jest-util: 27.5.1 + micromatch: 4.0.8 + pirates: 4.0.6 + slash: 3.0.0 + source-map: 0.6.1 + write-file-atomic: 3.0.3 + transitivePeerDependencies: + - supports-color + + '@jest/transform@28.1.3': + dependencies: + '@babel/core': 7.26.0 + '@jest/types': 28.1.3 + '@jridgewell/trace-mapping': 0.3.25 + babel-plugin-istanbul: 6.1.1 + chalk: 4.1.2 + convert-source-map: 1.9.0 + fast-json-stable-stringify: 2.1.0 + graceful-fs: 4.2.11 + jest-haste-map: 28.1.3 + jest-regex-util: 28.0.2 + jest-util: 28.1.3 + micromatch: 4.0.8 + pirates: 4.0.6 + slash: 3.0.0 + write-file-atomic: 4.0.2 + transitivePeerDependencies: + - supports-color + + '@jest/transform@29.7.0': + dependencies: + '@babel/core': 7.26.0 + '@jest/types': 29.6.3 + '@jridgewell/trace-mapping': 0.3.25 + babel-plugin-istanbul: 6.1.1 + chalk: 4.1.2 + convert-source-map: 2.0.0 + fast-json-stable-stringify: 2.1.0 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-regex-util: 29.6.3 + jest-util: 29.7.0 + micromatch: 4.0.8 + pirates: 4.0.6 + slash: 3.0.0 + write-file-atomic: 4.0.2 + transitivePeerDependencies: + - supports-color + + '@jest/types@27.5.1': + dependencies: + '@types/istanbul-lib-coverage': 2.0.6 + '@types/istanbul-reports': 3.0.4 + '@types/node': 18.19.61 + '@types/yargs': 16.0.9 + chalk: 4.1.2 + + '@jest/types@28.1.3': + dependencies: + '@jest/schemas': 28.1.3 + '@types/istanbul-lib-coverage': 2.0.6 + '@types/istanbul-reports': 3.0.4 + '@types/node': 18.19.61 + '@types/yargs': 17.0.33 + chalk: 4.1.2 + + '@jest/types@29.6.3': + dependencies: + '@jest/schemas': 29.6.3 + '@types/istanbul-lib-coverage': 2.0.6 + '@types/istanbul-reports': 3.0.4 + '@types/node': 18.19.61 + '@types/yargs': 17.0.33 + chalk: 4.1.2 + + '@jitsu/sdk-js@3.1.5': {} + + '@jridgewell/gen-mapping@0.3.12': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping': 0.3.29 + + '@jridgewell/gen-mapping@0.3.5': + dependencies: + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping': 0.3.25 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/set-array@1.2.1': {} + + '@jridgewell/source-map@0.3.6': + dependencies: + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + + '@jridgewell/sourcemap-codec@1.5.0': {} + + '@jridgewell/trace-mapping@0.3.25': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 + + '@jridgewell/trace-mapping@0.3.29': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 + + '@jridgewell/trace-mapping@0.3.9': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 + + '@js-sdsl/ordered-map@4.4.2': {} + + '@kurkle/color@0.3.2': {} + + '@kwsites/file-exists@1.1.1': + dependencies: + debug: 4.3.7(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + + '@kwsites/promise-deferred@1.1.1': {} + + '@leichtgewicht/ip-codec@2.0.5': {} + + '@loadable/component@5.16.4(react@18.3.1)': + dependencies: + '@babel/runtime': 7.26.0 + hoist-non-react-statics: 3.3.2 + react: 18.3.1 + react-is: 16.13.1 + + '@lottiefiles/dotlottie-react@0.13.3(react@19.0.0)': + dependencies: + '@lottiefiles/dotlottie-web': 0.42.0 + react: 19.0.0 + + '@lottiefiles/dotlottie-web@0.42.0': {} + + '@lukeed/csprng@1.1.0': {} + + '@lukeed/uuid@2.0.1': + dependencies: + '@lukeed/csprng': 1.1.0 + + '@mapbox/node-pre-gyp@1.0.11(encoding@0.1.13)': + dependencies: + detect-libc: 2.0.4 + https-proxy-agent: 5.0.1 + make-dir: 3.1.0 + node-fetch: 2.7.0(encoding@0.1.13) + nopt: 5.0.0 + npmlog: 5.0.1 + rimraf: 3.0.2 + semver: 7.7.2 + tar: 6.2.1 + transitivePeerDependencies: + - encoding + - supports-color + + '@maxmind/geoip2-node@5.0.0': + dependencies: + ip6addr: 0.2.5 + maxmind: 4.3.22 + + '@monaco-editor/loader@1.4.0(monaco-editor@0.52.0)': + dependencies: + monaco-editor: 0.52.0 + state-local: 1.0.7 + + '@monaco-editor/react@4.6.0(monaco-editor@0.52.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@monaco-editor/loader': 1.4.0(monaco-editor@0.52.0) + monaco-editor: 0.52.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@mongodb-js/saslprep@1.1.9': + dependencies: + sparse-bitfield: 3.0.3 + + '@mongodb-js/zstd@2.0.1': + dependencies: + node-addon-api: 4.3.0 + prebuild-install: 7.1.3 + + '@nangohq/frontend@0.21.17': {} + + '@next/bundle-analyzer@15.5.4': + dependencies: + webpack-bundle-analyzer: 4.10.1 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@next/env@15.5.4': {} + + '@next/eslint-plugin-next@15.5.4': + dependencies: + fast-glob: 3.3.1 + + '@next/swc-darwin-arm64@15.5.4': + optional: true + + '@next/swc-darwin-x64@15.5.4': + optional: true + + '@next/swc-linux-arm64-gnu@15.5.4': + optional: true + + '@next/swc-linux-arm64-musl@15.5.4': + optional: true + + '@next/swc-linux-x64-gnu@15.5.4': + optional: true + + '@next/swc-linux-x64-musl@15.5.4': + optional: true + + '@next/swc-win32-arm64-msvc@15.5.4': + optional: true + + '@next/swc-win32-x64-msvc@15.5.4': + optional: true + + '@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1': + dependencies: + eslint-scope: 5.1.1 + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.17.1 + + '@nolyfill/is-core-module@1.0.39': {} + + '@npmcli/arborist@4.3.1': + dependencies: + '@isaacs/string-locale-compare': 1.1.0 + '@npmcli/installed-package-contents': 1.0.7 + '@npmcli/map-workspaces': 2.0.4 + '@npmcli/metavuln-calculator': 2.0.0 + '@npmcli/move-file': 1.1.2 + '@npmcli/name-from-folder': 1.0.1 + '@npmcli/node-gyp': 1.0.3 + '@npmcli/package-json': 1.0.1 + '@npmcli/run-script': 2.0.0 + bin-links: 3.0.3 + cacache: 15.3.0 + common-ancestor-path: 1.0.1 + json-parse-even-better-errors: 2.3.1 + json-stringify-nice: 1.1.4 + mkdirp: 1.0.4 + mkdirp-infer-owner: 2.0.0 + npm-install-checks: 4.0.0 + npm-package-arg: 8.1.5 + npm-pick-manifest: 6.1.1 + npm-registry-fetch: 12.0.2 + pacote: 12.0.3 + parse-conflict-json: 2.0.2 + proc-log: 1.0.0 + promise-all-reject-late: 1.0.1 + promise-call-limit: 1.0.2 + read-package-json-fast: 2.0.3 + readdir-scoped-modules: 1.1.0 + rimraf: 3.0.2 + semver: 7.7.2 + ssri: 8.0.1 + treeverse: 1.0.4 + walk-up-path: 1.0.0 + transitivePeerDependencies: + - bluebird + - supports-color + + '@npmcli/fs@1.1.1': + dependencies: + '@gar/promisify': 1.1.3 + semver: 7.7.2 + + '@npmcli/fs@2.1.2': + dependencies: + '@gar/promisify': 1.1.3 + semver: 7.7.2 + + '@npmcli/fs@3.1.1': + dependencies: + semver: 7.7.2 + + '@npmcli/git@2.1.0': + dependencies: + '@npmcli/promise-spawn': 1.3.2 + lru-cache: 6.0.0 + mkdirp: 1.0.4 + npm-pick-manifest: 6.1.1 + promise-inflight: 1.0.1 + promise-retry: 2.0.1 + semver: 7.7.2 + which: 2.0.2 + transitivePeerDependencies: + - bluebird + + '@npmcli/git@4.1.0': + dependencies: + '@npmcli/promise-spawn': 6.0.2 + lru-cache: 7.18.3 + npm-pick-manifest: 8.0.2 + proc-log: 3.0.0 + promise-inflight: 1.0.1 + promise-retry: 2.0.1 + semver: 7.7.2 + which: 3.0.1 + transitivePeerDependencies: + - bluebird + + '@npmcli/installed-package-contents@1.0.7': + dependencies: + npm-bundled: 1.1.2 + npm-normalize-package-bin: 1.0.1 + + '@npmcli/installed-package-contents@2.1.0': + dependencies: + npm-bundled: 3.0.1 + npm-normalize-package-bin: 3.0.1 + + '@npmcli/map-workspaces@2.0.4': + dependencies: + '@npmcli/name-from-folder': 1.0.1 + glob: 8.1.0 + minimatch: 5.1.6 + read-package-json-fast: 2.0.3 + + '@npmcli/metavuln-calculator@2.0.0': + dependencies: + cacache: 15.3.0 + json-parse-even-better-errors: 2.3.1 + pacote: 12.0.3 + semver: 7.7.2 + transitivePeerDependencies: + - bluebird + - supports-color + + '@npmcli/move-file@1.1.2': + dependencies: + mkdirp: 1.0.4 + rimraf: 3.0.2 + + '@npmcli/move-file@2.0.1': + dependencies: + mkdirp: 1.0.4 + rimraf: 3.0.2 + + '@npmcli/name-from-folder@1.0.1': {} + + '@npmcli/node-gyp@1.0.3': {} + + '@npmcli/node-gyp@3.0.0': {} + + '@npmcli/package-json@1.0.1': + dependencies: + json-parse-even-better-errors: 2.3.1 + + '@npmcli/promise-spawn@1.3.2': + dependencies: + infer-owner: 1.0.4 + + '@npmcli/promise-spawn@6.0.2': + dependencies: + which: 3.0.1 + + '@npmcli/run-script@2.0.0': + dependencies: + '@npmcli/node-gyp': 1.0.3 + '@npmcli/promise-spawn': 1.3.2 + node-gyp: 8.4.1 + read-package-json-fast: 2.0.3 + transitivePeerDependencies: + - bluebird + - supports-color + + '@npmcli/run-script@6.0.2': + dependencies: + '@npmcli/node-gyp': 3.0.0 + '@npmcli/promise-spawn': 6.0.2 + node-gyp: 9.4.1 + read-package-json-fast: 3.0.2 + which: 3.0.1 + transitivePeerDependencies: + - bluebird + - supports-color + + '@octokit/auth-token@2.5.0': + dependencies: + '@octokit/types': 6.41.0 + + '@octokit/core@3.6.0(encoding@0.1.13)': + dependencies: + '@octokit/auth-token': 2.5.0 + '@octokit/graphql': 4.8.0(encoding@0.1.13) + '@octokit/request': 5.6.3(encoding@0.1.13) + '@octokit/request-error': 2.1.0 + '@octokit/types': 6.41.0 + before-after-hook: 2.2.3 + universal-user-agent: 6.0.1 + transitivePeerDependencies: + - encoding + + '@octokit/endpoint@6.0.12': + dependencies: + '@octokit/types': 6.41.0 + is-plain-object: 5.0.0 + universal-user-agent: 6.0.1 + + '@octokit/graphql@4.8.0(encoding@0.1.13)': + dependencies: + '@octokit/request': 5.6.3(encoding@0.1.13) + '@octokit/types': 6.41.0 + universal-user-agent: 6.0.1 + transitivePeerDependencies: + - encoding + + '@octokit/openapi-types@12.11.0': {} + + '@octokit/plugin-paginate-rest@2.21.3(@octokit/core@3.6.0(encoding@0.1.13))': + dependencies: + '@octokit/core': 3.6.0(encoding@0.1.13) + '@octokit/types': 6.41.0 + + '@octokit/plugin-request-log@1.0.4(@octokit/core@3.6.0(encoding@0.1.13))': + dependencies: + '@octokit/core': 3.6.0(encoding@0.1.13) + + '@octokit/plugin-rest-endpoint-methods@5.16.2(@octokit/core@3.6.0(encoding@0.1.13))': + dependencies: + '@octokit/core': 3.6.0(encoding@0.1.13) + '@octokit/types': 6.41.0 + deprecation: 2.3.1 + + '@octokit/request-error@2.1.0': + dependencies: + '@octokit/types': 6.41.0 + deprecation: 2.3.1 + once: 1.4.0 + + '@octokit/request@5.6.3(encoding@0.1.13)': + dependencies: + '@octokit/endpoint': 6.0.12 + '@octokit/request-error': 2.1.0 + '@octokit/types': 6.41.0 + is-plain-object: 5.0.0 + node-fetch: 2.7.0(encoding@0.1.13) + universal-user-agent: 6.0.1 + transitivePeerDependencies: + - encoding + + '@octokit/rest@18.12.0(encoding@0.1.13)': + dependencies: + '@octokit/core': 3.6.0(encoding@0.1.13) + '@octokit/plugin-paginate-rest': 2.21.3(@octokit/core@3.6.0(encoding@0.1.13)) + '@octokit/plugin-request-log': 1.0.4(@octokit/core@3.6.0(encoding@0.1.13)) + '@octokit/plugin-rest-endpoint-methods': 5.16.2(@octokit/core@3.6.0(encoding@0.1.13)) + transitivePeerDependencies: + - encoding + + '@octokit/types@6.41.0': + dependencies: + '@octokit/openapi-types': 12.11.0 + + '@one-ini/wasm@0.1.1': {} + + '@opentelemetry/api@1.9.0': {} + + '@panva/hkdf@1.2.1': {} + + '@pkgjs/parseargs@0.11.0': + optional: true + + '@playwright/test@1.39.0': + dependencies: + playwright: 1.39.0 + + '@pmmmwh/react-refresh-webpack-plugin@0.5.15(@types/webpack@5.28.5(@swc/core@1.11.10(@swc/helpers@0.5.15)))(react-refresh@0.11.0)(type-fest@3.13.1)(webpack-dev-server@4.15.2(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))))(webpack-hot-middleware@2.26.1)(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15)))': + dependencies: + ansi-html: 0.0.9 + core-js-pure: 3.38.1 + error-stack-parser: 2.1.4 + html-entities: 2.5.2 + loader-utils: 2.0.4 + react-refresh: 0.11.0 + schema-utils: 4.2.0 + source-map: 0.7.4 + webpack: 5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(esbuild@0.25.6) + optionalDependencies: + '@types/webpack': 5.28.5(@swc/core@1.11.10(@swc/helpers@0.5.15)) + type-fest: 3.13.1 + webpack-dev-server: 4.15.2(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))) + webpack-hot-middleware: 2.26.1 + + '@polka/url@1.0.0-next.28': {} + + '@prisma/client@6.5.0(prisma@6.5.0(typescript@5.6.3))(typescript@5.6.3)': + optionalDependencies: + prisma: 6.5.0(typescript@5.6.3) + typescript: 5.6.3 + + '@prisma/config@6.5.0': + dependencies: + esbuild: 0.23.1 + esbuild-register: 3.6.0(esbuild@0.23.1) + transitivePeerDependencies: + - supports-color + + '@prisma/debug@3.8.1': + dependencies: + '@types/debug': 4.1.7 + ms: 2.1.3 + strip-ansi: 6.0.1 + + '@prisma/debug@6.5.0': {} + + '@prisma/engines-version@6.5.0-73.173f8d54f8d52e692c7e27e72a88314ec7aeff60': {} + + '@prisma/engines@6.5.0': + dependencies: + '@prisma/debug': 6.5.0 + '@prisma/engines-version': 6.5.0-73.173f8d54f8d52e692c7e27e72a88314ec7aeff60 + '@prisma/fetch-engine': 6.5.0 + '@prisma/get-platform': 6.5.0 + + '@prisma/fetch-engine@6.5.0': + dependencies: + '@prisma/debug': 6.5.0 + '@prisma/engines-version': 6.5.0-73.173f8d54f8d52e692c7e27e72a88314ec7aeff60 + '@prisma/get-platform': 6.5.0 + + '@prisma/generator-helper@3.8.1': + dependencies: + '@prisma/debug': 3.8.1 + '@types/cross-spawn': 6.0.2 + chalk: 4.1.2 + cross-spawn: 7.0.6 + + '@prisma/get-platform@6.5.0': + dependencies: + '@prisma/debug': 6.5.0 + + '@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': + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/inquire': 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': {} + + '@radix-ui/colors@3.0.0': {} + + '@radix-ui/primitive@1.1.2': {} + + '@radix-ui/react-arrow@1.1.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/react-primitive': 2.1.0(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.10 + '@types/react-dom': 19.0.4(@types/react@19.0.10) + + '@radix-ui/react-collapsible@1.1.7(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-presence': 1.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-primitive': 2.1.0(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.0.10)(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.10 + '@types/react-dom': 19.0.4(@types/react@19.0.10) + + '@radix-ui/react-collection@1.1.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-primitive': 2.1.0(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-slot': 1.2.0(@types/react@19.0.10)(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.10 + '@types/react-dom': 19.0.4(@types/react@19.0.10) + + '@radix-ui/react-compose-refs@1.1.2(@types/react@19.0.10)(react@19.0.0)': + dependencies: + react: 19.0.0 + optionalDependencies: + '@types/react': 19.0.10 + + '@radix-ui/react-context@1.1.2(@types/react@19.0.10)(react@19.0.0)': + dependencies: + react: 19.0.0 + optionalDependencies: + '@types/react': 19.0.10 + + '@radix-ui/react-direction@1.1.1(@types/react@19.0.10)(react@19.0.0)': + dependencies: + react: 19.0.0 + optionalDependencies: + '@types/react': 19.0.10 + + '@radix-ui/react-dismissable-layer@1.1.7(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-primitive': 2.1.0(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.0.10)(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.10 + '@types/react-dom': 19.0.4(@types/react@19.0.10) + + '@radix-ui/react-dropdown-menu@2.1.10(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-menu': 2.1.10(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-primitive': 2.1.0(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.0.10)(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.10 + '@types/react-dom': 19.0.4(@types/react@19.0.10) + + '@radix-ui/react-focus-guards@1.1.2(@types/react@19.0.10)(react@19.0.0)': + dependencies: + react: 19.0.0 + optionalDependencies: + '@types/react': 19.0.10 + + '@radix-ui/react-focus-scope@1.1.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-primitive': 2.1.0(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.0.10)(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.10 + '@types/react-dom': 19.0.4(@types/react@19.0.10) + + '@radix-ui/react-id@1.1.1(@types/react@19.0.10)(react@19.0.0)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.0.10)(react@19.0.0) + react: 19.0.0 + optionalDependencies: + '@types/react': 19.0.10 + + '@radix-ui/react-menu@2.1.10(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-collection': 1.1.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-dismissable-layer': 1.1.7(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-focus-guards': 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-focus-scope': 1.1.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-popper': 1.2.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-portal': 1.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-presence': 1.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-primitive': 2.1.0(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-roving-focus': 1.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-slot': 1.2.0(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.0.10)(react@19.0.0) + aria-hidden: 1.2.6 + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + react-remove-scroll: 2.7.1(@types/react@19.0.10)(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.10 + '@types/react-dom': 19.0.4(@types/react@19.0.10) + + '@radix-ui/react-popover@1.1.10(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-dismissable-layer': 1.1.7(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-focus-guards': 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-focus-scope': 1.1.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-popper': 1.2.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-portal': 1.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-presence': 1.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-primitive': 2.1.0(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-slot': 1.2.0(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.0.10)(react@19.0.0) + aria-hidden: 1.2.6 + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + react-remove-scroll: 2.7.1(@types/react@19.0.10)(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.10 + '@types/react-dom': 19.0.4(@types/react@19.0.10) + + '@radix-ui/react-popper@1.2.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@floating-ui/react-dom': 2.1.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-arrow': 1.1.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-primitive': 2.1.0(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-use-rect': 1.1.1(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/rect': 1.1.1 + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.10 + '@types/react-dom': 19.0.4(@types/react@19.0.10) + + '@radix-ui/react-portal@1.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/react-primitive': 2.1.0(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.0.10)(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.10 + '@types/react-dom': 19.0.4(@types/react@19.0.10) + + '@radix-ui/react-presence@1.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.0.10)(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.10 + '@types/react-dom': 19.0.4(@types/react@19.0.10) + + '@radix-ui/react-primitive@2.1.0(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/react-slot': 1.2.0(@types/react@19.0.10)(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.10 + '@types/react-dom': 19.0.4(@types/react@19.0.10) + + '@radix-ui/react-roving-focus@1.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-collection': 1.1.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-primitive': 2.1.0(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.0.10)(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.10 + '@types/react-dom': 19.0.4(@types/react@19.0.10) + + '@radix-ui/react-slot@1.2.0(@types/react@19.0.10)(react@19.0.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.10)(react@19.0.0) + react: 19.0.0 + optionalDependencies: + '@types/react': 19.0.10 + + '@radix-ui/react-tabs@1.1.7(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-context': 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-presence': 1.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-primitive': 2.1.0(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-roving-focus': 1.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.0.10)(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.10 + '@types/react-dom': 19.0.4(@types/react@19.0.10) + + '@radix-ui/react-toggle-group@1.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-context': 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-primitive': 2.1.0(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-roving-focus': 1.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-toggle': 1.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.0.10)(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.10 + '@types/react-dom': 19.0.4(@types/react@19.0.10) + + '@radix-ui/react-toggle@1.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-primitive': 2.1.0(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.0.10)(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.10 + '@types/react-dom': 19.0.4(@types/react@19.0.10) + + '@radix-ui/react-tooltip@1.2.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-dismissable-layer': 1.1.7(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-popper': 1.2.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-portal': 1.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-presence': 1.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-primitive': 2.1.0(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-slot': 1.2.0(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-visually-hidden': 1.2.0(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.10 + '@types/react-dom': 19.0.4(@types/react@19.0.10) + + '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.0.10)(react@19.0.0)': + dependencies: + react: 19.0.0 + optionalDependencies: + '@types/react': 19.0.10 + + '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.0.10)(react@19.0.0)': + dependencies: + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.0.10)(react@19.0.0) + react: 19.0.0 + optionalDependencies: + '@types/react': 19.0.10 + + '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.0.10)(react@19.0.0)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.0.10)(react@19.0.0) + react: 19.0.0 + optionalDependencies: + '@types/react': 19.0.10 + + '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.0.10)(react@19.0.0)': + dependencies: + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.0.10)(react@19.0.0) + react: 19.0.0 + optionalDependencies: + '@types/react': 19.0.10 + + '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.0.10)(react@19.0.0)': + dependencies: + react: 19.0.0 + optionalDependencies: + '@types/react': 19.0.10 + + '@radix-ui/react-use-rect@1.1.1(@types/react@19.0.10)(react@19.0.0)': + dependencies: + '@radix-ui/rect': 1.1.1 + react: 19.0.0 + optionalDependencies: + '@types/react': 19.0.10 + + '@radix-ui/react-use-size@1.1.1(@types/react@19.0.10)(react@19.0.0)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.0.10)(react@19.0.0) + react: 19.0.0 + optionalDependencies: + '@types/react': 19.0.10 + + '@radix-ui/react-visually-hidden@1.2.0(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/react-primitive': 2.1.0(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.10 + '@types/react-dom': 19.0.4(@types/react@19.0.10) + + '@radix-ui/rect@1.1.1': {} + + '@rc-component/async-validator@5.0.4': + dependencies: + '@babel/runtime': 7.26.0 + + '@rc-component/color-picker@2.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@ant-design/fast-color': 2.0.6 + '@babel/runtime': 7.26.0 + classnames: 2.5.1 + rc-util: 5.43.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@rc-component/context@1.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.26.0 + rc-util: 5.43.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@rc-component/mini-decimal@1.1.0': + dependencies: + '@babel/runtime': 7.26.0 + + '@rc-component/mutate-observer@1.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.26.0 + classnames: 2.5.1 + rc-util: 5.43.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@rc-component/portal@1.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.26.0 + classnames: 2.5.1 + rc-util: 5.43.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@rc-component/qrcode@1.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.26.0 + classnames: 2.5.1 + rc-util: 5.43.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@rc-component/tour@1.15.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.26.0 + '@rc-component/portal': 1.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@rc-component/trigger': 2.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + classnames: 2.5.1 + rc-util: 5.43.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@rc-component/trigger@2.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.26.0 + '@rc-component/portal': 1.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + classnames: 2.5.1 + rc-motion: 2.9.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-resize-observer: 1.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.43.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@react-email/body@0.0.11(react@18.3.1)': + dependencies: + react: 18.3.1 + + '@react-email/button@0.2.0(react@18.3.1)': + dependencies: + react: 18.3.1 + + '@react-email/code-block@0.1.0(react@18.3.1)': + dependencies: + prismjs: 1.30.0 + react: 18.3.1 + + '@react-email/code-inline@0.0.5(react@18.3.1)': + dependencies: + react: 18.3.1 + + '@react-email/column@0.0.13(react@18.3.1)': + dependencies: + react: 18.3.1 + + '@react-email/components@0.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@react-email/body': 0.0.11(react@18.3.1) + '@react-email/button': 0.2.0(react@18.3.1) + '@react-email/code-block': 0.1.0(react@18.3.1) + '@react-email/code-inline': 0.0.5(react@18.3.1) + '@react-email/column': 0.0.13(react@18.3.1) + '@react-email/container': 0.0.15(react@18.3.1) + '@react-email/font': 0.0.9(react@18.3.1) + '@react-email/head': 0.0.12(react@18.3.1) + '@react-email/heading': 0.0.15(react@18.3.1) + '@react-email/hr': 0.0.11(react@18.3.1) + '@react-email/html': 0.0.11(react@18.3.1) + '@react-email/img': 0.0.11(react@18.3.1) + '@react-email/link': 0.0.12(react@18.3.1) + '@react-email/markdown': 0.0.15(react@18.3.1) + '@react-email/preview': 0.0.13(react@18.3.1) + '@react-email/render': 1.1.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@react-email/row': 0.0.12(react@18.3.1) + '@react-email/section': 0.0.16(react@18.3.1) + '@react-email/tailwind': 1.2.1(react@18.3.1) + '@react-email/text': 0.1.5(react@18.3.1) + react: 18.3.1 + transitivePeerDependencies: + - react-dom + + '@react-email/container@0.0.15(react@18.3.1)': + dependencies: + react: 18.3.1 + + '@react-email/font@0.0.9(react@18.3.1)': + dependencies: + react: 18.3.1 + + '@react-email/head@0.0.12(react@18.3.1)': + dependencies: + react: 18.3.1 + + '@react-email/heading@0.0.15(react@18.3.1)': + dependencies: + react: 18.3.1 + + '@react-email/hr@0.0.11(react@18.3.1)': + dependencies: + react: 18.3.1 + + '@react-email/html@0.0.11(react@18.3.1)': + dependencies: + react: 18.3.1 + + '@react-email/img@0.0.11(react@18.3.1)': + dependencies: + react: 18.3.1 + + '@react-email/link@0.0.12(react@18.3.1)': + dependencies: + react: 18.3.1 + + '@react-email/markdown@0.0.15(react@18.3.1)': + dependencies: + md-to-react-email: 5.0.5(react@18.3.1) + react: 18.3.1 + + '@react-email/preview-server@4.1.3(@opentelemetry/api@1.9.0)(@playwright/test@1.39.0)(@swc/core@1.11.10(@swc/helpers@0.5.15))(postcss@8.4.47)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3))': + dependencies: + '@babel/core': 7.26.10 + '@babel/parser': 7.28.0 + '@babel/traverse': 7.28.0 + '@lottiefiles/dotlottie-react': 0.13.3(react@19.0.0) + '@radix-ui/colors': 3.0.0 + '@radix-ui/react-collapsible': 1.1.7(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-dropdown-menu': 2.1.10(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-popover': 1.1.10(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-slot': 1.2.0(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-tabs': 1.1.7(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-toggle-group': 1.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-tooltip': 1.2.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@types/node': 22.14.1 + '@types/normalize-path': 3.0.2 + '@types/react': 19.0.10 + '@types/react-dom': 19.0.4(@types/react@19.0.10) + '@types/webpack': 5.28.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(esbuild@0.25.6) + autoprefixer: 10.4.21(postcss@8.4.47) + chalk: 4.1.2 + clsx: 2.1.1 + esbuild: 0.25.6 + framer-motion: 12.7.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + json5: 2.2.3 + log-symbols: 4.1.0 + module-punycode: punycode@2.3.1 + next: 15.5.4(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.39.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + node-html-parser: 7.0.1 + ora: 5.4.1 + pretty-bytes: 6.1.1 + prism-react-renderer: 2.4.1(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + sharp: 0.34.1 + socket.io-client: 4.8.1 + sonner: 2.0.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + source-map-js: 1.2.1 + spamc: 0.0.5 + stacktrace-parser: 0.1.11 + tailwind-merge: 3.2.0 + tailwindcss: 3.4.0(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)) + use-debounce: 10.0.4(react@19.0.0) + zod: 3.24.3 + transitivePeerDependencies: + - '@emotion/is-prop-valid' + - '@opentelemetry/api' + - '@playwright/test' + - '@swc/core' + - babel-plugin-macros + - babel-plugin-react-compiler + - bufferutil + - postcss + - sass + - supports-color + - ts-node + - uglify-js + - utf-8-validate + - webpack-cli + + '@react-email/preview@0.0.13(react@18.3.1)': + dependencies: + react: 18.3.1 + + '@react-email/render@0.0.17(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + html-to-text: 9.0.5 + js-beautify: 1.15.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-promise-suspense: 0.3.4 + + '@react-email/render@1.1.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + html-to-text: 9.0.5 + prettier: 3.6.2 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-promise-suspense: 0.3.4 + + '@react-email/row@0.0.12(react@18.3.1)': + dependencies: + react: 18.3.1 + + '@react-email/section@0.0.16(react@18.3.1)': + dependencies: + react: 18.3.1 + + '@react-email/tailwind@1.2.1(react@18.3.1)': + dependencies: + react: 18.3.1 + + '@react-email/text@0.1.5(react@18.3.1)': + dependencies: + react: 18.3.1 + + '@remix-run/router@1.20.0': {} + + '@rjsf/antd@5.22.4(@ant-design/icons@5.5.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@rjsf/core@5.22.4(@rjsf/utils@5.22.4(react@18.3.1))(react@18.3.1))(@rjsf/utils@5.22.4(react@18.3.1))(antd@5.22.0(date-fns@2.30.0)(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(dayjs@1.11.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@ant-design/icons': 5.5.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@rjsf/core': 5.22.4(@rjsf/utils@5.22.4(react@18.3.1))(react@18.3.1) + '@rjsf/utils': 5.22.4(react@18.3.1) + antd: 5.22.0(date-fns@2.30.0)(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + classnames: 2.5.1 + dayjs: 1.11.13 + lodash: 4.17.21 + lodash-es: 4.17.21 + rc-picker: 2.7.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + transitivePeerDependencies: + - react-dom + + '@rjsf/core@5.22.4(@rjsf/utils@5.22.4(react@18.3.1))(react@18.3.1)': + dependencies: + '@rjsf/utils': 5.22.4(react@18.3.1) + lodash: 4.17.21 + lodash-es: 4.17.21 + markdown-to-jsx: 7.5.0(react@18.3.1) + nanoid: 3.3.8 + prop-types: 15.8.1 + react: 18.3.1 + + '@rjsf/utils@5.22.4(react@18.3.1)': + dependencies: + json-schema-merge-allof: 0.8.1 + jsonpointer: 5.0.1 + lodash: 4.17.21 + lodash-es: 4.17.21 + react: 18.3.1 + react-is: 18.3.1 + + '@rjsf/validator-ajv8@5.22.4(@rjsf/utils@5.22.4(react@18.3.1))': + dependencies: + '@rjsf/utils': 5.22.4(react@18.3.1) + ajv: 8.17.1 + ajv-formats: 2.1.1(ajv@8.17.1) + lodash: 4.17.21 + lodash-es: 4.17.21 + + '@rollup/plugin-alias@3.1.9(rollup@2.79.2)': + dependencies: + rollup: 2.79.2 + slash: 3.0.0 + + '@rollup/plugin-babel@5.3.1(@babel/core@7.26.0)(@types/babel__core@7.20.5)(rollup@2.79.2)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-module-imports': 7.25.9 + '@rollup/pluginutils': 3.1.0(rollup@2.79.2) + rollup: 2.79.2 + optionalDependencies: + '@types/babel__core': 7.20.5 + transitivePeerDependencies: + - supports-color + + '@rollup/plugin-commonjs@17.1.0(rollup@2.79.2)': + dependencies: + '@rollup/pluginutils': 3.1.0(rollup@2.79.2) + commondir: 1.0.1 + estree-walker: 2.0.2 + glob: 7.2.3 + is-reference: 1.2.1 + magic-string: 0.25.9 + resolve: 1.22.8 + rollup: 2.79.2 + + '@rollup/plugin-commonjs@28.0.2(rollup@3.29.5)': + dependencies: + '@rollup/pluginutils': 5.1.3(rollup@3.29.5) + commondir: 1.0.1 + estree-walker: 2.0.2 + fdir: 6.4.2(picomatch@4.0.2) + is-reference: 1.2.1 + magic-string: 0.30.12 + picomatch: 4.0.2 + optionalDependencies: + rollup: 3.29.5 + + '@rollup/plugin-commonjs@28.0.2(rollup@4.24.3)': + dependencies: + '@rollup/pluginutils': 5.1.3(rollup@4.24.3) + commondir: 1.0.1 + estree-walker: 2.0.2 + fdir: 6.4.2(picomatch@4.0.2) + is-reference: 1.2.1 + magic-string: 0.30.12 + picomatch: 4.0.2 + optionalDependencies: + rollup: 4.24.3 + + '@rollup/plugin-json@4.1.0(rollup@2.79.2)': + dependencies: + '@rollup/pluginutils': 3.1.0(rollup@2.79.2) + rollup: 2.79.2 + + '@rollup/plugin-json@5.0.2(rollup@3.29.5)': + dependencies: + '@rollup/pluginutils': 5.1.3(rollup@3.29.5) + optionalDependencies: + rollup: 3.29.5 + + '@rollup/plugin-json@6.1.0(rollup@3.29.5)': + dependencies: + '@rollup/pluginutils': 5.1.3(rollup@3.29.5) + optionalDependencies: + rollup: 3.29.5 + + '@rollup/plugin-multi-entry@6.0.1(rollup@3.29.5)': + dependencies: + '@rollup/plugin-virtual': 3.0.2(rollup@3.29.5) + matched: 5.0.1 + optionalDependencies: + rollup: 3.29.5 + + '@rollup/plugin-node-resolve@11.2.1(rollup@2.79.2)': + dependencies: + '@rollup/pluginutils': 3.1.0(rollup@2.79.2) + '@types/resolve': 1.17.1 + builtin-modules: 3.3.0 + deepmerge: 4.3.1 + is-module: 1.0.0 + resolve: 1.22.8 + rollup: 2.79.2 + + '@rollup/plugin-node-resolve@16.0.0(rollup@3.29.5)': + dependencies: + '@rollup/pluginutils': 5.1.3(rollup@3.29.5) + '@types/resolve': 1.20.2 + deepmerge: 4.3.1 + is-module: 1.0.0 + resolve: 1.22.8 + optionalDependencies: + rollup: 3.29.5 + + '@rollup/plugin-node-resolve@16.0.0(rollup@4.24.3)': + dependencies: + '@rollup/pluginutils': 5.1.3(rollup@4.24.3) + '@types/resolve': 1.20.2 + deepmerge: 4.3.1 + is-module: 1.0.0 + resolve: 1.22.8 + optionalDependencies: + rollup: 4.24.3 + + '@rollup/plugin-replace@2.4.2(rollup@2.79.2)': + dependencies: + '@rollup/pluginutils': 3.1.0(rollup@2.79.2) + magic-string: 0.25.9 + rollup: 2.79.2 + + '@rollup/plugin-replace@5.0.7(rollup@3.29.5)': + dependencies: + '@rollup/pluginutils': 5.1.3(rollup@3.29.5) + magic-string: 0.30.12 + optionalDependencies: + rollup: 3.29.5 + + '@rollup/plugin-terser@0.1.0(rollup@3.29.5)': + dependencies: + terser: 5.36.0 + optionalDependencies: + rollup: 3.29.5 + + '@rollup/plugin-terser@0.4.4(rollup@3.29.5)': + dependencies: + serialize-javascript: 6.0.2 + smob: 1.5.0 + terser: 5.36.0 + optionalDependencies: + rollup: 3.29.5 + + '@rollup/plugin-typescript@11.1.6(rollup@3.29.5)(tslib@2.8.0)(typescript@5.6.3)': + dependencies: + '@rollup/pluginutils': 5.1.3(rollup@3.29.5) + resolve: 1.22.8 + typescript: 5.6.3 + optionalDependencies: + rollup: 3.29.5 + tslib: 2.8.0 + + '@rollup/plugin-typescript@11.1.6(rollup@4.24.3)(tslib@2.8.0)(typescript@5.6.3)': + dependencies: + '@rollup/pluginutils': 5.1.3(rollup@4.24.3) + resolve: 1.22.8 + typescript: 5.6.3 + optionalDependencies: + rollup: 4.24.3 + tslib: 2.8.0 + + '@rollup/plugin-virtual@3.0.2(rollup@3.29.5)': + optionalDependencies: + rollup: 3.29.5 + + '@rollup/pluginutils@3.1.0(rollup@2.79.2)': + dependencies: + '@types/estree': 0.0.39 + estree-walker: 1.0.1 + picomatch: 2.3.1 + rollup: 2.79.2 + + '@rollup/pluginutils@4.2.1': + dependencies: + estree-walker: 2.0.2 + picomatch: 2.3.1 + + '@rollup/pluginutils@5.1.3(rollup@3.29.5)': + dependencies: + '@types/estree': 1.0.6 + estree-walker: 2.0.2 + picomatch: 4.0.2 + optionalDependencies: + rollup: 3.29.5 + + '@rollup/pluginutils@5.1.3(rollup@4.24.3)': + dependencies: + '@types/estree': 1.0.6 + estree-walker: 2.0.2 + picomatch: 4.0.2 + optionalDependencies: + rollup: 4.24.3 + + '@rollup/rollup-android-arm-eabi@4.24.3': + optional: true + + '@rollup/rollup-android-arm64@4.24.3': + optional: true + + '@rollup/rollup-darwin-arm64@4.24.3': + optional: true + + '@rollup/rollup-darwin-x64@4.24.3': + optional: true + + '@rollup/rollup-freebsd-arm64@4.24.3': + optional: true + + '@rollup/rollup-freebsd-x64@4.24.3': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.24.3': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.24.3': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.24.3': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.24.3': + optional: true + + '@rollup/rollup-linux-powerpc64le-gnu@4.24.3': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.24.3': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.24.3': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.24.3': + optional: true + + '@rollup/rollup-linux-x64-musl@4.24.3': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.24.3': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.24.3': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.24.3': + optional: true + + '@rtsao/scc@1.1.0': {} + + '@rushstack/eslint-patch@1.10.4': {} + + '@segment/analytics-core@1.8.0': + dependencies: + '@lukeed/uuid': 2.0.1 + '@segment/analytics-generic-utils': 1.2.0 + dset: 3.1.4 + tslib: 2.8.0 + + '@segment/analytics-generic-utils@1.2.0': + dependencies: + tslib: 2.8.0 + + '@segment/analytics-next@1.75.0(encoding@0.1.13)': + dependencies: + '@lukeed/uuid': 2.0.1 + '@segment/analytics-core': 1.8.0 + '@segment/analytics-generic-utils': 1.2.0 + '@segment/analytics.js-video-plugins': 0.2.1 + '@segment/facade': 3.4.10 + dset: 3.1.4 + js-cookie: 3.0.1 + node-fetch: 2.7.0(encoding@0.1.13) + tslib: 2.8.0 + unfetch: 4.2.0 + transitivePeerDependencies: + - encoding + + '@segment/analytics.js-video-plugins@0.2.1': + dependencies: + unfetch: 3.1.2 + + '@segment/facade@3.4.10': + dependencies: + '@segment/isodate-traverse': 1.1.1 + inherits: 2.0.4 + new-date: 1.0.3 + obj-case: 0.2.1 + + '@segment/isodate-traverse@1.1.1': + dependencies: + '@segment/isodate': 1.0.3 + + '@segment/isodate@1.0.3': {} + + '@selderee/plugin-htmlparser2@0.11.0': + dependencies: + domhandler: 5.0.3 + selderee: 0.11.0 + + '@sigstore/bundle@1.1.0': + dependencies: + '@sigstore/protobuf-specs': 0.2.1 + + '@sigstore/protobuf-specs@0.2.1': {} + + '@sigstore/sign@1.0.0': + dependencies: + '@sigstore/bundle': 1.1.0 + '@sigstore/protobuf-specs': 0.2.1 + make-fetch-happen: 11.1.1 + transitivePeerDependencies: + - supports-color + + '@sigstore/tuf@1.0.3': + dependencies: + '@sigstore/protobuf-specs': 0.2.1 + tuf-js: 1.1.7 + transitivePeerDependencies: + - supports-color + + '@sinclair/typebox@0.24.51': {} + + '@sinclair/typebox@0.27.8': {} + + '@sindresorhus/merge-streams@2.3.0': {} + + '@sinonjs/commons@1.8.6': + dependencies: + type-detect: 4.0.8 + + '@sinonjs/commons@3.0.1': + dependencies: + type-detect: 4.0.8 + + '@sinonjs/fake-timers@10.3.0': + dependencies: + '@sinonjs/commons': 3.0.1 + + '@sinonjs/fake-timers@8.1.0': + dependencies: + '@sinonjs/commons': 1.8.6 + + '@sinonjs/fake-timers@9.1.2': + dependencies: + '@sinonjs/commons': 1.8.6 + + '@smithy/abort-controller@3.1.6': + dependencies: + '@smithy/types': 3.6.0 + tslib: 2.8.0 + + '@smithy/chunked-blob-reader-native@3.0.1': + dependencies: + '@smithy/util-base64': 3.0.0 + tslib: 2.8.0 + + '@smithy/chunked-blob-reader@4.0.0': + dependencies: + tslib: 2.8.0 + + '@smithy/config-resolver@3.0.10': + dependencies: + '@smithy/node-config-provider': 3.1.9 + '@smithy/types': 3.6.0 + '@smithy/util-config-provider': 3.0.0 + '@smithy/util-middleware': 3.0.8 + tslib: 2.8.0 + + '@smithy/core@2.5.1': + dependencies: + '@smithy/middleware-serde': 3.0.8 + '@smithy/protocol-http': 4.1.5 + '@smithy/types': 3.6.0 + '@smithy/util-body-length-browser': 3.0.0 + '@smithy/util-middleware': 3.0.8 + '@smithy/util-stream': 3.2.1 + '@smithy/util-utf8': 3.0.0 + tslib: 2.8.0 + + '@smithy/credential-provider-imds@3.2.5': + dependencies: + '@smithy/node-config-provider': 3.1.9 + '@smithy/property-provider': 3.1.8 + '@smithy/types': 3.6.0 + '@smithy/url-parser': 3.0.8 + tslib: 2.8.0 + + '@smithy/eventstream-codec@3.1.7': + dependencies: + '@aws-crypto/crc32': 5.2.0 + '@smithy/types': 3.6.0 + '@smithy/util-hex-encoding': 3.0.0 + tslib: 2.8.0 + + '@smithy/eventstream-serde-browser@3.0.11': + dependencies: + '@smithy/eventstream-serde-universal': 3.0.10 + '@smithy/types': 3.6.0 + tslib: 2.8.0 + + '@smithy/eventstream-serde-config-resolver@3.0.8': + dependencies: + '@smithy/types': 3.6.0 + tslib: 2.8.0 + + '@smithy/eventstream-serde-node@3.0.10': + dependencies: + '@smithy/eventstream-serde-universal': 3.0.10 + '@smithy/types': 3.6.0 + tslib: 2.8.0 + + '@smithy/eventstream-serde-universal@3.0.10': + dependencies: + '@smithy/eventstream-codec': 3.1.7 + '@smithy/types': 3.6.0 + tslib: 2.8.0 + + '@smithy/fetch-http-handler@3.2.9': + dependencies: + '@smithy/protocol-http': 4.1.5 + '@smithy/querystring-builder': 3.0.8 + '@smithy/types': 3.6.0 + '@smithy/util-base64': 3.0.0 + tslib: 2.8.0 + + '@smithy/fetch-http-handler@4.0.0': + dependencies: + '@smithy/protocol-http': 4.1.5 + '@smithy/querystring-builder': 3.0.8 + '@smithy/types': 3.6.0 + '@smithy/util-base64': 3.0.0 + tslib: 2.8.0 + + '@smithy/hash-blob-browser@3.1.7': + dependencies: + '@smithy/chunked-blob-reader': 4.0.0 + '@smithy/chunked-blob-reader-native': 3.0.1 + '@smithy/types': 3.6.0 + tslib: 2.8.0 + + '@smithy/hash-node@3.0.8': + dependencies: + '@smithy/types': 3.6.0 + '@smithy/util-buffer-from': 3.0.0 + '@smithy/util-utf8': 3.0.0 + tslib: 2.8.0 + + '@smithy/hash-stream-node@3.1.7': + dependencies: + '@smithy/types': 3.6.0 + '@smithy/util-utf8': 3.0.0 + tslib: 2.8.0 + + '@smithy/invalid-dependency@3.0.8': + dependencies: + '@smithy/types': 3.6.0 + tslib: 2.8.0 + + '@smithy/is-array-buffer@2.2.0': + dependencies: + tslib: 2.8.0 + + '@smithy/is-array-buffer@3.0.0': + dependencies: + tslib: 2.8.0 + + '@smithy/md5-js@3.0.8': + dependencies: + '@smithy/types': 3.6.0 + '@smithy/util-utf8': 3.0.0 + tslib: 2.8.0 + + '@smithy/middleware-content-length@3.0.10': + dependencies: + '@smithy/protocol-http': 4.1.5 + '@smithy/types': 3.6.0 + tslib: 2.8.0 + + '@smithy/middleware-endpoint@3.2.1': + dependencies: + '@smithy/core': 2.5.1 + '@smithy/middleware-serde': 3.0.8 + '@smithy/node-config-provider': 3.1.9 + '@smithy/shared-ini-file-loader': 3.1.9 + '@smithy/types': 3.6.0 + '@smithy/url-parser': 3.0.8 + '@smithy/util-middleware': 3.0.8 + tslib: 2.8.0 + + '@smithy/middleware-retry@3.0.25': + dependencies: + '@smithy/node-config-provider': 3.1.9 + '@smithy/protocol-http': 4.1.5 + '@smithy/service-error-classification': 3.0.8 + '@smithy/smithy-client': 3.4.2 + '@smithy/types': 3.6.0 + '@smithy/util-middleware': 3.0.8 + '@smithy/util-retry': 3.0.8 + tslib: 2.8.0 + uuid: 9.0.1 + + '@smithy/middleware-serde@3.0.8': + dependencies: + '@smithy/types': 3.6.0 + tslib: 2.8.0 + + '@smithy/middleware-stack@3.0.8': + dependencies: + '@smithy/types': 3.6.0 + tslib: 2.8.0 + + '@smithy/node-config-provider@3.1.9': + dependencies: + '@smithy/property-provider': 3.1.8 + '@smithy/shared-ini-file-loader': 3.1.9 + '@smithy/types': 3.6.0 + tslib: 2.8.0 + + '@smithy/node-http-handler@3.2.5': + dependencies: + '@smithy/abort-controller': 3.1.6 + '@smithy/protocol-http': 4.1.5 + '@smithy/querystring-builder': 3.0.8 + '@smithy/types': 3.6.0 + tslib: 2.8.0 + + '@smithy/property-provider@3.1.8': + dependencies: + '@smithy/types': 3.6.0 + tslib: 2.8.0 + + '@smithy/protocol-http@4.1.5': + dependencies: + '@smithy/types': 3.6.0 + tslib: 2.8.0 + + '@smithy/querystring-builder@3.0.8': + dependencies: + '@smithy/types': 3.6.0 + '@smithy/util-uri-escape': 3.0.0 + tslib: 2.8.0 + + '@smithy/querystring-parser@3.0.8': + dependencies: + '@smithy/types': 3.6.0 + tslib: 2.8.0 + + '@smithy/service-error-classification@3.0.8': + dependencies: + '@smithy/types': 3.6.0 + + '@smithy/shared-ini-file-loader@3.1.9': + dependencies: + '@smithy/types': 3.6.0 + tslib: 2.8.0 + + '@smithy/signature-v4@4.2.1': + dependencies: + '@smithy/is-array-buffer': 3.0.0 + '@smithy/protocol-http': 4.1.5 + '@smithy/types': 3.6.0 + '@smithy/util-hex-encoding': 3.0.0 + '@smithy/util-middleware': 3.0.8 + '@smithy/util-uri-escape': 3.0.0 + '@smithy/util-utf8': 3.0.0 + tslib: 2.8.0 + + '@smithy/smithy-client@3.4.2': + dependencies: + '@smithy/core': 2.5.1 + '@smithy/middleware-endpoint': 3.2.1 + '@smithy/middleware-stack': 3.0.8 + '@smithy/protocol-http': 4.1.5 + '@smithy/types': 3.6.0 + '@smithy/util-stream': 3.2.1 + tslib: 2.8.0 + + '@smithy/types@3.6.0': + dependencies: + tslib: 2.8.0 + + '@smithy/url-parser@3.0.8': + dependencies: + '@smithy/querystring-parser': 3.0.8 + '@smithy/types': 3.6.0 + tslib: 2.8.0 + + '@smithy/util-base64@3.0.0': + dependencies: + '@smithy/util-buffer-from': 3.0.0 + '@smithy/util-utf8': 3.0.0 + tslib: 2.8.0 + + '@smithy/util-body-length-browser@3.0.0': + dependencies: + tslib: 2.8.0 + + '@smithy/util-body-length-node@3.0.0': + dependencies: + tslib: 2.8.0 + + '@smithy/util-buffer-from@2.2.0': + dependencies: + '@smithy/is-array-buffer': 2.2.0 + tslib: 2.8.0 + + '@smithy/util-buffer-from@3.0.0': + dependencies: + '@smithy/is-array-buffer': 3.0.0 + tslib: 2.8.0 + + '@smithy/util-config-provider@3.0.0': + dependencies: + tslib: 2.8.0 + + '@smithy/util-defaults-mode-browser@3.0.25': + dependencies: + '@smithy/property-provider': 3.1.8 + '@smithy/smithy-client': 3.4.2 + '@smithy/types': 3.6.0 + bowser: 2.11.0 + tslib: 2.8.0 + + '@smithy/util-defaults-mode-node@3.0.25': + dependencies: + '@smithy/config-resolver': 3.0.10 + '@smithy/credential-provider-imds': 3.2.5 + '@smithy/node-config-provider': 3.1.9 + '@smithy/property-provider': 3.1.8 + '@smithy/smithy-client': 3.4.2 + '@smithy/types': 3.6.0 + tslib: 2.8.0 + + '@smithy/util-endpoints@2.1.4': + dependencies: + '@smithy/node-config-provider': 3.1.9 + '@smithy/types': 3.6.0 + tslib: 2.8.0 + + '@smithy/util-hex-encoding@3.0.0': + dependencies: + tslib: 2.8.0 + + '@smithy/util-middleware@3.0.8': + dependencies: + '@smithy/types': 3.6.0 + tslib: 2.8.0 + + '@smithy/util-retry@3.0.8': + dependencies: + '@smithy/service-error-classification': 3.0.8 + '@smithy/types': 3.6.0 + tslib: 2.8.0 + + '@smithy/util-stream@3.2.1': + dependencies: + '@smithy/fetch-http-handler': 4.0.0 + '@smithy/node-http-handler': 3.2.5 + '@smithy/types': 3.6.0 + '@smithy/util-base64': 3.0.0 + '@smithy/util-buffer-from': 3.0.0 + '@smithy/util-hex-encoding': 3.0.0 + '@smithy/util-utf8': 3.0.0 + tslib: 2.8.0 + + '@smithy/util-uri-escape@3.0.0': + dependencies: + tslib: 2.8.0 + + '@smithy/util-utf8@2.3.0': + dependencies: + '@smithy/util-buffer-from': 2.2.0 + tslib: 2.8.0 + + '@smithy/util-utf8@3.0.0': + dependencies: + '@smithy/util-buffer-from': 3.0.0 + tslib: 2.8.0 + + '@smithy/util-waiter@3.1.7': + dependencies: + '@smithy/abort-controller': 3.1.6 + '@smithy/types': 3.6.0 + tslib: 2.8.0 + + '@socket.io/component-emitter@3.1.2': {} + + '@surma/rollup-plugin-off-main-thread@2.2.3': + dependencies: + ejs: 3.1.10 + json5: 2.2.3 + magic-string: 0.25.9 + string.prototype.matchall: 4.0.11 + + '@svgr/babel-plugin-add-jsx-attribute@5.4.0': {} + + '@svgr/babel-plugin-remove-jsx-attribute@5.4.0': {} + + '@svgr/babel-plugin-remove-jsx-empty-expression@5.0.1': {} + + '@svgr/babel-plugin-replace-jsx-attribute-value@5.0.1': {} + + '@svgr/babel-plugin-svg-dynamic-title@5.4.0': {} + + '@svgr/babel-plugin-svg-em-dimensions@5.4.0': {} + + '@svgr/babel-plugin-transform-react-native-svg@5.4.0': {} + + '@svgr/babel-plugin-transform-svg-component@5.5.0': {} + + '@svgr/babel-preset@5.5.0': + dependencies: + '@svgr/babel-plugin-add-jsx-attribute': 5.4.0 + '@svgr/babel-plugin-remove-jsx-attribute': 5.4.0 + '@svgr/babel-plugin-remove-jsx-empty-expression': 5.0.1 + '@svgr/babel-plugin-replace-jsx-attribute-value': 5.0.1 + '@svgr/babel-plugin-svg-dynamic-title': 5.4.0 + '@svgr/babel-plugin-svg-em-dimensions': 5.4.0 + '@svgr/babel-plugin-transform-react-native-svg': 5.4.0 + '@svgr/babel-plugin-transform-svg-component': 5.5.0 + + '@svgr/core@5.5.0': + dependencies: + '@svgr/plugin-jsx': 5.5.0 + camelcase: 6.3.0 + cosmiconfig: 7.1.0 + transitivePeerDependencies: + - supports-color + + '@svgr/hast-util-to-babel-ast@5.5.0': + dependencies: + '@babel/types': 7.28.1 + + '@svgr/plugin-jsx@5.5.0': + dependencies: + '@babel/core': 7.26.0 + '@svgr/babel-preset': 5.5.0 + '@svgr/hast-util-to-babel-ast': 5.5.0 + svg-parser: 2.0.4 + transitivePeerDependencies: + - supports-color + + '@svgr/plugin-svgo@5.5.0': + dependencies: + cosmiconfig: 7.1.0 + deepmerge: 4.3.1 + svgo: 1.3.2 + + '@svgr/webpack@5.5.0': + dependencies: + '@babel/core': 7.26.0 + '@babel/plugin-transform-react-constant-elements': 7.25.9(@babel/core@7.26.0) + '@babel/preset-env': 7.26.0(@babel/core@7.26.0) + '@babel/preset-react': 7.25.9(@babel/core@7.26.0) + '@svgr/core': 5.5.0 + '@svgr/plugin-jsx': 5.5.0 + '@svgr/plugin-svgo': 5.5.0 + loader-utils: 2.0.4 + transitivePeerDependencies: + - supports-color + + '@swc/core-darwin-arm64@1.11.10': + optional: true + + '@swc/core-darwin-x64@1.11.10': + optional: true + + '@swc/core-linux-arm-gnueabihf@1.11.10': + optional: true + + '@swc/core-linux-arm64-gnu@1.11.10': + optional: true + + '@swc/core-linux-arm64-musl@1.11.10': + optional: true + + '@swc/core-linux-x64-gnu@1.11.10': + optional: true + + '@swc/core-linux-x64-musl@1.11.10': + optional: true + + '@swc/core-win32-arm64-msvc@1.11.10': + optional: true + + '@swc/core-win32-ia32-msvc@1.11.10': + optional: true + + '@swc/core-win32-x64-msvc@1.11.10': + optional: true + + '@swc/core@1.11.10(@swc/helpers@0.5.15)': + dependencies: + '@swc/counter': 0.1.3 + '@swc/types': 0.1.19 + optionalDependencies: + '@swc/core-darwin-arm64': 1.11.10 + '@swc/core-darwin-x64': 1.11.10 + '@swc/core-linux-arm-gnueabihf': 1.11.10 + '@swc/core-linux-arm64-gnu': 1.11.10 + '@swc/core-linux-arm64-musl': 1.11.10 + '@swc/core-linux-x64-gnu': 1.11.10 + '@swc/core-linux-x64-musl': 1.11.10 + '@swc/core-win32-arm64-msvc': 1.11.10 + '@swc/core-win32-ia32-msvc': 1.11.10 + '@swc/core-win32-x64-msvc': 1.11.10 + '@swc/helpers': 0.5.15 + optional: true + + '@swc/counter@0.1.3': + optional: true + + '@swc/helpers@0.5.15': + dependencies: + tslib: 2.8.0 + + '@swc/types@0.1.19': + dependencies: + '@swc/counter': 0.1.3 + optional: true + + '@tailwindcss/typography@0.5.15(tailwindcss@3.4.14(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)))': + dependencies: + lodash.castarray: 4.4.0 + lodash.isplainobject: 4.0.6 + lodash.merge: 4.6.2 + postcss-selector-parser: 6.0.10 + tailwindcss: 3.4.14(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)) + + '@tanstack/query-core@4.36.1': {} + + '@tanstack/react-query@4.36.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@tanstack/query-core': 4.36.1 + react: 18.3.1 + use-sync-external-store: 1.2.2(react@18.3.1) + optionalDependencies: + react-dom: 18.3.1(react@18.3.1) + + '@testing-library/dom@10.4.0': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/runtime': 7.26.0 + '@types/aria-query': 5.0.4 + aria-query: 5.3.0 + chalk: 4.1.2 + dom-accessibility-api: 0.5.16 + lz-string: 1.5.0 + pretty-format: 27.5.1 + + '@testing-library/dom@8.20.1': + dependencies: + '@babel/code-frame': 7.26.0 + '@babel/runtime': 7.26.0 + '@types/aria-query': 5.0.4 + aria-query: 5.1.3 + chalk: 4.1.2 + dom-accessibility-api: 0.5.16 + lz-string: 1.5.0 + pretty-format: 27.5.1 + + '@testing-library/jest-dom@6.6.2': + dependencies: + '@adobe/css-tools': 4.4.0 + aria-query: 5.3.2 + chalk: 3.0.0 + css.escape: 1.5.1 + dom-accessibility-api: 0.6.3 + lodash: 4.17.21 + redent: 3.0.0 + + '@testing-library/react@13.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.26.0 + '@testing-library/dom': 8.20.1 + '@types/react-dom': 18.3.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@testing-library/react@13.4.0(react-dom@19.0.0(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.26.0 + '@testing-library/dom': 8.20.1 + '@types/react-dom': 18.3.0 + react: 18.3.1 + react-dom: 19.0.0(react@18.3.1) + + '@testing-library/user-event@14.5.2(@testing-library/dom@10.4.0)': + dependencies: + '@testing-library/dom': 10.4.0 + + '@tootallnate/once@1.1.2': {} + + '@tootallnate/once@2.0.0': {} + + '@trysound/sax@0.2.0': {} + + '@ts-morph/common@0.12.3': + dependencies: + fast-glob: 3.3.2 + minimatch: 3.1.2 + mkdirp: 1.0.4 + path-browserify: 1.0.1 + + '@tsconfig/node10@1.0.11': {} + + '@tsconfig/node12@1.0.11': {} + + '@tsconfig/node14@1.0.3': {} + + '@tsconfig/node16@1.0.4': {} + + '@tufjs/canonical-json@1.0.0': {} + + '@tufjs/models@1.0.4': + dependencies: + '@tufjs/canonical-json': 1.0.0 + minimatch: 9.0.5 + + '@types/archiver@5.3.4': + dependencies: + '@types/readdir-glob': 1.1.5 + + '@types/aria-query@5.0.4': {} + + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.26.1 + '@babel/types': 7.26.0 + '@types/babel__generator': 7.6.8 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.20.6 + + '@types/babel__generator@7.6.8': + dependencies: + '@babel/types': 7.28.1 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.28.0 + '@babel/types': 7.28.1 + + '@types/babel__traverse@7.20.6': + dependencies: + '@babel/types': 7.28.1 + + '@types/body-parser@1.19.5': + dependencies: + '@types/connect': 3.4.38 + '@types/node': 18.19.61 + + '@types/bonjour@3.5.13': + dependencies: + '@types/node': 18.19.61 + + '@types/caseless@0.12.5': {} + + '@types/chalk@2.2.4': + dependencies: + chalk: 5.3.0 + + '@types/cli-progress@3.11.6': + dependencies: + '@types/node': 18.19.61 + + '@types/commander@2.12.5': + dependencies: + commander: 11.1.0 + + '@types/connect-history-api-fallback@1.5.4': + dependencies: + '@types/express-serve-static-core': 5.0.1 + '@types/node': 18.19.61 + + '@types/connect@3.4.38': + dependencies: + '@types/node': 18.19.61 + + '@types/cookie@1.0.0': + dependencies: + cookie: 1.0.1 + + '@types/cors@2.8.17': + dependencies: + '@types/node': 18.19.61 + + '@types/cross-spawn@6.0.2': + dependencies: + '@types/node': 18.19.61 + + '@types/crypto-js@4.2.2': {} + + '@types/d3-array@3.2.1': {} + + '@types/d3-color@3.1.3': {} + + '@types/d3-ease@3.0.2': {} + + '@types/d3-interpolate@3.0.4': + dependencies: + '@types/d3-color': 3.1.3 + + '@types/d3-path@3.1.0': {} + + '@types/d3-scale@4.0.8': + dependencies: + '@types/d3-time': 3.0.3 + + '@types/d3-shape@3.1.6': + dependencies: + '@types/d3-path': 3.1.0 + + '@types/d3-time@3.0.3': {} + + '@types/d3-timer@3.0.2': {} + + '@types/debug@4.1.7': + dependencies: + '@types/ms': 0.7.34 + + '@types/dlv@1.1.4': {} + + '@types/docker-modem@3.0.6': + dependencies: + '@types/node': 18.19.61 + '@types/ssh2': 1.15.1 + + '@types/dockerode@3.3.31': + dependencies: + '@types/docker-modem': 3.0.6 + '@types/node': 18.19.61 + '@types/ssh2': 1.15.1 + + '@types/eslint-scope@3.7.7': + dependencies: + '@types/eslint': 8.56.12 + '@types/estree': 1.0.6 + + '@types/eslint@8.56.12': + dependencies: + '@types/estree': 1.0.6 + '@types/json-schema': 7.0.15 + + '@types/estree@0.0.39': {} + + '@types/estree@1.0.6': {} + + '@types/expect@1.20.4': {} + + '@types/express-serve-static-core@4.19.6': + dependencies: + '@types/node': 18.19.61 + '@types/qs': 6.9.16 + '@types/range-parser': 1.2.7 + '@types/send': 0.17.4 + + '@types/express-serve-static-core@5.0.1': + dependencies: + '@types/node': 18.19.61 + '@types/qs': 6.9.16 + '@types/range-parser': 1.2.7 + '@types/send': 0.17.4 + + '@types/express@4.17.21': + dependencies: + '@types/body-parser': 1.19.5 + '@types/express-serve-static-core': 4.19.6 + '@types/qs': 6.9.16 + '@types/serve-static': 1.15.7 + + '@types/glob@7.2.0': + dependencies: + '@types/minimatch': 5.1.2 + '@types/node': 18.19.61 + + '@types/graceful-fs@4.1.9': + dependencies: + '@types/node': 18.19.61 + + '@types/html-minifier-terser@6.1.0': {} + + '@types/http-errors@2.0.4': {} + + '@types/http-proxy@1.17.15': + dependencies: + '@types/node': 18.19.61 + + '@types/inquirer@9.0.7': + dependencies: + '@types/through': 0.0.33 + rxjs: 7.8.1 + + '@types/istanbul-lib-coverage@2.0.6': {} + + '@types/istanbul-lib-report@3.0.3': + dependencies: + '@types/istanbul-lib-coverage': 2.0.6 + + '@types/istanbul-reports@3.0.4': + dependencies: + '@types/istanbul-lib-report': 3.0.3 + + '@types/jest@29.5.14': + dependencies: + expect: 29.7.0 + pretty-format: 29.7.0 + + '@types/js-cookie@2.2.7': {} + + '@types/js-yaml@4.0.9': {} + + '@types/json-schema@7.0.15': {} + + '@types/json5@0.0.29': {} + + '@types/jsonwebtoken@9.0.7': + dependencies: + '@types/node': 18.19.61 + + '@types/lodash@4.17.13': {} + + '@types/long@4.0.2': {} + + '@types/mime@1.3.5': {} + + '@types/minimatch@3.0.5': {} + + '@types/minimatch@5.1.2': {} + + '@types/ms@0.7.34': {} + + '@types/node-fetch@2.6.11': + dependencies: + '@types/node': 18.19.61 + form-data: 4.0.4 + + '@types/node-forge@1.3.11': + dependencies: + '@types/node': 18.19.61 + + '@types/node@15.14.9': {} + + '@types/node@18.19.61': + dependencies: + undici-types: 5.26.5 + + '@types/node@22.13.10': + dependencies: + undici-types: 6.20.0 + + '@types/node@22.14.1': + dependencies: + undici-types: 6.21.0 + + '@types/nodemailer@6.4.17': + dependencies: + '@types/node': 18.19.61 + + '@types/normalize-package-data@2.4.4': {} + + '@types/normalize-path@3.0.2': {} + + '@types/parse-json@4.0.2': {} + + '@types/pegjs@0.10.6': {} + + '@types/pg-cursor@2.7.2': + dependencies: + '@types/node': 18.19.61 + '@types/pg': 8.11.15 + + '@types/pg-promise@5.4.3(pg-query-stream@4.7.1(pg@8.11.6))': + dependencies: + pg-promise: 11.10.1(pg-query-stream@4.7.1(pg@8.11.6)) + transitivePeerDependencies: + - pg-native + - pg-query-stream + + '@types/pg-query-stream@3.4.0(pg@8.11.6)': + dependencies: + pg-query-stream: 4.7.1(pg@8.11.6) + transitivePeerDependencies: + - pg + + '@types/pg@8.11.15': + dependencies: + '@types/node': 18.19.61 + pg-protocol: 1.7.0 + pg-types: 4.0.2 + + '@types/prettier@2.7.3': {} + + '@types/prismjs@1.26.5': {} + + '@types/prop-types@15.7.13': {} + + '@types/q@1.5.8': {} + + '@types/qs@6.9.16': {} + + '@types/range-parser@1.2.7': {} + + '@types/react-dom@18.3.0': + dependencies: + '@types/react': 18.3.3 + + '@types/react-dom@19.0.4(@types/react@19.0.10)': + dependencies: + '@types/react': 19.0.10 + + '@types/react@18.3.3': + dependencies: + '@types/prop-types': 15.7.13 + csstype: 3.1.3 + + '@types/react@19.0.10': + dependencies: + csstype: 3.1.3 + + '@types/readdir-glob@1.1.5': + dependencies: + '@types/node': 18.19.61 + + '@types/request@2.48.12': + dependencies: + '@types/caseless': 0.12.5 + '@types/node': 18.19.61 + '@types/tough-cookie': 4.0.5 + form-data: 4.0.4 + + '@types/resolve@1.17.1': + dependencies: + '@types/node': 18.19.61 + + '@types/resolve@1.20.2': {} + + '@types/retry@0.12.0': {} + + '@types/semver@7.5.8': {} + + '@types/send@0.17.4': + dependencies: + '@types/mime': 1.3.5 + '@types/node': 18.19.61 + + '@types/serve-index@1.9.4': + dependencies: + '@types/express': 4.17.21 + + '@types/serve-static@1.15.7': + dependencies: + '@types/http-errors': 2.0.4 + '@types/node': 18.19.61 + '@types/send': 0.17.4 + + '@types/sockjs@0.3.36': + dependencies: + '@types/node': 18.19.61 + + '@types/ssh2-streams@0.1.12': + dependencies: + '@types/node': 18.19.61 + + '@types/ssh2@0.5.52': + dependencies: + '@types/node': 18.19.61 + '@types/ssh2-streams': 0.1.12 + + '@types/ssh2@1.15.1': + dependencies: + '@types/node': 18.19.61 + + '@types/stack-utils@2.0.3': {} + + '@types/strip-bom@3.0.0': {} + + '@types/strip-json-comments@0.0.30': {} + + '@types/stripe@8.0.417': + dependencies: + stripe: 11.18.0 + + '@types/through@0.0.33': + dependencies: + '@types/node': 18.19.61 + + '@types/tough-cookie@4.0.5': {} + + '@types/trusted-types@2.0.7': {} + + '@types/vinyl@2.0.12': + dependencies: + '@types/expect': 1.20.4 + '@types/node': 18.19.61 + + '@types/web@0.0.152': {} + + '@types/webidl-conversions@7.0.3': {} + + '@types/webpack@5.28.5(@swc/core@1.11.10(@swc/helpers@0.5.15))': + dependencies: + '@types/node': 18.19.61 + tapable: 2.2.1 + webpack: 5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(esbuild@0.25.6) + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + - webpack-cli + optional: true + + '@types/webpack@5.28.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(esbuild@0.25.6)': + dependencies: + '@types/node': 18.19.61 + tapable: 2.2.1 + webpack: 5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(esbuild@0.25.6) + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + - webpack-cli + + '@types/webpack@5.28.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(webpack-cli@5.1.4)': + dependencies: + '@types/node': 18.19.61 + tapable: 2.2.1 + webpack: 5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(webpack-cli@5.1.4) + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + - webpack-cli + + '@types/webpack@5.28.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(webpack-cli@6.0.1(webpack@5.99.5))': + dependencies: + '@types/node': 18.19.61 + tapable: 2.2.1 + webpack: 5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(webpack-cli@6.0.1) + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + - webpack-cli + + '@types/whatwg-url@11.0.5': + dependencies: + '@types/webidl-conversions': 7.0.3 + + '@types/ws@8.5.12': + dependencies: + '@types/node': 18.19.61 + + '@types/yargs-parser@21.0.3': {} + + '@types/yargs@16.0.9': + dependencies: + '@types/yargs-parser': 21.0.3 + + '@types/yargs@17.0.33': + dependencies: + '@types/yargs-parser': 21.0.3 + + '@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1)(typescript@5.6.3)': + dependencies: + '@eslint-community/regexpp': 4.12.1 + '@typescript-eslint/parser': 5.62.0(eslint@8.57.1)(typescript@5.6.3) + '@typescript-eslint/scope-manager': 5.62.0 + '@typescript-eslint/type-utils': 5.62.0(eslint@8.57.1)(typescript@5.6.3) + '@typescript-eslint/utils': 5.62.0(eslint@8.57.1)(typescript@5.6.3) + debug: 4.3.7(supports-color@5.5.0) + eslint: 8.57.1 + graphemer: 1.4.0 + ignore: 5.3.2 + natural-compare-lite: 1.4.0 + semver: 7.7.2 + tsutils: 3.21.0(typescript@5.6.3) + optionalDependencies: + typescript: 5.6.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/eslint-plugin@8.12.2(@typescript-eslint/parser@8.12.2(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1)(typescript@5.6.3)': + dependencies: + '@eslint-community/regexpp': 4.12.1 + '@typescript-eslint/parser': 8.12.2(eslint@8.57.1)(typescript@5.6.3) + '@typescript-eslint/scope-manager': 8.12.2 + '@typescript-eslint/type-utils': 8.12.2(eslint@8.57.1)(typescript@5.6.3) + '@typescript-eslint/utils': 8.12.2(eslint@8.57.1)(typescript@5.6.3) + '@typescript-eslint/visitor-keys': 8.12.2 + eslint: 8.57.1 + graphemer: 1.4.0 + ignore: 5.3.2 + natural-compare: 1.4.0 + ts-api-utils: 1.3.0(typescript@5.6.3) + optionalDependencies: + typescript: 5.6.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/experimental-utils@5.62.0(eslint@8.57.1)(typescript@5.6.3)': + dependencies: + '@typescript-eslint/utils': 5.62.0(eslint@8.57.1)(typescript@5.6.3) + eslint: 8.57.1 + transitivePeerDependencies: + - supports-color + - typescript + + '@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.6.3)': + dependencies: + '@typescript-eslint/scope-manager': 5.62.0 + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.6.3) + debug: 4.3.7(supports-color@5.5.0) + eslint: 8.57.1 + optionalDependencies: + typescript: 5.6.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.12.2(eslint@8.57.1)(typescript@5.6.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.12.2 + '@typescript-eslint/types': 8.12.2 + '@typescript-eslint/typescript-estree': 8.12.2(typescript@5.6.3) + '@typescript-eslint/visitor-keys': 8.12.2 + debug: 4.3.7(supports-color@5.5.0) + eslint: 8.57.1 + optionalDependencies: + typescript: 5.6.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@5.62.0': + dependencies: + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/visitor-keys': 5.62.0 + + '@typescript-eslint/scope-manager@8.12.2': + dependencies: + '@typescript-eslint/types': 8.12.2 + '@typescript-eslint/visitor-keys': 8.12.2 + + '@typescript-eslint/type-utils@5.62.0(eslint@8.57.1)(typescript@5.6.3)': + dependencies: + '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.6.3) + '@typescript-eslint/utils': 5.62.0(eslint@8.57.1)(typescript@5.6.3) + debug: 4.3.7(supports-color@5.5.0) + eslint: 8.57.1 + tsutils: 3.21.0(typescript@5.6.3) + optionalDependencies: + typescript: 5.6.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/type-utils@8.12.2(eslint@8.57.1)(typescript@5.6.3)': + dependencies: + '@typescript-eslint/typescript-estree': 8.12.2(typescript@5.6.3) + '@typescript-eslint/utils': 8.12.2(eslint@8.57.1)(typescript@5.6.3) + debug: 4.3.7(supports-color@5.5.0) + ts-api-utils: 1.3.0(typescript@5.6.3) + optionalDependencies: + typescript: 5.6.3 + transitivePeerDependencies: + - eslint + - supports-color + + '@typescript-eslint/types@5.62.0': {} + + '@typescript-eslint/types@8.12.2': {} + + '@typescript-eslint/typescript-estree@5.62.0(typescript@5.6.3)': + dependencies: + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/visitor-keys': 5.62.0 + debug: 4.3.7(supports-color@5.5.0) + globby: 11.1.0 + is-glob: 4.0.3 + semver: 7.7.2 + tsutils: 3.21.0(typescript@5.6.3) + optionalDependencies: + typescript: 5.6.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/typescript-estree@8.12.2(typescript@5.6.3)': + dependencies: + '@typescript-eslint/types': 8.12.2 + '@typescript-eslint/visitor-keys': 8.12.2 + debug: 4.3.7(supports-color@5.5.0) + fast-glob: 3.3.2 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.7.1 + ts-api-utils: 1.3.0(typescript@5.6.3) + optionalDependencies: + typescript: 5.6.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@5.62.0(eslint@8.57.1)(typescript@5.6.3)': + dependencies: + '@eslint-community/eslint-utils': 4.4.1(eslint@8.57.1) + '@types/json-schema': 7.0.15 + '@types/semver': 7.5.8 + '@typescript-eslint/scope-manager': 5.62.0 + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.6.3) + eslint: 8.57.1 + eslint-scope: 5.1.1 + semver: 7.7.2 + transitivePeerDependencies: + - supports-color + - typescript + + '@typescript-eslint/utils@8.12.2(eslint@8.57.1)(typescript@5.6.3)': + dependencies: + '@eslint-community/eslint-utils': 4.4.1(eslint@8.57.1) + '@typescript-eslint/scope-manager': 8.12.2 + '@typescript-eslint/types': 8.12.2 + '@typescript-eslint/typescript-estree': 8.12.2(typescript@5.6.3) + eslint: 8.57.1 + transitivePeerDependencies: + - supports-color + - typescript + + '@typescript-eslint/visitor-keys@5.62.0': + dependencies: + '@typescript-eslint/types': 5.62.0 + eslint-visitor-keys: 3.4.3 + + '@typescript-eslint/visitor-keys@8.12.2': + dependencies: + '@typescript-eslint/types': 8.12.2 + eslint-visitor-keys: 3.4.3 + + '@ungap/structured-clone@1.2.0': {} + + '@webassemblyjs/ast@1.12.1': + dependencies: + '@webassemblyjs/helper-numbers': 1.11.6 + '@webassemblyjs/helper-wasm-bytecode': 1.11.6 + + '@webassemblyjs/ast@1.14.1': + dependencies: + '@webassemblyjs/helper-numbers': 1.13.2 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + + '@webassemblyjs/floating-point-hex-parser@1.11.6': {} + + '@webassemblyjs/floating-point-hex-parser@1.13.2': {} + + '@webassemblyjs/helper-api-error@1.11.6': {} + + '@webassemblyjs/helper-api-error@1.13.2': {} + + '@webassemblyjs/helper-buffer@1.12.1': {} + + '@webassemblyjs/helper-buffer@1.14.1': {} + + '@webassemblyjs/helper-numbers@1.11.6': + dependencies: + '@webassemblyjs/floating-point-hex-parser': 1.11.6 + '@webassemblyjs/helper-api-error': 1.11.6 + '@xtuc/long': 4.2.2 + + '@webassemblyjs/helper-numbers@1.13.2': + dependencies: + '@webassemblyjs/floating-point-hex-parser': 1.13.2 + '@webassemblyjs/helper-api-error': 1.13.2 + '@xtuc/long': 4.2.2 + + '@webassemblyjs/helper-wasm-bytecode@1.11.6': {} + + '@webassemblyjs/helper-wasm-bytecode@1.13.2': {} + + '@webassemblyjs/helper-wasm-section@1.12.1': + dependencies: + '@webassemblyjs/ast': 1.12.1 + '@webassemblyjs/helper-buffer': 1.12.1 + '@webassemblyjs/helper-wasm-bytecode': 1.11.6 + '@webassemblyjs/wasm-gen': 1.12.1 + + '@webassemblyjs/helper-wasm-section@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/wasm-gen': 1.14.1 + + '@webassemblyjs/ieee754@1.11.6': + dependencies: + '@xtuc/ieee754': 1.2.0 + + '@webassemblyjs/ieee754@1.13.2': + dependencies: + '@xtuc/ieee754': 1.2.0 + + '@webassemblyjs/leb128@1.11.6': + dependencies: + '@xtuc/long': 4.2.2 + + '@webassemblyjs/leb128@1.13.2': + dependencies: + '@xtuc/long': 4.2.2 + + '@webassemblyjs/utf8@1.11.6': {} + + '@webassemblyjs/utf8@1.13.2': {} + + '@webassemblyjs/wasm-edit@1.12.1': + dependencies: + '@webassemblyjs/ast': 1.12.1 + '@webassemblyjs/helper-buffer': 1.12.1 + '@webassemblyjs/helper-wasm-bytecode': 1.11.6 + '@webassemblyjs/helper-wasm-section': 1.12.1 + '@webassemblyjs/wasm-gen': 1.12.1 + '@webassemblyjs/wasm-opt': 1.12.1 + '@webassemblyjs/wasm-parser': 1.12.1 + '@webassemblyjs/wast-printer': 1.12.1 + + '@webassemblyjs/wasm-edit@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/helper-wasm-section': 1.14.1 + '@webassemblyjs/wasm-gen': 1.14.1 + '@webassemblyjs/wasm-opt': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + '@webassemblyjs/wast-printer': 1.14.1 + + '@webassemblyjs/wasm-gen@1.12.1': + dependencies: + '@webassemblyjs/ast': 1.12.1 + '@webassemblyjs/helper-wasm-bytecode': 1.11.6 + '@webassemblyjs/ieee754': 1.11.6 + '@webassemblyjs/leb128': 1.11.6 + '@webassemblyjs/utf8': 1.11.6 + + '@webassemblyjs/wasm-gen@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/ieee754': 1.13.2 + '@webassemblyjs/leb128': 1.13.2 + '@webassemblyjs/utf8': 1.13.2 + + '@webassemblyjs/wasm-opt@1.12.1': + dependencies: + '@webassemblyjs/ast': 1.12.1 + '@webassemblyjs/helper-buffer': 1.12.1 + '@webassemblyjs/wasm-gen': 1.12.1 + '@webassemblyjs/wasm-parser': 1.12.1 + + '@webassemblyjs/wasm-opt@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/wasm-gen': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + + '@webassemblyjs/wasm-parser@1.12.1': + dependencies: + '@webassemblyjs/ast': 1.12.1 + '@webassemblyjs/helper-api-error': 1.11.6 + '@webassemblyjs/helper-wasm-bytecode': 1.11.6 + '@webassemblyjs/ieee754': 1.11.6 + '@webassemblyjs/leb128': 1.11.6 + '@webassemblyjs/utf8': 1.11.6 + + '@webassemblyjs/wasm-parser@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-api-error': 1.13.2 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/ieee754': 1.13.2 + '@webassemblyjs/leb128': 1.13.2 + '@webassemblyjs/utf8': 1.13.2 + + '@webassemblyjs/wast-printer@1.12.1': + dependencies: + '@webassemblyjs/ast': 1.12.1 + '@xtuc/long': 4.2.2 + + '@webassemblyjs/wast-printer@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@xtuc/long': 4.2.2 + + '@webpack-cli/configtest@2.1.1(webpack-cli@5.1.4)(webpack@5.95.0)': + dependencies: + webpack: 5.95.0(@swc/core@1.11.10(@swc/helpers@0.5.15))(webpack-cli@5.1.4) + webpack-cli: 5.1.4(@webpack-cli/generators@3.0.7)(webpack@5.95.0) + + '@webpack-cli/configtest@3.0.1(webpack-cli@6.0.1)(webpack@5.99.5)': + dependencies: + webpack: 5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(webpack-cli@6.0.1) + webpack-cli: 6.0.1(webpack@5.99.5) + + '@webpack-cli/generators@3.0.7(encoding@0.1.13)(mem-fs@2.3.0)(prettier@2.8.8)(webpack-cli@5.1.4)(webpack@5.95.0)': + dependencies: + webpack: 5.95.0(@swc/core@1.11.10(@swc/helpers@0.5.15))(webpack-cli@5.1.4) + webpack-cli: 5.1.4(@webpack-cli/generators@3.0.7)(webpack@5.95.0) + yeoman-environment: 3.19.3 + yeoman-generator: 5.10.0(encoding@0.1.13)(mem-fs@2.3.0)(yeoman-environment@3.19.3) + optionalDependencies: + prettier: 2.8.8 + transitivePeerDependencies: + - bluebird + - encoding + - mem-fs + - supports-color + + '@webpack-cli/generators@3.0.7(encoding@0.1.13)(mem-fs@2.3.0)(prettier@2.8.8)(webpack-cli@6.0.1(webpack@5.99.5))(webpack@5.99.5)': + dependencies: + webpack: 5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(webpack-cli@6.0.1) + webpack-cli: 6.0.1(webpack@5.99.5) + yeoman-environment: 3.19.3 + yeoman-generator: 5.10.0(encoding@0.1.13)(mem-fs@2.3.0)(yeoman-environment@3.19.3) + optionalDependencies: + prettier: 2.8.8 + transitivePeerDependencies: + - bluebird + - encoding + - mem-fs + - supports-color + + '@webpack-cli/info@2.0.2(webpack-cli@5.1.4)(webpack@5.95.0)': + dependencies: + webpack: 5.95.0(@swc/core@1.11.10(@swc/helpers@0.5.15))(webpack-cli@5.1.4) + webpack-cli: 5.1.4(@webpack-cli/generators@3.0.7)(webpack@5.95.0) + + '@webpack-cli/info@3.0.1(webpack-cli@6.0.1)(webpack@5.99.5)': + dependencies: + webpack: 5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(webpack-cli@6.0.1) + webpack-cli: 6.0.1(webpack@5.99.5) + + '@webpack-cli/serve@2.0.5(webpack-cli@5.1.4)(webpack@5.95.0)': + dependencies: + webpack: 5.95.0(@swc/core@1.11.10(@swc/helpers@0.5.15))(webpack-cli@5.1.4) + webpack-cli: 5.1.4(@webpack-cli/generators@3.0.7)(webpack@5.95.0) + + '@webpack-cli/serve@3.0.1(webpack-cli@6.0.1)(webpack@5.99.5)': + dependencies: + webpack: 5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(webpack-cli@6.0.1) + webpack-cli: 6.0.1(webpack@5.99.5) + + '@xobotyi/scrollbar-width@1.9.5': {} + + '@xtuc/ieee754@1.2.0': {} + + '@xtuc/long@4.2.2': {} + + abab@2.0.6: {} + + abbrev@1.1.1: {} + + abbrev@2.0.0: {} + + abort-controller@3.0.0: + dependencies: + event-target-shim: 5.0.1 + + accepts@1.3.8: + dependencies: + mime-types: 2.1.35 + negotiator: 0.6.3 + + acorn-globals@6.0.0: + dependencies: + acorn: 7.4.1 + acorn-walk: 7.2.0 + + acorn-import-attributes@1.9.5(acorn@8.14.0): + dependencies: + acorn: 8.14.0 + + acorn-jsx@5.3.2(acorn@8.14.0): + dependencies: + acorn: 8.14.0 + + acorn-walk@7.2.0: {} + + acorn-walk@8.3.4: + dependencies: + acorn: 8.14.0 + + acorn@7.4.1: {} + + acorn@8.14.0: {} + + address@1.2.2: {} + + adjust-sourcemap-loader@4.0.0: + dependencies: + loader-utils: 2.0.4 + regex-parser: 2.3.0 + + agent-base@6.0.2: + dependencies: + debug: 4.3.7(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + + agent-base@7.1.1: + dependencies: + debug: 4.3.7(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + + agentkeepalive@4.3.0: + dependencies: + debug: 4.3.7(supports-color@5.5.0) + depd: 2.0.0 + humanize-ms: 1.2.1 + transitivePeerDependencies: + - supports-color + + aggregate-error@3.1.0: + dependencies: + clean-stack: 2.2.0 + indent-string: 4.0.0 + + ajv-formats@2.1.1(ajv@8.17.1): + optionalDependencies: + ajv: 8.17.1 + + ajv-keywords@3.5.2(ajv@6.12.6): + dependencies: + ajv: 6.12.6 + + ajv-keywords@5.1.0(ajv@8.17.1): + dependencies: + ajv: 8.17.1 + fast-deep-equal: 3.1.3 + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ajv@8.17.1: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.0.3 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + analytics-utils@1.0.12(@types/dlv@1.1.4): + dependencies: + '@analytics/type-utils': 0.6.2 + '@types/dlv': 1.1.4 + dlv: 1.1.3 + + analytics@0.8.9(@types/dlv@1.1.4): + dependencies: + '@analytics/core': 0.12.15(@types/dlv@1.1.4) + '@analytics/storage-utils': 0.4.2 + transitivePeerDependencies: + - '@types/dlv' + + ansi-align@3.0.1: + dependencies: + string-width: 4.2.3 + + ansi-colors@4.1.3: {} + + ansi-escapes@4.3.2: + dependencies: + type-fest: 0.21.3 + + ansi-html-community@0.0.8: {} + + ansi-html@0.0.9: {} + + ansi-regex@2.1.1: {} + + ansi-regex@5.0.1: {} + + ansi-regex@6.1.0: {} + + ansi-styles@2.2.1: {} + + ansi-styles@3.2.1: + dependencies: + color-convert: 1.9.3 + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@5.2.0: {} + + ansi-styles@6.2.1: {} + + ansi-to-html@0.7.2: + dependencies: + entities: 2.2.0 + + antd@5.22.0(date-fns@2.30.0)(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@ant-design/colors': 7.1.0 + '@ant-design/cssinjs': 1.21.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@ant-design/cssinjs-utils': 1.1.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@ant-design/icons': 5.5.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@ant-design/react-slick': 1.1.2(react@18.3.1) + '@babel/runtime': 7.26.0 + '@ctrl/tinycolor': 3.6.1 + '@rc-component/color-picker': 2.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@rc-component/mutate-observer': 1.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@rc-component/qrcode': 1.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@rc-component/tour': 1.15.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@rc-component/trigger': 2.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + classnames: 2.5.1 + copy-to-clipboard: 3.3.3 + dayjs: 1.11.13 + rc-cascader: 3.30.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-checkbox: 3.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-collapse: 3.9.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-dialog: 9.6.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-drawer: 7.2.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-dropdown: 4.2.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-field-form: 2.5.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-image: 7.11.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-input: 1.6.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-input-number: 9.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-mentions: 2.17.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-menu: 9.16.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-motion: 2.9.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-notification: 5.6.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-pagination: 4.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-picker: 4.7.2(date-fns@2.30.0)(dayjs@1.11.13)(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-progress: 4.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-rate: 2.13.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-resize-observer: 1.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-segmented: 2.5.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-select: 14.16.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-slider: 11.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-steps: 6.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-switch: 4.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-table: 7.48.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-tabs: 15.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-textarea: 1.8.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-tooltip: 6.2.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-tree: 5.10.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-tree-select: 5.24.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-upload: 4.8.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.43.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + scroll-into-view-if-needed: 3.1.0 + throttle-debounce: 5.0.2 + transitivePeerDependencies: + - date-fns + - luxon + - moment + + any-promise@1.3.0: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + + aproba@2.0.0: {} + + archiver-utils@2.1.0: + dependencies: + glob: 7.2.3 + graceful-fs: 4.2.11 + lazystream: 1.0.1 + lodash.defaults: 4.2.0 + lodash.difference: 4.5.0 + lodash.flatten: 4.4.0 + lodash.isplainobject: 4.0.6 + lodash.union: 4.6.0 + normalize-path: 3.0.0 + readable-stream: 2.3.8 + + archiver-utils@3.0.4: + dependencies: + glob: 7.2.3 + graceful-fs: 4.2.11 + lazystream: 1.0.1 + lodash.defaults: 4.2.0 + lodash.difference: 4.5.0 + lodash.flatten: 4.4.0 + lodash.isplainobject: 4.0.6 + lodash.union: 4.6.0 + normalize-path: 3.0.0 + readable-stream: 3.6.2 + + archiver@5.3.2: + dependencies: + archiver-utils: 2.1.0 + async: 3.2.6 + buffer-crc32: 0.2.13 + readable-stream: 3.6.2 + readdir-glob: 1.1.3 + tar-stream: 2.2.0 + zip-stream: 4.1.1 + + are-we-there-yet@2.0.0: + dependencies: + delegates: 1.0.0 + readable-stream: 3.6.2 + + are-we-there-yet@3.0.1: + dependencies: + delegates: 1.0.0 + readable-stream: 3.6.2 + + arg@4.1.3: {} + + arg@5.0.2: {} + + argparse@1.0.10: + dependencies: + sprintf-js: 1.0.3 + + argparse@2.0.1: {} + + aria-hidden@1.2.6: + dependencies: + tslib: 2.8.0 + + aria-query@5.1.3: + dependencies: + deep-equal: 2.2.3 + + aria-query@5.3.0: + dependencies: + dequal: 2.0.3 + + aria-query@5.3.2: {} + + array-buffer-byte-length@1.0.1: + dependencies: + call-bind: 1.0.7 + is-array-buffer: 3.0.4 + + array-differ@3.0.0: {} + + array-flatten@1.1.1: {} + + array-includes@3.1.8: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-object-atoms: 1.0.0 + get-intrinsic: 1.2.4 + is-string: 1.0.7 + + array-union@1.0.2: + dependencies: + array-uniq: 1.0.3 + + array-union@2.1.0: {} + + array-uniq@1.0.3: {} + + array.prototype.findlast@1.2.5: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-errors: 1.3.0 + es-object-atoms: 1.0.0 + es-shim-unscopables: 1.0.2 + + array.prototype.findlastindex@1.2.5: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-shim-unscopables: 1.0.2 + + array.prototype.flat@1.3.2: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-shim-unscopables: 1.0.2 + + array.prototype.flatmap@1.3.2: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-shim-unscopables: 1.0.2 + + array.prototype.reduce@1.0.7: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-array-method-boxes-properly: 1.0.0 + es-errors: 1.3.0 + es-object-atoms: 1.0.0 + is-string: 1.0.7 + + array.prototype.tosorted@1.1.4: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-errors: 1.3.0 + es-shim-unscopables: 1.0.2 + + arraybuffer.prototype.slice@1.0.3: + dependencies: + array-buffer-byte-length: 1.0.1 + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 + is-array-buffer: 3.0.4 + is-shared-array-buffer: 1.0.3 + + arrify@2.0.1: {} + + asap@2.0.6: {} + + asn1@0.2.6: + dependencies: + safer-buffer: 2.1.2 + + assert-options@0.8.2: {} + + assert-plus@1.0.0: {} + + ast-types-flow@0.0.8: {} + + async-lock@1.4.1: {} + + async-retry@1.3.3: + dependencies: + retry: 0.13.1 + optional: true + + async@3.2.6: {} + + asynckit@0.4.0: {} + + asyncro@3.0.0: {} + + at-least-node@1.0.0: {} + + autoprefixer@10.4.20(postcss@8.4.47): + dependencies: + browserslist: 4.24.2 + caniuse-lite: 1.0.30001675 + fraction.js: 4.3.7 + normalize-range: 0.1.2 + picocolors: 1.1.1 + postcss: 8.4.47 + postcss-value-parser: 4.2.0 + + autoprefixer@10.4.21(postcss@8.4.47): + dependencies: + browserslist: 4.25.1 + caniuse-lite: 1.0.30001727 + fraction.js: 4.3.7 + normalize-range: 0.1.2 + picocolors: 1.1.1 + postcss: 8.4.47 + postcss-value-parser: 4.2.0 + + available-typed-arrays@1.0.7: + dependencies: + possible-typed-array-names: 1.0.0 + + axe-core@4.10.2: {} + + axios@1.8.2: + dependencies: + follow-redirects: 1.15.9 + form-data: 4.0.4 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + + axobject-query@4.1.0: {} + + babel-jest@27.5.1(@babel/core@7.26.0): + dependencies: + '@babel/core': 7.26.0 + '@jest/transform': 27.5.1 + '@jest/types': 27.5.1 + '@types/babel__core': 7.20.5 + babel-plugin-istanbul: 6.1.1 + babel-preset-jest: 27.5.1(@babel/core@7.26.0) + chalk: 4.1.2 + graceful-fs: 4.2.11 + slash: 3.0.0 + transitivePeerDependencies: + - supports-color + + babel-jest@28.1.3(@babel/core@7.26.0): + dependencies: + '@babel/core': 7.26.0 + '@jest/transform': 28.1.3 + '@types/babel__core': 7.20.5 + babel-plugin-istanbul: 6.1.1 + babel-preset-jest: 28.1.3(@babel/core@7.26.0) + chalk: 4.1.2 + graceful-fs: 4.2.11 + slash: 3.0.0 + transitivePeerDependencies: + - supports-color + + babel-jest@29.7.0(@babel/core@7.26.0): + dependencies: + '@babel/core': 7.26.0 + '@jest/transform': 29.7.0 + '@types/babel__core': 7.20.5 + babel-plugin-istanbul: 6.1.1 + babel-preset-jest: 29.6.3(@babel/core@7.26.0) + chalk: 4.1.2 + graceful-fs: 4.2.11 + slash: 3.0.0 + transitivePeerDependencies: + - supports-color + + babel-jest@29.7.0(@babel/core@7.26.10): + dependencies: + '@babel/core': 7.26.10 + '@jest/transform': 29.7.0 + '@types/babel__core': 7.20.5 + babel-plugin-istanbul: 6.1.1 + babel-preset-jest: 29.6.3(@babel/core@7.26.10) + chalk: 4.1.2 + graceful-fs: 4.2.11 + slash: 3.0.0 + transitivePeerDependencies: + - supports-color + optional: true + + babel-loader@8.4.1(@babel/core@7.26.0)(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))): + dependencies: + '@babel/core': 7.26.0 + find-cache-dir: 3.3.2 + loader-utils: 2.0.4 + make-dir: 3.1.0 + schema-utils: 2.7.1 + webpack: 5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(esbuild@0.25.6) + + babel-loader@9.2.1(@babel/core@7.26.0)(webpack@5.99.5): + dependencies: + '@babel/core': 7.26.0 + find-cache-dir: 4.0.0 + schema-utils: 4.2.0 + webpack: 5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(webpack-cli@6.0.1) + + babel-loader@9.2.1(@babel/core@7.26.10)(webpack@5.95.0): + dependencies: + '@babel/core': 7.26.10 + find-cache-dir: 4.0.0 + schema-utils: 4.2.0 + webpack: 5.95.0(@swc/core@1.11.10(@swc/helpers@0.5.15))(webpack-cli@5.1.4) + + babel-loader@9.2.1(@babel/core@7.26.10)(webpack@5.99.5): + dependencies: + '@babel/core': 7.26.10 + find-cache-dir: 4.0.0 + schema-utils: 4.2.0 + webpack: 5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(webpack-cli@6.0.1) + + babel-plugin-istanbul@6.1.1: + dependencies: + '@babel/helper-plugin-utils': 7.25.9 + '@istanbuljs/load-nyc-config': 1.1.0 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-instrument: 5.2.1 + test-exclude: 6.0.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-jest-hoist@27.5.1: + dependencies: + '@babel/template': 7.27.2 + '@babel/types': 7.28.1 + '@types/babel__core': 7.20.5 + '@types/babel__traverse': 7.20.6 + + babel-plugin-jest-hoist@28.1.3: + dependencies: + '@babel/template': 7.27.2 + '@babel/types': 7.28.1 + '@types/babel__core': 7.20.5 + '@types/babel__traverse': 7.20.6 + + babel-plugin-jest-hoist@29.6.3: + dependencies: + '@babel/template': 7.27.2 + '@babel/types': 7.28.1 + '@types/babel__core': 7.20.5 + '@types/babel__traverse': 7.20.6 + + babel-plugin-macros@3.1.0: + dependencies: + '@babel/runtime': 7.26.0 + cosmiconfig: 7.1.0 + resolve: 1.22.8 + + babel-plugin-named-asset-import@0.3.8(@babel/core@7.26.0): + dependencies: + '@babel/core': 7.26.0 + + babel-plugin-polyfill-corejs2@0.4.11(@babel/core@7.26.0): + dependencies: + '@babel/compat-data': 7.26.0 + '@babel/core': 7.26.0 + '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.26.0) + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-corejs2@0.4.11(@babel/core@7.26.10): + dependencies: + '@babel/compat-data': 7.26.0 + '@babel/core': 7.26.10 + '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.26.10) + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-corejs3@0.10.6(@babel/core@7.26.0): + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.26.0) + core-js-compat: 3.38.1 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-corejs3@0.10.6(@babel/core@7.26.10): + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.26.10) + core-js-compat: 3.38.1 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-regenerator@0.6.2(@babel/core@7.26.0): + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.26.0) + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-regenerator@0.6.2(@babel/core@7.26.10): + dependencies: + '@babel/core': 7.26.10 + '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.26.10) + transitivePeerDependencies: + - supports-color + + babel-plugin-transform-async-to-promises@0.8.18: {} + + babel-plugin-transform-react-remove-prop-types@0.4.24: {} + + babel-plugin-transform-replace-expressions@0.2.0(@babel/core@7.26.0): + dependencies: + '@babel/core': 7.26.0 + '@babel/parser': 7.26.1 + + babel-preset-current-node-syntax@1.1.0(@babel/core@7.26.0): + dependencies: + '@babel/core': 7.26.0 + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.26.0) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.26.0) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.26.0) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.26.0) + '@babel/plugin-syntax-import-attributes': 7.26.0(@babel/core@7.26.0) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.26.0) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.26.0) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.26.0) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.26.0) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.26.0) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.26.0) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.26.0) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.26.0) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.26.0) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.26.0) + + babel-preset-current-node-syntax@1.1.0(@babel/core@7.26.10): + dependencies: + '@babel/core': 7.26.10 + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.26.10) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.26.10) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.26.10) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.26.10) + '@babel/plugin-syntax-import-attributes': 7.26.0(@babel/core@7.26.10) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.26.10) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.26.10) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.26.10) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.26.10) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.26.10) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.26.10) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.26.10) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.26.10) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.26.10) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.26.10) + optional: true + + babel-preset-jest@27.5.1(@babel/core@7.26.0): + dependencies: + '@babel/core': 7.26.0 + babel-plugin-jest-hoist: 27.5.1 + babel-preset-current-node-syntax: 1.1.0(@babel/core@7.26.0) + + babel-preset-jest@28.1.3(@babel/core@7.26.0): + dependencies: + '@babel/core': 7.26.0 + babel-plugin-jest-hoist: 28.1.3 + babel-preset-current-node-syntax: 1.1.0(@babel/core@7.26.0) + + babel-preset-jest@29.6.3(@babel/core@7.26.0): + dependencies: + '@babel/core': 7.26.0 + babel-plugin-jest-hoist: 29.6.3 + babel-preset-current-node-syntax: 1.1.0(@babel/core@7.26.0) + + babel-preset-jest@29.6.3(@babel/core@7.26.10): + dependencies: + '@babel/core': 7.26.10 + babel-plugin-jest-hoist: 29.6.3 + babel-preset-current-node-syntax: 1.1.0(@babel/core@7.26.10) + optional: true + + babel-preset-react-app@10.0.1: + dependencies: + '@babel/core': 7.26.0 + '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.26.0) + '@babel/plugin-proposal-decorators': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.26.0) + '@babel/plugin-proposal-numeric-separator': 7.18.6(@babel/core@7.26.0) + '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.26.0) + '@babel/plugin-proposal-private-methods': 7.18.6(@babel/core@7.26.0) + '@babel/plugin-proposal-private-property-in-object': 7.21.11(@babel/core@7.26.0) + '@babel/plugin-transform-flow-strip-types': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-react-display-name': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-runtime': 7.25.9(@babel/core@7.26.0) + '@babel/preset-env': 7.26.0(@babel/core@7.26.0) + '@babel/preset-react': 7.25.9(@babel/core@7.26.0) + '@babel/preset-typescript': 7.26.0(@babel/core@7.26.0) + '@babel/runtime': 7.26.0 + babel-plugin-macros: 3.1.0 + babel-plugin-transform-react-remove-prop-types: 0.4.24 + transitivePeerDependencies: + - supports-color + + babel-runtime@6.25.0: + dependencies: + core-js: 2.6.12 + regenerator-runtime: 0.10.5 + + balanced-match@1.0.2: {} + + base16@1.0.0: {} + + base64-js@1.5.1: {} + + base64id@2.0.0: {} + + batch@0.6.1: {} + + bcrypt-pbkdf@1.0.2: + dependencies: + tweetnacl: 0.14.5 + + before-after-hook@2.2.3: {} + + bfj@7.1.0: + dependencies: + bluebird: 3.7.2 + check-types: 11.2.3 + hoopy: 0.1.4 + jsonpath: 1.1.1 + tryer: 1.0.1 + + big-integer@1.6.52: {} + + big.js@5.2.2: {} + + bignumber.js@9.1.2: {} + + bin-links@3.0.3: + dependencies: + cmd-shim: 5.0.0 + mkdirp-infer-owner: 2.0.0 + npm-normalize-package-bin: 2.0.0 + read-cmd-shim: 3.0.1 + rimraf: 3.0.2 + write-file-atomic: 4.0.2 + + binary-extensions@2.3.0: {} + + binaryextensions@4.19.0: {} + + bindings@1.5.0: + dependencies: + file-uri-to-path: 1.0.0 + + bintrees@1.0.2: {} + + bl@4.1.0: + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.2 + + bluebird@3.7.2: {} + + bn.js@4.12.0: {} + + body-parser@1.20.3: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + on-finished: 2.4.1 + qs: 6.13.0 + raw-body: 2.5.2 + type-is: 1.6.18 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + + bonjour-service@1.2.1: + dependencies: + fast-deep-equal: 3.1.3 + multicast-dns: 7.2.5 + + boolbase@1.0.0: {} + + bottleneck@2.19.5: {} + + bowser@2.11.0: {} + + boxen@7.1.1: + dependencies: + ansi-align: 3.0.1 + camelcase: 7.0.1 + chalk: 5.3.0 + cli-boxes: 3.0.0 + string-width: 5.1.2 + type-fest: 2.19.0 + widest-line: 4.0.1 + wrap-ansi: 8.1.0 + + brace-expansion@1.1.11: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.1: + dependencies: + balanced-match: 1.0.2 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + brorand@1.1.0: {} + + brotli-size@4.0.0: + dependencies: + duplexer: 0.1.1 + + browser-process-hrtime@1.0.0: {} + + browserslist@4.24.2: + dependencies: + caniuse-lite: 1.0.30001675 + electron-to-chromium: 1.5.49 + node-releases: 2.0.18 + update-browserslist-db: 1.1.1(browserslist@4.24.2) + + browserslist@4.25.1: + dependencies: + caniuse-lite: 1.0.30001727 + electron-to-chromium: 1.5.183 + node-releases: 2.0.19 + update-browserslist-db: 1.1.3(browserslist@4.25.1) + + bs-logger@0.2.6: + dependencies: + fast-json-stable-stringify: 2.1.0 + + bser@2.1.1: + dependencies: + node-int64: 0.4.0 + + bson@6.10.3: {} + + buffer-crc32@0.2.13: {} + + buffer-equal-constant-time@1.0.1: {} + + buffer-from@1.1.2: {} + + buffer@5.7.1: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + buffer@6.0.3: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + buildcheck@0.0.6: + optional: true + + builtin-modules@3.3.0: {} + + builtins@1.0.3: {} + + byline@5.0.0: {} + + bytes@3.0.0: {} + + bytes@3.1.2: {} + + cacache@15.3.0: + dependencies: + '@npmcli/fs': 1.1.1 + '@npmcli/move-file': 1.1.2 + chownr: 2.0.0 + fs-minipass: 2.1.0 + glob: 7.2.3 + infer-owner: 1.0.4 + lru-cache: 6.0.0 + minipass: 3.3.6 + minipass-collect: 1.0.2 + minipass-flush: 1.0.5 + minipass-pipeline: 1.2.4 + mkdirp: 1.0.4 + p-map: 4.0.0 + promise-inflight: 1.0.1 + rimraf: 3.0.2 + ssri: 8.0.1 + tar: 6.2.1 + unique-filename: 1.1.1 + transitivePeerDependencies: + - bluebird + + cacache@16.1.3: + dependencies: + '@npmcli/fs': 2.1.2 + '@npmcli/move-file': 2.0.1 + chownr: 2.0.0 + fs-minipass: 2.1.0 + glob: 8.1.0 + infer-owner: 1.0.4 + lru-cache: 7.18.3 + minipass: 3.3.6 + minipass-collect: 1.0.2 + minipass-flush: 1.0.5 + minipass-pipeline: 1.2.4 + mkdirp: 1.0.4 + p-map: 4.0.0 + promise-inflight: 1.0.1 + rimraf: 3.0.2 + ssri: 9.0.1 + tar: 6.2.1 + unique-filename: 2.0.1 + transitivePeerDependencies: + - bluebird + + cacache@17.1.4: + dependencies: + '@npmcli/fs': 3.1.1 + fs-minipass: 3.0.3 + glob: 10.4.5 + lru-cache: 7.18.3 + minipass: 7.1.2 + minipass-collect: 1.0.2 + minipass-flush: 1.0.5 + minipass-pipeline: 1.2.4 + p-map: 4.0.0 + ssri: 10.0.6 + tar: 6.2.1 + unique-filename: 3.0.0 + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bind@1.0.7: + dependencies: + es-define-property: 1.0.0 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + set-function-length: 1.2.2 + + callsites@3.1.0: {} + + camel-case@4.1.2: + dependencies: + pascal-case: 3.1.2 + tslib: 2.8.0 + + camelcase-css@2.0.1: {} + + camelcase@5.3.1: {} + + camelcase@6.3.0: {} + + camelcase@7.0.1: {} + + caniuse-api@3.0.0: + dependencies: + browserslist: 4.24.2 + caniuse-lite: 1.0.30001675 + lodash.memoize: 4.1.2 + lodash.uniq: 4.5.0 + + caniuse-lite@1.0.30001675: {} + + caniuse-lite@1.0.30001727: {} + + case-sensitive-paths-webpack-plugin@2.4.0: {} + + chalk@1.1.3: + dependencies: + ansi-styles: 2.2.1 + escape-string-regexp: 1.0.5 + has-ansi: 2.0.0 + strip-ansi: 3.0.1 + supports-color: 2.0.0 + + chalk@2.4.2: + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + + chalk@3.0.0: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chalk@5.3.0: {} + + char-regex@1.0.2: {} + + char-regex@2.0.1: {} + + chardet@0.7.0: {} + + chart.js@4.4.6: + dependencies: + '@kurkle/color': 0.3.2 + + check-types@11.2.3: {} + + cheerio-select@2.1.0: + dependencies: + boolbase: 1.0.0 + css-select: 5.1.0 + css-what: 6.1.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.1.0 + + cheerio@1.0.0: + dependencies: + cheerio-select: 2.1.0 + dom-serializer: 2.0.0 + domhandler: 5.0.3 + domutils: 3.1.0 + encoding-sniffer: 0.2.0 + htmlparser2: 9.1.0 + parse5: 7.2.1 + parse5-htmlparser2-tree-adapter: 7.1.0 + parse5-parser-stream: 7.1.2 + undici: 6.21.2 + whatwg-mimetype: 4.0.0 + + cheerio@1.0.0-rc.12: + dependencies: + cheerio-select: 2.1.0 + dom-serializer: 2.0.0 + domhandler: 5.0.3 + domutils: 3.1.0 + htmlparser2: 8.0.2 + parse5: 7.2.1 + parse5-htmlparser2-tree-adapter: 7.1.0 + + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + + chokidar@4.0.3: + dependencies: + readdirp: 4.1.2 + + chownr@1.1.4: {} + + chownr@2.0.0: {} + + chownr@3.0.0: {} + + chrome-trace-event@1.0.4: {} + + ci-info@3.9.0: {} + + circ-json@1.0.4: {} + + citty@0.1.6: + dependencies: + consola: 3.4.2 + + cjs-module-lexer@1.4.1: {} + + classnames@2.5.1: {} + + clean-css@5.3.3: + dependencies: + source-map: 0.6.1 + + clean-stack@2.2.0: {} + + clean-webpack-plugin@4.0.0(webpack@5.95.0): + dependencies: + del: 4.1.1 + webpack: 5.95.0(@swc/core@1.11.10(@swc/helpers@0.5.15))(webpack-cli@5.1.4) + + cli-boxes@3.0.0: {} + + cli-cursor@3.1.0: + dependencies: + restore-cursor: 3.1.0 + + cli-cursor@5.0.0: + dependencies: + restore-cursor: 5.1.0 + + cli-spinners@2.9.2: {} + + cli-table@0.3.11: + dependencies: + colors: 1.0.3 + + cli-width@3.0.0: {} + + cli-width@4.1.0: {} + + client-only@0.0.1: {} + + cliui@7.0.4: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + clone-buffer@1.0.0: {} + + clone-deep@4.0.1: + dependencies: + is-plain-object: 2.0.4 + kind-of: 6.0.3 + shallow-clone: 3.0.1 + + clone-stats@1.0.0: {} + + clone@1.0.4: {} + + clone@2.1.2: {} + + cloneable-readable@1.1.3: + dependencies: + inherits: 2.0.4 + process-nextick-args: 2.0.1 + readable-stream: 2.3.8 + + clsx@2.1.1: {} + + cluster-key-slot@1.1.2: {} + + cmd-shim@5.0.0: + dependencies: + mkdirp-infer-owner: 2.0.0 + + co@4.6.0: {} + + coa@2.0.2: + dependencies: + '@types/q': 1.5.8 + chalk: 2.4.2 + q: 1.5.1 + + code-block-writer@11.0.3: {} + + collect-v8-coverage@1.0.2: {} + + color-convert@1.9.3: + dependencies: + color-name: 1.1.3 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.3: {} + + color-name@1.1.4: {} + + color-string@1.9.1: + dependencies: + color-name: 1.1.4 + simple-swizzle: 0.2.2 + + color-support@1.1.3: {} + + color@3.2.1: + dependencies: + color-convert: 1.9.3 + color-string: 1.9.1 + + color@4.2.3: + dependencies: + color-convert: 2.0.1 + color-string: 1.9.1 + + colord@2.9.3: {} + + colorette@2.0.20: {} + + colors@1.0.3: {} + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + commander@10.0.1: {} + + commander@11.1.0: {} + + commander@12.1.0: {} + + commander@13.1.0: {} + + commander@2.20.3: {} + + commander@4.1.1: {} + + commander@7.1.0: {} + + commander@7.2.0: {} + + commander@8.3.0: {} + + common-ancestor-path@1.0.1: {} + + common-path-prefix@3.0.0: {} + + common-tags@1.8.2: {} + + commondir@1.0.1: {} + + compress-commons@4.1.2: + dependencies: + buffer-crc32: 0.2.13 + crc32-stream: 4.0.3 + normalize-path: 3.0.0 + readable-stream: 3.6.2 + + compressible@2.0.18: + dependencies: + mime-db: 1.53.0 + + compression@1.7.4: + dependencies: + accepts: 1.3.8 + bytes: 3.0.0 + compressible: 2.0.18 + debug: 2.6.9 + on-headers: 1.0.2 + safe-buffer: 5.1.2 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + compute-gcd@1.2.1: + dependencies: + validate.io-array: 1.0.6 + validate.io-function: 1.0.2 + validate.io-integer-array: 1.0.0 + + compute-lcm@1.1.2: + dependencies: + compute-gcd: 1.2.1 + validate.io-array: 1.0.6 + validate.io-function: 1.0.2 + validate.io-integer-array: 1.0.0 + + compute-scroll-into-view@3.1.0: {} + + concat-map@0.0.1: {} + + concat-with-sourcemaps@1.1.0: + dependencies: + source-map: 0.6.1 + + confbox@0.2.2: {} + + config-chain@1.1.13: + dependencies: + ini: 1.3.8 + proto-list: 1.2.4 + + confusing-browser-globals@1.0.11: {} + + connect-history-api-fallback@2.0.0: {} + + consola@3.4.2: {} + + console-control-strings@1.1.0: {} + + content-disposition@0.5.4: + dependencies: + safe-buffer: 5.2.1 + + content-type@1.0.5: {} + + convert-source-map@1.9.0: {} + + convert-source-map@2.0.0: {} + + cookie-parser@1.4.7: + dependencies: + cookie: 0.7.2 + cookie-signature: 1.0.6 + + cookie-signature@1.0.6: {} + + cookie@0.7.1: {} + + cookie@0.7.2: {} + + cookie@1.0.1: {} + + copy-anything@2.0.6: + dependencies: + is-what: 3.14.1 + + copy-to-clipboard@3.3.3: + dependencies: + toggle-selection: 1.0.6 + + copy-webpack-plugin@12.0.2(webpack@5.95.0): + dependencies: + fast-glob: 3.3.2 + glob-parent: 6.0.2 + globby: 14.0.2 + normalize-path: 3.0.0 + schema-utils: 4.2.0 + serialize-javascript: 6.0.2 + webpack: 5.95.0(@swc/core@1.11.10(@swc/helpers@0.5.15))(webpack-cli@5.1.4) + + core-js-compat@3.38.1: + dependencies: + browserslist: 4.24.2 + + core-js-pure@3.38.1: {} + + core-js@2.6.12: {} + + core-js@3.38.1: {} + + core-util-is@1.0.2: {} + + core-util-is@1.0.3: {} + + cors@2.8.5: + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + + cosmiconfig@6.0.0: + dependencies: + '@types/parse-json': 4.0.2 + import-fresh: 3.3.0 + parse-json: 5.2.0 + path-type: 4.0.0 + yaml: 1.10.2 + + cosmiconfig@7.1.0: + dependencies: + '@types/parse-json': 4.0.2 + import-fresh: 3.3.0 + parse-json: 5.2.0 + path-type: 4.0.0 + yaml: 1.10.2 + + cosmiconfig@9.0.0(typescript@5.6.3): + dependencies: + env-paths: 2.2.1 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + parse-json: 5.2.0 + optionalDependencies: + typescript: 5.6.3 + + cpu-features@0.0.10: + dependencies: + buildcheck: 0.0.6 + nan: 2.22.0 + optional: true + + crc-32@1.2.2: {} + + crc32-stream@4.0.3: + dependencies: + crc-32: 1.2.2 + readable-stream: 3.6.2 + + create-jest@29.7.0(@types/node@18.19.61)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)): + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-config: 29.7.0(@types/node@18.19.61)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)) + jest-util: 29.7.0 + prompts: 2.4.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + + create-jest@29.7.0(@types/node@18.19.61)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)): + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-config: 29.7.0(@types/node@18.19.61)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)) + jest-util: 29.7.0 + prompts: 2.4.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + + create-jest@29.7.0(@types/node@18.19.61)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@22.14.1)(typescript@5.6.3)): + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-config: 29.7.0(@types/node@18.19.61)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@22.14.1)(typescript@5.6.3)) + jest-util: 29.7.0 + prompts: 2.4.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + + create-jest@29.7.0(@types/node@22.14.1)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@22.14.1)(typescript@5.6.3)): + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-config: 29.7.0(@types/node@22.14.1)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@22.14.1)(typescript@5.6.3)) + jest-util: 29.7.0 + prompts: 2.4.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + + create-require@1.1.1: {} + + cross-fetch@3.1.8(encoding@0.1.13): + dependencies: + node-fetch: 2.7.0(encoding@0.1.13) + transitivePeerDependencies: + - encoding + + cross-fetch@4.0.0(encoding@0.1.13): + dependencies: + node-fetch: 2.7.0(encoding@0.1.13) + transitivePeerDependencies: + - encoding + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + crypto-js@4.2.0: {} + + crypto-random-string@2.0.0: {} + + css-blank-pseudo@3.0.3(postcss@8.4.47): + dependencies: + postcss: 8.4.47 + postcss-selector-parser: 6.1.2 + + css-declaration-sorter@6.4.1(postcss@8.4.47): + dependencies: + postcss: 8.4.47 + + css-declaration-sorter@7.2.0(postcss@8.4.47): + dependencies: + postcss: 8.4.47 + + css-has-pseudo@3.0.4(postcss@8.4.47): + dependencies: + postcss: 8.4.47 + postcss-selector-parser: 6.1.2 + + css-in-js-utils@3.1.0: + dependencies: + hyphenate-style-name: 1.1.0 + + css-loader@6.11.0(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))): + dependencies: + icss-utils: 5.1.0(postcss@8.4.47) + postcss: 8.4.47 + postcss-modules-extract-imports: 3.1.0(postcss@8.4.47) + postcss-modules-local-by-default: 4.0.5(postcss@8.4.47) + postcss-modules-scope: 3.2.0(postcss@8.4.47) + postcss-modules-values: 4.0.0(postcss@8.4.47) + postcss-value-parser: 4.2.0 + semver: 7.6.3 + optionalDependencies: + webpack: 5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(esbuild@0.25.6) + + css-minimizer-webpack-plugin@3.4.1(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))): + dependencies: + cssnano: 5.1.15(postcss@8.4.47) + jest-worker: 27.5.1 + postcss: 8.4.47 + schema-utils: 4.2.0 + serialize-javascript: 6.0.2 + source-map: 0.6.1 + webpack: 5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(esbuild@0.25.6) + + css-prefers-color-scheme@6.0.3(postcss@8.4.47): + dependencies: + postcss: 8.4.47 + + css-select-base-adapter@0.1.1: {} + + css-select@2.1.0: + dependencies: + boolbase: 1.0.0 + css-what: 3.4.2 + domutils: 1.7.0 + nth-check: 2.1.1 + + css-select@4.3.0: + dependencies: + boolbase: 1.0.0 + css-what: 6.1.0 + domhandler: 4.3.1 + domutils: 2.8.0 + nth-check: 2.1.1 + + css-select@5.1.0: + dependencies: + boolbase: 1.0.0 + css-what: 6.1.0 + domhandler: 5.0.3 + domutils: 3.1.0 + nth-check: 2.1.1 + + css-tree@1.0.0-alpha.37: + dependencies: + mdn-data: 2.0.4 + source-map: 0.6.1 + + css-tree@1.1.3: + dependencies: + mdn-data: 2.0.14 + source-map: 0.6.1 + + css-tree@2.2.1: + dependencies: + mdn-data: 2.0.28 + source-map-js: 1.2.1 + + css-tree@2.3.1: + dependencies: + mdn-data: 2.0.30 + source-map-js: 1.2.1 + + css-what@3.4.2: {} + + css-what@6.1.0: {} + + css.escape@1.5.1: {} + + cssdb@7.11.2: {} + + cssesc@3.0.0: {} + + cssnano-preset-default@5.2.14(postcss@8.4.47): + dependencies: + css-declaration-sorter: 6.4.1(postcss@8.4.47) + cssnano-utils: 3.1.0(postcss@8.4.47) + postcss: 8.4.47 + postcss-calc: 8.2.4(postcss@8.4.47) + postcss-colormin: 5.3.1(postcss@8.4.47) + postcss-convert-values: 5.1.3(postcss@8.4.47) + postcss-discard-comments: 5.1.2(postcss@8.4.47) + postcss-discard-duplicates: 5.1.0(postcss@8.4.47) + postcss-discard-empty: 5.1.1(postcss@8.4.47) + postcss-discard-overridden: 5.1.0(postcss@8.4.47) + postcss-merge-longhand: 5.1.7(postcss@8.4.47) + postcss-merge-rules: 5.1.4(postcss@8.4.47) + postcss-minify-font-values: 5.1.0(postcss@8.4.47) + postcss-minify-gradients: 5.1.1(postcss@8.4.47) + postcss-minify-params: 5.1.4(postcss@8.4.47) + postcss-minify-selectors: 5.2.1(postcss@8.4.47) + postcss-normalize-charset: 5.1.0(postcss@8.4.47) + postcss-normalize-display-values: 5.1.0(postcss@8.4.47) + postcss-normalize-positions: 5.1.1(postcss@8.4.47) + postcss-normalize-repeat-style: 5.1.1(postcss@8.4.47) + postcss-normalize-string: 5.1.0(postcss@8.4.47) + postcss-normalize-timing-functions: 5.1.0(postcss@8.4.47) + postcss-normalize-unicode: 5.1.1(postcss@8.4.47) + postcss-normalize-url: 5.1.0(postcss@8.4.47) + postcss-normalize-whitespace: 5.1.1(postcss@8.4.47) + postcss-ordered-values: 5.1.3(postcss@8.4.47) + postcss-reduce-initial: 5.1.2(postcss@8.4.47) + postcss-reduce-transforms: 5.1.0(postcss@8.4.47) + postcss-svgo: 5.1.0(postcss@8.4.47) + postcss-unique-selectors: 5.1.1(postcss@8.4.47) + + cssnano-preset-default@7.0.6(postcss@8.4.47): + dependencies: + browserslist: 4.24.2 + css-declaration-sorter: 7.2.0(postcss@8.4.47) + cssnano-utils: 5.0.0(postcss@8.4.47) + postcss: 8.4.47 + postcss-calc: 10.0.2(postcss@8.4.47) + postcss-colormin: 7.0.2(postcss@8.4.47) + postcss-convert-values: 7.0.4(postcss@8.4.47) + postcss-discard-comments: 7.0.3(postcss@8.4.47) + postcss-discard-duplicates: 7.0.1(postcss@8.4.47) + postcss-discard-empty: 7.0.0(postcss@8.4.47) + postcss-discard-overridden: 7.0.0(postcss@8.4.47) + postcss-merge-longhand: 7.0.4(postcss@8.4.47) + postcss-merge-rules: 7.0.4(postcss@8.4.47) + postcss-minify-font-values: 7.0.0(postcss@8.4.47) + postcss-minify-gradients: 7.0.0(postcss@8.4.47) + postcss-minify-params: 7.0.2(postcss@8.4.47) + postcss-minify-selectors: 7.0.4(postcss@8.4.47) + postcss-normalize-charset: 7.0.0(postcss@8.4.47) + postcss-normalize-display-values: 7.0.0(postcss@8.4.47) + postcss-normalize-positions: 7.0.0(postcss@8.4.47) + postcss-normalize-repeat-style: 7.0.0(postcss@8.4.47) + postcss-normalize-string: 7.0.0(postcss@8.4.47) + postcss-normalize-timing-functions: 7.0.0(postcss@8.4.47) + postcss-normalize-unicode: 7.0.2(postcss@8.4.47) + postcss-normalize-url: 7.0.0(postcss@8.4.47) + postcss-normalize-whitespace: 7.0.0(postcss@8.4.47) + postcss-ordered-values: 7.0.1(postcss@8.4.47) + postcss-reduce-initial: 7.0.2(postcss@8.4.47) + postcss-reduce-transforms: 7.0.0(postcss@8.4.47) + postcss-svgo: 7.0.1(postcss@8.4.47) + postcss-unique-selectors: 7.0.3(postcss@8.4.47) + + cssnano-utils@3.1.0(postcss@8.4.47): + dependencies: + postcss: 8.4.47 + + cssnano-utils@5.0.0(postcss@8.4.47): + dependencies: + postcss: 8.4.47 + + cssnano@5.1.15(postcss@8.4.47): + dependencies: + cssnano-preset-default: 5.2.14(postcss@8.4.47) + lilconfig: 2.1.0 + postcss: 8.4.47 + yaml: 1.10.2 + + cssnano@7.0.6(postcss@8.4.47): + dependencies: + cssnano-preset-default: 7.0.6(postcss@8.4.47) + lilconfig: 3.1.2 + postcss: 8.4.47 + + csso@4.2.0: + dependencies: + css-tree: 1.1.3 + + csso@5.0.5: + dependencies: + css-tree: 2.2.1 + + cssom@0.3.8: {} + + cssom@0.4.4: {} + + cssstyle@2.3.0: + dependencies: + cssom: 0.3.8 + + csstype@3.1.3: {} + + cuid@2.1.8: {} + + d3-array@3.2.4: + dependencies: + internmap: 2.0.3 + + d3-color@3.1.0: {} + + d3-ease@3.0.1: {} + + d3-format@3.1.0: {} + + d3-interpolate@3.0.1: + dependencies: + d3-color: 3.1.0 + + d3-path@3.1.0: {} + + d3-scale@4.0.2: + dependencies: + d3-array: 3.2.4 + d3-format: 3.1.0 + d3-interpolate: 3.0.1 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + + d3-shape@3.2.0: + dependencies: + d3-path: 3.1.0 + + d3-time-format@4.1.0: + dependencies: + d3-time: 3.1.0 + + d3-time@3.1.0: + dependencies: + d3-array: 3.2.4 + + d3-timer@3.0.1: {} + + damerau-levenshtein@1.0.8: {} + + dargs@7.0.0: {} + + data-uri-to-buffer@4.0.1: {} + + data-urls@2.0.0: + dependencies: + abab: 2.0.6 + whatwg-mimetype: 2.3.0 + whatwg-url: 8.7.0 + + data-view-buffer@1.0.1: + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-data-view: 1.0.1 + + data-view-byte-length@1.0.1: + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-data-view: 1.0.1 + + data-view-byte-offset@1.0.0: + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-data-view: 1.0.1 + + date-fns@2.30.0: + dependencies: + '@babel/runtime': 7.26.0 + + dateformat@4.6.3: {} + + dayjs@1.11.13: {} + + debounce@1.2.1: {} + + debounce@2.0.0: {} + + debug@2.6.9: + dependencies: + ms: 2.0.0 + + debug@3.2.7: + dependencies: + ms: 2.1.3 + + debug@4.3.7(supports-color@5.5.0): + dependencies: + ms: 2.1.3 + optionalDependencies: + supports-color: 5.5.0 + + debuglog@1.0.1: {} + + decimal.js-light@2.5.1: {} + + decimal.js@10.4.3: {} + + declaration-bundler-webpack-plugin@1.0.3: {} + + decompress-response@6.0.0: + dependencies: + mimic-response: 3.1.0 + + dedent@0.7.0: {} + + dedent@1.5.3(babel-plugin-macros@3.1.0): + optionalDependencies: + babel-plugin-macros: 3.1.0 + + deep-equal@2.2.3: + dependencies: + array-buffer-byte-length: 1.0.1 + call-bind: 1.0.7 + es-get-iterator: 1.1.3 + get-intrinsic: 1.2.4 + is-arguments: 1.1.1 + is-array-buffer: 3.0.4 + is-date-object: 1.0.5 + is-regex: 1.1.4 + is-shared-array-buffer: 1.0.3 + isarray: 2.0.5 + object-is: 1.1.6 + object-keys: 1.1.1 + object.assign: 4.1.5 + regexp.prototype.flags: 1.5.3 + side-channel: 1.0.6 + which-boxed-primitive: 1.0.2 + which-collection: 1.0.2 + which-typed-array: 1.1.15 + + deep-extend@0.6.0: {} + + deep-is@0.1.4: {} + + deepmerge@4.3.1: {} + + default-gateway@6.0.3: + dependencies: + execa: 5.1.1 + + defaults@1.0.4: + dependencies: + clone: 1.0.4 + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.0 + es-errors: 1.3.0 + gopd: 1.0.1 + + define-lazy-prop@2.0.0: {} + + define-properties@1.2.1: + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + + del@4.1.1: + dependencies: + '@types/glob': 7.2.0 + globby: 6.1.0 + is-path-cwd: 2.2.0 + is-path-in-cwd: 2.1.0 + p-map: 2.1.0 + pify: 4.0.1 + rimraf: 2.7.1 + + delayed-stream@1.0.0: {} + + delegates@1.0.0: {} + + denque@2.1.0: {} + + depd@1.1.2: {} + + depd@2.0.0: {} + + deprecation@2.3.1: {} + + dequal@2.0.3: {} + + destroy@1.2.0: {} + + detect-libc@2.0.3: {} + + detect-libc@2.0.4: {} + + detect-newline@3.1.0: {} + + detect-node-es@1.1.0: {} + + detect-node@2.1.0: {} + + detect-port-alt@1.1.6: + dependencies: + address: 1.2.2 + debug: 2.6.9 + transitivePeerDependencies: + - supports-color + + dezalgo@1.0.4: + dependencies: + asap: 2.0.6 + wrappy: 1.0.2 + + didyoumean@1.2.2: {} + + diff-match-patch@1.0.5: {} + + diff-sequences@27.5.1: {} + + diff-sequences@28.1.1: {} + + diff-sequences@29.6.3: {} + + diff@4.0.2: {} + + diff@5.2.0: {} + + dir-glob@3.0.1: + dependencies: + path-type: 4.0.0 + + dlv@1.1.3: {} + + dns-packet@5.6.1: + dependencies: + '@leichtgewicht/ip-codec': 2.0.5 + + docker-compose@0.24.8: + dependencies: + yaml: 2.6.0 + + docker-modem@3.0.8: + dependencies: + debug: 4.3.7(supports-color@5.5.0) + readable-stream: 3.6.2 + split-ca: 1.0.1 + ssh2: 1.16.0 + transitivePeerDependencies: + - supports-color + + dockerode@3.3.5: + dependencies: + '@balena/dockerignore': 1.0.2 + docker-modem: 3.0.8 + tar-fs: 2.0.1 + transitivePeerDependencies: + - supports-color + + doctrine@2.1.0: + dependencies: + esutils: 2.0.3 + + doctrine@3.0.0: + dependencies: + esutils: 2.0.3 + + dom-accessibility-api@0.5.16: {} + + dom-accessibility-api@0.6.3: {} + + dom-align@1.12.4: {} + + dom-converter@0.2.0: + dependencies: + utila: 0.4.0 + + dom-helpers@5.2.1: + dependencies: + '@babel/runtime': 7.26.0 + csstype: 3.1.3 + + dom-serializer@0.2.2: + dependencies: + domelementtype: 2.3.0 + entities: 2.2.0 + + dom-serializer@1.4.1: + dependencies: + domelementtype: 2.3.0 + domhandler: 4.3.1 + entities: 2.2.0 + + dom-serializer@2.0.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.5.0 + + domelementtype@1.3.1: {} + + domelementtype@2.3.0: {} + + domexception@2.0.1: + dependencies: + webidl-conversions: 5.0.0 + + domhandler@3.3.0: + dependencies: + domelementtype: 2.3.0 + + domhandler@4.3.1: + dependencies: + domelementtype: 2.3.0 + + domhandler@5.0.3: + dependencies: + domelementtype: 2.3.0 + + domutils@1.7.0: + dependencies: + dom-serializer: 0.2.2 + domelementtype: 1.3.1 + + domutils@2.8.0: + dependencies: + dom-serializer: 1.4.1 + domelementtype: 2.3.0 + domhandler: 4.3.1 + + domutils@3.1.0: + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + + dot-case@3.0.4: + dependencies: + no-case: 3.0.4 + tslib: 2.8.0 + + dotenv-cli@6.0.0: + dependencies: + cross-spawn: 7.0.6 + dotenv: 16.4.5 + dotenv-expand: 8.0.3 + minimist: 1.2.8 + + dotenv-cli@7.4.2: + dependencies: + cross-spawn: 7.0.6 + dotenv: 16.4.5 + dotenv-expand: 10.0.0 + minimist: 1.2.8 + + dotenv-expand@10.0.0: {} + + dotenv-expand@5.1.0: {} + + dotenv-expand@8.0.3: {} + + dotenv@10.0.0: {} + + dotenv@16.4.5: {} + + dset@3.1.4: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + duplexer@0.1.1: {} + + duplexer@0.1.2: {} + + duplexify@4.1.3: + dependencies: + end-of-stream: 1.4.4 + inherits: 2.0.4 + readable-stream: 3.6.2 + stream-shift: 1.0.3 + + dynamic-dedupe@0.3.0: + dependencies: + xtend: 4.0.2 + + eastasianwidth@0.2.0: {} + + ecdsa-sig-formatter@1.0.11: + dependencies: + safe-buffer: 5.2.1 + + editorconfig@1.0.4: + dependencies: + '@one-ini/wasm': 0.1.1 + commander: 10.0.1 + minimatch: 9.0.1 + semver: 7.7.2 + + ee-first@1.1.1: {} + + ejs@3.1.10: + dependencies: + jake: 10.9.2 + + electron-to-chromium@1.5.183: {} + + electron-to-chromium@1.5.49: {} + + elliptic@6.6.1: + dependencies: + bn.js: 4.12.0 + brorand: 1.1.0 + hash.js: 1.1.7 + hmac-drbg: 1.0.1 + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + minimalistic-crypto-utils: 1.0.1 + + emittery@0.10.2: {} + + emittery@0.13.1: {} + + emittery@0.8.1: {} + + emoji-regex@10.4.0: {} + + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + + emojis-list@3.0.0: {} + + encodeurl@1.0.2: {} + + encodeurl@2.0.0: {} + + encoding-sniffer@0.2.0: + dependencies: + iconv-lite: 0.6.3 + whatwg-encoding: 3.1.1 + + encoding@0.1.13: + dependencies: + iconv-lite: 0.6.3 + optional: true + + end-of-stream@1.4.4: + dependencies: + once: 1.4.0 + + engine.io-client@6.6.3: + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.3.7(supports-color@5.5.0) + engine.io-parser: 5.2.3 + ws: 8.17.1 + xmlhttprequest-ssl: 2.1.2 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + engine.io-parser@5.2.3: {} + + engine.io@6.6.4: + dependencies: + '@types/cors': 2.8.17 + '@types/node': 18.19.61 + accepts: 1.3.8 + base64id: 2.0.0 + cookie: 0.7.2 + cors: 2.8.5 + debug: 4.3.7(supports-color@5.5.0) + engine.io-parser: 5.2.3 + ws: 8.17.1 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + enhanced-resolve@5.17.1: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.2.1 + + entities@2.2.0: {} + + entities@3.0.1: {} + + entities@4.5.0: {} + + env-paths@2.2.1: {} + + envinfo@7.14.0: {} + + err-code@2.0.3: {} + + errno@0.1.8: + dependencies: + prr: 1.0.1 + optional: true + + error-ex@1.3.2: + dependencies: + is-arrayish: 0.2.1 + + error-stack-parser@2.1.4: + dependencies: + stackframe: 1.3.4 + + error@10.4.0: {} + + es-abstract@1.23.3: + dependencies: + array-buffer-byte-length: 1.0.1 + arraybuffer.prototype.slice: 1.0.3 + available-typed-arrays: 1.0.7 + call-bind: 1.0.7 + data-view-buffer: 1.0.1 + data-view-byte-length: 1.0.1 + data-view-byte-offset: 1.0.0 + es-define-property: 1.0.0 + es-errors: 1.3.0 + es-object-atoms: 1.0.0 + es-set-tostringtag: 2.0.3 + es-to-primitive: 1.2.1 + function.prototype.name: 1.1.6 + get-intrinsic: 1.2.4 + get-symbol-description: 1.0.2 + globalthis: 1.0.4 + gopd: 1.0.1 + has-property-descriptors: 1.0.2 + has-proto: 1.0.3 + has-symbols: 1.0.3 + hasown: 2.0.2 + internal-slot: 1.0.7 + is-array-buffer: 3.0.4 + is-callable: 1.2.7 + is-data-view: 1.0.1 + is-negative-zero: 2.0.3 + is-regex: 1.1.4 + is-shared-array-buffer: 1.0.3 + is-string: 1.0.7 + is-typed-array: 1.1.13 + is-weakref: 1.0.2 + object-inspect: 1.13.2 + object-keys: 1.1.1 + object.assign: 4.1.5 + regexp.prototype.flags: 1.5.3 + safe-array-concat: 1.1.2 + safe-regex-test: 1.0.3 + string.prototype.trim: 1.2.9 + string.prototype.trimend: 1.0.8 + string.prototype.trimstart: 1.0.8 + typed-array-buffer: 1.0.2 + typed-array-byte-length: 1.0.1 + typed-array-byte-offset: 1.0.2 + typed-array-length: 1.0.6 + unbox-primitive: 1.0.2 + which-typed-array: 1.1.15 + + es-array-method-boxes-properly@1.0.0: {} + + es-define-property@1.0.0: + dependencies: + get-intrinsic: 1.2.4 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-get-iterator@1.1.3: + dependencies: + call-bind: 1.0.7 + get-intrinsic: 1.2.4 + has-symbols: 1.0.3 + is-arguments: 1.1.1 + is-map: 2.0.3 + is-set: 2.0.3 + is-string: 1.0.7 + isarray: 2.0.5 + stop-iteration-iterator: 1.0.0 + + es-iterator-helpers@1.1.0: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-errors: 1.3.0 + es-set-tostringtag: 2.0.3 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + globalthis: 1.0.4 + has-property-descriptors: 1.0.2 + has-proto: 1.0.3 + has-symbols: 1.0.3 + internal-slot: 1.0.7 + iterator.prototype: 1.1.3 + safe-array-concat: 1.1.2 + + es-module-lexer@1.5.4: {} + + es-object-atoms@1.0.0: + dependencies: + es-errors: 1.3.0 + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.0.3: + dependencies: + get-intrinsic: 1.2.4 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + es-shim-unscopables@1.0.2: + dependencies: + hasown: 2.0.2 + + es-to-primitive@1.2.1: + dependencies: + is-callable: 1.2.7 + is-date-object: 1.0.5 + is-symbol: 1.0.4 + + es6-promise@4.2.8: {} + + esbuild-register@3.6.0(esbuild@0.23.1): + dependencies: + debug: 4.3.7(supports-color@5.5.0) + esbuild: 0.23.1 + transitivePeerDependencies: + - supports-color + + esbuild@0.23.1: + optionalDependencies: + '@esbuild/aix-ppc64': 0.23.1 + '@esbuild/android-arm': 0.23.1 + '@esbuild/android-arm64': 0.23.1 + '@esbuild/android-x64': 0.23.1 + '@esbuild/darwin-arm64': 0.23.1 + '@esbuild/darwin-x64': 0.23.1 + '@esbuild/freebsd-arm64': 0.23.1 + '@esbuild/freebsd-x64': 0.23.1 + '@esbuild/linux-arm': 0.23.1 + '@esbuild/linux-arm64': 0.23.1 + '@esbuild/linux-ia32': 0.23.1 + '@esbuild/linux-loong64': 0.23.1 + '@esbuild/linux-mips64el': 0.23.1 + '@esbuild/linux-ppc64': 0.23.1 + '@esbuild/linux-riscv64': 0.23.1 + '@esbuild/linux-s390x': 0.23.1 + '@esbuild/linux-x64': 0.23.1 + '@esbuild/netbsd-x64': 0.23.1 + '@esbuild/openbsd-arm64': 0.23.1 + '@esbuild/openbsd-x64': 0.23.1 + '@esbuild/sunos-x64': 0.23.1 + '@esbuild/win32-arm64': 0.23.1 + '@esbuild/win32-ia32': 0.23.1 + '@esbuild/win32-x64': 0.23.1 + + esbuild@0.25.6: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.6 + '@esbuild/android-arm': 0.25.6 + '@esbuild/android-arm64': 0.25.6 + '@esbuild/android-x64': 0.25.6 + '@esbuild/darwin-arm64': 0.25.6 + '@esbuild/darwin-x64': 0.25.6 + '@esbuild/freebsd-arm64': 0.25.6 + '@esbuild/freebsd-x64': 0.25.6 + '@esbuild/linux-arm': 0.25.6 + '@esbuild/linux-arm64': 0.25.6 + '@esbuild/linux-ia32': 0.25.6 + '@esbuild/linux-loong64': 0.25.6 + '@esbuild/linux-mips64el': 0.25.6 + '@esbuild/linux-ppc64': 0.25.6 + '@esbuild/linux-riscv64': 0.25.6 + '@esbuild/linux-s390x': 0.25.6 + '@esbuild/linux-x64': 0.25.6 + '@esbuild/netbsd-arm64': 0.25.6 + '@esbuild/netbsd-x64': 0.25.6 + '@esbuild/openbsd-arm64': 0.25.6 + '@esbuild/openbsd-x64': 0.25.6 + '@esbuild/openharmony-arm64': 0.25.6 + '@esbuild/sunos-x64': 0.25.6 + '@esbuild/win32-arm64': 0.25.6 + '@esbuild/win32-ia32': 0.25.6 + '@esbuild/win32-x64': 0.25.6 + + escalade@3.2.0: {} + + escape-goat@3.0.0: {} + + escape-html@1.0.3: {} + + escape-string-regexp@1.0.5: {} + + escape-string-regexp@2.0.0: {} + + escape-string-regexp@4.0.0: {} + + escodegen@1.14.3: + dependencies: + esprima: 4.0.1 + estraverse: 4.3.0 + esutils: 2.0.3 + optionator: 0.8.3 + optionalDependencies: + source-map: 0.6.1 + + escodegen@2.1.0: + dependencies: + esprima: 4.0.1 + estraverse: 5.3.0 + esutils: 2.0.3 + optionalDependencies: + source-map: 0.6.1 + + eslint-config-next@15.5.4(eslint@8.57.1)(typescript@5.6.3): + dependencies: + '@next/eslint-plugin-next': 15.5.4 + '@rushstack/eslint-patch': 1.10.4 + '@typescript-eslint/eslint-plugin': 8.12.2(@typescript-eslint/parser@8.12.2(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1)(typescript@5.6.3) + '@typescript-eslint/parser': 8.12.2(eslint@8.57.1)(typescript@5.6.3) + eslint: 8.57.1 + eslint-import-resolver-node: 0.3.9 + eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.12.2(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.1) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.12.2(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1) + eslint-plugin-jsx-a11y: 6.10.2(eslint@8.57.1) + eslint-plugin-react: 7.37.2(eslint@8.57.1) + eslint-plugin-react-hooks: 5.0.0(eslint@8.57.1) + optionalDependencies: + typescript: 5.6.3 + transitivePeerDependencies: + - eslint-import-resolver-webpack + - eslint-plugin-import-x + - supports-color + + eslint-config-prettier@9.1.0(eslint@8.57.1): + dependencies: + eslint: 8.57.1 + + eslint-config-react-app@7.0.1(@babel/plugin-syntax-flow@7.26.0(@babel/core@7.26.0))(@babel/plugin-transform-react-jsx@7.25.9(@babel/core@7.26.0))(eslint@8.57.1)(jest@27.5.1(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)))(typescript@5.6.3): + dependencies: + '@babel/core': 7.26.0 + '@babel/eslint-parser': 7.25.9(@babel/core@7.26.0)(eslint@8.57.1) + '@rushstack/eslint-patch': 1.10.4 + '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1)(typescript@5.6.3) + '@typescript-eslint/parser': 5.62.0(eslint@8.57.1)(typescript@5.6.3) + babel-preset-react-app: 10.0.1 + confusing-browser-globals: 1.0.11 + eslint: 8.57.1 + eslint-plugin-flowtype: 8.0.3(@babel/plugin-syntax-flow@7.26.0(@babel/core@7.26.0))(@babel/plugin-transform-react-jsx@7.25.9(@babel/core@7.26.0))(eslint@8.57.1) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1) + eslint-plugin-jest: 25.7.0(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1)(jest@27.5.1(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)))(typescript@5.6.3) + eslint-plugin-jsx-a11y: 6.10.2(eslint@8.57.1) + eslint-plugin-react: 7.37.2(eslint@8.57.1) + eslint-plugin-react-hooks: 4.6.2(eslint@8.57.1) + eslint-plugin-testing-library: 5.11.1(eslint@8.57.1)(typescript@5.6.3) + optionalDependencies: + typescript: 5.6.3 + transitivePeerDependencies: + - '@babel/plugin-syntax-flow' + - '@babel/plugin-transform-react-jsx' + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - jest + - supports-color + + eslint-import-resolver-node@0.3.9: + dependencies: + debug: 3.2.7 + is-core-module: 2.15.1 + resolve: 1.22.8 + transitivePeerDependencies: + - supports-color + + eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.12.2(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.1): + dependencies: + '@nolyfill/is-core-module': 1.0.39 + debug: 4.3.7(supports-color@5.5.0) + enhanced-resolve: 5.17.1 + eslint: 8.57.1 + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.12.2(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1) + fast-glob: 3.3.2 + get-tsconfig: 4.8.1 + is-bun-module: 1.2.1 + is-glob: 4.0.3 + optionalDependencies: + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.12.2(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1) + transitivePeerDependencies: + - '@typescript-eslint/parser' + - eslint-import-resolver-node + - eslint-import-resolver-webpack + - supports-color + + eslint-module-utils@2.12.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1): + dependencies: + debug: 3.2.7 + optionalDependencies: + '@typescript-eslint/parser': 5.62.0(eslint@8.57.1)(typescript@5.6.3) + eslint: 8.57.1 + eslint-import-resolver-node: 0.3.9 + transitivePeerDependencies: + - supports-color + + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.12.2(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1): + dependencies: + debug: 3.2.7 + optionalDependencies: + '@typescript-eslint/parser': 8.12.2(eslint@8.57.1)(typescript@5.6.3) + eslint: 8.57.1 + eslint-import-resolver-node: 0.3.9 + eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.12.2(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.1) + transitivePeerDependencies: + - supports-color + + eslint-plugin-flowtype@8.0.3(@babel/plugin-syntax-flow@7.26.0(@babel/core@7.26.0))(@babel/plugin-transform-react-jsx@7.25.9(@babel/core@7.26.0))(eslint@8.57.1): + dependencies: + '@babel/plugin-syntax-flow': 7.26.0(@babel/core@7.26.0) + '@babel/plugin-transform-react-jsx': 7.25.9(@babel/core@7.26.0) + eslint: 8.57.1 + lodash: 4.17.21 + string-natural-compare: 3.0.1 + + eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1): + dependencies: + '@rtsao/scc': 1.1.0 + array-includes: 3.1.8 + array.prototype.findlastindex: 1.2.5 + array.prototype.flat: 1.3.2 + array.prototype.flatmap: 1.3.2 + debug: 3.2.7 + doctrine: 2.1.0 + eslint: 8.57.1 + eslint-import-resolver-node: 0.3.9 + eslint-module-utils: 2.12.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1) + hasown: 2.0.2 + is-core-module: 2.15.1 + is-glob: 4.0.3 + minimatch: 3.1.2 + object.fromentries: 2.0.8 + object.groupby: 1.0.3 + object.values: 1.2.0 + semver: 6.3.1 + string.prototype.trimend: 1.0.8 + tsconfig-paths: 3.15.0 + optionalDependencies: + '@typescript-eslint/parser': 5.62.0(eslint@8.57.1)(typescript@5.6.3) + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + + eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.12.2(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1): + dependencies: + '@rtsao/scc': 1.1.0 + array-includes: 3.1.8 + array.prototype.findlastindex: 1.2.5 + array.prototype.flat: 1.3.2 + array.prototype.flatmap: 1.3.2 + debug: 3.2.7 + doctrine: 2.1.0 + eslint: 8.57.1 + eslint-import-resolver-node: 0.3.9 + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.12.2(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1) + hasown: 2.0.2 + is-core-module: 2.15.1 + is-glob: 4.0.3 + minimatch: 3.1.2 + object.fromentries: 2.0.8 + object.groupby: 1.0.3 + object.values: 1.2.0 + semver: 6.3.1 + string.prototype.trimend: 1.0.8 + tsconfig-paths: 3.15.0 + optionalDependencies: + '@typescript-eslint/parser': 8.12.2(eslint@8.57.1)(typescript@5.6.3) + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + + eslint-plugin-jest@25.7.0(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1)(jest@27.5.1(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)))(typescript@5.6.3): + dependencies: + '@typescript-eslint/experimental-utils': 5.62.0(eslint@8.57.1)(typescript@5.6.3) + eslint: 8.57.1 + optionalDependencies: + '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1)(typescript@5.6.3) + jest: 27.5.1(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)) + transitivePeerDependencies: + - supports-color + - typescript + + eslint-plugin-jsx-a11y@6.10.2(eslint@8.57.1): + dependencies: + aria-query: 5.3.2 + array-includes: 3.1.8 + array.prototype.flatmap: 1.3.2 + ast-types-flow: 0.0.8 + axe-core: 4.10.2 + axobject-query: 4.1.0 + damerau-levenshtein: 1.0.8 + emoji-regex: 9.2.2 + eslint: 8.57.1 + hasown: 2.0.2 + jsx-ast-utils: 3.3.5 + language-tags: 1.0.9 + minimatch: 3.1.2 + object.fromentries: 2.0.8 + safe-regex-test: 1.0.3 + string.prototype.includes: 2.0.1 + + eslint-plugin-react-hooks@4.6.2(eslint@8.57.1): + dependencies: + eslint: 8.57.1 + + eslint-plugin-react-hooks@5.0.0(eslint@8.57.1): + dependencies: + eslint: 8.57.1 + + eslint-plugin-react@7.37.2(eslint@8.57.1): + dependencies: + array-includes: 3.1.8 + array.prototype.findlast: 1.2.5 + array.prototype.flatmap: 1.3.2 + array.prototype.tosorted: 1.1.4 + doctrine: 2.1.0 + es-iterator-helpers: 1.1.0 + eslint: 8.57.1 + estraverse: 5.3.0 + hasown: 2.0.2 + jsx-ast-utils: 3.3.5 + minimatch: 3.1.2 + object.entries: 1.1.8 + object.fromentries: 2.0.8 + object.values: 1.2.0 + prop-types: 15.8.1 + resolve: 2.0.0-next.5 + semver: 6.3.1 + string.prototype.matchall: 4.0.11 + string.prototype.repeat: 1.0.0 + + eslint-plugin-testing-library@5.11.1(eslint@8.57.1)(typescript@5.6.3): + dependencies: + '@typescript-eslint/utils': 5.62.0(eslint@8.57.1)(typescript@5.6.3) + eslint: 8.57.1 + transitivePeerDependencies: + - supports-color + - typescript + + eslint-plugin-unused-imports@4.1.4(@typescript-eslint/eslint-plugin@8.12.2(@typescript-eslint/parser@8.12.2(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1): + dependencies: + eslint: 8.57.1 + optionalDependencies: + '@typescript-eslint/eslint-plugin': 8.12.2(@typescript-eslint/parser@8.12.2(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1)(typescript@5.6.3) + + eslint-scope@5.1.1: + dependencies: + esrecurse: 4.3.0 + estraverse: 4.3.0 + + eslint-scope@7.2.2: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@2.1.0: {} + + eslint-visitor-keys@3.4.3: {} + + eslint-webpack-plugin@3.2.0(eslint@8.57.1)(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))): + dependencies: + '@types/eslint': 8.56.12 + eslint: 8.57.1 + jest-worker: 28.1.3 + micromatch: 4.0.8 + normalize-path: 3.0.0 + schema-utils: 4.2.0 + webpack: 5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(esbuild@0.25.6) + + eslint@8.57.1: + dependencies: + '@eslint-community/eslint-utils': 4.4.1(eslint@8.57.1) + '@eslint-community/regexpp': 4.12.1 + '@eslint/eslintrc': 2.1.4 + '@eslint/js': 8.57.1 + '@humanwhocodes/config-array': 0.13.0 + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + '@ungap/structured-clone': 1.2.0 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.3.7(supports-color@5.5.0) + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.6.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.24.0 + graphemer: 1.4.0 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-yaml: 4.1.0 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + strip-ansi: 6.0.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + + espree@9.6.1: + dependencies: + acorn: 8.14.0 + acorn-jsx: 5.3.2(acorn@8.14.0) + eslint-visitor-keys: 3.4.3 + + esprima@1.2.2: {} + + esprima@4.0.1: {} + + esquery@1.6.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@4.3.0: {} + + estraverse@5.3.0: {} + + estree-walker@0.6.1: {} + + estree-walker@1.0.1: {} + + estree-walker@2.0.2: {} + + esutils@2.0.3: {} + + etag@1.8.1: {} + + event-target-shim@5.0.1: {} + + eventemitter3@4.0.7: {} + + events@3.3.0: {} + + execa@5.1.1: + dependencies: + cross-spawn: 7.0.6 + get-stream: 6.0.1 + human-signals: 2.1.0 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + + exit@0.1.2: {} + + expand-template@2.0.3: {} + + expect@27.5.1: + dependencies: + '@jest/types': 27.5.1 + jest-get-type: 27.5.1 + jest-matcher-utils: 27.5.1 + jest-message-util: 27.5.1 + + expect@28.1.3: + dependencies: + '@jest/expect-utils': 28.1.3 + jest-get-type: 28.0.2 + jest-matcher-utils: 28.1.3 + jest-message-util: 28.1.3 + jest-util: 28.1.3 + + expect@29.7.0: + dependencies: + '@jest/expect-utils': 29.7.0 + jest-get-type: 29.6.3 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + + exponential-backoff@3.1.1: {} + + express@4.21.2: + dependencies: + accepts: 1.3.8 + array-flatten: 1.1.1 + body-parser: 1.20.3 + content-disposition: 0.5.4 + content-type: 1.0.5 + cookie: 0.7.1 + cookie-signature: 1.0.6 + debug: 2.6.9 + depd: 2.0.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 1.3.1 + fresh: 0.5.2 + http-errors: 2.0.0 + merge-descriptors: 1.0.3 + methods: 1.1.2 + on-finished: 2.4.1 + parseurl: 1.3.3 + path-to-regexp: 0.1.12 + proxy-addr: 2.0.7 + qs: 6.13.0 + range-parser: 1.2.1 + safe-buffer: 5.2.1 + send: 0.19.0 + serve-static: 1.16.2 + setprototypeof: 1.2.0 + statuses: 2.0.1 + type-is: 1.6.18 + utils-merge: 1.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + exsolve@1.0.7: {} + + extend@3.0.2: {} + + external-editor@3.1.0: + dependencies: + chardet: 0.7.0 + iconv-lite: 0.4.24 + tmp: 0.0.33 + + extsprintf@1.3.0: {} + + farmhash-modern@1.1.0: {} + + fast-deep-equal@2.0.1: {} + + fast-deep-equal@3.1.3: {} + + fast-equals@5.0.1: {} + + fast-glob@3.3.1: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-glob@3.3.2: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fast-shallow-equal@1.0.0: {} + + fast-text-encoding@1.0.6: {} + + fast-uri@3.0.3: {} + + fast-xml-parser@4.4.1: + dependencies: + strnum: 1.0.5 + + fast-xml-parser@4.5.0: + dependencies: + strnum: 1.0.5 + optional: true + + fastest-levenshtein@1.0.16: {} + + fastest-stable-stringify@2.0.2: {} + + fastq@1.17.1: + dependencies: + reusify: 1.0.4 + + faye-websocket@0.11.4: + dependencies: + websocket-driver: 0.7.4 + + fb-watchman@2.0.2: + dependencies: + bser: 2.1.1 + + fbemitter@3.0.0(encoding@0.1.13): + dependencies: + fbjs: 3.0.5(encoding@0.1.13) + transitivePeerDependencies: + - encoding + + fbjs-css-vars@1.0.2: {} + + fbjs@3.0.5(encoding@0.1.13): + dependencies: + cross-fetch: 3.1.8(encoding@0.1.13) + fbjs-css-vars: 1.0.2 + loose-envify: 1.4.0 + object-assign: 4.1.1 + promise: 7.3.1 + setimmediate: 1.0.5 + ua-parser-js: 1.0.39 + transitivePeerDependencies: + - encoding + + fdir@6.4.2(picomatch@4.0.2): + optionalDependencies: + picomatch: 4.0.2 + + fetch-blob@3.2.0: + dependencies: + node-domexception: 1.0.0 + web-streams-polyfill: 3.3.3 + + figlet@1.8.0: {} + + figures@1.7.0: + dependencies: + escape-string-regexp: 1.0.5 + object-assign: 4.1.1 + + figures@3.2.0: + dependencies: + escape-string-regexp: 1.0.5 + + file-entry-cache@6.0.1: + dependencies: + flat-cache: 3.2.0 + + file-loader@6.2.0(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))): + dependencies: + loader-utils: 2.0.4 + schema-utils: 3.3.0 + webpack: 5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(esbuild@0.25.6) + + file-uri-to-path@1.0.0: {} + + filelist@1.0.4: + dependencies: + minimatch: 5.1.6 + + filesize@6.4.0: {} + + filesize@8.0.7: {} + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + finalhandler@1.3.1: + dependencies: + debug: 2.6.9 + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.1 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + + find-cache-dir@3.3.2: + dependencies: + commondir: 1.0.1 + make-dir: 3.1.0 + pkg-dir: 4.2.0 + + find-cache-dir@4.0.0: + dependencies: + common-path-prefix: 3.0.0 + pkg-dir: 7.0.0 + + find-free-port@2.0.0: {} + + find-up@3.0.0: + dependencies: + locate-path: 3.0.0 + + find-up@4.1.0: + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + find-up@6.3.0: + dependencies: + locate-path: 7.2.0 + path-exists: 5.0.0 + + find-yarn-workspace-root2@1.2.16: + dependencies: + micromatch: 4.0.8 + pkg-dir: 4.2.0 + + firebase-admin@13.2.0(encoding@0.1.13): + dependencies: + '@fastify/busboy': 3.0.0 + '@firebase/database-compat': 2.0.4 + '@firebase/database-types': 1.0.9 + '@types/node': 22.13.10 + farmhash-modern: 1.1.0 + google-auth-library: 9.14.2(encoding@0.1.13) + jsonwebtoken: 9.0.2 + jwks-rsa: 3.2.0 + node-forge: 1.3.1 + uuid: 11.1.0 + optionalDependencies: + '@google-cloud/firestore': 7.11.0(encoding@0.1.13) + '@google-cloud/storage': 7.15.2(encoding@0.1.13) + transitivePeerDependencies: + - encoding + - supports-color + + firebase@11.4.0: + dependencies: + '@firebase/analytics': 0.10.12(@firebase/app@0.11.2) + '@firebase/analytics-compat': 0.2.18(@firebase/app-compat@0.2.51)(@firebase/app@0.11.2) + '@firebase/app': 0.11.2 + '@firebase/app-check': 0.8.12(@firebase/app@0.11.2) + '@firebase/app-check-compat': 0.3.19(@firebase/app-compat@0.2.51)(@firebase/app@0.11.2) + '@firebase/app-compat': 0.2.51 + '@firebase/app-types': 0.9.3 + '@firebase/auth': 1.9.1(@firebase/app@0.11.2) + '@firebase/auth-compat': 0.5.19(@firebase/app-compat@0.2.51)(@firebase/app-types@0.9.3)(@firebase/app@0.11.2) + '@firebase/data-connect': 0.3.1(@firebase/app@0.11.2) + '@firebase/database': 1.0.13 + '@firebase/database-compat': 2.0.4 + '@firebase/firestore': 4.7.9(@firebase/app@0.11.2) + '@firebase/firestore-compat': 0.3.44(@firebase/app-compat@0.2.51)(@firebase/app-types@0.9.3)(@firebase/app@0.11.2) + '@firebase/functions': 0.12.3(@firebase/app@0.11.2) + '@firebase/functions-compat': 0.3.20(@firebase/app-compat@0.2.51)(@firebase/app@0.11.2) + '@firebase/installations': 0.6.13(@firebase/app@0.11.2) + '@firebase/installations-compat': 0.2.13(@firebase/app-compat@0.2.51)(@firebase/app-types@0.9.3)(@firebase/app@0.11.2) + '@firebase/messaging': 0.12.17(@firebase/app@0.11.2) + '@firebase/messaging-compat': 0.2.17(@firebase/app-compat@0.2.51)(@firebase/app@0.11.2) + '@firebase/performance': 0.7.1(@firebase/app@0.11.2) + '@firebase/performance-compat': 0.2.14(@firebase/app-compat@0.2.51)(@firebase/app@0.11.2) + '@firebase/remote-config': 0.6.0(@firebase/app@0.11.2) + '@firebase/remote-config-compat': 0.2.13(@firebase/app-compat@0.2.51)(@firebase/app@0.11.2) + '@firebase/storage': 0.13.7(@firebase/app@0.11.2) + '@firebase/storage-compat': 0.3.17(@firebase/app-compat@0.2.51)(@firebase/app-types@0.9.3)(@firebase/app@0.11.2) + '@firebase/util': 1.11.0 + '@firebase/vertexai': 1.1.0(@firebase/app-types@0.9.3)(@firebase/app@0.11.2) + transitivePeerDependencies: + - '@react-native-async-storage/async-storage' + + first-chunk-stream@2.0.0: + dependencies: + readable-stream: 2.3.8 + + flat-cache@3.2.0: + dependencies: + flatted: 3.3.1 + keyv: 4.5.4 + rimraf: 3.0.2 + + flat@5.0.2: {} + + flatted@3.3.1: {} + + flux@4.0.4(encoding@0.1.13)(react@18.3.1): + dependencies: + fbemitter: 3.0.0(encoding@0.1.13) + fbjs: 3.0.5(encoding@0.1.13) + react: 18.3.1 + transitivePeerDependencies: + - encoding + + follow-redirects@1.15.9: {} + + for-each@0.3.3: + dependencies: + is-callable: 1.2.7 + + foreground-child@3.3.0: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + + foreground-child@3.3.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + + fork-ts-checker-webpack-plugin@6.5.3(eslint@8.57.1)(typescript@5.6.3)(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))): + dependencies: + '@babel/code-frame': 7.26.0 + '@types/json-schema': 7.0.15 + chalk: 4.1.2 + chokidar: 3.6.0 + cosmiconfig: 6.0.0 + deepmerge: 4.3.1 + fs-extra: 9.1.0 + glob: 7.2.3 + memfs: 3.5.3 + minimatch: 3.1.2 + schema-utils: 2.7.0 + semver: 7.7.2 + tapable: 1.1.3 + typescript: 5.6.3 + webpack: 5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(esbuild@0.25.6) + optionalDependencies: + eslint: 8.57.1 + + form-data@4.0.4: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.2 + mime-types: 2.1.35 + + formdata-polyfill@4.0.10: + dependencies: + fetch-blob: 3.2.0 + + forwarded@0.2.0: {} + + fraction.js@4.3.7: {} + + framer-motion@12.7.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + dependencies: + motion-dom: 12.23.6 + motion-utils: 12.23.6 + tslib: 2.8.0 + optionalDependencies: + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + + fresh@0.5.2: {} + + fs-constants@1.0.0: {} + + fs-extra@10.1.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.1 + + fs-extra@9.1.0: + dependencies: + at-least-node: 1.0.0 + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.1 + + fs-minipass@2.1.0: + dependencies: + minipass: 3.3.6 + + fs-minipass@3.0.3: + dependencies: + minipass: 7.1.2 + + fs-monkey@1.0.6: {} + + fs.realpath@1.0.0: {} + + fsevents@2.3.2: + optional: true + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + function.prototype.name@1.1.6: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + functions-have-names: 1.2.3 + + functional-red-black-tree@1.0.1: + optional: true + + functions-have-names@1.2.3: {} + + gauge@3.0.2: + dependencies: + aproba: 2.0.0 + color-support: 1.1.3 + console-control-strings: 1.1.0 + has-unicode: 2.0.1 + object-assign: 4.1.1 + signal-exit: 3.0.7 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wide-align: 1.1.5 + + gauge@4.0.4: + dependencies: + aproba: 2.0.0 + color-support: 1.1.3 + console-control-strings: 1.1.0 + has-unicode: 2.0.1 + signal-exit: 3.0.7 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wide-align: 1.1.5 + + gaxios@4.3.3(encoding@0.1.13): + dependencies: + abort-controller: 3.0.0 + extend: 3.0.2 + https-proxy-agent: 5.0.1 + is-stream: 2.0.1 + node-fetch: 2.7.0(encoding@0.1.13) + transitivePeerDependencies: + - encoding + - supports-color + + gaxios@6.7.1(encoding@0.1.13): + dependencies: + extend: 3.0.2 + https-proxy-agent: 7.0.5 + is-stream: 2.0.1 + node-fetch: 2.7.0(encoding@0.1.13) + uuid: 9.0.1 + transitivePeerDependencies: + - encoding + - supports-color + + gcp-metadata@4.3.1(encoding@0.1.13): + dependencies: + gaxios: 4.3.3(encoding@0.1.13) + json-bigint: 1.0.0 + transitivePeerDependencies: + - encoding + - supports-color + + gcp-metadata@6.1.0(encoding@0.1.13): + dependencies: + gaxios: 6.7.1(encoding@0.1.13) + json-bigint: 1.0.0 + transitivePeerDependencies: + - encoding + - supports-color + + generic-names@4.0.0: + dependencies: + loader-utils: 3.3.1 + + gensync@1.0.0-beta.2: {} + + get-caller-file@2.0.5: {} + + get-east-asian-width@1.3.0: {} + + get-intrinsic@1.2.4: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + has-proto: 1.0.3 + has-symbols: 1.0.3 + hasown: 2.0.2 + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-nonce@1.0.1: {} + + get-own-enumerable-property-symbols@3.0.2: {} + + get-package-type@0.1.0: {} + + get-port@5.1.1: {} + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + get-stream@6.0.1: {} + + get-symbol-description@1.0.2: + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 + + get-tsconfig@4.8.1: + dependencies: + resolve-pkg-maps: 1.0.0 + + github-from-package@0.0.0: {} + + github-username@6.0.0(encoding@0.1.13): + dependencies: + '@octokit/rest': 18.12.0(encoding@0.1.13) + transitivePeerDependencies: + - encoding + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + glob-to-regexp@0.4.1: {} + + glob@10.4.5: + dependencies: + foreground-child: 3.3.0 + jackspeak: 3.4.3 + minimatch: 9.0.5 + minipass: 7.1.2 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 + + glob@11.0.3: + dependencies: + foreground-child: 3.3.1 + jackspeak: 4.1.1 + minimatch: 10.0.3 + minipass: 7.1.2 + package-json-from-dist: 1.0.1 + path-scurry: 2.0.0 + + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + + glob@8.1.0: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 5.1.6 + once: 1.4.0 + + global-modules@2.0.0: + dependencies: + global-prefix: 3.0.0 + + global-prefix@3.0.0: + dependencies: + ini: 1.3.8 + kind-of: 6.0.3 + which: 1.3.1 + + globals@11.12.0: {} + + globals@13.24.0: + dependencies: + type-fest: 0.20.2 + + globalthis@1.0.4: + dependencies: + define-properties: 1.2.1 + gopd: 1.0.1 + + globalyzer@0.1.0: {} + + globby@11.1.0: + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.2 + ignore: 5.3.2 + merge2: 1.4.1 + slash: 3.0.0 + + globby@14.0.2: + dependencies: + '@sindresorhus/merge-streams': 2.3.0 + fast-glob: 3.3.2 + ignore: 5.3.2 + path-type: 5.0.0 + slash: 5.1.0 + unicorn-magic: 0.1.0 + + globby@6.1.0: + dependencies: + array-union: 1.0.2 + glob: 7.2.3 + object-assign: 4.1.1 + pify: 2.3.0 + pinkie-promise: 2.0.1 + + globrex@0.1.2: {} + + google-ads-api@17.1.0-rest-beta(encoding@0.1.13): + dependencies: + '@isaacs/ttlcache': 1.4.1 + axios: 1.8.2 + circ-json: 1.0.4 + google-ads-node: 14.0.0(encoding@0.1.13) + google-auth-library: 7.14.1(encoding@0.1.13) + google-gax: 4.4.1(encoding@0.1.13) + long: 4.0.0 + map-obj: 4.3.0 + stream-json: 1.9.0 + transitivePeerDependencies: + - debug + - encoding + - supports-color + + google-ads-node@14.0.0(encoding@0.1.13): + dependencies: + google-gax: 4.4.1(encoding@0.1.13) + lru-cache: 10.4.3 + transitivePeerDependencies: + - encoding + - supports-color + + google-auth-library@7.14.1(encoding@0.1.13): + dependencies: + arrify: 2.0.1 + base64-js: 1.5.1 + ecdsa-sig-formatter: 1.0.11 + fast-text-encoding: 1.0.6 + gaxios: 4.3.3(encoding@0.1.13) + gcp-metadata: 4.3.1(encoding@0.1.13) + gtoken: 5.3.2(encoding@0.1.13) + jws: 4.0.0 + lru-cache: 6.0.0 + transitivePeerDependencies: + - encoding + - supports-color + + google-auth-library@9.14.2(encoding@0.1.13): + dependencies: + base64-js: 1.5.1 + ecdsa-sig-formatter: 1.0.11 + gaxios: 6.7.1(encoding@0.1.13) + gcp-metadata: 6.1.0(encoding@0.1.13) + gtoken: 7.1.0(encoding@0.1.13) + jws: 4.0.0 + transitivePeerDependencies: + - encoding + - supports-color + + google-gax@4.4.1(encoding@0.1.13): + dependencies: + '@grpc/grpc-js': 1.12.2 + '@grpc/proto-loader': 0.7.13 + '@types/long': 4.0.2 + abort-controller: 3.0.0 + duplexify: 4.1.3 + google-auth-library: 9.14.2(encoding@0.1.13) + node-fetch: 2.7.0(encoding@0.1.13) + object-hash: 3.0.0 + proto3-json-serializer: 2.0.2 + protobufjs: 7.4.0 + retry-request: 7.0.2(encoding@0.1.13) + uuid: 9.0.1 + transitivePeerDependencies: + - encoding + - supports-color + + google-p12-pem@3.1.4: + dependencies: + node-forge: 1.3.1 + + googleapis-common@7.2.0(encoding@0.1.13): + dependencies: + extend: 3.0.2 + gaxios: 6.7.1(encoding@0.1.13) + google-auth-library: 9.14.2(encoding@0.1.13) + qs: 6.13.0 + url-template: 2.0.8 + uuid: 9.0.1 + transitivePeerDependencies: + - encoding + - supports-color + + googleapis@126.0.1(encoding@0.1.13): + dependencies: + google-auth-library: 9.14.2(encoding@0.1.13) + googleapis-common: 7.2.0(encoding@0.1.13) + transitivePeerDependencies: + - encoding + - supports-color + + gopd@1.0.1: + dependencies: + get-intrinsic: 1.2.4 + + gopd@1.2.0: {} + + graceful-fs@4.2.11: {} + + graphemer@1.4.0: {} + + grouped-queue@2.0.0: {} + + gtoken@5.3.2(encoding@0.1.13): + dependencies: + gaxios: 4.3.3(encoding@0.1.13) + google-p12-pem: 3.1.4 + jws: 4.0.0 + transitivePeerDependencies: + - encoding + - supports-color + + gtoken@7.1.0(encoding@0.1.13): + dependencies: + gaxios: 6.7.1(encoding@0.1.13) + jws: 4.0.0 + transitivePeerDependencies: + - encoding + - supports-color + + gzip-size@3.0.0: + dependencies: + duplexer: 0.1.2 + + gzip-size@6.0.0: + dependencies: + duplexer: 0.1.2 + + handle-thing@2.0.1: {} + + harmony-reflect@1.6.2: {} + + has-ansi@2.0.0: + dependencies: + ansi-regex: 2.1.1 + + has-bigints@1.0.2: {} + + has-flag@3.0.0: {} + + has-flag@4.0.0: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.0 + + has-proto@1.0.3: {} + + has-symbols@1.0.3: {} + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.0.3 + + has-unicode@2.0.1: {} + + hash.js@1.1.7: + dependencies: + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + he@1.2.0: {} + + highlight.js@11.10.0: {} + + hmac-drbg@1.0.1: + dependencies: + hash.js: 1.1.7 + minimalistic-assert: 1.0.1 + minimalistic-crypto-utils: 1.0.1 + + hoist-non-react-statics@3.3.2: + dependencies: + react-is: 16.13.1 + + hoopy@0.1.4: {} + + hosted-git-info@2.8.9: {} + + hosted-git-info@4.1.0: + dependencies: + lru-cache: 6.0.0 + + hosted-git-info@6.1.1: + dependencies: + lru-cache: 7.18.3 + + hpack.js@2.1.6: + dependencies: + inherits: 2.0.4 + obuf: 1.1.2 + readable-stream: 2.3.8 + wbuf: 1.7.3 + + html-encoding-sniffer@2.0.1: + dependencies: + whatwg-encoding: 1.0.5 + + html-entities@2.5.2: {} + + html-escaper@2.0.2: {} + + html-minifier-terser@6.1.0: + dependencies: + camel-case: 4.1.2 + clean-css: 5.3.3 + commander: 8.3.0 + he: 1.2.0 + param-case: 3.0.4 + relateurl: 0.2.7 + terser: 5.36.0 + + html-to-text@9.0.5: + dependencies: + '@selderee/plugin-htmlparser2': 0.11.0 + deepmerge: 4.3.1 + dom-serializer: 2.0.0 + htmlparser2: 8.0.2 + selderee: 0.11.0 + + html-webpack-plugin@5.6.3(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))): + dependencies: + '@types/html-minifier-terser': 6.1.0 + html-minifier-terser: 6.1.0 + lodash: 4.17.21 + pretty-error: 4.0.0 + tapable: 2.2.1 + optionalDependencies: + webpack: 5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(esbuild@0.25.6) + + htmlencode@0.0.4: {} + + htmlnano@2.1.1(cssnano@7.0.6(postcss@8.4.47))(postcss@8.4.47)(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3): + dependencies: + cosmiconfig: 9.0.0(typescript@5.6.3) + posthtml: 0.16.6 + timsort: 0.3.0 + optionalDependencies: + cssnano: 7.0.6(postcss@8.4.47) + postcss: 8.4.47 + relateurl: 0.2.7 + svgo: 3.3.2 + terser: 5.36.0 + transitivePeerDependencies: + - typescript + + htmlparser2@5.0.1: + dependencies: + domelementtype: 2.3.0 + domhandler: 3.3.0 + domutils: 2.8.0 + entities: 2.2.0 + + htmlparser2@6.1.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 4.3.1 + domutils: 2.8.0 + entities: 2.2.0 + + htmlparser2@7.2.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 4.3.1 + domutils: 2.8.0 + entities: 3.0.1 + + htmlparser2@8.0.2: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.1.0 + entities: 4.5.0 + + htmlparser2@9.1.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.1.0 + entities: 4.5.0 + + http-cache-semantics@4.1.1: {} + + http-deceiver@1.2.7: {} + + http-errors@1.6.3: + dependencies: + depd: 1.1.2 + inherits: 2.0.3 + setprototypeof: 1.1.0 + statuses: 1.5.0 + + http-errors@2.0.0: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.1 + toidentifier: 1.0.1 + + http-parser-js@0.5.8: {} + + http-proxy-agent@4.0.1: + dependencies: + '@tootallnate/once': 1.1.2 + agent-base: 6.0.2 + debug: 4.3.7(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + + http-proxy-agent@5.0.0: + dependencies: + '@tootallnate/once': 2.0.0 + agent-base: 6.0.2 + debug: 4.3.7(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + + http-proxy-middleware@2.0.7(@types/express@4.17.21): + dependencies: + '@types/http-proxy': 1.17.15 + http-proxy: 1.18.1 + is-glob: 4.0.3 + is-plain-obj: 3.0.0 + micromatch: 4.0.8 + optionalDependencies: + '@types/express': 4.17.21 + transitivePeerDependencies: + - debug + + http-proxy@1.18.1: + dependencies: + eventemitter3: 4.0.7 + follow-redirects: 1.15.9 + requires-port: 1.0.0 + transitivePeerDependencies: + - debug + + https-proxy-agent@5.0.1: + dependencies: + agent-base: 6.0.2 + debug: 4.3.7(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + + https-proxy-agent@7.0.5: + dependencies: + agent-base: 7.1.1 + debug: 4.3.7(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + + human-signals@2.1.0: {} + + humanize-ms@1.2.1: + dependencies: + ms: 2.1.3 + + husky@8.0.3: {} + + hyphenate-style-name@1.1.0: {} + + iconv-lite@0.4.24: + dependencies: + safer-buffer: 2.1.2 + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + + icss-replace-symbols@1.1.0: {} + + icss-utils@5.1.0(postcss@8.4.47): + dependencies: + postcss: 8.4.47 + + idb@7.1.1: {} + + identity-obj-proxy@3.0.0: + dependencies: + harmony-reflect: 1.6.2 + + ieee754@1.2.1: {} + + ignore-by-default@1.0.1: {} + + ignore-walk@4.0.1: + dependencies: + minimatch: 3.1.2 + + ignore-walk@6.0.5: + dependencies: + minimatch: 9.0.5 + + ignore@5.3.2: {} + + image-size@0.5.5: + optional: true + + immer@9.0.21: {} + + import-cwd@3.0.0: + dependencies: + import-from: 3.0.0 + + import-fresh@3.3.0: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + import-from@3.0.0: + dependencies: + resolve-from: 5.0.0 + + import-local@3.2.0: + dependencies: + pkg-dir: 4.2.0 + resolve-cwd: 3.0.0 + + imurmurhash@0.1.4: {} + + indent-string@4.0.0: {} + + infer-owner@1.0.4: {} + + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.3: {} + + inherits@2.0.4: {} + + ini@1.3.8: {} + + inline-style-prefixer@7.0.1: + dependencies: + css-in-js-utils: 3.1.0 + + inquirer@8.2.6: + dependencies: + ansi-escapes: 4.3.2 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-width: 3.0.0 + external-editor: 3.1.0 + figures: 3.2.0 + lodash: 4.17.21 + mute-stream: 0.0.8 + ora: 5.4.1 + run-async: 2.4.1 + rxjs: 7.8.1 + string-width: 4.2.3 + strip-ansi: 6.0.1 + through: 2.3.8 + wrap-ansi: 6.2.0 + + inquirer@9.3.7: + dependencies: + '@inquirer/figures': 1.0.7 + ansi-escapes: 4.3.2 + cli-width: 4.1.0 + external-editor: 3.1.0 + mute-stream: 1.0.0 + ora: 5.4.1 + run-async: 3.0.0 + rxjs: 7.8.1 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 6.2.0 + yoctocolors-cjs: 2.1.2 + + intercom-client@5.0.0: + dependencies: + axios: 1.8.2 + htmlencode: 0.0.4 + lodash: 4.17.21 + transitivePeerDependencies: + - debug + + internal-slot@1.0.7: + dependencies: + es-errors: 1.3.0 + hasown: 2.0.2 + side-channel: 1.0.6 + + internmap@2.0.3: {} + + interpret@1.4.0: {} + + interpret@3.1.1: {} + + ioredis@5.4.1: + dependencies: + '@ioredis/commands': 1.2.0 + cluster-key-slot: 1.1.2 + debug: 4.3.7(supports-color@5.5.0) + denque: 2.1.0 + lodash.defaults: 4.2.0 + lodash.isarguments: 3.1.0 + redis-errors: 1.2.0 + redis-parser: 3.0.0 + standard-as-callback: 2.1.0 + transitivePeerDependencies: + - supports-color + + ip-address@9.0.5: + dependencies: + jsbn: 1.1.0 + sprintf-js: 1.1.3 + + ip6addr@0.2.5: + dependencies: + assert-plus: 1.0.0 + jsprim: 2.0.2 + + ip@2.0.1: {} + + ipaddr.js@1.9.1: {} + + ipaddr.js@2.2.0: {} + + is-arguments@1.1.1: + dependencies: + call-bind: 1.0.7 + has-tostringtag: 1.0.2 + + is-array-buffer@3.0.4: + dependencies: + call-bind: 1.0.7 + get-intrinsic: 1.2.4 + + is-arrayish@0.2.1: {} + + is-arrayish@0.3.2: {} + + is-async-function@2.0.0: + dependencies: + has-tostringtag: 1.0.2 + + is-bigint@1.0.4: + dependencies: + has-bigints: 1.0.2 + + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + + is-boolean-object@1.1.2: + dependencies: + call-bind: 1.0.7 + has-tostringtag: 1.0.2 + + is-bun-module@1.2.1: + dependencies: + semver: 7.7.2 + + is-callable@1.2.7: {} + + is-core-module@2.15.1: + dependencies: + hasown: 2.0.2 + + is-data-view@1.0.1: + dependencies: + is-typed-array: 1.1.13 + + is-date-object@1.0.5: + dependencies: + has-tostringtag: 1.0.2 + + is-docker@2.2.1: {} + + is-extglob@2.1.1: {} + + is-finalizationregistry@1.0.2: + dependencies: + call-bind: 1.0.7 + + is-fullwidth-code-point@3.0.0: {} + + is-generator-fn@2.1.0: {} + + is-generator-function@1.0.10: + dependencies: + has-tostringtag: 1.0.2 + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-interactive@1.0.0: {} + + is-interactive@2.0.0: {} + + is-json@2.0.1: {} + + is-lambda@1.0.1: {} + + is-map@2.0.3: {} + + is-module@1.0.0: {} + + is-negative-zero@2.0.3: {} + + is-number-object@1.0.7: + dependencies: + has-tostringtag: 1.0.2 + + is-number@7.0.0: {} + + is-obj@1.0.1: {} + + is-path-cwd@2.2.0: {} + + is-path-in-cwd@2.1.0: + dependencies: + is-path-inside: 2.1.0 + + is-path-inside@2.1.0: + dependencies: + path-is-inside: 1.0.2 + + is-path-inside@3.0.3: {} + + is-plain-obj@2.1.0: {} + + is-plain-obj@3.0.0: {} + + is-plain-object@2.0.4: + dependencies: + isobject: 3.0.1 + + is-plain-object@5.0.0: {} + + is-potential-custom-element-name@1.0.1: {} + + is-reference@1.2.1: + dependencies: + '@types/estree': 1.0.6 + + is-regex@1.1.4: + dependencies: + call-bind: 1.0.7 + has-tostringtag: 1.0.2 + + is-regexp@1.0.0: {} + + is-root@2.1.0: {} + + is-scoped@2.1.0: + dependencies: + scoped-regex: 2.1.0 + + is-set@2.0.3: {} + + is-shared-array-buffer@1.0.3: + dependencies: + call-bind: 1.0.7 + + is-stream@2.0.1: {} + + is-string@1.0.7: + dependencies: + has-tostringtag: 1.0.2 + + is-symbol@1.0.4: + dependencies: + has-symbols: 1.0.3 + + is-typed-array@1.1.13: + dependencies: + which-typed-array: 1.1.15 + + is-typedarray@1.0.0: {} + + is-unicode-supported@0.1.0: {} + + is-unicode-supported@1.3.0: {} + + is-unicode-supported@2.1.0: {} + + is-utf8@0.2.1: {} + + is-valid-domain@0.1.6: + dependencies: + punycode: 2.3.1 + + is-weakmap@2.0.2: {} + + is-weakref@1.0.2: + dependencies: + call-bind: 1.0.7 + + is-weakset@2.0.3: + dependencies: + call-bind: 1.0.7 + get-intrinsic: 1.2.4 + + is-what@3.14.1: {} + + is-wsl@2.2.0: + dependencies: + is-docker: 2.2.1 + + isarray@1.0.0: {} + + isarray@2.0.5: {} + + isbinaryfile@4.0.10: {} + + isbinaryfile@5.0.4: {} + + isexe@2.0.0: {} + + isobject@3.0.1: {} + + isolated-vm@6.0.0: + dependencies: + prebuild-install: 7.1.3 + + istanbul-lib-coverage@3.2.2: {} + + istanbul-lib-instrument@5.2.1: + dependencies: + '@babel/core': 7.26.0 + '@babel/parser': 7.28.0 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.2 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + istanbul-lib-instrument@6.0.3: + dependencies: + '@babel/core': 7.26.0 + '@babel/parser': 7.28.0 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.2 + semver: 7.7.2 + transitivePeerDependencies: + - supports-color + + istanbul-lib-report@3.0.1: + dependencies: + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 + + istanbul-lib-source-maps@4.0.1: + dependencies: + debug: 4.3.7(supports-color@5.5.0) + istanbul-lib-coverage: 3.2.2 + source-map: 0.6.1 + transitivePeerDependencies: + - supports-color + + istanbul-reports@3.1.7: + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + + iterator.prototype@1.1.3: + dependencies: + define-properties: 1.2.1 + get-intrinsic: 1.2.4 + has-symbols: 1.0.3 + reflect.getprototypeof: 1.0.6 + set-function-name: 2.0.2 + + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + + jackspeak@4.1.1: + dependencies: + '@isaacs/cliui': 8.0.2 + + jake@10.9.2: + dependencies: + async: 3.2.6 + chalk: 4.1.2 + filelist: 1.0.4 + minimatch: 3.1.2 + + jest-changed-files@27.5.1: + dependencies: + '@jest/types': 27.5.1 + execa: 5.1.1 + throat: 6.0.2 + + jest-changed-files@28.1.3: + dependencies: + execa: 5.1.1 + p-limit: 3.1.0 + + jest-changed-files@29.7.0: + dependencies: + execa: 5.1.1 + jest-util: 29.7.0 + p-limit: 3.1.0 + + jest-circus@27.5.1: + dependencies: + '@jest/environment': 27.5.1 + '@jest/test-result': 27.5.1 + '@jest/types': 27.5.1 + '@types/node': 18.19.61 + chalk: 4.1.2 + co: 4.6.0 + dedent: 0.7.0 + expect: 27.5.1 + is-generator-fn: 2.1.0 + jest-each: 27.5.1 + jest-matcher-utils: 27.5.1 + jest-message-util: 27.5.1 + jest-runtime: 27.5.1 + jest-snapshot: 27.5.1 + jest-util: 27.5.1 + pretty-format: 27.5.1 + slash: 3.0.0 + stack-utils: 2.0.6 + throat: 6.0.2 + transitivePeerDependencies: + - supports-color + + jest-circus@28.1.3: + dependencies: + '@jest/environment': 28.1.3 + '@jest/expect': 28.1.3 + '@jest/test-result': 28.1.3 + '@jest/types': 28.1.3 + '@types/node': 18.19.61 + chalk: 4.1.2 + co: 4.6.0 + dedent: 0.7.0 + is-generator-fn: 2.1.0 + jest-each: 28.1.3 + jest-matcher-utils: 28.1.3 + jest-message-util: 28.1.3 + jest-runtime: 28.1.3 + jest-snapshot: 28.1.3 + jest-util: 28.1.3 + p-limit: 3.1.0 + pretty-format: 28.1.3 + slash: 3.0.0 + stack-utils: 2.0.6 + transitivePeerDependencies: + - supports-color + + jest-circus@29.7.0(babel-plugin-macros@3.1.0): + dependencies: + '@jest/environment': 29.7.0 + '@jest/expect': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 18.19.61 + chalk: 4.1.2 + co: 4.6.0 + dedent: 1.5.3(babel-plugin-macros@3.1.0) + is-generator-fn: 2.1.0 + jest-each: 29.7.0 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + p-limit: 3.1.0 + pretty-format: 29.7.0 + pure-rand: 6.1.0 + slash: 3.0.0 + stack-utils: 2.0.6 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + + jest-cli@27.5.1(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)): + dependencies: + '@jest/core': 27.5.1(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)) + '@jest/test-result': 27.5.1 + '@jest/types': 27.5.1 + chalk: 4.1.2 + exit: 0.1.2 + graceful-fs: 4.2.11 + import-local: 3.2.0 + jest-config: 27.5.1(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)) + jest-util: 27.5.1 + jest-validate: 27.5.1 + prompts: 2.4.2 + yargs: 16.2.0 + transitivePeerDependencies: + - bufferutil + - canvas + - supports-color + - ts-node + - utf-8-validate + + jest-cli@28.1.3(@types/node@18.19.61)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)): + dependencies: + '@jest/core': 28.1.3(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)) + '@jest/test-result': 28.1.3 + '@jest/types': 28.1.3 + chalk: 4.1.2 + exit: 0.1.2 + graceful-fs: 4.2.11 + import-local: 3.2.0 + jest-config: 28.1.3(@types/node@18.19.61)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)) + jest-util: 28.1.3 + jest-validate: 28.1.3 + prompts: 2.4.2 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - supports-color + - ts-node + + jest-cli@29.7.0(@types/node@18.19.61)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)): + dependencies: + '@jest/core': 29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)) + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + chalk: 4.1.2 + create-jest: 29.7.0(@types/node@18.19.61)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)) + exit: 0.1.2 + import-local: 3.2.0 + jest-config: 29.7.0(@types/node@18.19.61)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)) + jest-util: 29.7.0 + jest-validate: 29.7.0 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + + jest-cli@29.7.0(@types/node@18.19.61)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)): + dependencies: + '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)) + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + chalk: 4.1.2 + create-jest: 29.7.0(@types/node@18.19.61)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)) + exit: 0.1.2 + import-local: 3.2.0 + jest-config: 29.7.0(@types/node@18.19.61)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)) + jest-util: 29.7.0 + jest-validate: 29.7.0 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + + jest-cli@29.7.0(@types/node@18.19.61)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@22.14.1)(typescript@5.6.3)): + dependencies: + '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@22.14.1)(typescript@5.6.3)) + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + chalk: 4.1.2 + create-jest: 29.7.0(@types/node@18.19.61)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@22.14.1)(typescript@5.6.3)) + exit: 0.1.2 + import-local: 3.2.0 + jest-config: 29.7.0(@types/node@18.19.61)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@22.14.1)(typescript@5.6.3)) + jest-util: 29.7.0 + jest-validate: 29.7.0 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + + jest-cli@29.7.0(@types/node@22.14.1)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@22.14.1)(typescript@5.6.3)): + dependencies: + '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@22.14.1)(typescript@5.6.3)) + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + chalk: 4.1.2 + create-jest: 29.7.0(@types/node@22.14.1)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@22.14.1)(typescript@5.6.3)) + exit: 0.1.2 + import-local: 3.2.0 + jest-config: 29.7.0(@types/node@22.14.1)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@22.14.1)(typescript@5.6.3)) + jest-util: 29.7.0 + jest-validate: 29.7.0 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + + jest-config@27.5.1(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)): + dependencies: + '@babel/core': 7.26.0 + '@jest/test-sequencer': 27.5.1 + '@jest/types': 27.5.1 + babel-jest: 27.5.1(@babel/core@7.26.0) + chalk: 4.1.2 + ci-info: 3.9.0 + deepmerge: 4.3.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-circus: 27.5.1 + jest-environment-jsdom: 27.5.1 + jest-environment-node: 27.5.1 + jest-get-type: 27.5.1 + jest-jasmine2: 27.5.1 + jest-regex-util: 27.5.1 + jest-resolve: 27.5.1 + jest-runner: 27.5.1 + jest-util: 27.5.1 + jest-validate: 27.5.1 + micromatch: 4.0.8 + parse-json: 5.2.0 + pretty-format: 27.5.1 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + ts-node: 10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3) + transitivePeerDependencies: + - bufferutil + - canvas + - supports-color + - utf-8-validate + + jest-config@28.1.3(@types/node@18.19.61)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)): + dependencies: + '@babel/core': 7.26.0 + '@jest/test-sequencer': 28.1.3 + '@jest/types': 28.1.3 + babel-jest: 28.1.3(@babel/core@7.26.0) + chalk: 4.1.2 + ci-info: 3.9.0 + deepmerge: 4.3.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-circus: 28.1.3 + jest-environment-node: 28.1.3 + jest-get-type: 28.0.2 + jest-regex-util: 28.0.2 + jest-resolve: 28.1.3 + jest-runner: 28.1.3 + jest-util: 28.1.3 + jest-validate: 28.1.3 + micromatch: 4.0.8 + parse-json: 5.2.0 + pretty-format: 28.1.3 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 18.19.61 + ts-node: 10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3) + transitivePeerDependencies: + - supports-color + + jest-config@29.7.0(@types/node@18.19.61)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)): + dependencies: + '@babel/core': 7.26.0 + '@jest/test-sequencer': 29.7.0 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.26.0) + chalk: 4.1.2 + ci-info: 3.9.0 + deepmerge: 4.3.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-circus: 29.7.0(babel-plugin-macros@3.1.0) + jest-environment-node: 29.7.0 + jest-get-type: 29.6.3 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-runner: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + micromatch: 4.0.8 + parse-json: 5.2.0 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 18.19.61 + ts-node: 10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3) + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + + jest-config@29.7.0(@types/node@18.19.61)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)): + dependencies: + '@babel/core': 7.26.0 + '@jest/test-sequencer': 29.7.0 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.26.0) + chalk: 4.1.2 + ci-info: 3.9.0 + deepmerge: 4.3.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-circus: 29.7.0(babel-plugin-macros@3.1.0) + jest-environment-node: 29.7.0 + jest-get-type: 29.6.3 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-runner: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + micromatch: 4.0.8 + parse-json: 5.2.0 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 18.19.61 + ts-node: 10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3) + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + + jest-config@29.7.0(@types/node@18.19.61)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@22.14.1)(typescript@5.6.3)): + dependencies: + '@babel/core': 7.26.0 + '@jest/test-sequencer': 29.7.0 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.26.0) + chalk: 4.1.2 + ci-info: 3.9.0 + deepmerge: 4.3.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-circus: 29.7.0(babel-plugin-macros@3.1.0) + jest-environment-node: 29.7.0 + jest-get-type: 29.6.3 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-runner: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + micromatch: 4.0.8 + parse-json: 5.2.0 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 18.19.61 + ts-node: 10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@22.14.1)(typescript@5.6.3) + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + + jest-config@29.7.0(@types/node@22.14.1)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@22.14.1)(typescript@5.6.3)): + dependencies: + '@babel/core': 7.26.0 + '@jest/test-sequencer': 29.7.0 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.26.0) + chalk: 4.1.2 + ci-info: 3.9.0 + deepmerge: 4.3.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-circus: 29.7.0(babel-plugin-macros@3.1.0) + jest-environment-node: 29.7.0 + jest-get-type: 29.6.3 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-runner: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + micromatch: 4.0.8 + parse-json: 5.2.0 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 22.14.1 + ts-node: 10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@22.14.1)(typescript@5.6.3) + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + + jest-diff@27.5.1: + dependencies: + chalk: 4.1.2 + diff-sequences: 27.5.1 + jest-get-type: 27.5.1 + pretty-format: 27.5.1 + + jest-diff@28.1.3: + dependencies: + chalk: 4.1.2 + diff-sequences: 28.1.1 + jest-get-type: 28.0.2 + pretty-format: 28.1.3 + + jest-diff@29.7.0: + dependencies: + chalk: 4.1.2 + diff-sequences: 29.6.3 + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + + jest-docblock@27.5.1: + dependencies: + detect-newline: 3.1.0 + + jest-docblock@28.1.1: + dependencies: + detect-newline: 3.1.0 + + jest-docblock@29.7.0: + dependencies: + detect-newline: 3.1.0 + + jest-each@27.5.1: + dependencies: + '@jest/types': 27.5.1 + chalk: 4.1.2 + jest-get-type: 27.5.1 + jest-util: 27.5.1 + pretty-format: 27.5.1 + + jest-each@28.1.3: + dependencies: + '@jest/types': 28.1.3 + chalk: 4.1.2 + jest-get-type: 28.0.2 + jest-util: 28.1.3 + pretty-format: 28.1.3 + + jest-each@29.7.0: + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + jest-get-type: 29.6.3 + jest-util: 29.7.0 + pretty-format: 29.7.0 + + jest-environment-jsdom@27.5.1: + dependencies: + '@jest/environment': 27.5.1 + '@jest/fake-timers': 27.5.1 + '@jest/types': 27.5.1 + '@types/node': 18.19.61 + jest-mock: 27.5.1 + jest-util: 27.5.1 + jsdom: 16.7.0 + transitivePeerDependencies: + - bufferutil + - canvas + - supports-color + - utf-8-validate + + jest-environment-node@27.5.1: + dependencies: + '@jest/environment': 27.5.1 + '@jest/fake-timers': 27.5.1 + '@jest/types': 27.5.1 + '@types/node': 18.19.61 + jest-mock: 27.5.1 + jest-util: 27.5.1 + + jest-environment-node@28.1.3: + dependencies: + '@jest/environment': 28.1.3 + '@jest/fake-timers': 28.1.3 + '@jest/types': 28.1.3 + '@types/node': 18.19.61 + jest-mock: 28.1.3 + jest-util: 28.1.3 + + jest-environment-node@29.7.0: + dependencies: + '@jest/environment': 29.7.0 + '@jest/fake-timers': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 18.19.61 + jest-mock: 29.7.0 + jest-util: 29.7.0 + + jest-get-type@27.5.1: {} + + jest-get-type@28.0.2: {} + + jest-get-type@29.6.3: {} + + jest-haste-map@27.5.1: + dependencies: + '@jest/types': 27.5.1 + '@types/graceful-fs': 4.1.9 + '@types/node': 18.19.61 + anymatch: 3.1.3 + fb-watchman: 2.0.2 + graceful-fs: 4.2.11 + jest-regex-util: 27.5.1 + jest-serializer: 27.5.1 + jest-util: 27.5.1 + jest-worker: 27.5.1 + micromatch: 4.0.8 + walker: 1.0.8 + optionalDependencies: + fsevents: 2.3.3 + + jest-haste-map@28.1.3: + dependencies: + '@jest/types': 28.1.3 + '@types/graceful-fs': 4.1.9 + '@types/node': 18.19.61 + anymatch: 3.1.3 + fb-watchman: 2.0.2 + graceful-fs: 4.2.11 + jest-regex-util: 28.0.2 + jest-util: 28.1.3 + jest-worker: 28.1.3 + micromatch: 4.0.8 + walker: 1.0.8 + optionalDependencies: + fsevents: 2.3.3 + + jest-haste-map@29.7.0: + dependencies: + '@jest/types': 29.6.3 + '@types/graceful-fs': 4.1.9 + '@types/node': 18.19.61 + anymatch: 3.1.3 + fb-watchman: 2.0.2 + graceful-fs: 4.2.11 + jest-regex-util: 29.6.3 + jest-util: 29.7.0 + jest-worker: 29.7.0 + micromatch: 4.0.8 + walker: 1.0.8 + optionalDependencies: + fsevents: 2.3.3 + + jest-jasmine2@27.5.1: + dependencies: + '@jest/environment': 27.5.1 + '@jest/source-map': 27.5.1 + '@jest/test-result': 27.5.1 + '@jest/types': 27.5.1 + '@types/node': 18.19.61 + chalk: 4.1.2 + co: 4.6.0 + expect: 27.5.1 + is-generator-fn: 2.1.0 + jest-each: 27.5.1 + jest-matcher-utils: 27.5.1 + jest-message-util: 27.5.1 + jest-runtime: 27.5.1 + jest-snapshot: 27.5.1 + jest-util: 27.5.1 + pretty-format: 27.5.1 + throat: 6.0.2 + transitivePeerDependencies: + - supports-color + + jest-leak-detector@27.5.1: + dependencies: + jest-get-type: 27.5.1 + pretty-format: 27.5.1 + + jest-leak-detector@28.1.3: + dependencies: + jest-get-type: 28.0.2 + pretty-format: 28.1.3 + + jest-leak-detector@29.7.0: + dependencies: + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + + jest-matcher-utils@27.5.1: + dependencies: + chalk: 4.1.2 + jest-diff: 27.5.1 + jest-get-type: 27.5.1 + pretty-format: 27.5.1 + + jest-matcher-utils@28.1.3: + dependencies: + chalk: 4.1.2 + jest-diff: 28.1.3 + jest-get-type: 28.0.2 + pretty-format: 28.1.3 + + jest-matcher-utils@29.7.0: + dependencies: + chalk: 4.1.2 + jest-diff: 29.7.0 + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + + jest-message-util@27.5.1: + dependencies: + '@babel/code-frame': 7.27.1 + '@jest/types': 27.5.1 + '@types/stack-utils': 2.0.3 + chalk: 4.1.2 + graceful-fs: 4.2.11 + micromatch: 4.0.8 + pretty-format: 27.5.1 + slash: 3.0.0 + stack-utils: 2.0.6 + + jest-message-util@28.1.3: + dependencies: + '@babel/code-frame': 7.26.0 + '@jest/types': 28.1.3 + '@types/stack-utils': 2.0.3 + chalk: 4.1.2 + graceful-fs: 4.2.11 + micromatch: 4.0.8 + pretty-format: 28.1.3 + slash: 3.0.0 + stack-utils: 2.0.6 + + jest-message-util@29.7.0: + dependencies: + '@babel/code-frame': 7.26.0 + '@jest/types': 29.6.3 + '@types/stack-utils': 2.0.3 + chalk: 4.1.2 + graceful-fs: 4.2.11 + micromatch: 4.0.8 + pretty-format: 29.7.0 + slash: 3.0.0 + stack-utils: 2.0.6 + + jest-mock@27.5.1: + dependencies: + '@jest/types': 27.5.1 + '@types/node': 18.19.61 + + jest-mock@28.1.3: + dependencies: + '@jest/types': 28.1.3 + '@types/node': 18.19.61 + + jest-mock@29.7.0: + dependencies: + '@jest/types': 29.6.3 + '@types/node': 18.19.61 + jest-util: 29.7.0 + + jest-pnp-resolver@1.2.3(jest-resolve@27.5.1): + optionalDependencies: + jest-resolve: 27.5.1 + + jest-pnp-resolver@1.2.3(jest-resolve@28.1.3): + optionalDependencies: + jest-resolve: 28.1.3 + + jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): + optionalDependencies: + jest-resolve: 29.7.0 + + jest-regex-util@27.5.1: {} + + jest-regex-util@28.0.2: {} + + jest-regex-util@29.6.3: {} + + jest-resolve-dependencies@27.5.1: + dependencies: + '@jest/types': 27.5.1 + jest-regex-util: 27.5.1 + jest-snapshot: 27.5.1 + transitivePeerDependencies: + - supports-color + + jest-resolve-dependencies@28.1.3: + dependencies: + jest-regex-util: 28.0.2 + jest-snapshot: 28.1.3 + transitivePeerDependencies: + - supports-color + + jest-resolve-dependencies@29.7.0: + dependencies: + jest-regex-util: 29.6.3 + jest-snapshot: 29.7.0 + transitivePeerDependencies: + - supports-color + + jest-resolve@27.5.1: + dependencies: + '@jest/types': 27.5.1 + chalk: 4.1.2 + graceful-fs: 4.2.11 + jest-haste-map: 27.5.1 + jest-pnp-resolver: 1.2.3(jest-resolve@27.5.1) + jest-util: 27.5.1 + jest-validate: 27.5.1 + resolve: 1.22.8 + resolve.exports: 1.1.1 + slash: 3.0.0 + + jest-resolve@28.1.3: + dependencies: + chalk: 4.1.2 + graceful-fs: 4.2.11 + jest-haste-map: 28.1.3 + jest-pnp-resolver: 1.2.3(jest-resolve@28.1.3) + jest-util: 28.1.3 + jest-validate: 28.1.3 + resolve: 1.22.8 + resolve.exports: 1.1.1 + slash: 3.0.0 + + jest-resolve@29.7.0: + dependencies: + chalk: 4.1.2 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-pnp-resolver: 1.2.3(jest-resolve@29.7.0) + jest-util: 29.7.0 + jest-validate: 29.7.0 + resolve: 1.22.8 + resolve.exports: 2.0.2 + slash: 3.0.0 + + jest-runner@27.5.1: + dependencies: + '@jest/console': 27.5.1 + '@jest/environment': 27.5.1 + '@jest/test-result': 27.5.1 + '@jest/transform': 27.5.1 + '@jest/types': 27.5.1 + '@types/node': 18.19.61 + chalk: 4.1.2 + emittery: 0.8.1 + graceful-fs: 4.2.11 + jest-docblock: 27.5.1 + jest-environment-jsdom: 27.5.1 + jest-environment-node: 27.5.1 + jest-haste-map: 27.5.1 + jest-leak-detector: 27.5.1 + jest-message-util: 27.5.1 + jest-resolve: 27.5.1 + jest-runtime: 27.5.1 + jest-util: 27.5.1 + jest-worker: 27.5.1 + source-map-support: 0.5.21 + throat: 6.0.2 + transitivePeerDependencies: + - bufferutil + - canvas + - supports-color + - utf-8-validate + + jest-runner@28.1.3: + dependencies: + '@jest/console': 28.1.3 + '@jest/environment': 28.1.3 + '@jest/test-result': 28.1.3 + '@jest/transform': 28.1.3 + '@jest/types': 28.1.3 + '@types/node': 18.19.61 + chalk: 4.1.2 + emittery: 0.10.2 + graceful-fs: 4.2.11 + jest-docblock: 28.1.1 + jest-environment-node: 28.1.3 + jest-haste-map: 28.1.3 + jest-leak-detector: 28.1.3 + jest-message-util: 28.1.3 + jest-resolve: 28.1.3 + jest-runtime: 28.1.3 + jest-util: 28.1.3 + jest-watcher: 28.1.3 + jest-worker: 28.1.3 + p-limit: 3.1.0 + source-map-support: 0.5.13 + transitivePeerDependencies: + - supports-color + + jest-runner@29.7.0: + dependencies: + '@jest/console': 29.7.0 + '@jest/environment': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 18.19.61 + chalk: 4.1.2 + emittery: 0.13.1 + graceful-fs: 4.2.11 + jest-docblock: 29.7.0 + jest-environment-node: 29.7.0 + jest-haste-map: 29.7.0 + jest-leak-detector: 29.7.0 + jest-message-util: 29.7.0 + jest-resolve: 29.7.0 + jest-runtime: 29.7.0 + jest-util: 29.7.0 + jest-watcher: 29.7.0 + jest-worker: 29.7.0 + p-limit: 3.1.0 + source-map-support: 0.5.13 + transitivePeerDependencies: + - supports-color + + jest-runtime@27.5.1: + dependencies: + '@jest/environment': 27.5.1 + '@jest/fake-timers': 27.5.1 + '@jest/globals': 27.5.1 + '@jest/source-map': 27.5.1 + '@jest/test-result': 27.5.1 + '@jest/transform': 27.5.1 + '@jest/types': 27.5.1 + chalk: 4.1.2 + cjs-module-lexer: 1.4.1 + collect-v8-coverage: 1.0.2 + execa: 5.1.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-haste-map: 27.5.1 + jest-message-util: 27.5.1 + jest-mock: 27.5.1 + jest-regex-util: 27.5.1 + jest-resolve: 27.5.1 + jest-snapshot: 27.5.1 + jest-util: 27.5.1 + slash: 3.0.0 + strip-bom: 4.0.0 + transitivePeerDependencies: + - supports-color + + jest-runtime@28.1.3: + dependencies: + '@jest/environment': 28.1.3 + '@jest/fake-timers': 28.1.3 + '@jest/globals': 28.1.3 + '@jest/source-map': 28.1.2 + '@jest/test-result': 28.1.3 + '@jest/transform': 28.1.3 + '@jest/types': 28.1.3 + chalk: 4.1.2 + cjs-module-lexer: 1.4.1 + collect-v8-coverage: 1.0.2 + execa: 5.1.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-haste-map: 28.1.3 + jest-message-util: 28.1.3 + jest-mock: 28.1.3 + jest-regex-util: 28.0.2 + jest-resolve: 28.1.3 + jest-snapshot: 28.1.3 + jest-util: 28.1.3 + slash: 3.0.0 + strip-bom: 4.0.0 + transitivePeerDependencies: + - supports-color + + jest-runtime@29.7.0: + dependencies: + '@jest/environment': 29.7.0 + '@jest/fake-timers': 29.7.0 + '@jest/globals': 29.7.0 + '@jest/source-map': 29.6.3 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 18.19.61 + chalk: 4.1.2 + cjs-module-lexer: 1.4.1 + collect-v8-coverage: 1.0.2 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-mock: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + slash: 3.0.0 + strip-bom: 4.0.0 + transitivePeerDependencies: + - supports-color + + jest-serializer@27.5.1: + dependencies: + '@types/node': 18.19.61 + graceful-fs: 4.2.11 + + jest-snapshot@27.5.1: + dependencies: + '@babel/core': 7.26.0 + '@babel/generator': 7.28.0 + '@babel/plugin-syntax-typescript': 7.25.9(@babel/core@7.26.0) + '@babel/traverse': 7.28.0 + '@babel/types': 7.28.1 + '@jest/transform': 27.5.1 + '@jest/types': 27.5.1 + '@types/babel__traverse': 7.20.6 + '@types/prettier': 2.7.3 + babel-preset-current-node-syntax: 1.1.0(@babel/core@7.26.0) + chalk: 4.1.2 + expect: 27.5.1 + graceful-fs: 4.2.11 + jest-diff: 27.5.1 + jest-get-type: 27.5.1 + jest-haste-map: 27.5.1 + jest-matcher-utils: 27.5.1 + jest-message-util: 27.5.1 + jest-util: 27.5.1 + natural-compare: 1.4.0 + pretty-format: 27.5.1 + semver: 7.7.2 + transitivePeerDependencies: + - supports-color + + jest-snapshot@28.1.3: + dependencies: + '@babel/core': 7.26.0 + '@babel/generator': 7.26.0 + '@babel/plugin-syntax-typescript': 7.25.9(@babel/core@7.26.0) + '@babel/traverse': 7.28.0 + '@babel/types': 7.26.0 + '@jest/expect-utils': 28.1.3 + '@jest/transform': 28.1.3 + '@jest/types': 28.1.3 + '@types/babel__traverse': 7.20.6 + '@types/prettier': 2.7.3 + babel-preset-current-node-syntax: 1.1.0(@babel/core@7.26.0) + chalk: 4.1.2 + expect: 28.1.3 + graceful-fs: 4.2.11 + jest-diff: 28.1.3 + jest-get-type: 28.0.2 + jest-haste-map: 28.1.3 + jest-matcher-utils: 28.1.3 + jest-message-util: 28.1.3 + jest-util: 28.1.3 + natural-compare: 1.4.0 + pretty-format: 28.1.3 + semver: 7.7.2 + transitivePeerDependencies: + - supports-color + + jest-snapshot@29.7.0: + dependencies: + '@babel/core': 7.26.0 + '@babel/generator': 7.26.0 + '@babel/plugin-syntax-jsx': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-syntax-typescript': 7.25.9(@babel/core@7.26.0) + '@babel/types': 7.26.0 + '@jest/expect-utils': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + babel-preset-current-node-syntax: 1.1.0(@babel/core@7.26.0) + chalk: 4.1.2 + expect: 29.7.0 + graceful-fs: 4.2.11 + jest-diff: 29.7.0 + jest-get-type: 29.6.3 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + natural-compare: 1.4.0 + pretty-format: 29.7.0 + semver: 7.7.2 + transitivePeerDependencies: + - supports-color - /micromark-util-sanitize-uri@1.1.0: - resolution: {integrity: sha512-RoxtuSCX6sUNtxhbmsEFQfWzs8VN7cTctmBPvYivo98xb/kDEoTCtJQX5wyzIYEmk/lvNFTat4hL8oW0KndFpg==} + jest-util@27.5.1: dependencies: - micromark-util-character: 1.1.0 - micromark-util-encode: 1.0.1 - micromark-util-symbol: 1.0.1 - dev: true + '@jest/types': 27.5.1 + '@types/node': 18.19.61 + chalk: 4.1.2 + ci-info: 3.9.0 + graceful-fs: 4.2.11 + picomatch: 2.3.1 - /micromark-util-subtokenize@1.0.2: - resolution: {integrity: sha512-d90uqCnXp/cy4G881Ub4psE57Sf8YD0pim9QdjCRNjfas2M1u6Lbt+XZK9gnHL2XFhnozZiEdCa9CNfXSfQ6xA==} + jest-util@28.1.3: dependencies: - micromark-util-chunked: 1.0.0 - micromark-util-symbol: 1.0.1 - micromark-util-types: 1.0.2 - uvu: 0.5.6 - dev: true - - /micromark-util-symbol@1.0.1: - resolution: {integrity: sha512-oKDEMK2u5qqAptasDAwWDXq0tG9AssVwAx3E9bBF3t/shRIGsWIRG+cGafs2p/SnDSOecnt6hZPCE2o6lHfFmQ==} - dev: true - - /micromark-util-types@1.0.2: - resolution: {integrity: sha512-DCfg/T8fcrhrRKTPjRrw/5LLvdGV7BHySf/1LOZx7TzWZdYRjogNtyNq885z3nNallwr3QUKARjqvHqX1/7t+w==} - dev: true + '@jest/types': 28.1.3 + '@types/node': 18.19.61 + chalk: 4.1.2 + ci-info: 3.9.0 + graceful-fs: 4.2.11 + picomatch: 2.3.1 - /micromark@3.1.0: - resolution: {integrity: sha512-6Mj0yHLdUZjHnOPgr5xfWIMqMWS12zDN6iws9SLuSz76W8jTtAv24MN4/CL7gJrl5vtxGInkkqDv/JIoRsQOvA==} - dependencies: - '@types/debug': 4.1.7 - debug: 4.3.4 - decode-named-character-reference: 1.0.2 - micromark-core-commonmark: 1.0.6 - micromark-factory-space: 1.0.0 - micromark-util-character: 1.1.0 - micromark-util-chunked: 1.0.0 - micromark-util-combine-extensions: 1.0.0 - micromark-util-decode-numeric-character-reference: 1.0.0 - micromark-util-encode: 1.0.1 - micromark-util-normalize-identifier: 1.0.0 - micromark-util-resolve-all: 1.0.0 - micromark-util-sanitize-uri: 1.1.0 - micromark-util-subtokenize: 1.0.2 - micromark-util-symbol: 1.0.1 - micromark-util-types: 1.0.2 - uvu: 0.5.6 - transitivePeerDependencies: - - supports-color - dev: true - - /micromatch@4.0.5: - resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} - engines: {node: '>=8.6'} + jest-util@29.7.0: dependencies: - braces: 3.0.2 + '@jest/types': 29.6.3 + '@types/node': 18.19.61 + chalk: 4.1.2 + ci-info: 3.9.0 + graceful-fs: 4.2.11 picomatch: 2.3.1 - /mime-db@1.52.0: - resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} - engines: {node: '>= 0.6'} - - /mime-types@2.1.35: - resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} - engines: {node: '>= 0.6'} + jest-validate@27.5.1: dependencies: - mime-db: 1.52.0 - - /mime@1.6.0: - resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} - engines: {node: '>=4'} - hasBin: true - - /mime@2.6.0: - resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==} - engines: {node: '>=4.0.0'} - hasBin: true - - /mime@3.0.0: - resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==} - engines: {node: '>=10.0.0'} - hasBin: true - dev: false - optional: true - - /mimic-fn@2.1.0: - resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} - engines: {node: '>=6'} - - /mimic-response@1.0.1: - resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==} - engines: {node: '>=4'} - dev: true - - /mimic-response@2.1.0: - resolution: {integrity: sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==} - engines: {node: '>=8'} - dev: false - - /mimic-response@3.1.0: - resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} - engines: {node: '>=10'} - dev: true - - /min-indent@1.0.1: - resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} - engines: {node: '>=4'} - dev: true + '@jest/types': 27.5.1 + camelcase: 6.3.0 + chalk: 4.1.2 + jest-get-type: 27.5.1 + leven: 3.1.0 + pretty-format: 27.5.1 - /mini-css-extract-plugin@2.7.5(webpack@5.78.0): - resolution: {integrity: sha512-9HaR++0mlgom81s95vvNjxkg52n2b5s//3ZTI1EtzFb98awsLSivs2LMsVqnQ3ay0PVhqWcGNyDaTE961FOcjQ==} - engines: {node: '>= 12.13.0'} - peerDependencies: - webpack: ^5.0.0 + jest-validate@28.1.3: dependencies: - schema-utils: 4.0.0 - webpack: 5.78.0(esbuild@0.16.3)(webpack-cli@5.0.1) - dev: false - - /minimalistic-assert@1.0.1: - resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} - dev: false + '@jest/types': 28.1.3 + camelcase: 6.3.0 + chalk: 4.1.2 + jest-get-type: 28.0.2 + leven: 3.1.0 + pretty-format: 28.1.3 - /minimatch@3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + jest-validate@29.7.0: dependencies: - brace-expansion: 1.1.11 + '@jest/types': 29.6.3 + camelcase: 6.3.0 + chalk: 4.1.2 + jest-get-type: 29.6.3 + leven: 3.1.0 + pretty-format: 29.7.0 - /minimatch@5.1.6: - resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} - engines: {node: '>=10'} + jest-watch-typeahead@1.1.0(jest@27.5.1(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3))): dependencies: - brace-expansion: 2.0.1 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + jest: 27.5.1(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)) + jest-regex-util: 28.0.2 + jest-watcher: 28.1.3 + slash: 4.0.0 + string-length: 5.0.1 + strip-ansi: 7.1.0 - /minimatch@7.4.6: - resolution: {integrity: sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==} - engines: {node: '>=10'} + jest-watcher@27.5.1: dependencies: - brace-expansion: 2.0.1 - - /minimist@1.2.8: - resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + '@jest/test-result': 27.5.1 + '@jest/types': 27.5.1 + '@types/node': 18.19.61 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + jest-util: 27.5.1 + string-length: 4.0.2 - /minipass-collect@1.0.2: - resolution: {integrity: sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==} - engines: {node: '>= 8'} + jest-watcher@28.1.3: dependencies: - minipass: 3.3.6 + '@jest/test-result': 28.1.3 + '@jest/types': 28.1.3 + '@types/node': 18.19.61 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + emittery: 0.10.2 + jest-util: 28.1.3 + string-length: 4.0.2 - /minipass-fetch@1.4.1: - resolution: {integrity: sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==} - engines: {node: '>=8'} + jest-watcher@29.7.0: dependencies: - minipass: 3.3.6 - minipass-sized: 1.0.3 - minizlib: 2.1.2 - optionalDependencies: - encoding: 0.1.13 + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 18.19.61 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + emittery: 0.13.1 + jest-util: 29.7.0 + string-length: 4.0.2 - /minipass-fetch@2.1.2: - resolution: {integrity: sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + jest-worker@26.6.2: dependencies: - minipass: 3.3.6 - minipass-sized: 1.0.3 - minizlib: 2.1.2 - optionalDependencies: - encoding: 0.1.13 + '@types/node': 18.19.61 + merge-stream: 2.0.0 + supports-color: 7.2.0 - /minipass-flush@1.0.5: - resolution: {integrity: sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==} - engines: {node: '>= 8'} + jest-worker@27.5.1: dependencies: - minipass: 3.3.6 + '@types/node': 18.19.61 + merge-stream: 2.0.0 + supports-color: 8.1.1 - /minipass-json-stream@1.0.1: - resolution: {integrity: sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg==} + jest-worker@28.1.3: dependencies: - jsonparse: 1.3.1 - minipass: 3.3.6 + '@types/node': 18.19.61 + merge-stream: 2.0.0 + supports-color: 8.1.1 - /minipass-pipeline@1.2.4: - resolution: {integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==} - engines: {node: '>=8'} + jest-worker@29.7.0: dependencies: - minipass: 3.3.6 + '@types/node': 18.19.61 + jest-util: 29.7.0 + merge-stream: 2.0.0 + supports-color: 8.1.1 - /minipass-sized@1.0.3: - resolution: {integrity: sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==} - engines: {node: '>=8'} + jest@27.5.1(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)): dependencies: - minipass: 3.3.6 + '@jest/core': 27.5.1(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)) + import-local: 3.2.0 + jest-cli: 27.5.1(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)) + transitivePeerDependencies: + - bufferutil + - canvas + - supports-color + - ts-node + - utf-8-validate - /minipass@3.3.6: - resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} - engines: {node: '>=8'} + jest@28.1.3(@types/node@18.19.61)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)): dependencies: - yallist: 4.0.0 - - /minipass@4.2.8: - resolution: {integrity: sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==} - engines: {node: '>=8'} + '@jest/core': 28.1.3(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)) + '@jest/types': 28.1.3 + import-local: 3.2.0 + jest-cli: 28.1.3(@types/node@18.19.61)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)) + transitivePeerDependencies: + - '@types/node' + - supports-color + - ts-node - /minizlib@2.1.2: - resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} - engines: {node: '>= 8'} + jest@29.7.0(@types/node@18.19.61)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)): dependencies: - minipass: 3.3.6 - yallist: 4.0.0 + '@jest/core': 29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)) + '@jest/types': 29.6.3 + import-local: 3.2.0 + jest-cli: 29.7.0(@types/node@18.19.61)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)) + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node - /mjml-accordion@4.14.1: - resolution: {integrity: sha512-dpNXyjnhYwhM75JSjD4wFUa9JgHm86M2pa0CoTzdv1zOQz67ilc4BoK5mc2S0gOjJpjBShM5eOJuCyVIuAPC6w==} + jest@29.7.0(@types/node@18.19.61)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)): dependencies: - '@babel/runtime': 7.21.0 - lodash: 4.17.21 - mjml-core: 4.14.1 + '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)) + '@jest/types': 29.6.3 + import-local: 3.2.0 + jest-cli: 29.7.0(@types/node@18.19.61)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)) transitivePeerDependencies: - - encoding - dev: false + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node - /mjml-body@4.14.1: - resolution: {integrity: sha512-YpXcK3o2o1U+fhI8f60xahrhXuHmav6BZez9vIN3ZEJOxPFSr+qgr1cT2iyFz50L5+ZsLIVj2ZY+ALQjdsg8ig==} + jest@29.7.0(@types/node@18.19.61)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@22.14.1)(typescript@5.6.3)): dependencies: - '@babel/runtime': 7.21.0 - lodash: 4.17.21 - mjml-core: 4.14.1 + '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@22.14.1)(typescript@5.6.3)) + '@jest/types': 29.6.3 + import-local: 3.2.0 + jest-cli: 29.7.0(@types/node@18.19.61)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@22.14.1)(typescript@5.6.3)) transitivePeerDependencies: - - encoding - dev: false + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node - /mjml-button@4.14.1: - resolution: {integrity: sha512-V1Tl1vQ3lXYvvqHJHvGcc8URr7V1l/ZOsv7iLV4QRrh7kjKBXaRS7uUJtz6/PzEbNsGQCiNtXrODqcijLWlgaw==} + jest@29.7.0(@types/node@22.14.1)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@22.14.1)(typescript@5.6.3)): dependencies: - '@babel/runtime': 7.21.0 - lodash: 4.17.21 - mjml-core: 4.14.1 + '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@22.14.1)(typescript@5.6.3)) + '@jest/types': 29.6.3 + import-local: 3.2.0 + jest-cli: 29.7.0(@types/node@22.14.1)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@22.14.1)(typescript@5.6.3)) transitivePeerDependencies: - - encoding - dev: false + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + + jiti@1.21.6: {} + + jiti@2.4.2: {} - /mjml-carousel@4.14.1: - resolution: {integrity: sha512-Ku3MUWPk/TwHxVgKEUtzspy/ePaWtN/3z6/qvNik0KIn0ZUIZ4zvR2JtaVL5nd30LHSmUaNj30XMPkCjYiKkFA==} + jose@4.15.9: {} + + js-beautify@1.15.1: dependencies: - '@babel/runtime': 7.21.0 - lodash: 4.17.21 - mjml-core: 4.14.1 - transitivePeerDependencies: - - encoding - dev: false + config-chain: 1.1.13 + editorconfig: 1.0.4 + glob: 10.4.5 + js-cookie: 3.0.5 + nopt: 7.2.1 - /mjml-cli@4.14.1: - resolution: {integrity: sha512-Gy6MnSygFXs0U1qOXTHqBg2vZX2VL/fAacgQzD4MHq4OuybWaTNSzXRwxBXYCxT3IJB874n2Q0Mxp+Xka+tnZg==} - hasBin: true + js-cookie@2.2.1: {} + + js-cookie@3.0.1: {} + + js-cookie@3.0.5: {} + + js-tokens@4.0.0: {} + + js-yaml@3.14.1: dependencies: - '@babel/runtime': 7.21.0 - chokidar: 3.5.3 - glob: 7.2.3 - html-minifier: 4.0.0 - js-beautify: 1.14.7 - lodash: 4.17.21 - mjml-core: 4.14.1 - mjml-migrate: 4.14.1 - mjml-parser-xml: 4.14.1 - mjml-validator: 4.13.0 - yargs: 16.2.0 - transitivePeerDependencies: - - encoding - dev: false + argparse: 1.0.10 + esprima: 4.0.1 - /mjml-column@4.14.1: - resolution: {integrity: sha512-iixVCIX1YJtpQuwG2WbDr7FqofQrlTtGQ4+YAZXGiLThs0En3xNIJFQX9xJ8sgLEGGltyooHiNICBRlzSp9fDg==} + js-yaml@4.1.0: dependencies: - '@babel/runtime': 7.21.0 - lodash: 4.17.21 - mjml-core: 4.14.1 - transitivePeerDependencies: - - encoding - dev: false + argparse: 2.0.1 + + jsbn@1.1.0: {} - /mjml-core@4.14.1: - resolution: {integrity: sha512-di88rSfX+8r4r+cEqlQCO7CRM4mYZrfe2wSCu2je38i+ujjkLpF72cgLnjBlSG5aOUCZgYvlsZ85stqIz9LQfA==} + jsdom@16.7.0: dependencies: - '@babel/runtime': 7.21.0 - cheerio: 1.0.0-rc.12 - detect-node: 2.1.0 - html-minifier: 4.0.0 - js-beautify: 1.14.7 - juice: 9.0.0 - lodash: 4.17.21 - mjml-migrate: 4.14.1 - mjml-parser-xml: 4.14.1 - mjml-validator: 4.13.0 + abab: 2.0.6 + acorn: 8.14.0 + acorn-globals: 6.0.0 + cssom: 0.4.4 + cssstyle: 2.3.0 + data-urls: 2.0.0 + decimal.js: 10.4.3 + domexception: 2.0.1 + escodegen: 2.1.0 + form-data: 4.0.4 + html-encoding-sniffer: 2.0.1 + http-proxy-agent: 4.0.1 + https-proxy-agent: 5.0.1 + is-potential-custom-element-name: 1.0.1 + nwsapi: 2.2.13 + parse5: 6.0.1 + saxes: 5.0.1 + symbol-tree: 3.2.4 + tough-cookie: 4.1.4 + w3c-hr-time: 1.0.2 + w3c-xmlserializer: 2.0.0 + webidl-conversions: 6.1.0 + whatwg-encoding: 1.0.5 + whatwg-mimetype: 2.3.0 + whatwg-url: 8.7.0 + ws: 7.5.10 + xml-name-validator: 3.0.0 transitivePeerDependencies: - - encoding - dev: false + - bufferutil + - supports-color + - utf-8-validate - /mjml-divider@4.14.1: - resolution: {integrity: sha512-agqWY0aW2xaMiUOhYKDvcAAfOLalpbbtjKZAl1vWmNkURaoK4L7MgDilKHSJDFUlHGm2ZOArTrq8i6K0iyThBQ==} + jsesc@3.0.2: {} + + json-bigint@1.0.0: dependencies: - '@babel/runtime': 7.21.0 - lodash: 4.17.21 - mjml-core: 4.14.1 - transitivePeerDependencies: - - encoding - dev: false + bignumber.js: 9.1.2 + + json-buffer@3.0.1: {} + + json-parse-even-better-errors@2.3.1: {} - /mjml-group@4.14.1: - resolution: {integrity: sha512-dJt5batgEJ7wxlxzqOfHOI94ABX+8DZBvAlHuddYO4CsLFHYv6XRIArLAMMnAKU76r6p3X8JxYeOjKZXdv49kg==} + json-parse-even-better-errors@3.0.2: {} + + json-schema-compare@0.2.2: dependencies: - '@babel/runtime': 7.21.0 lodash: 4.17.21 - mjml-core: 4.14.1 - transitivePeerDependencies: - - encoding - dev: false - /mjml-head-attributes@4.14.1: - resolution: {integrity: sha512-XdUNOp2csK28kBDSistInOyzWNwmu5HDNr4y1Z7vSQ1PfkmiuS6jWG7jHUjdoMhs27e6Leuyyc6a8gWSpqSWrg==} + json-schema-merge-allof@0.8.1: dependencies: - '@babel/runtime': 7.21.0 + compute-lcm: 1.1.2 + json-schema-compare: 0.2.2 lodash: 4.17.21 - mjml-core: 4.14.1 - transitivePeerDependencies: - - encoding - dev: false - /mjml-head-breakpoint@4.14.1: - resolution: {integrity: sha512-Qw9l/W/I5Z9p7I4ShgnEpAL9if4472ejcznbBnp+4Gq+sZoPa7iYoEPsa9UCGutlaCh3N3tIi2qKhl9qD8DFxA==} + json-schema-traverse@0.4.1: {} + + json-schema-traverse@1.0.0: {} + + json-schema@0.4.0: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json-stringify-nice@1.1.4: {} + + json2mq@0.2.0: dependencies: - '@babel/runtime': 7.21.0 - lodash: 4.17.21 - mjml-core: 4.14.1 - transitivePeerDependencies: - - encoding - dev: false + string-convert: 0.2.1 - /mjml-head-font@4.14.1: - resolution: {integrity: sha512-oBYm1gaOdEMjE5BoZouRRD4lCNZ1jcpz92NR/F7xDyMaKCGN6T/+r4S5dq1gOLm9zWqClRHaECdFJNEmrDpZqA==} + json5@1.0.2: dependencies: - '@babel/runtime': 7.21.0 - lodash: 4.17.21 - mjml-core: 4.14.1 - transitivePeerDependencies: - - encoding - dev: false + minimist: 1.2.8 - /mjml-head-html-attributes@4.14.1: - resolution: {integrity: sha512-vlJsJc1Sm4Ml2XvLmp01zsdmWmzm6+jNCO7X3eYi9ngEh8LjMCLIQOncnOgjqm9uGpQu2EgUhwvYFZP2luJOVg==} - dependencies: - '@babel/runtime': 7.21.0 - lodash: 4.17.21 - mjml-core: 4.14.1 - transitivePeerDependencies: - - encoding - dev: false + json5@2.2.3: {} - /mjml-head-preview@4.14.1: - resolution: {integrity: sha512-89gQtt3fhl2dkYpHLF5HDQXz/RLpzecU6wmAIT7Dz6etjLGE1dgq2Ay6Bu/OeHjDcT1gbM131zvBwuXw8OydNw==} + jsondiffpatch@0.5.0: dependencies: - '@babel/runtime': 7.21.0 - lodash: 4.17.21 - mjml-core: 4.14.1 - transitivePeerDependencies: - - encoding - dev: false + chalk: 3.0.0 + diff-match-patch: 1.0.5 - /mjml-head-style@4.14.1: - resolution: {integrity: sha512-XryOuf32EDuUCBT2k99C1+H87IOM919oY6IqxKFJCDkmsbywKIum7ibhweJdcxiYGONKTC6xjuibGD3fQTTYNQ==} + jsonfile@6.1.0: dependencies: - '@babel/runtime': 7.21.0 - lodash: 4.17.21 - mjml-core: 4.14.1 - transitivePeerDependencies: - - encoding - dev: false + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + + jsonparse@1.3.1: {} - /mjml-head-title@4.14.1: - resolution: {integrity: sha512-aIfpmlQdf1eJZSSrFodmlC4g5GudBti2eMyG42M7/3NeLM6anEWoe+UkF/6OG4Zy0tCQ40BDJ5iBZlMsjQICzw==} + jsonpath@1.1.1: dependencies: - '@babel/runtime': 7.21.0 - lodash: 4.17.21 - mjml-core: 4.14.1 - transitivePeerDependencies: - - encoding - dev: false + esprima: 1.2.2 + static-eval: 2.0.2 + underscore: 1.12.1 + + jsonpointer@5.0.1: {} - /mjml-head@4.14.1: - resolution: {integrity: sha512-KoCbtSeTAhx05Ugn9TB2UYt5sQinSCb7RGRer5iPQ3CrXj8hT5B5Svn6qvf/GACPkWl4auExHQh+XgLB+r3OEA==} + jsonwebtoken@9.0.2: dependencies: - '@babel/runtime': 7.21.0 - lodash: 4.17.21 - mjml-core: 4.14.1 - transitivePeerDependencies: - - encoding - dev: false + jws: 3.2.2 + lodash.includes: 4.3.0 + lodash.isboolean: 3.0.3 + lodash.isinteger: 4.0.4 + lodash.isnumber: 3.0.3 + lodash.isplainobject: 4.0.6 + lodash.isstring: 4.0.1 + lodash.once: 4.1.1 + ms: 2.1.3 + semver: 7.6.3 - /mjml-hero@4.14.1: - resolution: {integrity: sha512-TQJ3yfjrKYGkdEWjHLHhL99u/meKFYgnfJvlo9xeBvRjSM696jIjdqaPHaunfw4CP6d2OpCIMuacgOsvqQMWOA==} + jsprim@2.0.2: dependencies: - '@babel/runtime': 7.21.0 - lodash: 4.17.21 - mjml-core: 4.14.1 - transitivePeerDependencies: - - encoding - dev: false + assert-plus: 1.0.0 + extsprintf: 1.3.0 + json-schema: 0.4.0 + verror: 1.10.0 - /mjml-image@4.14.1: - resolution: {integrity: sha512-jfKLPHXuFq83okwlNM1Um/AEWeVDgs2JXIOsWp2TtvXosnRvGGMzA5stKLYdy1x6UfKF4c1ovpMS162aYGp+xQ==} + jsx-ast-utils@3.3.5: dependencies: - '@babel/runtime': 7.21.0 - lodash: 4.17.21 - mjml-core: 4.14.1 - transitivePeerDependencies: - - encoding - dev: false + array-includes: 3.1.8 + array.prototype.flat: 1.3.2 + object.assign: 4.1.5 + object.values: 1.2.0 - /mjml-migrate@4.14.1: - resolution: {integrity: sha512-d+9HKQOhZi3ZFAaFSDdjzJX9eDQGjMf3BArLWNm2okC4ZgfJSpOc77kgCyFV8ugvwc8fFegPnSV60Jl4xtvK2A==} - hasBin: true + juice@11.0.0: dependencies: - '@babel/runtime': 7.21.0 - js-beautify: 1.14.7 - lodash: 4.17.21 - mjml-core: 4.14.1 - mjml-parser-xml: 4.14.1 - yargs: 16.2.0 - transitivePeerDependencies: - - encoding - dev: false + cheerio: 1.0.0 + commander: 12.1.0 + mensch: 0.3.4 + slick: 1.12.2 + web-resource-inliner: 7.0.0 - /mjml-navbar@4.14.1: - resolution: {integrity: sha512-rNy1Kw8CR3WQ+M55PFBAUDz2VEOjz+sk06OFnsnmNjoMVCjo1EV7OFLDAkmxAwqkC8h4zQWEOFY0MBqqoAg7+A==} + just-diff-apply@5.5.0: {} + + just-diff@5.2.0: {} + + jwa@1.4.1: dependencies: - '@babel/runtime': 7.21.0 - lodash: 4.17.21 - mjml-core: 4.14.1 - transitivePeerDependencies: - - encoding - dev: false + buffer-equal-constant-time: 1.0.1 + ecdsa-sig-formatter: 1.0.11 + safe-buffer: 5.2.1 - /mjml-parser-xml@4.14.1: - resolution: {integrity: sha512-9WQVeukbXfq9DUcZ8wOsHC6BTdhaVwTAJDYMIQglXLwKwN7I4pTCguDDHy5d0kbbzK5OCVxCdZe+bfVI6XANOQ==} + jwa@2.0.0: dependencies: - '@babel/runtime': 7.21.0 - detect-node: 2.0.4 - htmlparser2: 8.0.2 - lodash: 4.17.21 - dev: false - - /mjml-preset-core@4.14.1: - resolution: {integrity: sha512-uUCqK9Z9d39rwB/+JDV2KWSZGB46W7rPQpc9Xnw1DRP7wD7qAfJwK6AZFCwfTgWdSxw0PwquVNcrUS9yBa9uhw==} - dependencies: - '@babel/runtime': 7.21.0 - mjml-accordion: 4.14.1 - mjml-body: 4.14.1 - mjml-button: 4.14.1 - mjml-carousel: 4.14.1 - mjml-column: 4.14.1 - mjml-divider: 4.14.1 - mjml-group: 4.14.1 - mjml-head: 4.14.1 - mjml-head-attributes: 4.14.1 - mjml-head-breakpoint: 4.14.1 - mjml-head-font: 4.14.1 - mjml-head-html-attributes: 4.14.1 - mjml-head-preview: 4.14.1 - mjml-head-style: 4.14.1 - mjml-head-title: 4.14.1 - mjml-hero: 4.14.1 - mjml-image: 4.14.1 - mjml-navbar: 4.14.1 - mjml-raw: 4.14.1 - mjml-section: 4.14.1 - mjml-social: 4.14.1 - mjml-spacer: 4.14.1 - mjml-table: 4.14.1 - mjml-text: 4.14.1 - mjml-wrapper: 4.14.1 - transitivePeerDependencies: - - encoding - dev: false + buffer-equal-constant-time: 1.0.1 + ecdsa-sig-formatter: 1.0.11 + safe-buffer: 5.2.1 - /mjml-raw@4.14.1: - resolution: {integrity: sha512-9+4wzoXnCtfV6QPmjfJkZ50hxFB4Z8QZnl2Ac0D1Cn3dUF46UkmO5NLMu7UDIlm5DdFyycZrMOwvZS4wv9ksPw==} + jwks-rsa@3.2.0: dependencies: - '@babel/runtime': 7.21.0 - lodash: 4.17.21 - mjml-core: 4.14.1 + '@types/express': 4.17.21 + '@types/jsonwebtoken': 9.0.7 + debug: 4.3.7(supports-color@5.5.0) + jose: 4.15.9 + limiter: 1.1.5 + lru-memoizer: 2.3.0 transitivePeerDependencies: - - encoding - dev: false + - supports-color - /mjml-react@2.0.8(mjml@4.14.1)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-3wtHZZs1Y7e7Tl+ImojD/+aPE8Z0xshMww7MKSQlD9A1E/92amWQilGZN3T+WjWWaDueKcH2Gc1RJ72PLGSRGA==} - peerDependencies: - mjml: ^4.7.0 - react: ^16.0.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 + jws@3.2.2: dependencies: - '@babel/runtime': 7.21.0 - babel-runtime: 6.25.0 - color: 3.2.1 - mjml: 4.14.1 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - react-reconciler: 0.26.2(react@18.2.0) - dev: false + jwa: 1.4.1 + safe-buffer: 5.2.1 - /mjml-section@4.14.1: - resolution: {integrity: sha512-Ik5pTUhpT3DOfB3hEmAWp8rZ0ilWtIivnL8XdUJRfgYE9D+MCRn+reIO+DAoJHxiQoI6gyeKkIP4B9OrQ7cHQw==} + jws@4.0.0: dependencies: - '@babel/runtime': 7.21.0 - lodash: 4.17.21 - mjml-core: 4.14.1 - transitivePeerDependencies: - - encoding - dev: false + jwa: 2.0.0 + safe-buffer: 5.2.1 - /mjml-social@4.14.1: - resolution: {integrity: sha512-G44aOZXgZHukirjkeQWTTV36UywtE2YvSwWGNfo/8d+k5JdJJhCIrlwaahyKEAyH63G1B0Zt8b2lEWx0jigYUw==} + keyv@4.5.4: dependencies: - '@babel/runtime': 7.21.0 - lodash: 4.17.21 - mjml-core: 4.14.1 - transitivePeerDependencies: - - encoding - dev: false + json-buffer: 3.0.1 - /mjml-spacer@4.14.1: - resolution: {integrity: sha512-5SfQCXTd3JBgRH1pUy6NVZ0lXBiRqFJPVHBdtC3OFvUS3q1w16eaAXlIUWMKTfy8CKhQrCiE6m65kc662ZpYxA==} - dependencies: - '@babel/runtime': 7.21.0 - lodash: 4.17.21 - mjml-core: 4.14.1 - transitivePeerDependencies: - - encoding - dev: false + kind-of@6.0.3: {} + + kleur@3.0.3: {} + + kleur@4.1.5: {} + + klona@2.0.6: {} - /mjml-table@4.14.1: - resolution: {integrity: sha512-aVBdX3WpyKVGh/PZNn2KgRem+PQhWlvnD00DKxDejRBsBSKYSwZ0t3EfFvZOoJ9DzfHsN0dHuwd6Z18Ps44NFQ==} + language-subtag-registry@0.3.23: {} + + language-tags@1.0.9: dependencies: - '@babel/runtime': 7.21.0 - lodash: 4.17.21 - mjml-core: 4.14.1 - transitivePeerDependencies: - - encoding - dev: false + language-subtag-registry: 0.3.23 - /mjml-text@4.14.1: - resolution: {integrity: sha512-yZuvf5z6qUxEo5CqOhCUltJlR6oySKVcQNHwoV5sneMaKdmBiaU4VDnlYFera9gMD9o3KBHIX6kUg7EHnCwBRQ==} + launch-editor@2.9.1: dependencies: - '@babel/runtime': 7.21.0 - lodash: 4.17.21 - mjml-core: 4.14.1 - transitivePeerDependencies: - - encoding - dev: false + picocolors: 1.1.1 + shell-quote: 1.8.1 - /mjml-validator@4.13.0: - resolution: {integrity: sha512-uURYfyQYtHJ6Qz/1A7/+E9ezfcoISoLZhYK3olsxKRViwaA2Mm8gy/J3yggZXnsUXWUns7Qymycm5LglLEIiQg==} + lazystream@1.0.1: dependencies: - '@babel/runtime': 7.21.0 - dev: false + readable-stream: 2.3.8 + + leac@0.6.0: {} - /mjml-wrapper@4.14.1: - resolution: {integrity: sha512-aA5Xlq6d0hZ5LY+RvSaBqmVcLkvPvdhyAv3vQf3G41Gfhel4oIPmkLnVpHselWhV14A0KwIOIAKVxHtSAxyOTQ==} + less-loader@10.2.0(less@4.2.0)(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(esbuild@0.23.1)): dependencies: - '@babel/runtime': 7.21.0 - lodash: 4.17.21 - mjml-core: 4.14.1 - mjml-section: 4.14.1 - transitivePeerDependencies: - - encoding - dev: false + klona: 2.0.6 + less: 4.2.0 + webpack: 5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(esbuild@0.23.1) - /mjml@4.14.1: - resolution: {integrity: sha512-f/wnWWIVbeb/ge3ff7c/KYYizI13QbGIp03odwwkCThsJsacw4gpZZAU7V4gXY3HxSXP2/q3jxOfaHVbkfNpOQ==} - hasBin: true + less@4.2.0: dependencies: - '@babel/runtime': 7.21.0 - mjml-cli: 4.14.1 - mjml-core: 4.14.1 - mjml-migrate: 4.14.1 - mjml-preset-core: 4.14.1 - mjml-validator: 4.13.0 - transitivePeerDependencies: - - encoding - dev: false + copy-anything: 2.0.6 + parse-node-version: 1.0.1 + tslib: 2.8.0 + optionalDependencies: + errno: 0.1.8 + graceful-fs: 4.2.11 + image-size: 0.5.5 + make-dir: 2.1.0 + mime: 1.6.0 + needle: 3.3.1 + source-map: 0.6.1 - /mkdirp-classic@0.5.3: - resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} + leven@3.1.0: {} - /mkdirp-infer-owner@2.0.0: - resolution: {integrity: sha512-sdqtiFt3lkOaYvTXSRIUjkIdPTcxgv5+fgqYE/5qgwdw12cOrAuzzgzvVExIkH/ul1oeHN3bCLOWSG3XOqbKKw==} - engines: {node: '>=10'} + levn@0.3.0: dependencies: - chownr: 2.0.0 - infer-owner: 1.0.4 - mkdirp: 1.0.4 + prelude-ls: 1.1.2 + type-check: 0.3.2 - /mkdirp@0.5.6: - resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} - hasBin: true + levn@0.4.1: dependencies: - minimist: 1.2.8 + prelude-ls: 1.2.1 + type-check: 0.4.0 - /mkdirp@1.0.4: - resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} - engines: {node: '>=10'} - hasBin: true + lilconfig@2.1.0: {} + + lilconfig@3.1.2: {} + + limiter@1.1.5: {} - /mlly@1.2.0: - resolution: {integrity: sha512-+c7A3CV0KGdKcylsI6khWyts/CYrGTrRVo4R/I7u/cUsy0Conxa6LUhiEzVKIw14lc2L5aiO4+SeVe4TeGRKww==} + lines-and-columns@1.2.4: {} + + load-yaml-file@0.2.0: dependencies: - acorn: 8.8.2 - pathe: 1.1.0 - pkg-types: 1.0.2 - ufo: 1.1.1 - dev: true + graceful-fs: 4.2.11 + js-yaml: 3.14.1 + pify: 4.0.1 + strip-bom: 3.0.0 - /module-details-from-path@1.0.3: - resolution: {integrity: sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==} - dev: true + loader-runner@4.3.0: {} - /moment@2.29.4: - resolution: {integrity: sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==} - dev: false + loader-utils@2.0.4: + dependencies: + big.js: 5.2.2 + emojis-list: 3.0.0 + json5: 2.2.3 - /monaco-editor@0.37.1: - resolution: {integrity: sha512-jLXEEYSbqMkT/FuJLBZAVWGuhIb4JNwHE9kPTorAVmsdZ4UzHAfgWxLsVtD7pLRFaOwYPhNG9nUCpmFL1t/dIg==} - dev: false + loader-utils@3.3.1: {} - /mongodb-connection-string-url@2.6.0: - resolution: {integrity: sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==} + locate-path@3.0.0: dependencies: - '@types/whatwg-url': 8.2.2 - whatwg-url: 11.0.0 - dev: false + p-locate: 3.0.0 + path-exists: 3.0.0 - /mongodb@5.2.0: - resolution: {integrity: sha512-nLgo95eP1acvjBcOdrUV3aqpWwHZCZwhYA2opB8StybbtQL/WoE5pk92qUUfjbKOWcGLYJczTqQbfOQhYtrkKg==} - engines: {node: '>=14.20.1'} - peerDependencies: - '@aws-sdk/credential-providers': ^3.201.0 - mongodb-client-encryption: ^2.3.0 - snappy: ^7.2.2 - peerDependenciesMeta: - '@aws-sdk/credential-providers': - optional: true - mongodb-client-encryption: - optional: true - snappy: - optional: true + locate-path@5.0.0: dependencies: - bson: 5.2.0 - mongodb-connection-string-url: 2.6.0 - socks: 2.7.1 - optionalDependencies: - saslprep: 1.0.3 - dev: false + p-locate: 4.1.0 - /monorel@0.4.2: - resolution: {integrity: sha512-CoMOQatkftXPegXzAh8Qnjc/+VQuiujhNjU6iWY5YZi0o2N0FKxPckja8LhKYoHBZlMt0jyKA+99EfnxjffjBQ==} - hasBin: true + locate-path@6.0.0: dependencies: - minimist: 1.2.8 - tslib: 2.5.0 - dev: true + p-locate: 5.0.0 - /mri@1.2.0: - resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} - engines: {node: '>=4'} - dev: true + locate-path@7.2.0: + dependencies: + p-locate: 6.0.0 - /ms@2.0.0: - resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + lodash-es@4.17.21: {} - /ms@2.1.2: - resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + lodash.camelcase@4.3.0: {} - /ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + lodash.castarray@4.4.0: {} - /multicast-dns@7.2.5: - resolution: {integrity: sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==} - hasBin: true - dependencies: - dns-packet: 5.5.0 - thunky: 1.1.0 - dev: false + lodash.clonedeep@4.5.0: {} - /multimatch@5.0.0: - resolution: {integrity: sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA==} - engines: {node: '>=10'} - dependencies: - '@types/minimatch': 3.0.5 - array-differ: 3.0.0 - array-union: 2.1.0 - arrify: 2.0.1 - minimatch: 3.1.2 + lodash.curry@4.1.1: {} - /mute-stream@0.0.8: - resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} + lodash.debounce@4.0.8: {} - /mz@2.7.0: - resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} - dependencies: - any-promise: 1.3.0 - object-assign: 4.1.1 - thenify-all: 1.6.0 + lodash.defaults@4.2.0: {} - /nan@2.17.0: - resolution: {integrity: sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==} - dev: true - optional: true + lodash.difference@4.5.0: {} - /nano-css@5.3.5(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-vSB9X12bbNu4ALBu7nigJgRViZ6ja3OU7CeuiV1zMIbXOdmkLahgtPmh3GBOlDxbKY0CitqlPdOReGlBLSp+yg==} - peerDependencies: - react: '*' - react-dom: '*' - dependencies: - css-tree: 1.1.3 - csstype: 3.1.2 - fastest-stable-stringify: 2.0.2 - inline-style-prefixer: 6.0.4 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - rtl-css-js: 1.16.1 - sourcemap-codec: 1.4.8 - stacktrace-js: 2.0.2 - stylis: 4.1.3 - dev: false + lodash.flatten@4.4.0: {} - /nanoid@3.3.6: - resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true + lodash.flow@3.5.0: {} - /napi-build-utils@1.0.2: - resolution: {integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==} - dev: false + lodash.get@4.4.2: {} - /natural-compare-lite@1.4.0: - resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} - dev: false + lodash.includes@4.3.0: {} - /natural-compare@1.4.0: - resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + lodash.isarguments@3.1.0: {} - /needle@3.2.0: - resolution: {integrity: sha512-oUvzXnyLiVyVGoianLijF9O/RecZUf7TkBfimjGrLM4eQhXyeJwM6GeAWccwfQ9aa4gMCZKqhAOuLaMIcQxajQ==} - engines: {node: '>= 4.4.x'} - hasBin: true - requiresBuild: true - dependencies: - debug: 3.2.7(supports-color@5.5.0) - iconv-lite: 0.6.3 - sax: 1.2.4 - transitivePeerDependencies: - - supports-color - dev: true - optional: true + lodash.isboolean@3.0.3: {} - /negotiator@0.6.3: - resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} - engines: {node: '>= 0.6'} + lodash.isinteger@4.0.4: {} - /neo-async@2.6.2: - resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + lodash.isnumber@3.0.3: {} - /netmask@2.0.2: - resolution: {integrity: sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==} - engines: {node: '>= 0.4.0'} - dev: true + lodash.isplainobject@4.0.6: {} - /new-date@1.0.3: - resolution: {integrity: sha512-0fsVvQPbo2I18DT2zVHpezmeeNYV2JaJSrseiHLc17GNOxJzUdx5mvSigPu8LtIfZSij5i1wXnXFspEs2CD6hA==} - dependencies: - '@segment/isodate': 1.0.3 - dev: true + lodash.isstring@4.0.1: {} - /next-auth@4.22.0(next@13.3.0)(nodemailer@6.9.1)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-08+kjnDoE7aQ52O996x6cwA3ffc2CbHIkrCgLYhbE+aDIJBKI0oA9UbIEIe19/+ODYJgpAHHOtJx4izmsgaVag==} - peerDependencies: - next: ^12.2.5 || ^13 - nodemailer: ^6.6.5 - react: ^17.0.2 || ^18 - react-dom: ^17.0.2 || ^18 - peerDependenciesMeta: - nodemailer: - optional: true - dependencies: - '@babel/runtime': 7.21.0 - '@panva/hkdf': 1.0.4 - cookie: 0.5.0 - jose: 4.13.1 - next: 13.3.0(@babel/core@7.21.4)(react-dom@18.2.0)(react@18.2.0) - nodemailer: 6.9.1 - oauth: 0.9.15 - openid-client: 5.4.0 - preact: 10.13.2 - preact-render-to-string: 5.2.6(preact@10.13.2) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - uuid: 8.3.2 - dev: false + lodash.memoize@4.1.2: {} - /next-transpile-modules@10.0.0: - resolution: {integrity: sha512-FyeJ++Lm2Fq31gbThiRCrJlYpIY9QaI7A3TjuhQLzOix8ChQrvn5ny4MhfIthS5cy6+uK1AhDRvxVdW17y3Xdw==} - deprecated: All features of next-transpile-modules are now natively built-in Next.js 13.1. Please use Next's transpilePackages option :) - dependencies: - enhanced-resolve: 5.12.0 - dev: true + lodash.merge@4.6.2: {} - /next@13.0.0(@babel/core@7.21.4)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-puH1WGM6rGeFOoFdXXYfUxN9Sgi4LMytCV5HkQJvVUOhHfC1DoVqOfvzaEteyp6P04IW+gbtK2Q9pInVSrltPA==} - engines: {node: '>=14.6.0'} - hasBin: true - peerDependencies: - fibers: '>= 3.1.0' - node-sass: ^6.0.0 || ^7.0.0 - react: ^18.0.0-0 - react-dom: ^18.0.0-0 - sass: ^1.3.0 - peerDependenciesMeta: - fibers: - optional: true - node-sass: - optional: true - sass: - optional: true - dependencies: - '@next/env': 13.0.0 - '@swc/helpers': 0.4.11 - caniuse-lite: 1.0.30001478 - postcss: 8.4.14 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - styled-jsx: 5.1.0(@babel/core@7.21.4)(react@18.2.0) - use-sync-external-store: 1.2.0(react@18.2.0) - optionalDependencies: - '@next/swc-android-arm-eabi': 13.0.0 - '@next/swc-android-arm64': 13.0.0 - '@next/swc-darwin-arm64': 13.0.0 - '@next/swc-darwin-x64': 13.0.0 - '@next/swc-freebsd-x64': 13.0.0 - '@next/swc-linux-arm-gnueabihf': 13.0.0 - '@next/swc-linux-arm64-gnu': 13.0.0 - '@next/swc-linux-arm64-musl': 13.0.0 - '@next/swc-linux-x64-gnu': 13.0.0 - '@next/swc-linux-x64-musl': 13.0.0 - '@next/swc-win32-arm64-msvc': 13.0.0 - '@next/swc-win32-ia32-msvc': 13.0.0 - '@next/swc-win32-x64-msvc': 13.0.0 - transitivePeerDependencies: - - '@babel/core' - - babel-plugin-macros - dev: false + lodash.once@4.1.1: {} - /next@13.3.0(@babel/core@7.21.4)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-OVTw8MpIPa12+DCUkPqRGPS3thlJPcwae2ZL4xti3iBff27goH024xy4q2lhlsdoYiKOi8Kz6uJoLW/GXwgfOA==} - engines: {node: '>=14.6.0'} - hasBin: true - peerDependencies: - '@opentelemetry/api': ^1.1.0 - fibers: '>= 3.1.0' - node-sass: ^6.0.0 || ^7.0.0 - react: ^18.2.0 - react-dom: ^18.2.0 - sass: ^1.3.0 - peerDependenciesMeta: - '@opentelemetry/api': - optional: true - fibers: - optional: true - node-sass: - optional: true - sass: - optional: true + lodash.sortby@4.7.0: {} + + lodash.union@4.6.0: {} + + lodash.uniq@4.5.0: {} + + lodash@4.17.21: {} + + log-symbols@4.1.0: dependencies: - '@next/env': 13.3.0 - '@swc/helpers': 0.4.14 - busboy: 1.6.0 - caniuse-lite: 1.0.30001478 - postcss: 8.4.14 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - styled-jsx: 5.1.1(@babel/core@7.21.4)(react@18.2.0) - optionalDependencies: - '@next/swc-darwin-arm64': 13.3.0 - '@next/swc-darwin-x64': 13.3.0 - '@next/swc-linux-arm64-gnu': 13.3.0 - '@next/swc-linux-arm64-musl': 13.3.0 - '@next/swc-linux-x64-gnu': 13.3.0 - '@next/swc-linux-x64-musl': 13.3.0 - '@next/swc-win32-arm64-msvc': 13.3.0 - '@next/swc-win32-ia32-msvc': 13.3.0 - '@next/swc-win32-x64-msvc': 13.3.0 - transitivePeerDependencies: - - '@babel/core' - - babel-plugin-macros - dev: false + chalk: 4.1.2 + is-unicode-supported: 0.1.0 - /no-case@2.3.2: - resolution: {integrity: sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==} + log-symbols@6.0.0: dependencies: - lower-case: 1.1.4 - dev: false + chalk: 5.3.0 + is-unicode-supported: 1.3.0 - /no-case@3.0.4: - resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} + log-symbols@7.0.1: dependencies: - lower-case: 2.0.2 - tslib: 2.5.0 - dev: false + is-unicode-supported: 2.1.0 + yoctocolors: 2.1.1 + + long@4.0.0: {} - /node-abi@2.30.1: - resolution: {integrity: sha512-/2D0wOQPgaUWzVSVgRMx+trKJRC2UG4SUc4oCJoXx9Uxjtp0Vy3/kt7zcbxHF8+Z/pK3UloLWzBISg72brfy1w==} + long@5.2.3: {} + + loose-envify@1.4.0: dependencies: - semver: 5.7.1 - dev: false + js-tokens: 4.0.0 - /node-abort-controller@3.1.1: - resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==} - dev: false + lower-case@2.0.2: + dependencies: + tslib: 2.8.0 - /node-addon-api@1.7.2: - resolution: {integrity: sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==} - dev: true - optional: true + lru-cache@10.4.3: {} - /node-addon-api@2.0.2: - resolution: {integrity: sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==} - dev: false + lru-cache@11.1.0: {} - /node-cache@5.1.2: - resolution: {integrity: sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg==} - engines: {node: '>= 8.0.0'} + lru-cache@5.1.1: dependencies: - clone: 2.1.2 - dev: false + yallist: 3.1.1 - /node-domexception@1.0.0: - resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} - engines: {node: '>=10.5.0'} + lru-cache@6.0.0: + dependencies: + yallist: 4.0.0 - /node-fetch-commonjs@3.2.4: - resolution: {integrity: sha512-bZW7+ldcuuMPLTJk8DufhT6qHDRdljYD0jqBjmrYfcInaYcReX5kK42SQsu/jvtit/tER28yYjnk63PEEmNPtg==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + lru-cache@7.18.3: {} + + lru-memoizer@2.3.0: dependencies: - formdata-polyfill: 4.0.10 - web-streams-polyfill: 3.2.1 + lodash.clonedeep: 4.5.0 + lru-cache: 6.0.0 - /node-fetch@2.6.7: - resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==} - engines: {node: 4.x || >=6.0.0} - peerDependencies: - encoding: ^0.1.0 - peerDependenciesMeta: - encoding: - optional: true + lucide-react@0.447.0(react@18.3.1): dependencies: - whatwg-url: 5.0.0 + react: 18.3.1 - /node-fetch@2.6.9: - resolution: {integrity: sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==} - engines: {node: 4.x || >=6.0.0} - peerDependencies: - encoding: ^0.1.0 - peerDependenciesMeta: - encoding: - optional: true + lz-string@1.5.0: {} + + magic-string@0.25.9: dependencies: - whatwg-url: 5.0.0 + sourcemap-codec: 1.4.8 - /node-forge@1.3.1: - resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==} - engines: {node: '>= 6.13.0'} + magic-string@0.30.12: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 - /node-gyp-build@4.6.0: - resolution: {integrity: sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==} - hasBin: true - dev: true + magic-string@0.30.17: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 - /node-gyp@8.4.1: - resolution: {integrity: sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==} - engines: {node: '>= 10.12.0'} - hasBin: true + make-dir@2.1.0: dependencies: - env-paths: 2.2.1 - glob: 7.2.3 - graceful-fs: 4.2.11 - make-fetch-happen: 9.1.0 - nopt: 5.0.0 - npmlog: 6.0.2 - rimraf: 3.0.2 - semver: 7.5.0 - tar: 6.1.13 - which: 2.0.2 + pify: 4.0.1 + semver: 5.7.2 + optional: true + + make-dir@3.1.0: + dependencies: + semver: 6.3.1 + + make-dir@4.0.0: + dependencies: + semver: 7.7.2 + + make-error@1.3.6: {} + + make-fetch-happen@10.2.1: + dependencies: + agentkeepalive: 4.3.0 + cacache: 16.1.3 + http-cache-semantics: 4.1.1 + http-proxy-agent: 5.0.0 + https-proxy-agent: 5.0.1 + is-lambda: 1.0.1 + lru-cache: 7.18.3 + minipass: 3.3.6 + minipass-collect: 1.0.2 + minipass-fetch: 2.1.2 + minipass-flush: 1.0.5 + minipass-pipeline: 1.2.4 + negotiator: 0.6.4 + promise-retry: 2.0.1 + socks-proxy-agent: 7.0.0 + ssri: 9.0.1 transitivePeerDependencies: - bluebird - supports-color - /node-int64@0.4.0: - resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} + make-fetch-happen@11.1.1: + dependencies: + agentkeepalive: 4.3.0 + cacache: 17.1.4 + http-cache-semantics: 4.1.1 + http-proxy-agent: 5.0.0 + https-proxy-agent: 5.0.1 + is-lambda: 1.0.1 + lru-cache: 7.18.3 + minipass: 5.0.0 + minipass-fetch: 3.0.5 + minipass-flush: 1.0.5 + minipass-pipeline: 1.2.4 + negotiator: 0.6.4 + promise-retry: 2.0.1 + socks-proxy-agent: 7.0.0 + ssri: 10.0.6 + transitivePeerDependencies: + - supports-color - /node-loader@2.0.0(webpack@5.78.0): - resolution: {integrity: sha512-I5VN34NO4/5UYJaUBtkrODPWxbobrE4hgDqPrjB25yPkonFhCmZ146vTH+Zg417E9Iwoh1l/MbRs1apc5J295Q==} - engines: {node: '>= 10.13.0'} - peerDependencies: - webpack: ^5.0.0 + make-fetch-happen@9.1.0: dependencies: - loader-utils: 2.0.4 - webpack: 5.78.0(esbuild@0.16.3)(webpack-cli@5.0.1) - dev: true + agentkeepalive: 4.3.0 + cacache: 15.3.0 + http-cache-semantics: 4.1.1 + http-proxy-agent: 4.0.1 + https-proxy-agent: 5.0.1 + is-lambda: 1.0.1 + lru-cache: 6.0.0 + minipass: 3.3.6 + minipass-collect: 1.0.2 + minipass-fetch: 1.4.1 + minipass-flush: 1.0.5 + minipass-pipeline: 1.2.4 + negotiator: 0.6.4 + promise-retry: 2.0.1 + socks-proxy-agent: 6.2.1 + ssri: 8.0.1 + transitivePeerDependencies: + - bluebird + - supports-color - /node-postgres@0.6.2: - resolution: {integrity: sha512-wjaW+KutPOFemtBDuogyy6OMMOs2vpSOwuqbUhFUbrDEOTk66K2lmRyvOdO4t26qGtnPyZXQ4bAuF17cBhi5aQ==} - dev: true + makeerror@1.0.12: + dependencies: + tmpl: 1.0.5 - /node-releases@2.0.10: - resolution: {integrity: sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==} + map-obj@4.3.0: {} - /node-sql-parser@4.6.6: - resolution: {integrity: sha512-zpash5xnRY6+0C9HFru32iRJV1LTkwtrVpO90i385tYVF6efyXK/B3Nsq/15Fuv2utxrqHNjKtL55OHb8sl+eQ==} - engines: {node: '>=8'} + markdown-to-jsx@7.5.0(react@18.3.1): dependencies: - big-integer: 1.6.51 - dev: false + react: 18.3.1 - /nodemailer@6.9.1: - resolution: {integrity: sha512-qHw7dOiU5UKNnQpXktdgQ1d3OFgRAekuvbJLcdG5dnEo/GtcTHRYM7+UfJARdOFU9WUQO8OiIamgWPmiSFHYAA==} - engines: {node: '>=6.0.0'} - dev: false + marked@7.0.4: {} - /nodemon@2.0.22: - resolution: {integrity: sha512-B8YqaKMmyuCO7BowF1Z1/mkPqLk6cs/l63Ojtd6otKjMx47Dq1utxfRxcavH1I7VSaL8n5BUaoutadnsX3AAVQ==} - engines: {node: '>=8.10.0'} - hasBin: true + matched@5.0.1: dependencies: - chokidar: 3.5.3 - debug: 3.2.7(supports-color@5.5.0) - ignore-by-default: 1.0.1 - minimatch: 3.1.2 - pstree.remy: 1.1.8 - semver: 5.7.1 - simple-update-notifier: 1.1.0 - supports-color: 5.5.0 - touch: 3.1.0 - undefsafe: 2.0.5 - dev: true + glob: 7.2.3 + picomatch: 2.3.1 - /noop-logger@0.1.1: - resolution: {integrity: sha512-6kM8CLXvuW5crTxsAtva2YLrRrDaiTIkIePWs9moLHqbFWT94WpNFjwS/5dfLfECg5i/lkmw3aoqVidxt23TEQ==} - dev: false + math-intrinsics@1.1.0: {} - /nopt@1.0.10: - resolution: {integrity: sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==} - hasBin: true + maxmin@2.1.0: dependencies: - abbrev: 1.1.1 - dev: true + chalk: 1.1.3 + figures: 1.7.0 + gzip-size: 3.0.0 + pretty-bytes: 3.0.1 - /nopt@5.0.0: - resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==} - engines: {node: '>=6'} - hasBin: true + maxmind@4.3.22: dependencies: - abbrev: 1.1.1 + mmdb-lib: 2.1.1 + tiny-lru: 11.2.11 - /nopt@6.0.0: - resolution: {integrity: sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - hasBin: true + md-to-react-email@5.0.5(react@18.3.1): dependencies: - abbrev: 1.1.1 - dev: false + marked: 7.0.4 + react: 18.3.1 - /normalize-package-data@2.5.0: - resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} - dependencies: - hosted-git-info: 2.8.9 - resolve: 1.22.2 - semver: 5.7.1 - validate-npm-package-license: 3.0.4 + mdn-data@2.0.14: {} - /normalize-path@3.0.0: - resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} - engines: {node: '>=0.10.0'} + mdn-data@2.0.28: {} - /normalize-range@0.1.2: - resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} - engines: {node: '>=0.10.0'} + mdn-data@2.0.30: {} - /normalize-url@3.3.0: - resolution: {integrity: sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==} - engines: {node: '>=6'} - dev: true + mdn-data@2.0.4: {} - /normalize-url@6.1.0: - resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==} - engines: {node: '>=10'} + media-typer@0.3.0: {} - /npm-bundled@1.1.2: - resolution: {integrity: sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ==} + mem-fs-editor@9.7.0(mem-fs@2.3.0): dependencies: - npm-normalize-package-bin: 1.0.1 + binaryextensions: 4.19.0 + commondir: 1.0.1 + deep-extend: 0.6.0 + ejs: 3.1.10 + globby: 11.1.0 + isbinaryfile: 5.0.4 + minimatch: 7.4.6 + multimatch: 5.0.0 + normalize-path: 3.0.0 + textextensions: 5.16.0 + optionalDependencies: + mem-fs: 2.3.0 - /npm-install-checks@4.0.0: - resolution: {integrity: sha512-09OmyDkNLYwqKPOnbI8exiOZU2GVVmQp7tgez2BPi5OZC8M82elDAps7sxC4l//uSUtotWqoEIDwjRvWH4qz8w==} - engines: {node: '>=10'} + mem-fs@2.3.0: dependencies: - semver: 7.5.0 + '@types/node': 15.14.9 + '@types/vinyl': 2.0.12 + vinyl: 2.2.1 + vinyl-file: 3.0.0 - /npm-normalize-package-bin@1.0.1: - resolution: {integrity: sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==} + memfs@3.5.3: + dependencies: + fs-monkey: 1.0.6 - /npm-normalize-package-bin@2.0.0: - resolution: {integrity: sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + memory-pager@1.5.0: {} - /npm-package-arg@8.1.5: - resolution: {integrity: sha512-LhgZrg0n0VgvzVdSm1oiZworPbTxYHUJCgtsJW8mGvlDpxTM1vSJc3m5QZeUkhAHIzbz3VCHd/R4osi1L1Tg/Q==} - engines: {node: '>=10'} - dependencies: - hosted-git-info: 4.1.0 - semver: 7.5.0 - validate-npm-package-name: 3.0.0 + mensch@0.3.4: {} - /npm-packlist@3.0.0: - resolution: {integrity: sha512-L/cbzmutAwII5glUcf2DBRNY/d0TFd4e/FnaZigJV6JD85RHZXJFGwCndjMWiiViiWSsWt3tiOLpI3ByTnIdFQ==} - engines: {node: '>=10'} - hasBin: true - dependencies: - glob: 7.2.3 - ignore-walk: 4.0.1 - npm-bundled: 1.1.2 - npm-normalize-package-bin: 1.0.1 + merge-descriptors@1.0.3: {} - /npm-pick-manifest@6.1.1: - resolution: {integrity: sha512-dBsdBtORT84S8V8UTad1WlUyKIY9iMsAmqxHbLdeEeBNMLQDlDWWra3wYUx9EBEIiG/YwAy0XyNHDd2goAsfuA==} - dependencies: - npm-install-checks: 4.0.0 - npm-normalize-package-bin: 1.0.1 - npm-package-arg: 8.1.5 - semver: 7.5.0 + merge-stream@2.0.0: {} - /npm-registry-fetch@12.0.2: - resolution: {integrity: sha512-Df5QT3RaJnXYuOwtXBXS9BWs+tHH2olvkCLh6jcR/b/u3DvPMlp3J0TvvYwplPKxHMOwfg287PYih9QqaVFoKA==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16} + merge2@1.4.1: {} + + methods@1.1.2: {} + + microbundle@0.15.1(@types/babel__core@7.20.5)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)): dependencies: - make-fetch-happen: 10.2.1 - minipass: 3.3.6 - minipass-fetch: 1.4.1 - minipass-json-stream: 1.0.1 - minizlib: 2.1.2 - npm-package-arg: 8.1.5 + '@babel/core': 7.26.0 + '@babel/plugin-proposal-class-properties': 7.12.1(@babel/core@7.26.0) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.26.0) + '@babel/plugin-syntax-jsx': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-flow-strip-types': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-react-jsx': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-transform-regenerator': 7.25.9(@babel/core@7.26.0) + '@babel/preset-env': 7.26.0(@babel/core@7.26.0) + '@babel/preset-flow': 7.25.9(@babel/core@7.26.0) + '@babel/preset-react': 7.25.9(@babel/core@7.26.0) + '@rollup/plugin-alias': 3.1.9(rollup@2.79.2) + '@rollup/plugin-babel': 5.3.1(@babel/core@7.26.0)(@types/babel__core@7.20.5)(rollup@2.79.2) + '@rollup/plugin-commonjs': 17.1.0(rollup@2.79.2) + '@rollup/plugin-json': 4.1.0(rollup@2.79.2) + '@rollup/plugin-node-resolve': 11.2.1(rollup@2.79.2) + '@surma/rollup-plugin-off-main-thread': 2.2.3 + asyncro: 3.0.0 + autoprefixer: 10.4.20(postcss@8.4.47) + babel-plugin-macros: 3.1.0 + babel-plugin-transform-async-to-promises: 0.8.18 + babel-plugin-transform-replace-expressions: 0.2.0(@babel/core@7.26.0) + brotli-size: 4.0.0 + builtin-modules: 3.3.0 + camelcase: 6.3.0 + escape-string-regexp: 4.0.0 + filesize: 6.4.0 + gzip-size: 6.0.0 + kleur: 4.1.5 + lodash.merge: 4.6.2 + postcss: 8.4.47 + pretty-bytes: 5.6.0 + rollup: 2.79.2 + rollup-plugin-bundle-size: 1.0.3 + rollup-plugin-postcss: 4.0.2(postcss@8.4.47)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)) + rollup-plugin-terser: 7.0.2(rollup@2.79.2) + rollup-plugin-typescript2: 0.32.1(rollup@2.79.2)(typescript@4.9.5) + rollup-plugin-visualizer: 5.12.0(rollup@2.79.2) + sade: 1.8.1 + terser: 5.36.0 + tiny-glob: 0.2.9 + tslib: 2.8.0 + typescript: 4.9.5 transitivePeerDependencies: - - bluebird + - '@types/babel__core' - supports-color + - ts-node - /npm-run-path@4.0.1: - resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} - engines: {node: '>=8'} + micromatch@4.0.8: dependencies: - path-key: 3.1.1 + braces: 3.0.3 + picomatch: 2.3.1 - /npmlog@4.1.2: - resolution: {integrity: sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==} - dependencies: - are-we-there-yet: 1.1.7 - console-control-strings: 1.1.0 - gauge: 2.7.4 - set-blocking: 2.0.0 - dev: false + mime-db@1.52.0: {} - /npmlog@5.0.1: - resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==} + mime-db@1.53.0: {} + + mime-db@1.54.0: {} + + mime-types@2.1.35: dependencies: - are-we-there-yet: 2.0.0 - console-control-strings: 1.1.0 - gauge: 3.0.2 - set-blocking: 2.0.0 + mime-db: 1.52.0 - /npmlog@6.0.2: - resolution: {integrity: sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + mime-types@3.0.1: dependencies: - are-we-there-yet: 3.0.1 - console-control-strings: 1.1.0 - gauge: 4.0.4 - set-blocking: 2.0.0 + mime-db: 1.54.0 + + mime@1.6.0: {} + + mime@2.6.0: {} + + mime@3.0.0: + optional: true + + mimic-fn@2.1.0: {} + + mimic-function@5.0.1: {} + + mimic-response@3.1.0: {} + + min-indent@1.0.1: {} - /nth-check@1.0.2: - resolution: {integrity: sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==} + mini-css-extract-plugin@2.9.1(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))): dependencies: - boolbase: 1.0.0 + schema-utils: 4.2.0 + tapable: 2.2.1 + webpack: 5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(esbuild@0.25.6) - /nth-check@2.1.1: - resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + minimalistic-assert@1.0.1: {} + + minimalistic-crypto-utils@1.0.1: {} + + minimatch@10.0.3: dependencies: - boolbase: 1.0.0 - dev: false + '@isaacs/brace-expansion': 5.0.0 - /num2fraction@1.2.2: - resolution: {integrity: sha512-Y1wZESM7VUThYY+4W+X4ySH2maqcA+p7UR+w8VWNWVAd6lwuXXWz/w/Cz43J/dI2I+PS6wD5N+bJUF+gjWvIqg==} - dev: true + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.11 - /number-is-nan@1.0.1: - resolution: {integrity: sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==} - engines: {node: '>=0.10.0'} + minimatch@5.1.6: + dependencies: + brace-expansion: 2.0.1 - /nwsapi@2.2.3: - resolution: {integrity: sha512-jscxIO4/VKScHlbmFBdV1Z6LXnLO+ZR4VMtypudUdfwtKxUN3TQcNFIHLwKtrUbDyHN4/GycY9+oRGZ2XMXYPw==} - dev: false + minimatch@7.4.6: + dependencies: + brace-expansion: 2.0.1 - /oauth@0.9.15: - resolution: {integrity: sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==} - dev: false + minimatch@9.0.1: + dependencies: + brace-expansion: 2.0.1 - /obj-case@0.2.1: - resolution: {integrity: sha512-PquYBBTy+Y6Ob/O2574XHhDtHJlV1cJHMCgW+rDRc9J5hhmRelJB3k5dTK/3cVmFVtzvAKuENeuLpoyTzMzkOg==} - dev: true + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.1 - /object-assign@4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} - engines: {node: '>=0.10.0'} + minimist@1.2.8: {} - /object-hash@2.2.0: - resolution: {integrity: sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==} - engines: {node: '>= 6'} - dev: false + minipass-collect@1.0.2: + dependencies: + minipass: 3.3.6 - /object-hash@3.0.0: - resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} - engines: {node: '>= 6'} + minipass-fetch@1.4.1: + dependencies: + minipass: 3.3.6 + minipass-sized: 1.0.3 + minizlib: 2.1.2 + optionalDependencies: + encoding: 0.1.13 - /object-inspect@1.12.3: - resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==} + minipass-fetch@2.1.2: + dependencies: + minipass: 3.3.6 + minipass-sized: 1.0.3 + minizlib: 2.1.2 + optionalDependencies: + encoding: 0.1.13 - /object-is@1.1.5: - resolution: {integrity: sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==} - engines: {node: '>= 0.4'} + minipass-fetch@3.0.5: dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 + minipass: 7.1.2 + minipass-sized: 1.0.3 + minizlib: 2.1.2 + optionalDependencies: + encoding: 0.1.13 - /object-keys@1.1.1: - resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} - engines: {node: '>= 0.4'} + minipass-flush@1.0.5: + dependencies: + minipass: 3.3.6 - /object.assign@4.1.4: - resolution: {integrity: sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==} - engines: {node: '>= 0.4'} + minipass-json-stream@1.0.2: dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - has-symbols: 1.0.3 - object-keys: 1.1.1 + jsonparse: 1.3.1 + minipass: 3.3.6 - /object.entries@1.1.6: - resolution: {integrity: sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w==} - engines: {node: '>= 0.4'} + minipass-pipeline@1.2.4: dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 + minipass: 3.3.6 - /object.fromentries@2.0.6: - resolution: {integrity: sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==} - engines: {node: '>= 0.4'} + minipass-sized@1.0.3: dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 + minipass: 3.3.6 - /object.getownpropertydescriptors@2.1.5: - resolution: {integrity: sha512-yDNzckpM6ntyQiGTik1fKV1DcVDRS+w8bvpWNCBanvH5LfRX9O8WTHqQzG4RZwRAM4I0oU7TV11Lj5v0g20ibw==} - engines: {node: '>= 0.8'} + minipass@3.3.6: dependencies: - array.prototype.reduce: 1.0.5 - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 + yallist: 4.0.0 - /object.hasown@1.1.2: - resolution: {integrity: sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==} + minipass@5.0.0: {} + + minipass@7.1.2: {} + + minizlib@2.1.2: dependencies: - define-properties: 1.2.0 - es-abstract: 1.21.2 + minipass: 3.3.6 + yallist: 4.0.0 - /object.values@1.1.6: - resolution: {integrity: sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==} - engines: {node: '>= 0.4'} + minizlib@3.0.1: dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 + minipass: 7.1.2 + rimraf: 5.0.10 - /obuf@1.1.2: - resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==} - dev: false + mjml-accordion@5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3): + dependencies: + '@babel/runtime': 7.26.0 + lodash: 4.17.21 + mjml-core: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + transitivePeerDependencies: + - purgecss + - relateurl + - srcset + - svgo + - terser + - typescript + - uncss - /oidc-token-hash@5.0.2: - resolution: {integrity: sha512-U91Ba78GtVBxcExLI7U+hC2AwJQqXQEW/D3fjmJC4hhSVIgdl954KO4Gu95WqAlgDKJdLATxkmuxraWLT0fVRQ==} - engines: {node: ^10.13.0 || >=12.0.0} - dev: false + mjml-body@5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3): + dependencies: + '@babel/runtime': 7.26.0 + lodash: 4.17.21 + mjml-core: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + transitivePeerDependencies: + - purgecss + - relateurl + - srcset + - svgo + - terser + - typescript + - uncss - /on-finished@2.4.1: - resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} - engines: {node: '>= 0.8'} + mjml-button@5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3): dependencies: - ee-first: 1.1.1 + '@babel/runtime': 7.26.0 + lodash: 4.17.21 + mjml-core: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + transitivePeerDependencies: + - purgecss + - relateurl + - srcset + - svgo + - terser + - typescript + - uncss - /on-headers@1.0.2: - resolution: {integrity: sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==} - engines: {node: '>= 0.8'} - dev: false + mjml-carousel@5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3): + dependencies: + '@babel/runtime': 7.26.0 + lodash: 4.17.21 + mjml-core: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + transitivePeerDependencies: + - purgecss + - relateurl + - srcset + - svgo + - terser + - typescript + - uncss - /once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + mjml-cli@5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3): dependencies: - wrappy: 1.0.2 + '@babel/runtime': 7.26.0 + chokidar: 3.6.0 + glob: 10.4.5 + lodash: 4.17.21 + minimatch: 9.0.5 + mjml-core: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + mjml-parser-xml: 5.0.0-alpha.6 + mjml-validator: 5.0.0-alpha.6 + yargs: 17.7.2 + transitivePeerDependencies: + - purgecss + - relateurl + - srcset + - svgo + - terser + - typescript + - uncss - /onetime@5.1.2: - resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} - engines: {node: '>=6'} + mjml-column@5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3): dependencies: - mimic-fn: 2.1.0 + '@babel/runtime': 7.26.0 + lodash: 4.17.21 + mjml-core: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + transitivePeerDependencies: + - purgecss + - relateurl + - srcset + - svgo + - terser + - typescript + - uncss - /ono@4.0.11: - resolution: {integrity: sha512-jQ31cORBFE6td25deYeD80wxKBMj+zBmHTrVxnc6CKhx8gho6ipmWM5zj/oeoqioZ99yqBls9Z/9Nss7J26G2g==} + mjml-core@5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3): dependencies: - format-util: 1.0.5 - dev: false + '@babel/runtime': 7.26.0 + cheerio: 1.0.0-rc.12 + cssnano: 7.0.6(postcss@8.4.47) + detect-node: 2.1.0 + htmlnano: 2.1.1(cssnano@7.0.6(postcss@8.4.47))(postcss@8.4.47)(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + juice: 11.0.0 + lodash: 4.17.21 + mjml-parser-xml: 5.0.0-alpha.6 + mjml-validator: 5.0.0-alpha.6 + postcss: 8.4.47 + prettier: 3.4.2 + transitivePeerDependencies: + - purgecss + - relateurl + - srcset + - svgo + - terser + - typescript + - uncss - /open@8.4.2: - resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} - engines: {node: '>=12'} + mjml-divider@5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3): dependencies: - define-lazy-prop: 2.0.0 - is-docker: 2.2.1 - is-wsl: 2.2.0 + '@babel/runtime': 7.26.0 + lodash: 4.17.21 + mjml-core: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + transitivePeerDependencies: + - purgecss + - relateurl + - srcset + - svgo + - terser + - typescript + - uncss - /openid-client@5.4.0: - resolution: {integrity: sha512-hgJa2aQKcM2hn3eyVtN12tEA45ECjTJPXCgUh5YzTzy9qwapCvmDTVPWOcWVL0d34zeQoQ/hbG9lJhl3AYxJlQ==} + mjml-group@5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3): dependencies: - jose: 4.13.1 - lru-cache: 6.0.0 - object-hash: 2.2.0 - oidc-token-hash: 5.0.2 - dev: false + '@babel/runtime': 7.26.0 + lodash: 4.17.21 + mjml-core: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + transitivePeerDependencies: + - purgecss + - relateurl + - srcset + - svgo + - terser + - typescript + - uncss - /optionator@0.8.3: - resolution: {integrity: sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==} - engines: {node: '>= 0.8.0'} + mjml-head-attributes@5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3): dependencies: - deep-is: 0.1.4 - fast-levenshtein: 2.0.6 - levn: 0.3.0 - prelude-ls: 1.1.2 - type-check: 0.3.2 - word-wrap: 1.2.3 + '@babel/runtime': 7.26.0 + lodash: 4.17.21 + mjml-core: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + transitivePeerDependencies: + - purgecss + - relateurl + - srcset + - svgo + - terser + - typescript + - uncss - /optionator@0.9.1: - resolution: {integrity: sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==} - engines: {node: '>= 0.8.0'} + mjml-head-breakpoint@5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3): dependencies: - deep-is: 0.1.4 - fast-levenshtein: 2.0.6 - levn: 0.4.1 - prelude-ls: 1.2.1 - type-check: 0.4.0 - word-wrap: 1.2.3 + '@babel/runtime': 7.26.0 + lodash: 4.17.21 + mjml-core: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + transitivePeerDependencies: + - purgecss + - relateurl + - srcset + - svgo + - terser + - typescript + - uncss - /ora@5.4.1: - resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} - engines: {node: '>=10'} + mjml-head-font@5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3): dependencies: - bl: 4.1.0 - chalk: 4.1.2 - cli-cursor: 3.1.0 - cli-spinners: 2.8.0 - is-interactive: 1.0.0 - is-unicode-supported: 0.1.0 - log-symbols: 4.1.0 - strip-ansi: 6.0.1 - wcwidth: 1.0.1 + '@babel/runtime': 7.26.0 + lodash: 4.17.21 + mjml-core: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + transitivePeerDependencies: + - purgecss + - relateurl + - srcset + - svgo + - terser + - typescript + - uncss - /os-tmpdir@1.0.2: - resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} - engines: {node: '>=0.10.0'} + mjml-head-html-attributes@5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3): + dependencies: + '@babel/runtime': 7.26.0 + lodash: 4.17.21 + mjml-core: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + transitivePeerDependencies: + - purgecss + - relateurl + - srcset + - svgo + - terser + - typescript + - uncss - /outdent@0.8.0: - resolution: {integrity: sha512-KiOAIsdpUTcAXuykya5fnVVT+/5uS0Q1mrkRHcF89tpieSmY33O/tmc54CqwA+bfhbtEfZUNLHaPUiB9X3jt1A==} - dev: true + mjml-head-preview@5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3): + dependencies: + '@babel/runtime': 7.26.0 + lodash: 4.17.21 + mjml-core: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + transitivePeerDependencies: + - purgecss + - relateurl + - srcset + - svgo + - terser + - typescript + - uncss - /p-cancelable@2.1.1: - resolution: {integrity: sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==} - engines: {node: '>=8'} - dev: true + mjml-head-style@5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3): + dependencies: + '@babel/runtime': 7.26.0 + lodash: 4.17.21 + mjml-core: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + transitivePeerDependencies: + - purgecss + - relateurl + - srcset + - svgo + - terser + - typescript + - uncss - /p-finally@1.0.0: - resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} - engines: {node: '>=4'} + mjml-head-title@5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3): + dependencies: + '@babel/runtime': 7.26.0 + lodash: 4.17.21 + mjml-core: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + transitivePeerDependencies: + - purgecss + - relateurl + - srcset + - svgo + - terser + - typescript + - uncss - /p-limit@2.3.0: - resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} - engines: {node: '>=6'} + mjml-head@5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3): dependencies: - p-try: 2.2.0 + '@babel/runtime': 7.26.0 + lodash: 4.17.21 + mjml-core: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + transitivePeerDependencies: + - purgecss + - relateurl + - srcset + - svgo + - terser + - typescript + - uncss - /p-limit@3.1.0: - resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} - engines: {node: '>=10'} + mjml-hero@5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3): dependencies: - yocto-queue: 0.1.0 + '@babel/runtime': 7.26.0 + lodash: 4.17.21 + mjml-core: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + transitivePeerDependencies: + - purgecss + - relateurl + - srcset + - svgo + - terser + - typescript + - uncss - /p-locate@3.0.0: - resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==} - engines: {node: '>=6'} + mjml-image@5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3): dependencies: - p-limit: 2.3.0 - dev: false + '@babel/runtime': 7.26.0 + lodash: 4.17.21 + mjml-core: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + transitivePeerDependencies: + - purgecss + - relateurl + - srcset + - svgo + - terser + - typescript + - uncss - /p-locate@4.1.0: - resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} - engines: {node: '>=8'} + mjml-navbar@5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3): dependencies: - p-limit: 2.3.0 + '@babel/runtime': 7.26.0 + lodash: 4.17.21 + mjml-core: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + transitivePeerDependencies: + - purgecss + - relateurl + - srcset + - svgo + - terser + - typescript + - uncss - /p-locate@5.0.0: - resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} - engines: {node: '>=10'} + mjml-parser-xml@5.0.0-alpha.6: dependencies: - p-limit: 3.1.0 + '@babel/runtime': 7.26.0 + detect-node: 2.1.0 + htmlparser2: 9.1.0 + lodash: 4.17.21 - /p-map@2.1.0: - resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} - engines: {node: '>=6'} - dev: true + mjml-preset-core@5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3): + dependencies: + '@babel/runtime': 7.26.0 + mjml-accordion: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + mjml-body: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + mjml-button: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + mjml-carousel: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + mjml-column: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + mjml-divider: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + mjml-group: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + mjml-head: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + mjml-head-attributes: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + mjml-head-breakpoint: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + mjml-head-font: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + mjml-head-html-attributes: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + mjml-head-preview: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + mjml-head-style: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + mjml-head-title: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + mjml-hero: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + mjml-image: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + mjml-navbar: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + mjml-raw: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + mjml-section: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + mjml-social: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + mjml-spacer: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + mjml-table: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + mjml-text: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + mjml-wrapper: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + transitivePeerDependencies: + - purgecss + - relateurl + - srcset + - svgo + - terser + - typescript + - uncss - /p-map@4.0.0: - resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} - engines: {node: '>=10'} + mjml-raw@5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3): dependencies: - aggregate-error: 3.1.0 + '@babel/runtime': 7.26.0 + lodash: 4.17.21 + mjml-core: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + transitivePeerDependencies: + - purgecss + - relateurl + - srcset + - svgo + - terser + - typescript + - uncss - /p-queue@6.6.2: - resolution: {integrity: sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==} - engines: {node: '>=8'} + mjml-react@2.0.8(mjml@5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - eventemitter3: 4.0.7 - p-timeout: 3.2.0 + '@babel/runtime': 7.26.0 + babel-runtime: 6.25.0 + color: 3.2.1 + mjml: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-reconciler: 0.26.2(react@18.3.1) - /p-retry@4.6.2: - resolution: {integrity: sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==} - engines: {node: '>=8'} + mjml-section@5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3): dependencies: - '@types/retry': 0.12.0 - retry: 0.13.1 - dev: false + '@babel/runtime': 7.26.0 + lodash: 4.17.21 + mjml-core: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + transitivePeerDependencies: + - purgecss + - relateurl + - srcset + - svgo + - terser + - typescript + - uncss - /p-timeout@3.2.0: - resolution: {integrity: sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==} - engines: {node: '>=8'} + mjml-social@5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3): dependencies: - p-finally: 1.0.0 + '@babel/runtime': 7.26.0 + lodash: 4.17.21 + mjml-core: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + transitivePeerDependencies: + - purgecss + - relateurl + - srcset + - svgo + - terser + - typescript + - uncss + + mjml-spacer@5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3): + dependencies: + '@babel/runtime': 7.26.0 + lodash: 4.17.21 + mjml-core: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + transitivePeerDependencies: + - purgecss + - relateurl + - srcset + - svgo + - terser + - typescript + - uncss + + mjml-table@5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3): + dependencies: + '@babel/runtime': 7.26.0 + lodash: 4.17.21 + mjml-core: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + transitivePeerDependencies: + - purgecss + - relateurl + - srcset + - svgo + - terser + - typescript + - uncss - /p-transform@1.3.0: - resolution: {integrity: sha512-UJKdSzgd3KOnXXAtqN5+/eeHcvTn1hBkesEmElVgvO/NAYcxAvmjzIGmnNd3Tb/gRAvMBdNRFD4qAWdHxY6QXg==} - engines: {node: '>=12.10.0'} + mjml-text@5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3): dependencies: - debug: 4.3.4 - p-queue: 6.6.2 + '@babel/runtime': 7.26.0 + lodash: 4.17.21 + mjml-core: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) transitivePeerDependencies: - - supports-color + - purgecss + - relateurl + - srcset + - svgo + - terser + - typescript + - uncss - /p-try@2.2.0: - resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} - engines: {node: '>=6'} + mjml-validator@5.0.0-alpha.6: + dependencies: + '@babel/runtime': 7.26.0 - /pac-proxy-agent@5.0.0: - resolution: {integrity: sha512-CcFG3ZtnxO8McDigozwE3AqAw15zDvGH+OjXO4kzf7IkEKkQ4gxQ+3sdF50WmhQ4P/bVusXcqNE2S3XrNURwzQ==} - engines: {node: '>= 8'} + mjml-wrapper@5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3): dependencies: - '@tootallnate/once': 1.1.2 - agent-base: 6.0.2 - debug: 4.3.4 - get-uri: 3.0.2 - http-proxy-agent: 4.0.1 - https-proxy-agent: 5.0.1 - pac-resolver: 5.0.1 - raw-body: 2.5.2 - socks-proxy-agent: 5.0.1 + '@babel/runtime': 7.26.0 + lodash: 4.17.21 + mjml-core: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + mjml-section: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) transitivePeerDependencies: - - supports-color - dev: true + - purgecss + - relateurl + - srcset + - svgo + - terser + - typescript + - uncss - /pac-resolver@5.0.1: - resolution: {integrity: sha512-cy7u00ko2KVgBAjuhevqpPeHIkCIqPe1v24cydhWjmeuzaBfmUWFCZJ1iAh5TuVzVZoUzXIW7K8sMYOZ84uZ9Q==} - engines: {node: '>= 8'} + mjml@5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3): dependencies: - degenerator: 3.0.3 - ip: 1.1.8 - netmask: 2.0.2 - dev: true + '@babel/runtime': 7.26.0 + mjml-cli: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + mjml-core: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + mjml-preset-core: 5.0.0-alpha.6(relateurl@0.2.7)(svgo@3.3.2)(terser@5.36.0)(typescript@5.6.3) + mjml-validator: 5.0.0-alpha.6 + transitivePeerDependencies: + - purgecss + - relateurl + - srcset + - svgo + - terser + - typescript + - uncss - /packet-reader@1.0.0: - resolution: {integrity: sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==} + mkdirp-classic@0.5.3: {} - /pacote@12.0.3: - resolution: {integrity: sha512-CdYEl03JDrRO3x18uHjBYA9TyoW8gy+ThVcypcDkxPtKlw76e4ejhYB6i9lJ+/cebbjpqPW/CijjqxwDTts8Ow==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16} - hasBin: true + mkdirp-infer-owner@2.0.0: dependencies: - '@npmcli/git': 2.1.0 - '@npmcli/installed-package-contents': 1.0.7 - '@npmcli/promise-spawn': 1.3.2 - '@npmcli/run-script': 2.0.0 - cacache: 15.3.0 chownr: 2.0.0 - fs-minipass: 2.1.0 infer-owner: 1.0.4 - minipass: 3.3.6 mkdirp: 1.0.4 - npm-package-arg: 8.1.5 - npm-packlist: 3.0.0 - npm-pick-manifest: 6.1.1 - npm-registry-fetch: 12.0.2 - promise-retry: 2.0.1 - read-package-json-fast: 2.0.3 - rimraf: 3.0.2 - ssri: 8.0.1 - tar: 6.1.13 - transitivePeerDependencies: - - bluebird - - supports-color - - /pako@0.2.9: - resolution: {integrity: sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==} - dev: true - /param-case@2.1.1: - resolution: {integrity: sha512-eQE845L6ot89sk2N8liD8HAuH4ca6Vvr7VWAWwt7+kvvG5aBcPmmphQ68JsEG2qa9n1TykS2DLeMt363AAH8/w==} + mkdirp@0.5.6: dependencies: - no-case: 2.3.2 - dev: false + minimist: 1.2.8 - /param-case@3.0.4: - resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==} - dependencies: - dot-case: 3.0.4 - tslib: 2.5.0 - dev: false + mkdirp@1.0.4: {} - /parent-module@1.0.1: - resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} - engines: {node: '>=6'} - dependencies: - callsites: 3.1.0 + mkdirp@3.0.1: {} - /parenthesis@3.1.8: - resolution: {integrity: sha512-KF/U8tk54BgQewkJPvB4s/US3VQY68BRDpH638+7O/n58TpnwiwnOtGIOsT2/i+M78s61BBpeC83STB88d8sqw==} - dev: true + mmdb-lib@2.1.1: {} - /parse-conflict-json@2.0.2: - resolution: {integrity: sha512-jDbRGb00TAPFsKWCpZZOT93SxVP9nONOSgES3AevqRq/CHvavEBvKAjxX9p5Y5F0RZLxH9Ufd9+RwtCsa+lFDA==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + moment@2.30.1: {} + + monaco-editor@0.52.0: {} + + mongodb-connection-string-url@3.0.1: dependencies: - json-parse-even-better-errors: 2.3.1 - just-diff: 5.2.0 - just-diff-apply: 5.5.0 + '@types/whatwg-url': 11.0.5 + whatwg-url: 13.0.0 - /parse-entities@4.0.1: - resolution: {integrity: sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w==} - dependencies: - '@types/unist': 2.0.6 - character-entities: 2.0.2 - character-entities-legacy: 3.0.0 - character-reference-invalid: 2.0.1 - decode-named-character-reference: 1.0.2 - is-alphanumerical: 2.0.1 - is-decimal: 2.0.1 - is-hexadecimal: 2.0.1 - dev: true - - /parse-json@4.0.0: - resolution: {integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==} - engines: {node: '>=4'} + mongodb@6.16.0(@mongodb-js/zstd@2.0.1)(socks@2.8.3): dependencies: - error-ex: 1.3.2 - json-parse-better-errors: 1.0.2 - dev: true + '@mongodb-js/saslprep': 1.1.9 + bson: 6.10.3 + mongodb-connection-string-url: 3.0.1 + optionalDependencies: + '@mongodb-js/zstd': 2.0.1 + socks: 2.8.3 - /parse-json@5.2.0: - resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} - engines: {node: '>=8'} + monorel@0.4.2: dependencies: - '@babel/code-frame': 7.21.4 - error-ex: 1.3.2 - json-parse-even-better-errors: 2.3.1 - lines-and-columns: 1.2.4 + minimist: 1.2.8 + tslib: 2.8.0 - /parse-ms@2.1.0: - resolution: {integrity: sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA==} - engines: {node: '>=6'} - dev: true + motion-dom@12.23.6: + dependencies: + motion-utils: 12.23.6 - /parse-node-version@1.0.1: - resolution: {integrity: sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==} - engines: {node: '>= 0.10'} - dev: true + motion-utils@12.23.6: {} + + mri@1.2.0: {} - /parse5-htmlparser2-tree-adapter@7.0.0: - resolution: {integrity: sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==} + mrmime@2.0.1: {} + + ms@2.0.0: {} + + ms@2.1.3: {} + + multicast-dns@7.2.5: dependencies: - domhandler: 5.0.3 - parse5: 7.1.2 - dev: false + dns-packet: 5.6.1 + thunky: 1.1.0 - /parse5@6.0.1: - resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==} - dev: false + multimatch@5.0.0: + dependencies: + '@types/minimatch': 3.0.5 + array-differ: 3.0.0 + array-union: 2.1.0 + arrify: 2.0.1 + minimatch: 3.1.2 - /parse5@7.1.2: - resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==} + mute-stream@0.0.8: {} + + mute-stream@1.0.0: {} + + mz@2.7.0: dependencies: - entities: 4.4.0 - dev: false + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 - /parseurl@1.3.3: - resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} - engines: {node: '>= 0.8'} + nan@2.22.0: {} - /pascal-case@3.1.2: - resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} + nano-css@5.6.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - no-case: 3.0.4 - tslib: 2.5.0 - dev: false + '@jridgewell/sourcemap-codec': 1.5.0 + css-tree: 1.1.3 + csstype: 3.1.3 + fastest-stable-stringify: 2.0.2 + inline-style-prefixer: 7.0.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + rtl-css-js: 1.16.1 + stacktrace-js: 2.0.2 + stylis: 4.3.4 - /path-browserify@1.0.1: - resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} - dev: true + nanoid@3.3.8: {} - /path-exists@3.0.0: - resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==} - engines: {node: '>=4'} - dev: false + napi-build-utils@2.0.0: {} - /path-exists@4.0.0: - resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} - engines: {node: '>=8'} + natural-compare-lite@1.4.0: {} - /path-is-absolute@1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} + natural-compare@1.4.0: {} - /path-is-inside@1.0.2: - resolution: {integrity: sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==} - dev: true + needle@3.3.1: + dependencies: + iconv-lite: 0.6.3 + sax: 1.4.1 + optional: true - /path-key@3.1.1: - resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} - engines: {node: '>=8'} + negotiator@0.6.3: {} - /path-parse@1.0.7: - resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + negotiator@0.6.4: {} - /path-to-regexp@0.1.7: - resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} + neo-async@2.6.2: {} - /path-to-regexp@6.1.0: - resolution: {integrity: sha512-h9DqehX3zZZDCEm+xbfU0ZmwCGFCAAraPJWMXJ4+v32NjZJilVg3k1TcKsRgIb8IQ/izZSaydDc1OhJCZvs2Dw==} - dev: true + new-date@1.0.3: + dependencies: + '@segment/isodate': 1.0.3 - /path-to-regexp@6.2.1: - resolution: {integrity: sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==} - dev: true + next-auth@4.24.11(next@15.5.4(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.39.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(nodemailer@6.10.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.26.0 + '@panva/hkdf': 1.2.1 + cookie: 0.7.2 + jose: 4.15.9 + next: 15.5.4(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.39.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + oauth: 0.9.15 + openid-client: 5.7.0 + preact: 10.24.3 + preact-render-to-string: 5.2.6(preact@10.24.3) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + uuid: 8.3.2 + optionalDependencies: + nodemailer: 6.10.1 - /path-type@4.0.0: - resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} - engines: {node: '>=8'} + next@15.5.4(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.39.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@next/env': 15.5.4 + '@swc/helpers': 0.5.15 + caniuse-lite: 1.0.30001727 + postcss: 8.4.47 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + styled-jsx: 5.1.6(@babel/core@7.26.0)(react@18.3.1) + optionalDependencies: + '@next/swc-darwin-arm64': 15.5.4 + '@next/swc-darwin-x64': 15.5.4 + '@next/swc-linux-arm64-gnu': 15.5.4 + '@next/swc-linux-arm64-musl': 15.5.4 + '@next/swc-linux-x64-gnu': 15.5.4 + '@next/swc-linux-x64-musl': 15.5.4 + '@next/swc-win32-arm64-msvc': 15.5.4 + '@next/swc-win32-x64-msvc': 15.5.4 + '@opentelemetry/api': 1.9.0 + '@playwright/test': 1.39.0 + sharp: 0.34.3 + transitivePeerDependencies: + - '@babel/core' + - babel-plugin-macros - /pathe@1.1.0: - resolution: {integrity: sha512-ODbEPR0KKHqECXW1GoxdDb+AZvULmXjVPy4rt+pGo2+TnjJTIPJQSVS6N63n8T2Ip+syHhbn52OewKicV0373w==} - dev: true + next@15.5.4(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.39.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@next/env': 15.5.4 + '@swc/helpers': 0.5.15 + caniuse-lite: 1.0.30001727 + postcss: 8.4.47 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + styled-jsx: 5.1.6(@babel/core@7.26.10)(react@18.3.1) + optionalDependencies: + '@next/swc-darwin-arm64': 15.5.4 + '@next/swc-darwin-x64': 15.5.4 + '@next/swc-linux-arm64-gnu': 15.5.4 + '@next/swc-linux-arm64-musl': 15.5.4 + '@next/swc-linux-x64-gnu': 15.5.4 + '@next/swc-linux-x64-musl': 15.5.4 + '@next/swc-win32-arm64-msvc': 15.5.4 + '@next/swc-win32-x64-msvc': 15.5.4 + '@opentelemetry/api': 1.9.0 + '@playwright/test': 1.39.0 + sharp: 0.34.3 + transitivePeerDependencies: + - '@babel/core' + - babel-plugin-macros - /peek-stream@1.1.3: - resolution: {integrity: sha512-FhJ+YbOSBb9/rIl2ZeE/QHEsWn7PqNYt8ARAY3kIgNGOk13g9FGyIY6JIl/xB/3TFRVoTv5as0l11weORrTekA==} + next@15.5.4(@babel/core@7.26.10)(@opentelemetry/api@1.9.0)(@playwright/test@1.39.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0): dependencies: - buffer-from: 1.1.2 - duplexify: 3.7.1 - through2: 2.0.5 - dev: true + '@next/env': 15.5.4 + '@swc/helpers': 0.5.15 + caniuse-lite: 1.0.30001727 + postcss: 8.4.47 + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + styled-jsx: 5.1.6(@babel/core@7.26.10)(react@19.0.0) + optionalDependencies: + '@next/swc-darwin-arm64': 15.5.4 + '@next/swc-darwin-x64': 15.5.4 + '@next/swc-linux-arm64-gnu': 15.5.4 + '@next/swc-linux-arm64-musl': 15.5.4 + '@next/swc-linux-x64-gnu': 15.5.4 + '@next/swc-linux-x64-musl': 15.5.4 + '@next/swc-win32-arm64-msvc': 15.5.4 + '@next/swc-win32-x64-msvc': 15.5.4 + '@opentelemetry/api': 1.9.0 + '@playwright/test': 1.39.0 + sharp: 0.34.3 + transitivePeerDependencies: + - '@babel/core' + - babel-plugin-macros - /performance-now@2.1.0: - resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==} - dev: false + no-case@3.0.4: + dependencies: + lower-case: 2.0.2 + tslib: 2.8.0 - /periscopic@3.1.0: - resolution: {integrity: sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==} + node-abi@3.71.0: dependencies: - '@types/estree': 1.0.1 - estree-walker: 3.0.3 - is-reference: 3.0.1 - dev: true + semver: 7.7.2 - /pg-connection-string@2.5.0: - resolution: {integrity: sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==} + node-addon-api@4.3.0: {} - /pg-cursor@2.9.0(pg@8.10.0): - resolution: {integrity: sha512-tNX0FbHX6+hlhZVNbxhSQPDMNMFF6mOWQvwDobPROAFpilmXrZo3FozawqaBQKonFKpBloZZyWUL3Kkf5rLn6A==} - peerDependencies: - pg: ^8 + node-cache@5.1.2: dependencies: - pg: 8.10.0 + clone: 2.1.2 - /pg-int8@1.0.1: - resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} - engines: {node: '>=4.0.0'} + node-domexception@1.0.0: {} - /pg-minify@1.6.3: - resolution: {integrity: sha512-NoSsPqXxbkD8RIe+peQCqiea4QzXgosdTKY8p7PsbbGsh2F8TifDj/vJxfuR8qJwNYrijdSs7uf0tAe6WOyCsQ==} - engines: {node: '>=12.0.0'} - dev: false + node-fetch-commonjs@3.3.2: + dependencies: + node-domexception: 1.0.0 + web-streams-polyfill: 3.3.3 - /pg-pool@3.6.0(pg@8.10.0): - resolution: {integrity: sha512-clFRf2ksqd+F497kWFyM21tMjeikn60oGDmqMT8UBrynEwVEX/5R5xd2sdvdo1cZCFlguORNpVuqxIj+aK4cfQ==} - peerDependencies: - pg: '>=8.0' + node-fetch@2.7.0(encoding@0.1.13): dependencies: - pg: 8.10.0 + whatwg-url: 5.0.0 + optionalDependencies: + encoding: 0.1.13 - /pg-promise@11.4.3: - resolution: {integrity: sha512-b4wuukB+pkrLRZ53Z+3L9IONlIhOUSM/VlLQV2SnQzNJPJmDZj6ticgcMtZMDanAUEj+zX1FJOBrSpSR9TumXg==} - engines: {node: '>=14.0'} + node-fetch@3.3.2: + dependencies: + data-uri-to-buffer: 4.0.1 + fetch-blob: 3.2.0 + formdata-polyfill: 4.0.10 + + node-forge@1.3.1: {} + + node-gyp@8.4.1: dependencies: - assert-options: 0.8.1 - pg: 8.10.0 - pg-minify: 1.6.3 - spex: 3.3.0 + env-paths: 2.2.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + make-fetch-happen: 9.1.0 + nopt: 5.0.0 + npmlog: 6.0.2 + rimraf: 3.0.2 + semver: 7.7.2 + tar: 6.2.1 + which: 2.0.2 transitivePeerDependencies: - - pg-native - dev: false + - bluebird + - supports-color - /pg-protocol@1.6.0: - resolution: {integrity: sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==} + node-gyp@9.4.1: + dependencies: + env-paths: 2.2.1 + exponential-backoff: 3.1.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + make-fetch-happen: 10.2.1 + nopt: 6.0.0 + npmlog: 6.0.2 + rimraf: 3.0.2 + semver: 7.7.2 + tar: 6.2.1 + which: 2.0.2 + transitivePeerDependencies: + - bluebird + - supports-color - /pg-query-stream@4.4.0(pg@8.10.0): - resolution: {integrity: sha512-shmP973/ruBw3exSmIEBW9iubWvyyZMzp6oWfcHgvlqAe4LtaRyVqPGxgnuorbRqksK03mjOC9PU+Ohz3b0GkQ==} - peerDependencies: - pg: ^8 + node-html-parser@7.0.1: dependencies: - pg: 8.10.0 - pg-cursor: 2.9.0(pg@8.10.0) + css-select: 5.1.0 + he: 1.2.0 - /pg-types@2.2.0: - resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} - engines: {node: '>=4'} + node-int64@0.4.0: {} + + node-loader@2.0.0(webpack@5.95.0): dependencies: - pg-int8: 1.0.1 - postgres-array: 2.0.0 - postgres-bytea: 1.0.0 - postgres-date: 1.0.7 - postgres-interval: 1.2.0 + loader-utils: 2.0.4 + webpack: 5.95.0(@swc/core@1.11.10(@swc/helpers@0.5.15))(webpack-cli@5.1.4) - /pg@8.10.0: - resolution: {integrity: sha512-ke7o7qSTMb47iwzOSaZMfeR7xToFdkE71ifIipOAAaLIM0DYzfOAXlgFFmYUIE2BcJtvnVlGCID84ZzCegE8CQ==} - engines: {node: '>= 8.0.0'} - peerDependencies: - pg-native: '>=3.0.1' - peerDependenciesMeta: - pg-native: - optional: true + node-loader@2.0.0(webpack@5.99.5): dependencies: - buffer-writer: 2.0.0 - packet-reader: 1.0.0 - pg-connection-string: 2.5.0 - pg-pool: 3.6.0(pg@8.10.0) - pg-protocol: 1.6.0 - pg-types: 2.2.0 - pgpass: 1.0.5 + loader-utils: 2.0.4 + webpack: 5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(webpack-cli@6.0.1) - /pgpass@1.0.5: - resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==} + node-postgres@0.6.2: {} + + node-releases@2.0.18: {} + + node-releases@2.0.19: {} + + node-sql-parser@5.3.8: dependencies: - split2: 4.2.0 + '@types/pegjs': 0.10.6 + big-integer: 1.6.52 - /picocolors@0.2.1: - resolution: {integrity: sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==} + nodemailer@6.10.1: {} - /picocolors@1.0.0: - resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + nodemon@3.1.7: + dependencies: + chokidar: 3.6.0 + debug: 4.3.7(supports-color@5.5.0) + ignore-by-default: 1.0.1 + minimatch: 3.1.2 + pstree.remy: 1.1.8 + semver: 7.6.3 + simple-update-notifier: 2.0.0 + supports-color: 5.5.0 + touch: 3.1.1 + undefsafe: 2.0.5 - /picomatch@2.3.1: - resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} - engines: {node: '>=8.6'} + nopt@5.0.0: + dependencies: + abbrev: 1.1.1 - /pify@2.3.0: - resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} - engines: {node: '>=0.10.0'} + nopt@6.0.0: + dependencies: + abbrev: 1.1.1 - /pify@4.0.1: - resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} - engines: {node: '>=6'} + nopt@7.2.1: + dependencies: + abbrev: 2.0.0 - /pify@5.0.0: - resolution: {integrity: sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA==} - engines: {node: '>=10'} - dev: true + normalize-package-data@2.5.0: + dependencies: + hosted-git-info: 2.8.9 + resolve: 1.22.8 + semver: 5.7.2 + validate-npm-package-license: 3.0.4 - /pinkie-promise@2.0.1: - resolution: {integrity: sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==} - engines: {node: '>=0.10.0'} + normalize-package-data@5.0.0: dependencies: - pinkie: 2.0.4 - dev: true + hosted-git-info: 6.1.1 + is-core-module: 2.15.1 + semver: 7.7.2 + validate-npm-package-license: 3.0.4 - /pinkie@2.0.4: - resolution: {integrity: sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==} - engines: {node: '>=0.10.0'} - dev: true + normalize-path@3.0.0: {} - /pirates@4.0.5: - resolution: {integrity: sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==} - engines: {node: '>= 6'} + normalize-range@0.1.2: {} - /pkg-dir@4.2.0: - resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} - engines: {node: '>=8'} + normalize-url@6.1.0: {} + + npm-bundled@1.1.2: dependencies: - find-up: 4.1.0 + npm-normalize-package-bin: 1.0.1 - /pkg-types@1.0.2: - resolution: {integrity: sha512-hM58GKXOcj8WTqUXnsQyJYXdeAPbythQgEF3nTcEo+nkD49chjQ9IKm/QJy9xf6JakXptz86h7ecP2024rrLaQ==} + npm-bundled@3.0.1: dependencies: - jsonc-parser: 3.2.0 - mlly: 1.2.0 - pathe: 1.1.0 - dev: true + npm-normalize-package-bin: 3.0.1 - /pkg-up@3.1.0: - resolution: {integrity: sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==} - engines: {node: '>=8'} + npm-install-checks@4.0.0: dependencies: - find-up: 3.0.0 - dev: false + semver: 7.7.2 + + npm-install-checks@6.3.0: + dependencies: + semver: 7.7.2 + + npm-normalize-package-bin@1.0.1: {} + + npm-normalize-package-bin@2.0.0: {} + + npm-normalize-package-bin@3.0.1: {} - /playwright-core@1.31.2: - resolution: {integrity: sha512-a1dFgCNQw4vCsG7bnojZjDnPewZcw7tZUNFN0ZkcLYKj+mPmXvg4MpaaKZ5SgqPsOmqIf2YsVRkgqiRDxD+fDQ==} - engines: {node: '>=14'} - hasBin: true - dev: true + npm-package-arg@10.1.0: + dependencies: + hosted-git-info: 6.1.1 + proc-log: 3.0.0 + semver: 7.7.2 + validate-npm-package-name: 5.0.1 - /postcss-attribute-case-insensitive@5.0.2(postcss@8.4.21): - resolution: {integrity: sha512-XIidXV8fDr0kKt28vqki84fRK8VW8eTuIa4PChv2MqKuT6C9UjmSKzen6KaWhWEoYvwxFCa7n/tC1SZ3tyq4SQ==} - engines: {node: ^12 || ^14 || >=16} - peerDependencies: - postcss: ^8.2 + npm-package-arg@8.1.5: dependencies: - postcss: 8.4.21 - postcss-selector-parser: 6.0.11 - dev: false + hosted-git-info: 4.1.0 + semver: 7.7.2 + validate-npm-package-name: 3.0.0 - /postcss-browser-comments@4.0.0(browserslist@4.21.5)(postcss@8.4.21): - resolution: {integrity: sha512-X9X9/WN3KIvY9+hNERUqX9gncsgBA25XaeR+jshHz2j8+sYyHktHw1JdKuMjeLpGktXidqDhA7b/qm1mrBDmgg==} - engines: {node: '>=8'} - peerDependencies: - browserslist: '>=4' - postcss: '>=8' + npm-packlist@3.0.0: dependencies: - browserslist: 4.21.5 - postcss: 8.4.21 - dev: false + glob: 7.2.3 + ignore-walk: 4.0.1 + npm-bundled: 1.1.2 + npm-normalize-package-bin: 1.0.1 - /postcss-calc@7.0.5: - resolution: {integrity: sha512-1tKHutbGtLtEZF6PT4JSihCHfIVldU72mZ8SdZHIYriIZ9fh9k9aWSppaT8rHsyI3dX+KSR+W+Ix9BMY3AODrg==} + npm-packlist@7.0.4: dependencies: - postcss: 7.0.39 - postcss-selector-parser: 6.0.11 - postcss-value-parser: 4.2.0 - dev: true + ignore-walk: 6.0.5 - /postcss-calc@8.2.4(postcss@8.4.21): - resolution: {integrity: sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==} - peerDependencies: - postcss: ^8.2.2 + npm-pick-manifest@6.1.1: dependencies: - postcss: 8.4.21 - postcss-selector-parser: 6.0.11 - postcss-value-parser: 4.2.0 - dev: false + npm-install-checks: 4.0.0 + npm-normalize-package-bin: 1.0.1 + npm-package-arg: 8.1.5 + semver: 7.7.2 - /postcss-clamp@4.1.0(postcss@8.4.21): - resolution: {integrity: sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow==} - engines: {node: '>=7.6.0'} - peerDependencies: - postcss: ^8.4.6 + npm-pick-manifest@8.0.2: dependencies: - postcss: 8.4.21 - postcss-value-parser: 4.2.0 - dev: false + npm-install-checks: 6.3.0 + npm-normalize-package-bin: 3.0.1 + npm-package-arg: 10.1.0 + semver: 7.7.2 - /postcss-color-functional-notation@4.2.4(postcss@8.4.21): - resolution: {integrity: sha512-2yrTAUZUab9s6CpxkxC4rVgFEVaR6/2Pipvi6qcgvnYiVqZcbDHEoBDhrXzyb7Efh2CCfHQNtcqWcIruDTIUeg==} - engines: {node: ^12 || ^14 || >=16} - peerDependencies: - postcss: ^8.2 + npm-registry-fetch@12.0.2: dependencies: - postcss: 8.4.21 - postcss-value-parser: 4.2.0 - dev: false + make-fetch-happen: 10.2.1 + minipass: 3.3.6 + minipass-fetch: 1.4.1 + minipass-json-stream: 1.0.2 + minizlib: 2.1.2 + npm-package-arg: 8.1.5 + transitivePeerDependencies: + - bluebird + - supports-color - /postcss-color-hex-alpha@8.0.4(postcss@8.4.21): - resolution: {integrity: sha512-nLo2DCRC9eE4w2JmuKgVA3fGL3d01kGq752pVALF68qpGLmx2Qrk91QTKkdUqqp45T1K1XV8IhQpcu1hoAQflQ==} - engines: {node: ^12 || ^14 || >=16} - peerDependencies: - postcss: ^8.4 + npm-registry-fetch@14.0.5: dependencies: - postcss: 8.4.21 - postcss-value-parser: 4.2.0 - dev: false + make-fetch-happen: 11.1.1 + minipass: 5.0.0 + minipass-fetch: 3.0.5 + minipass-json-stream: 1.0.2 + minizlib: 2.1.2 + npm-package-arg: 10.1.0 + proc-log: 3.0.0 + transitivePeerDependencies: + - supports-color - /postcss-color-rebeccapurple@7.1.1(postcss@8.4.21): - resolution: {integrity: sha512-pGxkuVEInwLHgkNxUc4sdg4g3py7zUeCQ9sMfwyHAT+Ezk8a4OaaVZ8lIY5+oNqA/BXXgLyXv0+5wHP68R79hg==} - engines: {node: ^12 || ^14 || >=16} - peerDependencies: - postcss: ^8.2 + npm-run-path@4.0.1: dependencies: - postcss: 8.4.21 - postcss-value-parser: 4.2.0 - dev: false + path-key: 3.1.1 - /postcss-colormin@4.0.3: - resolution: {integrity: sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw==} - engines: {node: '>=6.9.0'} + npmlog@5.0.1: dependencies: - browserslist: 4.21.5 - color: 3.2.1 - has: 1.0.3 - postcss: 7.0.39 - postcss-value-parser: 3.3.1 - dev: true + are-we-there-yet: 2.0.0 + console-control-strings: 1.1.0 + gauge: 3.0.2 + set-blocking: 2.0.0 - /postcss-colormin@5.3.1(postcss@8.4.21): - resolution: {integrity: sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 + npmlog@6.0.2: dependencies: - browserslist: 4.21.5 - caniuse-api: 3.0.0 - colord: 2.9.3 - postcss: 8.4.21 - postcss-value-parser: 4.2.0 - dev: false + are-we-there-yet: 3.0.1 + console-control-strings: 1.1.0 + gauge: 4.0.4 + set-blocking: 2.0.0 - /postcss-convert-values@4.0.1: - resolution: {integrity: sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ==} - engines: {node: '>=6.9.0'} + nth-check@2.1.1: dependencies: - postcss: 7.0.39 - postcss-value-parser: 3.3.1 - dev: true + boolbase: 1.0.0 - /postcss-convert-values@5.1.3(postcss@8.4.21): - resolution: {integrity: sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 + number-is-nan@1.0.1: {} + + nwsapi@2.2.13: {} + + nypm@0.6.0: dependencies: - browserslist: 4.21.5 - postcss: 8.4.21 - postcss-value-parser: 4.2.0 - dev: false + citty: 0.1.6 + consola: 3.4.2 + pathe: 2.0.3 + pkg-types: 2.2.0 + tinyexec: 0.3.2 - /postcss-custom-media@8.0.2(postcss@8.4.21): - resolution: {integrity: sha512-7yi25vDAoHAkbhAzX9dHx2yc6ntS4jQvejrNcC+csQJAXjj15e7VcWfMgLqBNAbOvqi5uIa9huOVwdHbf+sKqg==} - engines: {node: ^12 || ^14 || >=16} - peerDependencies: - postcss: ^8.3 + oauth@0.9.15: {} + + obj-case@0.2.1: {} + + object-assign@4.1.1: {} + + object-hash@2.2.0: {} + + object-hash@3.0.0: {} + + object-inspect@1.13.2: {} + + object-is@1.1.6: dependencies: - postcss: 8.4.21 - postcss-value-parser: 4.2.0 - dev: false + call-bind: 1.0.7 + define-properties: 1.2.1 - /postcss-custom-properties@12.1.11(postcss@8.4.21): - resolution: {integrity: sha512-0IDJYhgU8xDv1KY6+VgUwuQkVtmYzRwu+dMjnmdMafXYv86SWqfxkc7qdDvWS38vsjaEtv8e0vGOUQrAiMBLpQ==} - engines: {node: ^12 || ^14 || >=16} - peerDependencies: - postcss: ^8.2 + object-keys@1.1.1: {} + + object.assign@4.1.5: dependencies: - postcss: 8.4.21 - postcss-value-parser: 4.2.0 - dev: false + call-bind: 1.0.7 + define-properties: 1.2.1 + has-symbols: 1.0.3 + object-keys: 1.1.1 - /postcss-custom-selectors@6.0.3(postcss@8.4.21): - resolution: {integrity: sha512-fgVkmyiWDwmD3JbpCmB45SvvlCD6z9CG6Ie6Iere22W5aHea6oWa7EM2bpnv2Fj3I94L3VbtvX9KqwSi5aFzSg==} - engines: {node: ^12 || ^14 || >=16} - peerDependencies: - postcss: ^8.3 + object.entries@1.1.8: dependencies: - postcss: 8.4.21 - postcss-selector-parser: 6.0.11 - dev: false + call-bind: 1.0.7 + define-properties: 1.2.1 + es-object-atoms: 1.0.0 - /postcss-dir-pseudo-class@6.0.5(postcss@8.4.21): - resolution: {integrity: sha512-eqn4m70P031PF7ZQIvSgy9RSJ5uI2171O/OO/zcRNYpJbvaeKFUlar1aJ7rmgiQtbm0FSPsRewjpdS0Oew7MPA==} - engines: {node: ^12 || ^14 || >=16} - peerDependencies: - postcss: ^8.2 + object.fromentries@2.0.8: dependencies: - postcss: 8.4.21 - postcss-selector-parser: 6.0.11 - dev: false + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-object-atoms: 1.0.0 - /postcss-discard-comments@4.0.2: - resolution: {integrity: sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg==} - engines: {node: '>=6.9.0'} + object.getownpropertydescriptors@2.1.8: dependencies: - postcss: 7.0.39 - dev: true + array.prototype.reduce: 1.0.7 + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-object-atoms: 1.0.0 + gopd: 1.0.1 + safe-array-concat: 1.1.2 - /postcss-discard-comments@5.1.2(postcss@8.4.21): - resolution: {integrity: sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 + object.groupby@1.0.3: dependencies: - postcss: 8.4.21 - dev: false + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 - /postcss-discard-duplicates@4.0.2: - resolution: {integrity: sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ==} - engines: {node: '>=6.9.0'} + object.values@1.2.0: dependencies: - postcss: 7.0.39 - dev: true + call-bind: 1.0.7 + define-properties: 1.2.1 + es-object-atoms: 1.0.0 - /postcss-discard-duplicates@5.1.0(postcss@8.4.21): - resolution: {integrity: sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 + obuf@1.1.2: {} + + oidc-token-hash@5.0.3: {} + + on-finished@2.4.1: dependencies: - postcss: 8.4.21 - dev: false + ee-first: 1.1.1 - /postcss-discard-duplicates@5.1.0(postcss@8.4.22): - resolution: {integrity: sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 + on-headers@1.0.2: {} + + once@1.4.0: dependencies: - postcss: 8.4.22 - dev: true + wrappy: 1.0.2 - /postcss-discard-empty@4.0.1: - resolution: {integrity: sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w==} - engines: {node: '>=6.9.0'} + onetime@5.1.2: dependencies: - postcss: 7.0.39 - dev: true + mimic-fn: 2.1.0 - /postcss-discard-empty@5.1.1(postcss@8.4.21): - resolution: {integrity: sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 + onetime@7.0.0: dependencies: - postcss: 8.4.21 - dev: false + mimic-function: 5.0.1 - /postcss-discard-overridden@4.0.1: - resolution: {integrity: sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg==} - engines: {node: '>=6.9.0'} + open@8.4.2: dependencies: - postcss: 7.0.39 - dev: true + define-lazy-prop: 2.0.0 + is-docker: 2.2.1 + is-wsl: 2.2.0 - /postcss-discard-overridden@5.1.0(postcss@8.4.21): - resolution: {integrity: sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 + opener@1.5.2: {} + + openid-client@5.7.0: dependencies: - postcss: 8.4.21 - dev: false + jose: 4.15.9 + lru-cache: 6.0.0 + object-hash: 2.2.0 + oidc-token-hash: 5.0.3 - /postcss-double-position-gradients@3.1.2(postcss@8.4.21): - resolution: {integrity: sha512-GX+FuE/uBR6eskOK+4vkXgT6pDkexLokPaz/AbJna9s5Kzp/yl488pKPjhy0obB475ovfT1Wv8ho7U/cHNaRgQ==} - engines: {node: ^12 || ^14 || >=16} - peerDependencies: - postcss: ^8.2 + optionator@0.8.3: dependencies: - '@csstools/postcss-progressive-custom-properties': 1.3.0(postcss@8.4.21) - postcss: 8.4.21 - postcss-value-parser: 4.2.0 - dev: false + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.3.0 + prelude-ls: 1.1.2 + type-check: 0.3.2 + word-wrap: 1.2.5 - /postcss-env-function@4.0.6(postcss@8.4.21): - resolution: {integrity: sha512-kpA6FsLra+NqcFnL81TnsU+Z7orGtDTxcOhl6pwXeEq1yFPpRMkCDpHhrz8CFQDr/Wfm0jLiNQ1OsGGPjlqPwA==} - engines: {node: ^12 || ^14 || >=16} - peerDependencies: - postcss: ^8.4 + optionator@0.9.4: dependencies: - postcss: 8.4.21 - postcss-value-parser: 4.2.0 - dev: false + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 - /postcss-flexbugs-fixes@5.0.2(postcss@8.4.21): - resolution: {integrity: sha512-18f9voByak7bTktR2QgDveglpn9DTbBWPUzSOe9g0N4WR/2eSt6Vrcbf0hmspvMI6YWGywz6B9f7jzpFNJJgnQ==} - peerDependencies: - postcss: ^8.1.4 + ora@5.4.1: dependencies: - postcss: 8.4.21 - dev: false + bl: 4.1.0 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-spinners: 2.9.2 + is-interactive: 1.0.0 + is-unicode-supported: 0.1.0 + log-symbols: 4.1.0 + strip-ansi: 6.0.1 + wcwidth: 1.0.1 - /postcss-focus-visible@6.0.4(postcss@8.4.21): - resolution: {integrity: sha512-QcKuUU/dgNsstIK6HELFRT5Y3lbrMLEOwG+A4s5cA+fx3A3y/JTq3X9LaOj3OC3ALH0XqyrgQIgey/MIZ8Wczw==} - engines: {node: ^12 || ^14 || >=16} - peerDependencies: - postcss: ^8.4 + ora@8.2.0: dependencies: - postcss: 8.4.21 - postcss-selector-parser: 6.0.11 - dev: false + chalk: 5.3.0 + cli-cursor: 5.0.0 + cli-spinners: 2.9.2 + is-interactive: 2.0.0 + is-unicode-supported: 2.1.0 + log-symbols: 6.0.0 + stdin-discarder: 0.2.2 + string-width: 7.2.0 + strip-ansi: 7.1.0 - /postcss-focus-within@5.0.4(postcss@8.4.21): - resolution: {integrity: sha512-vvjDN++C0mu8jz4af5d52CB184ogg/sSxAFS+oUJQq2SuCe7T5U2iIsVJtsCp2d6R4j0jr5+q3rPkBVZkXD9fQ==} - engines: {node: ^12 || ^14 || >=16} - peerDependencies: - postcss: ^8.4 + os-tmpdir@1.0.2: {} + + p-finally@1.0.0: {} + + p-limit@2.3.0: dependencies: - postcss: 8.4.21 - postcss-selector-parser: 6.0.11 - dev: false + p-try: 2.2.0 - /postcss-font-variant@5.0.0(postcss@8.4.21): - resolution: {integrity: sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==} - peerDependencies: - postcss: ^8.1.0 + p-limit@3.1.0: dependencies: - postcss: 8.4.21 - dev: false + yocto-queue: 0.1.0 - /postcss-gap-properties@3.0.5(postcss@8.4.21): - resolution: {integrity: sha512-IuE6gKSdoUNcvkGIqdtjtcMtZIFyXZhmFd5RUlg97iVEvp1BZKV5ngsAjCjrVy+14uhGBQl9tzmi1Qwq4kqVOg==} - engines: {node: ^12 || ^14 || >=16} - peerDependencies: - postcss: ^8.2 + p-limit@4.0.0: dependencies: - postcss: 8.4.21 - dev: false + yocto-queue: 1.1.1 - /postcss-image-set-function@4.0.7(postcss@8.4.21): - resolution: {integrity: sha512-9T2r9rsvYzm5ndsBE8WgtrMlIT7VbtTfE7b3BQnudUqnBcBo7L758oc+o+pdj/dUV0l5wjwSdjeOH2DZtfv8qw==} - engines: {node: ^12 || ^14 || >=16} - peerDependencies: - postcss: ^8.2 + p-locate@3.0.0: dependencies: - postcss: 8.4.21 - postcss-value-parser: 4.2.0 - dev: false + p-limit: 2.3.0 - /postcss-import@14.1.0(postcss@8.4.21): - resolution: {integrity: sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==} - engines: {node: '>=10.0.0'} - peerDependencies: - postcss: ^8.0.0 + p-locate@4.1.0: dependencies: - postcss: 8.4.21 - postcss-value-parser: 4.2.0 - read-cache: 1.0.0 - resolve: 1.22.2 + p-limit: 2.3.0 - /postcss-import@14.1.0(postcss@8.4.22): - resolution: {integrity: sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==} - engines: {node: '>=10.0.0'} - peerDependencies: - postcss: ^8.0.0 + p-locate@5.0.0: dependencies: - postcss: 8.4.22 - postcss-value-parser: 4.2.0 - read-cache: 1.0.0 - resolve: 1.22.2 - dev: true + p-limit: 3.1.0 - /postcss-initial@4.0.1(postcss@8.4.21): - resolution: {integrity: sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==} - peerDependencies: - postcss: ^8.0.0 + p-locate@6.0.0: dependencies: - postcss: 8.4.21 - dev: false + p-limit: 4.0.0 - /postcss-js@4.0.1(postcss@8.4.21): - resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} - engines: {node: ^12 || ^14 || >= 16} - peerDependencies: - postcss: ^8.4.21 + p-map@2.1.0: {} + + p-map@4.0.0: dependencies: - camelcase-css: 2.0.1 - postcss: 8.4.21 + aggregate-error: 3.1.0 - /postcss-js@4.0.1(postcss@8.4.22): - resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} - engines: {node: ^12 || ^14 || >= 16} - peerDependencies: - postcss: ^8.4.21 + p-queue@6.6.2: dependencies: - camelcase-css: 2.0.1 - postcss: 8.4.22 - dev: true + eventemitter3: 4.0.7 + p-timeout: 3.2.0 - /postcss-lab-function@4.2.1(postcss@8.4.21): - resolution: {integrity: sha512-xuXll4isR03CrQsmxyz92LJB2xX9n+pZJ5jE9JgcnmsCammLyKdlzrBin+25dy6wIjfhJpKBAN80gsTlCgRk2w==} - engines: {node: ^12 || ^14 || >=16} - peerDependencies: - postcss: ^8.2 + p-retry@4.6.2: dependencies: - '@csstools/postcss-progressive-custom-properties': 1.3.0(postcss@8.4.21) - postcss: 8.4.21 - postcss-value-parser: 4.2.0 - dev: false + '@types/retry': 0.12.0 + retry: 0.13.1 - /postcss-load-config@2.1.2: - resolution: {integrity: sha512-/rDeGV6vMUo3mwJZmeHfEDvwnTKKqQ0S7OHUi/kJvvtx3aWtyWG2/0ZWnzCt2keEclwN6Tf0DST2v9kITdOKYw==} - engines: {node: '>= 4'} + p-timeout@3.2.0: dependencies: - cosmiconfig: 5.2.1 - import-cwd: 2.1.0 - dev: true + p-finally: 1.0.0 - /postcss-load-config@3.1.4(postcss@8.4.21)(ts-node@10.8.2): - resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==} - engines: {node: '>= 10'} - peerDependencies: - postcss: '>=8.0.9' - ts-node: '>=9.0.0' - peerDependenciesMeta: - postcss: - optional: true - ts-node: - optional: true + p-transform@1.3.0: dependencies: - lilconfig: 2.1.0 - postcss: 8.4.21 - ts-node: 10.8.2(@types/node@18.15.11)(typescript@4.9.5) - yaml: 1.10.2 + debug: 4.3.7(supports-color@5.5.0) + p-queue: 6.6.2 + transitivePeerDependencies: + - supports-color - /postcss-load-config@3.1.4(postcss@8.4.22)(ts-node@10.8.2): - resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==} - engines: {node: '>= 10'} - peerDependencies: - postcss: '>=8.0.9' - ts-node: '>=9.0.0' - peerDependenciesMeta: - postcss: - optional: true - ts-node: - optional: true + p-try@2.2.0: {} + + package-json-from-dist@1.0.1: {} + + pacote@12.0.3: dependencies: - lilconfig: 2.1.0 - postcss: 8.4.22 - ts-node: 10.8.2(@types/node@18.15.11)(typescript@4.9.5) - yaml: 1.10.2 - dev: true + '@npmcli/git': 2.1.0 + '@npmcli/installed-package-contents': 1.0.7 + '@npmcli/promise-spawn': 1.3.2 + '@npmcli/run-script': 2.0.0 + cacache: 15.3.0 + chownr: 2.0.0 + fs-minipass: 2.1.0 + infer-owner: 1.0.4 + minipass: 3.3.6 + mkdirp: 1.0.4 + npm-package-arg: 8.1.5 + npm-packlist: 3.0.0 + npm-pick-manifest: 6.1.1 + npm-registry-fetch: 12.0.2 + promise-retry: 2.0.1 + read-package-json-fast: 2.0.3 + rimraf: 3.0.2 + ssri: 8.0.1 + tar: 6.2.1 + transitivePeerDependencies: + - bluebird + - supports-color - /postcss-load-config@4.0.1(postcss@8.4.22)(ts-node@10.8.2): - resolution: {integrity: sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==} - engines: {node: '>= 14'} - peerDependencies: - postcss: '>=8.0.9' - ts-node: '>=9.0.0' - peerDependenciesMeta: - postcss: - optional: true - ts-node: - optional: true - dependencies: - lilconfig: 2.1.0 - postcss: 8.4.22 - ts-node: 10.8.2(@types/node@18.15.11)(typescript@4.9.5) - yaml: 2.2.1 - dev: true + pacote@15.2.0: + dependencies: + '@npmcli/git': 4.1.0 + '@npmcli/installed-package-contents': 2.1.0 + '@npmcli/promise-spawn': 6.0.2 + '@npmcli/run-script': 6.0.2 + cacache: 17.1.4 + fs-minipass: 3.0.3 + minipass: 5.0.0 + npm-package-arg: 10.1.0 + npm-packlist: 7.0.4 + npm-pick-manifest: 8.0.2 + npm-registry-fetch: 14.0.5 + proc-log: 3.0.0 + promise-retry: 2.0.1 + read-package-json: 6.0.4 + read-package-json-fast: 3.0.2 + sigstore: 1.9.0 + ssri: 10.0.6 + tar: 6.2.1 + transitivePeerDependencies: + - bluebird + - supports-color - /postcss-loader@6.2.1(postcss@8.4.21)(webpack@5.78.0): - resolution: {integrity: sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==} - engines: {node: '>= 12.13.0'} - peerDependencies: - postcss: ^7.0.0 || ^8.0.1 - webpack: ^5.0.0 + param-case@3.0.4: dependencies: - cosmiconfig: 7.1.0 - klona: 2.0.6 - postcss: 8.4.21 - semver: 7.5.0 - webpack: 5.78.0(esbuild@0.16.3)(webpack-cli@5.0.1) - dev: false + dot-case: 3.0.4 + tslib: 2.8.0 - /postcss-logical@5.0.4(postcss@8.4.21): - resolution: {integrity: sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==} - engines: {node: ^12 || ^14 || >=16} - peerDependencies: - postcss: ^8.4 + parent-module@1.0.1: dependencies: - postcss: 8.4.21 - dev: false + callsites: 3.1.0 - /postcss-media-minmax@5.0.0(postcss@8.4.21): - resolution: {integrity: sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==} - engines: {node: '>=10.0.0'} - peerDependencies: - postcss: ^8.1.0 - dependencies: - postcss: 8.4.21 - dev: false + parenthesis@3.1.8: {} - /postcss-merge-longhand@4.0.11: - resolution: {integrity: sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw==} - engines: {node: '>=6.9.0'} + parse-conflict-json@2.0.2: dependencies: - css-color-names: 0.0.4 - postcss: 7.0.39 - postcss-value-parser: 3.3.1 - stylehacks: 4.0.3 - dev: true + json-parse-even-better-errors: 2.3.1 + just-diff: 5.2.0 + just-diff-apply: 5.5.0 - /postcss-merge-longhand@5.1.7(postcss@8.4.21): - resolution: {integrity: sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - dependencies: - postcss: 8.4.21 - postcss-value-parser: 4.2.0 - stylehacks: 5.1.1(postcss@8.4.21) - dev: false + parse-duration@1.1.2: {} - /postcss-merge-rules@4.0.3: - resolution: {integrity: sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ==} - engines: {node: '>=6.9.0'} + parse-json@5.2.0: dependencies: - browserslist: 4.21.5 - caniuse-api: 3.0.0 - cssnano-util-same-parent: 4.0.1 - postcss: 7.0.39 - postcss-selector-parser: 3.1.2 - vendors: 1.0.4 - dev: true + '@babel/code-frame': 7.26.0 + error-ex: 1.3.2 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 - /postcss-merge-rules@5.1.4(postcss@8.4.21): - resolution: {integrity: sha512-0R2IuYpgU93y9lhVbO/OylTtKMVcHb67zjWIfCiKR9rWL3GUk1677LAqD/BcHizukdZEjT8Ru3oHRoAYoJy44g==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - dependencies: - browserslist: 4.21.5 - caniuse-api: 3.0.0 - cssnano-utils: 3.1.0(postcss@8.4.21) - postcss: 8.4.21 - postcss-selector-parser: 6.0.11 - dev: false + parse-node-version@1.0.1: {} - /postcss-minify-font-values@4.0.2: - resolution: {integrity: sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg==} - engines: {node: '>=6.9.0'} + parse5-htmlparser2-tree-adapter@7.1.0: dependencies: - postcss: 7.0.39 - postcss-value-parser: 3.3.1 - dev: true + domhandler: 5.0.3 + parse5: 7.2.1 - /postcss-minify-font-values@5.1.0(postcss@8.4.21): - resolution: {integrity: sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 + parse5-parser-stream@7.1.2: dependencies: - postcss: 8.4.21 - postcss-value-parser: 4.2.0 - dev: false + parse5: 7.2.1 - /postcss-minify-gradients@4.0.2: - resolution: {integrity: sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q==} - engines: {node: '>=6.9.0'} - dependencies: - cssnano-util-get-arguments: 4.0.0 - is-color-stop: 1.1.0 - postcss: 7.0.39 - postcss-value-parser: 3.3.1 - dev: true + parse5@6.0.1: {} - /postcss-minify-gradients@5.1.1(postcss@8.4.21): - resolution: {integrity: sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 + parse5@7.2.1: dependencies: - colord: 2.9.3 - cssnano-utils: 3.1.0(postcss@8.4.21) - postcss: 8.4.21 - postcss-value-parser: 4.2.0 - dev: false + entities: 4.5.0 - /postcss-minify-params@4.0.2: - resolution: {integrity: sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg==} - engines: {node: '>=6.9.0'} + parseley@0.12.1: dependencies: - alphanum-sort: 1.0.2 - browserslist: 4.21.5 - cssnano-util-get-arguments: 4.0.0 - postcss: 7.0.39 - postcss-value-parser: 3.3.1 - uniqs: 2.0.0 - dev: true + leac: 0.6.0 + peberminta: 0.9.0 - /postcss-minify-params@5.1.4(postcss@8.4.21): - resolution: {integrity: sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - dependencies: - browserslist: 4.21.5 - cssnano-utils: 3.1.0(postcss@8.4.21) - postcss: 8.4.21 - postcss-value-parser: 4.2.0 - dev: false + parseurl@1.3.3: {} - /postcss-minify-selectors@4.0.2: - resolution: {integrity: sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g==} - engines: {node: '>=6.9.0'} + pascal-case@3.1.2: dependencies: - alphanum-sort: 1.0.2 - has: 1.0.3 - postcss: 7.0.39 - postcss-selector-parser: 3.1.2 - dev: true + no-case: 3.0.4 + tslib: 2.8.0 - /postcss-minify-selectors@5.2.1(postcss@8.4.21): - resolution: {integrity: sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - dependencies: - postcss: 8.4.21 - postcss-selector-parser: 6.0.11 - dev: false + path-browserify@1.0.1: {} - /postcss-modules-extract-imports@1.1.0: - resolution: {integrity: sha512-zF9+UIEvtpeqMGxhpeT9XaIevQSrBBCz9fi7SwfkmjVacsSj8DY5eFVgn+wY8I9vvdDDwK5xC8Myq4UkoLFIkA==} - dependencies: - postcss: 6.0.1 - dev: true + path-exists@3.0.0: {} - /postcss-modules-extract-imports@3.0.0(postcss@8.4.21): - resolution: {integrity: sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==} - engines: {node: ^10 || ^12 || >= 14} - peerDependencies: - postcss: ^8.1.0 - dependencies: - postcss: 8.4.21 - dev: false + path-exists@4.0.0: {} - /postcss-modules-extract-imports@3.0.0(postcss@8.4.22): - resolution: {integrity: sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==} - engines: {node: ^10 || ^12 || >= 14} - peerDependencies: - postcss: ^8.1.0 - dependencies: - postcss: 8.4.22 - dev: true + path-exists@5.0.0: {} - /postcss-modules-local-by-default@1.2.0: - resolution: {integrity: sha512-X4cquUPIaAd86raVrBwO8fwRfkIdbwFu7CTfEOjiZQHVQwlHRSkTgH5NLDmMm5+1hQO8u6dZ+TOOJDbay1hYpA==} - dependencies: - css-selector-tokenizer: 0.7.3 - postcss: 6.0.1 - dev: true + path-is-absolute@1.0.1: {} - /postcss-modules-local-by-default@4.0.0(postcss@8.4.21): - resolution: {integrity: sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==} - engines: {node: ^10 || ^12 || >= 14} - peerDependencies: - postcss: ^8.1.0 + path-is-inside@1.0.2: {} + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + path-scurry@1.11.1: dependencies: - icss-utils: 5.1.0(postcss@8.4.21) - postcss: 8.4.21 - postcss-selector-parser: 6.0.11 - postcss-value-parser: 4.2.0 - dev: false + lru-cache: 10.4.3 + minipass: 7.1.2 - /postcss-modules-local-by-default@4.0.0(postcss@8.4.22): - resolution: {integrity: sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==} - engines: {node: ^10 || ^12 || >= 14} - peerDependencies: - postcss: ^8.1.0 + path-scurry@2.0.0: dependencies: - icss-utils: 5.1.0(postcss@8.4.22) - postcss: 8.4.22 - postcss-selector-parser: 6.0.11 - postcss-value-parser: 4.2.0 - dev: true + lru-cache: 11.1.0 + minipass: 7.1.2 + + path-to-regexp@0.1.12: {} + + path-type@4.0.0: {} + + path-type@5.0.0: {} + + pathe@2.0.3: {} - /postcss-modules-scope@1.1.0: - resolution: {integrity: sha512-LTYwnA4C1He1BKZXIx1CYiHixdSe9LWYVKadq9lK5aCCMkoOkFyZ7aigt+srfjlRplJY3gIol6KUNefdMQJdlw==} + peberminta@0.9.0: {} + + performance-now@2.1.0: {} + + pg-cloudflare@1.1.1: + optional: true + + pg-connection-string@2.7.0: {} + + pg-cursor@2.10.6(pg@8.11.6): dependencies: - css-selector-tokenizer: 0.7.3 - postcss: 6.0.1 - dev: true + pg: 8.11.6 - /postcss-modules-scope@3.0.0(postcss@8.4.21): - resolution: {integrity: sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==} - engines: {node: ^10 || ^12 || >= 14} - peerDependencies: - postcss: ^8.1.0 + pg-cursor@2.12.1(pg@8.11.6): dependencies: - postcss: 8.4.21 - postcss-selector-parser: 6.0.11 - dev: false + pg: 8.11.6 - /postcss-modules-scope@3.0.0(postcss@8.4.22): - resolution: {integrity: sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==} - engines: {node: ^10 || ^12 || >= 14} - peerDependencies: - postcss: ^8.1.0 + pg-int8@1.0.1: {} + + pg-minify@1.6.5: {} + + pg-numeric@1.0.2: {} + + pg-pool@3.7.0(pg@8.11.6): dependencies: - postcss: 8.4.22 - postcss-selector-parser: 6.0.11 - dev: true + pg: 8.11.6 - /postcss-modules-values@1.3.0: - resolution: {integrity: sha512-i7IFaR9hlQ6/0UgFuqM6YWaCfA1Ej8WMg8A5DggnH1UGKJvTV/ugqq/KaULixzzOi3T/tF6ClBXcHGCzdd5unA==} + pg-pool@3.7.0(pg@8.13.0): dependencies: - icss-replace-symbols: 1.1.0 - postcss: 6.0.1 - dev: true + pg: 8.13.0 - /postcss-modules-values@4.0.0(postcss@8.4.21): - resolution: {integrity: sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==} - engines: {node: ^10 || ^12 || >= 14} - peerDependencies: - postcss: ^8.1.0 + pg-promise@11.10.1(pg-query-stream@4.7.1(pg@8.11.6)): dependencies: - icss-utils: 5.1.0(postcss@8.4.21) - postcss: 8.4.21 - dev: false + assert-options: 0.8.2 + pg: 8.13.0 + pg-minify: 1.6.5 + pg-query-stream: 4.7.1(pg@8.11.6) + spex: 3.4.0 + transitivePeerDependencies: + - pg-native - /postcss-modules-values@4.0.0(postcss@8.4.22): - resolution: {integrity: sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==} - engines: {node: ^10 || ^12 || >= 14} - peerDependencies: - postcss: ^8.1.0 + pg-protocol@1.7.0: {} + + pg-query-stream@4.7.1(pg@8.11.6): dependencies: - icss-utils: 5.1.0(postcss@8.4.22) - postcss: 8.4.22 - dev: true + pg: 8.11.6 + pg-cursor: 2.12.1(pg@8.11.6) - /postcss-modules@2.0.0: - resolution: {integrity: sha512-eqp+Bva+U2cwQO7dECJ8/V+X+uH1HduNeITB0CPPFAu6d/8LKQ32/j+p9rQ2YL1QytVcrNU0X+fBqgGmQIA1Rw==} + pg-types@2.2.0: dependencies: - css-modules-loader-core: 1.1.0 - generic-names: 2.0.1 - lodash.camelcase: 4.3.0 - postcss: 7.0.39 - string-hash: 1.1.3 - dev: true + pg-int8: 1.0.1 + postgres-array: 2.0.0 + postgres-bytea: 1.0.0 + postgres-date: 1.0.7 + postgres-interval: 1.2.0 - /postcss-modules@6.0.0(postcss@8.4.22): - resolution: {integrity: sha512-7DGfnlyi/ju82BRzTIjWS5C4Tafmzl3R79YP/PASiocj+aa6yYphHhhKUOEoXQToId5rgyFgJ88+ccOUydjBXQ==} - peerDependencies: - postcss: ^8.0.0 + pg-types@4.0.2: dependencies: - generic-names: 4.0.0 - icss-utils: 5.1.0(postcss@8.4.22) - lodash.camelcase: 4.3.0 - postcss: 8.4.22 - postcss-modules-extract-imports: 3.0.0(postcss@8.4.22) - postcss-modules-local-by-default: 4.0.0(postcss@8.4.22) - postcss-modules-scope: 3.0.0(postcss@8.4.22) - postcss-modules-values: 4.0.0(postcss@8.4.22) - string-hash: 1.1.3 - dev: true + pg-int8: 1.0.1 + pg-numeric: 1.0.2 + postgres-array: 3.0.2 + postgres-bytea: 3.0.0 + postgres-date: 2.1.0 + postgres-interval: 3.0.0 + postgres-range: 1.1.4 + + pg@8.11.6: + dependencies: + pg-connection-string: 2.7.0 + pg-pool: 3.7.0(pg@8.11.6) + pg-protocol: 1.7.0 + pg-types: 2.2.0 + pgpass: 1.0.5 + optionalDependencies: + pg-cloudflare: 1.1.1 - /postcss-nested@6.0.0(postcss@8.4.21): - resolution: {integrity: sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w==} - engines: {node: '>=12.0'} - peerDependencies: - postcss: ^8.2.14 + pg@8.13.0: dependencies: - postcss: 8.4.21 - postcss-selector-parser: 6.0.11 + pg-connection-string: 2.7.0 + pg-pool: 3.7.0(pg@8.13.0) + pg-protocol: 1.7.0 + pg-types: 2.2.0 + pgpass: 1.0.5 + optionalDependencies: + pg-cloudflare: 1.1.1 - /postcss-nested@6.0.0(postcss@8.4.22): - resolution: {integrity: sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w==} - engines: {node: '>=12.0'} - peerDependencies: - postcss: ^8.2.14 + pgpass@1.0.5: dependencies: - postcss: 8.4.22 - postcss-selector-parser: 6.0.11 - dev: true + split2: 4.2.0 - /postcss-nesting@10.2.0(postcss@8.4.21): - resolution: {integrity: sha512-EwMkYchxiDiKUhlJGzWsD9b2zvq/r2SSubcRrgP+jujMXFzqvANLt16lJANC+5uZ6hjI7lpRmI6O8JIl+8l1KA==} - engines: {node: ^12 || ^14 || >=16} - peerDependencies: - postcss: ^8.2 + picocolors@1.1.1: {} + + picomatch@2.3.1: {} + + picomatch@4.0.2: {} + + pify@2.3.0: {} + + pify@4.0.1: {} + + pify@5.0.0: {} + + pinkie-promise@2.0.1: dependencies: - '@csstools/selector-specificity': 2.2.0(postcss-selector-parser@6.0.11) - postcss: 8.4.21 - postcss-selector-parser: 6.0.11 - dev: false + pinkie: 2.0.4 - /postcss-normalize-charset@4.0.1: - resolution: {integrity: sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g==} - engines: {node: '>=6.9.0'} + pinkie@2.0.4: {} + + pirates@4.0.6: {} + + pkg-dir@4.2.0: dependencies: - postcss: 7.0.39 - dev: true + find-up: 4.1.0 - /postcss-normalize-charset@5.1.0(postcss@8.4.21): - resolution: {integrity: sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 + pkg-dir@7.0.0: dependencies: - postcss: 8.4.21 - dev: false + find-up: 6.3.0 - /postcss-normalize-display-values@4.0.2: - resolution: {integrity: sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ==} - engines: {node: '>=6.9.0'} + pkg-types@2.2.0: dependencies: - cssnano-util-get-match: 4.0.0 - postcss: 7.0.39 - postcss-value-parser: 3.3.1 - dev: true + confbox: 0.2.2 + exsolve: 1.0.7 + pathe: 2.0.3 - /postcss-normalize-display-values@5.1.0(postcss@8.4.21): - resolution: {integrity: sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 + pkg-up@3.1.0: dependencies: - postcss: 8.4.21 - postcss-value-parser: 4.2.0 - dev: false + find-up: 3.0.0 - /postcss-normalize-positions@4.0.2: - resolution: {integrity: sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA==} - engines: {node: '>=6.9.0'} + playwright-core@1.39.0: {} + + playwright@1.39.0: dependencies: - cssnano-util-get-arguments: 4.0.0 - has: 1.0.3 - postcss: 7.0.39 - postcss-value-parser: 3.3.1 - dev: true + playwright-core: 1.39.0 + optionalDependencies: + fsevents: 2.3.2 - /postcss-normalize-positions@5.1.1(postcss@8.4.21): - resolution: {integrity: sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 + possible-typed-array-names@1.0.0: {} + + postcss-attribute-case-insensitive@5.0.2(postcss@8.4.47): dependencies: - postcss: 8.4.21 - postcss-value-parser: 4.2.0 - dev: false + postcss: 8.4.47 + postcss-selector-parser: 6.1.2 - /postcss-normalize-repeat-style@4.0.2: - resolution: {integrity: sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q==} - engines: {node: '>=6.9.0'} + postcss-browser-comments@4.0.0(browserslist@4.24.2)(postcss@8.4.47): dependencies: - cssnano-util-get-arguments: 4.0.0 - cssnano-util-get-match: 4.0.0 - postcss: 7.0.39 - postcss-value-parser: 3.3.1 - dev: true + browserslist: 4.24.2 + postcss: 8.4.47 - /postcss-normalize-repeat-style@5.1.1(postcss@8.4.21): - resolution: {integrity: sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 + postcss-calc@10.0.2(postcss@8.4.47): dependencies: - postcss: 8.4.21 + postcss: 8.4.47 + postcss-selector-parser: 6.1.2 postcss-value-parser: 4.2.0 - dev: false - /postcss-normalize-string@4.0.2: - resolution: {integrity: sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA==} - engines: {node: '>=6.9.0'} + postcss-calc@8.2.4(postcss@8.4.47): dependencies: - has: 1.0.3 - postcss: 7.0.39 - postcss-value-parser: 3.3.1 - dev: true + postcss: 8.4.47 + postcss-selector-parser: 6.1.2 + postcss-value-parser: 4.2.0 - /postcss-normalize-string@5.1.0(postcss@8.4.21): - resolution: {integrity: sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 + postcss-clamp@4.1.0(postcss@8.4.47): dependencies: - postcss: 8.4.21 + postcss: 8.4.47 postcss-value-parser: 4.2.0 - dev: false - /postcss-normalize-timing-functions@4.0.2: - resolution: {integrity: sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A==} - engines: {node: '>=6.9.0'} + postcss-color-functional-notation@4.2.4(postcss@8.4.47): dependencies: - cssnano-util-get-match: 4.0.0 - postcss: 7.0.39 - postcss-value-parser: 3.3.1 - dev: true + postcss: 8.4.47 + postcss-value-parser: 4.2.0 - /postcss-normalize-timing-functions@5.1.0(postcss@8.4.21): - resolution: {integrity: sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 + postcss-color-hex-alpha@8.0.4(postcss@8.4.47): dependencies: - postcss: 8.4.21 + postcss: 8.4.47 postcss-value-parser: 4.2.0 - dev: false - /postcss-normalize-unicode@4.0.1: - resolution: {integrity: sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg==} - engines: {node: '>=6.9.0'} + postcss-color-rebeccapurple@7.1.1(postcss@8.4.47): dependencies: - browserslist: 4.21.5 - postcss: 7.0.39 - postcss-value-parser: 3.3.1 - dev: true + postcss: 8.4.47 + postcss-value-parser: 4.2.0 - /postcss-normalize-unicode@5.1.1(postcss@8.4.21): - resolution: {integrity: sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 + postcss-colormin@5.3.1(postcss@8.4.47): dependencies: - browserslist: 4.21.5 - postcss: 8.4.21 + browserslist: 4.24.2 + caniuse-api: 3.0.0 + colord: 2.9.3 + postcss: 8.4.47 postcss-value-parser: 4.2.0 - dev: false - /postcss-normalize-url@4.0.1: - resolution: {integrity: sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA==} - engines: {node: '>=6.9.0'} + postcss-colormin@7.0.2(postcss@8.4.47): dependencies: - is-absolute-url: 2.1.0 - normalize-url: 3.3.0 - postcss: 7.0.39 - postcss-value-parser: 3.3.1 - dev: true + browserslist: 4.24.2 + caniuse-api: 3.0.0 + colord: 2.9.3 + postcss: 8.4.47 + postcss-value-parser: 4.2.0 - /postcss-normalize-url@5.1.0(postcss@8.4.21): - resolution: {integrity: sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 + postcss-convert-values@5.1.3(postcss@8.4.47): dependencies: - normalize-url: 6.1.0 - postcss: 8.4.21 + browserslist: 4.24.2 + postcss: 8.4.47 postcss-value-parser: 4.2.0 - dev: false - /postcss-normalize-whitespace@4.0.2: - resolution: {integrity: sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA==} - engines: {node: '>=6.9.0'} + postcss-convert-values@7.0.4(postcss@8.4.47): dependencies: - postcss: 7.0.39 - postcss-value-parser: 3.3.1 - dev: true + browserslist: 4.24.2 + postcss: 8.4.47 + postcss-value-parser: 4.2.0 - /postcss-normalize-whitespace@5.1.1(postcss@8.4.21): - resolution: {integrity: sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 + postcss-custom-media@8.0.2(postcss@8.4.47): dependencies: - postcss: 8.4.21 + postcss: 8.4.47 postcss-value-parser: 4.2.0 - dev: false - /postcss-normalize@10.0.1(browserslist@4.21.5)(postcss@8.4.21): - resolution: {integrity: sha512-+5w18/rDev5mqERcG3W5GZNMJa1eoYYNGo8gB7tEwaos0ajk3ZXAI4mHGcNT47NE+ZnZD1pEpUOFLvltIwmeJA==} - engines: {node: '>= 12'} - peerDependencies: - browserslist: '>= 4' - postcss: '>= 8' + postcss-custom-properties@12.1.11(postcss@8.4.47): dependencies: - '@csstools/normalize.css': 12.0.0 - browserslist: 4.21.5 - postcss: 8.4.21 - postcss-browser-comments: 4.0.0(browserslist@4.21.5)(postcss@8.4.21) - sanitize.css: 13.0.0 - dev: false + postcss: 8.4.47 + postcss-value-parser: 4.2.0 - /postcss-opacity-percentage@1.1.3(postcss@8.4.21): - resolution: {integrity: sha512-An6Ba4pHBiDtyVpSLymUUERMo2cU7s+Obz6BTrS+gxkbnSBNKSuD0AVUc+CpBMrpVPKKfoVz0WQCX+Tnst0i4A==} - engines: {node: ^12 || ^14 || >=16} - peerDependencies: - postcss: ^8.2 + postcss-custom-selectors@6.0.3(postcss@8.4.47): dependencies: - postcss: 8.4.21 - dev: false + postcss: 8.4.47 + postcss-selector-parser: 6.1.2 - /postcss-ordered-values@4.1.2: - resolution: {integrity: sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw==} - engines: {node: '>=6.9.0'} + postcss-dir-pseudo-class@6.0.5(postcss@8.4.47): dependencies: - cssnano-util-get-arguments: 4.0.0 - postcss: 7.0.39 - postcss-value-parser: 3.3.1 - dev: true + postcss: 8.4.47 + postcss-selector-parser: 6.1.2 - /postcss-ordered-values@5.1.3(postcss@8.4.21): - resolution: {integrity: sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 + postcss-discard-comments@5.1.2(postcss@8.4.47): dependencies: - cssnano-utils: 3.1.0(postcss@8.4.21) - postcss: 8.4.21 - postcss-value-parser: 4.2.0 - dev: false + postcss: 8.4.47 - /postcss-overflow-shorthand@3.0.4(postcss@8.4.21): - resolution: {integrity: sha512-otYl/ylHK8Y9bcBnPLo3foYFLL6a6Ak+3EQBPOTR7luMYCOsiVTUk1iLvNf6tVPNGXcoL9Hoz37kpfriRIFb4A==} - engines: {node: ^12 || ^14 || >=16} - peerDependencies: - postcss: ^8.2 + postcss-discard-comments@7.0.3(postcss@8.4.47): dependencies: - postcss: 8.4.21 - postcss-value-parser: 4.2.0 - dev: false + postcss: 8.4.47 + postcss-selector-parser: 6.1.2 - /postcss-page-break@3.0.4(postcss@8.4.21): - resolution: {integrity: sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==} - peerDependencies: - postcss: ^8 + postcss-discard-duplicates@5.1.0(postcss@8.4.47): dependencies: - postcss: 8.4.21 - dev: false + postcss: 8.4.47 - /postcss-place@7.0.5(postcss@8.4.21): - resolution: {integrity: sha512-wR8igaZROA6Z4pv0d+bvVrvGY4GVHihBCBQieXFY3kuSuMyOmEnnfFzHl/tQuqHZkfkIVBEbDvYcFfHmpSet9g==} - engines: {node: ^12 || ^14 || >=16} - peerDependencies: - postcss: ^8.2 + postcss-discard-duplicates@7.0.1(postcss@8.4.47): dependencies: - postcss: 8.4.21 - postcss-value-parser: 4.2.0 - dev: false - - /postcss-preset-env@7.8.3(postcss@8.4.21): - resolution: {integrity: sha512-T1LgRm5uEVFSEF83vHZJV2z19lHg4yJuZ6gXZZkqVsqv63nlr6zabMH3l4Pc01FQCyfWVrh2GaUeCVy9Po+Aag==} - engines: {node: ^12 || ^14 || >=16} - peerDependencies: - postcss: ^8.2 - dependencies: - '@csstools/postcss-cascade-layers': 1.1.1(postcss@8.4.21) - '@csstools/postcss-color-function': 1.1.1(postcss@8.4.21) - '@csstools/postcss-font-format-keywords': 1.0.1(postcss@8.4.21) - '@csstools/postcss-hwb-function': 1.0.2(postcss@8.4.21) - '@csstools/postcss-ic-unit': 1.0.1(postcss@8.4.21) - '@csstools/postcss-is-pseudo-class': 2.0.7(postcss@8.4.21) - '@csstools/postcss-nested-calc': 1.0.0(postcss@8.4.21) - '@csstools/postcss-normalize-display-values': 1.0.1(postcss@8.4.21) - '@csstools/postcss-oklab-function': 1.1.1(postcss@8.4.21) - '@csstools/postcss-progressive-custom-properties': 1.3.0(postcss@8.4.21) - '@csstools/postcss-stepped-value-functions': 1.0.1(postcss@8.4.21) - '@csstools/postcss-text-decoration-shorthand': 1.0.0(postcss@8.4.21) - '@csstools/postcss-trigonometric-functions': 1.0.2(postcss@8.4.21) - '@csstools/postcss-unset-value': 1.0.2(postcss@8.4.21) - autoprefixer: 10.4.14(postcss@8.4.21) - browserslist: 4.21.5 - css-blank-pseudo: 3.0.3(postcss@8.4.21) - css-has-pseudo: 3.0.4(postcss@8.4.21) - css-prefers-color-scheme: 6.0.3(postcss@8.4.21) - cssdb: 7.5.4 - postcss: 8.4.21 - postcss-attribute-case-insensitive: 5.0.2(postcss@8.4.21) - postcss-clamp: 4.1.0(postcss@8.4.21) - postcss-color-functional-notation: 4.2.4(postcss@8.4.21) - postcss-color-hex-alpha: 8.0.4(postcss@8.4.21) - postcss-color-rebeccapurple: 7.1.1(postcss@8.4.21) - postcss-custom-media: 8.0.2(postcss@8.4.21) - postcss-custom-properties: 12.1.11(postcss@8.4.21) - postcss-custom-selectors: 6.0.3(postcss@8.4.21) - postcss-dir-pseudo-class: 6.0.5(postcss@8.4.21) - postcss-double-position-gradients: 3.1.2(postcss@8.4.21) - postcss-env-function: 4.0.6(postcss@8.4.21) - postcss-focus-visible: 6.0.4(postcss@8.4.21) - postcss-focus-within: 5.0.4(postcss@8.4.21) - postcss-font-variant: 5.0.0(postcss@8.4.21) - postcss-gap-properties: 3.0.5(postcss@8.4.21) - postcss-image-set-function: 4.0.7(postcss@8.4.21) - postcss-initial: 4.0.1(postcss@8.4.21) - postcss-lab-function: 4.2.1(postcss@8.4.21) - postcss-logical: 5.0.4(postcss@8.4.21) - postcss-media-minmax: 5.0.0(postcss@8.4.21) - postcss-nesting: 10.2.0(postcss@8.4.21) - postcss-opacity-percentage: 1.1.3(postcss@8.4.21) - postcss-overflow-shorthand: 3.0.4(postcss@8.4.21) - postcss-page-break: 3.0.4(postcss@8.4.21) - postcss-place: 7.0.5(postcss@8.4.21) - postcss-pseudo-class-any-link: 7.1.6(postcss@8.4.21) - postcss-replace-overflow-wrap: 4.0.0(postcss@8.4.21) - postcss-selector-not: 6.0.1(postcss@8.4.21) - postcss-value-parser: 4.2.0 - dev: false + postcss: 8.4.47 - /postcss-pseudo-class-any-link@7.1.6(postcss@8.4.21): - resolution: {integrity: sha512-9sCtZkO6f/5ML9WcTLcIyV1yz9D1rf0tWc+ulKcvV30s0iZKS/ONyETvoWsr6vnrmW+X+KmuK3gV/w5EWnT37w==} - engines: {node: ^12 || ^14 || >=16} - peerDependencies: - postcss: ^8.2 + postcss-discard-empty@5.1.1(postcss@8.4.47): dependencies: - postcss: 8.4.21 - postcss-selector-parser: 6.0.11 - dev: false + postcss: 8.4.47 - /postcss-reduce-initial@4.0.3: - resolution: {integrity: sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA==} - engines: {node: '>=6.9.0'} + postcss-discard-empty@7.0.0(postcss@8.4.47): dependencies: - browserslist: 4.21.5 - caniuse-api: 3.0.0 - has: 1.0.3 - postcss: 7.0.39 - dev: true + postcss: 8.4.47 - /postcss-reduce-initial@5.1.2(postcss@8.4.21): - resolution: {integrity: sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 + postcss-discard-overridden@5.1.0(postcss@8.4.47): dependencies: - browserslist: 4.21.5 - caniuse-api: 3.0.0 - postcss: 8.4.21 - dev: false + postcss: 8.4.47 - /postcss-reduce-transforms@4.0.2: - resolution: {integrity: sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg==} - engines: {node: '>=6.9.0'} + postcss-discard-overridden@7.0.0(postcss@8.4.47): dependencies: - cssnano-util-get-match: 4.0.0 - has: 1.0.3 - postcss: 7.0.39 - postcss-value-parser: 3.3.1 - dev: true + postcss: 8.4.47 - /postcss-reduce-transforms@5.1.0(postcss@8.4.21): - resolution: {integrity: sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 + postcss-double-position-gradients@3.1.2(postcss@8.4.47): dependencies: - postcss: 8.4.21 + '@csstools/postcss-progressive-custom-properties': 1.3.0(postcss@8.4.47) + postcss: 8.4.47 postcss-value-parser: 4.2.0 - dev: false - /postcss-replace-overflow-wrap@4.0.0(postcss@8.4.21): - resolution: {integrity: sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==} - peerDependencies: - postcss: ^8.0.3 + postcss-env-function@4.0.6(postcss@8.4.47): dependencies: - postcss: 8.4.21 - dev: false + postcss: 8.4.47 + postcss-value-parser: 4.2.0 - /postcss-selector-not@6.0.1(postcss@8.4.21): - resolution: {integrity: sha512-1i9affjAe9xu/y9uqWH+tD4r6/hDaXJruk8xn2x1vzxC2U3J3LKO3zJW4CyxlNhA56pADJ/djpEwpH1RClI2rQ==} - engines: {node: ^12 || ^14 || >=16} - peerDependencies: - postcss: ^8.2 + postcss-flexbugs-fixes@5.0.2(postcss@8.4.47): + dependencies: + postcss: 8.4.47 + + postcss-focus-visible@6.0.4(postcss@8.4.47): dependencies: - postcss: 8.4.21 - postcss-selector-parser: 6.0.11 - dev: false + postcss: 8.4.47 + postcss-selector-parser: 6.1.2 - /postcss-selector-parser@3.1.2: - resolution: {integrity: sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==} - engines: {node: '>=8'} + postcss-focus-within@5.0.4(postcss@8.4.47): dependencies: - dot-prop: 5.3.0 - indexes-of: 1.0.1 - uniq: 1.0.1 - dev: true + postcss: 8.4.47 + postcss-selector-parser: 6.1.2 - /postcss-selector-parser@6.0.10: - resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==} - engines: {node: '>=4'} + postcss-font-variant@5.0.0(postcss@8.4.47): dependencies: - cssesc: 3.0.0 - util-deprecate: 1.0.2 - dev: true + postcss: 8.4.47 - /postcss-selector-parser@6.0.11: - resolution: {integrity: sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==} - engines: {node: '>=4'} + postcss-gap-properties@3.0.5(postcss@8.4.47): dependencies: - cssesc: 3.0.0 - util-deprecate: 1.0.2 + postcss: 8.4.47 - /postcss-svgo@4.0.3: - resolution: {integrity: sha512-NoRbrcMWTtUghzuKSoIm6XV+sJdvZ7GZSc3wdBN0W19FTtp2ko8NqLsgoh/m9CzNhU3KLPvQmjIwtaNFkaFTvw==} - engines: {node: '>=6.9.0'} + postcss-image-set-function@4.0.7(postcss@8.4.47): dependencies: - postcss: 7.0.39 - postcss-value-parser: 3.3.1 - svgo: 1.3.2 - dev: true + postcss: 8.4.47 + postcss-value-parser: 4.2.0 - /postcss-svgo@5.1.0(postcss@8.4.21): - resolution: {integrity: sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 + postcss-import@15.1.0(postcss@8.4.47): dependencies: - postcss: 8.4.21 + postcss: 8.4.47 postcss-value-parser: 4.2.0 - svgo: 2.8.0 - dev: false + read-cache: 1.0.0 + resolve: 1.22.8 - /postcss-unique-selectors@4.0.1: - resolution: {integrity: sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg==} - engines: {node: '>=6.9.0'} + postcss-initial@4.0.1(postcss@8.4.47): dependencies: - alphanum-sort: 1.0.2 - postcss: 7.0.39 - uniqs: 2.0.0 - dev: true + postcss: 8.4.47 - /postcss-unique-selectors@5.1.1(postcss@8.4.21): - resolution: {integrity: sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 + postcss-js@4.0.1(postcss@8.4.47): dependencies: - postcss: 8.4.21 - postcss-selector-parser: 6.0.11 - dev: false + camelcase-css: 2.0.1 + postcss: 8.4.47 - /postcss-value-parser@3.3.1: - resolution: {integrity: sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==} - dev: true + postcss-lab-function@4.2.1(postcss@8.4.47): + dependencies: + '@csstools/postcss-progressive-custom-properties': 1.3.0(postcss@8.4.47) + postcss: 8.4.47 + postcss-value-parser: 4.2.0 - /postcss-value-parser@4.2.0: - resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + postcss-load-config@3.1.4(postcss@8.4.47)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)): + dependencies: + lilconfig: 2.1.0 + yaml: 1.10.2 + optionalDependencies: + postcss: 8.4.47 + ts-node: 10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3) - /postcss@6.0.1: - resolution: {integrity: sha512-VbGX1LQgQbf9l3cZ3qbUuC3hGqIEOGQFHAEHQ/Diaeo0yLgpgK5Rb8J+OcamIfQ9PbAU/fzBjVtQX3AhJHUvZw==} - engines: {node: '>=4.0.0'} + postcss-load-config@4.0.2(postcss@8.4.47)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)): dependencies: - chalk: 1.1.3 - source-map: 0.5.7 - supports-color: 3.2.3 - dev: true + lilconfig: 3.1.2 + yaml: 2.6.0 + optionalDependencies: + postcss: 8.4.47 + ts-node: 10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3) - /postcss@7.0.39: - resolution: {integrity: sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==} - engines: {node: '>=6.0.0'} + postcss-loader@6.2.1(postcss@8.4.47)(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))): dependencies: - picocolors: 0.2.1 - source-map: 0.6.1 + cosmiconfig: 7.1.0 + klona: 2.0.6 + postcss: 8.4.47 + semver: 7.6.3 + webpack: 5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(esbuild@0.25.6) - /postcss@8.4.14: - resolution: {integrity: sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==} - engines: {node: ^10 || ^12 || >=14} + postcss-logical@5.0.4(postcss@8.4.47): dependencies: - nanoid: 3.3.6 - picocolors: 1.0.0 - source-map-js: 1.0.2 - dev: false + postcss: 8.4.47 - /postcss@8.4.21: - resolution: {integrity: sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==} - engines: {node: ^10 || ^12 || >=14} + postcss-media-minmax@5.0.0(postcss@8.4.47): dependencies: - nanoid: 3.3.6 - picocolors: 1.0.0 - source-map-js: 1.0.2 + postcss: 8.4.47 - /postcss@8.4.22: - resolution: {integrity: sha512-XseknLAfRHzVWjCEtdviapiBtfLdgyzExD50Rg2ePaucEesyh8Wv4VPdW0nbyDa1ydbrAxV19jvMT4+LFmcNUA==} - engines: {node: ^10 || ^12 || >=14} + postcss-merge-longhand@5.1.7(postcss@8.4.47): dependencies: - nanoid: 3.3.6 - picocolors: 1.0.0 - source-map-js: 1.0.2 - dev: true + postcss: 8.4.47 + postcss-value-parser: 4.2.0 + stylehacks: 5.1.1(postcss@8.4.47) - /postgres-array@2.0.0: - resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} - engines: {node: '>=4'} + postcss-merge-longhand@7.0.4(postcss@8.4.47): + dependencies: + postcss: 8.4.47 + postcss-value-parser: 4.2.0 + stylehacks: 7.0.4(postcss@8.4.47) - /postgres-bytea@1.0.0: - resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==} - engines: {node: '>=0.10.0'} + postcss-merge-rules@5.1.4(postcss@8.4.47): + dependencies: + browserslist: 4.24.2 + caniuse-api: 3.0.0 + cssnano-utils: 3.1.0(postcss@8.4.47) + postcss: 8.4.47 + postcss-selector-parser: 6.1.2 - /postgres-date@1.0.7: - resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==} - engines: {node: '>=0.10.0'} + postcss-merge-rules@7.0.4(postcss@8.4.47): + dependencies: + browserslist: 4.24.2 + caniuse-api: 3.0.0 + cssnano-utils: 5.0.0(postcss@8.4.47) + postcss: 8.4.47 + postcss-selector-parser: 6.1.2 - /postgres-interval@1.2.0: - resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} - engines: {node: '>=0.10.0'} + postcss-minify-font-values@5.1.0(postcss@8.4.47): dependencies: - xtend: 4.0.2 + postcss: 8.4.47 + postcss-value-parser: 4.2.0 - /posthog-node@2.6.0: - resolution: {integrity: sha512-/BiFw/jwdP0uJSRAIoYqLoBTjZ612xv74b1L/a3T/p1nJVL8e0OrHuxbJW56c6WVW/IKm9gBF/zhbqfaz0XgJQ==} - engines: {node: '>=15.0.0'} + postcss-minify-font-values@7.0.0(postcss@8.4.47): dependencies: - axios: 0.27.2 - transitivePeerDependencies: - - debug - dev: false + postcss: 8.4.47 + postcss-value-parser: 4.2.0 - /preact-render-to-string@5.2.6(preact@10.13.2): - resolution: {integrity: sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==} - peerDependencies: - preact: '>=10' + postcss-minify-gradients@5.1.1(postcss@8.4.47): dependencies: - preact: 10.13.2 - pretty-format: 3.8.0 - dev: false + colord: 2.9.3 + cssnano-utils: 3.1.0(postcss@8.4.47) + postcss: 8.4.47 + postcss-value-parser: 4.2.0 - /preact@10.13.2: - resolution: {integrity: sha512-q44QFLhOhty2Bd0Y46fnYW0gD/cbVM9dUVtNTDKPcdXSMA7jfY+Jpd6rk3GB0lcQss0z5s/6CmVP0Z/hV+g6pw==} - dev: false + postcss-minify-gradients@7.0.0(postcss@8.4.47): + dependencies: + colord: 2.9.3 + cssnano-utils: 5.0.0(postcss@8.4.47) + postcss: 8.4.47 + postcss-value-parser: 4.2.0 - /prebuild-install@5.3.6: - resolution: {integrity: sha512-s8Aai8++QQGi4sSbs/M1Qku62PFK49Jm1CbgXklGz4nmHveDq0wzJkg7Na5QbnO1uNH8K7iqx2EQ/mV0MZEmOg==} - engines: {node: '>=6'} - hasBin: true + postcss-minify-params@5.1.4(postcss@8.4.47): dependencies: - detect-libc: 1.0.3 - expand-template: 2.0.3 - github-from-package: 0.0.0 - minimist: 1.2.8 - mkdirp-classic: 0.5.3 - napi-build-utils: 1.0.2 - node-abi: 2.30.1 - noop-logger: 0.1.1 - npmlog: 4.1.2 - pump: 3.0.0 - rc: 1.2.8 - simple-get: 3.1.1 - tar-fs: 2.1.1 - tunnel-agent: 0.6.0 - which-pm-runs: 1.1.0 - dev: false + browserslist: 4.24.2 + cssnano-utils: 3.1.0(postcss@8.4.47) + postcss: 8.4.47 + postcss-value-parser: 4.2.0 - /preferred-pm@3.0.3: - resolution: {integrity: sha512-+wZgbxNES/KlJs9q40F/1sfOd/j7f1O9JaHcW5Dsn3aUUOZg3L2bjpVUcKV2jvtElYfoTuQiNeMfQJ4kwUAhCQ==} - engines: {node: '>=10'} + postcss-minify-params@7.0.2(postcss@8.4.47): dependencies: - find-up: 5.0.0 - find-yarn-workspace-root2: 1.2.16 - path-exists: 4.0.0 - which-pm: 2.0.0 + browserslist: 4.24.2 + cssnano-utils: 5.0.0(postcss@8.4.47) + postcss: 8.4.47 + postcss-value-parser: 4.2.0 - /prelude-ls@1.1.2: - resolution: {integrity: sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==} - engines: {node: '>= 0.8.0'} + postcss-minify-selectors@5.2.1(postcss@8.4.47): + dependencies: + postcss: 8.4.47 + postcss-selector-parser: 6.1.2 - /prelude-ls@1.2.1: - resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} - engines: {node: '>= 0.8.0'} + postcss-minify-selectors@7.0.4(postcss@8.4.47): + dependencies: + cssesc: 3.0.0 + postcss: 8.4.47 + postcss-selector-parser: 6.1.2 - /prettier@2.7.1: - resolution: {integrity: sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==} - engines: {node: '>=10.13.0'} - hasBin: true - dev: true + postcss-modules-extract-imports@3.1.0(postcss@8.4.47): + dependencies: + postcss: 8.4.47 - /prettier@2.8.7: - resolution: {integrity: sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw==} - engines: {node: '>=10.13.0'} - hasBin: true + postcss-modules-local-by-default@4.0.5(postcss@8.4.47): + dependencies: + icss-utils: 5.1.0(postcss@8.4.47) + postcss: 8.4.47 + postcss-selector-parser: 6.1.2 + postcss-value-parser: 4.2.0 - /pretty-bytes@3.0.1: - resolution: {integrity: sha512-eb7ZAeUTgfh294cElcu51w+OTRp/6ItW758LjwJSK72LDevcuJn0P4eD71PLMDGPwwatXmAmYHTkzvpKlJE3ow==} - engines: {node: '>=0.10.0'} + postcss-modules-scope@3.2.0(postcss@8.4.47): dependencies: - number-is-nan: 1.0.1 - dev: true + postcss: 8.4.47 + postcss-selector-parser: 6.1.2 - /pretty-bytes@5.6.0: - resolution: {integrity: sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==} - engines: {node: '>=6'} + postcss-modules-values@4.0.0(postcss@8.4.47): + dependencies: + icss-utils: 5.1.0(postcss@8.4.47) + postcss: 8.4.47 - /pretty-error@4.0.0: - resolution: {integrity: sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==} + postcss-modules@4.3.1(postcss@8.4.47): dependencies: - lodash: 4.17.21 - renderkid: 3.0.0 - dev: false + generic-names: 4.0.0 + icss-replace-symbols: 1.1.0 + lodash.camelcase: 4.3.0 + postcss: 8.4.47 + postcss-modules-extract-imports: 3.1.0(postcss@8.4.47) + postcss-modules-local-by-default: 4.0.5(postcss@8.4.47) + postcss-modules-scope: 3.2.0(postcss@8.4.47) + postcss-modules-values: 4.0.0(postcss@8.4.47) + string-hash: 1.1.3 - /pretty-format@27.5.1: - resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + postcss-nested@6.2.0(postcss@8.4.47): dependencies: - ansi-regex: 5.0.1 - ansi-styles: 5.2.0 - react-is: 17.0.2 + postcss: 8.4.47 + postcss-selector-parser: 6.1.2 - /pretty-format@28.1.3: - resolution: {integrity: sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + postcss-nesting@10.2.0(postcss@8.4.47): dependencies: - '@jest/schemas': 28.1.3 - ansi-regex: 5.0.1 - ansi-styles: 5.2.0 - react-is: 18.2.0 + '@csstools/selector-specificity': 2.2.0(postcss-selector-parser@6.1.2) + postcss: 8.4.47 + postcss-selector-parser: 6.1.2 - /pretty-format@29.5.0: - resolution: {integrity: sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + postcss-normalize-charset@5.1.0(postcss@8.4.47): dependencies: - '@jest/schemas': 29.4.3 - ansi-styles: 5.2.0 - react-is: 18.2.0 - dev: true + postcss: 8.4.47 - /pretty-format@3.8.0: - resolution: {integrity: sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==} - dev: false + postcss-normalize-charset@7.0.0(postcss@8.4.47): + dependencies: + postcss: 8.4.47 - /pretty-ms@7.0.1: - resolution: {integrity: sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q==} - engines: {node: '>=10'} + postcss-normalize-display-values@5.1.0(postcss@8.4.47): dependencies: - parse-ms: 2.1.0 - dev: true + postcss: 8.4.47 + postcss-value-parser: 4.2.0 - /prisma@4.12.0: - resolution: {integrity: sha512-xqVper4mbwl32BWzLpdznHAYvYDWQQWK2tBfXjdUD397XaveRyAP7SkBZ6kFlIg8kKayF4hvuaVtYwXd9BodAg==} - engines: {node: '>=14.17'} - hasBin: true - requiresBuild: true + postcss-normalize-display-values@7.0.0(postcss@8.4.47): dependencies: - '@prisma/engines': 4.12.0 + postcss: 8.4.47 + postcss-value-parser: 4.2.0 - /proc-log@1.0.0: - resolution: {integrity: sha512-aCk8AO51s+4JyuYGg3Q/a6gnrlDO09NpVWePtjp7xwphcoQ04x5WAfCyugcsbLooWcMJ87CLkD4+604IckEdhg==} + postcss-normalize-positions@5.1.1(postcss@8.4.47): + dependencies: + postcss: 8.4.47 + postcss-value-parser: 4.2.0 - /process-nextick-args@2.0.1: - resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + postcss-normalize-positions@7.0.0(postcss@8.4.47): + dependencies: + postcss: 8.4.47 + postcss-value-parser: 4.2.0 - /promise-all-reject-late@1.0.1: - resolution: {integrity: sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw==} + postcss-normalize-repeat-style@5.1.1(postcss@8.4.47): + dependencies: + postcss: 8.4.47 + postcss-value-parser: 4.2.0 - /promise-call-limit@1.0.2: - resolution: {integrity: sha512-1vTUnfI2hzui8AEIixbdAJlFY4LFDXqQswy/2eOlThAscXCY4It8FdVuI0fMJGAB2aWGbdQf/gv0skKYXmdrHA==} + postcss-normalize-repeat-style@7.0.0(postcss@8.4.47): + dependencies: + postcss: 8.4.47 + postcss-value-parser: 4.2.0 - /promise-inflight@1.0.1: - resolution: {integrity: sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==} - peerDependencies: - bluebird: '*' - peerDependenciesMeta: - bluebird: - optional: true + postcss-normalize-string@5.1.0(postcss@8.4.47): + dependencies: + postcss: 8.4.47 + postcss-value-parser: 4.2.0 - /promise-retry@2.0.1: - resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==} - engines: {node: '>=10'} + postcss-normalize-string@7.0.0(postcss@8.4.47): dependencies: - err-code: 2.0.3 - retry: 0.12.0 + postcss: 8.4.47 + postcss-value-parser: 4.2.0 - /promise.series@0.2.0: - resolution: {integrity: sha512-VWQJyU2bcDTgZw8kpfBpB/ejZASlCrzwz5f2hjb/zlujOEB4oeiAhHygAWq8ubsX2GVkD4kCU5V2dwOTaCY5EQ==} - engines: {node: '>=0.12'} - dev: true + postcss-normalize-timing-functions@5.1.0(postcss@8.4.47): + dependencies: + postcss: 8.4.47 + postcss-value-parser: 4.2.0 - /promise@7.3.1: - resolution: {integrity: sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==} + postcss-normalize-timing-functions@7.0.0(postcss@8.4.47): dependencies: - asap: 2.0.6 - dev: false + postcss: 8.4.47 + postcss-value-parser: 4.2.0 - /promise@8.3.0: - resolution: {integrity: sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==} + postcss-normalize-unicode@5.1.1(postcss@8.4.47): dependencies: - asap: 2.0.6 - dev: false + browserslist: 4.24.2 + postcss: 8.4.47 + postcss-value-parser: 4.2.0 - /prompts@2.4.2: - resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} - engines: {node: '>= 6'} + postcss-normalize-unicode@7.0.2(postcss@8.4.47): dependencies: - kleur: 3.0.3 - sisteransi: 1.0.5 + browserslist: 4.24.2 + postcss: 8.4.47 + postcss-value-parser: 4.2.0 - /prop-types@15.8.1: - resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + postcss-normalize-url@5.1.0(postcss@8.4.47): dependencies: - loose-envify: 1.4.0 - object-assign: 4.1.1 - react-is: 16.13.1 + normalize-url: 6.1.0 + postcss: 8.4.47 + postcss-value-parser: 4.2.0 - /properties-reader@2.2.0: - resolution: {integrity: sha512-CgVcr8MwGoBKK24r9TwHfZkLLaNFHQ6y4wgT9w/XzdpacOOi5ciH4hcuLechSDAwXsfrGQtI2JTutY2djOx2Ow==} - engines: {node: '>=10'} + postcss-normalize-url@7.0.0(postcss@8.4.47): dependencies: - mkdirp: 1.0.4 - dev: true + postcss: 8.4.47 + postcss-value-parser: 4.2.0 - /property-information@6.2.0: - resolution: {integrity: sha512-kma4U7AFCTwpqq5twzC1YVIDXSqg6qQK6JN0smOw8fgRy1OkMi0CYSzFmsy6dnqSenamAtj0CyXMUJ1Mf6oROg==} - dev: true + postcss-normalize-whitespace@5.1.1(postcss@8.4.47): + dependencies: + postcss: 8.4.47 + postcss-value-parser: 4.2.0 - /proto-list@1.2.4: - resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} - dev: false + postcss-normalize-whitespace@7.0.0(postcss@8.4.47): + dependencies: + postcss: 8.4.47 + postcss-value-parser: 4.2.0 - /proto3-json-serializer@1.1.0: - resolution: {integrity: sha512-SjXwUWe/vANGs/mJJTbw5++7U67nwsymg7qsoPtw6GiXqw3kUy8ByojrlEdVE2efxAdKreX8WkDafxvYW95ZQg==} - engines: {node: '>=12.0.0'} + postcss-normalize@10.0.1(browserslist@4.24.2)(postcss@8.4.47): dependencies: - protobufjs: 7.2.3 - dev: false - optional: true + '@csstools/normalize.css': 12.1.1 + browserslist: 4.24.2 + postcss: 8.4.47 + postcss-browser-comments: 4.0.0(browserslist@4.24.2)(postcss@8.4.47) + sanitize.css: 13.0.0 - /protobufjs-cli@1.1.1(protobufjs@7.2.3): - resolution: {integrity: sha512-VPWMgIcRNyQwWUv8OLPyGQ/0lQY/QTQAVN5fh+XzfDwsVw1FZ2L3DM/bcBf8WPiRz2tNpaov9lPZfNcmNo6LXA==} - engines: {node: '>=12.0.0'} - hasBin: true - peerDependencies: - protobufjs: ^7.0.0 + postcss-opacity-percentage@1.1.3(postcss@8.4.47): dependencies: - chalk: 4.1.2 - escodegen: 1.14.3 - espree: 9.5.1 - estraverse: 5.3.0 - glob: 8.1.0 - jsdoc: 4.0.2 - minimist: 1.2.8 - protobufjs: 7.2.3 - semver: 7.5.0 - tmp: 0.2.1 - uglify-js: 3.17.4 - dev: false - optional: true + postcss: 8.4.47 - /protobufjs@6.11.3: - resolution: {integrity: sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg==} - hasBin: true - requiresBuild: true + postcss-ordered-values@5.1.3(postcss@8.4.47): dependencies: - '@protobufjs/aspromise': 1.1.2 - '@protobufjs/base64': 1.1.2 - '@protobufjs/codegen': 2.0.4 - '@protobufjs/eventemitter': 1.1.0 - '@protobufjs/fetch': 1.1.0 - '@protobufjs/float': 1.0.2 - '@protobufjs/inquire': 1.1.0 - '@protobufjs/path': 1.1.2 - '@protobufjs/pool': 1.1.0 - '@protobufjs/utf8': 1.1.0 - '@types/long': 4.0.2 - '@types/node': 18.15.11 - long: 4.0.0 + cssnano-utils: 3.1.0(postcss@8.4.47) + postcss: 8.4.47 + postcss-value-parser: 4.2.0 - /protobufjs@7.2.3: - resolution: {integrity: sha512-TtpvOqwB5Gdz/PQmOjgsrGH1nHjAQVCN7JG4A6r1sXRWESL5rNMAiRcBQlCAdKxZcAbstExQePYG8xof/JVRgg==} - engines: {node: '>=12.0.0'} - requiresBuild: true + postcss-ordered-values@7.0.1(postcss@8.4.47): 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': 18.15.11 - long: 5.2.1 + cssnano-utils: 5.0.0(postcss@8.4.47) + postcss: 8.4.47 + postcss-value-parser: 4.2.0 - /proxy-addr@2.0.7: - resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} - engines: {node: '>= 0.10'} + postcss-overflow-shorthand@3.0.4(postcss@8.4.47): dependencies: - forwarded: 0.2.0 - ipaddr.js: 1.9.1 + postcss: 8.4.47 + postcss-value-parser: 4.2.0 - /proxy-agent@5.0.0: - resolution: {integrity: sha512-gkH7BkvLVkSfX9Dk27W6TyNOWWZWRilRfk1XxGNWOYJ2TuedAv1yFpCaU9QSBmBe716XOTNpYNOzhysyw8xn7g==} - engines: {node: '>= 8'} + postcss-page-break@3.0.4(postcss@8.4.47): dependencies: - agent-base: 6.0.2 - debug: 4.3.4 - http-proxy-agent: 4.0.1 - https-proxy-agent: 5.0.1 - lru-cache: 5.1.1 - pac-proxy-agent: 5.0.0 - proxy-from-env: 1.1.0 - socks-proxy-agent: 5.0.1 - transitivePeerDependencies: - - supports-color - dev: true + postcss: 8.4.47 - /proxy-from-env@1.1.0: - resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} - dev: true + postcss-place@7.0.5(postcss@8.4.47): + dependencies: + postcss: 8.4.47 + postcss-value-parser: 4.2.0 - /prr@1.0.1: - resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==} - dev: true - optional: true + postcss-preset-env@7.8.3(postcss@8.4.47): + dependencies: + '@csstools/postcss-cascade-layers': 1.1.1(postcss@8.4.47) + '@csstools/postcss-color-function': 1.1.1(postcss@8.4.47) + '@csstools/postcss-font-format-keywords': 1.0.1(postcss@8.4.47) + '@csstools/postcss-hwb-function': 1.0.2(postcss@8.4.47) + '@csstools/postcss-ic-unit': 1.0.1(postcss@8.4.47) + '@csstools/postcss-is-pseudo-class': 2.0.7(postcss@8.4.47) + '@csstools/postcss-nested-calc': 1.0.0(postcss@8.4.47) + '@csstools/postcss-normalize-display-values': 1.0.1(postcss@8.4.47) + '@csstools/postcss-oklab-function': 1.1.1(postcss@8.4.47) + '@csstools/postcss-progressive-custom-properties': 1.3.0(postcss@8.4.47) + '@csstools/postcss-stepped-value-functions': 1.0.1(postcss@8.4.47) + '@csstools/postcss-text-decoration-shorthand': 1.0.0(postcss@8.4.47) + '@csstools/postcss-trigonometric-functions': 1.0.2(postcss@8.4.47) + '@csstools/postcss-unset-value': 1.0.2(postcss@8.4.47) + autoprefixer: 10.4.20(postcss@8.4.47) + browserslist: 4.24.2 + css-blank-pseudo: 3.0.3(postcss@8.4.47) + css-has-pseudo: 3.0.4(postcss@8.4.47) + css-prefers-color-scheme: 6.0.3(postcss@8.4.47) + cssdb: 7.11.2 + postcss: 8.4.47 + postcss-attribute-case-insensitive: 5.0.2(postcss@8.4.47) + postcss-clamp: 4.1.0(postcss@8.4.47) + postcss-color-functional-notation: 4.2.4(postcss@8.4.47) + postcss-color-hex-alpha: 8.0.4(postcss@8.4.47) + postcss-color-rebeccapurple: 7.1.1(postcss@8.4.47) + postcss-custom-media: 8.0.2(postcss@8.4.47) + postcss-custom-properties: 12.1.11(postcss@8.4.47) + postcss-custom-selectors: 6.0.3(postcss@8.4.47) + postcss-dir-pseudo-class: 6.0.5(postcss@8.4.47) + postcss-double-position-gradients: 3.1.2(postcss@8.4.47) + postcss-env-function: 4.0.6(postcss@8.4.47) + postcss-focus-visible: 6.0.4(postcss@8.4.47) + postcss-focus-within: 5.0.4(postcss@8.4.47) + postcss-font-variant: 5.0.0(postcss@8.4.47) + postcss-gap-properties: 3.0.5(postcss@8.4.47) + postcss-image-set-function: 4.0.7(postcss@8.4.47) + postcss-initial: 4.0.1(postcss@8.4.47) + postcss-lab-function: 4.2.1(postcss@8.4.47) + postcss-logical: 5.0.4(postcss@8.4.47) + postcss-media-minmax: 5.0.0(postcss@8.4.47) + postcss-nesting: 10.2.0(postcss@8.4.47) + postcss-opacity-percentage: 1.1.3(postcss@8.4.47) + postcss-overflow-shorthand: 3.0.4(postcss@8.4.47) + postcss-page-break: 3.0.4(postcss@8.4.47) + postcss-place: 7.0.5(postcss@8.4.47) + postcss-pseudo-class-any-link: 7.1.6(postcss@8.4.47) + postcss-replace-overflow-wrap: 4.0.0(postcss@8.4.47) + postcss-selector-not: 6.0.1(postcss@8.4.47) + postcss-value-parser: 4.2.0 - /pseudomap@1.0.2: - resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==} - dev: false + postcss-pseudo-class-any-link@7.1.6(postcss@8.4.47): + dependencies: + postcss: 8.4.47 + postcss-selector-parser: 6.1.2 - /psl@1.9.0: - resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} - dev: false + postcss-reduce-initial@5.1.2(postcss@8.4.47): + dependencies: + browserslist: 4.24.2 + caniuse-api: 3.0.0 + postcss: 8.4.47 - /pstree.remy@1.1.8: - resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==} - dev: true + postcss-reduce-initial@7.0.2(postcss@8.4.47): + dependencies: + browserslist: 4.24.2 + caniuse-api: 3.0.0 + postcss: 8.4.47 - /pump@2.0.1: - resolution: {integrity: sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==} + postcss-reduce-transforms@5.1.0(postcss@8.4.47): dependencies: - end-of-stream: 1.4.4 - once: 1.4.0 - dev: true + postcss: 8.4.47 + postcss-value-parser: 4.2.0 - /pump@3.0.0: - resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} + postcss-reduce-transforms@7.0.0(postcss@8.4.47): dependencies: - end-of-stream: 1.4.4 - once: 1.4.0 + postcss: 8.4.47 + postcss-value-parser: 4.2.0 + + postcss-replace-overflow-wrap@4.0.0(postcss@8.4.47): + dependencies: + postcss: 8.4.47 - /pumpify@1.5.1: - resolution: {integrity: sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==} + postcss-selector-not@6.0.1(postcss@8.4.47): dependencies: - duplexify: 3.7.1 - inherits: 2.0.4 - pump: 2.0.1 - dev: true + postcss: 8.4.47 + postcss-selector-parser: 6.1.2 - /punycode@2.3.0: - resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} - engines: {node: '>=6'} + postcss-selector-parser@6.0.10: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 - /pure-color@1.3.0: - resolution: {integrity: sha512-QFADYnsVoBMw1srW7OVKEYjG+MbIa49s54w1MA1EDY6r2r/sTcKKYqRX1f4GYvnXP7eN/Pe9HFcX+hwzmrXRHA==} - dev: false + postcss-selector-parser@6.1.2: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 - /pure-rand@6.0.1: - resolution: {integrity: sha512-t+x1zEHDjBwkDGY5v5ApnZ/utcd4XYDiJsaQQoptTXgUXX95sDg1elCdJghzicm7n2mbCBJ3uYWr6M22SO19rg==} - dev: true + postcss-svgo@5.1.0(postcss@8.4.47): + dependencies: + postcss: 8.4.47 + postcss-value-parser: 4.2.0 + svgo: 2.8.0 - /q@1.5.1: - resolution: {integrity: sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==} - engines: {node: '>=0.6.0', teleport: '>=0.2.0'} + postcss-svgo@7.0.1(postcss@8.4.47): + dependencies: + postcss: 8.4.47 + postcss-value-parser: 4.2.0 + svgo: 3.3.2 - /qrcode.react@3.1.0(react@18.2.0): - resolution: {integrity: sha512-oyF+Urr3oAMUG/OiOuONL3HXM+53wvuH3mtIWQrYmsXoAq0DkvZp2RYUWFSMFtbdOpuS++9v+WAkzNVkMlNW6Q==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 + postcss-unique-selectors@5.1.1(postcss@8.4.47): dependencies: - react: 18.2.0 + postcss: 8.4.47 + postcss-selector-parser: 6.1.2 - /qs@6.11.0: - resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==} - engines: {node: '>=0.6'} + postcss-unique-selectors@7.0.3(postcss@8.4.47): dependencies: - side-channel: 1.0.4 + postcss: 8.4.47 + postcss-selector-parser: 6.1.2 - /qs@6.11.1: - resolution: {integrity: sha512-0wsrzgTz/kAVIeuxSjnpGC56rzYtr6JT/2BwEvMaPhFIoYa1aGO8LbzuU1R0uUYQkLpWBTOj0l/CLAJB64J6nQ==} - engines: {node: '>=0.6'} + postcss-value-parser@4.2.0: {} + + postcss@8.4.47: dependencies: - side-channel: 1.0.4 + nanoid: 3.3.8 + picocolors: 1.1.1 + source-map-js: 1.2.1 - /querystringify@2.2.0: - resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} - dev: false + postgres-array@2.0.0: {} - /queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + postgres-array@3.0.2: {} - /quick-lru@5.1.1: - resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} - engines: {node: '>=10'} + postgres-bytea@1.0.0: {} - /raf@3.4.1: - resolution: {integrity: sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==} + postgres-bytea@3.0.0: dependencies: - performance-now: 2.1.0 - dev: false + obuf: 1.1.2 - /randombytes@2.1.0: - resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} - dependencies: - safe-buffer: 5.2.1 + postgres-date@1.0.7: {} - /range-parser@1.2.1: - resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} - engines: {node: '>= 0.6'} + postgres-date@2.1.0: {} - /raw-body@2.5.1: - resolution: {integrity: sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==} - engines: {node: '>= 0.8'} + postgres-interval@1.2.0: dependencies: - bytes: 3.1.2 - http-errors: 2.0.0 - iconv-lite: 0.4.24 - unpipe: 1.0.0 + xtend: 4.0.2 - /raw-body@2.5.2: - resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} - engines: {node: '>= 0.8'} - dependencies: - bytes: 3.1.2 - http-errors: 2.0.0 - iconv-lite: 0.4.24 - unpipe: 1.0.0 - dev: true + postgres-interval@3.0.0: {} - /raw-loader@4.0.2(webpack@5.79.0): - resolution: {integrity: sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA==} - engines: {node: '>= 10.13.0'} - peerDependencies: - webpack: ^4.0.0 || ^5.0.0 - dependencies: - loader-utils: 2.0.4 - schema-utils: 3.1.1 - webpack: 5.79.0(esbuild@0.16.3) - dev: true + postgres-range@1.1.4: {} - /rc-align@4.0.15(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-wqJtVH60pka/nOX7/IspElA8gjPNQKIx/ZqJ6heATCkXpe1Zg4cPVrMD2vC96wjsFFL8WsmhPbx9tdMo1qqlIA==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' + posthog-node@4.2.1: dependencies: - '@babel/runtime': 7.21.0 - classnames: 2.3.2 - dom-align: 1.12.4 - rc-util: 5.29.3(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - resize-observer-polyfill: 1.5.1 + axios: 1.8.2 + rusha: 0.8.14 + transitivePeerDependencies: + - debug - /rc-cascader@3.10.1(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-tImBYEAqLlIZ+jnRmfQQEm5gOXa09N9aGV9AKxriXlCvsNEfdZMIRyY0p74sEZIUn0ycXHo8VcOlqsgLcgFknQ==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' + posthtml-parser@0.11.0: dependencies: - '@babel/runtime': 7.21.0 - array-tree-filter: 2.1.0 - classnames: 2.3.2 - rc-select: 14.4.3(react-dom@18.2.0)(react@18.2.0) - rc-tree: 5.7.3(react-dom@18.2.0)(react@18.2.0) - rc-util: 5.29.3(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + htmlparser2: 7.2.0 - /rc-checkbox@3.0.0(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-tOEs1+wWDUei7DuP2EsJCZfam5vxMjKTCGcZdXVgsiOcNszc41Esycbo31P0/jFwUAPmd5oPYFWkcnFUCTLZxA==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' + posthtml-render@3.0.0: dependencies: - '@babel/runtime': 7.21.0 - classnames: 2.3.2 - rc-util: 5.29.3(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + is-json: 2.0.1 - /rc-collapse@3.5.2(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-/TNiT3DW1t3sUCiVD/DPUYooJZ3BLA93/2rZsB3eM2bGJCCla2X9D2E4tgm7LGMQGy5Atb2lMUn2FQuvQNvavQ==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' + posthtml@0.16.6: dependencies: - '@babel/runtime': 7.21.0 - classnames: 2.3.2 - rc-motion: 2.6.3(react-dom@18.2.0)(react@18.2.0) - rc-util: 5.29.3(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + posthtml-parser: 0.11.0 + posthtml-render: 3.0.0 - /rc-dialog@9.1.0(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-5ry+JABAWEbaKyYsmITtrJbZbJys8CtMyzV8Xn4LYuXMeUx5XVHNyJRoqLFE4AzBuXXzOWeaC49cg+XkxK6kHA==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' + preact-render-to-string@5.2.6(preact@10.24.3): dependencies: - '@babel/runtime': 7.21.0 - '@rc-component/portal': 1.1.1(react-dom@18.2.0)(react@18.2.0) - classnames: 2.3.2 - rc-motion: 2.6.3(react-dom@18.2.0)(react@18.2.0) - rc-util: 5.29.3(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + preact: 10.24.3 + pretty-format: 3.8.0 - /rc-drawer@6.1.5(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-MDRomQXFi+tvDuwsRAddJ2Oy2ayLCZ29weMzp3rJFO9UNEVLEVV7nuyx5lEgNJIdM//tE6wWQV95cTUiMVqD6w==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' + preact@10.24.3: {} + + prebuild-install@7.1.3: dependencies: - '@babel/runtime': 7.21.0 - '@rc-component/portal': 1.1.1(react-dom@18.2.0)(react@18.2.0) - classnames: 2.3.2 - rc-motion: 2.6.3(react-dom@18.2.0)(react@18.2.0) - rc-util: 5.29.3(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + detect-libc: 2.0.3 + expand-template: 2.0.3 + github-from-package: 0.0.0 + minimist: 1.2.8 + mkdirp-classic: 0.5.3 + napi-build-utils: 2.0.0 + node-abi: 3.71.0 + pump: 3.0.2 + rc: 1.2.8 + simple-get: 4.0.1 + tar-fs: 2.1.1 + tunnel-agent: 0.6.0 - /rc-dropdown@4.0.1(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-OdpXuOcme1rm45cR0Jzgfl1otzmU4vuBVb+etXM8vcaULGokAKVpKlw8p6xzspG7jGd/XxShvq+N3VNEfk/l5g==} - peerDependencies: - react: '>=16.11.0' - react-dom: '>=16.11.0' + preferred-pm@3.1.4: dependencies: - '@babel/runtime': 7.21.0 - classnames: 2.3.2 - rc-trigger: 5.3.4(react-dom@18.2.0)(react@18.2.0) - rc-util: 5.29.3(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + find-up: 5.0.0 + find-yarn-workspace-root2: 1.2.16 + path-exists: 4.0.0 + which-pm: 2.2.0 - /rc-field-form@1.29.2(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-gXNkthHMUjJ7gDKYmD/lJWJrpMqAjiEPQE4QmlOuZoiHF51LybCL/y+iAmLXpdEjPfJ41WtZBH5hZMUEnEnHXA==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' + prelude-ls@1.1.2: {} + + prelude-ls@1.2.1: {} + + prettier@2.8.8: {} + + prettier@3.4.2: {} + + prettier@3.6.2: {} + + pretty-bytes@3.0.1: dependencies: - '@babel/runtime': 7.21.0 - async-validator: 4.2.5 - rc-util: 5.29.3(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + number-is-nan: 1.0.1 - /rc-image@5.16.0(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-11DOye57IgTXh2yTsmxFNynZJG3tdx8RZnnaqb38eYWrBPPyhVHIuURxyiSZ8B68lEUAggR7SBA0Zb95KP/CyQ==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' + pretty-bytes@5.6.0: {} + + pretty-bytes@6.1.1: {} + + pretty-error@4.0.0: dependencies: - '@babel/runtime': 7.21.0 - '@rc-component/portal': 1.1.1(react-dom@18.2.0)(react@18.2.0) - classnames: 2.3.2 - rc-dialog: 9.1.0(react-dom@18.2.0)(react@18.2.0) - rc-motion: 2.6.3(react-dom@18.2.0)(react@18.2.0) - rc-util: 5.29.3(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + lodash: 4.17.21 + renderkid: 3.0.0 - /rc-input-number@7.4.2(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-yGturTw7WGP+M1GbJ+UTAO7L4buxeW6oilhL9Sq3DezsRS8/9qec4UiXUbeoiX9bzvRXH11JvgskBtxSp4YSNg==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' + pretty-format@27.5.1: dependencies: - '@babel/runtime': 7.21.0 - '@rc-component/mini-decimal': 1.0.1 - classnames: 2.3.2 - rc-util: 5.29.3(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + ansi-regex: 5.0.1 + ansi-styles: 5.2.0 + react-is: 17.0.2 - /rc-input@1.0.4(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-clY4oneVHRtKHYf/HCxT/MO+4BGzCIywSNLosXWOm7fcQAS0jQW7n0an8Raa8JMB8kpxc8m28p7SNwFZmlMj6g==} - peerDependencies: - react: '>=16.0.0' - react-dom: '>=16.0.0' + pretty-format@28.1.3: dependencies: - '@babel/runtime': 7.21.0 - classnames: 2.3.2 - rc-util: 5.29.3(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + '@jest/schemas': 28.1.3 + ansi-regex: 5.0.1 + ansi-styles: 5.2.0 + react-is: 18.3.1 - /rc-mentions@2.2.0(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-R7ncCldr02uKgJBBPlXdtnOGQIjZ9C3uoIMi4fabU3CPFdmefYlNF6QM4u2AzgcGt8V0KkoHTN5T6HPdUpet8g==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' + pretty-format@29.7.0: dependencies: - '@babel/runtime': 7.21.0 - '@rc-component/trigger': 1.8.0(react-dom@18.2.0)(react@18.2.0) - classnames: 2.3.2 - rc-input: 1.0.4(react-dom@18.2.0)(react@18.2.0) - rc-menu: 9.8.4(react-dom@18.2.0)(react@18.2.0) - rc-textarea: 1.2.2(react-dom@18.2.0)(react@18.2.0) - rc-util: 5.29.3(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + '@jest/schemas': 29.6.3 + ansi-styles: 5.2.0 + react-is: 18.3.1 - /rc-menu@9.8.4(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-lmw2j8I2fhdIzHmC9ajfImfckt0WDb2KVJJBBRIsxPEw2kGkEfjLMUoB1NgiNT/Q5cC8PdjGOGQjHJIJMwyNMw==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' + pretty-format@3.8.0: {} + + prism-react-renderer@2.4.1(react@19.0.0): dependencies: - '@babel/runtime': 7.21.0 - classnames: 2.3.2 - rc-motion: 2.6.3(react-dom@18.2.0)(react@18.2.0) - rc-overflow: 1.3.0(react-dom@18.2.0)(react@18.2.0) - rc-trigger: 5.3.4(react-dom@18.2.0)(react@18.2.0) - rc-util: 5.29.3(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + '@types/prismjs': 1.26.5 + clsx: 2.1.1 + react: 19.0.0 - /rc-motion@2.6.3(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-xFLkes3/7VL/J+ah9jJruEW/Akbx5F6jVa2wG5o/ApGKQKSOd5FR3rseHLL9+xtJg4PmCwo6/1tqhDO/T+jFHA==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' + prisma@6.5.0(typescript@5.6.3): dependencies: - '@babel/runtime': 7.21.0 - classnames: 2.3.2 - rc-util: 5.29.3(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + '@prisma/config': 6.5.0 + '@prisma/engines': 6.5.0 + optionalDependencies: + fsevents: 2.3.3 + typescript: 5.6.3 + transitivePeerDependencies: + - supports-color - /rc-notification@5.0.3(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-+wHbHu6RiTNtsZYx42WxWA+tC5m0qyKvJAauO4/6LIEyJspK8fRlFQz+OCFgFwGuNs3cOdo9tLs+cPfztSZwbQ==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' + prismjs@1.30.0: {} + + proc-log@1.0.0: {} + + proc-log@3.0.0: {} + + process-nextick-args@2.0.1: {} + + process@0.11.10: {} + + prom-client@15.1.3: dependencies: - '@babel/runtime': 7.21.0 - classnames: 2.3.2 - rc-motion: 2.6.3(react-dom@18.2.0)(react@18.2.0) - rc-util: 5.29.3(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + '@opentelemetry/api': 1.9.0 + tdigest: 0.1.2 - /rc-overflow@1.3.0(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-p2Qt4SWPTHAYl4oAao1THy669Fm5q8pYBDBHRaFOekCvcdcrgIx0ByXQMEkyPm8wUDX4BK6aARWecvCRc/7CTA==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' + promise-all-reject-late@1.0.1: {} + + promise-call-limit@1.0.2: {} + + promise-inflight@1.0.1: {} + + promise-retry@2.0.1: dependencies: - '@babel/runtime': 7.21.0 - classnames: 2.3.2 - rc-resize-observer: 1.3.1(react-dom@18.2.0)(react@18.2.0) - rc-util: 5.29.3(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + err-code: 2.0.3 + retry: 0.12.0 - /rc-pagination@3.3.1(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-eI4dSeB3OrFxll7KzWa3ZH63LV2tHxt0AUmZmDwuI6vc3CK5lZhaKUYq0fRowb5586hN+L26j5WZoSz9cwEfjg==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' + promise.series@0.2.0: {} + + promise@7.3.1: dependencies: - '@babel/runtime': 7.21.0 - classnames: 2.3.2 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + asap: 2.0.6 - /rc-picker@2.7.0(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-oZH6FZ3j4iuBxHB4NvQ6ABRsS2If/Kpty1YFFsji7/aej6ruGmfM7WnJWQ88AoPfpJ++ya5z+nVEA8yCRYGKyw==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' + promise@8.3.0: dependencies: - '@babel/runtime': 7.21.0 - classnames: 2.3.2 - date-fns: 2.29.3 - dayjs: 1.11.7 - moment: 2.29.4 - rc-trigger: 5.3.4(react-dom@18.2.0)(react@18.2.0) - rc-util: 5.29.3(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - shallowequal: 1.1.0 - dev: false + asap: 2.0.6 - /rc-picker@3.6.2(dayjs@1.11.7)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-acLNCi2WTNAuvTtcEzKp72mU15ni0sqrIKVlEcj04KgLZxhlVPMabCS+Sc8VuOCPJbOcW0XeOydbNnJbWTvzxg==} - engines: {node: '>=8.x'} - peerDependencies: - date-fns: '>= 2.x' - dayjs: '>= 1.x' - luxon: '>= 3.x' - moment: '>= 2.x' - react: '>=16.9.0' - react-dom: '>=16.9.0' - peerDependenciesMeta: - date-fns: - optional: true - dayjs: - optional: true - luxon: - optional: true - moment: - optional: true + prompts@2.4.2: dependencies: - '@babel/runtime': 7.21.0 - '@rc-component/trigger': 1.8.0(react-dom@18.2.0)(react@18.2.0) - classnames: 2.3.2 - dayjs: 1.11.7 - rc-util: 5.29.3(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + kleur: 3.0.3 + sisteransi: 1.0.5 - /rc-progress@3.4.1(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-eAFDHXlk8aWpoXl0llrenPMt9qKHQXphxcVsnKs0FHC6eCSk1ebJtyaVjJUzKe0233ogiLDeEFK1Uihz3s67hw==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' + prop-types@15.8.1: dependencies: - '@babel/runtime': 7.21.0 - classnames: 2.3.2 - rc-util: 5.29.3(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 - /rc-rate@2.10.0(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-TCjEpKPeN1m0EnGDDbb1KyxjNTJRzoReiPdtbrBJEey4Ryf/UGOQ6vqmz2yC6DJdYVDVUoZPdoz043ryh0t/nQ==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' + proper-lockfile@4.1.2: dependencies: - '@babel/runtime': 7.21.0 - classnames: 2.3.2 - rc-util: 5.29.3(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + graceful-fs: 4.2.11 + retry: 0.12.0 + signal-exit: 3.0.7 - /rc-resize-observer@1.3.1(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-iFUdt3NNhflbY3mwySv5CA1TC06zdJ+pfo0oc27xpf4PIOvfZwZGtD9Kz41wGYqC4SLio93RVAirSSpYlV/uYg==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' + properties-reader@2.3.0: dependencies: - '@babel/runtime': 7.21.0 - classnames: 2.3.2 - rc-util: 5.29.3(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - resize-observer-polyfill: 1.5.1 + mkdirp: 1.0.4 - /rc-segmented@2.1.2(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-qGo1bCr83ESXpXVOCXjFe1QJlCAQXyi9KCiy8eX3rIMYlTeJr/ftySIaTnYsitL18SvWf5ZEHsfqIWoX0EMfFQ==} - peerDependencies: - react: '>=16.0.0' - react-dom: '>=16.0.0' + proto-list@1.2.4: {} + + proto3-json-serializer@2.0.2: dependencies: - '@babel/runtime': 7.21.0 - classnames: 2.3.2 - rc-motion: 2.6.3(react-dom@18.2.0)(react@18.2.0) - rc-util: 5.29.3(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + protobufjs: 7.4.0 - /rc-select@14.4.3(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-qoz4gNqm3SN+4dYKSCRiRkxKSEEdbS3jC6gdFYoYwEjDZ9sdQFo5jHlfQbF+hhai01HOoj1Hf8Gq6tpUvU+Gmw==} - engines: {node: '>=8.x'} - peerDependencies: - react: '*' - react-dom: '*' + protobufjs@7.4.0: dependencies: - '@babel/runtime': 7.21.0 - '@rc-component/trigger': 1.8.0(react-dom@18.2.0)(react@18.2.0) - classnames: 2.3.2 - rc-motion: 2.6.3(react-dom@18.2.0)(react@18.2.0) - rc-overflow: 1.3.0(react-dom@18.2.0)(react@18.2.0) - rc-util: 5.29.3(react-dom@18.2.0)(react@18.2.0) - rc-virtual-list: 3.4.13(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - - /rc-slider@10.1.1(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-gn8oXazZISEhnmRinI89Z/JD/joAaM35jp+gDtIVSTD/JJMCCBqThqLk1SVJmvtfeiEF/kKaFY0+qt4SDHFUDw==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' + '@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': 18.19.61 + long: 5.2.3 + + proxy-addr@2.0.7: dependencies: - '@babel/runtime': 7.21.0 - classnames: 2.3.2 - rc-util: 5.29.3(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + forwarded: 0.2.0 + ipaddr.js: 1.9.1 - /rc-steps@6.0.0(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-+KfMZIty40mYCQSDvYbZ1jwnuObLauTiIskT1hL4FFOBHP6ZOr8LK0m143yD3kEN5XKHSEX1DIwCj3AYZpoeNQ==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' + proxy-from-env@1.1.0: {} + + prr@1.0.1: + optional: true + + psl@1.9.0: {} + + pstree.remy@1.1.8: {} + + pump@3.0.2: dependencies: - '@babel/runtime': 7.21.0 - classnames: 2.3.2 - rc-util: 5.29.3(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + end-of-stream: 1.4.4 + once: 1.4.0 - /rc-switch@4.0.0(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-IfrYC99vN0gKaTyjQdqYuADU0eH00SAFHg3jOp8HrmUpJruhV1SohJzrCbPqPraZeX/6X/QKkdLfkdnUub05WA==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' + punycode@2.3.1: {} + + pure-color@1.3.0: {} + + pure-rand@6.1.0: {} + + q@1.5.1: {} + + qs@6.13.0: dependencies: - '@babel/runtime': 7.21.0 - classnames: 2.3.2 - rc-util: 5.29.3(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + side-channel: 1.0.6 - /rc-table@7.31.1(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-KZPi35aGpv2VaL1Jbc58FBJo063HtKyVjhOFWX4AkBV7tjHHQokMdUoua5E+GPJh6QZUpK/a8PjKa9IZzPLIEA==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' + querystringify@2.2.0: {} + + queue-microtask@1.2.3: {} + + raf@3.4.1: dependencies: - '@babel/runtime': 7.21.0 - '@rc-component/context': 1.3.0(react-dom@18.2.0)(react@18.2.0) - classnames: 2.3.2 - rc-resize-observer: 1.3.1(react-dom@18.2.0)(react@18.2.0) - rc-util: 5.29.3(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + performance-now: 2.1.0 - /rc-tabs@12.5.10(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-Ay0l0jtd4eXepFH9vWBvinBjqOpqzcsJTerBGwJy435P2S90Uu38q8U/mvc1sxUEVOXX5ZCFbxcWPnfG3dH+tQ==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' + randombytes@2.1.0: dependencies: - '@babel/runtime': 7.21.0 - classnames: 2.3.2 - rc-dropdown: 4.0.1(react-dom@18.2.0)(react@18.2.0) - rc-menu: 9.8.4(react-dom@18.2.0)(react@18.2.0) - rc-motion: 2.6.3(react-dom@18.2.0)(react@18.2.0) - rc-resize-observer: 1.3.1(react-dom@18.2.0)(react@18.2.0) - rc-util: 5.29.3(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + safe-buffer: 5.2.1 - /rc-textarea@1.2.2(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-S9fkiek5VezfwJe2McEs/NH63xgnnZ4iDh6a8n01mIfzyNJj0HkS0Uz6boyR3/eONYjmKaqhrpuJJuEClRDEBw==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' + range-parser@1.2.1: {} + + raw-body@2.5.2: dependencies: - '@babel/runtime': 7.21.0 - classnames: 2.3.2 - rc-input: 1.0.4(react-dom@18.2.0)(react@18.2.0) - rc-resize-observer: 1.3.1(react-dom@18.2.0)(react@18.2.0) - rc-util: 5.29.3(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + unpipe: 1.0.0 - /rc-tooltip@6.0.1(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-MdvPlsD1fDSxKp9+HjXrc/CxLmA/s11QYIh1R7aExxfodKP7CZA++DG1AjrW80F8IUdHYcR43HAm0Y2BYPelHA==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' + raw-loader@4.0.2(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(esbuild@0.23.1)): dependencies: - '@babel/runtime': 7.21.0 - '@rc-component/trigger': 1.8.0(react-dom@18.2.0)(react@18.2.0) - classnames: 2.3.2 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + loader-utils: 2.0.4 + schema-utils: 3.3.0 + webpack: 5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(esbuild@0.23.1) - /rc-tree-select@5.8.0(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-NozrkVLR8k3cpx8R5/YFmJMptgOacR5zEQHZGMQg31bD6jEgGiJeOn2cGRI6x0Xdyvi1CSqCbUsIoqiej74wzw==} - peerDependencies: - react: '*' - react-dom: '*' + raw-loader@4.0.2(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))): dependencies: - '@babel/runtime': 7.21.0 - classnames: 2.3.2 - rc-select: 14.4.3(react-dom@18.2.0)(react@18.2.0) - rc-tree: 5.7.3(react-dom@18.2.0)(react@18.2.0) - rc-util: 5.29.3(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + loader-utils: 2.0.4 + schema-utils: 3.3.0 + webpack: 5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(esbuild@0.25.6) - /rc-tree@5.7.3(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-Oql2S9+ZmT+mfTp5SNo1XM0QvkENjc0mPRFsHWRFSPuKird0OYMZZKmLznUJ+0aGDeFFWN42wiUZJtMFhrLgLw==} - engines: {node: '>=10.x'} - peerDependencies: - react: '*' - react-dom: '*' + rc-align@4.0.15(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - '@babel/runtime': 7.21.0 - classnames: 2.3.2 - rc-motion: 2.6.3(react-dom@18.2.0)(react@18.2.0) - rc-util: 5.29.3(react-dom@18.2.0)(react@18.2.0) - rc-virtual-list: 3.4.13(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + '@babel/runtime': 7.26.0 + classnames: 2.5.1 + dom-align: 1.12.4 + rc-util: 5.43.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + resize-observer-polyfill: 1.5.1 - /rc-trigger@5.3.4(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-mQv+vas0TwKcjAO2izNPkqR4j86OemLRmvL2nOzdP9OWNWA1ivoTt5hzFqYNW9zACwmTezRiN8bttrC7cZzYSw==} - engines: {node: '>=8.x'} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' + rc-cascader@3.30.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.26.0 + classnames: 2.5.1 + rc-select: 14.16.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-tree: 5.10.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.43.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-checkbox@3.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.26.0 + classnames: 2.5.1 + rc-util: 5.43.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-collapse@3.9.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.26.0 + classnames: 2.5.1 + rc-motion: 2.9.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.43.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-dialog@9.6.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.26.0 + '@rc-component/portal': 1.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + classnames: 2.5.1 + rc-motion: 2.9.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.43.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-drawer@7.2.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.26.0 + '@rc-component/portal': 1.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + classnames: 2.5.1 + rc-motion: 2.9.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.43.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-dropdown@4.2.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.26.0 + '@rc-component/trigger': 2.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + classnames: 2.5.1 + rc-util: 5.43.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-field-form@2.5.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.26.0 + '@rc-component/async-validator': 5.0.4 + rc-util: 5.43.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-image@7.11.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.26.0 + '@rc-component/portal': 1.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + classnames: 2.5.1 + rc-dialog: 9.6.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-motion: 2.9.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.43.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-input-number@9.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.26.0 + '@rc-component/mini-decimal': 1.1.0 + classnames: 2.5.1 + rc-input: 1.6.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.43.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-input@1.6.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.26.0 + classnames: 2.5.1 + rc-util: 5.43.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-mentions@2.17.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.26.0 + '@rc-component/trigger': 2.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + classnames: 2.5.1 + rc-input: 1.6.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-menu: 9.16.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-textarea: 1.8.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.43.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-menu@9.16.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.26.0 + '@rc-component/trigger': 2.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + classnames: 2.5.1 + rc-motion: 2.9.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-overflow: 1.3.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.43.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-motion@2.9.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.26.0 + classnames: 2.5.1 + rc-util: 5.43.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-notification@5.6.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.26.0 + classnames: 2.5.1 + rc-motion: 2.9.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.43.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-overflow@1.3.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.26.0 + classnames: 2.5.1 + rc-resize-observer: 1.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.43.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-pagination@4.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.26.0 + classnames: 2.5.1 + rc-util: 5.43.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-picker@2.7.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.26.0 + classnames: 2.5.1 + date-fns: 2.30.0 + dayjs: 1.11.13 + moment: 2.30.1 + rc-trigger: 5.3.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.43.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + shallowequal: 1.1.0 + + rc-picker@4.7.2(date-fns@2.30.0)(dayjs@1.11.13)(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - '@babel/runtime': 7.21.0 - classnames: 2.3.2 - rc-align: 4.0.15(react-dom@18.2.0)(react@18.2.0) - rc-motion: 2.6.3(react-dom@18.2.0)(react@18.2.0) - rc-util: 5.29.3(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + '@babel/runtime': 7.26.0 + '@rc-component/trigger': 2.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + classnames: 2.5.1 + rc-overflow: 1.3.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-resize-observer: 1.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.43.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + date-fns: 2.30.0 + dayjs: 1.11.13 + moment: 2.30.1 - /rc-upload@4.3.4(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-uVbtHFGNjHG/RyAfm9fluXB6pvArAGyAx8z7XzXXyorEgVIWj6mOlriuDm0XowDHYz4ycNK0nE0oP3cbFnzxiQ==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' + rc-progress@4.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - '@babel/runtime': 7.21.0 - classnames: 2.3.2 - rc-util: 5.29.3(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + '@babel/runtime': 7.26.0 + classnames: 2.5.1 + rc-util: 5.43.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) - /rc-util@5.29.3(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-wX6ZwQTzY2v7phJBquN4mSEIFR0E0qumlENx0zjENtDvoVSq2s7cR95UidKRO1hOHfDsecsfM9D1gO4Kebs7fA==} - peerDependencies: - react: '>=16.9.0' - react-dom: '>=16.9.0' + rc-rate@2.13.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - '@babel/runtime': 7.21.0 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - react-is: 16.13.1 + '@babel/runtime': 7.26.0 + classnames: 2.5.1 + rc-util: 5.43.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) - /rc-virtual-list@3.4.13(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-cPOVDmcNM7rH6ANotanMDilW/55XnFPw0Jh/GQYtrzZSy3AmWvCnqVNyNC/pgg3lfVmX2994dlzAhuUrd4jG7w==} - engines: {node: '>=8.x'} - peerDependencies: - react: '*' - react-dom: '*' + rc-resize-observer@1.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - '@babel/runtime': 7.21.0 - classnames: 2.3.2 - rc-resize-observer: 1.3.1(react-dom@18.2.0)(react@18.2.0) - rc-util: 5.29.3(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + '@babel/runtime': 7.26.0 + classnames: 2.5.1 + rc-util: 5.43.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + resize-observer-polyfill: 1.5.1 - /rc@1.2.8: - resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} - hasBin: true + rc-segmented@2.5.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.26.0 + classnames: 2.5.1 + rc-motion: 2.9.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.43.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-select@14.16.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.26.0 + '@rc-component/trigger': 2.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + classnames: 2.5.1 + rc-motion: 2.9.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-overflow: 1.3.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.43.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-virtual-list: 3.14.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-slider@11.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.26.0 + classnames: 2.5.1 + rc-util: 5.43.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-steps@6.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.26.0 + classnames: 2.5.1 + rc-util: 5.43.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-switch@4.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.26.0 + classnames: 2.5.1 + rc-util: 5.43.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-table@7.48.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.26.0 + '@rc-component/context': 1.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + classnames: 2.5.1 + rc-resize-observer: 1.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.43.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-virtual-list: 3.14.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-tabs@15.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.26.0 + classnames: 2.5.1 + rc-dropdown: 4.2.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-menu: 9.16.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-motion: 2.9.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-resize-observer: 1.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.43.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-textarea@1.8.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.26.0 + classnames: 2.5.1 + rc-input: 1.6.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-resize-observer: 1.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.43.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-tooltip@6.2.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.26.0 + '@rc-component/trigger': 2.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + classnames: 2.5.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-tree-select@5.24.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.26.0 + classnames: 2.5.1 + rc-select: 14.16.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-tree: 5.10.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.43.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-tree@5.10.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.26.0 + classnames: 2.5.1 + rc-motion: 2.9.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.43.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-virtual-list: 3.14.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-trigger@5.3.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.26.0 + classnames: 2.5.1 + rc-align: 4.0.15(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-motion: 2.9.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.43.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-upload@4.8.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.26.0 + classnames: 2.5.1 + rc-util: 5.43.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-util@5.43.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.26.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-is: 18.3.1 + + rc-virtual-list@3.14.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.26.0 + classnames: 2.5.1 + rc-resize-observer: 1.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.43.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc@1.2.8: dependencies: deep-extend: 0.6.0 ini: 1.3.8 minimist: 1.2.8 strip-json-comments: 2.0.1 - dev: false - /react-app-polyfill@3.1.0-next.14: - resolution: {integrity: sha512-ki6Vg6IbszoxR0L70KiAUc+Fg7snl3X9nDMM3aP9cwmnj3kJE33pEIxjQwACBOjGpx/kVRQ7JzQD4VLSrGqNYw==} - engines: {node: '>=14'} + react-app-polyfill@3.0.0: dependencies: - core-js: 3.30.0 + core-js: 3.38.1 object-assign: 4.1.1 promise: 8.3.0 raf: 3.4.1 regenerator-runtime: 0.13.11 - whatwg-fetch: 3.6.2 - dev: false + whatwg-fetch: 3.6.20 - /react-base16-styling@0.6.0: - resolution: {integrity: sha512-yvh/7CArceR/jNATXOKDlvTnPKPmGZz7zsenQ3jUwLzHkNUR0CvY3yGYJbWJ/nnxsL8Sgmt5cO3/SILVuPO6TQ==} + react-base16-styling@0.6.0: dependencies: base16: 1.0.0 lodash.curry: 4.1.1 lodash.flow: 3.5.0 pure-color: 1.3.0 - dev: false - /react-dev-utils@12.1.0-next.14(eslint@8.38.0)(typescript@4.9.5)(webpack@5.78.0): - resolution: {integrity: sha512-KJmfdZhLvsvNc2tWYyGFGwp71+0E+3hYwgEeYqG1JJ9i6z3TV4byweag/p84Htsra0Asfp5akHMVwyRQr1+VAg==} - engines: {node: '>=14'} + react-dev-utils@12.0.1(eslint@8.57.1)(typescript@5.6.3)(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))): dependencies: - '@babel/code-frame': 7.21.4 + '@babel/code-frame': 7.26.0 address: 1.2.2 - browserslist: 4.21.5 + browserslist: 4.24.2 chalk: 4.1.2 - cross-spawn: 7.0.3 + cross-spawn: 7.0.6 detect-port-alt: 1.1.6 escape-string-regexp: 4.0.0 filesize: 8.0.7 find-up: 5.0.0 - fork-ts-checker-webpack-plugin: 6.5.3(eslint@8.38.0)(typescript@4.9.5)(webpack@5.78.0) + fork-ts-checker-webpack-plugin: 6.5.3(eslint@8.57.1)(typescript@5.6.3)(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))) global-modules: 2.0.0 globby: 11.1.0 gzip-size: 6.0.0 immer: 9.0.21 is-root: 2.1.0 - loader-utils: 3.2.1 + loader-utils: 3.3.1 open: 8.4.2 pkg-up: 3.1.0 prompts: 2.4.2 - react-error-overlay: 6.1.0-next.14 + react-error-overlay: 6.0.11 recursive-readdir: 2.2.3 shell-quote: 1.8.1 strip-ansi: 6.0.1 text-table: 0.2.0 + webpack: 5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(esbuild@0.25.6) + optionalDependencies: + typescript: 5.6.3 transitivePeerDependencies: - eslint - supports-color - - typescript - vue-template-compiler - - webpack - dev: false - /react-dom@18.2.0(react@18.2.0): - resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==} - peerDependencies: - react: ^18.2.0 + react-dom@18.3.1(react@18.3.1): dependencies: loose-envify: 1.4.0 - react: 18.2.0 - scheduler: 0.23.0 + react: 18.3.1 + scheduler: 0.23.2 - /react-error-overlay@6.1.0-next.14: - resolution: {integrity: sha512-s6V1MF7/hheDh0WbbauMYu62H6z5n9PRPgUO/6RFm/V0srHcCO0gtBlz3AwkhldcQRcMaYAAkLO5o+6rTO/xkA==} - dev: false + react-dom@19.0.0(react@18.3.1): + dependencies: + react: 18.3.1 + scheduler: 0.25.0 - /react-icons@4.8.0(react@18.2.0): - resolution: {integrity: sha512-N6+kOLcihDiAnj5Czu637waJqSnwlMNROzVZMhfX68V/9bu9qHaMIJC4UdozWoOk57gahFCNHwVvWzm0MTzRjg==} - peerDependencies: - react: '*' + react-dom@19.0.0(react@19.0.0): dependencies: - react: 18.2.0 - dev: false + react: 19.0.0 + scheduler: 0.25.0 - /react-is@16.13.1: - resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + react-email@4.1.3: + dependencies: + '@babel/parser': 7.28.0 + '@babel/traverse': 7.28.0 + chalk: 5.3.0 + chokidar: 4.0.3 + commander: 13.1.0 + debounce: 2.0.0 + esbuild: 0.25.6 + glob: 11.0.3 + jiti: 2.4.2 + log-symbols: 7.0.1 + mime-types: 3.0.1 + normalize-path: 3.0.0 + nypm: 0.6.0 + ora: 8.2.0 + prompts: 2.4.2 + socket.io: 4.8.1 + tsconfig-paths: 4.2.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate - /react-is@17.0.2: - resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + react-error-overlay@6.0.11: {} - /react-is@18.2.0: - resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} + react-icons@4.12.0(react@18.3.1): + dependencies: + react: 18.3.1 - /react-json-view@1.21.3(@types/react@18.0.28)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-13p8IREj9/x/Ye4WI/JpjhoIwuzEgUAtgJZNBJckfzJt1qyh24BdTm6UQNGnyTq9dapQdrqvquZTo3dz1X6Cjw==} - peerDependencies: - react: ^17.0.0 || ^16.3.0 || ^15.5.4 - react-dom: ^17.0.0 || ^16.3.0 || ^15.5.4 + react-is@16.13.1: {} + + react-is@17.0.2: {} + + react-is@18.3.1: {} + + react-json-view@1.21.3(@types/react@18.3.3)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - flux: 4.0.4(react@18.2.0) - react: 18.2.0 + flux: 4.0.4(encoding@0.1.13)(react@18.3.1) + react: 18.3.1 react-base16-styling: 0.6.0 - react-dom: 18.2.0(react@18.2.0) + react-dom: 18.3.1(react@18.3.1) react-lifecycles-compat: 3.0.4 - react-textarea-autosize: 8.4.1(@types/react@18.0.28)(react@18.2.0) + react-textarea-autosize: 8.5.4(@types/react@18.3.3)(react@18.3.1) transitivePeerDependencies: - '@types/react' - encoding - dev: false - /react-lifecycles-compat@3.0.4: - resolution: {integrity: sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==} - dev: false + react-lifecycles-compat@3.0.4: {} - /react-reconciler@0.26.2(react@18.2.0): - resolution: {integrity: sha512-nK6kgY28HwrMNwDnMui3dvm3rCFjZrcGiuwLc5COUipBK5hWHLOxMJhSnSomirqWwjPBJKV1QcbkI0VJr7Gl1Q==} - engines: {node: '>=0.10.0'} - peerDependencies: - react: ^17.0.2 + react-promise-suspense@0.3.4: + dependencies: + fast-deep-equal: 2.0.1 + + react-reconciler@0.26.2(react@18.3.1): dependencies: loose-envify: 1.4.0 object-assign: 4.1.1 - react: 18.2.0 + react: 18.3.1 scheduler: 0.20.2 - dev: false - /react-refresh@0.11.0: - resolution: {integrity: sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==} - engines: {node: '>=0.10.0'} - dev: false + react-refresh@0.11.0: {} - /react-refresh@0.14.0: - resolution: {integrity: sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==} - engines: {node: '>=0.10.0'} - dev: true + react-remove-scroll-bar@2.3.8(@types/react@19.0.10)(react@19.0.0): + dependencies: + react: 19.0.0 + react-style-singleton: 2.2.3(@types/react@19.0.10)(react@19.0.0) + tslib: 2.8.0 + optionalDependencies: + '@types/react': 19.0.10 - /react-router-dom@6.10.0(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-E5dfxRPuXKJqzwSe/qGcqdwa18QiWC6f3H3cWXM24qj4N0/beCIf/CWTipop2xm7mR0RCS99NnaqPNjHtrAzCg==} - engines: {node: '>=14'} - peerDependencies: - react: '>=16.8' - react-dom: '>=16.8' + react-remove-scroll@2.7.1(@types/react@19.0.10)(react@19.0.0): dependencies: - '@remix-run/router': 1.5.0 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - react-router: 6.10.0(react@18.2.0) - dev: false + react: 19.0.0 + react-remove-scroll-bar: 2.3.8(@types/react@19.0.10)(react@19.0.0) + react-style-singleton: 2.2.3(@types/react@19.0.10)(react@19.0.0) + tslib: 2.8.0 + use-callback-ref: 1.3.3(@types/react@19.0.10)(react@19.0.0) + use-sidecar: 1.1.3(@types/react@19.0.10)(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.10 - /react-router@6.10.0(react@18.2.0): - resolution: {integrity: sha512-Nrg0BWpQqrC3ZFFkyewrflCud9dio9ME3ojHCF/WLsprJVzkq3q3UeEhMCAW1dobjeGbWgjNn/PVF6m46ANxXQ==} - engines: {node: '>=14'} - peerDependencies: - react: '>=16.8' + react-router-dom@6.27.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - '@remix-run/router': 1.5.0 - react: 18.2.0 - dev: false + '@remix-run/router': 1.20.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-router: 6.27.0(react@18.3.1) - /react-scripts@5.1.0-next.14(@babel/plugin-syntax-flow@7.21.4)(@babel/plugin-transform-react-jsx@7.21.0)(esbuild@0.16.3)(react@18.2.0)(ts-node@10.8.2)(typescript@4.9.5): - resolution: {integrity: sha512-c4Z8eO8nNm6LWDzovOm5gHcL1+eqo5Ll2FqKnfpydAGfEbPhxMWDau8NAg0d/QZ5jWjj9GgRoet5q9nqn9N/vQ==} - engines: {node: '>=14.0.0'} - hasBin: true - peerDependencies: - react: '>= 16' - typescript: ^3.2.1 || ^4 - peerDependenciesMeta: - typescript: - optional: true + react-router-dom@6.27.0(react-dom@19.0.0(react@18.3.1))(react@18.3.1): dependencies: - '@babel/core': 7.21.4 - '@pmmmwh/react-refresh-webpack-plugin': 0.5.10(react-refresh@0.11.0)(webpack-dev-server@4.13.2)(webpack@5.78.0) + '@remix-run/router': 1.20.0 + react: 18.3.1 + react-dom: 19.0.0(react@18.3.1) + react-router: 6.27.0(react@18.3.1) + + react-router@6.27.0(react@18.3.1): + dependencies: + '@remix-run/router': 1.20.0 + react: 18.3.1 + + react-scripts@5.0.1(@babel/plugin-syntax-flow@7.26.0(@babel/core@7.26.0))(@babel/plugin-transform-react-jsx@7.25.9(@babel/core@7.26.0))(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/babel__core@7.20.5)(@types/webpack@5.28.5(@swc/core@1.11.10(@swc/helpers@0.5.15)))(eslint@8.57.1)(react@18.3.1)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3))(type-fest@3.13.1)(typescript@5.6.3)(webpack-hot-middleware@2.26.1): + dependencies: + '@babel/core': 7.26.0 + '@pmmmwh/react-refresh-webpack-plugin': 0.5.15(@types/webpack@5.28.5(@swc/core@1.11.10(@swc/helpers@0.5.15)))(react-refresh@0.11.0)(type-fest@3.13.1)(webpack-dev-server@4.15.2(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))))(webpack-hot-middleware@2.26.1)(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))) '@svgr/webpack': 5.5.0 - babel-jest: 27.5.1(@babel/core@7.21.4) - babel-loader: 8.3.0(@babel/core@7.21.4)(webpack@5.78.0) - babel-plugin-named-asset-import: 0.4.0-next.14(@babel/core@7.21.4) - babel-preset-react-app: 10.1.0-next.14 - bfj: 7.0.2 - browserslist: 4.21.5 + babel-jest: 27.5.1(@babel/core@7.26.0) + babel-loader: 8.4.1(@babel/core@7.26.0)(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))) + babel-plugin-named-asset-import: 0.3.8(@babel/core@7.26.0) + babel-preset-react-app: 10.0.1 + bfj: 7.1.0 + browserslist: 4.24.2 camelcase: 6.3.0 case-sensitive-paths-webpack-plugin: 2.4.0 - css-loader: 6.7.3(webpack@5.78.0) - css-minimizer-webpack-plugin: 3.4.1(esbuild@0.16.3)(webpack@5.78.0) + css-loader: 6.11.0(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))) + css-minimizer-webpack-plugin: 3.4.1(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))) dotenv: 10.0.0 dotenv-expand: 5.1.0 - eslint: 8.38.0 - eslint-config-react-app: 7.1.0-next.14(@babel/plugin-syntax-flow@7.21.4)(@babel/plugin-transform-react-jsx@7.21.0)(eslint@8.38.0)(jest@27.5.1)(typescript@4.9.5) - eslint-webpack-plugin: 3.2.0(eslint@8.38.0)(webpack@5.78.0) - file-loader: 6.2.0(webpack@5.78.0) + eslint: 8.57.1 + eslint-config-react-app: 7.0.1(@babel/plugin-syntax-flow@7.26.0(@babel/core@7.26.0))(@babel/plugin-transform-react-jsx@7.25.9(@babel/core@7.26.0))(eslint@8.57.1)(jest@27.5.1(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)))(typescript@5.6.3) + eslint-webpack-plugin: 3.2.0(eslint@8.57.1)(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))) + file-loader: 6.2.0(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))) fs-extra: 10.1.0 - html-webpack-plugin: 5.5.0(webpack@5.78.0) + html-webpack-plugin: 5.6.3(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))) identity-obj-proxy: 3.0.0 - jest: 27.5.1(ts-node@10.8.2) + jest: 27.5.1(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)) jest-resolve: 27.5.1 - jest-watch-typeahead: 1.1.0(jest@27.5.1) - mini-css-extract-plugin: 2.7.5(webpack@5.78.0) - postcss: 8.4.21 - postcss-flexbugs-fixes: 5.0.2(postcss@8.4.21) - postcss-loader: 6.2.1(postcss@8.4.21)(webpack@5.78.0) - postcss-normalize: 10.0.1(browserslist@4.21.5)(postcss@8.4.21) - postcss-preset-env: 7.8.3(postcss@8.4.21) + jest-watch-typeahead: 1.1.0(jest@27.5.1(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3))) + mini-css-extract-plugin: 2.9.1(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))) + postcss: 8.4.47 + postcss-flexbugs-fixes: 5.0.2(postcss@8.4.47) + postcss-loader: 6.2.1(postcss@8.4.47)(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))) + postcss-normalize: 10.0.1(browserslist@4.24.2)(postcss@8.4.47) + postcss-preset-env: 7.8.3(postcss@8.4.47) prompts: 2.4.2 - react: 18.2.0 - react-app-polyfill: 3.1.0-next.14 - react-dev-utils: 12.1.0-next.14(eslint@8.38.0)(typescript@4.9.5)(webpack@5.78.0) + react: 18.3.1 + react-app-polyfill: 3.0.0 + react-dev-utils: 12.0.1(eslint@8.57.1)(typescript@5.6.3)(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))) react-refresh: 0.11.0 - resolve: 1.22.2 + resolve: 1.22.8 resolve-url-loader: 4.0.0 - sass-loader: 12.6.0(webpack@5.78.0) - semver: 7.4.0 - source-map-loader: 3.0.2(webpack@5.78.0) - style-loader: 3.3.2(webpack@5.78.0) - tailwindcss: 3.3.1(postcss@8.4.21)(ts-node@10.8.2) - terser-webpack-plugin: 5.3.7(esbuild@0.16.3)(webpack@5.78.0) - typescript: 4.9.5 - webpack: 5.78.0(esbuild@0.16.3)(webpack-cli@5.0.1) - webpack-dev-server: 4.13.2(webpack@5.78.0) - webpack-manifest-plugin: 4.1.1(webpack@5.78.0) - workbox-webpack-plugin: 6.5.4(webpack@5.78.0) + sass-loader: 12.6.0(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))) + semver: 7.6.3 + source-map-loader: 3.0.2(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))) + style-loader: 3.3.4(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))) + tailwindcss: 3.4.14(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)) + terser-webpack-plugin: 5.3.10(@swc/core@1.11.10(@swc/helpers@0.5.15))(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))) + webpack: 5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(esbuild@0.25.6) + webpack-dev-server: 4.15.2(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))) + webpack-manifest-plugin: 4.1.1(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))) + workbox-webpack-plugin: 6.6.0(@types/babel__core@7.20.5)(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))) optionalDependencies: - fsevents: 2.3.2 + fsevents: 2.3.3 + typescript: 5.6.3 transitivePeerDependencies: - '@babel/plugin-syntax-flow' - '@babel/plugin-transform-react-jsx' - '@parcel/css' + - '@rspack/core' - '@swc/core' - '@types/babel__core' - '@types/webpack' @@ -22299,37 +30676,47 @@ packages: - webpack-cli - webpack-hot-middleware - webpack-plugin-serve - dev: false - /react-textarea-autosize@8.4.1(@types/react@18.0.28)(react@18.2.0): - resolution: {integrity: sha512-aD2C+qK6QypknC+lCMzteOdIjoMbNlgSFmJjCV+DrfTPwp59i/it9mMNf2HDzvRjQgKAyBDPyLJhcrzElf2U4Q==} - engines: {node: '>=10'} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-smooth@4.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + fast-equals: 5.0.1 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-transition-group: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + + react-style-singleton@2.2.3(@types/react@19.0.10)(react@19.0.0): dependencies: - '@babel/runtime': 7.21.0 - react: 18.2.0 - use-composed-ref: 1.3.0(react@18.2.0) - use-latest: 1.2.1(@types/react@18.0.28)(react@18.2.0) + get-nonce: 1.0.1 + react: 19.0.0 + tslib: 2.8.0 + optionalDependencies: + '@types/react': 19.0.10 + + react-textarea-autosize@8.5.4(@types/react@18.3.3)(react@18.3.1): + dependencies: + '@babel/runtime': 7.26.0 + react: 18.3.1 + use-composed-ref: 1.3.0(react@18.3.1) + use-latest: 1.2.1(@types/react@18.3.3)(react@18.3.1) transitivePeerDependencies: - '@types/react' - dev: false - /react-universal-interface@0.6.2(react@18.2.0)(tslib@2.5.0): - resolution: {integrity: sha512-dg8yXdcQmvgR13RIlZbTRQOoUrDciFVoSBZILwjE2LFISxZZ8loVJKAkuzswl5js8BHda79bIb2b84ehU8IjXw==} - peerDependencies: - react: '*' - tslib: '*' + react-transition-group@4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.26.0 + dom-helpers: 5.2.1 + loose-envify: 1.4.0 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + react-universal-interface@0.6.2(react@18.3.1)(tslib@2.8.0): dependencies: - react: 18.2.0 - tslib: 2.5.0 - dev: false + react: 18.3.1 + tslib: 2.8.0 - /react-use@17.4.0(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-TgbNTCA33Wl7xzIJegn1HndB4qTS9u03QUwyNycUnXaweZkE4Kq2SB+Yoxx8qbshkZGYBDvUXbXWRUmQDcZZ/Q==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-use@17.5.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@types/js-cookie': 2.2.7 '@xobotyi/scrollbar-width': 1.9.5 @@ -22337,68 +30724,60 @@ packages: fast-deep-equal: 3.1.3 fast-shallow-equal: 1.0.0 js-cookie: 2.2.1 - nano-css: 5.3.5(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - react-universal-interface: 0.6.2(react@18.2.0)(tslib@2.5.0) + nano-css: 5.6.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-universal-interface: 0.6.2(react@18.3.1)(tslib@2.8.0) resize-observer-polyfill: 1.5.1 screenfull: 5.2.0 set-harmonic-interval: 1.0.1 throttle-debounce: 3.0.1 ts-easing: 0.2.0 - tslib: 2.5.0 - dev: false + tslib: 2.8.0 - /react@18.2.0: - resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} - engines: {node: '>=0.10.0'} + react@18.3.1: dependencies: loose-envify: 1.4.0 - /read-cache@1.0.0: - resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + react@19.0.0: {} + + read-cache@1.0.0: dependencies: pify: 2.3.0 - /read-cmd-shim@3.0.1: - resolution: {integrity: sha512-kEmDUoYf/CDy8yZbLTmhB1X9kkjf9Q80PCNsDMb7ufrGd6zZSQA1+UyjrO+pZm5K/S4OXCWJeiIt1JA8kAsa6g==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + read-cmd-shim@3.0.1: {} - /read-package-json-fast@2.0.3: - resolution: {integrity: sha512-W/BKtbL+dUjTuRL2vziuYhp76s5HZ9qQhd/dKfWIZveD0O40453QNyZhC0e63lqZrAQ4jiOapVoeJ7JrszenQQ==} - engines: {node: '>=10'} + read-package-json-fast@2.0.3: dependencies: json-parse-even-better-errors: 2.3.1 npm-normalize-package-bin: 1.0.1 - /read-pkg-up@7.0.1: - resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==} - engines: {node: '>=8'} + read-package-json-fast@3.0.2: + dependencies: + json-parse-even-better-errors: 3.0.2 + npm-normalize-package-bin: 3.0.1 + + read-package-json@6.0.4: + dependencies: + glob: 10.4.5 + json-parse-even-better-errors: 3.0.2 + normalize-package-data: 5.0.0 + npm-normalize-package-bin: 3.0.1 + + read-pkg-up@7.0.1: dependencies: find-up: 4.1.0 read-pkg: 5.2.0 type-fest: 0.8.1 - /read-pkg@5.2.0: - resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==} - engines: {node: '>=8'} + read-pkg@5.2.0: dependencies: - '@types/normalize-package-data': 2.4.1 + '@types/normalize-package-data': 2.4.4 normalize-package-data: 2.5.0 parse-json: 5.2.0 type-fest: 0.6.0 - /readable-stream@1.1.14: - resolution: {integrity: sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==} - dependencies: - core-util-is: 1.0.3 - inherits: 2.0.4 - isarray: 0.0.1 - string_decoder: 0.10.31 - dev: true - - /readable-stream@2.3.8: - resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + readable-stream@2.3.8: dependencies: core-util-is: 1.0.3 inherits: 2.0.4 @@ -22408,732 +30787,455 @@ packages: string_decoder: 1.1.1 util-deprecate: 1.0.2 - /readable-stream@3.6.2: - resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} - engines: {node: '>= 6'} + readable-stream@3.6.2: dependencies: inherits: 2.0.4 string_decoder: 1.3.0 util-deprecate: 1.0.2 - /readdir-glob@1.1.3: - resolution: {integrity: sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==} + readable-stream@4.5.2: + dependencies: + abort-controller: 3.0.0 + buffer: 6.0.3 + events: 3.3.0 + process: 0.11.10 + string_decoder: 1.3.0 + + readdir-glob@1.1.3: dependencies: minimatch: 5.1.6 - dev: true - /readdir-scoped-modules@1.1.0: - resolution: {integrity: sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw==} - deprecated: This functionality has been moved to @npmcli/fs + readdir-scoped-modules@1.1.0: dependencies: debuglog: 1.0.1 dezalgo: 1.0.4 graceful-fs: 4.2.11 once: 1.4.0 - /readdirp@3.6.0: - resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} - engines: {node: '>=8.10.0'} + readdirp@3.6.0: dependencies: picomatch: 2.3.1 - /recast@0.21.5: - resolution: {integrity: sha512-hjMmLaUXAm1hIuTqOdeYObMslq/q+Xff6QE3Y2P+uoHAg2nmVlLBps2hzh1UJDdMtDTMXOFewK6ky51JQIeECg==} - engines: {node: '>= 4'} + readdirp@4.1.2: {} + + recharts-scale@0.4.5: dependencies: - ast-types: 0.15.2 - esprima: 4.0.1 - source-map: 0.6.1 - tslib: 2.5.0 - dev: true + decimal.js-light: 2.5.1 - /rechoir@0.6.2: - resolution: {integrity: sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==} - engines: {node: '>= 0.10'} + recharts@2.13.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + clsx: 2.1.1 + eventemitter3: 4.0.7 + lodash: 4.17.21 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-is: 18.3.1 + react-smooth: 4.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + recharts-scale: 0.4.5 + tiny-invariant: 1.3.3 + victory-vendor: 36.9.2 + + rechoir@0.6.2: dependencies: - resolve: 1.22.2 + resolve: 1.22.8 - /rechoir@0.8.0: - resolution: {integrity: sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==} - engines: {node: '>= 10.13.0'} + rechoir@0.8.0: dependencies: - resolve: 1.22.2 + resolve: 1.22.8 - /recursive-readdir@2.2.3: - resolution: {integrity: sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==} - engines: {node: '>=6.0.0'} + recursive-readdir@2.2.3: dependencies: minimatch: 3.1.2 - dev: false - /redent@3.0.0: - resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} - engines: {node: '>=8'} + redent@3.0.0: dependencies: indent-string: 4.0.0 strip-indent: 3.0.0 - dev: true - /redis-errors@1.2.0: - resolution: {integrity: sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==} - engines: {node: '>=4'} + redis-errors@1.2.0: {} - /redis-parser@3.0.0: - resolution: {integrity: sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==} - engines: {node: '>=4'} + redis-parser@3.0.0: dependencies: redis-errors: 1.2.0 - /regenerate-unicode-properties@10.1.0: - resolution: {integrity: sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==} - engines: {node: '>=4'} + reflect.getprototypeof@1.0.6: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 + globalthis: 1.0.4 + which-builtin-type: 1.1.4 + + regenerate-unicode-properties@10.2.0: dependencies: regenerate: 1.4.2 - /regenerate@1.4.2: - resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==} + regenerate@1.4.2: {} - /regenerator-runtime@0.10.5: - resolution: {integrity: sha512-02YopEIhAgiBHWeoTiA8aitHDt8z6w+rQqNuIftlM+ZtvSl/brTouaU7DW6GO/cHtvxJvS4Hwv2ibKdxIRi24w==} - dev: false + regenerator-runtime@0.10.5: {} - /regenerator-runtime@0.13.11: - resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==} + regenerator-runtime@0.13.11: {} - /regenerator-transform@0.15.1: - resolution: {integrity: sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==} + regenerator-runtime@0.14.1: {} + + regenerator-transform@0.15.2: dependencies: - '@babel/runtime': 7.21.0 + '@babel/runtime': 7.26.0 - /regex-parser@2.2.11: - resolution: {integrity: sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==} - dev: false + regex-parser@2.3.0: {} - /regexp.prototype.flags@1.4.3: - resolution: {integrity: sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==} - engines: {node: '>= 0.4'} + regexp.prototype.flags@1.5.3: dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - functions-have-names: 1.2.3 - - /regexpp@3.2.0: - resolution: {integrity: sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==} - engines: {node: '>=8'} + call-bind: 1.0.7 + define-properties: 1.2.1 + es-errors: 1.3.0 + set-function-name: 2.0.2 - /regexpu-core@5.3.2: - resolution: {integrity: sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==} - engines: {node: '>=4'} + regexpu-core@6.1.1: dependencies: - '@babel/regjsgen': 0.8.0 regenerate: 1.4.2 - regenerate-unicode-properties: 10.1.0 - regjsparser: 0.9.1 + regenerate-unicode-properties: 10.2.0 + regjsgen: 0.8.0 + regjsparser: 0.11.2 unicode-match-property-ecmascript: 2.0.0 - unicode-match-property-value-ecmascript: 2.1.0 - - /regjsparser@0.9.1: - resolution: {integrity: sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==} - hasBin: true - dependencies: - jsesc: 0.5.0 - - /relateurl@0.2.7: - resolution: {integrity: sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==} - engines: {node: '>= 0.10'} - dev: false - - /remark-frontmatter@4.0.1: - resolution: {integrity: sha512-38fJrB0KnmD3E33a5jZC/5+gGAC2WKNiPw1/fdXJvijBlhA7RCsvJklrYJakS0HedninvaCYW8lQGf9C918GfA==} - dependencies: - '@types/mdast': 3.0.11 - mdast-util-frontmatter: 1.0.1 - micromark-extension-frontmatter: 1.1.0 - unified: 10.1.2 - dev: true + unicode-match-property-value-ecmascript: 2.2.0 - /remark-mdx-frontmatter@1.1.1: - resolution: {integrity: sha512-7teX9DW4tI2WZkXS4DBxneYSY7NHiXl4AKdWDO9LXVweULlCT8OPWsOjLEnMIXViN1j+QcY8mfbq3k0EK6x3uA==} - engines: {node: '>=12.2.0'} - dependencies: - estree-util-is-identifier-name: 1.1.0 - estree-util-value-to-estree: 1.3.0 - js-yaml: 4.1.0 - toml: 3.0.0 - dev: true + regjsgen@0.8.0: {} - /remark-parse@10.0.1: - resolution: {integrity: sha512-1fUyHr2jLsVOkhbvPRBJ5zTKZZyD6yZzYaWCS6BPBdQ8vEMBCH+9zNCDA6tET/zHCi/jLqjCWtlJZUPk+DbnFw==} + regjsparser@0.11.2: dependencies: - '@types/mdast': 3.0.11 - mdast-util-from-markdown: 1.3.0 - unified: 10.1.2 - transitivePeerDependencies: - - supports-color - dev: true + jsesc: 3.0.2 - /remark-rehype@9.1.0: - resolution: {integrity: sha512-oLa6YmgAYg19zb0ZrBACh40hpBLteYROaPLhBXzLgjqyHQrN+gVP9N/FJvfzuNNuzCutktkroXEZBrxAxKhh7Q==} - dependencies: - '@types/hast': 2.3.4 - '@types/mdast': 3.0.11 - mdast-util-to-hast: 11.3.0 - unified: 10.1.2 - dev: true + relateurl@0.2.7: {} - /remove-trailing-separator@1.1.0: - resolution: {integrity: sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==} + remove-trailing-separator@1.1.0: {} - /renderkid@3.0.0: - resolution: {integrity: sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==} + renderkid@3.0.0: dependencies: css-select: 4.3.0 dom-converter: 0.2.0 htmlparser2: 6.1.0 lodash: 4.17.21 strip-ansi: 6.0.1 - dev: false - /replace-ext@1.0.1: - resolution: {integrity: sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==} - engines: {node: '>= 0.10'} - - /require-directory@2.1.1: - resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} - engines: {node: '>=0.10.0'} + replace-ext@1.0.1: {} - /require-from-string@2.0.2: - resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} - engines: {node: '>=0.10.0'} + require-directory@2.1.1: {} - /require-like@0.1.2: - resolution: {integrity: sha512-oyrU88skkMtDdauHDuKVrgR+zuItqr6/c//FXzvmxRGMexSDc6hNvJInGW3LL46n+8b50RykrvwSUIIQH2LQ5A==} - dev: true + require-from-string@2.0.2: {} - /requires-port@1.0.0: - resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} - dev: false + requires-port@1.0.0: {} - /requizzle@0.2.4: - resolution: {integrity: sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw==} + resend@4.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - lodash: 4.17.21 - dev: false - optional: true - - /resize-observer-polyfill@1.5.1: - resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==} + '@react-email/render': 0.0.17(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + transitivePeerDependencies: + - react + - react-dom - /resolve-alpn@1.2.1: - resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} - dev: true + resize-observer-polyfill@1.5.1: {} - /resolve-cwd@3.0.0: - resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} - engines: {node: '>=8'} + resolve-cwd@3.0.0: dependencies: resolve-from: 5.0.0 - /resolve-from@3.0.0: - resolution: {integrity: sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==} - engines: {node: '>=4'} - dev: true + resolve-from@4.0.0: {} - /resolve-from@4.0.0: - resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} - engines: {node: '>=4'} + resolve-from@5.0.0: {} - /resolve-from@5.0.0: - resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} - engines: {node: '>=8'} + resolve-pkg-maps@1.0.0: {} - /resolve-url-loader@4.0.0: - resolution: {integrity: sha512-05VEMczVREcbtT7Bz+C+96eUO5HDNvdthIiMB34t7FcF8ehcu4wC0sSgPUubs3XW2Q3CNLJk/BJrCU9wVRymiA==} - engines: {node: '>=8.9'} - peerDependencies: - rework: 1.0.1 - rework-visit: 1.0.0 - peerDependenciesMeta: - rework: - optional: true - rework-visit: - optional: true + resolve-url-loader@4.0.0: dependencies: adjust-sourcemap-loader: 4.0.0 convert-source-map: 1.9.0 loader-utils: 2.0.4 - postcss: 7.0.39 + postcss: 8.4.47 source-map: 0.6.1 - dev: false - - /resolve.exports@1.1.1: - resolution: {integrity: sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ==} - engines: {node: '>=10'} - /resolve.exports@2.0.2: - resolution: {integrity: sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==} - engines: {node: '>=10'} - dev: true + resolve.exports@1.1.1: {} - /resolve@1.12.0: - resolution: {integrity: sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==} - dependencies: - path-parse: 1.0.7 - dev: true + resolve.exports@2.0.2: {} - /resolve@1.22.2: - resolution: {integrity: sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==} - hasBin: true + resolve@1.22.8: dependencies: - is-core-module: 2.12.0 + is-core-module: 2.15.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - /resolve@2.0.0-next.4: - resolution: {integrity: sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==} - hasBin: true + resolve@2.0.0-next.5: dependencies: - is-core-module: 2.12.0 + is-core-module: 2.15.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - /responselike@2.0.1: - resolution: {integrity: sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==} - dependencies: - lowercase-keys: 2.0.0 - dev: true - - /restore-cursor@3.1.0: - resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} - engines: {node: '>=8'} + restore-cursor@3.1.0: dependencies: onetime: 5.1.2 signal-exit: 3.0.7 - /retry-request@5.0.2: - resolution: {integrity: sha512-wfI3pk7EE80lCIXprqh7ym48IHYdwmAAzESdbU8Q9l7pnRCk9LEhpbOTNKjz6FARLm/Bl5m+4F0ABxOkYUujSQ==} - engines: {node: '>=12'} + restore-cursor@5.1.0: dependencies: - debug: 4.3.4 - extend: 3.0.2 - transitivePeerDependencies: - - supports-color - dev: false - optional: true - - /retry@0.12.0: - resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} - engines: {node: '>= 4'} + onetime: 7.0.0 + signal-exit: 4.1.0 - /retry@0.13.1: - resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} - engines: {node: '>= 4'} - dev: false + retry-request@7.0.2(encoding@0.1.13): + dependencies: + '@types/request': 2.48.12 + extend: 3.0.2 + teeny-request: 9.0.0(encoding@0.1.13) + transitivePeerDependencies: + - encoding + - supports-color - /reusify@1.0.4: - resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + retry@0.12.0: {} - /rgb-regex@1.0.1: - resolution: {integrity: sha512-gDK5mkALDFER2YLqH6imYvK6g02gpNGM4ILDZ472EwWfXZnC2ZEpoB2ECXTyOVUKuk/bPJZMzwQPBYICzP+D3w==} - dev: true + retry@0.13.1: {} - /rgba-regex@1.0.0: - resolution: {integrity: sha512-zgn5OjNQXLUTdq8m17KdaicF6w89TZs8ZU8y0AYENIU6wG8GG6LLm0yLSiPY8DmaYmHdgRW8rnApjoT0fQRfMg==} - dev: true + reusify@1.0.4: {} - /rimraf@2.7.1: - resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} - hasBin: true + rimraf@2.7.1: dependencies: glob: 7.2.3 - dev: true - /rimraf@3.0.2: - resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} - hasBin: true + rimraf@3.0.2: dependencies: glob: 7.2.3 - /rollup-plugin-babel@4.4.0(@babel/core@7.21.4)(rollup@1.32.1): - resolution: {integrity: sha512-Lek/TYp1+7g7I+uMfJnnSJ7YWoD58ajo6Oarhlex7lvUce+RCKRuGRSgztDO3/MF/PuGKmUL5iTHKf208UNszw==} - deprecated: This package has been deprecated and is no longer maintained. Please use @rollup/plugin-babel. - peerDependencies: - '@babel/core': 7 || ^7.0.0-rc.2 - rollup: '>=0.60.0 <3' + rimraf@5.0.10: dependencies: - '@babel/core': 7.21.4 - '@babel/helper-module-imports': 7.21.4 - rollup: 1.32.1 - rollup-pluginutils: 2.8.2 - dev: true + glob: 10.4.5 - /rollup-plugin-bundle-size@1.0.3: - resolution: {integrity: sha512-aWj0Pvzq90fqbI5vN1IvUrlf4utOqy+AERYxwWjegH1G8PzheMnrRIgQ5tkwKVtQMDP0bHZEACW/zLDF+XgfXQ==} + rollup-plugin-bundle-size@1.0.3: dependencies: chalk: 1.1.3 maxmin: 2.1.0 - dev: true - - /rollup-plugin-es3@1.1.0: - resolution: {integrity: sha512-jTMqQgMZ/tkjRW4scf4ln5c0OiTSi+Lx/IEyFd41ldgGoLvvg9AQxmVOl93+KaoyB7XRYToYjiHDvO40NPF/fA==} - dependencies: - magic-string: 0.22.5 - dev: true - - /rollup-plugin-inject@3.0.2: - resolution: {integrity: sha512-ptg9PQwzs3orn4jkgXJ74bfs5vYz1NCZlSQMBUA0wKcGp5i5pA1AO3fOUEte8enhGUC+iapTCzEWw2jEFFUO/w==} - deprecated: This package has been deprecated and is no longer maintained. Please use @rollup/plugin-inject. - dependencies: - estree-walker: 0.6.1 - magic-string: 0.25.9 - rollup-pluginutils: 2.8.2 - dev: true - /rollup-plugin-node-polyfills@0.2.1: - resolution: {integrity: sha512-4kCrKPTJ6sK4/gLL/U5QzVT8cxJcofO0OU74tnB19F40cmuAKSzH5/siithxlofFEjwvw1YAhPmbvGNA6jEroA==} + rollup-plugin-dts@6.2.1(rollup@3.29.5)(typescript@5.6.3): dependencies: - rollup-plugin-inject: 3.0.2 - dev: true + magic-string: 0.30.17 + rollup: 3.29.5 + typescript: 5.6.3 + optionalDependencies: + '@babel/code-frame': 7.26.2 - /rollup-plugin-postcss@2.9.0: - resolution: {integrity: sha512-Y7qDwlqjZMBexbB1kRJf+jKIQL8HR6C+ay53YzN+nNJ64hn1PNZfBE3c61hFUhD//zrMwmm7uBW30RuTi+CD0w==} - engines: {node: '>=10'} + rollup-plugin-postcss@4.0.2(postcss@8.4.47)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)): dependencies: chalk: 4.1.2 concat-with-sourcemaps: 1.1.0 - cssnano: 4.1.11 + cssnano: 5.1.15(postcss@8.4.47) import-cwd: 3.0.0 p-queue: 6.6.2 pify: 5.0.0 - postcss: 7.0.39 - postcss-load-config: 2.1.2 - postcss-modules: 2.0.0 + postcss: 8.4.47 + postcss-load-config: 3.1.4(postcss@8.4.47)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)) + postcss-modules: 4.3.1(postcss@8.4.47) promise.series: 0.2.0 - resolve: 1.22.2 + resolve: 1.22.8 rollup-pluginutils: 2.8.2 safe-identifier: 0.4.2 style-inject: 0.3.0 - dev: true - - /rollup-plugin-smart-asset@2.1.2(rollup@1.32.1): - resolution: {integrity: sha512-Y/gD3abliw3F20uY6ILpmH6sokXKntuTFKvDdLEJxHFcyQRsTb12u2KbsapqPZDa4JN/Nxx7BslYlZ/KmYAtPg==} - peerDependencies: - rollup: '>0.60' - dependencies: - big.js: 6.2.1 - magic-string: 0.25.9 - mime: 2.6.0 - mkdirp: 1.0.4 - rollup: 1.32.1 - rollup-pluginutils: 2.8.2 - dev: true - - /rollup-plugin-terser@5.3.1(rollup@1.32.1): - resolution: {integrity: sha512-1pkwkervMJQGFYvM9nscrUoncPwiKR/K+bHdjv6PFgRo3cgPHoRT83y2Aa3GvINj4539S15t/tpFPb775TDs6w==} - deprecated: This package has been deprecated and is no longer maintained. Please use @rollup/plugin-terser - peerDependencies: - rollup: '>=0.66.0 <3' - dependencies: - '@babel/code-frame': 7.21.4 - jest-worker: 24.9.0 - rollup: 1.32.1 - rollup-pluginutils: 2.8.2 - serialize-javascript: 4.0.0 - terser: 4.8.1 - dev: true + transitivePeerDependencies: + - ts-node - /rollup-plugin-terser@7.0.2(rollup@2.79.1): - resolution: {integrity: sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==} - deprecated: This package has been deprecated and is no longer maintained. Please use @rollup/plugin-terser - peerDependencies: - rollup: ^2.0.0 + rollup-plugin-terser@7.0.2(rollup@2.79.2): dependencies: - '@babel/code-frame': 7.21.4 + '@babel/code-frame': 7.26.0 jest-worker: 26.6.2 - rollup: 2.79.1 + rollup: 2.79.2 serialize-javascript: 4.0.0 - terser: 5.16.9 - dev: false + terser: 5.36.0 - /rollup-plugin-typescript2@0.25.3(rollup@1.32.1)(typescript@3.9.10): - resolution: {integrity: sha512-ADkSaidKBovJmf5VBnZBZe+WzaZwofuvYdzGAKTN/J4hN7QJCFYAq7IrH9caxlru6T5qhX41PNFS1S4HqhsGQg==} - peerDependencies: - rollup: '>=1.26.3' - typescript: '>=2.4.0' + rollup-plugin-typescript2@0.32.1(rollup@2.79.2)(typescript@4.9.5): dependencies: + '@rollup/pluginutils': 4.2.1 find-cache-dir: 3.3.2 - fs-extra: 8.1.0 - resolve: 1.12.0 - rollup: 1.32.1 - rollup-pluginutils: 2.8.1 - tslib: 1.10.0 - typescript: 3.9.10 - dev: true + fs-extra: 10.1.0 + resolve: 1.22.8 + rollup: 2.79.2 + tslib: 2.8.0 + typescript: 4.9.5 - /rollup-pluginutils@2.8.1: - resolution: {integrity: sha512-J5oAoysWar6GuZo0s+3bZ6sVZAC0pfqKz68De7ZgDi5z63jOVZn1uJL/+z1jeKHNbGII8kAyHF5q8LnxSX5lQg==} + rollup-plugin-visualizer@5.12.0(rollup@2.79.2): dependencies: - estree-walker: 0.6.1 - dev: true + open: 8.4.2 + picomatch: 2.3.1 + source-map: 0.7.4 + yargs: 17.7.2 + optionalDependencies: + rollup: 2.79.2 - /rollup-pluginutils@2.8.2: - resolution: {integrity: sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==} + rollup-pluginutils@2.8.2: dependencies: estree-walker: 0.6.1 - dev: true - - /rollup@1.32.1: - resolution: {integrity: sha512-/2HA0Ec70TvQnXdzynFffkjA6XN+1e2pEv/uKS5Ulca40g2L7KuOE3riasHoNVHOsFD5KKZgDsMk1CP3Tw9s+A==} - hasBin: true - dependencies: - '@types/estree': 1.0.0 - '@types/node': 18.15.11 - acorn: 7.4.1 - dev: true - /rollup@2.79.1: - resolution: {integrity: sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==} - engines: {node: '>=10.0.0'} - hasBin: true + rollup@2.79.2: optionalDependencies: - fsevents: 2.3.2 - dev: false + fsevents: 2.3.3 - /rollup@3.20.2: - resolution: {integrity: sha512-3zwkBQl7Ai7MFYQE0y1MeQ15+9jsi7XxfrqwTb/9EK8D9C9+//EBR4M+CuA1KODRaNbFez/lWxA5vhEGZp4MUg==} - engines: {node: '>=14.18.0', npm: '>=8.0.0'} - hasBin: true + rollup@3.29.5: optionalDependencies: - fsevents: 2.3.2 - dev: true + fsevents: 2.3.3 - /rollup@3.20.6: - resolution: {integrity: sha512-2yEB3nQXp/tBQDN0hJScJQheXdvU2wFhh6ld7K/aiZ1vYcak6N/BKjY1QrU6BvO2JWYS8bEs14FRaxXosxy2zw==} - engines: {node: '>=14.18.0', npm: '>=8.0.0'} - hasBin: true + rollup@4.24.3: + dependencies: + '@types/estree': 1.0.6 optionalDependencies: - fsevents: 2.3.2 - dev: true - - /rtl-css-js@1.16.1: - resolution: {integrity: sha512-lRQgou1mu19e+Ya0LsTvKrVJ5TYUbqCVPAiImX3UfLTenarvPUl1QFdvu5Z3PYmHT9RCcwIfbjRQBntExyj3Zg==} + '@rollup/rollup-android-arm-eabi': 4.24.3 + '@rollup/rollup-android-arm64': 4.24.3 + '@rollup/rollup-darwin-arm64': 4.24.3 + '@rollup/rollup-darwin-x64': 4.24.3 + '@rollup/rollup-freebsd-arm64': 4.24.3 + '@rollup/rollup-freebsd-x64': 4.24.3 + '@rollup/rollup-linux-arm-gnueabihf': 4.24.3 + '@rollup/rollup-linux-arm-musleabihf': 4.24.3 + '@rollup/rollup-linux-arm64-gnu': 4.24.3 + '@rollup/rollup-linux-arm64-musl': 4.24.3 + '@rollup/rollup-linux-powerpc64le-gnu': 4.24.3 + '@rollup/rollup-linux-riscv64-gnu': 4.24.3 + '@rollup/rollup-linux-s390x-gnu': 4.24.3 + '@rollup/rollup-linux-x64-gnu': 4.24.3 + '@rollup/rollup-linux-x64-musl': 4.24.3 + '@rollup/rollup-win32-arm64-msvc': 4.24.3 + '@rollup/rollup-win32-ia32-msvc': 4.24.3 + '@rollup/rollup-win32-x64-msvc': 4.24.3 + fsevents: 2.3.3 + + rtl-css-js@1.16.1: + dependencies: + '@babel/runtime': 7.26.0 + + run-async@2.4.1: {} + + run-async@3.0.0: {} + + run-parallel@1.2.0: dependencies: - '@babel/runtime': 7.21.0 - dev: false + queue-microtask: 1.2.3 - /run-async@2.4.1: - resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==} - engines: {node: '>=0.12.0'} + rusha@0.8.14: {} - /run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + rxjs@7.8.1: dependencies: - queue-microtask: 1.2.3 + tslib: 2.8.0 - /rxjs@7.8.0: - resolution: {integrity: sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==} + sade@1.8.1: dependencies: - tslib: 2.5.0 + mri: 1.2.0 - /sade@1.8.1: - resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} - engines: {node: '>=6'} + safe-array-concat@1.1.2: dependencies: - mri: 1.2.0 - dev: true + call-bind: 1.0.7 + get-intrinsic: 1.2.4 + has-symbols: 1.0.3 + isarray: 2.0.5 - /safe-buffer@5.1.2: - resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + safe-buffer@5.1.2: {} - /safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + safe-buffer@5.2.1: {} - /safe-identifier@0.4.2: - resolution: {integrity: sha512-6pNbSMW6OhAi9j+N8V+U715yBQsaWJ7eyEUaOrawX+isg5ZxhUlV1NipNtgaKHmFGiABwt+ZF04Ii+3Xjkg+8w==} - dev: true + safe-identifier@0.4.2: {} - /safe-regex-test@1.0.0: - resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==} + safe-regex-test@1.0.3: dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.0 + call-bind: 1.0.7 + es-errors: 1.3.0 is-regex: 1.1.4 - /safer-buffer@2.1.2: - resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - - /sanitize.css@13.0.0: - resolution: {integrity: sha512-ZRwKbh/eQ6w9vmTjkuG0Ioi3HBwPFce0O+v//ve+aOq1oeCy7jMV2qzzAlpsNuqpqCBjjriM1lbtZbF/Q8jVyA==} - dev: false + safer-buffer@2.1.2: {} - /saslprep@1.0.3: - resolution: {integrity: sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==} - engines: {node: '>=6'} - requiresBuild: true - dependencies: - sparse-bitfield: 3.0.3 - dev: false - optional: true + sanitize.css@13.0.0: {} - /sass-loader@12.6.0(webpack@5.78.0): - resolution: {integrity: sha512-oLTaH0YCtX4cfnJZxKSLAyglED0naiYfNG1iXfU5w1LNZ+ukoA5DtyDIN5zmKVZwYNJP4KRc5Y3hkWga+7tYfA==} - engines: {node: '>= 12.13.0'} - peerDependencies: - fibers: '>= 3.1.0' - node-sass: ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 - sass: ^1.3.0 - sass-embedded: '*' - webpack: ^5.0.0 - peerDependenciesMeta: - fibers: - optional: true - node-sass: - optional: true - sass: - optional: true - sass-embedded: - optional: true + sass-loader@12.6.0(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))): dependencies: klona: 2.0.6 neo-async: 2.6.2 - webpack: 5.78.0(esbuild@0.16.3)(webpack-cli@5.0.1) - dev: false + webpack: 5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(esbuild@0.25.6) - /sax@1.2.4: - resolution: {integrity: sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==} + sax@1.2.4: {} - /saxes@5.0.1: - resolution: {integrity: sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==} - engines: {node: '>=10'} + sax@1.4.1: + optional: true + + saxes@5.0.1: dependencies: xmlchars: 2.2.0 - dev: false - /scheduler@0.20.2: - resolution: {integrity: sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==} + scheduler@0.20.2: dependencies: loose-envify: 1.4.0 object-assign: 4.1.1 - dev: false - /scheduler@0.23.0: - resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==} + scheduler@0.23.2: dependencies: loose-envify: 1.4.0 - /schema-utils@2.7.0: - resolution: {integrity: sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==} - engines: {node: '>= 8.9.0'} + scheduler@0.25.0: {} + + schema-utils@2.7.0: dependencies: - '@types/json-schema': 7.0.11 + '@types/json-schema': 7.0.15 ajv: 6.12.6 ajv-keywords: 3.5.2(ajv@6.12.6) - dev: false - /schema-utils@2.7.1: - resolution: {integrity: sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==} - engines: {node: '>= 8.9.0'} + schema-utils@2.7.1: dependencies: - '@types/json-schema': 7.0.11 + '@types/json-schema': 7.0.15 ajv: 6.12.6 ajv-keywords: 3.5.2(ajv@6.12.6) - dev: false - /schema-utils@3.1.1: - resolution: {integrity: sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==} - engines: {node: '>= 10.13.0'} + schema-utils@3.3.0: dependencies: - '@types/json-schema': 7.0.11 + '@types/json-schema': 7.0.15 ajv: 6.12.6 ajv-keywords: 3.5.2(ajv@6.12.6) - /schema-utils@3.1.2: - resolution: {integrity: sha512-pvjEHOgWc9OWA/f/DE3ohBWTD6EleVLf7iFUkoSwAxttdBhB9QUebQgxER2kWueOvRJXPHNnyrvvh9eZINB8Eg==} - engines: {node: '>= 10.13.0'} + schema-utils@4.2.0: dependencies: - '@types/json-schema': 7.0.11 - ajv: 6.12.6 - ajv-keywords: 3.5.2(ajv@6.12.6) - dev: true + '@types/json-schema': 7.0.15 + ajv: 8.17.1 + ajv-formats: 2.1.1(ajv@8.17.1) + ajv-keywords: 5.1.0(ajv@8.17.1) - /schema-utils@4.0.0: - resolution: {integrity: sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==} - engines: {node: '>= 12.13.0'} + schema-utils@4.3.0: dependencies: - '@types/json-schema': 7.0.11 - ajv: 8.12.0 - ajv-formats: 2.1.1(ajv@8.12.0) - ajv-keywords: 5.1.0(ajv@8.12.0) + '@types/json-schema': 7.0.15 + ajv: 8.17.1 + ajv-formats: 2.1.1(ajv@8.17.1) + ajv-keywords: 5.1.0(ajv@8.17.1) - /scoped-regex@2.1.0: - resolution: {integrity: sha512-g3WxHrqSWCZHGHlSrF51VXFdjImhwvH8ZO/pryFH56Qi0cDsZfylQa/t0jCzVQFNbNvM00HfHjkDPEuarKDSWQ==} - engines: {node: '>=8'} + scoped-regex@2.1.0: {} - /screenfull@5.2.0: - resolution: {integrity: sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA==} - engines: {node: '>=0.10.0'} - dev: false + screenfull@5.2.0: {} - /scroll-into-view-if-needed@3.0.10: - resolution: {integrity: sha512-t44QCeDKAPf1mtQH3fYpWz8IM/DyvHLjs8wUvvwMYxk5moOqCzrMSxK6HQVD0QVmVjXFavoFIPRVrMuJPKAvtg==} + scroll-into-view-if-needed@3.1.0: dependencies: - compute-scroll-into-view: 3.0.3 - - /select-hose@2.0.0: - resolution: {integrity: sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==} - dev: false + compute-scroll-into-view: 3.1.0 - /selfsigned@2.1.1: - resolution: {integrity: sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ==} - engines: {node: '>=10'} + selderee@0.11.0: dependencies: - node-forge: 1.3.1 - dev: false + parseley: 0.12.1 - /semver@5.7.1: - resolution: {integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==} - hasBin: true + select-hose@2.0.0: {} - /semver@6.1.1: - resolution: {integrity: sha512-rWYq2e5iYW+fFe/oPPtYJxYgjBm8sC4rmoGdUOgBB7VnwKt6HrL793l2voH1UlsyYZpJ4g0wfjnTEO1s1NP2eQ==} - hasBin: true - dev: true + selfsigned@2.4.1: + dependencies: + '@types/node-forge': 1.3.11 + node-forge: 1.3.1 - /semver@6.3.0: - resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==} - hasBin: true + semver@5.7.2: {} - /semver@7.0.0: - resolution: {integrity: sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==} - hasBin: true - dev: true + semver@6.3.1: {} - /semver@7.3.8: - resolution: {integrity: sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==} - engines: {node: '>=10'} - hasBin: true - dependencies: - lru-cache: 6.0.0 - dev: true + semver@7.6.3: {} - /semver@7.4.0: - resolution: {integrity: sha512-RgOxM8Mw+7Zus0+zcLEUn8+JfoLpj/huFTItQy2hsM4khuC1HYRDp0cU482Ewn/Fcy6bCjufD8vAj7voC66KQw==} - engines: {node: '>=10'} - hasBin: true - dependencies: - lru-cache: 6.0.0 + semver@7.7.1: {} - /semver@7.5.0: - resolution: {integrity: sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==} - engines: {node: '>=10'} - hasBin: true - dependencies: - lru-cache: 6.0.0 + semver@7.7.2: {} - /send@0.18.0: - resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} - engines: {node: '>= 0.8.0'} + send@0.19.0: dependencies: debug: 2.6.9 depd: 2.0.0 @@ -23151,19 +31253,15 @@ packages: transitivePeerDependencies: - supports-color - /serialize-javascript@4.0.0: - resolution: {integrity: sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==} + serialize-javascript@4.0.0: dependencies: randombytes: 2.1.0 - /serialize-javascript@6.0.1: - resolution: {integrity: sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==} + serialize-javascript@6.0.2: dependencies: randombytes: 2.1.0 - /serve-index@1.9.1: - resolution: {integrity: sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==} - engines: {node: '>= 0.8.0'} + serve-index@1.9.1: dependencies: accepts: 1.3.8 batch: 0.6.1 @@ -23174,303 +31272,317 @@ packages: parseurl: 1.3.3 transitivePeerDependencies: - supports-color - dev: false - /serve-static@1.15.0: - resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==} - engines: {node: '>= 0.8.0'} + serve-static@1.16.2: dependencies: - encodeurl: 1.0.2 + encodeurl: 2.0.0 escape-html: 1.0.3 parseurl: 1.3.3 - send: 0.18.0 + send: 0.19.0 transitivePeerDependencies: - supports-color - /set-blocking@2.0.0: - resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + set-blocking@2.0.0: {} - /set-cookie-parser@2.6.0: - resolution: {integrity: sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==} - dev: true + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + gopd: 1.0.1 + has-property-descriptors: 1.0.2 - /set-harmonic-interval@1.0.1: - resolution: {integrity: sha512-AhICkFV84tBP1aWqPwLZqFvAwqEoVA9kxNMniGEUvzOlm4vLmOFLiTT3UZ6bziJTy4bOVpzWGTfSCbmaayGx8g==} - engines: {node: '>=6.9'} - dev: false + set-function-name@2.0.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 - /setimmediate@1.0.5: - resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} - dev: false + set-harmonic-interval@1.0.1: {} - /setprototypeof@1.1.0: - resolution: {integrity: sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==} - dev: false + setimmediate@1.0.5: {} - /setprototypeof@1.2.0: - resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + setprototypeof@1.1.0: {} - /shallow-clone@3.0.1: - resolution: {integrity: sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==} - engines: {node: '>=8'} + setprototypeof@1.2.0: {} + + shallow-clone@3.0.1: dependencies: kind-of: 6.0.3 - /shallowequal@1.1.0: - resolution: {integrity: sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==} - dev: false + shallowequal@1.1.0: {} - /shebang-command@2.0.0: - resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} - engines: {node: '>=8'} + sharp@0.34.1: + dependencies: + color: 4.2.3 + detect-libc: 2.0.4 + semver: 7.7.2 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.34.1 + '@img/sharp-darwin-x64': 0.34.1 + '@img/sharp-libvips-darwin-arm64': 1.1.0 + '@img/sharp-libvips-darwin-x64': 1.1.0 + '@img/sharp-libvips-linux-arm': 1.1.0 + '@img/sharp-libvips-linux-arm64': 1.1.0 + '@img/sharp-libvips-linux-ppc64': 1.1.0 + '@img/sharp-libvips-linux-s390x': 1.1.0 + '@img/sharp-libvips-linux-x64': 1.1.0 + '@img/sharp-libvips-linuxmusl-arm64': 1.1.0 + '@img/sharp-libvips-linuxmusl-x64': 1.1.0 + '@img/sharp-linux-arm': 0.34.1 + '@img/sharp-linux-arm64': 0.34.1 + '@img/sharp-linux-s390x': 0.34.1 + '@img/sharp-linux-x64': 0.34.1 + '@img/sharp-linuxmusl-arm64': 0.34.1 + '@img/sharp-linuxmusl-x64': 0.34.1 + '@img/sharp-wasm32': 0.34.1 + '@img/sharp-win32-ia32': 0.34.1 + '@img/sharp-win32-x64': 0.34.1 + + sharp@0.34.3: + dependencies: + color: 4.2.3 + detect-libc: 2.0.4 + semver: 7.7.2 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.34.3 + '@img/sharp-darwin-x64': 0.34.3 + '@img/sharp-libvips-darwin-arm64': 1.2.0 + '@img/sharp-libvips-darwin-x64': 1.2.0 + '@img/sharp-libvips-linux-arm': 1.2.0 + '@img/sharp-libvips-linux-arm64': 1.2.0 + '@img/sharp-libvips-linux-ppc64': 1.2.0 + '@img/sharp-libvips-linux-s390x': 1.2.0 + '@img/sharp-libvips-linux-x64': 1.2.0 + '@img/sharp-libvips-linuxmusl-arm64': 1.2.0 + '@img/sharp-libvips-linuxmusl-x64': 1.2.0 + '@img/sharp-linux-arm': 0.34.3 + '@img/sharp-linux-arm64': 0.34.3 + '@img/sharp-linux-ppc64': 0.34.3 + '@img/sharp-linux-s390x': 0.34.3 + '@img/sharp-linux-x64': 0.34.3 + '@img/sharp-linuxmusl-arm64': 0.34.3 + '@img/sharp-linuxmusl-x64': 0.34.3 + '@img/sharp-wasm32': 0.34.3 + '@img/sharp-win32-arm64': 0.34.3 + '@img/sharp-win32-ia32': 0.34.3 + '@img/sharp-win32-x64': 0.34.3 + optional: true + + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 - /shebang-regex@3.0.0: - resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} - engines: {node: '>=8'} + shebang-regex@3.0.0: {} - /shell-quote@1.8.1: - resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==} - dev: false + shell-quote@1.8.1: {} - /shelljs@0.8.5: - resolution: {integrity: sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==} - engines: {node: '>=4'} - hasBin: true + shelljs@0.8.5: dependencies: glob: 7.2.3 interpret: 1.4.0 rechoir: 0.6.2 - /side-channel@1.0.4: - resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} + side-channel@1.0.6: dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.0 - object-inspect: 1.12.3 + call-bind: 1.0.7 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 + object-inspect: 1.13.2 - /sigmund@1.0.1: - resolution: {integrity: sha512-fCvEXfh6NWpm+YSuY2bpXb/VIihqWA6hLsgboC+0nl71Q7N7o2eaCW8mJa/NLvQhs6jpd3VZV4UiUQlV6+lc8g==} - dev: false + signal-exit@3.0.7: {} - /signal-exit@3.0.7: - resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + signal-exit@4.1.0: {} - /simple-concat@1.0.1: - resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} - dev: false + sigstore@1.9.0: + dependencies: + '@sigstore/bundle': 1.1.0 + '@sigstore/protobuf-specs': 0.2.1 + '@sigstore/sign': 1.0.0 + '@sigstore/tuf': 1.0.3 + make-fetch-happen: 11.1.1 + transitivePeerDependencies: + - supports-color - /simple-get@3.1.1: - resolution: {integrity: sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==} + simple-concat@1.0.1: {} + + simple-get@4.0.1: dependencies: - decompress-response: 4.2.1 + decompress-response: 6.0.0 once: 1.4.0 simple-concat: 1.0.1 - dev: false - /simple-swizzle@0.2.2: - resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + simple-git@3.27.0: + dependencies: + '@kwsites/file-exists': 1.1.1 + '@kwsites/promise-deferred': 1.1.1 + debug: 4.3.7(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + + simple-swizzle@0.2.2: dependencies: is-arrayish: 0.3.2 - /simple-update-notifier@1.1.0: - resolution: {integrity: sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==} - engines: {node: '>=8.10.0'} + simple-update-notifier@2.0.0: dependencies: - semver: 7.0.0 - dev: true + semver: 7.6.3 - /sisteransi@1.0.5: - resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + sirv@2.0.4: + dependencies: + '@polka/url': 1.0.0-next.28 + mrmime: 2.0.1 + totalist: 3.0.1 - /slash@3.0.0: - resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} - engines: {node: '>=8'} + sisteransi@1.0.5: {} - /slash@4.0.0: - resolution: {integrity: sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==} - engines: {node: '>=12'} + slash@3.0.0: {} - /slick@1.12.2: - resolution: {integrity: sha512-4qdtOGcBjral6YIBCWJ0ljFSKNLz9KkhbWtuGvUyRowl1kxfuE1x/Z/aJcaiilpb3do9bl5K7/1h9XC5wWpY/A==} - dev: false + slash@4.0.0: {} - /smart-buffer@4.2.0: - resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} - engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + slash@5.1.0: {} - /snappyjs@0.6.1: - resolution: {integrity: sha512-YIK6I2lsH072UE0aOFxxY1dPDCS43I5ktqHpeAsuLNYWkE5pGxRGWfDM4/vSUfNzXjC1Ivzt3qx31PCLmc9yqg==} - dev: false + slick@1.12.2: {} - /sockjs@0.3.24: - resolution: {integrity: sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==} + smart-buffer@4.2.0: {} + + smob@1.5.0: {} + + socket.io-adapter@2.5.5: dependencies: - faye-websocket: 0.11.4 - uuid: 8.3.2 - websocket-driver: 0.7.4 - dev: false + debug: 4.3.7(supports-color@5.5.0) + ws: 8.17.1 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate - /socks-proxy-agent@5.0.1: - resolution: {integrity: sha512-vZdmnjb9a2Tz6WEQVIurybSwElwPxMZaIc7PzqbJTrezcKNznv6giT7J7tZDZ1BojVaa1jvO/UiUdhDVB0ACoQ==} - engines: {node: '>= 6'} + socket.io-client@4.8.1: dependencies: - agent-base: 6.0.2 - debug: 4.3.4 - socks: 2.7.1 + '@socket.io/component-emitter': 3.1.2 + debug: 4.3.7(supports-color@5.5.0) + engine.io-client: 6.6.3 + socket.io-parser: 4.2.4 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + socket.io-parser@4.2.4: + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.3.7(supports-color@5.5.0) transitivePeerDependencies: - supports-color - dev: true - /socks-proxy-agent@6.2.1: - resolution: {integrity: sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==} - engines: {node: '>= 10'} + socket.io@4.8.1: + dependencies: + accepts: 1.3.8 + base64id: 2.0.0 + cors: 2.8.5 + debug: 4.3.7(supports-color@5.5.0) + engine.io: 6.6.4 + socket.io-adapter: 2.5.5 + socket.io-parser: 4.2.4 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + sockjs@0.3.24: + dependencies: + faye-websocket: 0.11.4 + uuid: 8.3.2 + websocket-driver: 0.7.4 + + socks-proxy-agent@6.2.1: dependencies: agent-base: 6.0.2 - debug: 4.3.4 - socks: 2.7.1 + debug: 4.3.7(supports-color@5.5.0) + socks: 2.8.3 transitivePeerDependencies: - supports-color - /socks-proxy-agent@7.0.0: - resolution: {integrity: sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==} - engines: {node: '>= 10'} + socks-proxy-agent@7.0.0: dependencies: agent-base: 6.0.2 - debug: 4.3.4 - socks: 2.7.1 + debug: 4.3.7(supports-color@5.5.0) + socks: 2.8.3 transitivePeerDependencies: - supports-color - /socks@2.7.1: - resolution: {integrity: sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==} - engines: {node: '>= 10.13.0', npm: '>= 3.0.0'} + socks@2.8.3: dependencies: - ip: 2.0.0 + ip-address: 9.0.5 smart-buffer: 4.2.0 - /sort-keys@4.2.0: - resolution: {integrity: sha512-aUYIEU/UviqPgc8mHR6IW1EGxkAXpeRETYcrzg8cLAvUPZcpAlleSXHV2mY7G12GphSH6Gzv+4MMVSSkbdteHg==} - engines: {node: '>=8'} + sonner@2.0.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0): dependencies: - is-plain-obj: 2.1.0 + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) - /sort-object-keys@1.1.3: - resolution: {integrity: sha512-855pvK+VkU7PaKYPc+Jjnmt4EzejQHyhhF33q31qG8x7maDzkeFhAAThdCYay11CISO+qAMwjOBP+fPZe0IPyg==} - dev: true - - /sort-package-json@1.57.0: - resolution: {integrity: sha512-FYsjYn2dHTRb41wqnv+uEqCUvBpK3jZcTp9rbz2qDTmel7Pmdtf+i2rLaaPMRZeSVM60V3Se31GyWFpmKs4Q5Q==} - hasBin: true + sort-keys@4.2.0: dependencies: - detect-indent: 6.1.0 - detect-newline: 3.1.0 - git-hooks-list: 1.0.3 - globby: 10.0.0 is-plain-obj: 2.1.0 - sort-object-keys: 1.1.3 - dev: true - /source-list-map@2.0.1: - resolution: {integrity: sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==} - dev: false + source-list-map@2.0.1: {} - /source-map-js@1.0.2: - resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} - engines: {node: '>=0.10.0'} + source-map-js@1.2.1: {} - /source-map-loader@3.0.2(webpack@5.78.0): - resolution: {integrity: sha512-BokxPoLjyl3iOrgkWaakaxqnelAJSS+0V+De0kKIq6lyWrXuiPgYTGp6z3iHmqljKAaLXwZa+ctD8GccRJeVvg==} - engines: {node: '>= 12.13.0'} - peerDependencies: - webpack: ^5.0.0 + source-map-loader@3.0.2(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))): dependencies: abab: 2.0.6 iconv-lite: 0.6.3 - source-map-js: 1.0.2 - webpack: 5.78.0(esbuild@0.16.3)(webpack-cli@5.0.1) - dev: false + source-map-js: 1.2.1 + webpack: 5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(esbuild@0.25.6) - /source-map-support@0.5.13: - resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} + source-map-support@0.5.13: dependencies: buffer-from: 1.1.2 source-map: 0.6.1 - dev: true - /source-map-support@0.5.21: - resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + source-map-support@0.5.21: dependencies: - buffer-from: 1.1.2 - source-map: 0.6.1 - - /source-map@0.5.6: - resolution: {integrity: sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==} - engines: {node: '>=0.10.0'} - dev: false + buffer-from: 1.1.2 + source-map: 0.6.1 - /source-map@0.5.7: - resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} - engines: {node: '>=0.10.0'} - dev: true + source-map@0.5.6: {} - /source-map@0.6.1: - resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} - engines: {node: '>=0.10.0'} + source-map@0.6.1: {} - /source-map@0.7.4: - resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} - engines: {node: '>= 8'} + source-map@0.7.4: {} - /source-map@0.8.0-beta.0: - resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} - engines: {node: '>= 8'} + source-map@0.8.0-beta.0: dependencies: whatwg-url: 7.1.0 - dev: false - - /sourcemap-codec@1.4.8: - resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} - deprecated: Please use @jridgewell/sourcemap-codec instead - /space-separated-tokens@2.0.2: - resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} - dev: true + sourcemap-codec@1.4.8: {} - /spark-md5@3.0.2: - resolution: {integrity: sha512-wcFzz9cDfbuqe0FZzfi2or1sgyIrsDwmPwfZC4hiNidPdPINjeUwNfv5kldczoEAcjl9Y1L3SM7Uz2PUEQzxQw==} - dev: true + spamc@0.0.5: {} - /sparse-bitfield@3.0.3: - resolution: {integrity: sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==} + sparse-bitfield@3.0.3: dependencies: memory-pager: 1.5.0 - dev: false - optional: true - /spdx-correct@3.2.0: - resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} + spdx-correct@3.2.0: dependencies: spdx-expression-parse: 3.0.1 - spdx-license-ids: 3.0.13 + spdx-license-ids: 3.0.20 - /spdx-exceptions@2.3.0: - resolution: {integrity: sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==} + spdx-exceptions@2.5.0: {} - /spdx-expression-parse@3.0.1: - resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} + spdx-expression-parse@3.0.1: dependencies: - spdx-exceptions: 2.3.0 - spdx-license-ids: 3.0.13 + spdx-exceptions: 2.5.0 + spdx-license-ids: 3.0.20 - /spdx-license-ids@3.0.13: - resolution: {integrity: sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==} + spdx-license-ids@3.0.20: {} - /spdy-transport@3.0.0: - resolution: {integrity: sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==} + spdy-transport@3.0.0: dependencies: - debug: 4.3.4 + debug: 4.3.7(supports-color@5.5.0) detect-node: 2.1.0 hpack.js: 2.1.6 obuf: 1.1.2 @@ -23478,489 +31590,331 @@ packages: wbuf: 1.7.3 transitivePeerDependencies: - supports-color - dev: false - /spdy@4.0.2: - resolution: {integrity: sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==} - engines: {node: '>=6.0.0'} + spdy@4.0.2: dependencies: - debug: 4.3.4 + debug: 4.3.7(supports-color@5.5.0) handle-thing: 2.0.1 http-deceiver: 1.2.7 select-hose: 2.0.0 spdy-transport: 3.0.0 transitivePeerDependencies: - supports-color - dev: false - /spex@3.3.0: - resolution: {integrity: sha512-VNiXjFp6R4ldPbVRYbpxlD35yRHceecVXlct1J4/X80KuuPnW2AXMq3sGwhnJOhKkUsOxAT6nRGfGE5pocVw5w==} - engines: {node: '>=10.0.0'} - dev: false + spex@3.4.0: {} - /split-ca@1.0.1: - resolution: {integrity: sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ==} - dev: true + split-ca@1.0.1: {} - /split2@4.2.0: - resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} - engines: {node: '>= 10.x'} + split2@4.2.0: {} - /sprintf-js@1.0.3: - resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + sprintf-js@1.0.3: {} - /ssh-remote-port-forward@1.0.4: - resolution: {integrity: sha512-x0LV1eVDwjf1gmG7TTnfqIzf+3VPRz7vrNIjX6oYLbeCrf/PeVY6hkT68Mg+q02qXxQhrLjB0jfgvhevoCRmLQ==} + sprintf-js@1.1.3: {} + + ssh-remote-port-forward@1.0.4: dependencies: '@types/ssh2': 0.5.52 - ssh2: 1.11.0 - dev: true + ssh2: 1.16.0 - /ssh2@1.11.0: - resolution: {integrity: sha512-nfg0wZWGSsfUe/IBJkXVll3PEZ//YH2guww+mP88gTpuSU4FtZN7zu9JoeTGOyCNx2dTDtT9fOpWwlzyj4uOOw==} - engines: {node: '>=10.16.0'} - requiresBuild: true + ssh2@1.16.0: dependencies: asn1: 0.2.6 bcrypt-pbkdf: 1.0.2 optionalDependencies: - cpu-features: 0.0.6 - nan: 2.17.0 - dev: true + cpu-features: 0.0.10 + nan: 2.22.0 - /ssri@8.0.1: - resolution: {integrity: sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==} - engines: {node: '>= 8'} + ssri@10.0.6: + dependencies: + minipass: 7.1.2 + + ssri@8.0.1: dependencies: minipass: 3.3.6 - /ssri@9.0.1: - resolution: {integrity: sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + ssri@9.0.1: dependencies: minipass: 3.3.6 - /stable-hash@0.0.3: - resolution: {integrity: sha512-c63fvNCQ7ip1wBfPv54MflMA5L6OE5J0p6Fg13IpKft4JAFoNal8+s/jtJ8PibrwqXm4onnbeQsADs8k0NQGUA==} - dev: false + stable-hash@0.0.3: {} - /stable@0.1.8: - resolution: {integrity: sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==} - deprecated: 'Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility' + stable@0.1.8: {} - /stack-generator@2.0.10: - resolution: {integrity: sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ==} + stack-generator@2.0.10: dependencies: stackframe: 1.3.4 - dev: false - /stack-utils@2.0.6: - resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} - engines: {node: '>=10'} + stack-utils@2.0.6: dependencies: escape-string-regexp: 2.0.0 - /stackframe@1.3.4: - resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==} - dev: false + stackframe@1.3.4: {} - /stacktrace-gps@3.1.2: - resolution: {integrity: sha512-GcUgbO4Jsqqg6RxfyTHFiPxdPqF+3LFmQhm7MgCuYQOYuWyqxo5pwRPz5d/u6/WYJdEnWfK4r+jGbyD8TSggXQ==} + stacktrace-gps@3.1.2: dependencies: source-map: 0.5.6 stackframe: 1.3.4 - dev: false - /stacktrace-js@2.0.2: - resolution: {integrity: sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg==} + stacktrace-js@2.0.2: dependencies: error-stack-parser: 2.1.4 stack-generator: 2.0.10 stacktrace-gps: 3.1.2 - dev: false - /standard-as-callback@2.1.0: - resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==} + stacktrace-parser@0.1.11: + dependencies: + type-fest: 0.7.1 - /state-local@1.0.7: - resolution: {integrity: sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==} - dev: false + standard-as-callback@2.1.0: {} - /statuses@1.5.0: - resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==} - engines: {node: '>= 0.6'} - dev: false + state-local@1.0.7: {} - /statuses@2.0.1: - resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} - engines: {node: '>= 0.8'} + static-eval@2.0.2: + dependencies: + escodegen: 1.14.3 - /stop-iteration-iterator@1.0.0: - resolution: {integrity: sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==} - engines: {node: '>= 0.4'} + statuses@1.5.0: {} + + statuses@2.0.1: {} + + stdin-discarder@0.2.2: {} + + stop-iteration-iterator@1.0.0: dependencies: - internal-slot: 1.0.5 + internal-slot: 1.0.7 - /stopwatch-node@1.1.0: - resolution: {integrity: sha512-TpRTztFjiq/B5wn84oKK0YHHtprgrC8pLOUkqVU+MN+2DgdBMaEIeSyGeAn2n+7mT9zrfJyhM33Yw4j/rRV2Bg==} - dev: false + stopwatch-node@1.1.0: {} - /stream-events@1.0.5: - resolution: {integrity: sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==} + stream-chain@2.2.5: {} + + stream-events@1.0.5: dependencies: stubs: 3.0.0 - dev: false - optional: true - /stream-shift@1.0.1: - resolution: {integrity: sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==} + stream-json@1.9.0: + dependencies: + stream-chain: 2.2.5 - /streamsearch@1.1.0: - resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} - engines: {node: '>=10.0.0'} - dev: false + stream-shift@1.0.3: {} - /string-convert@0.2.1: - resolution: {integrity: sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==} + string-convert@0.2.1: {} - /string-hash@1.1.3: - resolution: {integrity: sha512-kJUvRUFK49aub+a7T1nNE66EJbZBMnBgoC1UbCZ5n6bsZKBRga4KgBRTMn/pFkeCZSYtNeSyMxPDM0AXWELk2A==} - dev: true + string-hash@1.1.3: {} - /string-length@4.0.2: - resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} - engines: {node: '>=10'} + string-length@4.0.2: dependencies: char-regex: 1.0.2 strip-ansi: 6.0.1 - /string-length@5.0.1: - resolution: {integrity: sha512-9Ep08KAMUn0OadnVaBuRdE2l615CQ508kr0XMadjClfYpdCyvrbFp6Taebo8yyxokQ4viUd/xPPUA4FGgUa0ow==} - engines: {node: '>=12.20'} + string-length@5.0.1: dependencies: char-regex: 2.0.1 - strip-ansi: 7.0.1 - dev: false + strip-ansi: 7.1.0 - /string-natural-compare@3.0.1: - resolution: {integrity: sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==} - dev: false - - /string-width@1.0.2: - resolution: {integrity: sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==} - engines: {node: '>=0.10.0'} - dependencies: - code-point-at: 1.1.0 - is-fullwidth-code-point: 1.0.0 - strip-ansi: 3.0.1 - dev: false + string-natural-compare@3.0.1: {} - /string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} + string-width@4.2.3: dependencies: emoji-regex: 8.0.0 is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 - /string.prototype.matchall@4.0.8: - resolution: {integrity: sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==} + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.0 + + string-width@7.2.0: + dependencies: + emoji-regex: 10.4.0 + get-east-asian-width: 1.3.0 + strip-ansi: 7.1.0 + + string.prototype.includes@2.0.1: dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 - get-intrinsic: 1.2.0 + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + + string.prototype.matchall@4.0.11: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-errors: 1.3.0 + es-object-atoms: 1.0.0 + get-intrinsic: 1.2.4 + gopd: 1.0.1 has-symbols: 1.0.3 - internal-slot: 1.0.5 - regexp.prototype.flags: 1.4.3 - side-channel: 1.0.4 + internal-slot: 1.0.7 + regexp.prototype.flags: 1.5.3 + set-function-name: 2.0.2 + side-channel: 1.0.6 - /string.prototype.trim@1.2.7: - resolution: {integrity: sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==} - engines: {node: '>= 0.4'} + string.prototype.repeat@1.0.0: dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 + define-properties: 1.2.1 + es-abstract: 1.23.3 - /string.prototype.trimend@1.0.6: - resolution: {integrity: sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==} + string.prototype.trim@1.2.9: dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-object-atoms: 1.0.0 - /string.prototype.trimstart@1.0.6: - resolution: {integrity: sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==} + string.prototype.trimend@1.0.8: dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 + call-bind: 1.0.7 + define-properties: 1.2.1 + es-object-atoms: 1.0.0 - /string_decoder@0.10.31: - resolution: {integrity: sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==} - dev: true + string.prototype.trimstart@1.0.8: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-object-atoms: 1.0.0 - /string_decoder@1.1.1: - resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + string_decoder@1.1.1: dependencies: safe-buffer: 5.1.2 - /string_decoder@1.3.0: - resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + string_decoder@1.3.0: dependencies: safe-buffer: 5.2.1 - /stringify-entities@4.0.3: - resolution: {integrity: sha512-BP9nNHMhhfcMbiuQKCqMjhDP5yBCAxsPu4pHFFzJ6Alo9dZgY4VLDPutXqIjpRiMoKdp7Av85Gr73Q5uH9k7+g==} - dependencies: - character-entities-html4: 2.1.0 - character-entities-legacy: 3.0.0 - dev: true - - /stringify-object@3.3.0: - resolution: {integrity: sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==} - engines: {node: '>=4'} + stringify-object@3.3.0: dependencies: get-own-enumerable-property-symbols: 3.0.2 is-obj: 1.0.1 is-regexp: 1.0.0 - dev: false - /strip-ansi@3.0.1: - resolution: {integrity: sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==} - engines: {node: '>=0.10.0'} + strip-ansi@3.0.1: dependencies: ansi-regex: 2.1.1 - /strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} + strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 - /strip-ansi@7.0.1: - resolution: {integrity: sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==} - engines: {node: '>=12'} + strip-ansi@7.1.0: dependencies: - ansi-regex: 6.0.1 - dev: false + ansi-regex: 6.1.0 - /strip-bom-buf@1.0.0: - resolution: {integrity: sha512-1sUIL1jck0T1mhOLP2c696BIznzT525Lkub+n4jjMHjhjhoAQA6Ye659DxdlZBr0aLDMQoTxKIpnlqxgtwjsuQ==} - engines: {node: '>=4'} + strip-bom-buf@1.0.0: dependencies: is-utf8: 0.2.1 - /strip-bom-stream@2.0.0: - resolution: {integrity: sha512-yH0+mD8oahBZWnY43vxs4pSinn8SMKAdml/EOGBewoe1Y0Eitd0h2Mg3ZRiXruUW6L4P+lvZiEgbh0NgUGia1w==} - engines: {node: '>=0.10.0'} + strip-bom-stream@2.0.0: dependencies: first-chunk-stream: 2.0.0 strip-bom: 2.0.0 - /strip-bom@2.0.0: - resolution: {integrity: sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g==} - engines: {node: '>=0.10.0'} + strip-bom@2.0.0: dependencies: is-utf8: 0.2.1 - /strip-bom@3.0.0: - resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} - engines: {node: '>=4'} + strip-bom@3.0.0: {} - /strip-bom@4.0.0: - resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} - engines: {node: '>=8'} + strip-bom@4.0.0: {} - /strip-comments@2.0.1: - resolution: {integrity: sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==} - engines: {node: '>=10'} - dev: false + strip-comments@2.0.1: {} - /strip-final-newline@2.0.0: - resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} - engines: {node: '>=6'} + strip-final-newline@2.0.0: {} - /strip-indent@3.0.0: - resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} - engines: {node: '>=8'} + strip-indent@3.0.0: dependencies: min-indent: 1.0.1 - dev: true - /strip-json-comments@2.0.1: - resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} - engines: {node: '>=0.10.0'} - dev: false + strip-json-comments@2.0.1: {} - /strip-json-comments@3.1.1: - resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} - engines: {node: '>=8'} + strip-json-comments@3.1.1: {} - /stripe@11.18.0: - resolution: {integrity: sha512-OUA32uhNoSoM6wOodyFbV+3IBCoO140uzdXmBArQ0S88D4EbH91xl2v+Ml1sKalcFKUBadHLeHfU/p9AbsOfGw==} - engines: {node: '>=12.*'} + stripe@11.18.0: dependencies: - '@types/node': 18.11.12 - qs: 6.11.1 + '@types/node': 18.19.61 + qs: 6.13.0 - /strnum@1.0.5: - resolution: {integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==} - dev: false + strnum@1.0.5: {} - /stubs@3.0.0: - resolution: {integrity: sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==} - dev: false - optional: true + stubs@3.0.0: {} - /style-inject@0.3.0: - resolution: {integrity: sha512-IezA2qp+vcdlhJaVm5SOdPPTUu0FCEqfNSli2vRuSIBbu5Nq5UvygTk/VzeCqfLz2Atj3dVII5QBKGZRZ0edzw==} - dev: true + style-inject@0.3.0: {} - /style-loader@3.3.2(webpack@5.78.0): - resolution: {integrity: sha512-RHs/vcrKdQK8wZliteNK4NKzxvLBzpuHMqYmUVWeKa6MkaIQ97ZTOS0b+zapZhy6GcrgWnvWYCMHRirC3FsUmw==} - engines: {node: '>= 12.13.0'} - peerDependencies: - webpack: ^5.0.0 + style-loader@3.3.4(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))): dependencies: - webpack: 5.78.0(esbuild@0.16.3)(webpack-cli@5.0.1) - dev: false + webpack: 5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(esbuild@0.25.6) - /style-to-object@0.4.1: - resolution: {integrity: sha512-HFpbb5gr2ypci7Qw+IOhnP2zOU7e77b+rzM+wTzXzfi1PrtBCX0E7Pk4wL4iTLnhzZ+JgEGAhX81ebTg/aYjQw==} + styled-jsx@5.1.6(@babel/core@7.26.0)(react@18.3.1): dependencies: - inline-style-parser: 0.1.1 - dev: true + client-only: 0.0.1 + react: 18.3.1 + optionalDependencies: + '@babel/core': 7.26.0 - /styled-jsx@5.1.0(@babel/core@7.21.4)(react@18.2.0): - resolution: {integrity: sha512-/iHaRJt9U7T+5tp6TRelLnqBqiaIT0HsO0+vgyj8hK2KUk7aejFqRrumqPUlAqDwAj8IbS/1hk3IhBAAK/FCUQ==} - engines: {node: '>= 12.0.0'} - peerDependencies: - '@babel/core': '*' - babel-plugin-macros: '*' - react: '>= 16.8.0 || 17.x.x || ^18.0.0-0' - peerDependenciesMeta: - '@babel/core': - optional: true - babel-plugin-macros: - optional: true + styled-jsx@5.1.6(@babel/core@7.26.10)(react@18.3.1): dependencies: - '@babel/core': 7.21.4 client-only: 0.0.1 - react: 18.2.0 - dev: false + react: 18.3.1 + optionalDependencies: + '@babel/core': 7.26.10 - /styled-jsx@5.1.1(@babel/core@7.21.4)(react@18.2.0): - resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==} - engines: {node: '>= 12.0.0'} - peerDependencies: - '@babel/core': '*' - babel-plugin-macros: '*' - react: '>= 16.8.0 || 17.x.x || ^18.0.0-0' - peerDependenciesMeta: - '@babel/core': - optional: true - babel-plugin-macros: - optional: true + styled-jsx@5.1.6(@babel/core@7.26.10)(react@19.0.0): dependencies: - '@babel/core': 7.21.4 client-only: 0.0.1 - react: 18.2.0 - dev: false + react: 19.0.0 + optionalDependencies: + '@babel/core': 7.26.10 - /stylehacks@4.0.3: - resolution: {integrity: sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g==} - engines: {node: '>=6.9.0'} + stylehacks@5.1.1(postcss@8.4.47): dependencies: - browserslist: 4.21.5 - postcss: 7.0.39 - postcss-selector-parser: 3.1.2 - dev: true + browserslist: 4.24.2 + postcss: 8.4.47 + postcss-selector-parser: 6.1.2 - /stylehacks@5.1.1(postcss@8.4.21): - resolution: {integrity: sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 + stylehacks@7.0.4(postcss@8.4.47): dependencies: - browserslist: 4.21.5 - postcss: 8.4.21 - postcss-selector-parser: 6.0.11 - dev: false + browserslist: 4.24.2 + postcss: 8.4.47 + postcss-selector-parser: 6.1.2 - /stylis@4.1.3: - resolution: {integrity: sha512-GP6WDNWf+o403jrEp9c5jibKavrtLW+/qYGhFxFrG8maXhwTBI7gLLhiBb0o7uFccWN+EOS9aMO6cGHWAO07OA==} + stylis@4.3.4: {} - /sucrase@3.32.0: - resolution: {integrity: sha512-ydQOU34rpSyj2TGyz4D2p8rbktIOZ8QY9s+DGLvFU1i5pWJE8vkpruCjGCMHsdXwnD7JDcS+noSwM/a7zyNFDQ==} - engines: {node: '>=8'} - hasBin: true + sucrase@3.35.0: dependencies: - '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/gen-mapping': 0.3.5 commander: 4.1.1 - glob: 7.1.6 + glob: 10.4.5 lines-and-columns: 1.2.4 mz: 2.7.0 - pirates: 4.0.5 + pirates: 4.0.6 ts-interface-checker: 0.1.13 - /supports-color@2.0.0: - resolution: {integrity: sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==} - engines: {node: '>=0.8.0'} - dev: true - - /supports-color@3.2.3: - resolution: {integrity: sha512-Jds2VIYDrlp5ui7t8abHN2bjAu4LV/q4N2KivFPpGH0lrka0BMq/33AmECUXlKPcHigkNaqfXRENFju+rlcy+A==} - engines: {node: '>=0.8.0'} - dependencies: - has-flag: 1.0.0 - dev: true - - /supports-color@5.5.0: - resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} - engines: {node: '>=4'} - dependencies: - has-flag: 3.0.0 + supports-color@2.0.0: {} - /supports-color@6.1.0: - resolution: {integrity: sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==} - engines: {node: '>=6'} + supports-color@5.5.0: dependencies: has-flag: 3.0.0 - dev: true - /supports-color@7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} + supports-color@7.2.0: dependencies: has-flag: 4.0.0 - /supports-color@8.1.1: - resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} - engines: {node: '>=10'} + supports-color@8.1.1: dependencies: has-flag: 4.0.0 - /supports-hyperlinks@2.3.0: - resolution: {integrity: sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==} - engines: {node: '>=8'} + supports-hyperlinks@2.3.0: dependencies: has-flag: 4.0.0 supports-color: 7.2.0 - /supports-preserve-symlinks-flag@1.0.0: - resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} - engines: {node: '>= 0.4'} + supports-preserve-symlinks-flag@1.0.0: {} - /svg-parser@2.0.4: - resolution: {integrity: sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==} + svg-parser@2.0.4: {} - /svgo@1.3.2: - resolution: {integrity: sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==} - engines: {node: '>=4.0.0'} - deprecated: This SVGO version is no longer supported. Upgrade to v2.x.x. - hasBin: true + svgo@1.3.2: dependencies: chalk: 2.4.2 coa: 2.0.2 @@ -23970,136 +31924,109 @@ packages: csso: 4.2.0 js-yaml: 3.14.1 mkdirp: 0.5.6 - object.values: 1.1.6 + object.values: 1.2.0 sax: 1.2.4 stable: 0.1.8 unquote: 1.1.1 util.promisify: 1.0.1 - /svgo@2.8.0: - resolution: {integrity: sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==} - engines: {node: '>=10.13.0'} - hasBin: true + svgo@2.8.0: dependencies: '@trysound/sax': 0.2.0 commander: 7.2.0 css-select: 4.3.0 css-tree: 1.1.3 csso: 4.2.0 - picocolors: 1.0.0 + picocolors: 1.1.1 stable: 0.1.8 - dev: false - /symbol-tree@3.2.4: - resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} - dev: false - - /synckit@0.8.5: - resolution: {integrity: sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==} - engines: {node: ^14.18.0 || >=16.0.0} + svgo@3.3.2: dependencies: - '@pkgr/utils': 2.3.1 - tslib: 2.5.0 - dev: true + '@trysound/sax': 0.2.0 + commander: 7.2.0 + css-select: 5.1.0 + css-tree: 2.3.1 + css-what: 6.1.0 + csso: 5.0.5 + picocolors: 1.1.1 - /tailwindcss@3.3.1(postcss@8.4.21)(ts-node@10.8.2): - resolution: {integrity: sha512-Vkiouc41d4CEq0ujXl6oiGFQ7bA3WEhUZdTgXAhtKxSy49OmKs8rEfQmupsfF0IGW8fv2iQkp1EVUuapCFrZ9g==} - engines: {node: '>=12.13.0'} - hasBin: true - peerDependencies: - postcss: ^8.0.9 + symbol-tree@3.2.4: {} + + tailwind-merge@3.2.0: {} + + tailwindcss@3.4.0(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)): dependencies: + '@alloc/quick-lru': 5.2.0 arg: 5.0.2 - chokidar: 3.5.3 - color-name: 1.1.4 + chokidar: 3.6.0 didyoumean: 1.2.2 dlv: 1.1.3 - fast-glob: 3.2.12 + fast-glob: 3.3.2 glob-parent: 6.0.2 is-glob: 4.0.3 - jiti: 1.18.2 + jiti: 1.21.6 lilconfig: 2.1.0 - micromatch: 4.0.5 + micromatch: 4.0.8 normalize-path: 3.0.0 object-hash: 3.0.0 - picocolors: 1.0.0 - postcss: 8.4.21 - postcss-import: 14.1.0(postcss@8.4.21) - postcss-js: 4.0.1(postcss@8.4.21) - postcss-load-config: 3.1.4(postcss@8.4.21)(ts-node@10.8.2) - postcss-nested: 6.0.0(postcss@8.4.21) - postcss-selector-parser: 6.0.11 - postcss-value-parser: 4.2.0 - quick-lru: 5.1.1 - resolve: 1.22.2 - sucrase: 3.32.0 + picocolors: 1.1.1 + postcss: 8.4.47 + postcss-import: 15.1.0(postcss@8.4.47) + postcss-js: 4.0.1(postcss@8.4.47) + postcss-load-config: 4.0.2(postcss@8.4.47)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)) + postcss-nested: 6.2.0(postcss@8.4.47) + postcss-selector-parser: 6.1.2 + resolve: 1.22.8 + sucrase: 3.35.0 transitivePeerDependencies: - ts-node - /tailwindcss@3.3.1(postcss@8.4.22)(ts-node@10.8.2): - resolution: {integrity: sha512-Vkiouc41d4CEq0ujXl6oiGFQ7bA3WEhUZdTgXAhtKxSy49OmKs8rEfQmupsfF0IGW8fv2iQkp1EVUuapCFrZ9g==} - engines: {node: '>=12.13.0'} - hasBin: true - peerDependencies: - postcss: ^8.0.9 + tailwindcss@3.4.14(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)): dependencies: + '@alloc/quick-lru': 5.2.0 arg: 5.0.2 - chokidar: 3.5.3 - color-name: 1.1.4 + chokidar: 3.6.0 didyoumean: 1.2.2 dlv: 1.1.3 - fast-glob: 3.2.12 + fast-glob: 3.3.2 glob-parent: 6.0.2 is-glob: 4.0.3 - jiti: 1.18.2 + jiti: 1.21.6 lilconfig: 2.1.0 - micromatch: 4.0.5 + micromatch: 4.0.8 normalize-path: 3.0.0 object-hash: 3.0.0 - picocolors: 1.0.0 - postcss: 8.4.22 - postcss-import: 14.1.0(postcss@8.4.22) - postcss-js: 4.0.1(postcss@8.4.22) - postcss-load-config: 3.1.4(postcss@8.4.22)(ts-node@10.8.2) - postcss-nested: 6.0.0(postcss@8.4.22) - postcss-selector-parser: 6.0.11 - postcss-value-parser: 4.2.0 - quick-lru: 5.1.1 - resolve: 1.22.2 - sucrase: 3.32.0 + picocolors: 1.1.1 + postcss: 8.4.47 + postcss-import: 15.1.0(postcss@8.4.47) + postcss-js: 4.0.1(postcss@8.4.47) + postcss-load-config: 4.0.2(postcss@8.4.47)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)) + postcss-nested: 6.2.0(postcss@8.4.47) + postcss-selector-parser: 6.1.2 + resolve: 1.22.8 + sucrase: 3.35.0 transitivePeerDependencies: - ts-node - dev: true - /tapable@1.1.3: - resolution: {integrity: sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==} - engines: {node: '>=6'} - dev: false + tapable@1.1.3: {} - /tapable@2.2.1: - resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} - engines: {node: '>=6'} + tapable@2.2.1: {} - /tar-fs@2.0.1: - resolution: {integrity: sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA==} + tar-fs@2.0.1: dependencies: chownr: 1.1.4 mkdirp-classic: 0.5.3 - pump: 3.0.0 + pump: 3.0.2 tar-stream: 2.2.0 - dev: true - /tar-fs@2.1.1: - resolution: {integrity: sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==} + tar-fs@2.1.1: dependencies: chownr: 1.1.4 mkdirp-classic: 0.5.3 - pump: 3.0.0 + pump: 3.0.2 tar-stream: 2.2.0 - /tar-stream@2.2.0: - resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} - engines: {node: '>=6'} + tar-stream@2.2.0: dependencies: bl: 4.1.0 end-of-stream: 1.4.4 @@ -24107,645 +32034,524 @@ packages: inherits: 2.0.4 readable-stream: 3.6.2 - /tar@6.1.13: - resolution: {integrity: sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==} - engines: {node: '>=10'} + tar@6.2.1: dependencies: chownr: 2.0.0 fs-minipass: 2.1.0 - minipass: 4.2.8 + minipass: 5.0.0 minizlib: 2.1.2 mkdirp: 1.0.4 yallist: 4.0.0 - /teeny-request@8.0.3: - resolution: {integrity: sha512-jJZpA5He2y52yUhA7pyAGZlgQpcB+xLjcN0eUFxr9c8hP/H7uOXbBNVo/O0C/xVfJLJs680jvkFgVJEEvk9+ww==} - engines: {node: '>=12'} + tar@7.4.3: + dependencies: + '@isaacs/fs-minipass': 4.0.1 + chownr: 3.0.0 + minipass: 7.1.2 + minizlib: 3.0.1 + mkdirp: 3.0.1 + yallist: 5.0.0 + + tdigest@0.1.2: + dependencies: + bintrees: 1.0.2 + + teeny-request@9.0.0(encoding@0.1.13): dependencies: http-proxy-agent: 5.0.0 https-proxy-agent: 5.0.1 - node-fetch: 2.6.9 + node-fetch: 2.7.0(encoding@0.1.13) stream-events: 1.0.5 - uuid: 9.0.0 + uuid: 9.0.1 transitivePeerDependencies: - encoding - supports-color - dev: false - optional: true - /temp-dir@2.0.0: - resolution: {integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==} - engines: {node: '>=8'} - dev: false + temp-dir@2.0.0: {} - /tempy@0.6.0: - resolution: {integrity: sha512-G13vtMYPT/J8A4X2SjdtBTphZlrp1gKv6hZiOjw14RCWg6GbHuQBGtjlx75xLbYV/wEc0D7G5K4rxKP/cXk8Bw==} - engines: {node: '>=10'} + tempy@0.6.0: dependencies: is-stream: 2.0.1 temp-dir: 2.0.0 type-fest: 0.16.0 unique-string: 2.0.0 - dev: false - /terminal-link@2.1.1: - resolution: {integrity: sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==} - engines: {node: '>=8'} + terminal-link@2.1.1: dependencies: ansi-escapes: 4.3.2 supports-hyperlinks: 2.3.0 - /terser-webpack-plugin@5.3.7(esbuild@0.16.3)(webpack@5.78.0): - resolution: {integrity: sha512-AfKwIktyP7Cu50xNjXF/6Qb5lBNzYaWpU6YfoX3uZicTx0zTy0stDDCsvjDapKsSDvOeWo5MEq4TmdBy2cNoHw==} - engines: {node: '>= 10.13.0'} - peerDependencies: - '@swc/core': '*' - esbuild: '*' - uglify-js: '*' - webpack: ^5.1.0 - peerDependenciesMeta: - '@swc/core': - optional: true - esbuild: - optional: true - uglify-js: - optional: true + terser-webpack-plugin@5.3.10(@swc/core@1.11.10(@swc/helpers@0.5.15))(webpack@5.95.0): dependencies: - '@jridgewell/trace-mapping': 0.3.18 - esbuild: 0.16.3 + '@jridgewell/trace-mapping': 0.3.25 jest-worker: 27.5.1 - schema-utils: 3.1.1 - serialize-javascript: 6.0.1 - terser: 5.16.9 - webpack: 5.78.0(esbuild@0.16.3)(webpack-cli@5.0.1) + schema-utils: 3.3.0 + serialize-javascript: 6.0.2 + terser: 5.36.0 + webpack: 5.95.0(@swc/core@1.11.10(@swc/helpers@0.5.15))(webpack-cli@5.1.4) + optionalDependencies: + '@swc/core': 1.11.10(@swc/helpers@0.5.15) - /terser-webpack-plugin@5.3.7(esbuild@0.16.3)(webpack@5.79.0): - resolution: {integrity: sha512-AfKwIktyP7Cu50xNjXF/6Qb5lBNzYaWpU6YfoX3uZicTx0zTy0stDDCsvjDapKsSDvOeWo5MEq4TmdBy2cNoHw==} - engines: {node: '>= 10.13.0'} - peerDependencies: - '@swc/core': '*' - esbuild: '*' - uglify-js: '*' - webpack: ^5.1.0 - peerDependenciesMeta: - '@swc/core': - optional: true - esbuild: - optional: true - uglify-js: - optional: true + terser-webpack-plugin@5.3.10(@swc/core@1.11.10(@swc/helpers@0.5.15))(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))): + dependencies: + '@jridgewell/trace-mapping': 0.3.25 + jest-worker: 27.5.1 + schema-utils: 3.3.0 + serialize-javascript: 6.0.2 + terser: 5.36.0 + webpack: 5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(esbuild@0.25.6) + optionalDependencies: + '@swc/core': 1.11.10(@swc/helpers@0.5.15) + + terser-webpack-plugin@5.3.14(@swc/core@1.11.10(@swc/helpers@0.5.15))(esbuild@0.23.1)(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(esbuild@0.23.1)): + dependencies: + '@jridgewell/trace-mapping': 0.3.25 + jest-worker: 27.5.1 + schema-utils: 4.3.0 + serialize-javascript: 6.0.2 + terser: 5.36.0 + webpack: 5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(esbuild@0.23.1) + optionalDependencies: + '@swc/core': 1.11.10(@swc/helpers@0.5.15) + esbuild: 0.23.1 + + terser-webpack-plugin@5.3.14(@swc/core@1.11.10(@swc/helpers@0.5.15))(esbuild@0.25.6)(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))): + dependencies: + '@jridgewell/trace-mapping': 0.3.25 + jest-worker: 27.5.1 + schema-utils: 4.3.0 + serialize-javascript: 6.0.2 + terser: 5.36.0 + webpack: 5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(esbuild@0.25.6) + optionalDependencies: + '@swc/core': 1.11.10(@swc/helpers@0.5.15) + esbuild: 0.25.6 + + terser-webpack-plugin@5.3.14(@swc/core@1.11.10(@swc/helpers@0.5.15))(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(webpack-cli@5.1.4)): dependencies: - '@jridgewell/trace-mapping': 0.3.18 - esbuild: 0.16.3 + '@jridgewell/trace-mapping': 0.3.25 jest-worker: 27.5.1 - schema-utils: 3.1.1 - serialize-javascript: 6.0.1 - terser: 5.16.9 - webpack: 5.79.0(esbuild@0.16.3) - dev: true - - /terser@4.8.1: - resolution: {integrity: sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw==} - engines: {node: '>=6.0.0'} - hasBin: true + schema-utils: 4.3.0 + serialize-javascript: 6.0.2 + terser: 5.36.0 + webpack: 5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(webpack-cli@5.1.4) + optionalDependencies: + '@swc/core': 1.11.10(@swc/helpers@0.5.15) + + terser-webpack-plugin@5.3.14(@swc/core@1.11.10(@swc/helpers@0.5.15))(webpack@5.99.5): dependencies: - acorn: 8.8.2 - commander: 2.20.3 - source-map: 0.6.1 - source-map-support: 0.5.21 - dev: true + '@jridgewell/trace-mapping': 0.3.25 + jest-worker: 27.5.1 + schema-utils: 4.3.0 + serialize-javascript: 6.0.2 + terser: 5.36.0 + webpack: 5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(webpack-cli@6.0.1) + optionalDependencies: + '@swc/core': 1.11.10(@swc/helpers@0.5.15) - /terser@5.16.9: - resolution: {integrity: sha512-HPa/FdTB9XGI2H1/keLFZHxl6WNvAI4YalHGtDQTlMnJcoqSab1UwL4l1hGEhs6/GmLHBZIg/YgB++jcbzoOEg==} - engines: {node: '>=10'} - hasBin: true + terser@5.36.0: dependencies: - '@jridgewell/source-map': 0.3.3 - acorn: 8.8.2 + '@jridgewell/source-map': 0.3.6 + acorn: 8.14.0 commander: 2.20.3 source-map-support: 0.5.21 - /test-exclude@6.0.0: - resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} - engines: {node: '>=8'} + test-exclude@6.0.0: dependencies: '@istanbuljs/schema': 0.1.3 glob: 7.2.3 minimatch: 3.1.2 - /testcontainers@9.4.0: - resolution: {integrity: sha512-Mg7GMYENWd4U6KavKZ8XUTqYeW7dAJRYxltEmNid+4YcFNanG2qqUhrqIUvW4sZsjuH+kfjtlTUoOaEDavRJYw==} - engines: {node: '>= 10.16'} + testcontainers@9.12.0(encoding@0.1.13): dependencies: '@balena/dockerignore': 1.0.2 - '@types/archiver': 5.3.2 - '@types/dockerode': 3.3.16 - archiver: 5.3.1 - async-lock: 1.4.0 + '@types/archiver': 5.3.4 + '@types/dockerode': 3.3.31 + archiver: 5.3.2 + async-lock: 1.4.1 byline: 5.0.0 - debug: 4.3.4 - docker-compose: 0.23.19 + debug: 4.3.7(supports-color@5.5.0) + docker-compose: 0.24.8 dockerode: 3.3.5 get-port: 5.1.1 - node-fetch: 2.6.9 - properties-reader: 2.2.0 + node-fetch: 2.7.0(encoding@0.1.13) + proper-lockfile: 4.1.2 + properties-reader: 2.3.0 ssh-remote-port-forward: 1.0.4 tar-fs: 2.1.1 + tmp: 0.2.3 transitivePeerDependencies: - encoding - supports-color - dev: true - - /text-decoding@1.0.0: - resolution: {integrity: sha512-/0TJD42KDnVwKmDK6jj3xP7E2MG7SHAOG4tyTgyUCRPdHwvkquYNLEQltmdMa3owq3TkddCVcTsoctJI8VQNKA==} - dev: false - /text-table@0.2.0: - resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + text-table@0.2.0: {} - /textextensions@5.15.0: - resolution: {integrity: sha512-MeqZRHLuaGamUXGuVn2ivtU3LA3mLCCIO5kUGoohTCoGmCBg/+8yPhWVX9WSl9telvVd8erftjFk9Fwb2dD6rw==} - engines: {node: '>=0.8'} + textextensions@5.16.0: {} - /thenify-all@1.6.0: - resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} - engines: {node: '>=0.8'} + thenify-all@1.6.0: dependencies: thenify: 3.3.1 - /thenify@3.3.1: - resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + thenify@3.3.1: dependencies: any-promise: 1.3.0 - /throat@6.0.2: - resolution: {integrity: sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ==} - dev: false - - /throttle-debounce@3.0.1: - resolution: {integrity: sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg==} - engines: {node: '>=10'} - dev: false + throat@6.0.2: {} - /throttle-debounce@5.0.0: - resolution: {integrity: sha512-2iQTSgkkc1Zyk0MeVrt/3BvuOXYPl/R8Z0U2xxo9rjwNciaHDG3R+Lm6dh4EeUci49DanvBnuqI6jshoQQRGEg==} - engines: {node: '>=12.22'} + throttle-debounce@3.0.1: {} - /through2@2.0.5: - resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==} - dependencies: - readable-stream: 2.3.8 - xtend: 4.0.2 - dev: true + throttle-debounce@5.0.2: {} - /through@2.3.8: - resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + through@2.3.8: {} - /thunky@1.1.0: - resolution: {integrity: sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==} - dev: false + thunky@1.1.0: {} - /time-span@4.0.0: - resolution: {integrity: sha512-MyqZCTGLDZ77u4k+jqg4UlrzPTPZ49NDlaekU6uuFaJLzPIN1woaRXCbGeqOfxwc3Y37ZROGAJ614Rdv7Olt+g==} - engines: {node: '>=10'} - dependencies: - convert-hrtime: 3.0.0 - dev: true + timezones-list@3.0.3: {} - /timsort@0.3.0: - resolution: {integrity: sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A==} - dev: true + timsort@0.3.0: {} - /tiny-glob@0.2.9: - resolution: {integrity: sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==} + tiny-glob@0.2.9: dependencies: globalyzer: 0.1.0 globrex: 0.1.2 - dev: true - /tiny-hashes@1.0.1: - resolution: {integrity: sha512-knIN5zj4fl7kW4EBU5sLP20DWUvi/rVouvJezV0UAym2DkQaqm365Nyc8F3QEiOvunNDMxR8UhcXd1d5g+Wg1g==} - dev: true + tiny-invariant@1.3.3: {} - /tmp@0.0.33: - resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} - engines: {node: '>=0.6.0'} - dependencies: - os-tmpdir: 1.0.2 + tiny-lru@11.2.11: {} - /tmp@0.2.1: - resolution: {integrity: sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==} - engines: {node: '>=8.17.0'} + tinyexec@0.3.2: {} + + tmp@0.0.33: dependencies: - rimraf: 3.0.2 - dev: false - optional: true + os-tmpdir: 1.0.2 - /tmpl@1.0.5: - resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} + tmp@0.2.3: {} - /to-fast-properties@2.0.0: - resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} - engines: {node: '>=4'} + tmpl@1.0.5: {} - /to-regex-range@5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 - /toggle-selection@1.0.6: - resolution: {integrity: sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==} + toggle-selection@1.0.6: {} - /toidentifier@1.0.1: - resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} - engines: {node: '>=0.6'} + toidentifier@1.0.1: {} - /toml@3.0.0: - resolution: {integrity: sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==} - dev: true + totalist@3.0.1: {} - /touch@3.1.0: - resolution: {integrity: sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==} - hasBin: true - dependencies: - nopt: 1.0.10 - dev: true + touch@3.1.1: {} - /tough-cookie@4.1.2: - resolution: {integrity: sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==} - engines: {node: '>=6'} + tough-cookie@4.1.4: dependencies: psl: 1.9.0 - punycode: 2.3.0 + punycode: 2.3.1 universalify: 0.2.0 url-parse: 1.5.10 - dev: false - /tr46@0.0.3: - resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + tr46@0.0.3: {} - /tr46@1.0.1: - resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} + tr46@1.0.1: dependencies: - punycode: 2.3.0 - dev: false + punycode: 2.3.1 - /tr46@2.1.0: - resolution: {integrity: sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==} - engines: {node: '>=8'} + tr46@2.1.0: dependencies: - punycode: 2.3.0 - dev: false + punycode: 2.3.1 - /tr46@3.0.0: - resolution: {integrity: sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==} - engines: {node: '>=12'} + tr46@4.1.1: dependencies: - punycode: 2.3.0 - dev: false + punycode: 2.3.1 - /treeverse@1.0.4: - resolution: {integrity: sha512-whw60l7r+8ZU8Tu/Uc2yxtc4ZTZbR/PF3u1IPNKGQ6p8EICLb3Z2lAgoqw9bqYd8IkgnsaOcLzYHFckjqNsf0g==} + tree-kill@1.2.2: {} - /trough@2.1.0: - resolution: {integrity: sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==} - dev: true + treeverse@1.0.4: {} - /tryer@1.0.1: - resolution: {integrity: sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==} - dev: false + tryer@1.0.1: {} - /ts-easing@0.2.0: - resolution: {integrity: sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ==} - dev: false + ts-api-utils@1.3.0(typescript@5.6.3): + dependencies: + typescript: 5.6.3 - /ts-interface-checker@0.1.13: - resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + ts-easing@0.2.0: {} - /ts-jest@29.0.5(@babel/core@7.21.4)(esbuild@0.16.3)(jest@29.5.0)(typescript@4.9.5): - resolution: {integrity: sha512-PL3UciSgIpQ7f6XjVOmbi96vmDHUqAyqDr8YxzopDqX3kfgYtX1cuNeBjP+L9sFXi6nzsGGA6R3fP3DDDJyrxA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - hasBin: true - peerDependencies: - '@babel/core': '>=7.0.0-beta.0 <8' - '@jest/types': ^29.0.0 - babel-jest: ^29.0.0 - esbuild: '*' - jest: ^29.0.0 - typescript: '>=4.3' - peerDependenciesMeta: - '@babel/core': - optional: true - '@jest/types': - optional: true - babel-jest: - optional: true - esbuild: - optional: true + ts-interface-checker@0.1.13: {} + + ts-jest@29.0.5(@babel/core@7.26.10)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.10))(jest@29.7.0(@types/node@18.19.61)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@22.14.1)(typescript@5.6.3)))(typescript@5.6.3): dependencies: - '@babel/core': 7.21.4 bs-logger: 0.2.6 - esbuild: 0.16.3 fast-json-stable-stringify: 2.1.0 - jest: 29.5.0(@types/node@18.15.11)(ts-node@10.8.2) - jest-util: 29.5.0 + jest: 29.7.0(@types/node@18.19.61)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@22.14.1)(typescript@5.6.3)) + jest-util: 29.7.0 json5: 2.2.3 lodash.memoize: 4.1.2 make-error: 1.3.6 - semver: 7.5.0 - typescript: 4.9.5 + semver: 7.6.3 + typescript: 5.6.3 yargs-parser: 21.1.1 - dev: true + optionalDependencies: + '@babel/core': 7.26.10 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.26.10) - /ts-loader@9.4.2(typescript@4.9.5)(webpack@5.78.0): - resolution: {integrity: sha512-OmlC4WVmFv5I0PpaxYb+qGeGOdm5giHU7HwDDUjw59emP2UYMHy9fFSDcYgSNoH8sXcj4hGCSEhlDZ9ULeDraA==} - engines: {node: '>=12.0.0'} - peerDependencies: - typescript: '*' - webpack: ^5.0.0 + ts-jest@29.0.5(@babel/core@7.26.10)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.10))(jest@29.7.0(@types/node@22.14.1)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@22.14.1)(typescript@5.6.3)))(typescript@5.6.3): + dependencies: + bs-logger: 0.2.6 + fast-json-stable-stringify: 2.1.0 + jest: 29.7.0(@types/node@22.14.1)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@22.14.1)(typescript@5.6.3)) + jest-util: 29.7.0 + json5: 2.2.3 + lodash.memoize: 4.1.2 + make-error: 1.3.6 + semver: 7.6.3 + typescript: 5.6.3 + yargs-parser: 21.1.1 + optionalDependencies: + '@babel/core': 7.26.10 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.26.10) + + ts-jest@29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(jest@29.7.0(@types/node@18.19.61)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)))(typescript@5.6.3): + dependencies: + bs-logger: 0.2.6 + ejs: 3.1.10 + fast-json-stable-stringify: 2.1.0 + jest: 29.7.0(@types/node@18.19.61)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)) + jest-util: 29.7.0 + json5: 2.2.3 + lodash.memoize: 4.1.2 + make-error: 1.3.6 + semver: 7.6.3 + typescript: 5.6.3 + yargs-parser: 21.1.1 + optionalDependencies: + '@babel/core': 7.26.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.26.0) + + ts-jest@29.2.5(@babel/core@7.26.10)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.10))(jest@29.7.0(@types/node@18.19.61)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)))(typescript@5.6.3): + dependencies: + bs-logger: 0.2.6 + ejs: 3.1.10 + fast-json-stable-stringify: 2.1.0 + jest: 29.7.0(@types/node@18.19.61)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3)) + jest-util: 29.7.0 + json5: 2.2.3 + lodash.memoize: 4.1.2 + make-error: 1.3.6 + semver: 7.6.3 + typescript: 5.6.3 + yargs-parser: 21.1.1 + optionalDependencies: + '@babel/core': 7.26.10 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.26.10) + + ts-jest@29.2.5(@babel/core@7.26.10)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.10))(jest@29.7.0(@types/node@18.19.61)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@22.14.1)(typescript@5.6.3)))(typescript@5.6.3): + dependencies: + bs-logger: 0.2.6 + ejs: 3.1.10 + fast-json-stable-stringify: 2.1.0 + jest: 29.7.0(@types/node@18.19.61)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@22.14.1)(typescript@5.6.3)) + jest-util: 29.7.0 + json5: 2.2.3 + lodash.memoize: 4.1.2 + make-error: 1.3.6 + semver: 7.6.3 + typescript: 5.6.3 + yargs-parser: 21.1.1 + optionalDependencies: + '@babel/core': 7.26.10 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.26.10) + + ts-jest@29.2.5(@babel/core@7.26.10)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.10))(jest@29.7.0(@types/node@22.14.1)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@22.14.1)(typescript@5.6.3)))(typescript@5.6.3): + dependencies: + bs-logger: 0.2.6 + ejs: 3.1.10 + fast-json-stable-stringify: 2.1.0 + jest: 29.7.0(@types/node@22.14.1)(ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@22.14.1)(typescript@5.6.3)) + jest-util: 29.7.0 + json5: 2.2.3 + lodash.memoize: 4.1.2 + make-error: 1.3.6 + semver: 7.6.3 + typescript: 5.6.3 + yargs-parser: 21.1.1 + optionalDependencies: + '@babel/core': 7.26.10 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.26.10) + + ts-loader@9.5.1(typescript@5.6.3)(webpack@5.95.0): dependencies: chalk: 4.1.2 - enhanced-resolve: 5.12.0 - micromatch: 4.0.5 - semver: 7.4.0 - typescript: 4.9.5 - webpack: 5.78.0(esbuild@0.16.3)(webpack-cli@5.0.1) - dev: true + enhanced-resolve: 5.17.1 + micromatch: 4.0.8 + semver: 7.6.3 + source-map: 0.7.4 + typescript: 5.6.3 + webpack: 5.95.0(@swc/core@1.11.10(@swc/helpers@0.5.15))(webpack-cli@5.1.4) - /ts-morph@12.0.0: - resolution: {integrity: sha512-VHC8XgU2fFW7yO1f/b3mxKDje1vmyzFXHWzOYmKEkCEwcLjDtbdLgBQviqj4ZwP4MJkQtRo6Ha2I29lq/B+VxA==} + ts-loader@9.5.1(typescript@5.6.3)(webpack@5.99.5): dependencies: - '@ts-morph/common': 0.11.1 - code-block-writer: 10.1.1 - dev: true + chalk: 4.1.2 + enhanced-resolve: 5.17.1 + micromatch: 4.0.8 + semver: 7.6.3 + source-map: 0.7.4 + typescript: 5.6.3 + webpack: 5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(webpack-cli@6.0.1) - /ts-morph@13.0.3: - resolution: {integrity: sha512-pSOfUMx8Ld/WUreoSzvMFQG5i9uEiWIsBYjpU9+TTASOeUa89j5HykomeqVULm1oqWtBdleI3KEFRLrlA3zGIw==} + ts-morph@13.0.3: dependencies: '@ts-morph/common': 0.12.3 code-block-writer: 11.0.3 - dev: true - /ts-node@10.8.2(@types/node@16.11.6)(typescript@4.9.5): - resolution: {integrity: sha512-LYdGnoGddf1D6v8REPtIH+5iq/gTDuZqv2/UJUU7tKjuEU8xVZorBM+buCGNjj+pGEud+sOoM4CX3/YzINpENA==} - hasBin: true - peerDependencies: - '@swc/core': '>=1.2.50' - '@swc/wasm': '>=1.2.50' - '@types/node': '*' - typescript: '>=2.7' - peerDependenciesMeta: - '@swc/core': - optional: true - '@swc/wasm': - optional: true + ts-node-dev@2.0.0(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3): dependencies: - '@cspotcode/source-map-support': 0.8.1 - '@tsconfig/node10': 1.0.9 - '@tsconfig/node12': 1.0.11 - '@tsconfig/node14': 1.0.3 - '@tsconfig/node16': 1.0.3 - '@types/node': 16.11.6 - acorn: 8.8.2 - acorn-walk: 8.2.0 - arg: 4.1.3 - create-require: 1.1.1 - diff: 4.0.2 - make-error: 1.3.6 - typescript: 4.9.5 - v8-compile-cache-lib: 3.0.1 - yn: 3.1.1 - dev: true + chokidar: 3.6.0 + dynamic-dedupe: 0.3.0 + minimist: 1.2.8 + mkdirp: 1.0.4 + resolve: 1.22.8 + rimraf: 2.7.1 + source-map-support: 0.5.21 + tree-kill: 1.2.2 + ts-node: 10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3) + tsconfig: 7.0.0 + typescript: 5.6.3 + transitivePeerDependencies: + - '@swc/core' + - '@swc/wasm' + - '@types/node' - /ts-node@10.8.2(@types/node@18.15.11)(typescript@4.9.5): - resolution: {integrity: sha512-LYdGnoGddf1D6v8REPtIH+5iq/gTDuZqv2/UJUU7tKjuEU8xVZorBM+buCGNjj+pGEud+sOoM4CX3/YzINpENA==} - hasBin: true - peerDependencies: - '@swc/core': '>=1.2.50' - '@swc/wasm': '>=1.2.50' - '@types/node': '*' - typescript: '>=2.7' - peerDependenciesMeta: - '@swc/core': - optional: true - '@swc/wasm': - optional: true + ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@18.19.61)(typescript@5.6.3): dependencies: '@cspotcode/source-map-support': 0.8.1 - '@tsconfig/node10': 1.0.9 + '@tsconfig/node10': 1.0.11 '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 - '@tsconfig/node16': 1.0.3 - '@types/node': 18.15.11 - acorn: 8.8.2 - acorn-walk: 8.2.0 + '@tsconfig/node16': 1.0.4 + '@types/node': 18.19.61 + acorn: 8.14.0 + acorn-walk: 8.3.4 arg: 4.1.3 create-require: 1.1.1 diff: 4.0.2 make-error: 1.3.6 - typescript: 4.9.5 + typescript: 5.6.3 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 + optionalDependencies: + '@swc/core': 1.11.10(@swc/helpers@0.5.15) - /ts-node@10.9.1(@types/node@14.18.33)(typescript@4.3.4): - resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} - hasBin: true - peerDependencies: - '@swc/core': '>=1.2.50' - '@swc/wasm': '>=1.2.50' - '@types/node': '*' - typescript: '>=2.7' - peerDependenciesMeta: - '@swc/core': - optional: true - '@swc/wasm': - optional: true + ts-node@10.9.2(@swc/core@1.11.10(@swc/helpers@0.5.15))(@types/node@22.14.1)(typescript@5.6.3): dependencies: '@cspotcode/source-map-support': 0.8.1 - '@tsconfig/node10': 1.0.9 + '@tsconfig/node10': 1.0.11 '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 - '@tsconfig/node16': 1.0.3 - '@types/node': 14.18.33 - acorn: 8.8.2 - acorn-walk: 8.2.0 + '@tsconfig/node16': 1.0.4 + '@types/node': 22.14.1 + acorn: 8.14.0 + acorn-walk: 8.3.4 arg: 4.1.3 create-require: 1.1.1 diff: 4.0.2 make-error: 1.3.6 - typescript: 4.3.4 + typescript: 5.6.3 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 - dev: true - - /ts-toolbelt@6.15.5: - resolution: {integrity: sha512-FZIXf1ksVyLcfr7M317jbB67XFJhOO1YqdTcuGaq9q5jLUoTikukZ+98TPjKiP2jC5CgmYdWWYs0s2nLSU0/1A==} - dev: true + optionalDependencies: + '@swc/core': 1.11.10(@swc/helpers@0.5.15) - /ts-toolbelt@9.6.0: - resolution: {integrity: sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w==} - dev: true + ts-toolbelt@9.6.0: {} - /tsconfig-paths@3.14.2: - resolution: {integrity: sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==} + tsconfig-paths@3.15.0: dependencies: '@types/json5': 0.0.29 json5: 1.0.2 minimist: 1.2.8 strip-bom: 3.0.0 - /tsconfig-paths@4.2.0: - resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} - engines: {node: '>=6'} + tsconfig-paths@4.2.0: dependencies: json5: 2.2.3 minimist: 1.2.8 strip-bom: 3.0.0 - dev: true - /tslib@1.10.0: - resolution: {integrity: sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==} - dev: true + tsconfig@7.0.0: + dependencies: + '@types/strip-bom': 3.0.0 + '@types/strip-json-comments': 0.0.30 + strip-bom: 3.0.0 + strip-json-comments: 2.0.1 - /tslib@1.14.1: - resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + tslib@1.14.1: {} - /tslib@2.5.0: - resolution: {integrity: sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==} + tslib@2.8.0: {} - /tsutils@3.21.0(typescript@4.9.4): - resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} - engines: {node: '>= 6'} - peerDependencies: - typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' + tsutils@3.21.0(typescript@5.6.3): dependencies: tslib: 1.14.1 - typescript: 4.9.4 + typescript: 5.6.3 - /tsutils@3.21.0(typescript@4.9.5): - resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} - engines: {node: '>= 6'} - peerDependencies: - typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' + tuf-js@1.1.7: dependencies: - tslib: 1.14.1 - typescript: 4.9.5 + '@tufjs/models': 1.0.4 + debug: 4.3.7(supports-color@5.5.0) + make-fetch-happen: 11.1.1 + transitivePeerDependencies: + - supports-color - /tunnel-agent@0.6.0: - resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} + tunnel-agent@0.6.0: dependencies: safe-buffer: 5.2.1 - dev: false - /turbo-darwin-64@1.2.16: - resolution: {integrity: sha512-dyitLQJdH3uLVdlH9jAkP4LqEO/K+wOXjUqOzjTciRLjQPzmsNY60/bmFHODADK4eBBl1nxbtn7tmmoT4vS1qA==} - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: true + turbo-darwin-64@1.2.16: optional: true - /turbo-darwin-arm64@1.2.16: - resolution: {integrity: sha512-Ex6uM4HU7rGXdhvJMpzNpp6qxglJ98nWeIi5qR/lBXHLjK3UCvSW8BEALArUJYJTXS9FZBq1a5LowFqXYsfDcA==} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: true + turbo-darwin-arm64@1.2.16: optional: true - /turbo-freebsd-64@1.2.16: - resolution: {integrity: sha512-onRGKMvog8B3XDssSBIAg+FrEq9pcBoAybP7bpi/uYIH1L/WQ7YMmLn88X9JX19ehYuVOVZrjap4jWH2GIkU8A==} - cpu: [x64] - os: [freebsd] - requiresBuild: true - dev: true + turbo-freebsd-64@1.2.16: optional: true - /turbo-freebsd-arm64@1.2.16: - resolution: {integrity: sha512-S0EqPqxwnJuVNNXRgcHB0r8ai8LSrpHdihVJKRM7WYmIR7isccBEf/G9agrt73sCXwjvenxFs4HDR7cSvGt14Q==} - cpu: [arm64] - os: [freebsd] - requiresBuild: true - dev: true + turbo-freebsd-arm64@1.2.16: optional: true - /turbo-linux-32@1.2.16: - resolution: {integrity: sha512-ecbqmGOxgTWePGrowtwyvZGfvwaLxFWmPK21cU0PS+fzoZBaVmzYmniTdd/2EkGCw7TOPhtiT22v96fWcnRycA==} - cpu: [ia32] - os: [linux] - requiresBuild: true - dev: true + turbo-linux-32@1.2.16: optional: true - /turbo-linux-64@1.2.16: - resolution: {integrity: sha512-q6gtdMWCzM0Sktkd73zcaQjNoeM1MjtrbwQBctWN/Sgj0eiPBPnzpIvokvx98x7RLf4qyI99/mlme0Dn5fx21A==} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true + turbo-linux-64@1.2.16: optional: true - /turbo-linux-arm64@1.2.16: - resolution: {integrity: sha512-gUf67tYJ/N09WAZTTmtUWYrqm381tZxiulnRGAIM+iRsaTrweyUKZaYXwJvlPpI/cQOw25wCG9/IyvxLeagL8A==} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true + turbo-linux-arm64@1.2.16: optional: true - /turbo-linux-arm@1.2.16: - resolution: {integrity: sha512-du7uvExELNb89V3g7iM0XP21fR1Yl3EoHRcOfQz32oUqnS7idCKvbEowM9LtiluQl1dKcOIJjn1nlvvsqzkhOg==} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: true + turbo-linux-arm@1.2.16: optional: true - /turbo-linux-mips64le@1.2.16: - resolution: {integrity: sha512-U5BM+Ql3z13uRtwMmKH/8eL+9DdTgyijC2gaX4xP0RTlcN7WfAstg8Fg/Tn2Vw9vtpVDdxwpw7dvX4kw2ghhpA==} - cpu: [mips64el] - os: [linux] - requiresBuild: true - dev: true + turbo-linux-mips64le@1.2.16: optional: true - /turbo-linux-ppc64le@1.2.16: - resolution: {integrity: sha512-HQWSCmVZyc5chw7Ie2ZcfZPfmM06mbEEu0Wl11Y5QWh1ZzhPNQHs/TsF4I9r146wHi62XgcrKFjkw4ARZiWsLA==} - cpu: [ppc64] - os: [linux] - requiresBuild: true - dev: true + turbo-linux-ppc64le@1.2.16: optional: true - /turbo-windows-32@1.2.16: - resolution: {integrity: sha512-0ZtPz5FK2qZjznMG4vvRyaabrhO8BgbN+tBx1wjXSuoICTAjYi5TwRVVRh59c3x7qQmR21Cv33CrhLBPRfeAlg==} - cpu: [ia32] - os: [win32] - requiresBuild: true - dev: true + turbo-windows-32@1.2.16: optional: true - /turbo-windows-64@1.2.16: - resolution: {integrity: sha512-j8iAIixq/rGfBpHNbYOosxMasZrGuMzLILEuQGDxZgKNpYgobJ15QFHQlGR9sit1b8qPU5zZX4CtByRtkgH1Bw==} - cpu: [x64] - os: [win32] - requiresBuild: true - dev: true + turbo-windows-64@1.2.16: optional: true - /turbo-windows-arm64@1.2.16: - resolution: {integrity: sha512-4GpcJG3B8R9WDhwfT8fu6ZmOOfseCg6Q1cy/G8/zpJQk769yYcSnD8MgQhYgHB58aVFxZcMxBvLL6UA0UrpgWA==} - cpu: [arm64] - os: [win32] - requiresBuild: true - dev: true + turbo-windows-arm64@1.2.16: optional: true - /turbo@1.2.16: - resolution: {integrity: sha512-PPUa2COKgFkyb6N3uF9AnIY3l9FZkF15QQ3U1K2wpI01D3gyGKQO0Q3DUQ4ipmciP0teBfL7H+l/QTrUA9IVvQ==} - hasBin: true - requiresBuild: true + turbo@1.2.16: optionalDependencies: turbo-darwin-64: 1.2.16 turbo-darwin-arm64: 1.2.16 @@ -24760,540 +32566,316 @@ packages: turbo-windows-32: 1.2.16 turbo-windows-64: 1.2.16 turbo-windows-arm64: 1.2.16 - dev: true - /tweetnacl@0.14.5: - resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==} - dev: true + tweetnacl@0.14.5: {} - /type-check@0.3.2: - resolution: {integrity: sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==} - engines: {node: '>= 0.8.0'} + type-check@0.3.2: dependencies: prelude-ls: 1.1.2 - /type-check@0.4.0: - resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} - engines: {node: '>= 0.8.0'} + type-check@0.4.0: dependencies: prelude-ls: 1.2.1 - /type-detect@4.0.8: - resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} - engines: {node: '>=4'} + type-detect@4.0.8: {} - /type-fest@0.16.0: - resolution: {integrity: sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==} - engines: {node: '>=10'} - dev: false + type-fest@0.16.0: {} - /type-fest@0.20.2: - resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} - engines: {node: '>=10'} + type-fest@0.20.2: {} - /type-fest@0.21.3: - resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} - engines: {node: '>=10'} + type-fest@0.21.3: {} - /type-fest@0.6.0: - resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==} - engines: {node: '>=8'} + type-fest@0.6.0: {} - /type-fest@0.8.1: - resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} - engines: {node: '>=8'} + type-fest@0.7.1: {} - /type-fest@3.8.0: - resolution: {integrity: sha512-FVNSzGQz9Th+/9R6Lvv7WIAkstylfHN2/JYxkyhhmKFYh9At2DST8t6L6Lref9eYO8PXFTfG9Sg1Agg0K3vq3Q==} - engines: {node: '>=14.16'} - dev: true + type-fest@0.8.1: {} - /type-is@1.6.18: - resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} - engines: {node: '>= 0.6'} + type-fest@2.19.0: {} + + type-fest@3.13.1: {} + + type-is@1.6.18: dependencies: media-typer: 0.3.0 mime-types: 2.1.35 - /typed-array-length@1.0.4: - resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==} + typed-array-buffer@1.0.2: dependencies: - call-bind: 1.0.2 - for-each: 0.3.3 - is-typed-array: 1.1.10 + call-bind: 1.0.7 + es-errors: 1.3.0 + is-typed-array: 1.1.13 - /typedarray-to-buffer@3.1.5: - resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} + typed-array-byte-length@1.0.1: dependencies: - is-typedarray: 1.0.0 - dev: false - - /typescript@3.9.10: - resolution: {integrity: sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==} - engines: {node: '>=4.2.0'} - hasBin: true - dev: true - - /typescript@4.3.4: - resolution: {integrity: sha512-uauPG7XZn9F/mo+7MrsRjyvbxFpzemRjKEZXS4AK83oP2KKOJPvb+9cO/gmnv8arWZvhnjVOXz7B49m1l0e9Ew==} - engines: {node: '>=4.2.0'} - hasBin: true - dev: true + call-bind: 1.0.7 + for-each: 0.3.3 + gopd: 1.0.1 + has-proto: 1.0.3 + is-typed-array: 1.1.13 - /typescript@4.9.4: - resolution: {integrity: sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==} - engines: {node: '>=4.2.0'} - hasBin: true + typed-array-byte-offset@1.0.2: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.7 + for-each: 0.3.3 + gopd: 1.0.1 + has-proto: 1.0.3 + is-typed-array: 1.1.13 - /typescript@4.9.5: - resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} - engines: {node: '>=4.2.0'} - hasBin: true + typed-array-length@1.0.6: + dependencies: + call-bind: 1.0.7 + for-each: 0.3.3 + gopd: 1.0.1 + has-proto: 1.0.3 + is-typed-array: 1.1.13 + possible-typed-array-names: 1.0.0 - /ua-parser-js@0.7.35: - resolution: {integrity: sha512-veRf7dawaj9xaWEu9HoTVn5Pggtc/qj+kqTOFvNiN1l0YdxwC1kvel57UCjThjGa3BHBihE8/UJAHI+uQHmd/g==} - dev: false + typedarray-to-buffer@3.1.5: + dependencies: + is-typedarray: 1.0.0 - /uc.micro@1.0.6: - resolution: {integrity: sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==} - dev: false - optional: true + typescript@4.9.5: {} - /ufo@1.1.1: - resolution: {integrity: sha512-MvlCc4GHrmZdAllBc0iUDowff36Q9Ndw/UzqmEKyrfSzokTd9ZCy1i+IIk5hrYKkjoYVQyNbrw7/F8XJ2rEwTg==} - dev: true + typescript@5.6.3: {} - /uglify-js@3.17.4: - resolution: {integrity: sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==} - engines: {node: '>=0.8.0'} - hasBin: true - dev: false + ua-parser-js@1.0.39: {} - /unbox-primitive@1.0.2: - resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} + unbox-primitive@1.0.2: dependencies: - call-bind: 1.0.2 + call-bind: 1.0.7 has-bigints: 1.0.2 has-symbols: 1.0.3 which-boxed-primitive: 1.0.2 - /undefsafe@2.0.5: - resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==} - dev: true + undefsafe@2.0.5: {} - /underscore@1.13.6: - resolution: {integrity: sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==} - dev: false - optional: true + underscore@1.12.1: {} - /unfetch@3.1.2: - resolution: {integrity: sha512-L0qrK7ZeAudGiKYw6nzFjnJ2D5WHblUBwmHIqtPS6oKUd+Hcpk7/hKsSmcHsTlpd1TbTNsiRBUKRq3bHLNIqIw==} - dev: true + undici-types@5.26.5: {} - /unfetch@4.2.0: - resolution: {integrity: sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==} - dev: true + undici-types@6.20.0: {} - /unicode-canonical-property-names-ecmascript@2.0.0: - resolution: {integrity: sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==} - engines: {node: '>=4'} + undici-types@6.21.0: {} - /unicode-match-property-ecmascript@2.0.0: - resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==} - engines: {node: '>=4'} - dependencies: - unicode-canonical-property-names-ecmascript: 2.0.0 - unicode-property-aliases-ecmascript: 2.1.0 + undici@6.21.2: {} - /unicode-match-property-value-ecmascript@2.1.0: - resolution: {integrity: sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==} - engines: {node: '>=4'} + undici@7.11.0: {} - /unicode-property-aliases-ecmascript@2.1.0: - resolution: {integrity: sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==} - engines: {node: '>=4'} + unfetch@3.1.2: {} + + unfetch@4.2.0: {} + + unicode-canonical-property-names-ecmascript@2.0.1: {} - /unified@10.1.2: - resolution: {integrity: sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==} + unicode-match-property-ecmascript@2.0.0: dependencies: - '@types/unist': 2.0.6 - bail: 2.0.2 - extend: 3.0.2 - is-buffer: 2.0.5 - is-plain-obj: 4.1.0 - trough: 2.1.0 - vfile: 5.3.7 - dev: true + unicode-canonical-property-names-ecmascript: 2.0.1 + unicode-property-aliases-ecmascript: 2.1.0 - /uniq@1.0.1: - resolution: {integrity: sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA==} - dev: true + unicode-match-property-value-ecmascript@2.2.0: {} - /uniqs@2.0.0: - resolution: {integrity: sha512-mZdDpf3vBV5Efh29kMw5tXoup/buMgxLzOt/XKFKcVmi+15ManNQWr6HfZ2aiZTYlYixbdNJ0KFmIZIv52tHSQ==} - dev: true + unicode-property-aliases-ecmascript@2.1.0: {} - /unique-filename@1.1.1: - resolution: {integrity: sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==} + unicorn-magic@0.1.0: {} + + unique-filename@1.1.1: dependencies: unique-slug: 2.0.2 - /unique-filename@2.0.1: - resolution: {integrity: sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + unique-filename@2.0.1: dependencies: unique-slug: 3.0.0 - /unique-slug@2.0.2: - resolution: {integrity: sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==} + unique-filename@3.0.0: dependencies: - imurmurhash: 0.1.4 + unique-slug: 4.0.0 - /unique-slug@3.0.0: - resolution: {integrity: sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + unique-slug@2.0.2: dependencies: imurmurhash: 0.1.4 - /unique-string@2.0.0: - resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==} - engines: {node: '>=8'} - dependencies: - crypto-random-string: 2.0.0 - dev: false - - /unist-builder@3.0.1: - resolution: {integrity: sha512-gnpOw7DIpCA0vpr6NqdPvTWnlPTApCTRzr+38E6hCWx3rz/cjo83SsKIlS1Z+L5ttScQ2AwutNnb8+tAvpb6qQ==} - dependencies: - '@types/unist': 2.0.6 - dev: true - - /unist-util-generated@2.0.1: - resolution: {integrity: sha512-qF72kLmPxAw0oN2fwpWIqbXAVyEqUzDHMsbtPvOudIlUzXYFIeQIuxXQCRCFh22B7cixvU0MG7m3MW8FTq/S+A==} - dev: true - - /unist-util-is@5.2.1: - resolution: {integrity: sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==} + unique-slug@3.0.0: dependencies: - '@types/unist': 2.0.6 - dev: true + imurmurhash: 0.1.4 - /unist-util-position-from-estree@1.1.2: - resolution: {integrity: sha512-poZa0eXpS+/XpoQwGwl79UUdea4ol2ZuCYguVaJS4qzIOMDzbqz8a3erUCOmubSZkaOuGamb3tX790iwOIROww==} + unique-slug@4.0.0: dependencies: - '@types/unist': 2.0.6 - dev: true + imurmurhash: 0.1.4 - /unist-util-position@4.0.4: - resolution: {integrity: sha512-kUBE91efOWfIVBo8xzh/uZQ7p9ffYRtUbMRZBNFYwf0RK8koUMx6dGUfwylLOKmaT2cs4wSW96QoYUSXAyEtpg==} + unique-string@2.0.0: dependencies: - '@types/unist': 2.0.6 - dev: true + crypto-random-string: 2.0.0 - /unist-util-remove-position@4.0.2: - resolution: {integrity: sha512-TkBb0HABNmxzAcfLf4qsIbFbaPDvMO6wa3b3j4VcEzFVaw1LBKwnW4/sRJ/atSLSzoIg41JWEdnE7N6DIhGDGQ==} - dependencies: - '@types/unist': 2.0.6 - unist-util-visit: 4.1.2 - dev: true + universal-user-agent@6.0.1: {} - /unist-util-stringify-position@3.0.3: - resolution: {integrity: sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg==} - dependencies: - '@types/unist': 2.0.6 - dev: true + universalify@0.2.0: {} - /unist-util-visit-parents@5.1.3: - resolution: {integrity: sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==} - dependencies: - '@types/unist': 2.0.6 - unist-util-is: 5.2.1 - dev: true + universalify@2.0.1: {} - /unist-util-visit@4.1.2: - resolution: {integrity: sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==} - dependencies: - '@types/unist': 2.0.6 - unist-util-is: 5.2.1 - unist-util-visit-parents: 5.1.3 - dev: true + unpipe@1.0.0: {} - /universal-user-agent@6.0.0: - resolution: {integrity: sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==} + unquote@1.1.1: {} - /universalify@0.1.2: - resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} - engines: {node: '>= 4.0.0'} - dev: true + untildify@4.0.0: {} - /universalify@0.2.0: - resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} - engines: {node: '>= 4.0.0'} - dev: false + upath@1.2.0: {} - /universalify@2.0.0: - resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==} - engines: {node: '>= 10.0.0'} + update-browserslist-db@1.1.1(browserslist@4.24.2): + dependencies: + browserslist: 4.24.2 + escalade: 3.2.0 + picocolors: 1.1.1 - /unpipe@1.0.0: - resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} - engines: {node: '>= 0.8'} + update-browserslist-db@1.1.3(browserslist@4.25.1): + dependencies: + browserslist: 4.25.1 + escalade: 3.2.0 + picocolors: 1.1.1 - /unquote@1.1.1: - resolution: {integrity: sha512-vRCqFv6UhXpWxZPyGDh/F3ZpNv8/qo7w6iufLpQg9aKnQ71qM4B5KiI7Mia9COcjEhrO9LueHpMYjYzsWH3OIg==} + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 - /untildify@4.0.0: - resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==} - engines: {node: '>=8'} + url-parse@1.5.10: + dependencies: + querystringify: 2.2.0 + requires-port: 1.0.0 - /upath@1.2.0: - resolution: {integrity: sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==} - engines: {node: '>=4'} - dev: false + url-template@2.0.8: {} - /update-browserslist-db@1.0.11(browserslist@4.21.5): - resolution: {integrity: sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' + use-callback-ref@1.3.3(@types/react@19.0.10)(react@19.0.0): dependencies: - browserslist: 4.21.5 - escalade: 3.1.1 - picocolors: 1.0.0 - - /upper-case@1.1.3: - resolution: {integrity: sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA==} - dev: false + react: 19.0.0 + tslib: 2.8.0 + optionalDependencies: + '@types/react': 19.0.10 - /uri-js@4.4.1: - resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + use-composed-ref@1.3.0(react@18.3.1): dependencies: - punycode: 2.3.0 + react: 18.3.1 - /url-parse@1.5.10: - resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} + use-debounce@10.0.4(react@19.0.0): dependencies: - querystringify: 2.2.0 - requires-port: 1.0.0 - dev: false + react: 19.0.0 - /url-template@2.0.8: - resolution: {integrity: sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw==} - dev: false + use-debounce@10.0.5(react@18.3.1): + dependencies: + react: 18.3.1 - /use-composed-ref@1.3.0(react@18.2.0): - resolution: {integrity: sha512-GLMG0Jc/jiKov/3Ulid1wbv3r54K9HlMW29IWcDFPEqFkSO2nS0MuefWgMJpeHQ9YJeXDL3ZUF+P3jdXlZX/cQ==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 + use-isomorphic-layout-effect@1.1.2(@types/react@18.3.3)(react@18.3.1): dependencies: - react: 18.2.0 - dev: false + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.3 - /use-isomorphic-layout-effect@1.1.2(@types/react@18.0.28)(react@18.2.0): - resolution: {integrity: sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==} - peerDependencies: - '@types/react': '*' - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@types/react': - optional: true + use-latest@1.2.1(@types/react@18.3.3)(react@18.3.1): dependencies: - '@types/react': 18.0.28 - react: 18.2.0 - dev: false + react: 18.3.1 + use-isomorphic-layout-effect: 1.1.2(@types/react@18.3.3)(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.3 - /use-latest@1.2.1(@types/react@18.0.28)(react@18.2.0): - resolution: {integrity: sha512-xA+AVm/Wlg3e2P/JiItTziwS7FK92LWrDB0p+hgXloIMuVCeJJ8v6f0eeHyPZaJrM+usM1FkFfbNCrJGs8A/zw==} - peerDependencies: - '@types/react': '*' - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@types/react': - optional: true + use-sidecar@1.1.3(@types/react@19.0.10)(react@19.0.0): dependencies: - '@types/react': 18.0.28 - react: 18.2.0 - use-isomorphic-layout-effect: 1.1.2(@types/react@18.0.28)(react@18.2.0) - dev: false + detect-node-es: 1.1.0 + react: 19.0.0 + tslib: 2.8.0 + optionalDependencies: + '@types/react': 19.0.10 - /use-sync-external-store@1.2.0(react@18.2.0): - resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 + use-sync-external-store@1.2.2(react@18.3.1): dependencies: - react: 18.2.0 - dev: false + react: 18.3.1 - /util-deprecate@1.0.2: - resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + util-deprecate@1.0.2: {} - /util.promisify@1.0.1: - resolution: {integrity: sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==} + util.promisify@1.0.1: dependencies: - define-properties: 1.2.0 - es-abstract: 1.21.2 + define-properties: 1.2.1 + es-abstract: 1.23.3 has-symbols: 1.0.3 - object.getownpropertydescriptors: 2.1.5 + object.getownpropertydescriptors: 2.1.8 - /utila@0.4.0: - resolution: {integrity: sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==} - dev: false + utila@0.4.0: {} - /utils-merge@1.0.1: - resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} - engines: {node: '>= 0.4.0'} + utils-merge@1.0.1: {} - /uuid@8.3.2: - resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} - hasBin: true - dev: false + uuid@11.1.0: {} - /uuid@9.0.0: - resolution: {integrity: sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==} - hasBin: true - dev: false + uuid@8.3.2: {} - /uvu@0.5.6: - resolution: {integrity: sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==} - engines: {node: '>=8'} - hasBin: true - dependencies: - dequal: 2.0.3 - diff: 5.1.0 - kleur: 4.1.5 - sade: 1.8.1 - dev: true + uuid@9.0.1: {} - /v8-compile-cache-lib@3.0.1: - resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + v8-compile-cache-lib@3.0.1: {} - /v8-to-istanbul@8.1.1: - resolution: {integrity: sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w==} - engines: {node: '>=10.12.0'} + v8-to-istanbul@8.1.1: dependencies: - '@types/istanbul-lib-coverage': 2.0.4 + '@types/istanbul-lib-coverage': 2.0.6 convert-source-map: 1.9.0 source-map: 0.7.4 - dev: false - /v8-to-istanbul@9.1.0: - resolution: {integrity: sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==} - engines: {node: '>=10.12.0'} + v8-to-istanbul@9.3.0: dependencies: - '@jridgewell/trace-mapping': 0.3.18 - '@types/istanbul-lib-coverage': 2.0.4 - convert-source-map: 1.9.0 - dev: true + '@jridgewell/trace-mapping': 0.3.25 + '@types/istanbul-lib-coverage': 2.0.6 + convert-source-map: 2.0.0 - /valid-data-url@3.0.1: - resolution: {integrity: sha512-jOWVmzVceKlVVdwjNSenT4PbGghU0SBIizAev8ofZVgivk/TVHXSbNL8LP6M3spZvkR9/QolkyJavGSX5Cs0UA==} - engines: {node: '>=10'} - dev: false + valid-data-url@3.0.1: {} - /validate-npm-package-license@3.0.4: - resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} + validate-npm-package-license@3.0.4: dependencies: spdx-correct: 3.2.0 spdx-expression-parse: 3.0.1 - /validate-npm-package-name@3.0.0: - resolution: {integrity: sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw==} + validate-npm-package-name@3.0.0: dependencies: builtins: 1.0.3 - /validate.io-array@1.0.6: - resolution: {integrity: sha512-DeOy7CnPEziggrOO5CZhVKJw6S3Yi7e9e65R1Nl/RTN1vTQKnzjfvks0/8kQ40FP/dsjRAOd4hxmJ7uLa6vxkg==} - dev: false + validate-npm-package-name@5.0.1: {} - /validate.io-function@1.0.2: - resolution: {integrity: sha512-LlFybRJEriSuBnUhQyG5bwglhh50EpTL2ul23MPIuR1odjO7XaMLFV8vHGwp7AZciFxtYOeiSCT5st+XSPONiQ==} - dev: false + validate.io-array@1.0.6: {} - /validate.io-integer-array@1.0.0: - resolution: {integrity: sha512-mTrMk/1ytQHtCY0oNO3dztafHYyGU88KL+jRxWuzfOmQb+4qqnWmI+gykvGp8usKZOM0H7keJHEbRaFiYA0VrA==} + validate.io-function@1.0.2: {} + + validate.io-integer-array@1.0.0: dependencies: validate.io-array: 1.0.6 validate.io-integer: 1.0.5 - dev: false - /validate.io-integer@1.0.5: - resolution: {integrity: sha512-22izsYSLojN/P6bppBqhgUDjCkr5RY2jd+N2a3DCAUey8ydvrZ/OkGvFPR7qfOpwR2LC5p4Ngzxz36g5Vgr/hQ==} + validate.io-integer@1.0.5: dependencies: validate.io-number: 1.0.3 - dev: false - /validate.io-number@1.0.3: - resolution: {integrity: sha512-kRAyotcbNaSYoDnXvb4MHg/0a1egJdLwS6oJ38TJY7aw9n93Fl/3blIXdyYvPOp55CNxywooG/3BcrwNrBpcSg==} - dev: false - - /vary@1.1.2: - resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} - engines: {node: '>= 0.8'} - - /vendors@1.0.4: - resolution: {integrity: sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w==} - dev: true - - /vercel@28.18.5(@types/node@18.15.11)(ts-node@10.8.2): - resolution: {integrity: sha512-WdW5s7w2eGmvKwrIyG32BR/N0hKz9iiZe/INyqopW2Ql+E768ROSP+r+SFpWLMpkGGw14QnmZsFutZ5r7IbpJg==} - engines: {node: '>= 14'} - hasBin: true - requiresBuild: true - dependencies: - '@vercel/build-utils': 6.7.1 - '@vercel/go': 2.4.4 - '@vercel/hydrogen': 0.0.62 - '@vercel/next': 3.7.4 - '@vercel/node': 2.10.3 - '@vercel/python': 3.1.58 - '@vercel/redwood': 1.1.14 - '@vercel/remix-builder': 1.8.4(@types/node@18.15.11)(ts-node@10.8.2) - '@vercel/ruby': 1.3.75 - '@vercel/static-build': 1.3.23 - transitivePeerDependencies: - - '@remix-run/serve' - - '@swc/core' - - '@swc/wasm' - - '@types/node' - - bluebird - - bufferutil - - encoding - - less - - sass - - stylus - - sugarss - - supports-color - - terser - - ts-node - - utf-8-validate - dev: true + validate.io-number@1.0.3: {} - /vfile-location@4.1.0: - resolution: {integrity: sha512-YF23YMyASIIJXpktBa4vIGLJ5Gs88UB/XePgqPmTa7cDA+JeO3yclbpheQYCHjVHBn/yePzrXuygIL+xbvRYHw==} - dependencies: - '@types/unist': 2.0.6 - vfile: 5.3.7 - dev: true + vary@1.1.2: {} - /vfile-message@3.1.4: - resolution: {integrity: sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==} + verror@1.10.0: dependencies: - '@types/unist': 2.0.6 - unist-util-stringify-position: 3.0.3 - dev: true + assert-plus: 1.0.0 + core-util-is: 1.0.2 + extsprintf: 1.3.0 - /vfile@5.3.7: - resolution: {integrity: sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g==} + victory-vendor@36.9.2: dependencies: - '@types/unist': 2.0.6 - is-buffer: 2.0.5 - unist-util-stringify-position: 3.0.3 - vfile-message: 3.1.4 - dev: true + '@types/d3-array': 3.2.1 + '@types/d3-ease': 3.0.2 + '@types/d3-interpolate': 3.0.4 + '@types/d3-scale': 4.0.8 + '@types/d3-shape': 3.1.6 + '@types/d3-time': 3.0.3 + '@types/d3-timer': 3.0.2 + d3-array: 3.2.4 + d3-ease: 3.0.1 + d3-interpolate: 3.0.1 + d3-scale: 4.0.2 + d3-shape: 3.2.0 + d3-time: 3.1.0 + d3-timer: 3.0.1 - /vinyl-file@3.0.0: - resolution: {integrity: sha512-BoJDj+ca3D9xOuPEM6RWVtWQtvEPQiQYn82LvdxhLWplfQsBzBqtgK0yhCP0s1BNTi6dH9BO+dzybvyQIacifg==} - engines: {node: '>=4'} + vinyl-file@3.0.0: dependencies: graceful-fs: 4.2.11 pify: 2.3.0 @@ -25301,9 +32883,7 @@ packages: strip-bom-stream: 2.0.0 vinyl: 2.2.1 - /vinyl@2.2.1: - resolution: {integrity: sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw==} - engines: {node: '>= 0.10'} + vinyl@2.2.1: dependencies: clone: 2.1.2 clone-buffer: 1.0.0 @@ -25312,329 +32892,210 @@ packages: remove-trailing-separator: 1.1.0 replace-ext: 1.0.1 - /vite-node@0.28.5(@types/node@18.15.11): - resolution: {integrity: sha512-LmXb9saMGlrMZbXTvOveJKwMTBTNUH66c8rJnQ0ZPNX+myPEol64+szRzXtV5ORb0Hb/91yq+/D3oERoyAt6LA==} - engines: {node: '>=v14.16.0'} - hasBin: true - dependencies: - cac: 6.7.14 - debug: 4.3.4 - mlly: 1.2.0 - pathe: 1.1.0 - picocolors: 1.0.0 - source-map: 0.6.1 - source-map-support: 0.5.21 - vite: 4.2.2(@types/node@18.15.11) - transitivePeerDependencies: - - '@types/node' - - less - - sass - - stylus - - sugarss - - supports-color - - terser - dev: true - - /vite@4.2.2(@types/node@18.15.11): - resolution: {integrity: sha512-PcNtT5HeDxb3QaSqFYkEum8f5sCVe0R3WK20qxgIvNBZPXU/Obxs/+ubBMeE7nLWeCo2LDzv+8hRYSlcaSehig==} - engines: {node: ^14.18.0 || >=16.0.0} - hasBin: true - peerDependencies: - '@types/node': '>= 14' - less: '*' - sass: '*' - stylus: '*' - sugarss: '*' - terser: ^5.4.0 - peerDependenciesMeta: - '@types/node': - optional: true - less: - optional: true - sass: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - dependencies: - '@types/node': 18.15.11 - esbuild: 0.17.6 - postcss: 8.4.22 - resolve: 1.22.2 - rollup: 3.20.6 - optionalDependencies: - fsevents: 2.3.2 - dev: true - - /vlq@0.2.3: - resolution: {integrity: sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow==} - dev: true - - /vm2@3.9.16: - resolution: {integrity: sha512-3T9LscojNTxdOyG+e8gFeyBXkMlOBYDoF6dqZbj+MPVHi9x10UfiTAJIobuchRCp3QvC+inybTbMJIUrLsig0w==} - engines: {node: '>=6.0'} - hasBin: true - dependencies: - acorn: 8.8.2 - acorn-walk: 8.2.0 - dev: false - - /vm2@3.9.17: - resolution: {integrity: sha512-AqwtCnZ/ERcX+AVj9vUsphY56YANXxRuqMb7GsDtAr0m0PcQX3u0Aj3KWiXM0YAHy7i6JEeHrwOnwXbGYgRpAw==} - engines: {node: '>=6.0'} - hasBin: true - dependencies: - acorn: 8.8.2 - acorn-walk: 8.2.0 - dev: true - - /w3c-hr-time@1.0.2: - resolution: {integrity: sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==} - deprecated: Use your platform's native performance.now() and performance.timeOrigin. + w3c-hr-time@1.0.2: dependencies: browser-process-hrtime: 1.0.0 - dev: false - /w3c-xmlserializer@2.0.0: - resolution: {integrity: sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==} - engines: {node: '>=10'} + w3c-xmlserializer@2.0.0: dependencies: xml-name-validator: 3.0.0 - dev: false - /walk-up-path@1.0.0: - resolution: {integrity: sha512-hwj/qMDUEjCU5h0xr90KGCf0tg0/LgJbmOWgrWKYlcJZM7XvquvUJZ0G/HMGr7F7OQMOUuPHWP9JpriinkAlkg==} + walk-up-path@1.0.0: {} - /walker@1.0.8: - resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} + walker@1.0.8: dependencies: makeerror: 1.0.12 - /watchpack@2.4.0: - resolution: {integrity: sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==} - engines: {node: '>=10.13.0'} + watchpack@2.4.2: dependencies: glob-to-regexp: 0.4.1 graceful-fs: 4.2.11 - /wbuf@1.7.3: - resolution: {integrity: sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==} + wbuf@1.7.3: dependencies: minimalistic-assert: 1.0.1 - dev: false - /wcwidth@1.0.1: - resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + wcwidth@1.0.1: dependencies: defaults: 1.0.4 - /web-resource-inliner@6.0.1: - resolution: {integrity: sha512-kfqDxt5dTB1JhqsCUQVFDj0rmY+4HLwGQIsLPbyrsN9y9WV/1oFDSx3BQ4GfCv9X+jVeQ7rouTqwK53rA/7t8A==} - engines: {node: '>=10.0.0'} + web-resource-inliner@7.0.0: dependencies: ansi-colors: 4.1.3 escape-goat: 3.0.0 htmlparser2: 5.0.1 mime: 2.6.0 - node-fetch: 2.6.9 valid-data-url: 3.0.1 - transitivePeerDependencies: - - encoding - dev: false - /web-streams-polyfill@3.2.1: - resolution: {integrity: sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==} - engines: {node: '>= 8'} + web-streams-polyfill@3.3.3: {} - /web-vitals@0.2.4: - resolution: {integrity: sha512-6BjspCO9VriYy12z356nL6JBS0GYeEcA457YyRzD+dD6XYCQ75NKhcOHUMHentOE7OcVCIXXDvOm0jKFfQG2Gg==} - dev: true + web-vitals@2.1.4: {} - /web-vitals@2.1.4: - resolution: {integrity: sha512-sVWcwhU5mX6crfI5Vd2dC4qchyTqxV8URinzt25XqVh+bHEPGH4C3NPrNionCP7Obx59wrYEbNlw4Z8sjALzZg==} - dev: false + web-vitals@4.2.4: {} - /webidl-conversions@3.0.1: - resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + webidl-conversions@3.0.1: {} - /webidl-conversions@4.0.2: - resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} - dev: false + webidl-conversions@4.0.2: {} - /webidl-conversions@5.0.0: - resolution: {integrity: sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==} - engines: {node: '>=8'} - dev: false + webidl-conversions@5.0.0: {} - /webidl-conversions@6.1.0: - resolution: {integrity: sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==} - engines: {node: '>=10.4'} - dev: false + webidl-conversions@6.1.0: {} - /webidl-conversions@7.0.0: - resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} - engines: {node: '>=12'} - dev: false + webidl-conversions@7.0.0: {} + + webpack-bundle-analyzer@4.10.1: + dependencies: + '@discoveryjs/json-ext': 0.5.7 + acorn: 8.14.0 + acorn-walk: 8.3.4 + commander: 7.2.0 + debounce: 1.2.1 + escape-string-regexp: 4.0.0 + gzip-size: 6.0.0 + html-escaper: 2.0.2 + is-plain-object: 5.0.0 + opener: 1.5.2 + picocolors: 1.1.1 + sirv: 2.0.4 + ws: 7.5.10 + transitivePeerDependencies: + - bufferutil + - utf-8-validate - /webpack-cli@5.0.1(@webpack-cli/generators@3.0.1)(webpack@5.78.0): - resolution: {integrity: sha512-S3KVAyfwUqr0Mo/ur3NzIp6jnerNpo7GUO6so51mxLi1spqsA17YcMXy0WOIJtBSnj748lthxC6XLbNKh/ZC+A==} - engines: {node: '>=14.15.0'} - hasBin: true - peerDependencies: - '@webpack-cli/generators': '*' - webpack: 5.x.x - webpack-bundle-analyzer: '*' - webpack-dev-server: '*' - peerDependenciesMeta: - '@webpack-cli/generators': - optional: true - webpack-bundle-analyzer: - optional: true - webpack-dev-server: - optional: true + webpack-cli@5.1.4(@webpack-cli/generators@3.0.7)(webpack@5.95.0): dependencies: '@discoveryjs/json-ext': 0.5.7 - '@webpack-cli/configtest': 2.0.1(webpack-cli@5.0.1)(webpack@5.78.0) - '@webpack-cli/generators': 3.0.1(mem-fs-editor@9.7.0)(mem-fs@2.3.0)(prettier@2.8.7)(webpack-cli@5.0.1)(webpack@5.78.0) - '@webpack-cli/info': 2.0.1(webpack-cli@5.0.1)(webpack@5.78.0) - '@webpack-cli/serve': 2.0.1(webpack-cli@5.0.1)(webpack@5.78.0) - colorette: 2.0.19 - commander: 9.5.0 - cross-spawn: 7.0.3 - envinfo: 7.8.1 + '@webpack-cli/configtest': 2.1.1(webpack-cli@5.1.4)(webpack@5.95.0) + '@webpack-cli/info': 2.0.2(webpack-cli@5.1.4)(webpack@5.95.0) + '@webpack-cli/serve': 2.0.5(webpack-cli@5.1.4)(webpack@5.95.0) + colorette: 2.0.20 + commander: 10.0.1 + cross-spawn: 7.0.6 + envinfo: 7.14.0 + fastest-levenshtein: 1.0.16 + import-local: 3.2.0 + interpret: 3.1.1 + rechoir: 0.8.0 + webpack: 5.95.0(@swc/core@1.11.10(@swc/helpers@0.5.15))(webpack-cli@5.1.4) + webpack-merge: 5.10.0 + optionalDependencies: + '@webpack-cli/generators': 3.0.7(encoding@0.1.13)(mem-fs@2.3.0)(prettier@2.8.8)(webpack-cli@5.1.4)(webpack@5.95.0) + + webpack-cli@6.0.1(webpack@5.99.5): + dependencies: + '@discoveryjs/json-ext': 0.6.3 + '@webpack-cli/configtest': 3.0.1(webpack-cli@6.0.1)(webpack@5.99.5) + '@webpack-cli/info': 3.0.1(webpack-cli@6.0.1)(webpack@5.99.5) + '@webpack-cli/serve': 3.0.1(webpack-cli@6.0.1)(webpack@5.99.5) + colorette: 2.0.20 + commander: 12.1.0 + cross-spawn: 7.0.6 + envinfo: 7.14.0 fastest-levenshtein: 1.0.16 - import-local: 3.1.0 + import-local: 3.2.0 interpret: 3.1.1 rechoir: 0.8.0 - webpack: 5.78.0(esbuild@0.16.3)(webpack-cli@5.0.1) - webpack-merge: 5.8.0 + webpack: 5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(webpack-cli@6.0.1) + webpack-merge: 6.0.1 - /webpack-dev-middleware@5.3.3(webpack@5.78.0): - resolution: {integrity: sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==} - engines: {node: '>= 12.13.0'} - peerDependencies: - webpack: ^4.0.0 || ^5.0.0 + webpack-dev-middleware@5.3.4(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))): dependencies: - colorette: 2.0.19 - memfs: 3.5.0 + colorette: 2.0.20 + memfs: 3.5.3 mime-types: 2.1.35 range-parser: 1.2.1 - schema-utils: 4.0.0 - webpack: 5.78.0(esbuild@0.16.3)(webpack-cli@5.0.1) - dev: false - - /webpack-dev-server@4.13.2(webpack@5.78.0): - resolution: {integrity: sha512-5i6TrGBRxG4vnfDpB6qSQGfnB6skGBXNL5/542w2uRGLimX6qeE5BQMLrzIC3JYV/xlGOv+s+hTleI9AZKUQNw==} - engines: {node: '>= 12.13.0'} - hasBin: true - peerDependencies: - webpack: ^4.37.0 || ^5.0.0 - webpack-cli: '*' - peerDependenciesMeta: - webpack: - optional: true - webpack-cli: - optional: true - dependencies: - '@types/bonjour': 3.5.10 - '@types/connect-history-api-fallback': 1.3.5 - '@types/express': 4.17.17 - '@types/serve-index': 1.9.1 - '@types/serve-static': 1.15.1 - '@types/sockjs': 0.3.33 - '@types/ws': 8.5.4 + schema-utils: 4.2.0 + webpack: 5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(esbuild@0.25.6) + + webpack-dev-server@4.15.2(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))): + dependencies: + '@types/bonjour': 3.5.13 + '@types/connect-history-api-fallback': 1.5.4 + '@types/express': 4.17.21 + '@types/serve-index': 1.9.4 + '@types/serve-static': 1.15.7 + '@types/sockjs': 0.3.36 + '@types/ws': 8.5.12 ansi-html-community: 0.0.8 - bonjour-service: 1.1.1 - chokidar: 3.5.3 - colorette: 2.0.19 + bonjour-service: 1.2.1 + chokidar: 3.6.0 + colorette: 2.0.20 compression: 1.7.4 connect-history-api-fallback: 2.0.0 default-gateway: 6.0.3 - express: 4.18.2 + express: 4.21.2 graceful-fs: 4.2.11 - html-entities: 2.3.3 - http-proxy-middleware: 2.0.6(@types/express@4.17.17) - ipaddr.js: 2.0.1 - launch-editor: 2.6.0 + html-entities: 2.5.2 + http-proxy-middleware: 2.0.7(@types/express@4.17.21) + ipaddr.js: 2.2.0 + launch-editor: 2.9.1 open: 8.4.2 p-retry: 4.6.2 rimraf: 3.0.2 - schema-utils: 4.0.0 - selfsigned: 2.1.1 + schema-utils: 4.2.0 + selfsigned: 2.4.1 serve-index: 1.9.1 sockjs: 0.3.24 spdy: 4.0.2 - webpack: 5.78.0(esbuild@0.16.3)(webpack-cli@5.0.1) - webpack-dev-middleware: 5.3.3(webpack@5.78.0) - ws: 8.13.0 + webpack-dev-middleware: 5.3.4(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))) + ws: 8.18.0 + optionalDependencies: + webpack: 5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(esbuild@0.25.6) transitivePeerDependencies: - bufferutil - debug - supports-color - utf-8-validate - dev: false - /webpack-manifest-plugin@4.1.1(webpack@5.78.0): - resolution: {integrity: sha512-YXUAwxtfKIJIKkhg03MKuiFAD72PlrqCiwdwO4VEXdRO5V0ORCNwaOwAZawPZalCbmH9kBDmXnNeQOw+BIEiow==} - engines: {node: '>=12.22.0'} - peerDependencies: - webpack: ^4.44.2 || ^5.47.0 + webpack-hot-middleware@2.26.1: + dependencies: + ansi-html-community: 0.0.8 + html-entities: 2.5.2 + strip-ansi: 6.0.1 + optional: true + + webpack-manifest-plugin@4.1.1(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))): dependencies: tapable: 2.2.1 - webpack: 5.78.0(esbuild@0.16.3)(webpack-cli@5.0.1) + webpack: 5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(esbuild@0.25.6) webpack-sources: 2.3.1 - dev: false - /webpack-merge@5.8.0: - resolution: {integrity: sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==} - engines: {node: '>=10.0.0'} + webpack-merge@5.10.0: dependencies: clone-deep: 4.0.1 - wildcard: 2.0.0 + flat: 5.0.2 + wildcard: 2.0.1 - /webpack-sources@1.4.3: - resolution: {integrity: sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==} + webpack-merge@6.0.1: + dependencies: + clone-deep: 4.0.1 + flat: 5.0.2 + wildcard: 2.0.1 + + webpack-sources@1.4.3: dependencies: source-list-map: 2.0.1 source-map: 0.6.1 - dev: false - /webpack-sources@2.3.1: - resolution: {integrity: sha512-y9EI9AO42JjEcrTJFOYmVywVZdKVUfOvDUPsJea5GIr1JOEGFVqwlY2K098fFoIjOkDzHn2AjRvM8dsBZu+gCA==} - engines: {node: '>=10.13.0'} + webpack-sources@2.3.1: dependencies: source-list-map: 2.0.1 source-map: 0.6.1 - dev: false - /webpack-sources@3.2.3: - resolution: {integrity: sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==} - engines: {node: '>=10.13.0'} + webpack-sources@3.2.3: {} - /webpack@5.78.0(esbuild@0.16.3)(webpack-cli@5.0.1): - resolution: {integrity: sha512-gT5DP72KInmE/3azEaQrISjTvLYlSM0j1Ezhht/KLVkrqtv10JoP/RXhwmX/frrutOPuSq3o5Vq0ehR/4Vmd1g==} - engines: {node: '>=10.13.0'} - hasBin: true - peerDependencies: - webpack-cli: '*' - peerDependenciesMeta: - webpack-cli: - optional: true + webpack@5.95.0(@swc/core@1.11.10(@swc/helpers@0.5.15))(webpack-cli@5.1.4): dependencies: - '@types/eslint-scope': 3.7.4 - '@types/estree': 0.0.51 - '@webassemblyjs/ast': 1.11.1 - '@webassemblyjs/wasm-edit': 1.11.1 - '@webassemblyjs/wasm-parser': 1.11.1 - acorn: 8.8.2 - acorn-import-assertions: 1.8.0(acorn@8.8.2) - browserslist: 4.21.5 - chrome-trace-event: 1.0.3 - enhanced-resolve: 5.12.0 - es-module-lexer: 0.9.3 + '@types/estree': 1.0.6 + '@webassemblyjs/ast': 1.12.1 + '@webassemblyjs/wasm-edit': 1.12.1 + '@webassemblyjs/wasm-parser': 1.12.1 + acorn: 8.14.0 + acorn-import-attributes: 1.9.5(acorn@8.14.0) + browserslist: 4.24.2 + chrome-trace-event: 1.0.4 + enhanced-resolve: 5.17.1 + es-module-lexer: 1.5.4 eslint-scope: 5.1.1 events: 3.3.0 glob-to-regexp: 0.4.1 @@ -25643,38 +33104,30 @@ packages: loader-runner: 4.3.0 mime-types: 2.1.35 neo-async: 2.6.2 - schema-utils: 3.1.1 + schema-utils: 3.3.0 tapable: 2.2.1 - terser-webpack-plugin: 5.3.7(esbuild@0.16.3)(webpack@5.78.0) - watchpack: 2.4.0 - webpack-cli: 5.0.1(@webpack-cli/generators@3.0.1)(webpack@5.78.0) + terser-webpack-plugin: 5.3.10(@swc/core@1.11.10(@swc/helpers@0.5.15))(webpack@5.95.0) + watchpack: 2.4.2 webpack-sources: 3.2.3 + optionalDependencies: + webpack-cli: 5.1.4(@webpack-cli/generators@3.0.7)(webpack@5.95.0) transitivePeerDependencies: - '@swc/core' - esbuild - uglify-js - /webpack@5.79.0(esbuild@0.16.3): - resolution: {integrity: sha512-3mN4rR2Xq+INd6NnYuL9RC9GAmc1ROPKJoHhrZ4pAjdMFEkJJWrsPw8o2JjCIyQyTu7rTXYn4VG6OpyB3CobZg==} - engines: {node: '>=10.13.0'} - hasBin: true - peerDependencies: - webpack-cli: '*' - peerDependenciesMeta: - webpack-cli: - optional: true - dependencies: - '@types/eslint-scope': 3.7.4 - '@types/estree': 1.0.1 - '@webassemblyjs/ast': 1.11.1 - '@webassemblyjs/wasm-edit': 1.11.1 - '@webassemblyjs/wasm-parser': 1.11.1 - acorn: 8.8.2 - acorn-import-assertions: 1.8.0(acorn@8.8.2) - browserslist: 4.21.5 - chrome-trace-event: 1.0.3 - enhanced-resolve: 5.13.0 - es-module-lexer: 1.2.1 + webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(esbuild@0.23.1): + dependencies: + '@types/eslint-scope': 3.7.7 + '@types/estree': 1.0.6 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/wasm-edit': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + acorn: 8.14.0 + browserslist: 4.24.2 + chrome-trace-event: 1.0.4 + enhanced-resolve: 5.17.1 + es-module-lexer: 1.5.4 eslint-scope: 5.1.1 events: 3.3.0 glob-to-regexp: 0.4.1 @@ -25683,80 +33136,155 @@ packages: loader-runner: 4.3.0 mime-types: 2.1.35 neo-async: 2.6.2 - schema-utils: 3.1.2 + schema-utils: 4.3.0 tapable: 2.2.1 - terser-webpack-plugin: 5.3.7(esbuild@0.16.3)(webpack@5.79.0) - watchpack: 2.4.0 + terser-webpack-plugin: 5.3.14(@swc/core@1.11.10(@swc/helpers@0.5.15))(esbuild@0.23.1)(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(esbuild@0.23.1)) + watchpack: 2.4.2 webpack-sources: 3.2.3 transitivePeerDependencies: - '@swc/core' - esbuild - uglify-js - dev: true - /websocket-driver@0.7.4: - resolution: {integrity: sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==} - engines: {node: '>=0.8.0'} + webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(esbuild@0.25.6): + dependencies: + '@types/eslint-scope': 3.7.7 + '@types/estree': 1.0.6 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/wasm-edit': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + acorn: 8.14.0 + browserslist: 4.24.2 + chrome-trace-event: 1.0.4 + enhanced-resolve: 5.17.1 + es-module-lexer: 1.5.4 + eslint-scope: 5.1.1 + events: 3.3.0 + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + json-parse-even-better-errors: 2.3.1 + loader-runner: 4.3.0 + mime-types: 2.1.35 + neo-async: 2.6.2 + schema-utils: 4.3.0 + tapable: 2.2.1 + terser-webpack-plugin: 5.3.14(@swc/core@1.11.10(@swc/helpers@0.5.15))(esbuild@0.25.6)(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))) + watchpack: 2.4.2 + webpack-sources: 3.2.3 + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + + webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(webpack-cli@5.1.4): + dependencies: + '@types/eslint-scope': 3.7.7 + '@types/estree': 1.0.6 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/wasm-edit': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + acorn: 8.14.0 + browserslist: 4.24.2 + chrome-trace-event: 1.0.4 + enhanced-resolve: 5.17.1 + es-module-lexer: 1.5.4 + eslint-scope: 5.1.1 + events: 3.3.0 + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + json-parse-even-better-errors: 2.3.1 + loader-runner: 4.3.0 + mime-types: 2.1.35 + neo-async: 2.6.2 + schema-utils: 4.3.0 + tapable: 2.2.1 + terser-webpack-plugin: 5.3.14(@swc/core@1.11.10(@swc/helpers@0.5.15))(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(webpack-cli@5.1.4)) + watchpack: 2.4.2 + webpack-sources: 3.2.3 + optionalDependencies: + webpack-cli: 5.1.4(@webpack-cli/generators@3.0.7)(webpack@5.95.0) + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + + webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(webpack-cli@6.0.1): + dependencies: + '@types/eslint-scope': 3.7.7 + '@types/estree': 1.0.6 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/wasm-edit': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + acorn: 8.14.0 + browserslist: 4.24.2 + chrome-trace-event: 1.0.4 + enhanced-resolve: 5.17.1 + es-module-lexer: 1.5.4 + eslint-scope: 5.1.1 + events: 3.3.0 + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + json-parse-even-better-errors: 2.3.1 + loader-runner: 4.3.0 + mime-types: 2.1.35 + neo-async: 2.6.2 + schema-utils: 4.3.0 + tapable: 2.2.1 + terser-webpack-plugin: 5.3.14(@swc/core@1.11.10(@swc/helpers@0.5.15))(webpack@5.99.5) + watchpack: 2.4.2 + webpack-sources: 3.2.3 + optionalDependencies: + webpack-cli: 6.0.1(webpack@5.99.5) + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + + websocket-driver@0.7.4: dependencies: http-parser-js: 0.5.8 safe-buffer: 5.2.1 websocket-extensions: 0.1.4 - /websocket-extensions@0.1.4: - resolution: {integrity: sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==} - engines: {node: '>=0.8.0'} - - /wepack-cli@0.0.1-security: - resolution: {integrity: sha512-10l/6mMWz+LWQO+Pic8ZZRZtEHvs0JIiyZsnbWVVm8SHA3dOkxl2nu8EyCag4W/lSDXoWHcVgFCImkHkAw1aVQ==} - dev: true + websocket-extensions@0.1.4: {} - /whatwg-encoding@1.0.5: - resolution: {integrity: sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==} + whatwg-encoding@1.0.5: dependencies: iconv-lite: 0.4.24 - dev: false - /whatwg-fetch@3.6.2: - resolution: {integrity: sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==} - dev: false + whatwg-encoding@3.1.1: + dependencies: + iconv-lite: 0.6.3 + + whatwg-fetch@3.6.20: {} - /whatwg-mimetype@2.3.0: - resolution: {integrity: sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==} - dev: false + whatwg-mimetype@2.3.0: {} - /whatwg-url@11.0.0: - resolution: {integrity: sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==} - engines: {node: '>=12'} + whatwg-mimetype@4.0.0: {} + + whatwg-url@13.0.0: dependencies: - tr46: 3.0.0 + tr46: 4.1.1 webidl-conversions: 7.0.0 - dev: false - /whatwg-url@5.0.0: - resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + whatwg-url@5.0.0: dependencies: tr46: 0.0.3 webidl-conversions: 3.0.1 - /whatwg-url@7.1.0: - resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} + whatwg-url@7.1.0: dependencies: lodash.sortby: 4.7.0 tr46: 1.0.1 webidl-conversions: 4.0.2 - dev: false - /whatwg-url@8.7.0: - resolution: {integrity: sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==} - engines: {node: '>=10'} + whatwg-url@8.7.0: dependencies: lodash: 4.17.21 tr46: 2.1.0 webidl-conversions: 6.1.0 - dev: false - /which-boxed-primitive@1.0.2: - resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + which-boxed-primitive@1.0.2: dependencies: is-bigint: 1.0.4 is-boolean-object: 1.1.2 @@ -25764,411 +33292,291 @@ packages: is-string: 1.0.7 is-symbol: 1.0.4 - /which-collection@1.0.1: - resolution: {integrity: sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==} + which-builtin-type@1.1.4: dependencies: - is-map: 2.0.2 - is-set: 2.0.2 - is-weakmap: 2.0.1 - is-weakset: 2.0.2 + function.prototype.name: 1.1.6 + has-tostringtag: 1.0.2 + is-async-function: 2.0.0 + is-date-object: 1.0.5 + is-finalizationregistry: 1.0.2 + is-generator-function: 1.0.10 + is-regex: 1.1.4 + is-weakref: 1.0.2 + isarray: 2.0.5 + which-boxed-primitive: 1.0.2 + which-collection: 1.0.2 + which-typed-array: 1.1.15 - /which-pm-runs@1.1.0: - resolution: {integrity: sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA==} - engines: {node: '>=4'} - dev: false + which-collection@1.0.2: + dependencies: + is-map: 2.0.3 + is-set: 2.0.3 + is-weakmap: 2.0.2 + is-weakset: 2.0.3 - /which-pm@2.0.0: - resolution: {integrity: sha512-Lhs9Pmyph0p5n5Z3mVnN0yWcbQYUAD7rbQUiMsQxOJ3T57k7RFe35SUwWMf7dsbDZks1uOmw4AecB/JMDj3v/w==} - engines: {node: '>=8.15'} + which-pm@2.2.0: dependencies: load-yaml-file: 0.2.0 path-exists: 4.0.0 - /which-typed-array@1.1.9: - resolution: {integrity: sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==} - engines: {node: '>= 0.4'} + which-typed-array@1.1.15: dependencies: - available-typed-arrays: 1.0.5 - call-bind: 1.0.2 + available-typed-arrays: 1.0.7 + call-bind: 1.0.7 for-each: 0.3.3 gopd: 1.0.1 - has-tostringtag: 1.0.0 - is-typed-array: 1.1.10 + has-tostringtag: 1.0.2 - /which@1.3.1: - resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} - hasBin: true + which@1.3.1: dependencies: isexe: 2.0.0 - dev: false - /which@2.0.2: - resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} - engines: {node: '>= 8'} - hasBin: true + which@2.0.2: dependencies: isexe: 2.0.0 - /wide-align@1.1.5: - resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} + which@3.0.1: + dependencies: + isexe: 2.0.0 + + wide-align@1.1.5: dependencies: string-width: 4.2.3 - /wildcard@2.0.0: - resolution: {integrity: sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==} + widest-line@4.0.1: + dependencies: + string-width: 5.1.2 - /word-wrap@1.2.3: - resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==} - engines: {node: '>=0.10.0'} + wildcard@2.0.1: {} - /workbox-background-sync@6.5.4: - resolution: {integrity: sha512-0r4INQZMyPky/lj4Ou98qxcThrETucOde+7mRGJl13MPJugQNKeZQOdIJe/1AchOP23cTqHcN/YVpD6r8E6I8g==} + word-wrap@1.2.5: {} + + workbox-background-sync@6.6.0: dependencies: idb: 7.1.1 - workbox-core: 6.5.4 - dev: false + workbox-core: 6.6.0 - /workbox-broadcast-update@6.5.4: - resolution: {integrity: sha512-I/lBERoH1u3zyBosnpPEtcAVe5lwykx9Yg1k6f8/BGEPGaMMgZrwVrqL1uA9QZ1NGGFoyE6t9i7lBjOlDhFEEw==} + workbox-broadcast-update@6.6.0: dependencies: - workbox-core: 6.5.4 - dev: false + workbox-core: 6.6.0 - /workbox-build@6.5.4: - resolution: {integrity: sha512-kgRevLXEYvUW9WS4XoziYqZ8Q9j/2ziJYEtTrjdz5/L/cTUa2XfyMP2i7c3p34lgqJ03+mTiz13SdFef2POwbA==} - engines: {node: '>=10.0.0'} + workbox-build@6.6.0(@types/babel__core@7.20.5): dependencies: - '@apideck/better-ajv-errors': 0.3.6(ajv@8.12.0) - '@babel/core': 7.21.4 - '@babel/preset-env': 7.21.4(@babel/core@7.21.4) - '@babel/runtime': 7.21.0 - '@rollup/plugin-babel': 5.3.1(@babel/core@7.21.4)(rollup@2.79.1) - '@rollup/plugin-node-resolve': 11.2.1(rollup@2.79.1) - '@rollup/plugin-replace': 2.4.2(rollup@2.79.1) + '@apideck/better-ajv-errors': 0.3.6(ajv@8.17.1) + '@babel/core': 7.26.0 + '@babel/preset-env': 7.26.0(@babel/core@7.26.0) + '@babel/runtime': 7.26.0 + '@rollup/plugin-babel': 5.3.1(@babel/core@7.26.0)(@types/babel__core@7.20.5)(rollup@2.79.2) + '@rollup/plugin-node-resolve': 11.2.1(rollup@2.79.2) + '@rollup/plugin-replace': 2.4.2(rollup@2.79.2) '@surma/rollup-plugin-off-main-thread': 2.2.3 - ajv: 8.12.0 + ajv: 8.17.1 common-tags: 1.8.2 fast-json-stable-stringify: 2.1.0 fs-extra: 9.1.0 glob: 7.2.3 lodash: 4.17.21 pretty-bytes: 5.6.0 - rollup: 2.79.1 - rollup-plugin-terser: 7.0.2(rollup@2.79.1) + rollup: 2.79.2 + rollup-plugin-terser: 7.0.2(rollup@2.79.2) source-map: 0.8.0-beta.0 stringify-object: 3.3.0 strip-comments: 2.0.1 tempy: 0.6.0 upath: 1.2.0 - workbox-background-sync: 6.5.4 - workbox-broadcast-update: 6.5.4 - workbox-cacheable-response: 6.5.4 - workbox-core: 6.5.4 - workbox-expiration: 6.5.4 - workbox-google-analytics: 6.5.4 - workbox-navigation-preload: 6.5.4 - workbox-precaching: 6.5.4 - workbox-range-requests: 6.5.4 - workbox-recipes: 6.5.4 - workbox-routing: 6.5.4 - workbox-strategies: 6.5.4 - workbox-streams: 6.5.4 - workbox-sw: 6.5.4 - workbox-window: 6.5.4 + workbox-background-sync: 6.6.0 + workbox-broadcast-update: 6.6.0 + workbox-cacheable-response: 6.6.0 + workbox-core: 6.6.0 + workbox-expiration: 6.6.0 + workbox-google-analytics: 6.6.0 + workbox-navigation-preload: 6.6.0 + workbox-precaching: 6.6.0 + workbox-range-requests: 6.6.0 + workbox-recipes: 6.6.0 + workbox-routing: 6.6.0 + workbox-strategies: 6.6.0 + workbox-streams: 6.6.0 + workbox-sw: 6.6.0 + workbox-window: 6.6.0 transitivePeerDependencies: - '@types/babel__core' - supports-color - dev: false - /workbox-cacheable-response@6.5.4: - resolution: {integrity: sha512-DCR9uD0Fqj8oB2TSWQEm1hbFs/85hXXoayVwFKLVuIuxwJaihBsLsp4y7J9bvZbqtPJ1KlCkmYVGQKrBU4KAug==} + workbox-cacheable-response@6.6.0: dependencies: - workbox-core: 6.5.4 - dev: false + workbox-core: 6.6.0 - /workbox-core@6.5.4: - resolution: {integrity: sha512-OXYb+m9wZm8GrORlV2vBbE5EC1FKu71GGp0H4rjmxmF4/HLbMCoTFws87M3dFwgpmg0v00K++PImpNQ6J5NQ6Q==} - dev: false + workbox-core@6.6.0: {} - /workbox-expiration@6.5.4: - resolution: {integrity: sha512-jUP5qPOpH1nXtjGGh1fRBa1wJL2QlIb5mGpct3NzepjGG2uFFBn4iiEBiI9GUmfAFR2ApuRhDydjcRmYXddiEQ==} + workbox-expiration@6.6.0: dependencies: idb: 7.1.1 - workbox-core: 6.5.4 - dev: false + workbox-core: 6.6.0 - /workbox-google-analytics@6.5.4: - resolution: {integrity: sha512-8AU1WuaXsD49249Wq0B2zn4a/vvFfHkpcFfqAFHNHwln3jK9QUYmzdkKXGIZl9wyKNP+RRX30vcgcyWMcZ9VAg==} + workbox-google-analytics@6.6.0: dependencies: - workbox-background-sync: 6.5.4 - workbox-core: 6.5.4 - workbox-routing: 6.5.4 - workbox-strategies: 6.5.4 - dev: false + workbox-background-sync: 6.6.0 + workbox-core: 6.6.0 + workbox-routing: 6.6.0 + workbox-strategies: 6.6.0 - /workbox-navigation-preload@6.5.4: - resolution: {integrity: sha512-IIwf80eO3cr8h6XSQJF+Hxj26rg2RPFVUmJLUlM0+A2GzB4HFbQyKkrgD5y2d84g2IbJzP4B4j5dPBRzamHrng==} + workbox-navigation-preload@6.6.0: dependencies: - workbox-core: 6.5.4 - dev: false + workbox-core: 6.6.0 - /workbox-precaching@6.5.4: - resolution: {integrity: sha512-hSMezMsW6btKnxHB4bFy2Qfwey/8SYdGWvVIKFaUm8vJ4E53JAY+U2JwLTRD8wbLWoP6OVUdFlXsTdKu9yoLTg==} + workbox-precaching@6.6.0: dependencies: - workbox-core: 6.5.4 - workbox-routing: 6.5.4 - workbox-strategies: 6.5.4 - dev: false + workbox-core: 6.6.0 + workbox-routing: 6.6.0 + workbox-strategies: 6.6.0 - /workbox-range-requests@6.5.4: - resolution: {integrity: sha512-Je2qR1NXCFC8xVJ/Lux6saH6IrQGhMpDrPXWZWWS8n/RD+WZfKa6dSZwU+/QksfEadJEr/NfY+aP/CXFFK5JFg==} + workbox-range-requests@6.6.0: dependencies: - workbox-core: 6.5.4 - dev: false + workbox-core: 6.6.0 - /workbox-recipes@6.5.4: - resolution: {integrity: sha512-QZNO8Ez708NNwzLNEXTG4QYSKQ1ochzEtRLGaq+mr2PyoEIC1xFW7MrWxrONUxBFOByksds9Z4//lKAX8tHyUA==} + workbox-recipes@6.6.0: dependencies: - workbox-cacheable-response: 6.5.4 - workbox-core: 6.5.4 - workbox-expiration: 6.5.4 - workbox-precaching: 6.5.4 - workbox-routing: 6.5.4 - workbox-strategies: 6.5.4 - dev: false + workbox-cacheable-response: 6.6.0 + workbox-core: 6.6.0 + workbox-expiration: 6.6.0 + workbox-precaching: 6.6.0 + workbox-routing: 6.6.0 + workbox-strategies: 6.6.0 - /workbox-routing@6.5.4: - resolution: {integrity: sha512-apQswLsbrrOsBUWtr9Lf80F+P1sHnQdYodRo32SjiByYi36IDyL2r7BH1lJtFX8fwNHDa1QOVY74WKLLS6o5Pg==} + workbox-routing@6.6.0: dependencies: - workbox-core: 6.5.4 - dev: false + workbox-core: 6.6.0 - /workbox-strategies@6.5.4: - resolution: {integrity: sha512-DEtsxhx0LIYWkJBTQolRxG4EI0setTJkqR4m7r4YpBdxtWJH1Mbg01Cj8ZjNOO8etqfA3IZaOPHUxCs8cBsKLw==} + workbox-strategies@6.6.0: dependencies: - workbox-core: 6.5.4 - dev: false + workbox-core: 6.6.0 - /workbox-streams@6.5.4: - resolution: {integrity: sha512-FXKVh87d2RFXkliAIheBojBELIPnWbQdyDvsH3t74Cwhg0fDheL1T8BqSM86hZvC0ZESLsznSYWw+Va+KVbUzg==} + workbox-streams@6.6.0: dependencies: - workbox-core: 6.5.4 - workbox-routing: 6.5.4 - dev: false + workbox-core: 6.6.0 + workbox-routing: 6.6.0 - /workbox-sw@6.5.4: - resolution: {integrity: sha512-vo2RQo7DILVRoH5LjGqw3nphavEjK4Qk+FenXeUsknKn14eCNedHOXWbmnvP4ipKhlE35pvJ4yl4YYf6YsJArA==} - dev: false + workbox-sw@6.6.0: {} - /workbox-webpack-plugin@6.5.4(webpack@5.78.0): - resolution: {integrity: sha512-LmWm/zoaahe0EGmMTrSLUi+BjyR3cdGEfU3fS6PN1zKFYbqAKuQ+Oy/27e4VSXsyIwAw8+QDfk1XHNGtZu9nQg==} - engines: {node: '>=10.0.0'} - peerDependencies: - webpack: ^4.4.0 || ^5.9.0 + workbox-webpack-plugin@6.6.0(@types/babel__core@7.20.5)(webpack@5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))): dependencies: fast-json-stable-stringify: 2.1.0 pretty-bytes: 5.6.0 upath: 1.2.0 - webpack: 5.78.0(esbuild@0.16.3)(webpack-cli@5.0.1) + webpack: 5.99.5(@swc/core@1.11.10(@swc/helpers@0.5.15))(esbuild@0.25.6) webpack-sources: 1.4.3 - workbox-build: 6.5.4 + workbox-build: 6.6.0(@types/babel__core@7.20.5) transitivePeerDependencies: - '@types/babel__core' - supports-color - dev: false - /workbox-window@6.5.4: - resolution: {integrity: sha512-HnLZJDwYBE+hpG25AQBO8RUWBJRaCsI9ksQJEp3aCOFCaG5kqaToAYXFRAHxzRluM2cQbGzdQF5rjKPWPA1fug==} + workbox-window@6.6.0: dependencies: - '@types/trusted-types': 2.0.3 - workbox-core: 6.5.4 - dev: false + '@types/trusted-types': 2.0.7 + workbox-core: 6.6.0 - /wrap-ansi@7.0.0: - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} - engines: {node: '>=10'} + wrap-ansi@6.2.0: dependencies: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 - /wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 - /write-file-atomic@3.0.3: - resolution: {integrity: sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==} + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.1 + string-width: 5.1.2 + strip-ansi: 7.1.0 + + wrappy@1.0.2: {} + + write-file-atomic@3.0.3: dependencies: imurmurhash: 0.1.4 is-typedarray: 1.0.0 signal-exit: 3.0.7 typedarray-to-buffer: 3.1.5 - dev: false - /write-file-atomic@4.0.2: - resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + write-file-atomic@4.0.2: dependencies: imurmurhash: 0.1.4 signal-exit: 3.0.7 - /ws@7.5.9: - resolution: {integrity: sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==} - engines: {node: '>=8.3.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: ^5.0.2 - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - - /ws@8.13.0: - resolution: {integrity: sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - dev: false + ws@7.5.10: {} - /xdm@2.1.0: - resolution: {integrity: sha512-3LxxbxKcRogYY7cQSMy1tUuU1zKNK9YPqMT7/S0r7Cz2QpyF8O9yFySGD7caOZt+LWUOQioOIX+6ZzCoBCpcAA==} - dependencies: - '@rollup/pluginutils': 4.2.1 - '@types/estree-jsx': 0.0.1 - astring: 1.8.4 - estree-util-build-jsx: 2.2.2 - estree-util-is-identifier-name: 2.1.0 - estree-walker: 3.0.3 - got: 11.8.6 - hast-util-to-estree: 2.3.2 - loader-utils: 2.0.4 - markdown-extensions: 1.1.1 - mdast-util-mdx: 1.1.0 - micromark-extension-mdxjs: 1.0.0 - periscopic: 3.1.0 - remark-parse: 10.0.1 - remark-rehype: 9.1.0 - source-map: 0.7.4 - unified: 10.1.2 - unist-util-position-from-estree: 1.1.2 - unist-util-stringify-position: 3.0.3 - unist-util-visit: 4.1.2 - vfile: 5.3.7 - optionalDependencies: - deasync: 0.1.28 - transitivePeerDependencies: - - supports-color - dev: true + ws@8.17.1: {} - /xml-name-validator@3.0.0: - resolution: {integrity: sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==} - dev: false + ws@8.18.0: {} - /xmlchars@2.2.0: - resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} - dev: false + xml-name-validator@3.0.0: {} - /xmlcreate@2.0.4: - resolution: {integrity: sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==} - dev: false - optional: true + xmlchars@2.2.0: {} - /xregexp@2.0.0: - resolution: {integrity: sha512-xl/50/Cf32VsGq/1R8jJE5ajH1yMCQkpmoS10QbFZWl2Oor4H0Me64Pu2yxvsRWK3m6soJbmGfzSR7BYmDcWAA==} - dev: true + xmlhttprequest-ssl@2.1.2: {} - /xtend@4.0.2: - resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} - engines: {node: '>=0.4'} + xtend@4.0.2: {} - /y18n@5.0.8: - resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} - engines: {node: '>=10'} + y18n@5.0.8: {} - /yallist@2.1.2: - resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==} - dev: false + yallist@3.1.1: {} - /yallist@3.1.1: - resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yallist@4.0.0: {} - /yallist@4.0.0: - resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + yallist@5.0.0: {} - /yaml@1.10.2: - resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} - engines: {node: '>= 6'} + yaml@1.10.2: {} - /yaml@2.2.1: - resolution: {integrity: sha512-e0WHiYql7+9wr4cWMx3TVQrNwejKaEe7/rHNmQmqRjazfOP5W8PB6Jpebb5o6fIapbz9o9+2ipcaTM2ZwDI6lw==} - engines: {node: '>= 14'} - dev: true + yaml@2.6.0: {} - /yargs-parser@20.2.9: - resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} - engines: {node: '>=10'} + yargs-parser@20.2.9: {} - /yargs-parser@21.1.1: - resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} - engines: {node: '>=12'} - dev: true + yargs-parser@21.1.1: {} - /yargs@16.2.0: - resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} - engines: {node: '>=10'} + yargs@16.2.0: dependencies: cliui: 7.0.4 - escalade: 3.1.1 + escalade: 3.2.0 get-caller-file: 2.0.5 require-directory: 2.1.1 string-width: 4.2.3 y18n: 5.0.8 yargs-parser: 20.2.9 - /yargs@17.7.1: - resolution: {integrity: sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==} - engines: {node: '>=12'} + yargs@17.7.2: dependencies: cliui: 8.0.1 - escalade: 3.1.1 + escalade: 3.2.0 get-caller-file: 2.0.5 require-directory: 2.1.1 string-width: 4.2.3 y18n: 5.0.8 yargs-parser: 21.1.1 - dev: true - /yeoman-environment@3.15.1(mem-fs-editor@9.7.0)(mem-fs@2.3.0): - resolution: {integrity: sha512-P4DTQxqCxNTBD7gph+P+dIckBdx0xyHmvOYgO3vsc9/Sl67KJ6QInz5Qv6tlXET3CFFJ/YxPIdl9rKb0XwTRLg==} - engines: {node: '>=12.10.0'} - hasBin: true - peerDependencies: - mem-fs: ^1.2.0 || ^2.0.0 - mem-fs-editor: ^8.1.2 || ^9.0.0 + yeoman-environment@3.19.3: dependencies: '@npmcli/arborist': 4.3.1 are-we-there-yet: 2.0.0 arrify: 2.0.1 - binaryextensions: 4.18.0 + binaryextensions: 4.19.0 chalk: 4.1.2 cli-table: 0.3.11 commander: 7.1.0 dateformat: 4.6.3 - debug: 4.3.4 - diff: 5.1.0 + debug: 4.3.7(supports-color@5.5.0) + diff: 5.2.0 error: 10.4.0 escape-string-regexp: 4.0.0 execa: 5.1.1 find-up: 5.0.0 globby: 11.1.0 grouped-queue: 2.0.0 - inquirer: 8.2.5 + inquirer: 8.2.6 is-scoped: 2.1.0 isbinaryfile: 4.0.10 lodash: 4.17.21 @@ -26180,103 +33588,74 @@ packages: p-queue: 6.6.2 p-transform: 1.3.0 pacote: 12.0.3 - preferred-pm: 3.0.3 + preferred-pm: 3.1.4 pretty-bytes: 5.6.0 - semver: 7.5.0 + readable-stream: 4.5.2 + semver: 7.6.3 slash: 3.0.0 strip-ansi: 6.0.1 text-table: 0.2.0 - textextensions: 5.15.0 + textextensions: 5.16.0 untildify: 4.0.0 transitivePeerDependencies: - bluebird - supports-color - /yeoman-generator@5.8.0(mem-fs@2.3.0)(yeoman-environment@3.15.1): - resolution: {integrity: sha512-dsrwFn9/c2/MOe80sa2nKfbZd/GaPTgmmehdgkFifs1VN/I7qPsW2xcBfvSkHNGK+PZly7uHyH8kaVYSFNUDhQ==} - engines: {node: '>=12.10.0'} - peerDependencies: - yeoman-environment: ^3.2.0 - peerDependenciesMeta: - yeoman-environment: - optional: true + yeoman-generator@5.10.0(encoding@0.1.13)(mem-fs@2.3.0)(yeoman-environment@3.19.3): dependencies: chalk: 4.1.2 dargs: 7.0.0 - debug: 4.3.4 + debug: 4.3.7(supports-color@5.5.0) execa: 5.1.1 - github-username: 6.0.0 + github-username: 6.0.0(encoding@0.1.13) lodash: 4.17.21 mem-fs-editor: 9.7.0(mem-fs@2.3.0) minimist: 1.2.8 + pacote: 15.2.0 read-pkg-up: 7.0.1 run-async: 2.4.1 - semver: 7.5.0 + semver: 7.6.3 shelljs: 0.8.5 sort-keys: 4.2.0 text-table: 0.2.0 - yeoman-environment: 3.15.1(mem-fs-editor@9.7.0)(mem-fs@2.3.0) + optionalDependencies: + yeoman-environment: 3.19.3 transitivePeerDependencies: + - bluebird - encoding - mem-fs - supports-color - /yn@3.1.1: - resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} - engines: {node: '>=6'} + yn@3.1.1: {} - /yocto-queue@0.1.0: - resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} - engines: {node: '>=10'} + yocto-queue@0.1.0: {} - /zip-stream@4.1.0: - resolution: {integrity: sha512-zshzwQW7gG7hjpBlgeQP9RuyPGNxvJdzR8SUM3QhxCnLjWN2E7j3dOvpeDcQoETfHx0urRS7EtmVToql7YpU4A==} - engines: {node: '>= 10'} + yocto-queue@1.1.1: {} + + yoctocolors-cjs@2.1.2: {} + + yoctocolors@2.1.1: {} + + zip-stream@4.1.1: dependencies: - archiver-utils: 2.1.0 - compress-commons: 4.1.1 + archiver-utils: 3.0.4 + compress-commons: 4.1.2 readable-stream: 3.6.2 - dev: true - /zod-prisma@0.5.4(prisma@4.12.0)(zod@3.21.4): - resolution: {integrity: sha512-5Ca4Qd1a1jy1T/NqCEpbr0c+EsbjJfJ/7euEHob3zDvtUK2rTuD1Rc/vfzH8q8PtaR2TZbysD88NHmrLwpv3Xg==} - engines: {node: '>=14'} - hasBin: true - peerDependencies: - decimal.js: ^10.0.0 - prisma: ^3.0.0 - zod: ^3.0.0 - peerDependenciesMeta: - decimal.js: - optional: true + zod-prisma@0.5.4(decimal.js@10.4.3)(prisma@6.5.0(typescript@5.6.3))(zod@3.23.8): dependencies: '@prisma/generator-helper': 3.8.1 parenthesis: 3.1.8 - prisma: 4.12.0 + prisma: 6.5.0(typescript@5.6.3) ts-morph: 13.0.3 - zod: 3.21.4 - dev: true + zod: 3.23.8 + optionalDependencies: + decimal.js: 10.4.3 - /zod-to-json-schema@3.20.4(zod@3.21.4): - resolution: {integrity: sha512-Un9+kInJ2Zt63n6Z7mLqBifzzPcOyX+b+Exuzf7L1+xqck9Q2EPByyTRduV3kmSPaXaRer1JCsucubpgL1fipg==} - peerDependencies: - zod: ^3.20.0 + zod-to-json-schema@3.23.5(zod@3.23.8): dependencies: - zod: 3.21.4 - dev: false + zod: 3.23.8 - /zod@3.21.4: - resolution: {integrity: sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==} - - /zstd-napi@0.0.6: - resolution: {integrity: sha512-CKYrptAvjMhxQTuC7J42UXdmgKc73piuZHNGg0dya6qYZzPmaH31fXxuFv7B9QGAWX8tBmFlDJo9aXne7bmx3g==} - requiresBuild: true - dependencies: - '@types/node': 18.15.11 - node-addon-api: 2.0.2 - prebuild-install: 5.3.6 - dev: false + zod@3.23.8: {} - /zwitch@2.0.4: - resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} - dev: true + zod@3.24.3: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 049420e23..bb1d7f0d2 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,8 +1,24 @@ packages: - - "e2e" - - "examples/*" - - "types/*" - - "webapps/*" - - "services/*" - - "cli/*" - - "libs/*" + - e2e + - examples/* + - types/* + - webapps/* + - services/* + - cli/* + - libs/* +onlyBuiltDependencies: + - "@confluentinc/kafka-javascript" + - "@firebase/util" + - "@prisma/client" + - "@prisma/engines" + - core-js + - core-js-pure + - cpu-features + - esbuild + - isolated-vm + - prisma + - protobufjs + - sharp + - ssh2 + - turbo + - zstd-napi diff --git a/release.sh b/release.sh new file mode 100755 index 000000000..39262f969 --- /dev/null +++ b/release.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +pnpm run build-scripts docker ../../ -t console,rotor,profiles --platform linux/amd64,linux/arm64 --push $@ + diff --git a/rotor.Dockerfile b/rotor.Dockerfile deleted file mode 100644 index 8c5dc51e5..000000000 --- a/rotor.Dockerfile +++ /dev/null @@ -1,33 +0,0 @@ -# Run `docker login`, use jitsucom account -# Build & push it with -# docker buildx build --platform linux/amd64 . -f rotor.Dockerfile --push -t jitsucom/rotor:latest - - -FROM node:16-bullseye-slim as builder - -RUN apt-get update -y -RUN apt-get install nano curl git openssl1.1 procps python3 make g++ bash -y - -# Create app directory -WORKDIR /app -RUN npm -g install pnpm -COPY pnpm-lock.yaml . -RUN --mount=type=cache,id=onetag_pnpm,target=/root/.local/share/pnpm/store/v3 pnpm fetch -COPY . . -RUN --mount=type=cache,id=onetag_pnpm,target=/root/.local/share/pnpm/store/v3 pnpm install -r --unsafe-perm -RUN --mount=type=cache,id=rotor_turborepo,target=/app/node_modules/.cache/turbo pnpm build --filter=@jitsu-internal/rotor - - -FROM node:16-bullseye-slim AS runner - -WORKDIR /app -RUN addgroup --system --gid 1001 runner -RUN adduser --system --uid 1001 runner -USER runner - -EXPOSE 3401 - - -COPY --from=builder /app/services/rotor/dist . - -ENTRYPOINT ["sh", "-c", "node main.js"] diff --git a/rotorbuild.sh b/rotorbuild.sh deleted file mode 100755 index afbfe71eb..000000000 --- a/rotorbuild.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash - -docker buildx build --platform linux/amd64 . -f rotor.Dockerfile --load -t jitsucom/rotor:latest \ No newline at end of file diff --git a/services/profiles/.gitignore b/services/profiles/.gitignore new file mode 100644 index 000000000..cdd602b1c --- /dev/null +++ b/services/profiles/.gitignore @@ -0,0 +1,3 @@ +/compiled/ +/dist/ +/__tests__/artifacts/ diff --git a/services/profiles/babel.config.js b/services/profiles/babel.config.js new file mode 100644 index 000000000..e2e42dc19 --- /dev/null +++ b/services/profiles/babel.config.js @@ -0,0 +1,8 @@ +module.exports = { + presets: [ + "@babel/preset-env", + "@babel/preset-typescript", + ["@babel/preset-react", { importSource: "react", runtime: "automatic" }], + ], + plugins: [], +}; diff --git a/services/profiles/dist_package.json b/services/profiles/dist_package.json new file mode 100644 index 000000000..193ab1f11 --- /dev/null +++ b/services/profiles/dist_package.json @@ -0,0 +1,9 @@ +{ + "name": "@jitsu-internal/profile-builder", + "version": "0.0.0", + "dependencies": { + "isolated-vm": "6.0.0", + "@confluentinc/kafka-javascript": "^1.4.1", + "@mongodb-js/zstd": "^2.0.1" + } +} diff --git a/services/profiles/jest.config.js b/services/profiles/jest.config.js new file mode 100644 index 000000000..de6e6d30e --- /dev/null +++ b/services/profiles/jest.config.js @@ -0,0 +1,9 @@ +/** @type {import("ts-jest").JestConfigWithTsJest} */ +module.exports = { + //preset: "ts-jest", + preset: "ts-jest", + testMatch: ["**/__tests__/**/*.test.ts"], + testEnvironment: "node", + runner: "jest-runner", + "setupFiles": ["./jest.setup.js"] +}; diff --git a/services/profiles/jest.setup.js b/services/profiles/jest.setup.js new file mode 100644 index 000000000..fa6ee7ee1 --- /dev/null +++ b/services/profiles/jest.setup.js @@ -0,0 +1,8 @@ +process.env.JUAVA_LOG_LEVEL = 'debug'; +global.console = { + log: message => process.stdout.write(message + '\n'), + error: console.error, + warn: console.warn, + info: console.info, + debug: console.debug, +}; diff --git a/services/profiles/package.json b/services/profiles/package.json new file mode 100644 index 000000000..4d800fdcf --- /dev/null +++ b/services/profiles/package.json @@ -0,0 +1,68 @@ +{ + "name": "@jitsu-internal/profile-builder", + "version": "0.0.0", + "description": "", + "author": "Jitsu Dev Team ", + "publishConfig": { + "access": "public" + }, + "license": "MIT", + "private": false, + "scripts": { + "compile": "rm -rf ./dist && tsc -p . ", + "bundle_extenals": "cp dist_package.json ./dist/package.json && cd ./dist/ && npm install", + "build": "pnpm compile && webpack && pnpm bundle_extenals", + "start": "dotenv -e ../../.env.local -- node dist/main.js", + "profiles:dev": "dotenv -e ../../.env.local -- nodemon --watch \"src/**\" --ext \"ts,json,tsx\" --exec \"ts-node src/index.ts\"", + "profiles:profile": "dotenv -e ../../.env.local -- nodemon --watch \"src/**\" --ext \"ts,json,tsx\" --exec \"ts-node-dev --inspect -- src/index.ts\"" + }, + "dependencies": { + "mongodb": "^6.16.0", + "isolated-vm": "6.0.0", + "@jitsu/core-functions": "workspace:*", + "@jitsu/functions-lib": "workspace:*", + "juava": "workspace:*", + "node-cache": "^5.1.2", + "p-queue": "^6.6.2", + "express": "^4.21.2", + "pg": "~8.11.6", + "pg-cursor": "^2.11.0", + "prom-client": "^15.1.3", + "tslib": "^2.6.3", + "@confluentinc/kafka-javascript": "^1.4.1", + "@clickhouse/client": "^1.10.1" + }, + "devDependencies": { + "@babel/preset-env": "^7.25.2", + "@babel/preset-react": "^7.24.7", + "@babel/preset-typescript": "^7.24.7", + "@jitsu/protocols": "workspace:*", + "@types/jest": "^29.5.11", + "@types/lodash": "^4.17.7", + "@types/node": "^18.15.3", + "@types/pg": "~8.11.15", + "@types/pg-cursor": "^2.7.2", + "@types/web": "^0.0.152", + "@types/webpack": "^5.28.5", + "@types/express": "^4.17.21", + "@webpack-cli/generators": "^3.0.7", + "babel-loader": "^9.1.3", + "clean-webpack-plugin": "^4.0.0", + "copy-webpack-plugin": "^12.0.2", + "declaration-bundler-webpack-plugin": "^1.0.3", + "dotenv-cli": "^7.4.2", + "jest": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/types": "^29.6.3", + "lodash": "^4.17.21", + "node-loader": "^2.0.0", + "nodemon": "^3.1.4", + "ts-jest": "^29.2.3", + "ts-loader": "^9.5.1", + "ts-node": "^10.9.2", + "ts-node-dev": "^2.0.0", + "typescript": "^5.6.3", + "webpack": "^5.95.0", + "webpack-cli": "^5.1.4" + } +} diff --git a/services/profiles/src/builder.ts b/services/profiles/src/builder.ts new file mode 100644 index 000000000..108770daf --- /dev/null +++ b/services/profiles/src/builder.ts @@ -0,0 +1,571 @@ +import { + createClient, + ProfileBuilder, + mongodb, + ProfilesConfig, + pbEnsureMongoCollection, + profileIdHashColumn, + EventsStore, + bulkerDestination, + FunctionContext, + FunctionChainContext, + profileIdColumn, + ProfileUser, + Profile, +} from "@jitsu/core-functions"; +import { FindCursor, AggregationCursor, MongoClient, WithId, Document, ReadPreference } from "mongodb"; +import { db, ProfileBuilderQueueInfo } from "./lib/db"; +import { getLog, getSingleton, hash, LogFactory, parseNumber, requireDefined, stopwatch, int32Hash } from "juava"; +import NodeCache from "node-cache"; +import { buildFunctionChain, FuncChain, runChain } from "./lib/functions-chain"; +import { FullContext } from "@jitsu/protocols/functions"; +import { AnalyticsServerEvent } from "@jitsu/protocols/analytics"; +import { TableNameParameter, transfer } from "@jitsu/functions-lib"; +import { connectionsStore } from "./lib/repositories"; +import { HighLevelProducer, OffsetSpec, TopicPartitionOffsetSpec } from "@confluentinc/kafka-javascript"; +import { createPriorityConsumer, TopicsReport } from "./lib/priority-consumer"; +import { kafkaAdmin, kafkaCredentials, topicName } from "./lib/kafka"; +import { promProfileStatuses, promQueueProcessed, promQueueSize } from "./lib/metrics"; + +const bulkerBase = requireDefined(process.env.BULKER_URL, "env BULKER_URL is not defined"); +const bulkerAuthKey = requireDefined(process.env.BULKER_AUTH_KEY, "env BULKER_AUTH_KEY is not defined"); + +const fetchTimeoutMs = parseNumber(process.env.FETCH_TIMEOUT_MS, 2000); +export const metricsInterval = parseNumber(process.env.METRICS_INTERVAL_MS, 5000); + +const instanceIndex = parseNumber(process.env.INSTANCE_INDEX, 0); +const priorityLevels = parseNumber(process.env.PRIORITY_LEVELS, 3); + +//cache function chains for 1m +const funcsChainTTL = 60; +const funcsChainCache = new NodeCache({ stdTTL: funcsChainTTL, checkperiod: 60, useClones: false }); + +const funcCtx: FunctionContext = { + function: { + id: "profile-builder", + type: "profile-builder", + }, + props: {}, +}; + +const bulkerSchema = { + name: "profiles", + fields: [ + { + name: "profile_id", + type: 4, //string. See bulker's DataType + }, + { + name: "traits", + type: 6, //json + }, + { + name: "version", + type: 2, //int. See bulker's DataType + }, + { + name: "updated_at", + type: 5, // timestamp + }, + ], +}; + +export type ProfileBuilderRunner = { + close: () => Promise; + version: () => number; +}; + +export async function profileBuilder( + workspaceId: string, + profileBuilder: ProfileBuilder, + eventsLogger: EventsStore +): Promise { + const pbLongId = `${workspaceId}-${profileBuilder.id}-v${profileBuilder.version}`; + const log = getLog(`pb-${pbLongId}`); + + let closed = false; + let closePromise: Promise | undefined = undefined; + + const cacheKey = pbLongId; + let funcChain: FuncChain | undefined = funcsChainCache.get(cacheKey); + if (!funcChain) { + log.atInfo().log(`Refreshing function chain`); + funcChain = buildFunctionChain(profileBuilder, connectionsStore.getCurrent()!, eventsLogger, fetchTimeoutMs); + funcsChainCache.set(cacheKey, funcChain); + } + + const config = ProfilesConfig.parse({ + ...profileBuilder.intermediateStorageCredentials, + profileBuilderId: profileBuilder.id, + profileWindowDays: profileBuilder.connectionOptions.profileWindow, + eventsDatabase: `profiles`, + eventsCollectionName: `profiles-raw-${workspaceId}-${profileBuilder.id}`, + traitsCollectionName: `profiles-traits-${workspaceId}-${profileBuilder.id}`, + }); + + const mongoSingleton = config.mongoUrl + ? getSingleton( + `profiles-mongodb-${profileBuilder.id}-${hash("md5", config.mongoUrl)}`, + () => { + log.atInfo().log(`Connecting to MongoDB server.`); + const cl = createClient({ + mongoUrl: config.mongoUrl!, + }); + log.atInfo().log(`Connected successfully to MongoDB server.`); + return cl; + }, + { + optional: true, + cleanupFunc: client => client.close(), + } + ) + : mongodb; + + const mongo = await mongoSingleton.waitInit(); + + await pbEnsureMongoCollection(mongo, config.eventsDatabase, config.eventsCollectionName, config.profileWindowDays, [ + profileIdHashColumn, + profileIdColumn, + "type", + ]); + await pbEnsureMongoCollection( + mongo, + config.eventsDatabase, + config.traitsCollectionName, + config.profileWindowDays, + [profileIdColumn], + "updatedAt", + true + ); + + const priorityConsumer = createPriorityConsumer( + profileBuilder, + priorityLevels, + (profileId: string, priority: number) => { + return () => processProfile(profileBuilder, funcChain!, mongo, log, config, profileId, priority); + } + ); + + let timer: NodeJS.Timeout | undefined; + if (instanceIndex === 0) { + let previousOffsets: TopicsReport | undefined = undefined; + timer = setInterval(async () => { + reportQueueSize(profileBuilder, priorityLevels, previousOffsets) + .then(r => { + previousOffsets = r; + }) + .catch(e => { + log.atError().log(`Error while reporting queue size: ${e.message}`); + }); + }, metricsInterval); + } + const startConsumer = async () => { + log.atInfo().log("Starting consumer"); + return priorityConsumer.start(); + }; + const startFullRebuilder = async () => { + log.atInfo().log("Starting full rebuilder"); + let closeResolve: ((value: void | PromiseLike) => void) | undefined; + let producer: HighLevelProducer | undefined; + closePromise = new Promise((resolve, reject) => { + closeResolve = resolve; + }); + try { + producer = new HighLevelProducer({ + "bootstrap.servers": kafkaCredentials.brokers.join(","), + "allow.auto.create.topics": false, + "linger.ms": 200, + }); + producer.connect(); + const topic = topicName(profileBuilder.id, priorityLevels - 1); + while (!closed) { + const started = Date.now(); + const loadedState = await db.pgHelper().getProfileBuilderState(profileBuilder.id); + + if (typeof loadedState?.fullRebuildInfo?.profilesCount !== "undefined") { + // sleep 5 sec + await new Promise(resolve => setTimeout(resolve, 5 * 1000)); + continue; + } + log.atInfo().log(`Starting full rebuild for ${profileBuilder.id}`); + try { + let processed = 0; + const producerCallback = (err, offset) => { + if (err) { + log.atError().log(`Error while producing message to Kafka: ${err.message}`); + } + }; + await processProfileIds(mongo, config, profileId => { + producer!.produce(topic, null, null, profileId, Date.now(), producerCallback); + processed++; + }); + + log.atInfo().log(`Processed ${processed} users in ${Date.now() - started}ms`); + await db.pgHelper().updateProfileBuilderFullRebuildInfo(profileBuilder.id, { + version: profileBuilder.version, + timestamp: new Date(), + profilesCount: processed, + }); + } catch (e: any) { + funcChain?.context.log.error(funcCtx, `Error while running profile builder: ${e.message}`); + } + } + } finally { + if (producer) { + producer.disconnect(); + } + if (closeResolve) { + closeResolve(); + } + } + }; + + const reportQueueSize = async function ( + profileBuilder: ProfileBuilder, + priorityLevels: number, + previousOffsets?: TopicsReport + ): Promise { + log.atDebug().log(`Reporting queue size for ${profileBuilder.id}`); + const topics: TopicsReport = {}; + for (let i = 0; i < priorityLevels; i++) { + const topic = topicName(profileBuilder.id, i); + topics[topic] = {}; + } + const { promise, resolve, reject } = createDeferred(); + + kafkaAdmin.listConsumerGroupOffsets([{ groupId: "profile-builder-" + profileBuilder.id }], undefined, (e, data) => { + if (e) { + log + .atError() + .withCause(e) + .log(`Failed to describe topics ${JSON.stringify(topics)}`); + reject(e); + return; + } + for (const group of data) { + const partitions = group.partitions; + for (const partition of partitions) { + if (partition.error) { + log + .atError() + .log(`Failed to get partition ${partition.topic}:${partition.partition} offset: ${partition.error}`); + reject(partition.error); + return; + } + const topic = topics[partition.topic]; + if (!topic) { + continue; + } + const previousOffset = previousOffsets?.[partition.topic]?.[partition.partition]?.offset; + const partitionInfo = topic[partition.partition]; + if (!partitionInfo) { + topic[partition.partition] = { offset: partition.offset, highOffset: 0, previousOffset }; + } else { + partitionInfo.offset = partition.offset; + partitionInfo.previousOffset = previousOffset; + } + } + } + resolve(); + }); + await promise; + + const { promise: promise2, resolve: resolve2, reject: reject2 } = createDeferred(); + + kafkaAdmin.describeTopics(Object.keys(topics), undefined, (e, data) => { + if (e) { + log + .atError() + .withCause(e) + .log(`Failed to describe topics ${JSON.stringify(topics)}`); + reject2(e); + return; + } + const specs: TopicPartitionOffsetSpec[] = []; + for (const topic of data) { + if (topic.error) { + log.atError().log(`Failed to describe topic ${topic.name} : ${topic.error}`); + reject2(topic.error); + return; + } + const partitions = topic.partitions; + for (const partition of partitions) { + specs.push({ + topic: topic.name, + partition: partition.partition, + offset: OffsetSpec.LATEST, + }); + } + } + kafkaAdmin.listOffsets(specs, undefined, (e, data) => { + if (e) { + log + .atError() + .withCause(e) + .log(`Failed to list offsets ${JSON.stringify(topics)}`); + reject2(e); + return; + } + for (const partition of data) { + const topic = topics[partition.topic]; + const partitionInfo = topic[partition.partition]; + if (!partitionInfo) { + topic[partition.partition] = { highOffset: partition.offset, offset: 0 }; + } else { + partitionInfo.highOffset = partition.offset; + } + } + resolve2(); + }); + }); + + await promise2; + + const queues: ProfileBuilderQueueInfo["queues"] = {}; + for (let i = 0; i < priorityLevels; i++) { + const name = topicName(profileBuilder.id, i); + const topic = topics[name]; + const size = Object.values(topic).reduce((acc, partition) => { + if (partition.highOffset) { + return acc + (partition.highOffset - partition.offset); + } + return acc; + }, 0); + const processed = Object.values(topic).reduce((acc, partition) => { + if (partition.previousOffset) { + return acc + (partition.offset - partition.previousOffset); + } + return acc; + }, 0); + promQueueSize.labels({ builderId: profileBuilder.id, priority: i }).set(size); + promQueueProcessed.labels({ builderId: profileBuilder.id, priority: i }).inc(processed); + queues[i] = { + priority: i, + size, + processed, + }; + } + log.atDebug().log(`Queue size: ${JSON.stringify(queues)}`); + await db.pgHelper().updateProfileBuilderQueuesInfo(profileBuilder.id, { + timestamp: new Date(), + intervalSec: metricsInterval / 1000, + queues, + }); + return topics; + }; + + const pb = { + close: async () => { + closed = true; + clearInterval(timer); + const promises: Promise[] = [priorityConsumer.close()]; + if (closePromise) { + promises.push(closePromise); + } + await Promise.all(promises); + log.atInfo().log("Closed"); + }, + version: () => profileBuilder.version, + }; + if (instanceIndex === 0) { + setImmediate(startFullRebuilder); + } + setImmediate(startConsumer); + + return pb; +} + +async function processProfile( + profileBuilder: ProfileBuilder, + funcChain: FuncChain, + mongo: MongoClient, + log: LogFactory, + config: ProfilesConfig, + profileId: string, + priority: number = 0 +) { + const ms = stopwatch(); + let status = "success"; + let cursor: FindCursor>; + try { + const metrics = { db_events: 0 } as any; + cursor = await getProfileEvents(mongo, config, profileId); + metrics.db_find = ms.lapMs(); + const userPromise = getProfileUser(mongo, config, profileId, metrics); + let count = 0; + const userProvider = async () => { + return await userPromise; + }; + + const eventsProvider = async () => { + const start = Date.now(); + const next = await cursor.next(); + metrics.db_events += Date.now() - start; + if (next) { + count++; + return next as unknown as AnalyticsServerEvent; + } else { + return undefined; + } + }; + + const result = await runChain(profileBuilder, profileId, funcChain, eventsProvider, userProvider); + metrics.udf = ms.lapMs(); + metrics.db = metrics.db_events + metrics.db_user + metrics.db_find; + if (result) { + await sendToBulker(profileBuilder, result, funcChain.context); + metrics.bulker = ms.lapMs(); + funcChain.context.log.info( + funcCtx, + `User ${profileId} processed in ${ms.elapsedMs()}ms (events: ${count}). Result: ${JSON.stringify( + result + )} Metrics: ${JSON.stringify(metrics)}` + ); + } else { + funcChain.context.log.warn( + funcCtx, + `No profile result for user ${profileId}. processed in ${ms.elapsedMs()}ms (events: ${count}). Metrics: ${JSON.stringify( + metrics + )}` + ); + } + } catch (e: any) { + status = "error"; + funcChain.context.log.error(funcCtx, `Error while processing user ${profileId}: ${e.message}`); + } finally { + // @ts-ignore + cursor?.close(); + promProfileStatuses.labels({ builderId: profileBuilder.id, priority, status }).observe(ms.elapsedMs() / 1000); + } +} + +async function sendToBulker(profileBuilder: ProfileBuilder, profile: Profile, context: FunctionChainContext) { + const ctx: FullContext = { + log: { + error: (message: string, ...args: any[]) => { + context.log.error(funcCtx, message, ...args); + }, + info: (message: string, ...args: any[]) => { + context.log.info(funcCtx, message, ...args); + }, + warn: (message: string, ...args: any[]) => { + context.log.warn(funcCtx, message, ...args); + }, + debug: (message: string, ...args: any[]) => { + context.log.debug(funcCtx, message, ...args); + }, + }, + fetch: context.fetch, + store: context.store, + getWarehouse: () => { + throw new Error("Warehouse API is not available in builtin functions"); + }, + props: { + bulkerEndpoint: bulkerBase, + destinationId: profile.destination_id || profileBuilder.destinationId, + authToken: bulkerAuthKey, + dataLayout: "passthrough", + streamOptions: { + primaryKey: "profile_id", + schema: JSON.stringify(bulkerSchema), + }, + }, + connection: { + id: profile.destination_id || profileBuilder.destinationId, + }, + destination: { + id: profileBuilder.destinationId, + type: "", + hash: "", + }, + source: { + id: "", + type: "s2s", + }, + headers: {}, + receivedAt: new Date(), + workspace: { id: profileBuilder.workspaceId }, + }; + const payload = { + [TableNameParameter]: profile.table_name || "profiles", + }; + transfer(payload, profile, ["destination_id", "table_name"]); + + await bulkerDestination.default(payload as unknown as AnalyticsServerEvent, ctx); +} + +async function getProfileEvents(mongo: MongoClient, config: ProfilesConfig, profileId: string) { + return mongo + .db(config.eventsDatabase) + .collection(config.eventsCollectionName) + .find( + { + [profileIdHashColumn]: int32Hash(profileId), + [profileIdColumn]: profileId, + }, + { readPreference: ReadPreference.NEAREST } + ); +} + +async function getProfileUser( + mongo: MongoClient, + config: ProfilesConfig, + profileId: string, + metrics: any +): Promise { + const start = Date.now(); + const u = await mongo + .db(config.eventsDatabase) + .collection(config.traitsCollectionName) + .findOne({ [profileIdColumn]: profileId }, { readPreference: ReadPreference.NEAREST }); + metrics.db_user = Date.now() - start; + if (!u) { + return { + profileId, + userId: "", + anonymousId: "", + traits: {}, + }; + } else { + return { + profileId, + userId: u.userId, + anonymousId: u.anonymousId, + traits: u.traits, + }; + } +} + +async function processProfileIds(mongo: MongoClient, config: ProfilesConfig, cb: (profileId: string) => void) { + let cursor: AggregationCursor; + try { + cursor = mongo + .db(config.eventsDatabase) + .collection(config.eventsCollectionName) + .aggregate([ + { + $group: { + _id: "$" + profileIdColumn, + }, + }, + ]) + .withReadPreference(ReadPreference.NEAREST); + for await (const doc of cursor) { + cb(doc._id); + } + } finally { + // @ts-ignore + cursor?.close(); + } +} + +function createDeferred() { + let resolve, reject; + + const promise = new Promise((res, rej) => { + resolve = res; + reject = rej; + }); + + return { promise, resolve, reject }; +} diff --git a/services/profiles/src/index.ts b/services/profiles/src/index.ts new file mode 100644 index 000000000..92bbf3d42 --- /dev/null +++ b/services/profiles/src/index.ts @@ -0,0 +1,274 @@ +import { checkHash, checkRawToken, getLog, setServerJsonFormat } from "juava"; +import express from "express"; +import Prometheus from "prom-client"; +import { Server } from "node:net"; +import { getHeapSnapshot } from "node:v8"; +import { connectionsStore, profilesStore } from "./lib/repositories"; +import { db } from "./lib/db"; +import { profileBuilder, ProfileBuilderRunner } from "./builder"; +import { createClickhouseLogger, DummyEventsStore, EventsStore, mongodb } from "@jitsu/core-functions"; + +const log = getLog("profile-builder"); + +setServerJsonFormat(process.env.LOG_FORMAT === "json"); + +const http = express(); +http.use(express.json({ limit: "20mb" })); +http.use(express.urlencoded({ limit: "20mb" })); + +const metricsHttp = express(); + +const httpPort = process.env.HTTP_PORT || process.env.PORT || 3402; +const metricsPort = process.env.METRICS_PORT || 9091; + +let started = false; +let closed = false; + +const profileBuilders: Map = new Map(); +const repoUpdateInterval = process.env.REPOSITORY_REFRESH_PERIOD_SEC + ? parseInt(process.env.REPOSITORY_REFRESH_PERIOD_SEC) * 1000 + : 2000; + +async function main() { + const errorTypes = ["unhandledRejection", "uncaughtException"]; + const signalTraps = ["SIGTERM", "SIGINT", "SIGUSR2"]; + + errorTypes.forEach(type => { + process.on(type, err => { + log.atError().withCause(err).log(`process.on ${type}`); + }); + }); + + process.on("exit", code => { + log.atInfo().log(`Process exited with code ${code}`); + }); + + await mongodb.waitInit(); + let eventsLogger: EventsStore = DummyEventsStore; + if (process.env.CLICKHOUSE_HOST || process.env.CLICKHOUSE_URL) { + eventsLogger = createClickhouseLogger(); + } + + let httpServer: Server; + let metricsServer: Server | undefined; + + const gracefulShutdown = async () => { + closed = true; + + for (const [pbId, profileBuilder] of profileBuilders) { + await profileBuilder + .close() + .catch(e => + log.atError().withCause(e).log(`Failed to shutdown profile builder for: ${pbId} v: ${profileBuilder.version}`) + ); + } + + if (eventsLogger) { + eventsLogger.close(); + } + if (httpServer) { + httpServer.close(); + } + + profilesStore.stop(); + connectionsStore.stop(); + + await db.pgPool.close(); + + const extraDelay = process.env.SHUTDOWN_EXTRA_DELAY_SEC + ? 1000 * parseInt(process.env.SHUTDOWN_EXTRA_DELAY_SEC) + : 5000; + if (extraDelay > 0) { + log.atInfo().log(`Giving extra ${extraDelay / 1000}s. to flush logs and scrape metrics...`); + //extra time to flush logs + setTimeout(() => { + if (metricsServer) { + metricsServer.close(); + } + process.exit(started ? 0 : 1); + }, extraDelay); + } + }; + + signalTraps.forEach(type => { + process.once(type, () => { + gracefulShutdown(); + }); + }); + + try { + Prometheus.collectDefaultMetrics(); + await db.pgPool.waitInit(); + + const ws = await profilesStore.get(); + if (!ws.enabled) { + log.atError().log("Connection store is not configured. Profile Builder will not work"); + process.exit(1); + } + const connStore = await connectionsStore.get(); + if (!connStore.enabled) { + log.atError().log("Connection store is not configured. Profile Builder will not work"); + process.exit(1); + } + + metricsServer = initMetricsServer(); + } catch (e) { + log.atError().withCause(e).log("Failed to start"); + process.exit(1); + } + + httpServer = initHTTP(); + + refreshLoop(eventsLogger); +} + +function refreshLoop(eventsLogger: EventsStore) { + (async () => { + while (!closed) { + const started = Date.now(); + const ws = profilesStore.getCurrent()!; + const actualProfileBuilders = new Set(); + for (const [workspaceId, workspace] of Object.entries(ws.getAll())) { + for (const pb of workspace.profileBuilders) { + try { + const currentPb = profileBuilders.get(pb.id); + if (currentPb) { + if (pb.version != currentPb.version()) { + log + .atInfo() + .log(`Profile builder version changed for: ${pb.id} v: ${pb.version} old: ${currentPb.version()}`); + await currentPb.close(); + profileBuilders.delete(pb.id); + profileBuilders.set(pb.id, await profileBuilder(workspaceId, pb, eventsLogger)); + } + } else { + log.atInfo().log(`Starting profile builder for: ${pb.id} v: ${pb.version}`); + profileBuilders.set(pb.id, await profileBuilder(workspaceId, pb, eventsLogger)); + } + actualProfileBuilders.add(pb.id); + } catch (e) { + log.atError().withCause(e).log(`Failed to start profile builder for: ${pb.id} v: ${pb.version}`); + } + } + } + for (const [pbId, pb] of profileBuilders) { + if (!actualProfileBuilders.has(pbId)) { + try { + log.atInfo().log(`Deleting profile builder for: ${pbId} v: ${pb.version}`); + await pb.close(); + profileBuilders.delete(pbId); + } catch (e) { + log.atError().withCause(e).log(`Failed to shutdown profile builder for: ${pbId} v: ${pb.version}`); + } + } + } + const waitMs = repoUpdateInterval - (Date.now() - started); + if (waitMs > 0) { + await new Promise(resolve => setTimeout(resolve, repoUpdateInterval)); + } + } + })().catch(async e => { + log.atError().withCause(e).log("Failed refresh loop"); + process.kill(process.pid, "SIGTERM"); + }); +} + +function initHTTP() { + http.use((req, res, next) => { + if (req.path === "/health" || req.path === "/version") { + return next(); + } + let token = req.headers.authorization || ""; + if (token) { + if (token.startsWith("Bearer ")) { + token = token.substring("Bearer ".length); + } else { + res.status(401).json({ error: "Authorization header with Bearer token is required" }); + return; + } + } + if (!checkAuth(token)) { + if (token) { + res.status(401).json({ error: `Invalid token: ${token}` }); + } else { + res.status(401).json({ error: "Authorization header with Bearer token is required" }); + } + return; + } + next(); + }); + http.get("/health", (req, res) => { + res.json({ + status: "pass", + workspaceStore: { + enabled: profilesStore.getCurrent()?.enabled || "loading", + status: profilesStore.status(), + lastUpdated: profilesStore.lastRefresh(), + lastModified: profilesStore.lastModified(), + }, + connectionsStore: { + enabled: connectionsStore.getCurrent()?.enabled || "loading", + status: connectionsStore.status(), + lastUpdated: connectionsStore.lastRefresh(), + lastModified: connectionsStore.lastModified(), + }, + }); + }); + http.get("/wtfheap", async (req, res) => { + const snapshot = getHeapSnapshot(); + log.atInfo().log("snapshot"); + snapshot.pipe(res); + log.atInfo().log("snapshot2"); + }); + const httpServer = http.listen(httpPort, () => { + log.atInfo().log(`Listening on port ${httpPort}`); + started = true; + }); + httpServer.on("error", e => { + log.atError().withCause(e).log("Failed to start http server. Exiting..."); + process.kill(process.pid, "SIGTERM"); + }); + return httpServer; +} + +function initMetricsServer() { + metricsHttp.get("/metrics", async (req, res) => { + res.set("Content-Type", Prometheus.register.contentType); + const result = await Prometheus.register.metrics(); + res.end(result); + }); + const metricsServer = metricsHttp.listen(metricsPort, () => { + log.atInfo().log(`Listening metrics on port ${metricsPort}`); + }); + metricsServer.on("error", e => { + log.atError().withCause(e).log("Failed to start metrics server"); + }); + return metricsServer; +} + +function checkAuth(token: string): boolean { + let tokens: string[] = []; + let checkFunction: (token: string, secret: string) => boolean = () => false; + if (process.env.ROTOR_AUTH_TOKENS) { + tokens = process.env.ROTOR_AUTH_TOKENS.split(","); + checkFunction = checkHash; + } else if (process.env.ROTOR_RAW_AUTH_TOKENS) { + tokens = process.env.ROTOR_RAW_AUTH_TOKENS.split(","); + checkFunction = checkRawToken; + } else { + log.atWarn().log("No auth tokens are configured. Profile Builder is open for everyone."); + return true; + } + if (tokens.length > 0) { + for (const tokenHashOrPlain of tokens) { + if (checkFunction(tokenHashOrPlain, token)) { + return true; + } + } + } + return false; +} + +main(); + +export {}; diff --git a/services/profiles/src/lib/db.ts b/services/profiles/src/lib/db.ts new file mode 100644 index 000000000..d223a1efc --- /dev/null +++ b/services/profiles/src/lib/db.ts @@ -0,0 +1,204 @@ +import { Pool, PoolClient } from "pg"; +import Cursor from "pg-cursor"; +import { getLog, getSingleton, hideSensitiveInfo, namedParameters, newError, requireDefined, stopwatch } from "juava"; + +export type Handler = (row: Record) => Promise | void; + +//we will need to support named params in future +export type ParamValues = any[] | Record; +const log = getLog("db"); + +export type ProfileBuilderState = { + profileBuilderId: string; + updatedAt?: Date; + fullRebuildInfo?: any; + queuesInfo?: any; + metrics?: any; +}; + +export type ProfileBuilderFullRebuildInfo = { + version: number; + timestamp: Date; + profilesCount: number; +}; + +export type ProfileBuilderQueueInfo = { + intervalSec?: number; + timestamp: Date; + queues: Record< + number, + { + priority: number; + size: number; + processed?: number; + } + >; +}; + +type PgHelper = { + getProfileBuilderState(profileBuilderId: string): Promise; + updateProfileBuilderFullRebuildInfo(profileBuilderId: string, info: ProfileBuilderFullRebuildInfo): Promise; + updateProfileBuilderQueuesInfo(profileBuilderId: string, info: ProfileBuilderQueueInfo): Promise; + updateProfileBuilderMetrics(profileBuilderId: string, metrics: any): Promise; + streamQuery(query: string, values?: ParamValues | Handler, handler?: Handler | undefined): Promise<{ rows: number }>; +}; + +const pgHelper: PgHelper = { + async getProfileBuilderState(profileBuilderId: string): Promise { + let rows = await db.pgPool().query( + `select * + from newjitsu."ProfileBuilderState2" + where "profileBuilderId" = $1::text`, + [profileBuilderId] + ); + if (rows.rowCount) { + return rows.rows[0]; + } + return undefined; + }, + async updateProfileBuilderFullRebuildInfo(profileBuilderId: string, info: ProfileBuilderFullRebuildInfo) { + await db.pgPool().query( + ` + insert into newjitsu."ProfileBuilderState2" + values ($1, now(), $2, null, null) + ON CONFLICT ON CONSTRAINT "ProfileBuilderState2_pkey" DO UPDATE SET "updatedAt" = now(), + "fullRebuildInfo" = $2`, + [profileBuilderId, info] + ); + }, + async updateProfileBuilderQueuesInfo(profileBuilderId: string, info: ProfileBuilderQueueInfo) { + await db.pgPool().query( + ` + insert into newjitsu."ProfileBuilderState2" + values ($1, now(), null, $2, null) + ON CONFLICT ON CONSTRAINT "ProfileBuilderState2_pkey" DO UPDATE SET "updatedAt" = now(), + "queuesInfo" = $2`, + [profileBuilderId, info] + ); + }, + async updateProfileBuilderMetrics(profileBuilderId: string, metrics: any) { + await db.pgPool().query( + ` + insert into newjitsu."ProfileBuilderState2" + values ($1, now(), null, null, $2) + ON CONFLICT ON CONSTRAINT "ProfileBuilderState2_pkey" DO UPDATE SET "updatedAt" = now(), + "metrics" = $2`, + [profileBuilderId, metrics] + ); + }, + async streamQuery( + query: string, + _values: ParamValues | Handler, + _handler: Handler | undefined + ): Promise<{ rows: number }> { + const values = typeof _values === "function" ? undefined : _values; + const handler = + typeof _values === "function" + ? _values + : requireDefined( + _handler, + "handler is not defined. It should be passed as second 3rd argument of streamQuery()" + ); + const { query: processedQuery, values: processedParams } = namedParameters(query, values || []); + const sw = stopwatch(); + let totalRows = 0; + let cursor: Cursor | undefined = undefined; + const client: PoolClient = await db.pgPool().connect(); + try { + cursor = client.query(new Cursor(processedQuery, processedParams)); + let rows = await cursor.read(100); + while (rows.length > 0) { + for (let i = 0; i < rows.length; i++) { + await handler(rows[i]); + totalRows++; + } + rows = await cursor.read(100); + } + let queryResult; + + queryResult = await db.pgPool().query(processedQuery, processedParams); + } catch (e) { + log + .atError() + .withCause(e) + .log("Error executing query: \n" + processedQuery + "\n with params: " + JSON.stringify(processedParams)); + throw newError("Error executing the query. See query in logs", e); + } finally { + if (cursor) { + await cursor.close(() => { + client.release(); + }); + } else if (client) { + client.release(); + } + } + + log.atDebug().log(`Query executed in ${sw.elapsedMs()}ms: ${processedQuery}${processedParams}`); + + return { rows: totalRows }; + }, +}; + +export const db = { + pgPool: getSingleton("pg", createPg), + pgHelper: () => pgHelper, +} as const; + +export type DatabaseConnection = typeof db; + +export type PgSSLMode = "disable" | "prefer" | "require" | "no-verify"; + +export function createPg(): Pool { + const connectionUrl = getApplicationDatabaseUrl(); + const parsedUrl = new URL(connectionUrl); + const schema = parsedUrl.searchParams.get("schema"); + if (schema !== "newjitsu") { + const tBorder = `┌─────────────────────────────────────────────────────────────────────┐`; + const bBorder = `└─────────────────────────────────────────────────────────────────────┘`; + const msg = [ + "\n", + tBorder, + `│ Jitsu requires to connect to the database with "newjitsu" schema`.padEnd(tBorder.length - 2, " ") + "│", + bBorder, + ].join("\n"); + log.atError().log(msg); + throw new Error(`Invalid schema ${schema} in database connection URL. Expected 'newjitsu' schema.`); + } + const sslMode = parsedUrl.searchParams.get("sslmode") || ("disable" as PgSSLMode); + if (sslMode === "require" || sslMode === "prefer") { + throw new Error(`sslmode=${sslMode} is not supported`); + } + + const pool = new Pool({ + max: 20, + idleTimeoutMillis: 600000, + connectionString: requireDefined(process.env.DATABASE_URL, "env.DATABASE_URL is not defined"), + ssl: sslMode === "no-verify" ? { rejectUnauthorized: false } : undefined, + application_name: (parsedUrl.searchParams.get("application_name") || "console") + "-raw-pg", + }); + pool.on("connect", async client => { + log + .atInfo() + .log( + `Connecting new client ${hideSensitiveInfo(connectionUrl)}. Pool stat: idle=${pool.idleCount}, waiting=${ + pool.waitingCount + }, total=${pool.totalCount}` + (schema ? `. Default schema: ${schema}` : "") + ); + //this is commented on purpose, it won't work for pgbouncer in transaction mode https://www.pgbouncer.org/features.html + //let's leave it commented for information purposes + // if (schema) { + // await client.query(`SET search_path TO "${schema}"`); + // } + }); + pool.on("error", error => { + log.atError().withCause(error).log("Pool error"); + }); + return pool; +} + +export function getApplicationDatabaseUrl(): string { + return requireDefined( + process.env.APP_DATABASE_URL || process.env.DATABASE_URL, + "neither env.DATABASE_URL, nor env.APP_DATABASE_URL is not defined" + ); +} diff --git a/services/profiles/src/lib/functions-chain.ts b/services/profiles/src/lib/functions-chain.ts new file mode 100644 index 000000000..9e985b8f8 --- /dev/null +++ b/services/profiles/src/lib/functions-chain.ts @@ -0,0 +1,186 @@ +import { + createMongoStore, + EventsStore, + FunctionChainContext, + FunctionConfig, + FunctionContext, + makeFetch, + makeLog, + MetricsMeta, + mongodb, + Profile, + ProfileBuilder, + ProfileFunctionWrapper, + ProfileUDFWrapper, + EventsProvider, + ProfileUserProvider, + EntityStore, + EnrichedConnectionConfig, + warehouseQuery, +} from "@jitsu/core-functions"; + +import { getLog, newError } from "juava"; +import NodeCache from "node-cache"; +import isEqual from "lodash/isEqual"; +import { ProfileResult } from "@jitsu/protocols/profile"; +import { metrics } from "./metrics"; + +export type Func = { + id: string; + exec: ProfileFunctionWrapper; + context: FunctionContext; + hash?: string; +}; + +export type FuncChain = { + context: FunctionChainContext; + functions: Func[]; +}; + +const log = getLog("functions-chain"); + +//cache compiled udfs for 10min +const udfTTL = 60 * 10; +const udfCache = new NodeCache({ stdTTL: udfTTL, checkperiod: 60, useClones: false }); +udfCache.on("del", (key, value) => { + log.atDebug().log(`UDF ${key} deleted from cache`); + value.wrapper?.close(); +}); + +export type FunctionExecRes = { + receivedAt?: any; + eventIndex: number; + event?: any; + metricsMeta?: MetricsMeta; + functionId: string; + error?: any; + dropped?: boolean; + ms: number; +}; + +export type FunctionExecLog = FunctionExecRes[]; + +export function buildFunctionChain( + profileBuilder: ProfileBuilder, + connStore: EntityStore, + eventsLogger: EventsStore, + fetchTimeoutMs: number = 2000 +): FuncChain { + const pbLongId = `${profileBuilder.workspaceId}-${profileBuilder.id}-v${profileBuilder.version}`; + const store = createMongoStore(profileBuilder.workspaceId, mongodb, false, true, metrics); + + const chainCtx: FunctionChainContext = { + fetch: makeFetch(profileBuilder.id, eventsLogger, "info", fetchTimeoutMs), + log: makeLog(profileBuilder.id, eventsLogger, false), + store, + query: async (conId: string, query: string, params: any) => { + return warehouseQuery(profileBuilder.workspaceId, connStore, conId, query, params, metrics); + }, + }; + const funcCtx = { + function: { + id: profileBuilder.id, + type: "profile", + debugTill: profileBuilder.debugTill ? new Date(profileBuilder.debugTill) : undefined, + }, + props: profileBuilder.connectionOptions?.variables || {}, + }; + const udfFuncs: FunctionConfig[] = profileBuilder.functions || []; + if (udfFuncs.length === 0) { + throw newError(`No UDF functions found for profile builder ${pbLongId}`); + } + let cached: any; + let hash: any[]; + hash = udfFuncs.map(f => f.codeHash); + hash.push(profileBuilder.updatedAt); + cached = udfCache.get(pbLongId); + if (!cached || !isEqual(cached?.hash, hash)) { + log.atInfo().log(`UDF for connection ${pbLongId} changed (hash ${hash} != ${cached?.hash}). Reloading`); + const wrapper = ProfileUDFWrapper( + profileBuilder.id, + profileBuilder.version, + pbLongId, + chainCtx, + funcCtx, + udfFuncs.map(f => ({ id: profileBuilder.id, name: f.name, code: f.code })) + ); + const oldWrapper = cached?.wrapper; + if (oldWrapper) { + setTimeout(() => { + oldWrapper.close(); + }, 10000); + } + cached = { wrapper, hash }; + udfCache.set(pbLongId, cached); + } + udfCache.ttl(pbLongId, udfTTL); + + const udfPipelineFunc = (chainCtx: FunctionChainContext): ProfileFunctionWrapper => { + return async (events, user, ctx) => { + try { + return await cached.wrapper.userFunction(events, user, ctx); + } catch (e: any) { + if ((e?.message ?? "").includes("Isolate is disposed")) { + // due to async nature other 'thread' could already replace this isolate. So check it + if (cached.wrapper.isDisposed()) { + log.atError().log(`UDF for pb:${pbLongId} VM was disposed. Reloading`); + const wrapper = ProfileUDFWrapper( + profileBuilder.id, + profileBuilder.version, + pbLongId, + chainCtx, + funcCtx, + udfFuncs.map(f => ({ id: profileBuilder.id, name: f.name, code: f.code })) + ); + cached = { wrapper, hash }; + udfCache.set(pbLongId, cached); + return wrapper.userFunction(events, user, ctx); + } else { + // we have alive isolate now. try again + return await cached.wrapper.userFunction(events, user, ctx); + } + } else { + throw e; + } + } + }; + }; + + const funcs: Func[] = [ + { + id: "udf.PIPELINE", + context: funcCtx, + exec: udfPipelineFunc(chainCtx), + }, + ]; + + return { + functions: funcs, + context: chainCtx, + }; +} + +export async function runChain( + profileBuilder: ProfileBuilder, + profileId: string, + chain: FuncChain, + eventsProvider: EventsProvider, + userProvider: ProfileUserProvider +): Promise { + const f = chain.functions[0]; + let result: ProfileResult | undefined = undefined; + try { + result = await f.exec(eventsProvider, userProvider, f.context); + return { + profile_id: result?.profileId || result?.["profile_id"] || profileId, + destination_id: result?.destinationId || result?.["destination_id"] || profileBuilder.destinationId, + table_name: + result?.tableName || result?.["table_name"] || profileBuilder.connectionOptions?.tableName || "profiles", + traits: { ...(await userProvider()).traits, ...result?.traits }, + version: profileBuilder.version, + updated_at: new Date(), + }; + } catch (err: any) { + throw newError(`Function execution failed`, err); + } +} diff --git a/services/profiles/src/lib/kafka.ts b/services/profiles/src/lib/kafka.ts new file mode 100644 index 000000000..20c810984 --- /dev/null +++ b/services/profiles/src/lib/kafka.ts @@ -0,0 +1,77 @@ +import { isTruish, parseNumber, requireDefined } from "juava"; +import { readFileSync } from "fs"; +import { AdminClient } from "@confluentinc/kafka-javascript"; + +export type KafkaCredentials = { + brokers: string[]; + ssl?: boolean | Record; + sasl?: { + mechanism: "scram-sha-256" | "scram-sha-512"; + username: string; + password: string; + }; +}; + +export type KafkaSettings = { + topicPrefix?: string; + topicReplicationFactor: number; + topicRetentionMs: number; + topicSegmentMs: number; +}; + +function getKafkaCredentialsFromEnv(): KafkaCredentials { + const ssl = isTruish(process.env.KAFKA_SSL); + const sslSkipVerify = isTruish(process.env.KAFKA_SSL_SKIP_VERIFY); + + let sslOption: KafkaCredentials["ssl"] = undefined; + + if (ssl) { + if (sslSkipVerify) { + // TLS enabled, but server TLS certificate is not verified + sslOption = { + rejectUnauthorized: false, + checkServerIdentity: () => undefined, + }; + } else if (process.env.KAFKA_SSL_CA) { + // TLS enabled, server TLS certificate is verified using a custom CA certificate + sslOption = { + ca: process.env.KAFKA_SSL_CA, + }; + } else if (process.env.KAFKA_SSL_CA_FILE) { + // TLS enabled, server TLS certificate is verified using a custom CA certificate (loaded from a local file) + sslOption = { + ca: readFileSync(process.env.KAFKA_SSL_CA_FILE, "utf-8"), + }; + } else { + // TLS enabled, no extra configurations + sslOption = true; + } + } + + return { + brokers: requireDefined(process.env.KAFKA_BOOTSTRAP_SERVERS, "env KAFKA_BOOTSTRAP_SERVERS is required").split(","), + ssl: sslOption, + sasl: process.env.KAFKA_SASL ? JSON.parse(process.env.KAFKA_SASL) : undefined, + }; +} + +function getKafkaSettingsFromEnv(): KafkaSettings { + return { + topicPrefix: process.env.KAFKA_TOPIC_PREFIX, + topicReplicationFactor: parseNumber(process.env.KAFKA_TOPIC_REPLICATION_FACTOR, 1), + topicRetentionMs: parseNumber(process.env.KAFKA_TOPIC_RETENTION_HOURS, 48) * 60 * 60 * 1000, + topicSegmentMs: parseNumber(process.env.KAFKA_TOPIC_SEGMENT_HOURS, 24) * 60 * 60 * 1000, + }; +} + +export function topicName(profileBuilderId: string, priority: number): string { + return `${kafkaSettings.topicPrefix ?? ""}in.id.${profileBuilderId}.m.profiles.t.${priority}`; +} + +export const kafkaSettings = getKafkaSettingsFromEnv(); +export const kafkaCredentials = getKafkaCredentialsFromEnv(); + +export const kafkaAdmin = AdminClient.create({ + "bootstrap.servers": kafkaCredentials.brokers.join(","), + "client.id": "profile-builder-" + process.env.INSTANCE_INDEX, +}); diff --git a/services/profiles/src/lib/metrics.ts b/services/profiles/src/lib/metrics.ts new file mode 100644 index 000000000..49952f63b --- /dev/null +++ b/services/profiles/src/lib/metrics.ts @@ -0,0 +1,44 @@ +import { getLog } from "juava"; +import { StoreMetrics } from "@jitsu/core-functions"; + +import { Counter, Gauge, Histogram } from "prom-client"; + +const log = getLog("metrics"); + +export const promStoreStatuses = new Counter({ + name: "profiles_store_statuses", + help: "profiles store statuses", + labelNames: ["namespace", "operation", "status"] as const, +}); +export const promWarehouseStatuses = new Histogram({ + name: "profiles_warehouse_statuses", + help: "profiles warehouse statuses", + labelNames: ["id", "table", "status"] as const, + buckets: [0.02, 0.05, 0.2, 0.5, 1, 2], // durations in seconds +}); +export const promQueueSize = new Gauge({ + name: "profiles_queue_size", + help: "profiles queue size", + // add `as const` here to enforce label names + labelNames: ["builderId", "priority"] as const, +}); +export const promQueueProcessed = new Counter({ + name: "profiles_queue_processed", + help: "profiles queuue processed", + labelNames: ["builderId", "priority"] as const, +}); +export const promProfileStatuses = new Histogram({ + name: "profiles_statuses", + help: "profiles statuses", + buckets: [0.2, 1, 2, 5, 10, 60, 300], // durations in seconds + labelNames: ["builderId", "priority", "status"] as const, +}); + +export const metrics: StoreMetrics = { + storeStatus: (namespace: string, operation: string, status: string) => { + promStoreStatuses.labels(namespace, operation, status).inc(); + }, + warehouseStatus: (id: string, table: string, status: string, timeMs: number) => { + promWarehouseStatuses.labels(id, table, status).observe(timeMs / 1000); + }, +}; diff --git a/services/profiles/src/lib/priority-consumer.ts b/services/profiles/src/lib/priority-consumer.ts new file mode 100644 index 000000000..104b94c5e --- /dev/null +++ b/services/profiles/src/lib/priority-consumer.ts @@ -0,0 +1,168 @@ +import { ProfileBuilder } from "@jitsu/core-functions"; +import { kafkaAdmin, kafkaCredentials, kafkaSettings, topicName } from "./kafka"; +import PQueue from "p-queue"; +import { getLog, parseNumber } from "juava"; +import { KafkaJS } from "@confluentinc/kafka-javascript"; +const concurrency = parseNumber(process.env.CONCURRENCY, 10); +const instancesCount = parseNumber(process.env.INSTANCES_COUNT, 1); + +interface PriorityConsumer { + start(): Promise; + close(): Promise; +} + +type ProfileId = string; +type RateLimitWindow = { + activated: boolean; +}; + +export function createPriorityConsumer( + profileBuilder: ProfileBuilder, + priorityLevels: number, + profileTask: (profileId: string, priority: number) => () => Promise +): PriorityConsumer { + const pbLongId = `${profileBuilder.workspaceId}-${profileBuilder.id}-v${profileBuilder.version}`; + const log = getLog(`pb-${pbLongId}`); + let consumers: KafkaJS.Consumer[] = []; + const rateLimitWindows: Record = {}; + const queue = new PQueue({ concurrency }); + + const onSizeLessThan = async (limit: number) => { + if (queue.size < limit) { + return; + } + return new Promise(resolve => { + const listener = () => { + if (queue.size < limit) { + queue.removeListener("next", listener); + resolve(); + } + }; + queue.on("next", listener); + }); + }; + + const closeQueue = async () => { + await queue.onIdle(); + }; + + async function rateLimitedExecution( + key: string, + task: () => Promise, + intervalMs: number = 1000 * 30 + ): Promise { + const rateLimitWindow = rateLimitWindows[key]; + // First event for key or event after a long pause (more than intervalMs) + if (!rateLimitWindow) { + const newRateLimitWindow: RateLimitWindow = { + activated: false, + }; + rateLimitWindows[key] = newRateLimitWindow; + let timeout: NodeJS.Timeout; + // The newRateLimitWindow collapses all events received for a key in the last intervalMs into the one + // timer will execute the one in that case + timeout = setTimeout(() => { + if (!newRateLimitWindow.activated) { + // No events received in the last intervalMs. Removing the rate limit window + log.atDebug().log(`Deactivating rate limit window for ${key}`); + clearTimeout(timeout); + delete rateLimitWindows[key]; + } else { + // reset the timer and newRateLimitWindow state + timeout.refresh(); + newRateLimitWindow.activated = false; + // execute the task + task(); + } + }, intervalMs); + // First event for key or event after a long pause (more than intervalMs). Execute the task right away + await task(); + } else if (!rateLimitWindow.activated) { + // Event received for key during the intervalMs. Activate the rate limit window + // Task will be executed after interval ends + log.atDebug().log(`Activating rate limit window for ${key}`); + rateLimitWindow.activated = true; + } else { + log.atDebug().log(`Rate limit window for ${key} is already activated`); + } + } + + return { + async start(): Promise { + for (let i = 0; i < priorityLevels; i++) { + const sizeCap = concurrency * (1 - i / 10); + const topic = topicName(profileBuilder.id, i); + kafkaAdmin.createTopic( + { + topic, + num_partitions: instancesCount, + replication_factor: kafkaSettings.topicReplicationFactor, + config: { + "cleanup.policy": "compact,delete", + "retention.ms": kafkaSettings.topicRetentionMs.toString(), + "segment.ms": kafkaSettings.topicSegmentMs.toString(), + }, + }, + e => { + if (!e) { + log.atInfo().log(`Topic ${topic} created`); + } else if (e.code !== 36) { + log + .atError() + .withCause(e) + .log(`Failed to create topic ${topic} : ${JSON.stringify(e)}`); + } else { + log.atDebug().log(`Topic ${topic} already exists`); + } + } + ); + + const consumer = new KafkaJS.Kafka({}).consumer({ + "bootstrap.servers": kafkaCredentials.brokers.join(","), + "group.id": "profile-builder-" + profileBuilder.id, + }); + + await consumer.connect(); + await consumer.subscribe({ topics: [topic] }); + + consumer.run({ + eachMessage: async ({ message }) => { + const profileId = message.key?.toString(); + if (!profileId) { + log.atError().log("Message without key"); + return; + } + await onSizeLessThan(sizeCap); + queue + .add( + async () => { + await rateLimitedExecution(profileId, profileTask(profileId, i), 1000 * 30); + }, + { priority: priorityLevels - i } + ) + .catch(e => { + log.atError().withCause(e).log("Failed to process message"); + }); + }, + }); + + consumers.push(consumer); + } + }, + + async close(): Promise { + log.atInfo().log("Closing consumers..."); + for (const consumer of consumers) { + await consumer.disconnect(); + } + log.atInfo().log("Closing priority queue..."); + await closeQueue(); + log.atInfo().log("Consumers closed"); + }, + }; +} + +export type TopicsReport = Record< + string, + Record +>; diff --git a/services/profiles/src/lib/repositories.ts b/services/profiles/src/lib/repositories.ts new file mode 100644 index 000000000..49dc091f3 --- /dev/null +++ b/services/profiles/src/lib/repositories.ts @@ -0,0 +1,4 @@ +import { EnrichedConnectionConfig, storeFunc, WorkspaceWithProfiles } from "@jitsu/core-functions"; + +export const profilesStore = storeFunc("workspaces-with-profiles"); +export const connectionsStore = storeFunc("rotor-connections"); diff --git a/services/profiles/tsconfig.json b/services/profiles/tsconfig.json new file mode 100644 index 000000000..e54b28d99 --- /dev/null +++ b/services/profiles/tsconfig.json @@ -0,0 +1,29 @@ +{ + "compilerOptions": { + "outDir": "./compiled", + "rootDir": ".", + "noImplicitAny": false, + "allowSyntheticDefaultImports": true, + "importHelpers": true, + "removeComments": true, + "target": "ESNext", + "module": "commonjs", + "lib": [ + "ESNext", "DOM" + ], + "allowJs": true, + "strict": true, + "esModuleInterop": true, + "jsx": "react-jsx", + "moduleResolution": "node", + "resolveJsonModule": true, + "skipLibCheck": true, + }, + "exclude": [ + "node_modules" + ], + "include": [ + "src/**/*" + ] +} + diff --git a/services/profiles/webpack.config.js b/services/profiles/webpack.config.js new file mode 100644 index 000000000..88591c08d --- /dev/null +++ b/services/profiles/webpack.config.js @@ -0,0 +1,48 @@ +const path = require("path"); +const webpack = require("webpack"); + +const config = { + entry: "./src/index.ts", + target: "node", + externals: { + "isolated-vm": "require('isolated-vm')", + "@confluentinc/kafka-javascript": "require('@confluentinc/kafka-javascript')", + }, + node: { + __dirname: false, + }, + devtool: "source-map", + output: { + path: path.resolve(__dirname, "dist"), + }, + plugins: [ + new webpack.IgnorePlugin({ resourceRegExp: /^pg-native$/ }), // Ignore native module + // Add your plugins here + // Learn more about plugins from https://webpack.js.org/configuration/plugins/ + ], + module: { + rules: [ + { + test: /\.(ts|tsx)$/i, + //loader: "ts-loader", + //exclude: ["/node_modules/"], + use: { + loader: "babel-loader", + }, + }, + { + test: /\.node$/, + loader: "node-loader", + }, + ], + }, + optimization: { + minimize: false, + }, + resolve: { + extensions: [".tsx", ".ts", ".jsx", ".js", ".node", "..."], + }, + mode: "production", +}; + +module.exports = () => config; diff --git a/services/rotor/.gitignore b/services/rotor/.gitignore index fb5e6cf40..cdd602b1c 100644 --- a/services/rotor/.gitignore +++ b/services/rotor/.gitignore @@ -1,2 +1,3 @@ /compiled/ /dist/ +/__tests__/artifacts/ diff --git a/services/rotor/__tests__/functions-chain-data.ts b/services/rotor/__tests__/functions-chain-data.ts new file mode 100644 index 000000000..a82c47770 --- /dev/null +++ b/services/rotor/__tests__/functions-chain-data.ts @@ -0,0 +1,305 @@ +export const functions = { + function1: { + id: "function1", + workspaceId: "workspace1", + name: "Function 1", + code: `export default async function(event, { log, fetch, retries, store, props: config }) { + event.properties.first = "1st" + event.properties.retries = retries + event.properties.counter = (event.properties.counter || 0) + 1 + return event +}`, + }, + function2: { + id: "function2", + workspaceId: "workspace1", + name: "Function 2", + code: `export default async function(event, { log, fetch, store, props: config }) { + event.properties.second = "2nd" + event.properties.counter = (event.properties.counter || 0) + 1 + return event +}`, + }, + function2error: { + id: "function2error", + workspaceId: "workspace1", + name: "Function 2", + code: `export default async function(event, { log, fetch, store, retries, props: config }) { + throw new Error("Function is not meant to run") +}`, + }, + function2retry: { + id: "function2retry", + workspaceId: "workspace1", + name: "Function 2", + code: `export default async function(event, { log, fetch, store, retries, props: config }) { + event.properties.second = "2nd" + event.properties.counter = (event.properties.counter || 0) + 1 + if (retries < 1) { + throw new RetryError("Function runs successfully only on 2nd attempt") + } + return event +}`, + }, + function2dropretry: { + id: "function2dropretry", + workspaceId: "workspace1", + name: "Function 2", + code: `export default async function(event, { log, fetch, store, retries, props: config }) { + event.properties.second = "2nd" + event.properties.counter = (event.properties.counter || 0) + 1 + if (retries < 1) { + throw new RetryError("Function runs successfully only on 2nd attempt", { drop: true }) + } + return event +}`, + }, + function3: { + id: "function3", + workspaceId: "workspace1", + name: "Function 3", + code: `export default async function(event, { log, fetch, store, props: config }) { + event.properties.third = "3rd" + event.properties.counter = (event.properties.counter || 0) + 1 + return event +}`, + }, + functionmulti: { + id: "functionmulti", + workspaceId: "workspace1", + name: "Function Multi", + code: `export default async function(event, { log, fetch, store, props: config }) { + return [{...event, n: 1}, {...event, n: 2}] +}`, + }, + functionmultiretry: { + id: "functionmultiretry", + workspaceId: "workspace1", + name: "Function Multi Retry", + code: `export default async function(event, { log, fetch, retries, store, props: config }) { + if (retries < 1) { + throw new RetryError("Function runs successfully only on 2nd attempt") + } + return [{...event, n: 1}, {...event, n: 2}] +}`, + }, +}; + +export const connections = { + simple: { + id: "simple", + workspaceId: "workspace1", + updatedAt: new Date(), + destinationId: "destination1", + streamId: "stream1", + usesBulker: false, + type: "webhook", + options: { + functions: [ + { + functionId: "udf.function1", + }, + { + functionId: "udf.function2", + }, + { + functionId: "udf.function3", + }, + ], + }, + credentials: { + url: "http://localhost:3089/simple", + method: "POST", + headers: [], + }, + }, + error: { + id: "error", + workspaceId: "workspace1", + updatedAt: new Date(), + destinationId: "destination1", + streamId: "stream1", + usesBulker: false, + type: "webhook", + options: { + functions: [ + { + functionId: "udf.function1", + }, + { + functionId: "udf.function2error", + }, + { + functionId: "udf.function3", + }, + ], + }, + credentials: { + url: "http://localhost:3089/error", + method: "POST", + headers: [], + }, + }, + retry: { + id: "retry", + workspaceId: "workspace1", + updatedAt: new Date(), + destinationId: "destination1", + streamId: "stream1", + usesBulker: false, + type: "webhook", + options: { + functions: [ + { + functionId: "udf.function1", + }, + { + functionId: "udf.function2retry", + }, + { + functionId: "udf.function3", + }, + ], + }, + credentials: { + url: "http://localhost:3089/retry", + method: "POST", + headers: [], + }, + }, + drop_retry: { + id: "drop_retry", + workspaceId: "workspace1", + updatedAt: new Date(), + destinationId: "destination1", + streamId: "stream1", + usesBulker: false, + type: "webhook", + options: { + functions: [ + { + functionId: "udf.function1", + }, + { + functionId: "udf.function2dropretry", + }, + { + functionId: "udf.function3", + }, + ], + }, + credentials: { + url: "http://localhost:3089/drop_retry", + method: "POST", + headers: [], + }, + }, + dst_retry: { + id: "dst_retry", + workspaceId: "workspace1", + updatedAt: new Date(), + destinationId: "destination1", + streamId: "stream1", + usesBulker: false, + type: "webhook", + options: { + functions: [ + { + functionId: "udf.function1", + }, + { + functionId: "udf.function2", + }, + { + functionId: "udf.function3", + }, + ], + }, + credentials: { + url: "http://localhost:3089/dst_retry", + method: "POST", + headers: [], + }, + }, + multi: { + id: "multi", + workspaceId: "workspace1", + updatedAt: new Date(), + destinationId: "destination1", + streamId: "stream1", + usesBulker: false, + type: "webhook", + options: { + functions: [ + { + functionId: "udf.function1", + }, + { + functionId: "udf.function2", + }, + { + functionId: "udf.functionmulti", + }, + ], + }, + credentials: { + url: "http://localhost:3089/multi", + method: "POST", + headers: [], + }, + }, + multi_middle: { + id: "multi_middle", + workspaceId: "workspace1", + updatedAt: new Date(), + destinationId: "destination1", + streamId: "stream1", + usesBulker: false, + type: "webhook", + options: { + functions: [ + { + functionId: "udf.function1", + }, + { + functionId: "udf.functionmulti", + }, + { + functionId: "udf.function2", + }, + ], + }, + credentials: { + url: "http://localhost:3089/multi_middle", + method: "POST", + headers: [], + }, + }, + multi_retry: { + id: "multi_retry", + workspaceId: "workspace1", + updatedAt: new Date(), + destinationId: "destination1", + streamId: "stream1", + usesBulker: false, + type: "webhook", + options: { + functions: [ + { + functionId: "udf.function1", + }, + { + functionId: "udf.function2", + }, + { + functionId: "udf.functionmultiretry", + }, + ], + }, + credentials: { + url: "http://localhost:3089/multi_retry", + method: "POST", + headers: [], + }, + }, +}; diff --git a/services/rotor/__tests__/functions-chain.test.ts b/services/rotor/__tests__/functions-chain.test.ts new file mode 100644 index 000000000..f2eb3a2f5 --- /dev/null +++ b/services/rotor/__tests__/functions-chain.test.ts @@ -0,0 +1,574 @@ +import { getLog } from "juava"; +import { createServer, SimpleSyrup } from "./simple-syrup"; +import { functionFilter, rotorMessageHandler } from "../src/lib/message-handler"; +import { CONNECTION_IDS_HEADER } from "../src/lib/rotor"; +import { + createMemoryStore, + DummyEventsStore, + EnrichedConnectionConfig, + EntityStore, + FunctionConfig, + StreamWithDestinations, +} from "@jitsu/core-functions"; +import { IngestMessage } from "@jitsu/protocols/async-request"; +import { isEqual } from "lodash"; +import { functions, connections } from "./functions-chain-data"; +import { expect, test, describe, beforeAll, afterAll } from "@jest/globals"; +import { FuncChainFilter } from "../src/lib/functions-chain"; + +const log = getLog("functions-chain-test"); + +const incomingEvent = { + type: "track", + properties: {}, +}; + +const expectedEvents = { + simple_0: { + type: "track", + properties: { + retries: 0, + first: "1st", + counter: 3, + second: "2nd", + third: "3rd", + }, + context: {}, + }, + error_0: { + type: "track", + properties: { + retries: 0, + first: "1st", + counter: 2, + third: "3rd", + }, + context: {}, + }, + retry_0: { + type: "track", + properties: { + first: "1st", + retries: 0, + counter: 2, + third: "3rd", + }, + context: {}, + }, + retry_1: { + type: "track", + properties: { + retries: 1, + first: "1st", + counter: 3, + second: "2nd", + third: "3rd", + }, + context: {}, + }, + drop_retry_0: { + type: "track", + properties: { + retries: 1, + first: "1st", + counter: 3, + second: "2nd", + third: "3rd", + }, + context: {}, + }, + dst_retry_0: { + type: "INTENTIONALY_INCORRECT", + properties: { + retries: 0, + first: "1st", + counter: 3, + second: "2nd", + third: "3rd", + }, + context: {}, + }, + dst_retry_1: { + type: "track", + properties: { + // that is set by functions. but udf step is skipped for destination retries + retries: 0, + first: "1st", + counter: 3, + second: "2nd", + third: "3rd", + }, + context: {}, + }, + multi_0: { + n: 1, + type: "track", + properties: { + first: "1st", + retries: 0, + counter: 2, + second: "2nd", + }, + context: {}, + }, + multi_1: { + n: 2, + type: "track", + properties: { + first: "1st", + retries: 0, + counter: 2, + second: "2nd", + }, + context: {}, + }, + multi_middle_0: { + type: "track", + properties: { + first: "1st", + retries: 0, + counter: 2, + second: "2nd", + }, + context: {}, + }, + multi_retry_0: { + type: "track", + properties: { + first: "1st", + retries: 0, + counter: 2, + second: "2nd", + }, + context: {}, + }, + multi_retry_1: { + n: 1, + type: "track", + properties: { + first: "1st", + retries: 1, + counter: 2, + second: "2nd", + }, + context: {}, + }, + multi_retry_2: { + n: 2, + type: "track", + properties: { + first: "1st", + retries: 1, + counter: 2, + second: "2nd", + }, + context: {}, + }, +}; + +const funcStore: EntityStore = { + getObject: (id: string) => { + return functions[id]; + }, + getAll: () => { + return functions as unknown as Record; + }, + toJSON: () => "", + enabled: true, + lastModified: new Date(), +}; + +const connectionStore: EntityStore = { + getObject: (id: string) => { + return connections[id]; + }, + getAll: () => { + return connections as unknown as Record; + }, + toJSON: () => "", + enabled: true, + lastModified: new Date(), +}; + +const streamsStore: EntityStore = { + getObject: (id: string) => { + return undefined; + }, + getAll: () => { + return {} as Record; + }, + toJSON: () => "", + enabled: true, + lastModified: new Date(), +}; + +function ingestMessage(connectionId: string, messageId: string, event: any): IngestMessage { + return { + type: "track", + ingestType: "browser", + messageId, + connectionId, + httpPayload: event, + httpHeaders: {}, + origin: { + baseUrl: "example.com", + }, + writeKey: "", + messageCreated: new Date().toISOString(), + }; +} + +describe("Test Functions Chain", () => { + let server: SimpleSyrup; + let lastError: any; + const counters: Record = {}; + + beforeAll(async () => { + let handlerF = (testName: string) => (req, res) => { + lastError = undefined; + if (!counters[testName]) { + counters[testName] = 0; + } + const counter = counters[testName]; + log.atInfo().log( + `[${testName}] received ${counter} request: +`, + JSON.stringify(req.body, null, 2) + ); + res.setHeader("Content-Type", "application/json"); + if (isEqual(req.body, expectedEvents[`${testName}_${counter}`])) { + res.send({ ok: true }); + } else { + lastError = new Error( + `${testName}_${counter} unexpected webhook request:\n${JSON.stringify(req.body, null, 2)}` + ); + res.status(444).send({ ok: false }); + } + counters[testName]++; + }; + server = await createServer({ + port: 3089, + https: false, + handlers: { + "/simple": handlerF("simple"), + "/error": handlerF("error"), + "/retry": handlerF("retry"), + "/drop_retry": handlerF("drop_retry"), + "/dst_retry": handlerF("dst_retry"), + "/multi": handlerF("multi"), + "/multi_middle": handlerF("multi_middle"), + "/multi_retry": handlerF("multi_retry"), + }, + }); + console.log("Running on " + server.baseUrl); + }); + + afterAll(async () => { + console.log("Shutting down server " + server.baseUrl); + await server.close(); + console.log("Server is down " + server.baseUrl); + }); + + test("simple", async () => { + try { + const res = await rotorMessageHandler( + ingestMessage("simple", "message1", incomingEvent), + { + connectionStore: connectionStore, + functionsStore: funcStore, + streamsStore: streamsStore, + eventsLogger: DummyEventsStore, + dummyPersistentStore: createMemoryStore({}), + }, + "all", + { [CONNECTION_IDS_HEADER]: "simple" }, + true, + 0, + 5000 + ); + //log.atInfo().log("Result: ", JSON.stringify(res, null, 2)); + } catch (e: any) { + if (e.message === "HTTP Error: 444 unknown") { + expect(e.event).toEqual(expectedEvents.simple_0); + } + throw e; + } + }); + + test("error", async () => { + try { + await rotorMessageHandler( + ingestMessage("error", "message1", incomingEvent), + { + connectionStore: connectionStore, + functionsStore: funcStore, + streamsStore: streamsStore, + eventsLogger: DummyEventsStore, + dummyPersistentStore: createMemoryStore({}), + }, + "all", + { [CONNECTION_IDS_HEADER]: "error" }, + true, + 0, + 5000 + ); + } catch (e: any) { + if (e.message === "HTTP Error: 444 unknown") { + expect(e.event).toEqual(expectedEvents.error_0); + } + throw e; + } + }); + + test("retry", async () => { + const iMessage = ingestMessage("retry", "message1", incomingEvent); + let filter: FuncChainFilter = "all"; + try { + await rotorMessageHandler( + iMessage, + { + connectionStore: connectionStore, + functionsStore: funcStore, + streamsStore: streamsStore, + eventsLogger: DummyEventsStore, + dummyPersistentStore: createMemoryStore({}), + }, + filter, + { [CONNECTION_IDS_HEADER]: "retry" }, + true, + 0, + 5000 + ); + } catch (e: any) { + expect(e.name).toEqual("RetryError"); + expect(e.message).toEqual("Function runs successfully only on 2nd attempt"); + expect(lastError).toBeUndefined(); + filter = functionFilter(e.functionId); + iMessage.httpPayload = e.event; + } + //retry + try { + await rotorMessageHandler( + iMessage, + { + connectionStore: connectionStore, + functionsStore: funcStore, + streamsStore: streamsStore, + eventsLogger: DummyEventsStore, + dummyPersistentStore: createMemoryStore({}), + }, + filter, + { [CONNECTION_IDS_HEADER]: "retry" }, + true, + 1, + 5000 + ); + } catch (e: any) { + if (e.message === "HTTP Error: 444 unknown") { + expect(e.event).toEqual(expectedEvents.retry_1); + } + throw e; + } + }); + + test("drop_retry", async () => { + const iMessage = ingestMessage("drop_retry", "message1", incomingEvent); + let filter: FuncChainFilter = "all"; + try { + const res = await rotorMessageHandler( + iMessage, + { + connectionStore: connectionStore, + functionsStore: funcStore, + streamsStore: streamsStore, + eventsLogger: DummyEventsStore, + dummyPersistentStore: createMemoryStore({}), + }, + filter, + { [CONNECTION_IDS_HEADER]: "drop_retry" }, + true, + 0, + 5000 + ); + expect(res?.events).toHaveLength(0); + } catch (e: any) { + expect(e.name).toEqual("Drop & RetryError"); + expect(e.message).toEqual("Function runs successfully only on 2nd attempt"); + expect(lastError).toBeUndefined(); + filter = functionFilter(e.functionId); + iMessage.httpPayload = e.event; + } + //retry + try { + await rotorMessageHandler( + iMessage, + { + connectionStore: connectionStore, + functionsStore: funcStore, + streamsStore: streamsStore, + eventsLogger: DummyEventsStore, + dummyPersistentStore: createMemoryStore({}), + }, + filter, + { [CONNECTION_IDS_HEADER]: "drop_retry" }, + true, + 1, + 5000 + ); + } catch (e: any) { + if (e.message === "HTTP Error: 444 unknown") { + expect(e.event).toEqual(expectedEvents.drop_retry_0); + } + throw e; + } + }); + + test("dst_retry", async () => { + const iMessage = ingestMessage("dst_retry", "message1", incomingEvent); + let filter: FuncChainFilter = "all"; + try { + await rotorMessageHandler( + iMessage, + { + connectionStore: connectionStore, + functionsStore: funcStore, + streamsStore: streamsStore, + eventsLogger: DummyEventsStore, + dummyPersistentStore: createMemoryStore({}), + }, + filter, + { [CONNECTION_IDS_HEADER]: "dst_retry" }, + true, + 0, + 5000 + ); + } catch (e: any) { + expect(e.name).toEqual("RetryError"); + expect(e.message).toEqual("HTTP Error: 444 unknown"); + filter = functionFilter(e.functionId); + iMessage.httpPayload = e.event; + } + //retry + try { + const res = await rotorMessageHandler( + iMessage, + { + connectionStore: connectionStore, + functionsStore: funcStore, + streamsStore: streamsStore, + eventsLogger: DummyEventsStore, + dummyPersistentStore: createMemoryStore({}), + }, + filter, + { [CONNECTION_IDS_HEADER]: "dst_retry" }, + true, + 1, + 5000 + ); + //log.atInfo().log("Result: ", JSON.stringify(res, null, 2)); + } catch (e: any) { + throw e; + } + expect(lastError).toBeUndefined(); + }); + + test("multi", async () => { + try { + const res = await rotorMessageHandler( + ingestMessage("multi", "message1", incomingEvent), + { + connectionStore: connectionStore, + functionsStore: funcStore, + streamsStore: streamsStore, + eventsLogger: DummyEventsStore, + dummyPersistentStore: createMemoryStore({}), + }, + "all", + { [CONNECTION_IDS_HEADER]: "multi" }, + true, + 0, + 5000 + ); + expect(res?.events).toHaveLength(2); + } catch (e: any) { + throw e; + } + expect(lastError).toBeUndefined(); + }); + + test("multi_middle", async () => { + try { + const res = await rotorMessageHandler( + ingestMessage("multi_middle", "message1", incomingEvent), + { + connectionStore: connectionStore, + functionsStore: funcStore, + streamsStore: streamsStore, + eventsLogger: DummyEventsStore, + dummyPersistentStore: createMemoryStore({}), + }, + "all", + { [CONNECTION_IDS_HEADER]: "multi_middle" }, + true, + 0, + 5000 + ); + } catch (e: any) { + if (e.message === "HTTP Error: 444 unknown") { + expect(e.event).toEqual(expectedEvents.multi_middle_0); + } + throw e; + } + }); + + test("multi_retry", async () => { + const iMessage = ingestMessage("multi_retry", "message1", incomingEvent); + let filter: FuncChainFilter = "all"; + try { + const res = await rotorMessageHandler( + iMessage, + { + connectionStore: connectionStore, + functionsStore: funcStore, + streamsStore: streamsStore, + eventsLogger: DummyEventsStore, + dummyPersistentStore: createMemoryStore({}), + }, + filter, + { [CONNECTION_IDS_HEADER]: "multi_retry" }, + true, + 0, + 5000 + ); + expect(res?.events).toHaveLength(1); + } catch (e: any) { + expect(e.name).toEqual("RetryError"); + expect(e.message).toEqual("Function runs successfully only on 2nd attempt"); + expect(lastError).toBeUndefined(); + filter = functionFilter(e.functionId); + iMessage.httpPayload = e.event; + } + //retry + try { + const res = await rotorMessageHandler( + iMessage, + { + connectionStore: connectionStore, + functionsStore: funcStore, + streamsStore: streamsStore, + eventsLogger: DummyEventsStore, + dummyPersistentStore: createMemoryStore({}), + }, + filter, + { [CONNECTION_IDS_HEADER]: "multi_retry" }, + true, + 1, + 5000 + ); + expect(res?.events).toHaveLength(2); + } catch (e: any) { + throw e; + } + expect(lastError).toBeUndefined(); + }); +}); diff --git a/services/rotor/__tests__/inmem-store.test.ts b/services/rotor/__tests__/inmem-store.test.ts new file mode 100644 index 000000000..8faed5af8 --- /dev/null +++ b/services/rotor/__tests__/inmem-store.test.ts @@ -0,0 +1,61 @@ +import { getLog } from "juava"; +import * as path from "path"; +import { expect, test } from "@jest/globals"; +import { createInMemoryStore } from "@jitsu/core-functions"; + +const log = getLog("inmem-store-test"); +test("Test in-mem store", async () => { + const artifactsDir = path.join(__dirname, "artifacts"); + const cacheDir = path.join(artifactsDir, "in-mem-store"); + const loader = (ifModifiedSince?: Date) => { + return new Promise<{ store: Date; lastModified: Date }>(resolve => + setTimeout(() => resolve({ store: new Date(), lastModified: new Date() }), 300) + ); + }; + const badLoader = (ifModifiedSince?: Date) => { + return new Promise<{ store: Date; lastModified: Date }>((resolve, reject) => + setTimeout(() => reject(new Error("")), 300) + ); + }; + + const store = createInMemoryStore({ + name: "test", + refresh: loader, + refreshIntervalMillis: 200, + localDir: cacheDir, + }); + + try { + expect(store.getCurrent()).toBeUndefined(); + expect(store.status()).toBe("initializing"); + expect(await store.get()).toBeDefined(); + expect(store.status()).toBe("ok"); + expect(store.getCurrent()).toBeDefined(); + + const date1 = await store.get(); + log.atInfo().log(`Date1: ${date1.toISOString()}`); + await new Promise(resolve => setTimeout(resolve, 1000)); + const date2 = await store.get(); + log.atInfo().log(`Date2: ${date2.toISOString()}`); + expect(date1.getTime() < date2.getTime()).toBeTruthy(); + } finally { + store.stop(); + } + log.atInfo().log("Testing bad store"); + const badStore = createInMemoryStore({ + name: "test", + refresh: badLoader, + refreshIntervalMillis: 200, + localDir: cacheDir, + }); + + try { + expect(badStore.getCurrent()).toBeUndefined(); + expect(badStore.status()).toBe("initializing"); + expect(await badStore.get()).toBeDefined(); + expect(badStore.status()).toBe("outdated"); + expect(badStore.getCurrent()).toBeDefined(); + } finally { + badStore.stop(); + } +}, 10000); diff --git a/services/rotor/__tests__/kafka.test.ts b/services/rotor/__tests__/kafka.test.ts new file mode 100644 index 000000000..5b8058838 --- /dev/null +++ b/services/rotor/__tests__/kafka.test.ts @@ -0,0 +1,47 @@ +import { getLog } from "juava"; +import { connectToKafka } from "../src/lib/kafka-config"; +import { test } from "@jest/globals"; + +const log = getLog("kafka-test"); +test.skip("Kafka Test", async () => { + const kafka = connectToKafka({ defaultAppId: "test", brokers: ["localhost:9092"] }); + const consumer = kafka.consumer({ + kafkaJS: { + groupId: "test", + allowAutoTopicCreation: true, + sessionTimeout: 10000, + autoCommitInterval: 10000, + autoCommit: true, + fromBeginning: true, + }, + }); + await consumer.connect(); + await consumer.subscribe({ topics: ["autocommit-test"] }); + + const producer = kafka.producer({ kafkaJS: { allowAutoTopicCreation: false } }); + await producer.connect(); + + for (let i = 0; i < 200; i++) { + producer.send({ + topic: "autocommit-test", + messages: [ + { + value: `message #${i}`, + }, + ], + }); + } + + await consumer.run({ + partitionsConsumedConcurrently: 8, + eachMessage: async ({ topic, partition, message }) => { + log.atInfo().log(`${topic}:${partition}: ${message.offset} => ${message.value?.toString()}`); + await new Promise(resolve => setTimeout(resolve, 100)); + }, + }); + setTimeout(() => { + consumer.disconnect(); + }, 15000); + + await new Promise(resolve => setTimeout(resolve, 20000)); +}, 40000); diff --git a/services/rotor/__tests__/simple-syrup.ts b/services/rotor/__tests__/simple-syrup.ts new file mode 100644 index 000000000..afe55b9d5 --- /dev/null +++ b/services/rotor/__tests__/simple-syrup.ts @@ -0,0 +1,136 @@ +import type { Application, RequestHandler } from "express"; +import http from "http"; +import https from "https"; + +const express = require("express"); + +const forge = require("node-forge"); + +export type SimpleSyrupOpts = { + port?: number; + https?: boolean; + handlers?: Record; +}; + +export type SimpleSyrup = { + app: Application; + server: http.Server; + port: number; + baseUrl: string; + close: () => Promise; +}; + +function getNextAvailablePort(port: number) { + return new Promise(resolve => { + const server = http.createServer(); + server.on("error", () => { + resolve(getNextAvailablePort(port + 1)); + }); + server.on("listening", () => { + server.close(() => { + resolve(port); + }); + }); + server.listen(port); + }); +} + +function generateX509Certificate(altNames: { type: number; value: string }[]) { + const issuer = [ + { name: "commonName", value: "localhost" }, + { name: "organizationName", value: "ACME Corp" }, + { name: "organizationalUnitName", value: "XYZ Department" }, + ]; + const certificateExtensions = [ + { name: "basicConstraints", cA: true }, + { + name: "keyUsage", + keyCertSign: true, + digitalSignature: true, + nonRepudiation: true, + keyEncipherment: true, + dataEncipherment: true, + }, + { + name: "extKeyUsage", + serverAuth: true, + clientAuth: true, + codeSigning: true, + emailProtection: true, + timeStamping: true, + }, + { + name: "nsCertType", + client: true, + server: true, + email: true, + objsign: true, + sslCA: true, + emailCA: true, + objCA: true, + }, + { name: "subjectAltName", altNames }, + { name: "subjectKeyIdentifier" }, + ]; + const keys = forge.pki.rsa.generateKeyPair(2048); + const cert = forge.pki.createCertificate(); + cert.validity.notBefore = new Date(); + cert.validity.notAfter = new Date(); + cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 1); + cert.publicKey = keys.publicKey; + cert.setSubject(issuer); + cert.setIssuer(issuer); + cert.setExtensions(certificateExtensions); + cert.sign(keys.privateKey); + return { + key: forge.pki.privateKeyToPem(keys.privateKey), + cert: forge.pki.certificateToPem(cert), + }; +} + +function shutdownFunction(server): Promise { + return new Promise(resolve => { + let resolved = false; + const resolveIfNeeded = () => { + if (!resolved) { + resolved = true; + resolve(); + } + }; + setTimeout(resolveIfNeeded, 5000); + server.close(resolveIfNeeded); + }); +} + +export function createServer(opts: SimpleSyrupOpts = {}): Promise { + return new Promise(async (resolve, reject) => { + const app: Application = express(); + app.use(express.json()); + const server = opts?.https ? https.createServer(generateX509Certificate([]), app) : http.createServer(app); + const port = opts?.port ? await getNextAvailablePort(opts.port) : 0; + server.listen(port, () => { + const address = server.address(); + if (!address) { + reject(new Error(`Unable to get server address`)); + return; + } + if (typeof address === "string") { + reject(new Error(`Address is not an of network. This is not supported: ${address} `)); + return; + } + const port = address.port; + if (opts?.handlers) { + for (const [path, handler] of Object.entries(opts?.handlers)) { + app.all(path, handler); + } + } + resolve({ + app: app, + port, + close: () => shutdownFunction(server), + baseUrl: `${opts.https ? "https" : "http"}://localhost:${port}`, + server: server, + }); + }); + }); +} diff --git a/services/rotor/dist_package.json b/services/rotor/dist_package.json index 799fda6cf..49880315a 100644 --- a/services/rotor/dist_package.json +++ b/services/rotor/dist_package.json @@ -2,6 +2,9 @@ "name": "rotor", "version": "0.0.0", "dependencies": { - "vm2": "^3.9.14" + "undici": "^7.11.0", + "isolated-vm": "6.0.0", + "@confluentinc/kafka-javascript": "^1.4.1", + "@mongodb-js/zstd": "^2.0.1" } } diff --git a/services/rotor/jest.config.js b/services/rotor/jest.config.js new file mode 100644 index 000000000..de6e6d30e --- /dev/null +++ b/services/rotor/jest.config.js @@ -0,0 +1,9 @@ +/** @type {import("ts-jest").JestConfigWithTsJest} */ +module.exports = { + //preset: "ts-jest", + preset: "ts-jest", + testMatch: ["**/__tests__/**/*.test.ts"], + testEnvironment: "node", + runner: "jest-runner", + "setupFiles": ["./jest.setup.js"] +}; diff --git a/services/rotor/jest.setup.js b/services/rotor/jest.setup.js new file mode 100644 index 000000000..fa6ee7ee1 --- /dev/null +++ b/services/rotor/jest.setup.js @@ -0,0 +1,8 @@ +process.env.JUAVA_LOG_LEVEL = 'debug'; +global.console = { + log: message => process.stdout.write(message + '\n'), + error: console.error, + warn: console.warn, + info: console.info, + debug: console.debug, +}; diff --git a/services/rotor/package.json b/services/rotor/package.json index 9d74712e0..9270e8598 100644 --- a/services/rotor/package.json +++ b/services/rotor/package.json @@ -11,56 +11,69 @@ "scripts": { "compile": "rm -rf ./dist && tsc -p . ", "bundle_extenals": "cp dist_package.json ./dist/package.json && cd ./dist/ && npm install", - "build": "pnpm compile && webpack && cp ../../webapps/console/prisma/schema.prisma ./dist/schema.prisma && pnpm bundle_extenals", + "build": "pnpm compile && webpack && pnpm bundle_extenals", "start": "dotenv -e ../../.env.local -- node dist/main.js", - "rotor:dev": "dotenv -e ../../.env.local -- nodemon --watch \"src/**\" --ext \"ts,json,tsx\" --exec \"ts-node src/index.ts\"" + "rotor:dev": "dotenv -e ../../.env.local -- nodemon --watch \"src/**\" --ext \"ts,json,tsx\" --exec \"ts-node src/index.ts\"", + "rotor:profile": "dotenv -e ../../.env.local -- nodemon --watch \"src/**\" --ext \"ts,json,tsx\" --exec \"ts-node-dev --inspect -- src/index.ts\"", + "test": "tsc -p . && BULKER_URL=dummy BULKER_AUTH_KEY=dummy jest --verbose" }, "dependencies": { - "object-hash": "^3.0.0", - "node-cache": "^5.1.2", - "@jitsu-internal/console": "workspace:*", + "undici": "^7.11.0", + "@amplitude/ua-parser-js": "^0.7.33", + "@aws-sdk/client-s3": "^3.621.0", + "@clickhouse/client": "^1.10.1", "@jitsu/core-functions": "workspace:*", + "@jitsu/functions-lib": "workspace:*", + "@maxmind/geoip2-node": "^5.0.0", + "@types/pg-cursor": "^2.7.2", + "dayjs": "^1.11.12", + "express": "^4.21.2", + "ioredis": "^5.4.1", + "json5": "^2.2.3", + "jsondiffpatch": "workspace:*", "juava": "workspace:*", - "express": "^4.18.2", - "glob": "^8.0.3", - "ioredis": "^5.2.3", - "json-grab": "0.1.0-alpha.2", - "json5": "^2.1.0", - "minimist": "^1.2.7", - "node-fetch-commonjs": "^3.2.4", - "tslib": "^2.4.0" + "@confluentinc/kafka-javascript": "^1.4.1", + "node-cache": "^5.1.2", + "node-fetch-commonjs": "^3.3.2", + "object-hash": "^3.0.0", + "p-queue": "^6.6.2", + "pg": "~8.11.6", + "pg-cursor": "^2.11.0", + "prom-client": "^15.1.3", + "semver": "^7.6.3", + "tar": "^7.4.3", + "tslib": "^2.6.3" }, "devDependencies": { - "node-loader": "^2.0.0", - "@babel/preset-env": "^7.20.2", - "@babel/preset-typescript": "^7.21.0", - "@babel/preset-react": "^7.18.6", + "@babel/preset-env": "^7.25.2", + "@babel/preset-react": "^7.24.7", + "@babel/preset-typescript": "^7.24.7", + "@jest/globals": "^29.7.0", + "@jest/types": "^29.6.3", "@jitsu/protocols": "workspace:*", - "@rollup/plugin-babel": "^6.0.2", - "@rollup/plugin-commonjs": "^23.0.2", - "@rollup/plugin-json": "^5.0.1", - "@rollup/plugin-multi-entry": "^6.0.0", - "@rollup/plugin-node-resolve": "^15.0.1", - "@rollup/plugin-terser": "^0.1.0", - "@rollup/plugin-typescript": "^9.0.2", - "@types/glob": "^8.0.0", - "@types/minimist": "^1.2.2", - "@types/node": "16.11.6", - "@types/web": "0.0.69", - "@types/webpack": "^5.28.0", - "@webpack-cli/generators": "^3.0.0", - "babel-loader": "^9.1.2", + "@types/express": "^4.17.21", + "@types/jest": "^29.5.11", + "@types/lodash": "^4.17.7", + "@types/node": "^18.15.3", + "@types/pg": "~8.11.15", + "@types/web": "^0.0.152", + "@types/webpack": "^5.28.5", + "@webpack-cli/generators": "^3.0.7", + "babel-loader": "^9.1.3", "clean-webpack-plugin": "^4.0.0", - "copy-webpack-plugin": "^11.0.0", + "copy-webpack-plugin": "^12.0.2", "declaration-bundler-webpack-plugin": "^1.0.3", - "dotenv-cli": "^6.0.0", - "nodemon": "^2.0.20", - "rollup": "^3.2.5", - "ts-loader": "^9.4.2", - "ts-node": "~10.8.1", - "typescript": "^4.9.3", - "webpack": "^5.75.0", - "webpack-cli": "^5.0.0", - "wepack-cli": "0.0.1-security" + "dotenv-cli": "^7.4.2", + "jest": "^29.7.0", + "lodash": "^4.17.21", + "node-loader": "^2.0.0", + "nodemon": "^3.1.4", + "ts-jest": "^29.2.3", + "ts-loader": "^9.5.1", + "ts-node": "^10.9.2", + "ts-node-dev": "^2.0.0", + "typescript": "^5.6.3", + "webpack": "^5.95.0", + "webpack-cli": "^5.1.4" } } diff --git a/services/rotor/rollup.config.js b/services/rotor/rollup.config.js deleted file mode 100644 index bdae6cb18..000000000 --- a/services/rotor/rollup.config.js +++ /dev/null @@ -1,29 +0,0 @@ -const multi = require("@rollup/plugin-multi-entry"); -const resolve = require("@rollup/plugin-node-resolve"); -const commonjs = require("@rollup/plugin-commonjs"); -const rollupJson = require("@rollup/plugin-json"); -const babel = require("@rollup/plugin-babel"); -const typescript = require("@rollup/plugin-typescript"); - -module.exports = [ - { - plugins: [ - multi(), - resolve({ preferBuiltins: false }), - commonjs(), - rollupJson(), - babel({ - babelpHelpers: "inline", - babelrc: false, - extensions: ['.js', '.ts'] - - }), - ], - input: "./compiled/index.js", - output: { - file: `dist/rotor.js`, - format: "iife", - sourcemap: true, - }, - }, -]; diff --git a/services/rotor/src/http/functions.ts b/services/rotor/src/http/functions.ts new file mode 100644 index 000000000..b1bc3a0d1 --- /dev/null +++ b/services/rotor/src/http/functions.ts @@ -0,0 +1,111 @@ +import { getLog, requireDefined } from "juava"; +import { IngestMessage } from "@jitsu/protocols/async-request"; +import { MessageHandlerContext, rotorMessageHandler } from "../lib/message-handler"; +import { CONNECTION_IDS_HEADER } from "../lib/rotor"; +import { AnyEvent } from "@jitsu/protocols/functions"; +import isEqual from "lodash/isEqual"; +import { parse as semverParse } from "semver"; +import * as jsondiffpatch from "jsondiffpatch"; +import { connectionsStore, functionsStore, streamsStore } from "../lib/repositories"; +import { promHandlerMetric } from "../lib/metrics"; + +const jsondiffpatchInstance = jsondiffpatch.create(); +const log = getLog("functions_handler"); + +export const FunctionsHandler = + (rotorContext: Omit) => + async (req, res) => { + const message = req.body as IngestMessage; + //log.atInfo().log(`Functions handler. Message ID: ${message.messageId} connectionId: ${message.connectionId}`); + const result = await rotorMessageHandler(message, { + ...rotorContext, + connectionStore: requireDefined(connectionsStore.getCurrent(), "Connection store is not initialized"), + functionsStore: requireDefined(functionsStore.getCurrent(), "Functions store is not initialized"), + streamsStore: requireDefined(streamsStore.getCurrent(), "Streams store is not initialized"), + }); + if (result?.events && result.events.length > 0) { + res.json(result.events); + } else { + res.status(204).send(); + } + }; + +export const FunctionsHandlerMulti = + (rotorContext: Omit) => + async (req, res, next) => { + const connectionIds = (req.query.ids ?? "").split(",") as string[]; + const message = req.body as IngestMessage; + const functionsFetchTimeout = req.headers["x-request-timeout-ms"] + ? parseInt(req.headers["x-request-timeout-ms"] as string) + : 2000; + const prom = connectionIds + .filter(id => !!id) + .map(id => { + //log.atInfo().log(`Functions handler2. Message ID: ${message.messageId} connectionId: ${id}`); + return rotorMessageHandler( + message, + { + ...rotorContext, + connectionStore: requireDefined(connectionsStore.getCurrent(), "Connection store is not initialized"), + functionsStore: requireDefined(functionsStore.getCurrent(), "Functions store is not initialized"), + streamsStore: requireDefined(streamsStore.getCurrent(), "Streams store is not initialized"), + }, + "all", + { [CONNECTION_IDS_HEADER]: id }, + false, + 0, + functionsFetchTimeout + ); + }); + await Promise.all(prom) + .then(results => { + connectionIds.forEach((id, i) => { + promHandlerMetric.inc({ connectionId: id, status: "success" }, 1); + }); + const events = Object.fromEntries( + results.map(result => [result?.connectionId, mapDiff(message, result?.events)]) + ); + res.json(events); + }) + .catch(e => { + connectionIds.forEach((id, i) => { + promHandlerMetric.inc({ connectionId: id, status: "error" }, 1); + }); + next(`[${connectionIds}] Error processing functions: ${e.name}: ${e.message}`); + }); + }; + +function mapDiff(message: IngestMessage, newEvents?: AnyEvent[]) { + if (!newEvents) { + return []; + } + + return newEvents.map(e => { + if (isEqual(message.httpPayload, e)) { + return "same"; + } + let supportsDiff = false; + const library = message.httpPayload?.context?.library; + if (library?.name === "@jitsu/js") { + const semver = semverParse(library.version); + if (semver && semver.major >= 2) { + supportsDiff = true; + } + } + if (!supportsDiff) { + return e; + } + + const originalSize = JSON.stringify(message.httpPayload).length; + const diff = jsondiffpatchInstance.diff(message.httpPayload, e); + if (!diff) { + return "same"; + } + const diffSize = JSON.stringify(diff).length; + if (diffSize > originalSize) { + return e; + } else { + return { __diff: diff }; + } + }); +} diff --git a/services/rotor/src/http/profiles-udf.ts b/services/rotor/src/http/profiles-udf.ts new file mode 100644 index 000000000..53e7263fa --- /dev/null +++ b/services/rotor/src/http/profiles-udf.ts @@ -0,0 +1,20 @@ +import { mongodb, createMongoStore, ProfileUDFTestRequest, ProfileUDFTestRun } from "@jitsu/core-functions"; +import { getLog } from "juava"; +import { connectionsStore } from "../lib/repositories"; + +const log = getLog("profile-udf-run"); + +export const ProfileUDFRunHandler = async (req, res) => { + const body = req.body as ProfileUDFTestRequest; + log.atInfo().log(`Running profile func: ${body?.id} workspace: ${body?.workspaceId}`, JSON.stringify(body)); + body.store = createMongoStore(body?.workspaceId, mongodb, true, false); + const result = await ProfileUDFTestRun(body, connectionsStore.getCurrent()); + if (result.error) { + log + .atError() + .log( + `Error running profile function: ${body?.id} workspace: ${body?.workspaceId}\n${result.error.name}: ${result.error.message}` + ); + } + res.json(result); +}; diff --git a/services/rotor/src/http/udf.ts b/services/rotor/src/http/udf.ts index d24c3a094..15c240d98 100644 --- a/services/rotor/src/http/udf.ts +++ b/services/rotor/src/http/udf.ts @@ -1,108 +1,20 @@ -import { EventContext, EventsStore, Store } from "@jitsu/protocols/functions"; -import { createFullContext, UDFWrapper } from "@jitsu/core-functions"; +import { UDFTestRun, UDFTestRequest, mongodb, createMongoStore } from "@jitsu/core-functions"; import { getLog } from "juava"; -import { logType } from "@jitsu-internal/console/pages/api/[workspaceId]/function/run"; +import { connectionsStore } from "../lib/repositories"; const log = getLog("udf_run"); -type bodyType = { - functionId: string; - functionName: string; - code: string; - event: any; - config: any; - store: any; - workspaceId: string; -}; - export const UDFRunHandler = async (req, res) => { - const body = req.body as bodyType; - log.atInfo().log(`Running function: ${body?.functionId} workspace: ${body?.workspaceId}`, body); - const logs: logType[] = []; - try { - const eventContext: EventContext = { - headers: {}, - source: { - id: "functionsDebugger-streamId", - }, - destination: { - id: "functionsDebugger-destinationId", - type: "clickhouse", - updatedAt: new Date(), - hash: "hash", - }, - connection: { - id: "functionsDebugger", - }, - }; - - const store: Store = { - get: async (key: string) => { - return body.store[key]; - }, - set: async (key: string, obj: any) => { - body.store[key] = obj; - }, - del: async (key: string) => { - delete body.store[key]; - }, - }; - const eventsStore: EventsStore = { - log(error: boolean, msg: Record): Promise { - switch (msg.type) { - case "log-info": - case "log-warn": - case "log-debug": - case "log-error": - logs.push({ - message: - msg.message?.text + - (Array.isArray(msg.message?.args) && msg.message.args.length > 0 - ? `, ${msg.message?.args.join(",")}` - : ""), - level: msg.type.replace("log-", ""), - timestamp: new Date(), - type: "log", - }); - break; - case "http-request": - let statusText; - if (msg.error) { - statusText = `${msg.error}`; - } else { - statusText = `${msg.statusText ?? ""}${msg.status ? `(${msg.status})` : ""}`; - } - logs.push({ - message: `${msg.method} ${msg.url} :: ${statusText}`, - level: msg.error ? "error" : "info", - timestamp: new Date(), - type: "http", - data: { - body: msg.body, - headers: msg.headers, - response: msg.response, - }, - }); - } - return Promise.resolve(); - }, - }; - const ctx = createFullContext(body.functionId, eventsStore, store, eventContext, {}, body.config); - const wrapper = UDFWrapper(body.functionId, body.functionName, body.code); - const result = await wrapper.userFunction(body.event, ctx); - res.json({ - error: "", - result: result || body.event, - store: body.store, - logs, - }); - } catch (e) { - log.atError().withCause(e).log(`Error running function: ${body?.functionId} workspace: ${body?.workspaceId}`); - res.json({ - error: `${e}`, - result: {}, - store: body?.store ?? {}, - logs, - }); + const body = req.body as UDFTestRequest; + log.atInfo().log(`Running function: ${body?.functionId} workspace: ${body?.workspaceId}`, JSON.stringify(body)); + body.store = createMongoStore(body?.workspaceId, mongodb, true, false); + const result = await UDFTestRun(body, connectionsStore.getCurrent()); + if (result.error) { + log + .atError() + .log( + `Error running function: ${body?.functionId} workspace: ${body?.workspaceId}\n${result.error.name}: ${result.error.message}` + ); } + res.json(result); }; diff --git a/services/rotor/src/index.ts b/services/rotor/src/index.ts index 59d1e95c7..92a7d3a37 100644 --- a/services/rotor/src/index.ts +++ b/services/rotor/src/index.ts @@ -1,295 +1,300 @@ -import { disableService, getLog, newError, randomId, requireDefined, setServerJsonFormat } from "juava"; - -disableService("prisma"); -disableService("pg"); - -import { - connectToKafka, - destinationMessagesTopic, - getCredentialsFromEnv, - rotorConsumerGroupId, -} from "@jitsu-internal/console/lib/server/kafka-config"; - -import { IngestMessage } from "@jitsu/protocols/async-request"; +import { checkHash, checkRawToken, disableService, getLog, setServerJsonFormat, isTruish } from "juava"; +import { destinationMessagesTopic, getCredentialsFromEnv, rotorConsumerGroupId } from "./lib/kafka-config"; import { kafkaRotor } from "./lib/rotor"; -import { fastStore } from "@jitsu-internal/console/lib/server/fast-store"; - -import minimist from "minimist"; -import { glob } from "glob"; -import fs from "fs"; -import { AnalyticsServerEvent } from "@jitsu/protocols/analytics"; -import { createRedisLogger, FuncChain, runChain } from "./lib/functions-chain"; -import { getBuiltinFunction, mongoAnonymousEventsStore, UDFWrapper } from "@jitsu/core-functions"; -import { EventContext, JitsuFunction, Store, SystemContext } from "@jitsu/protocols/functions"; -import { redis } from "@jitsu-internal/console/lib/server/redis"; +import { createClickhouseLogger, DummyEventsStore, EventsStore, mongodb } from "@jitsu/core-functions"; import express from "express"; -import NodeCache from "node-cache"; -import hash from "object-hash"; import { UDFRunHandler } from "./http/udf"; +import Prometheus from "prom-client"; +import { FunctionsHandler, FunctionsHandlerMulti } from "./http/functions"; +import { initMaxMindClient, GeoResolver } from "./lib/maxmind"; +import { MessageHandlerContext, rotorMessageHandler } from "./lib/message-handler"; +import { createMetrics } from "./lib/metrics"; +import { connectionsStore, functionsStore, streamsStore } from "./lib/repositories"; +import { Server } from "node:net"; +import { getApplicationVersion, getDiagnostics } from "./lib/version"; +import { Redis } from "ioredis"; +import { createRedis } from "./lib/redis"; +import * as util from "util"; +import { getHeapSnapshot } from "node:v8"; +import { ProfileUDFRunHandler } from "./http/profiles-udf"; +const log = getLog("rotor"); + +disableService("prisma"); +disableService("pg"); setServerJsonFormat(process.env.LOG_FORMAT === "json"); -const log = getLog("rotor"); const http = express(); -http.use(express.json()); -//cache connections for 20sec -const connectionsCache = new NodeCache({ stdTTL: 20, checkperiod: 60, useClones: false }); -//cache functions for 20sec -const functionsCache = new NodeCache({ stdTTL: 20, checkperiod: 60, useClones: false }); -//cache compiled udfs for 10min (ttl is extended on each access) -const udfTTL = 60 * 10; -const udfCache = new NodeCache({ stdTTL: udfTTL, checkperiod: 60, useClones: false }); -udfCache.on("del", (key, value) => { - log.atInfo().log(`UDF ${key} deleted from cache`); - value.wrapper?.close(); -}); +http.use(express.json({ limit: "20mb" })); +http.use(express.urlencoded({ limit: "20mb" })); + +const metricsHttp = express(); const rotorHttpPort = process.env.ROTOR_HTTP_PORT || process.env.PORT || 3401; -const bulkerBase = requireDefined(process.env.BULKER_URL, "env BULKER_URL is not defined"); -const bulkerAuthKey = requireDefined(process.env.BULKER_AUTH_KEY, "env BULKER_AUTH_KEY is not defined"); +const rotorMetricsPort = process.env.ROTOR_METRICS_PORT || 9091; -const getCachedOrLoad = async (cache: NodeCache, key: string, loader: (key: string) => Promise) => { - const cached = cache.get(key); - if (cached) { - return cached; - } - const loaded = await loader(key); - cache.set(key, loaded); - return loaded; -}; +let started = false; -export async function rotorMessageHandler(_message: string | undefined) { - if (!_message) { - return; - } - const message = JSON.parse(_message) as IngestMessage; - const connection = requireDefined( - await getCachedOrLoad(connectionsCache, message.connectionId, fastStore.getEnrichedConnection), - `Unknown connection: ${message.connectionId}` - ); +async function main() { + const errorTypes = ["unhandledRejection", "uncaughtException"]; + const signalTraps = ["SIGTERM", "SIGINT", "SIGUSR2"]; - log - .atInfo() - .log( - `Processing ${message.type} Message ID: ${message.messageId} for: ${connection.id} (${connection.streamId} → ${connection.destinationId}(${connection.type}))` - ); + errorTypes.forEach(type => { + process.on(type, err => { + log.atError().withCause(err).log(`process.on ${type}`); + }); + }); - const connectionData = connection.options as any; + process.on("exit", code => { + log.atInfo().log(`Process exited with code ${code}`); + }); - let mainFunction; - if (connection.usesBulker) { - mainFunction = { - functionId: "builtin.destination.bulker", - functionOptions: { - bulkerEndpoint: bulkerBase, - destinationId: message.connectionId, - authToken: bulkerAuthKey, - dataLayout: connectionData.dataLayout ?? "segment-single-table", - }, - }; - } else { - const builtin = getBuiltinFunction(`builtin.destination.${connection.type}`); - if (builtin) { - mainFunction = { - functionId: `builtin.destination.${connection.type}`, - functionOptions: connection.credentials, - }; + let httpServer: Server; + let metricsServer: Server | undefined; + let geoResolver: GeoResolver; + let eventsLogger: EventsStore; + let redisClient: Redis | undefined; + try { + Prometheus.collectDefaultMetrics(); + try { + await mongodb.waitInit(); + } catch (e: any) { + log.atWarn().log("Failed to connect to mongodb. Functions Persistent Store won't work: " + e.message); + } + if (process.env.CLICKHOUSE_HOST || process.env.CLICKHOUSE_URL) { + eventsLogger = createClickhouseLogger(); } else { - throw newError( - `Connection with id ${connection.id} has no functions assigned to it's destination type - ${connection.type}` - ); + eventsLogger = DummyEventsStore; + } + if (process.env.REDIS_URL) { + redisClient = createRedis(); } - } - const functions = [...(connectionData.functions || []), mainFunction]; - //system context for builtin functions only - const systemContext: SystemContext = { - $system: { - anonymousEventsStore: mongoAnonymousEventsStore(), - }, - }; - - const funcChain: FuncChain = await Promise.all( - functions.map(async f => { - if (f.functionId.startsWith("builtin.")) { - return { - id: f.functionId as string, - config: f.functionOptions as any, - exec: requireDefined(getBuiltinFunction(f.functionId), `Unknown function ${f.functionId}`) as JitsuFunction, - context: systemContext, - }; - } else if (f.functionId.startsWith("udf.")) { - const functionId = f.functionId.substring(4); - const userFunctionObj = requireDefined( - await getCachedOrLoad(connectionsCache, functionId, key => { - return fastStore.getConfig("function", key); - }), - `Unknown function: ${functionId}` - ); - if (userFunctionObj.workspaceId !== connection.workspaceId) { - throw newError( - `Function ${functionId} is not in the same workspace as connection ${connection.id} (${connection.workspaceId})` - ); - } - const code = userFunctionObj.code; - const codeHash = hash(code); - let cached = await getCachedOrLoad(udfCache, functionId, async key => { - return { wrapper: UDFWrapper(key, userFunctionObj.name, code), hash: codeHash }; - }); - if (cached.hash !== codeHash) { - log.atInfo().log(`UDF ${functionId} changed (hash ${codeHash} != ${cached.hash}). Reloading`); - cached = { wrapper: UDFWrapper(functionId, userFunctionObj.name, code), hash: codeHash }; - udfCache.set(functionId, cached); - } - udfCache.ttl(functionId, udfTTL); - return { - id: f.functionId as string, - config: f.functionOptions as any, - exec: cached.wrapper.userFunction, - context: {}, - }; - } else { - throw newError(`Function of unknown type: ${f.functionId}`); - } - }) - ); - const event = message.httpPayload as AnalyticsServerEvent; - const ctx: EventContext = { - headers: message.httpHeaders, - source: { - id: connection.streamId, - domain: message.origin?.domain, - }, - destination: { - id: connection.destinationId, - type: connection.type, - updatedAt: connection.updatedAt, - hash: connection.credentialsHash, - }, - connection: { - id: connection.id, - mode: connection.mode, - options: connection.options, - }, - }; - const redisClient = redis(); - const redisLogger = createRedisLogger( - redisClient, - isErr => `events_log:functions.${isErr ? "error" : "all"}#${connection.id}`, - false - ); - const store: Store = { - get: async (key: string) => { - const res = await redisClient.hget(`store:${connection.id}`, key); - return res ? JSON.parse(res) : undefined; - }, - set: async (key: string, obj: any) => { - await redisClient.hset(`store:${connection.id}`, key, JSON.stringify(obj)); - }, - del: async (key: string) => { - await redisClient.hdel(`store:${connection.id}`, key); - }, - }; - await runChain(funcChain, event, connection, redisLogger, store, ctx); -} -const retrySettings = { - maxMinutesInQueue: process.env.KAFKA_MAX_MINUTES_IN_QUEUE ? parseInt(process.env.KAFKA_MAX_MINUTES_IN_QUEUE) : 120, -}; + const connStore = await connectionsStore.get(); + if (!connStore.enabled) { + log.atError().log("Connection store is not configured. Rotor will not work"); + process.exit(1); + } + const funcStore = await functionsStore.get(); + if (!funcStore.enabled) { + log.atError().log("Functions store is not configured. Rotor will not work"); + process.exit(1); + } -async function main() { - process.on("uncaughtException", function (err) { - // Handle the error safely - log.atError().withCause(err).log("UncaughtException"); - }); + geoResolver = await initMaxMindClient({ + licenseKey: process.env.MAXMIND_LICENSE_KEY, + url: process.env.MAXMIND_URL, + s3Bucket: process.env.MAXMIND_S3_BUCKET, + }); + metricsServer = initMetricsServer(); + } catch (e) { + log.atError().withCause(e).log("Failed to start"); + process.exit(1); + } - const args = minimist(process.argv.slice(2)); - if (args._?.[0] === "local") { - if (args["f"]) { - glob(args["f"], async (err, files) => { - if (err) { - log.atError().withCause(err).log(`Failed to read files ${args["f"]}`); - process.exit(1); - } - for (const file of files) { - const content = JSON.parse(fs.readFileSync(file, "utf8")); - const events = Array.isArray(content) ? content : [content]; - log.atInfo().log(`Reading file ${file}. Events: ${events.length}`); - for (const event of events) { - try { - await rotorMessageHandler(JSON.stringify(event)); - } catch (e) { - log - .atError() - .withCause(e) - .log(`Failed to process event from ${file}: ${JSON.stringify(event)}`); - } - } - } - }); - } else if (args["j"]) { - const content = JSON.parse(args["j"]); - const events = Array.isArray(content) ? content : [content]; - for (const event of events) { - try { - await rotorMessageHandler(JSON.stringify(event)); - } catch (e) { - log - .atError() - .withCause(e) - .log(`Failed to process event: ${JSON.stringify(event)}`); + const gracefulShutdown = async () => { + if (httpServer) { + httpServer.close(); + } + connectionsStore.stop(); + functionsStore.stop(); + eventsLogger.close(); + mongodb.close(); + if (redisClient) { + redisClient.disconnect(); + } + const extraDelay = process.env.SHUTDOWN_EXTRA_DELAY_SEC + ? 1000 * parseInt(process.env.SHUTDOWN_EXTRA_DELAY_SEC) + : 5000; + if (extraDelay > 0) { + log.atInfo().log(`Giving extra ${extraDelay / 1000}s. to flush logs and scrape metrics...`); + //extra time to flush logs + setTimeout(() => { + if (metricsServer) { + metricsServer.close(); } - } + process.exit(started ? 0 : 1); + }, extraDelay); } - } else if (args._?.[0] === "test-connection") { - const credentials = getCredentialsFromEnv(); - const kafka = connectToKafka({ ...credentials, defaultAppId: "connection-tester" }); - const producer = kafka.producer(); - await producer.connect(); - const topic = "connection-tester" + randomId(5); - const testMessage = { value: "test" }; - await producer.send({ topic, messages: [testMessage] }); - await producer.disconnect(); + }; - const consumer = kafka.consumer({ groupId: topic }); - await consumer.connect(); - await consumer.subscribe({ topic, fromBeginning: true }); - const message = (await new Promise(resolve => { - consumer.run({ - eachMessage: async ({ message }) => { - resolve(message); - }, - }); - })) as any; - log.atInfo().log(`Received message: ${message.value}`); - await consumer.disconnect(); - } else { - const kafkaTopic = destinationMessagesTopic(); + if (process.env.KAFKA_BOOTSTRAP_SERVERS && !isTruish(process.env.HTTP_ONLY)) { + //kafka consumer mode + const kafkaTopics = [destinationMessagesTopic()]; const consumerGroupId = rotorConsumerGroupId(); const rotor = kafkaRotor({ credentials: getCredentialsFromEnv(), - kafkaTopic, + kafkaTopics: kafkaTopics, consumerGroupId, - maxSecondsInQueueAfterFailure: retrySettings.maxMinutesInQueue * 60, + rotorContext: { geoResolver, eventsLogger, redisClient }, handle: rotorMessageHandler, }); log.atInfo().log("Starting kafka processing"); rotor .start() - .then(() => { - log.atInfo().log(`Kafka processing started. Listening for topic ${kafkaTopic} with group ${consumerGroupId}`); - http.get("/health", (req, res) => { - res.send("OK"); - }); - http.post("/udfrun", UDFRunHandler); - http.listen(rotorHttpPort, () => { - log.atInfo().log(`Listening health-checks on port ${rotorHttpPort}`); - }); + .then(chMetrics => { + log.atInfo().log(`Kafka processing started. Listening for topics ${kafkaTopics} with group ${consumerGroupId}`); + httpServer = initHTTP({ eventsLogger, metrics: chMetrics, geoResolver, redisClient }); }) - .catch(e => { - log.atError().withCause(e).log("Failed to start kafka processing"); + .catch(async e => { + log.atError().withCause(e).log("Failed to start rotor processing"); + await rotor.close(); process.exit(1); }); + + signalTraps.forEach(type => { + process.once(type, () => { + log.atInfo().log(`Signal ${type} received. Closing rotor`); + rotor.close().then(gracefulShutdown); + }); + }); + } else { + const metrics = createMetrics(); + httpServer = initHTTP({ eventsLogger, metrics: metrics, geoResolver, redisClient }); + signalTraps.forEach(type => { + process.once(type, () => { + gracefulShutdown(); + metrics.close(); + }); + }); + } +} + +function initHTTP(rotorContext: Omit) { + http.use((req, res, next) => { + if (req.path === "/health" || req.path === "/version") { + return next(); + } + let token = req.headers.authorization || ""; + if (token) { + if (token.startsWith("Bearer ")) { + token = token.substring("Bearer ".length); + } else { + res.status(401).json({ error: "Authorization header with Bearer token is required" }); + return; + } + } + if (!checkAuth(token)) { + if (token) { + res.status(401).json({ error: `Invalid token: ${token}` }); + } else { + res.status(401).json({ error: "Authorization header with Bearer token is required" }); + } + return; + } + next(); + }); + http.get("/version", (req, res) => { + res.json({ + ...getApplicationVersion(), + node: { + version: process.version, + platform: process.platform, + arch: process.arch, + env: process.env.NODE_ENV, + }, + diagnostics: isTruish(process.env.__DANGEROUS_ENABLE_FULL_DIAGNOSTICS) ? getDiagnostics() : undefined, + }); + }); + http.get("/health", async (req, res) => { + const mongoRequired = (process.env.REQUIRED_STORES ?? "").split(",").includes("mongodb"); + if (mongoRequired) { + try { + await pingMongo(); + } catch (e: any) { + log.atError().withCause(e).log("MongoDB is not healthy"); + res.status(500).json({ error: "MongoDB is not healthy" }); + return; + } + } + res.json({ + status: "pass", + connectionsStore: { + enabled: connectionsStore.getCurrent()?.enabled || "loading", + status: connectionsStore.status(), + lastUpdated: connectionsStore.lastRefresh(), + lastModified: connectionsStore.lastModified(), + }, + functionsStore: { + enabled: functionsStore.getCurrent()?.enabled || "loading", + status: functionsStore.status(), + lastUpdated: functionsStore.lastRefresh(), + lastModified: functionsStore.lastModified(), + }, + streamsStore: { + enabled: streamsStore.getCurrent()?.enabled || "loading", + status: streamsStore.status(), + lastUpdated: streamsStore.lastRefresh(), + lastModified: streamsStore.lastModified(), + }, + }); + }); + http.post("/udfrun", UDFRunHandler); + http.post("/profileudfrun", ProfileUDFRunHandler); + http.post("/func", FunctionsHandler(rotorContext)); + http.get("/wtf", async (req, res) => { + res.setHeader("Content-Type", "text/plain"); + res.write(util.inspect(process["_getActiveHandles"]())); + res.end(); + }); + http.get("/wtfheap", async (req, res) => { + const snapshot = getHeapSnapshot(); + log.atInfo().log("snapshot"); + snapshot.pipe(res); + log.atInfo().log("snapshot2"); + }); + http.post("/func/multi", FunctionsHandlerMulti(rotorContext)); + const httpServer = http.listen(rotorHttpPort, () => { + log.atInfo().log(`Listening on port ${rotorHttpPort}`); + started = true; + }); + httpServer.on("error", e => { + log.atError().withCause(e).log("Failed to start http server. Exiting..."); + process.kill(process.pid, "SIGTERM"); + }); + return httpServer; +} + +function initMetricsServer() { + metricsHttp.get("/metrics", async (req, res) => { + res.writeHead(200, { "Content-Type": Prometheus.register.contentType }); + const result = await Prometheus.register.metrics(); + res.end(result); + }); + const metricsServer = metricsHttp.listen(parseInt(rotorMetricsPort + ""), () => { + log.atInfo().log(`Listening metrics on port ${rotorMetricsPort}`); + }); + metricsServer.on("error", e => { + log.atError().withCause(e).log("Failed to start metrics server"); + }); + return metricsServer; +} + +async function pingMongo() { + await mongodb.waitInit().then(c => c.connect().then(c => c.db().admin().ping())); +} + +function checkAuth(token: string): boolean { + let tokens: string[] = []; + let checkFunction: (token: string, secret: string) => boolean = () => false; + if (process.env.ROTOR_AUTH_TOKENS) { + tokens = process.env.ROTOR_AUTH_TOKENS.split(","); + checkFunction = checkHash; + } else if (process.env.ROTOR_RAW_AUTH_TOKENS) { + tokens = process.env.ROTOR_RAW_AUTH_TOKENS.split(","); + checkFunction = checkRawToken; + } else { + log.atWarn().log("No auth tokens are configured. Rotor is open for everyone."); + return true; + } + if (tokens.length > 0) { + for (const tokenHashOrPlain of tokens) { + if (checkFunction(tokenHashOrPlain, token)) { + return true; + } + } } + return false; } main(); diff --git a/services/rotor/src/lib/data-layout/jitsu-legacy.ts b/services/rotor/src/lib/data-layout/jitsu-legacy.ts deleted file mode 100644 index e69de29bb..000000000 diff --git a/services/rotor/src/lib/functions-chain.ts b/services/rotor/src/lib/functions-chain.ts index 97275bd9d..c10b494fa 100644 --- a/services/rotor/src/lib/functions-chain.ts +++ b/services/rotor/src/lib/functions-chain.ts @@ -1,101 +1,369 @@ +import { AnonymousEventsStore, AnyEvent, EventContext, FuncReturn, TTLStore } from "@jitsu/protocols/functions"; import { - AnyEvent, - EventContext, - EventsStore, - FuncReturn, - JitsuFunction, - Store, - SystemContext, -} from "@jitsu/protocols/functions"; -import { createFullContext } from "@jitsu/core-functions"; - -import Redis from "ioredis"; -import { getErrorMessage, getLog, stopwatch } from "juava"; -import { EnrichedConnectionConfig } from "@jitsu-internal/console/lib/server/fast-store"; - -const eventsLogSize = process.env.EVENTS_LOG_MAX_SIZE ? parseInt(process.env.EVENTS_LOG_MAX_SIZE) : 1000; + createDummyStore, + createMongoStore, + createMultiStore, + createRedisStore, + EnrichedConnectionConfig, + EntityStore, + FuncChainResult, + FunctionChainContext, + FunctionConfig, + FunctionContext, + FunctionExecLog, + FunctionExecRes, + getBuiltinFunction, + isDropResult, + JitsuFunctionWrapper, + makeFetch, + makeLog, + MetricsMeta, + mongodb, + UDFWrapper, + UserRecognitionParameter, + warehouseQuery, + wrapperFunction, +} from "@jitsu/core-functions"; +import { DropRetryErrorName, RetryErrorName } from "@jitsu/functions-lib"; + +import { getLog, newError, requireDefined, stopwatch } from "juava"; +import { retryObject } from "./retries"; +import NodeCache from "node-cache"; +import isEqual from "lodash/isEqual"; +import { MessageHandlerContext } from "./message-handler"; +import { promFunctionsInFlight, promFunctionsTime } from "./metrics"; + +const fastStoreWorkspaceId = (process.env.FAST_STORE_WORKSPACE_ID ?? "").split(",").filter(x => x.length > 0); export type Func = { id: string; - exec: JitsuFunction; - config: any; - context: SystemContext | {}; + exec: JitsuFunctionWrapper; + context: FunctionContext; + hash?: string; +}; + +export type FuncChain = { + context: FunctionChainContext; + functions: Func[]; }; -export type FuncChain = Func[]; +export type FuncChainFilter = "all" | "udf-n-dst" | "dst-only"; const log = getLog("functions-chain"); +const bulkerBase = requireDefined(process.env.BULKER_URL, "env BULKER_URL is not defined"); +const bulkerAuthKey = requireDefined(process.env.BULKER_AUTH_KEY, "env BULKER_AUTH_KEY is not defined"); + +//cache compiled udfs for 5min +const udfTTL = 60 * 10; +const udfCache = new NodeCache({ stdTTL: udfTTL, checkperiod: 60, useClones: false }); +udfCache.on("del", (key, value) => { + log.atDebug().log(`UDF ${key} deleted from cache`); + value.wrapper?.close(); +}); + +export function checkError(chainRes: FuncChainResult) { + for (const el of chainRes.execLog) { + if (el.error && (el.error.name === DropRetryErrorName || el.error.name === RetryErrorName)) { + // throw retry errors above to schedule retry + const err = el.error; + err.event = el.event; + err.functionId = err.functionId || el.functionId; + throw err; + } + } +} + +export function buildFunctionChain( + connection: EnrichedConnectionConfig, + connStore: EntityStore, + funcStore: EntityStore, + rotorContext: MessageHandlerContext, + anonymousEventsStore: AnonymousEventsStore, + fetchTimeoutMs: number = 2000 +): FuncChain { + let mainFunction; + const connectionData = connection.options as any; + const conId = connection.id; + const conWorkspaceId = connection.workspaceId; + if (connection.usesBulker) { + mainFunction = { + functionId: "builtin.destination.bulker", + functionOptions: { + bulkerEndpoint: bulkerBase, + destinationId: conId, + authToken: bulkerAuthKey, + dataLayout: connectionData.dataLayout ?? "segment-single-table", + keepOriginalNames: connectionData.keepOriginalNames, + }, + }; + } else { + const builtin = getBuiltinFunction(`builtin.destination.${connection.type}`); + if (builtin) { + mainFunction = { + functionId: `builtin.destination.${connection.type}`, + functionOptions: connection.credentials, + }; + } else { + throw newError( + `Connection with id ${conId} has no functions assigned to it's destination type - ${connection.type}` + ); + } + } + let store: TTLStore | undefined = rotorContext.dummyPersistentStore; + if (!store) { + let mongodbStore: TTLStore | undefined, redisStore: TTLStore | undefined; + + if (process.env.MONGODB_URL) { + mongodbStore = createMongoStore( + conWorkspaceId, + mongodb, + false, + fastStoreWorkspaceId.includes(conWorkspaceId), + rotorContext.metrics + ); + } + + if (rotorContext.redisClient) { + redisStore = createRedisStore(conWorkspaceId, rotorContext.redisClient, rotorContext.metrics); + } + + if (mongodbStore && redisStore) { + store = createMultiStore(mongodbStore, redisStore); + } else if (mongodbStore) { + store = mongodbStore; + } else if (redisStore) { + store = redisStore; + } else { + store = createDummyStore(); + log.atWarn().log(`No persistence storage configured. MONGODB_URL or REDIS_URL environment variable is required`); + } + } + + const chainCtx: FunctionChainContext = { + fetch: makeFetch(conId, rotorContext.eventsLogger, connectionData.fetchLogLevel || "info", fetchTimeoutMs), + log: makeLog(conId, rotorContext.eventsLogger), + store, + query: async (conId: string, query: string, params: any) => { + return warehouseQuery(conWorkspaceId, connStore, conId, query, params, rotorContext.metrics); + }, + anonymousEventsStore, + connectionOptions: connectionData, + }; + const udfFuncCtx = { + function: { + id: "PIPELINE", + type: "udf", + debugTill: connectionData.debugTill ? new Date(connectionData.debugTill) : undefined, + }, + props: connectionData.functionsEnv || {}, + }; + const udfFuncs: FunctionConfig[] = (connectionData?.functions || []) + .filter(f => f.functionId.startsWith("udf.")) + .map(f => { + const functionId = f.functionId.substring(4); + const userFunctionObj = funcStore.getObject(functionId); + if (!userFunctionObj || userFunctionObj.workspaceId !== conWorkspaceId) { + return { + id: functionId as string, + code: `export default async function (event,ctx) { + throw newError(\`Function ${functionId} not found in workspace: ${conWorkspaceId}\`); + }`, + codeHash: "0", + }; + } + return userFunctionObj; + }); + let cached: any; + let hash: any[]; + if (udfFuncs.length > 0) { + hash = udfFuncs.map(f => f.codeHash); + hash.push(connection.updatedAt); + cached = udfCache.get(conId); + if (!cached || !isEqual(cached?.hash, hash)) { + log.atInfo().log(`UDF for connection ${conId} changed (hash ${hash} != ${cached?.hash}). Reloading`); + const wrapper = UDFWrapper( + conId, + chainCtx, + udfFuncCtx, + udfFuncs.map(f => ({ id: f.id, name: f.name, code: f.code })) + ); + const oldWrapper = cached?.wrapper; + if (oldWrapper) { + setTimeout(() => { + oldWrapper.close(); + }, 10000); + } + cached = { wrapper, hash }; + udfCache.set(conId, cached); + } + udfCache.ttl(conId, udfTTL); + } + const aggregatedFunctions: any[] = [ + ...(connectionData.functions || []).filter(f => f.functionId.startsWith("builtin.transformation.")), + ...(udfFuncs.length > 0 ? [{ functionId: "udf.PIPELINE" }] : []), + mainFunction, + ]; + + const udfPipelineFunc = (chainCtx: FunctionChainContext): JitsuFunctionWrapper => { + return async (event: AnyEvent, ctx: EventContext) => { + try { + return await cached.wrapper.userFunction(event, ctx); + } catch (e: any) { + if ((e?.message ?? "").includes("Isolate is disposed")) { + // due to async nature other 'thread' could already replace this isolate. So check it + if (cached.wrapper.isDisposed()) { + log.atError().log(`UDF for con:${conId} VM was disposed. Reloading`); + const wrapper = UDFWrapper( + conId, + chainCtx, + udfFuncCtx, + udfFuncs.map(f => ({ id: f.id, name: f.name, code: f.code })) + ); + cached = { wrapper, hash }; + udfCache.set(conId, cached); + return wrapper.userFunction(event, ctx); + } else { + // we have alive isolate now. try again + return await cached.wrapper.userFunction(event, ctx); + } + } else { + throw e; + } + } + }; + }; -export type FunctionExecLog = { - eventIndex: number; - functionId: string; - error?: string; - ms: number; -}[]; + const funcs: Func[] = aggregatedFunctions.map(f => { + const ar = f.functionId.split("."); + const id = ar.pop(); + const type = ar.join("."); + const funcCtx: FunctionContext = { + function: { + id, + type, + debugTill: connectionData.debugTill ? new Date(connectionData.debugTill) : undefined, + }, + props: f.functionOptions || {}, + }; + if (f.functionId.startsWith("builtin.")) { + return { + id: f.functionId as string, + context: funcCtx, + exec: wrapperFunction( + chainCtx, + funcCtx, + requireDefined(getBuiltinFunction(f.functionId), `Unknown function ${f.functionId}`) + ), + } as Func; + } else if (f.functionId === "udf.PIPELINE") { + return { + id: f.functionId as string, + context: funcCtx, + exec: udfPipelineFunc(chainCtx), + }; + } else { + throw newError(`Function of unknown type: ${f.functionId}`); + } + }); + + return { + functions: funcs, + context: chainCtx, + }; +} export async function runChain( chain: FuncChain, event: AnyEvent, - connection: EnrichedConnectionConfig, - eventsStore: EventsStore, - store: Store, - eventContext: EventContext -): Promise { + eventContext: EventContext, + metricsMeta: MetricsMeta, + runFuncs: FuncChainFilter = "all", + retriesEnabled: boolean = true +): Promise { const execLog: FunctionExecLog = []; let events = [event]; - for (const f of chain) { + for (const f of chain.functions) { + switch (runFuncs) { + case "udf-n-dst": + if (f.id !== "udf.PIPELINE" && !f.id.startsWith("builtin.destination.")) { + continue; + } + break; + case "dst-only": + if (!f.id.startsWith("builtin.destination.")) { + continue; + } + break; + } + const metricsLabels = { connectionId: eventContext.connection?.id ?? "", functionId: f.id }; const newEvents: AnyEvent[] = []; for (let i = 0; i < events.length; i++) { + promFunctionsInFlight.inc(metricsLabels); const event = events[i]; - let result: FuncReturn; + let result: FuncReturn = undefined; const sw = stopwatch(); - const funcCtx = createFullContext(f.id, eventsStore, store, eventContext, f.context, f.config); + const execLogEvent: Partial = { + // we don't multiply active incoming metrics for events produced by user recognition + eventIndex: event[UserRecognitionParameter] ? 0 : i, + receivedAt: !isNaN(eventContext.receivedAt.getTime()) ? eventContext.receivedAt : new Date(), + functionId: f.id, + metricsMeta: metricsMeta, + }; try { - result = await f.exec(event, funcCtx); - } catch (err) { - funcCtx.log.error(`Function execution failed`, err); - newEvents.push(event); - execLog.push({ - eventIndex: i, - functionId: f.id, - error: getErrorMessage(err), - ms: sw.elapsedMs(), - }); - continue; + result = await f.exec(event, eventContext); + } catch (err: any) { + if (err.name === DropRetryErrorName) { + result = "drop"; + } + execLogEvent.event = event; + execLogEvent.error = err; + const args = [err?.name, err?.message]; + const r = retriesEnabled ? retryObject(err, eventContext.retries ?? 0) : undefined; + if (r) { + args.push(r); + } + if (r?.retry?.left ?? 0 > 0) { + chain.context.log.warn(f.context, `Function execution failed`, ...args); + } else { + chain.context.log.error(f.context, `Function execution failed`, ...args); + } + if (f.id === "udf.PIPELINE") { + if (err.name !== DropRetryErrorName) { + const errEvent = err.event || event; + // if udf pipeline failed w/o drop error pass partial result of pipeline to the destination function + if (Array.isArray(errEvent)) { + newEvents.push(...errEvent); + } else { + newEvents.push(errEvent); + } + continue; + } + } + } finally { + const ms = sw.elapsedMs(); + promFunctionsTime.observe(metricsLabels, ms); + execLogEvent.ms = ms; + execLogEvent.dropped = isDropResult(result); + execLog.push(execLogEvent as FunctionExecRes); + promFunctionsInFlight.dec(metricsLabels); } - execLog.push({ - eventIndex: i, - functionId: f.id, - ms: sw.elapsedMs(), - }); - if (result === "drop") { - return execLog; - } else if (result) { - newEvents.push(...(Array.isArray(result) ? result : [result])); - } else { - newEvents.push(event); + if (!execLogEvent.dropped) { + if (result) { + if (Array.isArray(result)) { + newEvents.push(...result); + } else { + // @ts-ignore + newEvents.push(result); + } + } else { + newEvents.push(event); + } } } events = newEvents; + if (events.length === 0) { + break; + } } - return execLog; -} - -export function createRedisLogger(redis: Redis, key: (err: boolean) => string, storeDebug): EventsStore { - return { - log: async (error, msg) => { - try { - if (msg.type === "log-debug" && !storeDebug) { - return; - } - const logEntry = JSON.stringify({ timestamp: new Date().toISOString(), error, ...msg }); - if (error) { - await redis.xadd(key(true), "MAXLEN", "~", eventsLogSize, "*", "event", logEntry); - } - await redis.xadd(key(false), "MAXLEN", "~", eventsLogSize, "*", "event", logEntry); - } catch (e) { - log.atError().withCause(e).log("Failed to put event to redis events log"); - } - }, - }; + return { events, execLog }; } diff --git a/services/rotor/src/lib/kafka-config.ts b/services/rotor/src/lib/kafka-config.ts new file mode 100644 index 000000000..4dae6387f --- /dev/null +++ b/services/rotor/src/lib/kafka-config.ts @@ -0,0 +1,101 @@ +import { KafkaJS, GlobalConfig } from "@confluentinc/kafka-javascript"; + +import { isTruish, LogMessageBuilder, requireDefined, randomId, getLog } from "juava"; +import JSON5 from "json5"; +const log = getLog("kafka"); + +function translateLevel(l: KafkaJS.logLevel): LogMessageBuilder { + switch (l) { + case KafkaJS.logLevel.ERROR: + return log.atError(); + case KafkaJS.logLevel.WARN: + return log.atWarn(); + case KafkaJS.logLevel.INFO: + return log.atDebug(); + case KafkaJS.logLevel.DEBUG: + return log.atDebug(); + default: + return log.atInfo(); + } +} + +export type KafkaCredentials = { + brokers: KafkaJS.KafkaConfig["brokers"]; + ssl?: GlobalConfig; + sasl?: KafkaJS.KafkaConfig["sasl"]; +}; + +export function getCredentialsFromEnv(): KafkaCredentials { + const ssl = isTruish(process.env.KAFKA_SSL); + const sslSkipVerify = isTruish(process.env.KAFKA_SSL_SKIP_VERIFY); + let sslOption: KafkaCredentials["ssl"] = undefined; + + if (ssl) { + sslOption = { + "security.protocol": process.env.KAFKA_SASL ? "sasl_ssl" : "ssl", + }; + if (sslSkipVerify) { + // TLS enabled, but server TLS certificate is not verified + sslOption["ssl.endpoint.identification.algorithm"] = "none"; + sslOption["enable.ssl.certificate.verification"] = false; + } else if (process.env.KAFKA_SSL_CA) { + // TLS enabled, server TLS certificate is verified using a custom CA certificate + sslOption["ssl.ca.pem"] = process.env.KAFKA_SSL_CA; + } else if (process.env.KAFKA_SSL_CA_FILE) { + // TLS enabled, server TLS certificate is verified using a custom CA certificate (loaded from a local file) + sslOption["ssl.ca.location"] = process.env.KAFKA_SSL_CA_FILE; + } + } + + return { + brokers: requireDefined(process.env.KAFKA_BOOTSTRAP_SERVERS, "env KAFKA_BOOTSTRAP_SERVERS is required").split(","), + ssl: sslOption, + sasl: process.env.KAFKA_SASL ? JSON5.parse(process.env.KAFKA_SASL) : undefined, + }; +} + +export function connectToKafka(opts: { defaultAppId: string } & KafkaCredentials): KafkaJS.Kafka { + const sasl = opts.sasl + ? { + sasl: opts.sasl as any, + } + : {}; + log.atDebug().log("SASL config", JSON.stringify(opts.sasl)); + return new KafkaJS.Kafka({ + kafkaJS: { + logLevel: KafkaJS.logLevel.ERROR, + // logCreator: logLevel => log => { + // translateLevel(logLevel).log( + // `${log.namespace ? `${log.namespace} # ` : ""}${JSON.stringify(omit(log.log, "timestamp", "logger"))}` + // ); + // }, + clientId: process.env.APPLICATION_ID || opts.defaultAppId, + brokers: typeof opts.brokers === "string" ? (opts.brokers as string).split(",") : opts.brokers, + ...(opts.ssl ? { ssl: true } : {}), + ...sasl, + }, + ...opts.ssl, + }); +} + +export function destinationMessagesTopic(): string { + return process.env.KAFKA_DESTINATIONS_TOPIC_NAME || "destination-messages"; +} + +export function deatLetterTopic(): string { + return process.env.KAFKA_DESTINATIONS_DEAD_LETTER_TOPIC_NAME || "destination-messages-dead-letter"; +} + +export function retryTopic(): string { + return process.env.KAFKA_DESTINATIONS_RETRY_TOPIC_NAME || "destination-messages-retry"; +} + +export function destinationMessagesTopicMultiThreaded(): string { + return process.env.KAFKA_DESTINATIONS_MT_TOPIC_NAME || "destination-messages-mt"; +} + +export function rotorConsumerGroupId(): string { + return process.env.KAFKA_CONSUMER_GROUP_ID !== undefined + ? process.env.KAFKA_CONSUMER_GROUP_ID.replace("$random", randomId(5)) + : "rotor"; +} diff --git a/services/rotor/src/lib/maxmind.ts b/services/rotor/src/lib/maxmind.ts new file mode 100644 index 000000000..cbc4e8780 --- /dev/null +++ b/services/rotor/src/lib/maxmind.ts @@ -0,0 +1,361 @@ +import { Reader, ReaderModel, City, Isp, Names } from "@maxmind/geoip2-node"; +import * as zlib from "zlib"; +import * as tar from "tar"; +import { Geo } from "@jitsu/protocols/analytics"; +import NodeCache from "node-cache"; +import { getLog, isTruish, requireDefined } from "juava"; +import { S3Client, GetObjectCommand } from "@aws-sdk/client-s3"; + +const log = getLog("maxmind"); + +const InvalidLicenseKey = "Invalid license key"; + +type PaidEdition = "GeoIP2-City" | "GeoIP2-Country" | "GeoIP2-ISP" | "GeoIP2-Domain" | "GeoIP2-Connection-Type"; +type FreeEditions = "GeoLite2-City" | "GeoLite2-Country" | "GeoLite2-ASN"; +type Edition = PaidEdition | FreeEditions | "NotRequired" | ""; + +type LoadFunction = (edition: Edition) => Promise; + +type GetLocalizedNameFunction = (names: Names) => string; + +const composeURL = (licenseKeyOrURL: string, edition: Edition) => { + if (licenseKeyOrURL.startsWith("http")) { + return licenseKeyOrURL + (licenseKeyOrURL.endsWith("/") ? "" : "/") + edition + ".tar.gz"; + } else { + return `https://download.maxmind.com/app/geoip_download?license_key=${licenseKeyOrURL}&edition_id=${edition}&suffix=tar.gz`; + } +}; + +const geoIpCache = new NodeCache({ stdTTL: 60 * 5, checkperiod: 60, useClones: false }); + +export interface GeoResolver { + resolve(ip: string): Promise; +} + +const DummyResolver: GeoResolver = { + resolve: async (ip: string) => { + return {}; + }, +}; + +const createS3Client = () => { + const region = requireDefined( + process.env.MAXMIND_S3_REGION ?? process.env.S3_REGION, + "MAXMIND_S3_REGION or S3_REGION is not provided" + ); + const accessKeyId = requireDefined( + process.env.MAXMIND_S3_ACCESS_KEY_ID ?? process.env.S3_ACCESS_KEY_ID, + "MAXMIND_S3_ACCESS_KEY_ID or S3_ACCESS_KEY_ID is not provided" + ); + const secretAccessKey = requireDefined( + process.env.MAXMIND_S3_SECRET_ACCESS_KEY ?? process.env.S3_SECRET_ACCESS_KEY, + "MAXMIND_S3_SECRET_ACCESS_KEY or S3_SECRET_ACCESS_KEY is not provided" + ); + + const endpoint = process.env.MAXMIND_S3_ENDPOINT; // Optional: AWS S3 will be used if not provided + const forcePathStyle = isTruish(process.env.MAXMIND_S3_FORCE_PATH_STYLE); // Optional: virtual-hosted style is used by default + + return new S3Client({ + credentials: { + accessKeyId, + secretAccessKey, + }, + endpoint, + forcePathStyle, + region, + }); +}; + +async function test() { + const maxMindClient = await initMaxMindClient({ + licenseKey: process.env.MAXMIND_LICENSE_KEY || "", + }); + + log.atInfo().log(`IP 209.142.68.29:`, JSON.stringify(await maxMindClient.resolve("209.142.68.29"), null, 2)); +} + +export async function initMaxMindClient(opts: { + licenseKey?: string; + url?: string; + s3Bucket?: string; +}): Promise { + const { licenseKey, s3Bucket, url } = opts; + if (!licenseKey && !url && !s3Bucket) { + log.atError().log("licenseKey, url or s3Bucket must be provided. GeoIP resolution will not work."); + return DummyResolver; + } + let loadFunc: LoadFunction; + let s3client: S3Client = undefined as any as S3Client; + if (s3Bucket) { + s3client = createS3Client(); + loadFunc = (edition: Edition) => loadFromS3(s3client, s3Bucket, edition); + } else { + loadFunc = (edition: Edition) => loadFromURL(composeURL(licenseKey || url || "", edition)); + } + let getLocalizedName: GetLocalizedNameFunction; + if (process.env.MAXMIND_LOCALE) { + getLocalizedName = (names: Names) => { + return names[process.env.MAXMIND_LOCALE!] || names.en; + }; + } else { + getLocalizedName = (names: Names) => names.en; + } + + let cityReader: ReaderModel | undefined; + let countryReader: ReaderModel | undefined; + let ispReader: ReaderModel | undefined; + let asnReader: ReaderModel | undefined; + let domainReader: ReaderModel | undefined; + let connectionTypeReader: ReaderModel | undefined; + + const cityDb = await download(loadFunc, "GeoIP2-City"); + if (cityDb.reader) { + cityReader = cityDb.reader; + } + const countryDb = await download(loadFunc, "GeoIP2-Country"); + if (countryDb.reader) { + countryReader = countryDb.reader; + } + + const ispDb = await download(loadFunc, "GeoIP2-ISP"); + if (ispDb.reader) { + if (ispDb.edition === "GeoIP2-ISP") { + ispReader = ispDb.reader; + } else if (ispDb.edition === "GeoLite2-ASN") { + asnReader = ispDb.reader; + } + } + const domainDb = await download(loadFunc, "GeoIP2-Domain"); + if (domainDb.reader) { + domainReader = domainDb.reader; + } + const contTypeDb = await download(loadFunc, "GeoIP2-Connection-Type"); + if (contTypeDb.reader) { + connectionTypeReader = contTypeDb.reader; + } + + if (s3client) { + s3client.destroy(); + } + + if (!cityReader && !countryReader && !ispReader && !asnReader && !domainReader && !connectionTypeReader) { + log.atError().log("Failed to load MaxMind databases. GeoIP resolution will not work."); + return DummyResolver; + } else { + return { + resolve: async (ip: string) => { + try { + if (!ip) { + return {}; + } + const cached = geoIpCache.get(ip); + if (cached) { + geoIpCache.ttl(ip); + return cached as Geo; + } + const geo = ( + cityReader ? cityReader.city(ip) : countryReader ? countryReader.country(ip) : undefined + ) as City; + const isp = (ispReader ? ispReader.isp(ip) : asnReader ? asnReader.asn(ip) : undefined) as Isp; + const domain = domainReader ? domainReader.domain(ip) : undefined; + const connectionType = connectionTypeReader ? connectionTypeReader.connectionType(ip) : undefined; + if (!geo && !isp && !domain && !connectionType) { + geoIpCache.set(ip, {}); + return {}; + } + let geoPart: Geo = geo + ? { + continent: geo.continent + ? { + code: geo.continent.code, + name: getLocalizedName(geo.continent.names), + } + : undefined, + country: geo.country + ? { + code: geo.country.isoCode, + name: getLocalizedName(geo.country.names), + isEU: !!geo.country.isInEuropeanUnion, + } + : undefined, + region: geo.subdivisions?.length + ? { + code: geo.subdivisions[0].isoCode, + confidence: geo.subdivisions[0].confidence, + name: getLocalizedName(geo.subdivisions[0].names), + } + : undefined, + city: geo.city + ? { + confidence: geo.city.confidence, + name: getLocalizedName(geo.city.names), + } + : undefined, + postalCode: geo.postal + ? { + code: geo.postal.code, + } + : undefined, + location: geo.location + ? { + latitude: geo.location.latitude, + longitude: geo.location.longitude, + timezone: geo.location.timeZone, + accuracyRadius: geo.location.accuracyRadius, + ...(geo?.country?.isoCode === "US" + ? { + usaData: { + populationDensity: geo.location.populationDensity, + metroCode: geo.location.metroCode, + averageIncome: geo.location.averageIncome, + }, + } + : {}), + } + : undefined, + } + : {}; + let ispPart: Geo = + geo || isp || domain || connectionType + ? { + provider: { + ...(isp + ? { + as: { + ...(isp?.autonomousSystemNumber ? { num: isp.autonomousSystemNumber } : {}), + ...(isp?.autonomousSystemOrganization ? { name: isp.autonomousSystemOrganization } : {}), + }, + isp: isp?.isp, + } + : {}), + connectionType: connectionType?.connectionType, + domain: domain?.domain, + ...(geo.traits + ? { + // isAnonymousVpn: geo.traits.isAnonymousVpn, + // isHostingProvider: geo.traits.isHostingProvider, + // isLegitimateProxy: geo.traits.isLegitimateProxy, + // isPublicProxy: geo.traits.isPublicProxy, + // isResidentialProxy: geo.traits.isResidentialProxy, + // isTorExitNode: geo.traits.isTorExitNode, + // userType: geo.traits.userType, + } + : {}), + }, + } + : {}; + const finalGeo: Geo = { + ...geoPart, + ...ispPart, + }; + geoIpCache.set(ip, finalGeo); + return finalGeo; + } catch (e: any) { + log.atDebug().log(`Failed to resolve geo for ${ip}: ${e.message}`); + geoIpCache.set(ip, {}); + return {}; + } + }, + }; + } +} + +async function loadFromS3(client: S3Client, bucket: string, edition: Edition): Promise { + try { + const command = new GetObjectCommand({ Bucket: bucket, Key: edition + ".tar.gz" }); + const response = await client.send(command); + if (response.Body) { + return await untar(Buffer.from(await response.Body.transformToByteArray())); + } else { + throw new Error(`no response body`); + } + } catch (e: any) { + throw new Error(`Failed to download ${edition} edition from S3 bucket: ${bucket}: ${e.message}`); + } +} + +async function download( + loadFunction: LoadFunction, + edition: Edition +): Promise<{ reader?: ReaderModel; edition: Edition }> { + try { + const b = await loadFunction(edition); + const reader = Reader.openBuffer(b); + log.atInfo().log(`Successfully downloaded ${edition} edition`); + return { reader, edition }; + } catch (e: any) { + const freeEdition = freeAnalog(edition as PaidEdition); + if (!freeEdition) { + throw e; + } + if (freeEdition === "NotRequired") { + log.atWarn().log(`Failed to download optional ${edition} edition: ${e.message}`); + return { edition: edition }; + } + log + .atError() + .log(`Failed to download ${edition} edition: ${e.message}. Trying to download free ${freeEdition} edition`); + try { + const b = await loadFunction(freeEdition); + const reader = Reader.openBuffer(b); + log.atInfo().log(`Successfully downloaded free ${freeEdition} edition`); + return { reader, edition: freeEdition }; + } catch (e: any) { + log.atError().log(`Failed to download ${freeEdition} edition: ${e.message}`); + return { edition: freeEdition }; + } + } +} + +async function loadFromURL(url: string): Promise { + const res = await fetch(url); + if (res.ok) { + return await untar(Buffer.from(await res.arrayBuffer())); + } else { + if (res.status === 401 || res.status === 403) { + throw new Error(InvalidLicenseKey); + } + throw new Error(`Failed to download ${url}: ${res.status} ${res.statusText} response: ${await res.text()}`); + } +} + +async function untar(b: Buffer): Promise { + return new Promise((resolve, reject) => { + const gunzip = zlib.createGunzip(); + const tarParser = new tar.Parser(); + tarParser.on("entry", entry => { + if (entry.type === "File" && entry.path.endsWith(".mmdb")) { + const chunks: Buffer[] = []; + entry.on("data", chunk => chunks.push(chunk)); + entry.on("end", () => { + resolve(Buffer.concat(chunks)); + }); + } else { + entry.resume(); + } + }); + tarParser.on("error", err => { + reject(err); + }); + gunzip.pipe(tarParser); + gunzip.write(b); + gunzip.end(); + }); +} + +function freeAnalog(edition: PaidEdition): Edition { + switch (edition) { + case "GeoIP2-City": + return "GeoLite2-City"; + case "GeoIP2-Country": + return "GeoLite2-Country"; + case "GeoIP2-ISP": + return "GeoLite2-ASN"; + case "GeoIP2-Domain": + return "NotRequired"; + case "GeoIP2-Connection-Type": + return "NotRequired"; + default: + return ""; + } +} diff --git a/services/rotor/src/lib/message-handler.ts b/services/rotor/src/lib/message-handler.ts new file mode 100644 index 000000000..e1007f27f --- /dev/null +++ b/services/rotor/src/lib/message-handler.ts @@ -0,0 +1,171 @@ +import { getLog, requireDefined } from "juava"; +import { GeoResolver } from "./maxmind"; +import { IngestMessage } from "@jitsu/protocols/async-request"; +import { CONNECTION_IDS_HEADER } from "./rotor"; +import { AnalyticsServerEvent } from "@jitsu/protocols/analytics"; +import { EventContext, TTLStore } from "@jitsu/protocols/functions"; +import { + MetricsMeta, + mongoAnonymousEventsStore, + parseUserAgent, + EventsStore, + EntityStore, + EnrichedConnectionConfig, + FunctionConfig, + RotorMetrics, + StreamWithDestinations, +} from "@jitsu/core-functions"; +import NodeCache from "node-cache"; +import { buildFunctionChain, checkError, FuncChain, FuncChainFilter, runChain } from "./functions-chain"; +import { Redis } from "ioredis"; +import { fromJitsuClassic } from "@jitsu/functions-lib"; +const log = getLog("rotor"); + +const anonymousEventsStore = mongoAnonymousEventsStore(); + +//cache function chains for 1m +const funcsChainTTL = 60; +const funcsChainCache = new NodeCache({ stdTTL: funcsChainTTL, checkperiod: 60, useClones: false }); + +export type MessageHandlerContext = { + connectionStore: EntityStore; + functionsStore: EntityStore; + streamsStore: EntityStore; + eventsLogger: EventsStore; + metrics?: RotorMetrics; + geoResolver?: GeoResolver; + dummyPersistentStore?: TTLStore; + redisClient?: Redis; +}; + +export function functionFilter(errorFunctionId?: string) { + let runFuncs: FuncChainFilter = "all"; + const fid = errorFunctionId || ""; + if (fid.startsWith("udf.")) { + runFuncs = "udf-n-dst"; + } else if (fid.startsWith("builtin.destination.")) { + runFuncs = "dst-only"; + } + return runFuncs; +} + +export async function rotorMessageHandler( + _message: string | object | undefined, + rotorContext: MessageHandlerContext, + runFuncs: FuncChainFilter = "all", + headers?, + retriesEnabled: boolean = true, + retries: number = 0, + fetchTimeoutMs: number = 2000 +) { + if (!_message) { + return; + } + const connStore = rotorContext.connectionStore; + const funcStore = rotorContext.functionsStore; + const streamsStore = rotorContext.streamsStore; + + const message = (typeof _message === "string" ? JSON.parse(_message) : _message) as IngestMessage; + const connectionId = + headers && headers[CONNECTION_IDS_HEADER] ? headers[CONNECTION_IDS_HEADER].toString() : message.connectionId; + const connection = requireDefined(connStore.getObject(connectionId), `Unknown connection: ${connectionId}`); + + log.inDebug(l => + l.log( + `Processing ${message.type} Message ID: ${message.messageId} for: ${connection.id} (${connection.streamId} → ${connection.destinationId}(${connection.type}))` + ) + ); + + const event = ( + message.origin?.classic && retries === 0 ? fromJitsuClassic(message.httpPayload) : message.httpPayload + ) as AnalyticsServerEvent; + + if (!event.context) { + event.context = {}; + } + const geo = + Object.keys(event.context.geo || {}).length > 0 + ? event.context.geo + : rotorContext.geoResolver && event.context.ip + ? await rotorContext.geoResolver.resolve(event.context.ip) + : undefined; + if (geo) { + event.context.geo = geo; + } + const ctx: EventContext = { + receivedAt: new Date(message.messageCreated), + headers: message.httpHeaders, + geo: geo, + ua: parseUserAgent(event.context.userAgent), + retries, + source: { + type: message.ingestType, + id: message.origin?.sourceId || connection.streamId, + name: message.origin?.sourceName || connection.streamName, + domain: message.origin?.domain, + }, + destination: { + id: connection.destinationId, + type: connection.type, + updatedAt: connection.updatedAt, + hash: connection.credentialsHash, + }, + connection: { + id: connection.id, + options: connection.options, + }, + workspace: { + id: connection.workspaceId, + }, + }; + if (connection.type === "profiles") { + ctx.allConnections = streamsStore.getObject(ctx.source.id)?.destinations?.map(d => ({ + id: d.connectionId, + destinationId: d.id, + destinationName: d.name, + type: d.destinationType, + mode: d.options?.mode, + })); + } + + const metricsMeta: MetricsMeta = { + workspaceId: connection.workspaceId, + messageId: message.messageId, + streamId: connection.streamId, + destinationId: connection.destinationId, + connectionId: connection.id, + retries, + }; + + let lastUpdated = Math.max( + new Date(connection.updatedAt || 0).getTime(), + (funcStore.lastModified || new Date(0)).getTime() + ); + const cacheKey = `${connection.id}_${lastUpdated}`; + let funcChain: FuncChain | undefined = funcsChainCache.get(cacheKey); + if (!funcChain) { + log.atDebug().log(`[${connection.id}] Refreshing function chain. Dt: ${lastUpdated}`); + funcChain = buildFunctionChain( + connection, + connStore, + funcStore, + rotorContext, + anonymousEventsStore, + fetchTimeoutMs + ); + funcsChainCache.set(cacheKey, funcChain); + } + + const chainRes = await runChain( + funcChain, + event, + ctx, + metricsMeta, + runFuncs, + !!connection.options?.noretry ? false : retriesEnabled + ); + chainRes.connectionId = connectionId; + rotorContext.metrics?.logMetrics(chainRes.execLog); + checkError(chainRes); + return chainRes; +} diff --git a/services/rotor/src/lib/metrics.ts b/services/rotor/src/lib/metrics.ts new file mode 100644 index 000000000..f8eb7e9fd --- /dev/null +++ b/services/rotor/src/lib/metrics.ts @@ -0,0 +1,329 @@ +import { getLog, isTruish, requireDefined, stopwatch } from "juava"; +import { FunctionExecLog, FunctionExecRes, RotorMetrics } from "@jitsu/core-functions"; + +import { KafkaJS } from "@confluentinc/kafka-javascript"; +import { Readable } from "stream"; +import { Counter, Gauge, Histogram } from "prom-client"; +import { createClient } from "@clickhouse/client"; + +const log = getLog("metrics"); +const metricsDestinationId = process.env.METRICS_DESTINATION_ID; +const billingMetricsTable = "active_incoming"; +const metricsTable = "metrics"; + +const max_batch_size = 10000; +const flush_interval_ms = 60000; + +export const promStoreStatuses = new Counter({ + name: "rotor_store_statuses", + help: "rotor store statuses", + labelNames: ["namespace", "operation", "status"] as const, +}); +export const promWarehouseStatuses = new Histogram({ + name: "rotor_warehouse_statuses", + help: "rotor warehouse statuses", + labelNames: ["id", "table", "status"] as const, + buckets: [0.02, 0.05, 0.2, 0.5, 1, 2], // durations in seconds +}); +export const promTopicOffsets = new Gauge({ + name: "rotor_topic_offsets2", + help: "topic offsets", + // add `as const` here to enforce label names + labelNames: ["topic", "partition", "offset"] as const, +}); +export const promMessagesConsumed = new Counter({ + name: "rotor_messages_consumed", + help: "messages consumed", + // add `as const` here to enforce label names + labelNames: ["topic", "partition"] as const, +}); +export const promMessagesProcessed = new Counter({ + name: "rotor_messages_processed", + help: "messages processed", + labelNames: ["topic", "partition"] as const, +}); +export const promMessagesRequeued = new Counter({ + name: "rotor_messages_requeued", + help: "messages requeued", + labelNames: ["topic"] as const, +}); +export const promMessagesDeadLettered = new Counter({ + name: "rotor_messages_dead_lettered", + help: "messages dead lettered", + labelNames: ["topic"] as const, +}); +export const promConnectionMessageStatuses = new Counter({ + name: "connection_message_statuses", + help: "connection message statuses", + labelNames: ["destinationId", "tableName", "status"] as const, +}); +export const promFunctionsInFlight = new Gauge({ + name: "rotor_functions_in_flight", + help: "Functions in flight", + // add `as const` here to enforce label names + labelNames: ["connectionId", "functionId"] as const, +}); +export const promFunctionsTime = new Histogram({ + name: "rotor_functions_time", + help: "Functions execution time in ms", + buckets: [1, 10, 100, 200, 1000, 2000, 3000, 4000, 5000], + // add `as const` here to enforce label names + labelNames: ["connectionId", "functionId"] as const, +}); + +export const promHandlerMetric = new Counter({ + name: "rotor_function_handler", + help: "function handler status", + labelNames: ["connectionId", "status"] as const, +}); + +const _EpochTime = 0; +const _MessageId = 1; +const _WorkspaceId = 2; +const _StreamId = 3; +const _ConnectionId = 4; +const _FunctionId = 5; +const _DestinationId = 6; +const _Status = 7; +const _Count = 8; +const _EventIndex = 9; + +type MetricsEvent = [number, string, string, string, string, string, string, string, number, number]; + +export const DummyMetrics: RotorMetrics = { + logMetrics: () => {}, + storeStatus: () => {}, + warehouseStatus: () => {}, + close: () => {}, +}; + +export function createMetrics(producer?: KafkaJS.Producer): RotorMetrics { + const buffer: MetricsEvent[] = []; + const metricsSchema = process.env.CLICKHOUSE_METRICS_SCHEMA || process.env.CLICKHOUSE_DATABASE || "newjitsu_metrics"; + + const clickhouse = createClient({ + url: clickhouseHost(), + username: process.env.CLICKHOUSE_USERNAME || "default", + password: requireDefined(process.env.CLICKHOUSE_PASSWORD, `env CLICKHOUSE_PASSWORD is not defined`), + clickhouse_settings: { + async_insert: 1, + wait_for_async_insert: 0, + async_insert_busy_timeout_ms: 30000, + date_time_input_format: "best_effort", + }, + }); + + const flushBillingMetrics = async (buf: MetricsEvent[]) => { + if (producer) { + const asyncWrite = async () => { + return producer.send({ + topic: `in.id.metrics.m.batch.t.${billingMetricsTable}`, + messages: buf + .filter(m => m[_FunctionId].startsWith("builtin.destination.") && m[_Status] !== "dropped") + .map(m => { + const hourTrunc = Math.floor(m[_EpochTime] / 3600) * 3600; + const d = new Date(hourTrunc * 1000); + const key = m[_MessageId] + "_" + m[_EventIndex] + "_" + (m[_EpochTime] - hourTrunc); + return { + key: key, + value: JSON.stringify({ + timestamp: d, + workspaceId: m[_WorkspaceId], + // to count active events use composed key: messageId_eventIndex_receivedAt + messageId: key, + }), + }; + }), + }); + }; + return asyncWrite().catch(e => { + log.atError().withCause(e).log(`Failed to flush billing metrics`); + }); + } else { + const billingStream = new Readable({ objectMode: true }); + const billingResponse = clickhouse.insert({ + table: metricsSchema + "." + billingMetricsTable, + format: "JSONCompactEachRow", + values: billingStream, + }); + + const asyncWrite = async () => { + for (let i = 0; i < buf.length; i++) { + const m = buf[i]; + if (m[_FunctionId].startsWith("builtin.destination.") && m[_Status] !== "dropped") { + const hourTrunc = Math.floor(m[_EpochTime] / 3600) * 3600; + const key = m[_MessageId] + "_" + m[_EventIndex] + "_" + (m[_EpochTime] - hourTrunc); + billingStream.push([hourTrunc, m[_WorkspaceId], key]); + } + } + billingStream.push(null); + return billingResponse; + }; + return asyncWrite() + .then(async r => { + if (!r.executed) { + log.atError().log(`Failed to insert ${buf.length} billing metrics: ${JSON.stringify(r)}`); + } + }) + .catch(e => { + log.atError().withCause(e).log(`Failed to insert billing metrics.`); + }); + } + }; + + const flush = async (buf: MetricsEvent[]) => { + const promises: Promise[] = [flushBillingMetrics(buf)]; + + const metricsStream = new Readable({ objectMode: true }); + const metricsResponse = clickhouse.insert({ + table: metricsSchema + "." + metricsTable, + format: "JSONCompactEachRow", + values: metricsStream, + }); + const asyncWrite = async () => { + for (let i = 0; i < buf.length; i++) { + metricsStream.push(buf[i]); + } + metricsStream.push(null); + return metricsResponse; + }; + + promises.push( + asyncWrite() + .then(async r => { + if (!r.executed) { + log.atError().log(`Failed to insert ${buf.length} records: ${JSON.stringify(r)}`); + } + }) + .catch(e => { + log.atError().withCause(e).log(`Failed to flush metrics events`); + }) + ); + + await Promise.all(promises); + }; + + const interval = setInterval(async () => { + const length = buffer.length; + if (length > 0) { + const sw = stopwatch(); + try { + const copy = buffer.slice(); + buffer.length = 0; + await flush(copy); + log.atDebug().log(`Periodic flushing ${copy.length} metrics events took ${sw.elapsedPretty()}`); + } catch (e) { + log.atError().withCause(e).log(`Failed to flush metrics`); + } + } + }, flush_interval_ms); + + return { + logMetrics: (execLog: FunctionExecLog) => { + if (!metricsDestinationId) { + return; + } + + for (let i = 0; i < execLog.length; i++) { + const el = execLog[i]; + if (!el.metricsMeta) { + continue; + } + const status = ((el: FunctionExecRes) => { + if (el.metricsMeta?.retries) { + promConnectionMessageStatuses.inc({ + destinationId: el.metricsMeta!.connectionId, + tableName: "_all_", + status: "retry", + }); + } + let prefix = el.functionId.startsWith("builtin.destination.") + ? "" + : el.functionId.startsWith("builtin.transformation.") + ? "builtin_function_" + : "function_"; + let status = "success"; + if (el.error) { + if (el.metricsMeta?.retries) { + prefix = prefix + "retry_"; + } + promConnectionMessageStatuses.inc({ + destinationId: el.metricsMeta!.connectionId, + tableName: "_all_", + status: "error", + }); + status = "error"; + if (el.dropped) { + promConnectionMessageStatuses.inc({ + destinationId: el.metricsMeta!.connectionId, + tableName: "_all_", + status: "drop", + }); + } + } else if (el.dropped) { + prefix = ""; + status = "dropped"; + promConnectionMessageStatuses.inc({ + destinationId: el.metricsMeta!.connectionId, + tableName: "_all_", + status: "drop", + }); + } else if (el.functionId === "builtin.destination.bulker") { + status = "processed"; + } else { + promConnectionMessageStatuses.inc({ + destinationId: el.metricsMeta!.connectionId, + tableName: "_all_", + status: "success", + }); + } + return prefix + status; + })(el); + buffer.push([ + Math.floor((el.receivedAt ? el.receivedAt.getTime() : Date.now()) / 1000), + el.metricsMeta.messageId, + el.metricsMeta.workspaceId, + el.metricsMeta.streamId, + el.metricsMeta.connectionId, + el.functionId, + el.metricsMeta.destinationId, + status, + 1, + el.eventIndex, + ]); + } + if (buffer.length >= max_batch_size) { + const sw = stopwatch(); + const copy = buffer.slice(); + buffer.length = 0; + setImmediate(async () => + flush(copy) + .then(() => log.atDebug().log(`Flushed ${copy.length} metrics events. Took: ${sw.elapsedPretty()}`)) + .catch(e => { + log.atError().withCause(e).log(`Failed to flush metrics`); + }) + ); + } + }, + storeStatus: (namespace: string, operation: string, status: string) => { + promStoreStatuses.labels(namespace, operation, status).inc(); + }, + warehouseStatus: (id: string, table: string, status: string, timeMs: number) => { + promWarehouseStatuses.labels(id, table, status).observe(timeMs / 1000); + }, + close: () => { + clearInterval(interval); + clickhouse.close(); + }, + }; +} + +function clickhouseHost() { + if (process.env.CLICKHOUSE_URL) { + return process.env.CLICKHOUSE_URL; + } + return `${isTruish(process.env.CLICKHOUSE_SSL) ? "https://" : "http://"}${requireDefined( + process.env.CLICKHOUSE_HOST, + "env CLICKHOUSE_HOST is not defined" + )}`; +} diff --git a/services/rotor/src/lib/redis.ts b/services/rotor/src/lib/redis.ts new file mode 100644 index 000000000..2013b3f2d --- /dev/null +++ b/services/rotor/src/lib/redis.ts @@ -0,0 +1,83 @@ +import { Redis } from "ioredis"; +import { requireDefined, getLog } from "juava"; + +const log = getLog("redis"); + +function hideSensitiveInfoFromURL(url: string) { + let parsed: URL; + try { + parsed = new URL(url); + } catch (e) { + //if URL is not parseable, we just return it as is. We can't fail and + //rethrow error + return url; + } + if (parsed.password) { + parsed.password = "****"; + } + return parsed.toString(); +} + +function resolveRedisConnectionOptions( + redisUrl: string, + redisSentinelAddress: string | undefined +): Record { + let sentinels: any; + + if (redisSentinelAddress) { + sentinels = redisSentinelAddress.split(",").map(sentinel => { + const [host, port] = sentinel.split(":"); + return { + host, + port: port ? parseInt(port, 10) : undefined, + }; + }); + } + + let tls: any; + if (redisUrl.startsWith("rediss://")) { + tls = { + rejectUnauthorized: false, + }; + } + + return { + enableAutoPipelining: true, + lazyConnect: false, + maxRetriesPerRequest: 3, + sentinels, + tls, + }; +} + +/** + * Example `REDIS_URL` values for a standalone Redis instance: + * - Standalone Redis: `redis://username:password@localhost:6379/1` + * - Standalone Redis with SSL: `rediss://username:password@localhost:6379/2` + * + * Example `REDIS_URL` and `REDIS_SENTINEL_ADDRESS` values for Redis Sentinel: + * - Redis URL: `redis://username:password@/3?name=mymaster` + * - Redis Sentinel Address: `sentinel1:26379,sentinel2:26379,sentinel3:26379` + */ +export function createRedis(): Redis { + const redisUrl = requireDefined(process.env.REDIS_URL, "env REDIS_URL is not defined"); + const redisSentinelAddress = process.env.REDIS_SENTINEL_ADDRESS; + + let sanitizedRedisUrl: string = hideSensitiveInfoFromURL(redisUrl); + if (redisSentinelAddress) { + sanitizedRedisUrl += ` (${redisSentinelAddress})`; + } + + log.atDebug().log(`Building redis client for ${sanitizedRedisUrl}`); + + const connectionOptions = resolveRedisConnectionOptions(redisUrl, redisSentinelAddress); + const redisClient = new Redis(redisUrl, connectionOptions); + + redisClient.on("error", err => { + log.atWarn().withCause(err).log(`Redis @ ${sanitizedRedisUrl} - failed to connect`); + }); + redisClient.on("connect", () => { + log.atInfo().log(`Redis @ ${sanitizedRedisUrl} - successfully connected`); + }); + return redisClient; +} diff --git a/services/rotor/src/lib/repositories.ts b/services/rotor/src/lib/repositories.ts new file mode 100644 index 000000000..8076cd526 --- /dev/null +++ b/services/rotor/src/lib/repositories.ts @@ -0,0 +1,5 @@ +import { EnrichedConnectionConfig, FunctionConfig, storeFunc, StreamWithDestinations } from "@jitsu/core-functions"; + +export const functionsStore = storeFunc("functions"); +export const connectionsStore = storeFunc("rotor-connections"); +export const streamsStore = storeFunc("streams-with-destinations"); diff --git a/services/rotor/src/lib/retries.ts b/services/rotor/src/lib/retries.ts new file mode 100644 index 000000000..155764fa6 --- /dev/null +++ b/services/rotor/src/lib/retries.ts @@ -0,0 +1,76 @@ +import dayjs from "dayjs"; +import utc from "dayjs/plugin/utc"; +dayjs.extend(utc); +import { RetryErrorName, DropRetryErrorName } from "@jitsu/functions-lib"; + +const MESSAGES_RETRY_COUNT = process.env.MESSAGES_RETRY_COUNT ? parseInt(process.env.MESSAGES_RETRY_COUNT) : 3; +// MESSAGES_RETRY_BACKOFF_BASE defines base for exponential backoff in minutes. +// For example, if MESSAGES_RETRY_COUNT is 3 and base is 5, then retry delays will be 5, 25, 125 minutes. +const MESSAGES_RETRY_BACKOFF_BASE = process.env.MESSAGES_RETRY_BACKOFF_BASE + ? parseInt(process.env.MESSAGES_RETRY_BACKOFF_BASE) + : 10; +// MESSAGES_RETRY_BACKOFF_MAX_DELAY defines maximum possible retry delay in minutes. Default: 1440 minutes = 24 hours +const MESSAGES_RETRY_BACKOFF_MAX_DELAY = process.env.MESSAGES_RETRY_BACKOFF_MAX_DELAY + ? parseInt(process.env.MESSAGES_RETRY_BACKOFF_MAX_DELAY) + : 1440; + +export type retryPolicy = { + retries: number; + delays: number[]; +}; + +const retryDefaultDelays = (() => { + const delays: number[] = []; + for (let i = 0; i < MESSAGES_RETRY_COUNT; i++) { + delays.push(Math.min(Math.pow(MESSAGES_RETRY_BACKOFF_BASE, i + 1), MESSAGES_RETRY_BACKOFF_MAX_DELAY)); + } + return delays; +})(); + +export const retryDefaultPolicy: retryPolicy = { + retries: MESSAGES_RETRY_COUNT, + delays: retryDefaultDelays, +}; + +export function getRetryPolicy(e: Error & { retryPolicy?: retryPolicy }): retryPolicy { + let retryPolicy = retryDefaultPolicy; + if (e.retryPolicy) { + retryPolicy = { ...retryPolicy, ...e.retryPolicy }; + retryPolicy.retries = Math.min(MESSAGES_RETRY_COUNT, retryPolicy.retries); + retryPolicy.delays = retryPolicy.delays.map(d => Math.min(MESSAGES_RETRY_BACKOFF_MAX_DELAY, d)); + } + return retryPolicy; +} + +export function retryBackOffTime(retryPolicy: retryPolicy, attempt: number) { + if (attempt > retryPolicy.retries) { + return ""; + } + const delays = retryPolicy?.delays?.length > 0 ? retryPolicy.delays : retryDefaultDelays; + const backOffDelayMin = delays[attempt - 1] || delays[delays.length - 1]; + return dayjs().add(backOffDelayMin, "minute").utc().toISOString(); +} + +export function retryLogMessage(retryPolicy: retryPolicy, retries: number): string { + const retryTime = retryBackOffTime(retryPolicy, retries + 1); + return `${retries > 0 ? `Retry attempt: ${retries} of ${retryPolicy.retries}. ` : ""}${ + retryTime ? `Scheduled retry time: ${retryTime}` : "Putting to dead-letter queue." + }`; +} + +export function retryObject(e: Error & { retryPolicy?: retryPolicy }, retries: number) { + if (e.name === DropRetryErrorName || e.name === RetryErrorName) { + const retryPolicy = getRetryPolicy(e); + const retryTime = retryBackOffTime(retryPolicy, retries + 1); + return { retry: { left: retryPolicy.retries - retries, ...(retryTime ? { time: retryTime } : {}) } }; + } else { + return undefined; + } +} + +export function retryLogMessageIfNeeded(e: Error & { retryPolicy?: retryPolicy }, retries: number) { + if (e.name === DropRetryErrorName || e.name === RetryErrorName) { + const retryPolicy = getRetryPolicy(e); + return retryLogMessage(retryPolicy, retries); + } +} diff --git a/services/rotor/src/lib/rotor.ts b/services/rotor/src/lib/rotor.ts index 077ff00bc..a3bfd9377 100644 --- a/services/rotor/src/lib/rotor.ts +++ b/services/rotor/src/lib/rotor.ts @@ -1,124 +1,275 @@ -import { getLog } from "juava"; -import { connectToKafka, KafkaCredentials } from "@jitsu-internal/console/lib/server/kafka-config"; +import { getLog, isTruish, parseNumber, requireDefined } from "juava"; +import { connectToKafka, deatLetterTopic, KafkaCredentials, retryTopic } from "./kafka-config"; +import PQueue from "p-queue"; +import dayjs from "dayjs"; +import utc from "dayjs/plugin/utc"; +import { getRetryPolicy, retryBackOffTime, retryLogMessage } from "./retries"; +import { + createMetrics, + promMessagesConsumed, + promMessagesDeadLettered, + promMessagesProcessed, + promMessagesRequeued, + promConnectionMessageStatuses, + promTopicOffsets, +} from "./metrics"; +import { FuncChainFilter } from "./functions-chain"; +import { KafkaJS } from "@confluentinc/kafka-javascript"; -const log = getLog("kafka-rotor"); +import { functionFilter, MessageHandlerContext } from "./message-handler"; +import { connectionsStore, functionsStore, streamsStore } from "./repositories"; +import { FuncChainResult, RotorMetrics } from "@jitsu/core-functions"; -const maxLocalRetries = 2; +dayjs.extend(utc); -export class NotRetryableError extends Error { - public doNotRetry: boolean = true; +const log = getLog("kafka-rotor"); - constructor(message: string) { - super(message); - } -} +const RETRY_TIME_HEADER = "retry_time"; +const RETRY_COUNT_HEADER = "retries"; +const ERROR_HEADER = "error"; +const ORIGINAL_TOPIC_HEADER = "original_topic"; +const FUNCTION_ID_HEADER = "function_id"; +export const CONNECTION_IDS_HEADER = "connection_ids"; -async function withRetries(f: () => Promise, opts: { maxRetries?: number; pauseSeconds?: number } = {}) { - const { maxRetries = 2, pauseSeconds = 5 } = opts; - while (true) { - try { - await f(); - break; - } catch (e: any) { - if (e.doNotRetry) { - throw e; - } - log - .atDebug() - .withCause(e) - .log(`Failed to process message. (Local) retries left: ${maxRetries - 1}`); - if (maxRetries > 1) { - await new Promise(resolve => setTimeout(resolve, pauseSeconds * 1000)); - return withRetries(f, { maxRetries: maxRetries - 1, pauseSeconds }); - } else { - throw e; - } - } - } -} +const concurrency = parseNumber(process.env.CONCURRENCY, 10); +const fetchTimeoutMs = parseNumber(process.env.FETCH_TIMEOUT_MS, 2000); +const rotorIndex = parseNumber(process.env.INSTANCE_INDEX, 0); export type KafkaRotorConfig = { credentials: KafkaCredentials; consumerGroupId: string; - kafkaTopic?: string; + kafkaTopics: string[]; kafkaClientId?: string; - maxSecondsInQueueAfterFailure?: number; - handle: (message: string) => Promise; + rotorContext: Omit; + handle: ( + message: string, + rotorContext: MessageHandlerContext, + runFuncs: FuncChainFilter, + headers?, + retriesEnabled?: boolean, + retries?: number, + fetchTimeoutMs?: number + ) => Promise; }; export type KafkaRotor = { - start: () => Promise; + start: () => Promise; + close: () => Promise; }; -function shouldReturnToQueue(firstProcessed: Date, maxSecondsInQueueAfterFailure: number, error: any) { - return !error.doNotRetry && Date.now() - firstProcessed.getTime() <= maxSecondsInQueueAfterFailure * 1000; -} - export function kafkaRotor(cfg: KafkaRotorConfig): KafkaRotor { - const { kafkaTopic = "incoming-queue", kafkaClientId = "kafka-rotor", maxSecondsInQueueAfterFailure = 3600 } = cfg; + const { kafkaTopics, consumerGroupId, rotorContext, handle, kafkaClientId = "kafka-rotor" } = cfg; + let consumer: KafkaJS.Consumer; + let producer: KafkaJS.Producer; + let admin: KafkaJS.Admin; + let closeQueue: () => Promise; + let interval: any; + let metrics: RotorMetrics; return { start: async () => { const kafka = connectToKafka({ defaultAppId: kafkaClientId, ...cfg.credentials }); - const consumer = kafka.consumer({ groupId: cfg.consumerGroupId, allowAutoTopicCreation: false }); + consumer = kafka.consumer({ + kafkaJS: { + groupId: consumerGroupId, + allowAutoTopicCreation: false, + fromBeginning: true, + autoCommitInterval: 10000, + autoCommit: true, + }, + ...(isTruish(process.env.CONSUMER_PROTOCOL) + ? { + "group.protocol": "consumer", + } + : {}), + "group.instance.id": process.env.INSTANCE_ID || process.env.ROTOR_INSTANCE_ID, + }); await consumer.connect(); - await consumer.subscribe({ topic: kafkaTopic, fromBeginning: true }); + log.atInfo().log("Subscribing to kafka topics: ", kafkaTopics); + await consumer.subscribe({ topics: kafkaTopics }); - const producer = kafka.producer({ allowAutoTopicCreation: false }); + producer = kafka.producer({ + kafkaJS: { allowAutoTopicCreation: false, acks: 1, compression: getCompressionType() }, + }); await producer.connect(); + metrics = createMetrics(producer); + admin = kafka.admin({ kafkaJS: {} }); + await admin.connect(); - await consumer.run({ - autoCommit: true, - - eachMessage: async ({ message }) => { - const firstProcessed = message.headers?.firstProcessed - ? new Date(message.headers?.firstProcessed.toString()) - : new Date(); - const retries = message.headers?.retries ? parseInt(message.headers?.retries.toString()) : 0; - + if (rotorIndex === 0) { + interval = setInterval(async () => { try { - await withRetries( - async () => { - if (message.value) { - await cfg.handle(message.value?.toString()); - } - }, - { maxRetries: maxLocalRetries } - ); - } catch (e) { - const returnToQueue = shouldReturnToQueue(firstProcessed, maxSecondsInQueueAfterFailure, e); - log - .atError() - .withCause(e) - .log( - `Failed to process message for ${ - message.key || "(no key set)" - } after ${maxLocalRetries} local retries. ${ - returnToQueue - ? "Message will be returned to reprocessing" - : "Message will be sent to dead-letter queue" - }: ${message.value}` - ); - const topic = returnToQueue ? kafkaTopic : kafkaTopic + "-dead-letter"; - try { - await producer.send({ - topic, - messages: [ - { - value: message.value, - key: message.key, - headers: { - firstProcessed: firstProcessed.toISOString(), - retries: `${retries + 1}`, - }, - }, - ], + for (const topic of kafkaTopics) { + const watermarks = await admin.fetchTopicOffsets(topic, { + timeout: 10000, + isolationLevel: KafkaJS.IsolationLevel.READ_COMMITTED, }); - } catch (e) { - log.atDebug().withCause(e).log(`Failed to put message to ${topic}: ${message.value}`); + for (const o of watermarks) { + promTopicOffsets.set({ topic: topic, partition: o.partition, offset: "high" }, parseInt(o.high)); + promTopicOffsets.set({ topic: topic, partition: o.partition, offset: "low" }, parseInt(o.low)); + } + } + const offsets = await admin.fetchOffsets({ groupId: consumerGroupId, topics: kafkaTopics }); + for (const o of offsets) { + for (const p of o.partitions) { + promTopicOffsets.set({ topic: o.topic, partition: p.partition, offset: "offset" }, parseInt(p.offset)); + } } + } catch (e) { + log.atError().withCause(e).log("Failed to commit offsets"); } + }, 60000); + } + + async function onMessage(message: KafkaJS.KafkaMessage, topic: string, partition: number) { + promMessagesConsumed.inc({ topic, partition }); + const value = message.value; + if (!value) { + return; + } + const headers = message.headers || {}; + const retries = headers[RETRY_COUNT_HEADER] ? parseInt(headers[RETRY_COUNT_HEADER].toString()) : 0; + const retriedFunctionId = headers[FUNCTION_ID_HEADER] ? headers[FUNCTION_ID_HEADER].toString() : ""; + const connectionIds = + headers && headers[CONNECTION_IDS_HEADER] ? headers[CONNECTION_IDS_HEADER].toString().split(",") : [""]; + const conProms = connectionIds.map(connectionId => + handle( + value.toString(), + { + ...rotorContext, + connectionStore: requireDefined(connectionsStore.getCurrent(), "Connection store is not initialized"), + functionsStore: requireDefined(functionsStore.getCurrent(), "Functions store is not initialized"), + streamsStore: requireDefined(streamsStore.getCurrent(), "Streams store is not initialized"), + metrics, + }, + functionFilter(retriedFunctionId), + { + ...headers, + [CONNECTION_IDS_HEADER]: connectionId, + }, + true, + retries, + fetchTimeoutMs + ) + .then(() => { + promMessagesProcessed.inc({ topic, partition }); + }) + .catch(async e => { + const retryPolicy = getRetryPolicy(e); + const retryTime = retryBackOffTime(retryPolicy, retries + 1); + const newMessage = e.event + ? JSON.stringify({ ...JSON.parse(value.toString()), httpPayload: e.event }) + : value; + log + .atError() + .withCause(e) + .log( + `Failed to process function ${e.functionId} for connection ${connectionId} messageId: ${ + message.key || "(no key set)" + }. ${retryLogMessage(retryPolicy, retries)}` + ); + if (!retryTime) { + promMessagesDeadLettered.inc({ topic }); + promConnectionMessageStatuses.inc({ + destinationId: connectionId, + tableName: "_all_", + status: "deadLettered", + }); + } else { + promMessagesRequeued.inc({ topic }); + } + const requeueTopic = retryTime ? retryTopic() : deatLetterTopic(); + try { + await producer.send({ + topic: requeueTopic, + messages: [ + { + value: newMessage, + // on first retry we create a new key so if more than one destination fails - they will be retried independently + key: retries === 0 ? `${message.key}_${connectionId}` : message.key, + headers: { + [ERROR_HEADER]: e.message?.substring(0, 1024) || "unknown error", + [RETRY_COUNT_HEADER]: `${retries}`, + [ORIGINAL_TOPIC_HEADER]: topic, + [RETRY_TIME_HEADER]: retryTime, + [CONNECTION_IDS_HEADER]: connectionId, + ...(e.functionId ? { [FUNCTION_ID_HEADER]: e.functionId } : {}), + }, + }, + ], + }); + } catch (e) { + log.atDebug().withCause(e).log(`Failed to put message to ${topic}: ${message.value}`); + } + }) + ); + await Promise.all(conProms); + } + + const queue = new PQueue({ concurrency }); + + const onSizeLessThan = async (limit: number) => { + // Instantly resolve if the queue is empty. + if (queue.size < limit) { + return; + } + + return new Promise(resolve => { + const listener = () => { + if (queue.size < limit) { + queue.removeListener("next", listener); + resolve(); + } + }; + + queue.on("next", listener); + }); + }; + closeQueue = async () => { + log.atInfo().log("Closing queue..."); + await queue.onIdle(); + }; + + await consumer.run({ + partitionsConsumedConcurrently: 8, + eachMessage: async ({ message, topic, partition }) => { + //make sure that queue has no more entities than concurrency limit (running tasks not included) + await onSizeLessThan(concurrency); + queue.add(async () => onMessage(message, topic, partition)); }, }); + + return metrics; + }, + close: async () => { + log.atInfo().log("Closing kafka-rotor"); + await consumer?.disconnect(); + await admin?.disconnect(); + await closeQueue?.(); + if (metrics) { + metrics.close(); + } + await producer?.disconnect(); + if (interval) { + clearInterval(interval); + } + log.atInfo().log("Kafka-rotor closed gracefully. 💜"); }, }; } + +export function getCompressionType() { + switch (process.env.KAFKA_TOPIC_COMPRESSION) { + case "gzip": + return KafkaJS.CompressionTypes.GZIP; + case "snappy": + return KafkaJS.CompressionTypes.Snappy; + case "lz4": + return KafkaJS.CompressionTypes.LZ4; + case "zstd": + return KafkaJS.CompressionTypes.ZSTD; + case "none": + return KafkaJS.CompressionTypes.None; + default: + return undefined; + } +} diff --git a/services/rotor/src/lib/version.ts b/services/rotor/src/lib/version.ts new file mode 100644 index 000000000..aa40c3cf2 --- /dev/null +++ b/services/rotor/src/lib/version.ts @@ -0,0 +1,67 @@ +import { isTruish } from "juava"; + +/** + * A little code duplication with console. Not ideal, although better than + * moving it to common dependency (juava). + */ +export type ApplicationVersion = { + /** + * Version as 3.2.1 or "dev" + */ + version: string; + /** + * "latest" or "beta" or "dev" + */ + stream: string; + + git?: { + //Id of latest commit + commitId: string; + }; +}; + +export type EnvVars = { + JITSU_VERSION_COMMIT_SHA?: string; + JITSU_VERSION_DOCKER_TAG?: string; + JITSU_VERSION_STRING?: string; + VERCEL_GIT_COMMIT_SHA?: string; +}; + +function getGit(env: EnvVars): ApplicationVersion["git"] { + if (env.JITSU_VERSION_COMMIT_SHA) { + return { + commitId: env.JITSU_VERSION_COMMIT_SHA, + }; + } else if (env.JITSU_VERSION_COMMIT_SHA) { + return { + commitId: env.JITSU_VERSION_COMMIT_SHA, + }; + } +} + +export function getApplicationVersion(): ApplicationVersion { + const env = process.env as EnvVars; + return { + version: env.JITSU_VERSION_STRING || "dev", + stream: env.JITSU_VERSION_DOCKER_TAG || "dev", + git: getGit(env), + }; +} + +function sortByKey(dict: Record): Record { + return Object.fromEntries(Object.entries(dict).sort(([a], [b]) => a.localeCompare(b))); +} + +export function getDiagnostics() { + if (isTruish(process.env.__DANGEROUS_ENABLE_FULL_DIAGNOSTICS)) { + return { + env: sortByKey(process.env), + proc: { + config: sortByKey(process.config), + versions: sortByKey(process.versions), + execPath: process.execPath, + argv: process.argv, + }, + }; + } +} diff --git a/services/rotor/tsconfig.json b/services/rotor/tsconfig.json index b520ae1a2..e54b28d99 100644 --- a/services/rotor/tsconfig.json +++ b/services/rotor/tsconfig.json @@ -6,17 +6,18 @@ "allowSyntheticDefaultImports": true, "importHelpers": true, "removeComments": true, - "target": "ES2021", + "target": "ESNext", "module": "commonjs", "lib": [ - "esnext" + "ESNext", "DOM" ], "allowJs": true, "strict": true, "esModuleInterop": true, "jsx": "react-jsx", "moduleResolution": "node", - "resolveJsonModule": true + "resolveJsonModule": true, + "skipLibCheck": true, }, "exclude": [ "node_modules" diff --git a/services/rotor/webpack.config.js b/services/rotor/webpack.config.js index 2efd7183e..88591c08d 100644 --- a/services/rotor/webpack.config.js +++ b/services/rotor/webpack.config.js @@ -5,7 +5,8 @@ const config = { entry: "./src/index.ts", target: "node", externals: { - vm2: "require('vm2')", + "isolated-vm": "require('isolated-vm')", + "@confluentinc/kafka-javascript": "require('@confluentinc/kafka-javascript')", }, node: { __dirname: false, diff --git a/tsconfig.base.json b/tsconfig.base.json index 261232a14..31eeaa739 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -12,7 +12,8 @@ "moduleResolution": "node", "resolveJsonModule": true, "importHelpers": true, - "removeComments": true + "removeComments": true, + "skipLibCheck": true }, "exclude": ["node_modules", "dist", "test_projects", "test", "templates"] } diff --git a/turbo.json b/turbo.json index 940947e00..57c710b7f 100644 --- a/turbo.json +++ b/turbo.json @@ -8,6 +8,10 @@ "dependsOn": ["^console:start"], "cache": false }, + "console:storybook": { + "dependsOn": ["^console:storybook"], + "cache": false + }, "ee-api:dev": { "dependsOn": ["^ee-api:dev"], "cache": false @@ -16,12 +20,12 @@ "dependsOn": ["^rotor:dev"], "cache": false }, - "tool:hash": { - "dependsOn": ["^tool:hash"], + "profiles:dev": { + "dependsOn": ["^profiles:dev"], "cache": false }, - "compile": { - "dependsOn": ["^compile"], + "tool:hash": { + "dependsOn": ["^tool:hash"], "cache": false }, "dev": { diff --git a/types/protocols/analytics.d.ts b/types/protocols/analytics.d.ts index 32dc0bbe3..f253d61d1 100644 --- a/types/protocols/analytics.d.ts +++ b/types/protocols/analytics.d.ts @@ -2,8 +2,88 @@ import { ISO8601Date } from "./iso8601.d"; export type ID = string | null | undefined; -export type DataLayoutType = "segment" | "jitsu-legacy" | "segment-single-table"; +export type DataLayoutType = "segment" | "jitsu-legacy" | "segment-single-table" | "passthrough"; +export type WithConfidence = T & { + //A value from 0-100 indicating how confident we are in the result + confidence?: number; +}; + +export type Geo = { + continent?: { + code: "AF" | "AN" | "AS" | "EU" | "NA" | "OC" | "SA"; + /** + * Localized name of the continent + */ + name: string; + }; + country?: { + /** + * Two-letter country code (ISO 3166-1 alpha-2): https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 + */ + code: string; + /** + * Localized name of the country + */ + name: string; + isEU: boolean; + }; + region?: WithConfidence<{ + /** + * Code of the region (ISO 3166-2): https://en.wikipedia.org/wiki/ISO_3166-2. + * For USA it's two-letter capitaluzed state code (such as NY) + */ + code: string; + /** + * Localized name of the region + */ + name: string; + }>; + city?: WithConfidence<{ + /** + * Localized name of the city + */ + name: string; + }>; + + postalCode?: WithConfidence<{ + code: string; + }>; + + location?: { + latitude: number; + longitude: number; + accuracyRadius?: number; + timezone?: string; + /** + * Only for USA locations + */ + usaData?: { + populationDensity?: number; + metroCode?: number; + averageIncome?: number; + }; + }; + provider?: { + /** + * Autonomous system number + */ + as?: { + num?: number; + name?: string; + }; + connectionType?: string; + domain?: string; + isAnonymousVpn?: boolean; + isHostingProvider?: boolean; + isLegitimateProxy?: boolean; + isPublicProxy?: boolean; + isResidentialProxy?: boolean; + isTorExitNode?: boolean; + userType?: string; + isp?: string; + }; +}; /** * Event coming from client library */ @@ -43,7 +123,7 @@ export type ServerContextReservedProps = { //if request came server-to-server, then it's an IP of a server //for device events it will be an IP of a device //don't use this field in functions, use context.ip instead - request_ip?: string; + requestIp?: string; receivedAt?: ISO8601Date; sentAt?: ISO8601Date; timestamp?: ISO8601Date; @@ -63,7 +143,7 @@ interface ProcessingContext { export type AnalyticsServerEvent = AnalyticsClientEvent & ServerContext & ProcessingContext; -export type JSONPrimitive = string | number | boolean | null; +export type JSONPrimitive = string | number | boolean | null | undefined; export type JSONValue = JSONPrimitive | JSONObject | JSONArray; export type JSONObject = { [member: string]: JSONValue }; export type JSONArray = Array; @@ -76,6 +156,7 @@ export type Integrations = { export type Options = { integrations?: Integrations; userId?: ID; + groupId?: ID; anonymousId?: ID; timestamp?: Date | string; context?: AnalyticsContext; @@ -108,7 +189,7 @@ export type PageReservedProps = { interface AnalyticsContext { /** * IP address of the originating request. If event is sent from a device, then it's an IP of a device - * (copied from request_ip) + * (copied from requestIp) * If request is sent from server-to-server, this field is not automatically populated * and should be filled manually */ @@ -126,6 +207,8 @@ interface AnalyticsContext { library?: { name: string; version: string; + //allow to add custom fields + [key: string]: any; }; traits?: { crossDomainId?: string } & JSONObject; @@ -148,6 +231,68 @@ interface AnalyticsContext { id: string; }; + consent?: { + categoryPreferences?: Record; + }; + + /** + * Other tracking tools client ids + */ + clientIds?: { + //Client ID of GA4 property + ga4?: { + clientId: string; + sessionIds?: any; + }; + firebase?: { + appInstanceId: string; + }; + //from cookies: _fbc - Facebook click ID, _fbp - Facebook browser ID. + //see https://developers.facebook.com/docs/marketing-api/conversions-api/parameters/customer-information-parameters + fbc?: string; + fbp?: string; + [key: string]: any; + }; + + geo?: Geo; + + //mobile + app?: { + name: string; + version: string; + build?: string; + namespace?: string; + [key: string]: any; + }; + device?: { + id?: string; + advertisingId?: string; + adTrackingEnabled?: boolean; + manufacturer?: string; + model?: string; + name?: string; + type?: string; + token?: string; + [key: string]: any; + }; + network?: { + bluetooth?: boolean; + carrier?: string; + cellular?: boolean; + wifi?: boolean; + [key: string]: any; + }; + os?: { + name: string; + version: string; + }; + screen?: { + width: number; + height: number; + density?: number; + [key: string]: any; + }; + [key: string]: any; } @@ -156,6 +301,148 @@ export type DispatchedEvent = Context; export type Callback = (ctx: Context | undefined) => Promise | unknown; +type PersistentStorage = { + getItem: (key: string, options?: any) => any; + setItem: (key: string, value: any, options?: any) => void; + removeItem: (key: string, options?: any) => void; + reset: () => void; +}; + +export type RuntimeFacade = { + store?(): PersistentStorage; + userAgent(): string | undefined; + language(): string | undefined; + pageUrl(): string | undefined; + documentEncoding(): string | undefined; + getCookie(name: string): string | undefined; + getCookies(): Record; + + timezoneOffset(): number | undefined; + ip?(): string | undefined; + screen(): + | { + width: number; + height: number; + innerWidth: number; + innerHeight: number; + density: number; + } + | undefined; + referrer(): string | undefined; + pageTitle(): string | undefined; +}; + +export type ErrorHandler = (message: string, ...args: any[]) => void; + +export type JitsuOptions = { + /** + * API Key. Optional. If not set, Jitsu will send event to the server without auth, and server + * will link the call to configured source by domain name + */ + writeKey?: string; + /** + * API Host. Default value: same host as script origin + */ + host?: string; + /** + * To enable debug logging + */ + debug?: boolean; + /** + * Default payload context to be included in all requests. + * These attributes are merged with the request-specific payload context, + * allowing common or global data (e.g., browser timezone, some extra user identifier) + * to be automatically included to every request. + * + * The context can be a nested structure. + */ + defaultPayloadContext?: Record; + /** + * Explicitly specify cookie domain. If not set, cookie domain will be set to top level + * of the current domain. Example: if JS lives on "app.example.com", cookie domain will be + * set to ".example.com". If it lives on "example.com", cookie domain will be set to ".example.com" too + */ + cookieDomain?: string; + /** + * Name of cookies + */ + cookieNames?: { + anonymousId?: string; + userId?: string; + userTraits?: string; + groupId?: string; + groupTraits?: string; + }; + /** + * Additional cookies to capture, where the keys are cookie names and + * the values are the corresponding cookie values. By default, the following cookies are captured: + * - Facebook: `_fbc`, `_fbp` + * - Google Analytics 4: GA4 client ID + * This property allows you to capture additional cookies beyond the defaults. + */ + cookieCapture?: Record; + /** + * Provide fetch implementation. It is required if you want to use Jitsu in NodeJS + */ + fetch?: typeof fetch; + /** + * Which runtime to use. Runtime is used for obtaining context of the event: cookies, + * url, etc. At the moment, Jitsu supports browser runtime and NodeJS runtime, but you + * can provide your own implementation. + * + * If it's not set, the runtime will be detected automatically by presence of `window` object + */ + runtime?: RuntimeFacade; + /** + * If set to true, jitsu will output events in console. In this case you don't need to set + * writeKey / host. It's useful for debugging development environment + */ + echoEvents?: boolean; + + /** + * If true, events will go to s2s endpoints like ${host}/api/s/s2s/{type}. Otherwise, they'll go to ${host}/api/s/{type}. + * + */ + s2s?: boolean; + + /** + * Timeout for fetch requests. Default value: 5000 + */ + fetchTimeoutMs?: number; + + /** + * Endpoint that makes sure that Jitsu anonymousId cookie is set as server (httpOnly) cookie. + * Endpoint must be hosted on the same domain as the site where Jitsu code is installed. + * Required to overcome Safari ITP restrictions. + */ + idEndpoint?: string; + + /** + * What to do with errors. It can log it, rethrow or run a custom handler. Default value: "log" + */ + errorPolicy?: ErrorHandler | "rethrow" | "log"; + + privacy?: { + dontSend?: boolean; + disableUserIds?: boolean; + ipPolicy?: "keep" | "remove" | "stripLastOctet"; + consentCategories?: Record; + }; +}; + +export type DynamicJitsuOptions = Pick; + +export type Traits = { + [key: string]: JSONValue; + //some traits, all starting with $, are reserved for signalling the behavior of the event + + /** + * Tells Jitsu to process event, but not send it to server. Used for .identify() and .group() calls + * that are intended to update user/group state, but not send events + */ + $doNotSend?: boolean; +}; + export interface AnalyticsInterface { track( eventName: string | JSONObject, @@ -174,17 +461,27 @@ export interface AnalyticsInterface { group( groupId?: ID | object, - traits?: JSONObject | null, + traits?: Traits | null, options?: Options, callback?: Callback ): Promise; - identify(id?: ID | object, traits?: JSONObject | Callback | null, callback?: Callback): Promise; + identify(id?: ID | Traits, traits?: Traits | Callback | null, callback?: Callback): Promise; reset(callback?: (...params: any[]) => any): Promise; user(): any; + setAnonymousId(id: string | undefined): void; + + setContextProperty(name: string, value: JSONValue): void; + + getContextProperty(name: string): JSONValue; + + configure(options: DynamicJitsuOptions): void; + + getConfiguration(): JitsuOptions; + // alias( // to: string | number, // from?: string | number | Options, @@ -200,3 +497,5 @@ export interface AnalyticsInterface { // callback?: Callback // ): Promise; } + +export declare function jitsuAnalytics(opts: JitsuOptions): AnalyticsInterface; diff --git a/types/protocols/async-request.d.ts b/types/protocols/async-request.d.ts index b7b0e9f29..40c5be897 100644 --- a/types/protocols/async-request.d.ts +++ b/types/protocols/async-request.d.ts @@ -1,18 +1,24 @@ import { ISO8601Date } from "./iso8601"; +import type { Geo } from "./analytics"; export type IngestType = "s2s" | "browser"; export type IngestMessage = { + geo?: Geo; ingestType: IngestType; messageCreated: ISO8601Date; writeKey: string; messageId: string; + //currently this not being filled connectionId: string; type: string; origin: { baseUrl: string; slug?: string; + sourceId?: string; + sourceName?: string; domain?: string; + classic?: boolean; }; httpHeaders: Record; httpPayload: any; diff --git a/types/protocols/functions.d.ts b/types/protocols/functions.d.ts index 5b424d13e..735bff6a0 100644 --- a/types/protocols/functions.d.ts +++ b/types/protocols/functions.d.ts @@ -1,22 +1,40 @@ -import { AnalyticsServerEvent } from "./analytics"; -import { RequestOptions } from "http"; +import { AnalyticsServerEvent, Geo } from "./analytics"; -export type SetOpts = { ttlMs?: number } | { ttlSec?: number }; +/** + * Store set options: ttl in seconds or string representation of duration (e.g. "1d") + */ +export type SetOpts = number | string | { ttl: number }; /** * A key value store that exposed to a function */ -interface Store { +export interface Store { get(key: string): Promise; + // getWithMeta(key: string): Promise<{ + // value: any; + // lastUpdated: Date + // initialTTLSeconds: number + // leftTTLSeconds: number + // } | undefined>; del(key: string): Promise; set(key: string, value: any, opts?: SetOpts): Promise; + ttl(key: string): Promise; } -/** - * Store for incoming events, destination results and function log messages - */ -interface EventsStore { - log(error: boolean, msg: Record): Promise; +export interface FunctionMetrics { + counter(name: string): { + //increment / decrement counter. Supports negative values + inc: (value: number) => void; + }; + gauge(name: string): { + set: (value: number) => void; + //increment / decrement counter. Supports negative values + inc: (value: number) => void; + }; +} + +export interface TTLStore extends Store { + getWithTTL(key: string): Promise<{ value: any; ttl: number } | undefined>; } /** @@ -33,28 +51,54 @@ export type EventControlOpts = { export type AnyEvent = Record & EventControlOpts; export type AnyProps = Record; -export type FetchResponse = { - status: number; - statusText: string; - text: () => Promise; - json: () => Promise; -}; +export type FetchResponse = Response; + +export type FetchType = (url: string, opts?: FetchOpts, extras?: any) => Promise; export type FetchOpts = { method?: string; headers?: Record; - agent?: RequestOptions["agent"] | ((parsedUrl: URL) => RequestOptions["agent"]); - body?: string; + body?: string | Buffer; }; -export type FunctionContext = { - log: { - info: (message: string, ...args: any[]) => void; - warn: (message: string, ...args: any[]) => void; - debug: (message: string, ...args: any[]) => void; - error: (message: string, ...args: any[]) => void; - }; - fetch: (url: string, opts?: FetchOpts, logToRedis?: boolean) => Promise; - store: Store; +export type FunctionLogger = { + info: (message: string, ...args: any[]) => void | Promise; + warn: (message: string, ...args: any[]) => void | Promise; + debug: (message: string, ...args: any[]) => void | Promise; + error: (message: string, ...args: any[]) => void | Promise; +}; + +interface Warehouse { + query: (sql: string, params?: Record) => Promise; +} + +export type FunctionContext

= { + log: FunctionLogger; + fetch: FetchType; + store: TTLStore; + getWarehouse: (destinationId: string) => Warehouse; + metrics?: FunctionMetrics; + props: P; +}; + +export type PrivacyOpts = { + /** + * IP address processing mode + * "keep" means that the IP address will be kept as is + * "reduce" means that the IP address will be reduced to the first 3 octets + * "hash" means that the IP address will be hashed with SHA256 (not implemented yet) + * + * "reduce" is the default behavior + */ + ip?: "reduce" | "keep" | "hash"; + /** + * Replaces cookie based anonymous ID with a fingerprint hash + * "hash1" means that anyonymous ID will be replaced with hash(ip + user_agent) + */ + anonymousId?: "hash1" | "keep" | ((opts: { ip: string; ip3octets: string; userAgent: string }) => string); +}; + +export type CoreLib = { + privacy(event: AnalyticsServerEvent, opts?: PrivacyOpts): AnalyticsServerEvent; }; export type AnonymousEventsStore = { @@ -62,16 +106,40 @@ export type AnonymousEventsStore = { evictEvents(collectionName: string, anonymousId: string): Promise; }; -/** - * Function execution context available for builin functions only - */ -export type SystemContext = { - $system: { - anonymousEventsStore: AnonymousEventsStore; +export type UserAgent = { + browser: { + name: string; + version: string; + major: string; //@deprecated + }; + engine: { + name: string; + version: string; }; + os: { + name: string; + version: string; + }; + device: { + model: string; + type: "console" | "mobile" | "tablet" | "smarttv" | "wearable" | "embedded" | "desktop"; + vendor: string; + }; + cpu: { + architecture: string; + }; + bot?: boolean; }; export type EventContext = { + /** + * Geo data of incoming request + */ + geo?: Geo; + /** + * Parsed User Agent of incoming request + */ + ua?: UserAgent; /** * Headers of incoming request */ @@ -79,40 +147,63 @@ export type EventContext = { /** * Source of the incoming event */ - source?: { + source: { id: string; name?: string; + //if request was sent from browser or server-to-server api + type: "browser" | "s2s"; domain?: string; }; - destination?: { + destination: { id: string; type: string; - updatedAt: Date; + updatedAt?: Date; hash: string; }; - connection?: { + connection: { id: string; mode?: string; options?: any; }; -}; - -export type FunctionConfigContext

= { - props: P; + // available in Profile Builder transformations + allConnections?: { + id: string; + destinationId: string; + destinationName: string; + type: string; + mode: string; + }[]; + workspace: { + id: string; + }; + // original receivedAt as it stamped by the ingest service; + receivedAt: Date; + // number of retries attempted + retries?: number; }; /** * Parameters for a function */ -export type FullContext

= EventContext & - FunctionContext & - FunctionConfigContext

& - (SystemContext | {}); +export type FullContext

= EventContext & FunctionContext

; //equivalent to returning [] from a function export type FunctionCommand = "drop"; -export type FuncReturn = AnyEvent[] | AnyEvent | null | undefined | FunctionCommand; +export type FuncReturn = AnyEvent[] | AnyEvent | null | undefined | FunctionCommand | false; + +export type SyncFunction< + Dest extends AnyProps = AnyProps, + Cred extends AnyProps = AnyProps, + SyncProps extends AnyProps = AnyProps +> = (p: { + source: { package: string; credentials: Cred; syncProps: SyncProps }; + destination: Dest; + ctx: { + log: FunctionLogger; + store: Store; + }; +}) => Promise; export interface JitsuFunction { (event: E, ctx: FullContext

): Promise | FuncReturn; diff --git a/types/protocols/profile.d.ts b/types/protocols/profile.d.ts new file mode 100644 index 000000000..ab41e82a7 --- /dev/null +++ b/types/protocols/profile.d.ts @@ -0,0 +1,29 @@ +import { FunctionContext } from "./functions"; +import { AnalyticsServerEvent } from "./analytics"; + +export type ProfileResult = { + profileId?: string; + destinationId?: string; + tableName?: string; + traits: Record; +}; + +export type ProfileUser = { + profileId: string; + userId?: string; + anonymousId?: string; + traits: Record; +}; + +export type ProfileBuilderContext = { + profileBuilder: { + id: string; + version: number; + }; +}; + +export type ProfileFunction = ( + events: Iterable, + user: ProfileUser, + context: FunctionContext & ProfileBuilderContext +) => Promise; diff --git a/types/protocols/tsconfig.json b/types/protocols/tsconfig.json new file mode 100644 index 000000000..378b3b9c1 --- /dev/null +++ b/types/protocols/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../../tsconfig.base.json", +} \ No newline at end of file diff --git a/vercel.json b/vercel.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/vercel.json @@ -0,0 +1 @@ +{} diff --git a/webapps/console/.eslintrc.json b/webapps/console/.eslintrc.json index dd59aa201..97f62cea1 100644 --- a/webapps/console/.eslintrc.json +++ b/webapps/console/.eslintrc.json @@ -6,6 +6,7 @@ "rules": { "no-unused-vars": "off", "unused-imports/no-unused-imports": "error", + "react/jsx-curly-brace-presence": ["off", { "props": "never" }], // "unused-imports/no-unused-vars": [ // "warn", // { diff --git a/webapps/console/.gitignore b/webapps/console/.gitignore index afa9deac1..b3622ba80 100644 --- a/webapps/console/.gitignore +++ b/webapps/console/.gitignore @@ -1,2 +1,4 @@ /prisma/schema/ /.next/ + +*storybook.log diff --git a/webapps/console/.storybook/main.ts b/webapps/console/.storybook/main.ts new file mode 100644 index 000000000..21818d3dd --- /dev/null +++ b/webapps/console/.storybook/main.ts @@ -0,0 +1,27 @@ +import type { StorybookConfig } from "@storybook/nextjs"; + +import { join, dirname } from "path"; + +/** + * This function is used to resolve the absolute path of a package. + * It is needed in projects that use Yarn PnP or are set up within a monorepo. + */ +function getAbsolutePath(value: string): any { + return dirname(require.resolve(join(value, "package.json"))); +} +const config: StorybookConfig = { + stories: ["../components/**/*.stories.@(js|jsx|mjs|ts|tsx)"], + addons: [ + getAbsolutePath("@storybook/addon-onboarding"), + getAbsolutePath("@storybook/addon-links"), + getAbsolutePath("@storybook/addon-essentials"), + getAbsolutePath("@chromatic-com/storybook"), + getAbsolutePath("@storybook/addon-interactions"), + ], + framework: { + name: getAbsolutePath("@storybook/nextjs"), + options: {}, + }, + staticDirs: ["../public"], +}; +export default config; diff --git a/webapps/console/.storybook/preview.ts b/webapps/console/.storybook/preview.ts new file mode 100644 index 000000000..183008745 --- /dev/null +++ b/webapps/console/.storybook/preview.ts @@ -0,0 +1,16 @@ +import "../styles/globals.css"; + +import type { Preview } from "@storybook/react"; + +const preview: Preview = { + parameters: { + controls: { + matchers: { + color: /(background|color)$/i, + date: /Date$/i, + }, + }, + }, +}; + +export default preview; diff --git a/webapps/console/components/AntdTheme/AntdTheme.tsx b/webapps/console/components/AntdTheme/AntdTheme.tsx index 9efa5e23b..2f512fdc4 100644 --- a/webapps/console/components/AntdTheme/AntdTheme.tsx +++ b/webapps/console/components/AntdTheme/AntdTheme.tsx @@ -9,7 +9,16 @@ export const AntdTheme: React.FC> = ({ children }) => { colorPrimary: theme.primary, }; return ( - + {children} ); diff --git a/webapps/console/components/ApiKeyEditor/ApiKeyEditor.tsx b/webapps/console/components/ApiKeyEditor/ApiKeyEditor.tsx index 73400810a..c0b09fcc4 100644 --- a/webapps/console/components/ApiKeyEditor/ApiKeyEditor.tsx +++ b/webapps/console/components/ApiKeyEditor/ApiKeyEditor.tsx @@ -1,11 +1,16 @@ -import { CustomWidgetProps } from "../ConfigObjectEditor/ConfigEditor"; import { ApiKey } from "../../lib/schema"; import { useState } from "react"; -import { Button, Tooltip } from "antd"; +import { Button, Table, Tooltip } from "antd"; import { branding } from "../../lib/branding"; -import { confirmOp, copyTextToClipboard } from "../../lib/ui"; +import { confirmOp, copyTextToClipboard, feedbackSuccess } from "../../lib/ui"; import { FaCopy, FaPlus, FaTrash } from "react-icons/fa"; import { randomId } from "juava"; +import { WidgetProps } from "@rjsf/utils"; +import { getConfigApi } from "../../lib/useApi"; +import { useWorkspace } from "../../lib/context"; +import { RefreshCw } from "lucide-react"; +import { CustomWidgetProps } from "../ConfigObjectEditor/Editors"; +import { JitsuButton } from "../JitsuButton/JitsuButton"; const CopyToClipboard: React.FC<{ text: string }> = ({ text }) => { const [copied, setCopied] = useState(false); @@ -29,76 +34,121 @@ function hint(key: string) { return key.substring(0, 3) + "*" + key.substring(key.length - 3); } -export const ApiKeysEditor: React.FC> = props => { +export const ApiKeysEditor: React.FC & { compact?: boolean }> = props => { const [keys, setKeys] = useState(props.value || []); - return ( -

- {keys.length === 0 &&
API keys list us empty
} - {keys.find(key => !!key.plaintext) && ( -
- Congrats! You're generated a new key(s). Copy it an keep in the safe place. You will not be able to see it - again once you leave the page -
- )} - {keys.map(key => ( -
-
- {key.plaintext ? ( -
- - {" "} - Key generated! Copy this key and store it in a safe place. You will not be able - to see it again. - - } - > - - {key.id}:{key.plaintext} - - - -
- ) : ( - <> - - {branding.productName} doesn't store full version of a ket. If you haven't recorded the key, - generate a new one - - } - > - - {key.id}:{key?.hint?.replace("*", "*".repeat(32 - 6))} - - - - )} -
-
- + + {key.id}:{key.plaintext} + + +
+ ) : ( + <> + + {branding.productName} doesn't store full version of a key. If you haven't recorded the key, generate + a new one + + } + > + + {key.id}:{key?.hint?.replace("*", "*".repeat(32 - 6))} + + + + ); + }, + }, + ]; + if (!props.compact) { + columns.push( + { + title:
Created
, + className: "text-right text-xs text-text whitespace-nowrap", + dataIndex: "createdAt", + render: createdAt => { + return
{createdAt ? new Date(createdAt).toLocaleString() : ""}
; + }, + }, + { + title:
Last Used
, + className: "text-right text-xs text-text whitespace-nowrap", + dataIndex: "lastUsed", + render: lastUsed => { + return lastUsed ? new Date(lastUsed).toLocaleString() : "Never"; + }, + } + ); + } + columns.push({ + title: "", + className: "text-right", + key: "actions", + render: (key: ApiKey) => { + return ( +
+
- ))} -
+ ); + }, + }); + return ( +
+ {keys.length === 0 &&
Keys list is empty
} + {(keys.length > 0 || !props.compact) && ( + k.id} + /> + )} +
+ {keys.find(key => !!key.plaintext) ? ( +
+ Congrats! You're generated a new key(s). Copy it an keep in the safe place. +
+ You will not be able to see it again once you leave the page +
+ ) : ( +
+ )}
); }; + +export const StreamKeysEditor: React.FC> = props => { + const context = props.formContext; + const workspace = useWorkspace(); + const type = context?.type; + const [loading, setLoading] = useState(false); + const [keys, setKeys] = useState(props.value || []); + const columns: any[] = [ + { + title: "Key", + key: "id", + width: "90%", + render: (key: ApiKey) => { + return key.plaintext && key.id !== "Generating key" ? ( +
+ + {" "} + Key generated! Copy this key and store it in a safe place. You will not be able to + see it again. + + } + > + + {key.id}:{key.plaintext} + + + +
+ ) : ( + <> + + {branding.productName} doesn't store full version of a key. If you haven't recorded the key, generate + a new one + + } + > + + {key.id}:{key?.hint?.replace("*", "*".repeat(32 - 6))} + + + + ); + }, + }, + ]; + columns.push({ + title: "", + className: "text-right", + key: "actions", + render: (key: ApiKey) => { + if (key.id === "Generating key") { + return <>; + } + return ( +
+ { + if ( + key.plaintext || + (await confirmOp("Are you sure you want to delete this API key? You won't be able to recover it")) + ) { + const newVal = keys.filter(k => k.id !== key.id); + setKeys(newVal); + props.onChange(newVal); + } + }} + > + + +
+ ); + }, + }); + return ( +
+ {keys.length === 0 && !loading &&
Keys list is empty
} + {(keys.length > 0 || loading) && ( +
k.id} + /> + )} +
+ {keys.find(key => !!key.plaintext) ? ( +
+ Congrats! You're generated a new key(s). Copy it an keep in the safe place. +
+ You will not be able to see it again once you leave the page +
+ ) : ( +
+ )} + { + try { + const newKey = randomId(32); + const newVal = [...keys, { id: randomId(32), plaintext: newKey, hint: hint(newKey) }]; + if (type === "stream" && !context.isNew) { + setLoading(true); + await getConfigApi(workspace.id, "stream").update(context.object.id, { + [props.name]: newVal, + }); + setKeys(newVal); + props.onChange(newVal); + feedbackSuccess("Write Key Saved"); + } else { + setKeys(newVal); + props.onChange(newVal); + } + } finally { + setLoading(false); + } + }} + > + {loading ? : } + +
+ + ); +}; diff --git a/webapps/console/components/BackButton/BackButton.tsx b/webapps/console/components/BackButton/BackButton.tsx new file mode 100644 index 000000000..869be6b74 --- /dev/null +++ b/webapps/console/components/BackButton/BackButton.tsx @@ -0,0 +1,37 @@ +import React, { FC } from "react"; +import { ChevronLeft } from "lucide-react"; +import { JitsuButton } from "../JitsuButton/JitsuButton"; +import { useRouter } from "next/router"; +import { usePreviousRoute } from "../../lib/previous-route"; + +type BackButtonProps = { + href?: string; + onClick?: () => void; + useHistory?: boolean; +}; + +export const BackButton: FC = ({ href, onClick, useHistory }) => { + const router = useRouter(); + const previousRoute = usePreviousRoute(); + const canUseHistory = !!useHistory && !!previousRoute; + + if (!href && !onClick && !canUseHistory) { + return <>; + } + return ( + } + type="link" + size="small" + onClick={() => { + if (canUseHistory) { + router.push(previousRoute!); + return; + } + onClick ? onClick() : router.push(href!); + }} + > + Back + + ); +}; diff --git a/webapps/console/components/Billing/BillingBlockingDialog.tsx b/webapps/console/components/Billing/BillingBlockingDialog.tsx index 737fb4547..9f9590034 100644 --- a/webapps/console/components/Billing/BillingBlockingDialog.tsx +++ b/webapps/console/components/Billing/BillingBlockingDialog.tsx @@ -1,20 +1,28 @@ -import { useBilling } from "./BillingProvider"; +import { useBilling, UseBillingResult } from "./BillingProvider"; import { assertDefined, assertFalse, assertTrue, getLog } from "juava"; -import { useUsage } from "./use-usage"; +import { useEventsUsage } from "./use-events-usage"; import { Modal } from "antd"; import { upgradeRequired } from "./copy"; import { AlertTriangle, ArrowRight } from "lucide-react"; import { WJitsuButton } from "../JitsuButton/JitsuButton"; +import { useWorkspace, WorkspaceContext } from "../../lib/context"; +import { useApi } from "../../lib/useApi"; +import { useState } from "react"; const log = getLog("billing"); function LoadAndBlockIfNeed() { const billing = useBilling(); + const { data } = useApi(`/api/user/properties`); + const [showModal, setShowModal] = useState(true); assertTrue(billing.enabled); assertFalse(billing.loading); - - const { isLoading, error, usage } = useUsage(); + const { isLoading, error, usage } = useEventsUsage({ skipSubscribed: true }); + if (!billing.settings?.pastDue && billing.settings?.planId !== "free") { + //paid project with no past due subscriptions, we never block those + return <>; + } if (isLoading) { return <>; } else if (error) { @@ -22,17 +30,53 @@ function LoadAndBlockIfNeed() { return <>; } assertDefined(usage); - if (usage.usagePercentage > 1) { + if (billing.settings?.pastDue && billing.settings?.planKind !== "enterprise") { + return ( + + +

Your subscription is past-due.

+ + } + closable={!!data?.admin} + onCancel={() => setShowModal(false)} + maskClosable={false} + footer={ +
+ } + > + Manage Billing {"&"} Plan + +
+ } + > +
+ You have unpaid invoices. Please arrange the payment as soon as possible to avoid service interruption +
+
+ ); + } else if (usage.usagePercentage > 1) { return (

Upgrade required

} - closable={false} + closable={!!data?.admin} + onCancel={() => setShowModal(false)} + maskClosable={false} footer={
{ const billing = useBilling(); - if (!billing.enabled || billing.loading || billing.settings.planId !== "free") { + const workspace = useWorkspace(); + if (neverBlock(billing, workspace)) { return <>; } else { return ; diff --git a/webapps/console/components/Billing/BillingManager.tsx b/webapps/console/components/Billing/BillingManager.tsx index 4de6b498e..96a2b5ff7 100644 --- a/webapps/console/components/Billing/BillingManager.tsx +++ b/webapps/console/components/Billing/BillingManager.tsx @@ -5,14 +5,15 @@ import { assertDefined, assertFalse, assertTrue, requireDefined, rpc } from "jua import { BillingSettings } from "../../lib/schema"; import { Alert, Button, Progress, Skeleton, Tooltip } from "antd"; import Link from "next/link"; -import { Check, Edit2, Info, XCircle } from "lucide-react"; +import { Check, ChevronRight, Edit2, ExternalLink, Info, XCircle } from "lucide-react"; import styles from "./BillingManager.module.css"; import { useQuery } from "@tanstack/react-query"; import { ErrorCard } from "../GlobalError/GlobalError"; -import { useUsage } from "./use-usage"; +import { useEventsUsage } from "./use-events-usage"; import { upgradeRequired } from "./copy"; import { JitsuButton } from "../JitsuButton/JitsuButton"; +import dayjs from "dayjs"; function formatNumber(n: number) { return n.toLocaleString("en-US", { maximumFractionDigits: 0 }); @@ -57,12 +58,13 @@ const ComparisonSection: React.FC<{ ); }; -const UsageSection: React.FC<{}> = () => { +const EventsUsageSection: React.FC<{}> = () => { const billing = useBilling(); + const workspace = useWorkspace(); assertTrue(billing.enabled); assertFalse(billing.loading, "Billing must be loaded before using UsageSection component"); - const { isLoading, error, usage } = useUsage(); + const { isLoading, error, usage, throttle } = useEventsUsage(); if (isLoading) { return ; @@ -72,16 +74,11 @@ const UsageSection: React.FC<{}> = () => { assertDefined(usage, "Data should be defined"); - const startStr = usage.periodStart.toLocaleString("en-Us", { - month: "short", - day: "numeric", - year: "numeric", - }); - const endStr = usage.periodEnd.toLocaleString("en-Us", { - month: "short", - day: "numeric", - year: "numeric", - }); + const usageExceeded = usage.usagePercentage > 1 && billing.settings.planId === "free"; + const usageIsAboutToExceed = + usage?.projectionByTheEndOfPeriod && + usage?.projectionByTheEndOfPeriod > usage?.maxAllowedDestinatonEvents && + billing.settings.planId == "free"; return (
= () => { showInfo={false} status={usage.usagePercentage > 1 ? "exception" : undefined} /> -
- {formatNumber(Math.round(usage.destinationEvents))} / {formatNumber(usage.maxAllowedDestinatonEvents)}{" "} - destination events used from {startStr} to {endStr}. The quota will be reset on {endStr}. +
+
+ {formatNumber(Math.round(usage?.events))} / {formatNumber(usage.maxAllowedDestinatonEvents)} destination + events used from {dayjs(usage.periodStart).utc().format("MMM DD, YYYY")} to{" "} + {dayjs(usage.periodEnd).utc().format("MMM DD, YYYY")}. The quota will be reset on{" "} + {dayjs(usage.periodEnd).add(1, "day").utc().format("MMM DD")}. +
+ {billing?.settings?.overagePricePer100k && ( +
+ Overage fee: ${billing.settings.overagePricePer100k * 10} per 1,000,000 events +
+ )} +
+ + View detailed stat + +
- {usage.usagePercentage > 1 && billing.settings.planId === "free" && ( + + {billing.settings?.pastDue && ( +
+ You have unpaid invoices!} + description={ +
+ Please{" "} + + + update your payment method and pay outstanding invoices + + + {" "} + to avoid service interruption +
+ } + type="error" + showIcon + /> +
+ )} + {usageExceeded && (
Upgrade your plan to keep using Jitsu} @@ -103,21 +145,65 @@ const UsageSection: React.FC<{}> = () => { />
)} - {usage.usagePercentage > 1 && billing.settings.planId !== "free" && ( + {throttle && ( +
+ Throttling warning} + description={ +
+ You have repeatedly exceeded your monthly events destination limit, so you're incoming events are + throttled at rate of {throttle}% events per second. It means that only {100 - throttle}%{" "} + of incoming events are processed. Please upgrade your plan to restore the full processing capacity. +
+ } + type="error" + showIcon + /> +
+ )} + {usageIsAboutToExceed && !usageExceeded && !throttle ? ( +
+ Account quota warning!} + showIcon + type={"warning"} + description={ + <> + You are projected to exceed your monthly events destination limit by{" "} + {formatNumber((usage?.projectionByTheEndOfPeriod || 0) - usage?.maxAllowedDestinatonEvents)}{" "} + events. Please upgrade your plan to avoid service disruption. + + } + /> +
+ ) : undefined} + {usage.usagePercentage > 1 && billing.settings.planId !== "free" && !throttle && (
Overage fee warning} description={
You have exceeded your monthly events destination limit by{" "} - {formatNumber(usage.destinationEvents - usage.maxAllowedDestinatonEvents)}. The overage fee of $ + {formatNumber(usage.events - usage.maxAllowedDestinatonEvents)}. The overage fee of at least $ {( - ((usage.destinationEvents - usage.maxAllowedDestinatonEvents) / 100_000) * + ((usage.events - usage.maxAllowedDestinatonEvents) / 100_000) * (billing.settings?.overagePricePer100k || 0) ).toLocaleString("en-us", { maximumFractionDigits: 2 })} {" "} - will be added to your next invoice + will be added to your next invoice.{" "} + {usage.projectionByTheEndOfPeriod && ( + <> + The projected overage fee by end of the month is{" "} + + $ + {( + ((usage?.projectionByTheEndOfPeriod - usage.maxAllowedDestinatonEvents) / 100_000) * + (billing.settings?.overagePricePer100k || 0) + ).toLocaleString("en-us", { maximumFractionDigits: 2 })} + {" "} + + )}
} type="info" @@ -129,6 +215,78 @@ const UsageSection: React.FC<{}> = () => { ); }; +const ConnectorUsageSection: React.FC<{}> = () => { + const billing = useBilling(); + assertTrue(billing.enabled, "Billing is not enabled"); + assertFalse(billing.loading, "Billing must be loaded before using CurrentSubscription component"); + const workspace = useWorkspace(); + const user = useUser(); + let periodStart: Date; + let periodEnd: Date; + if (billing.settings?.currentPeriod) { + periodEnd = new Date(billing.settings?.currentPeriod.end); + periodStart = new Date(billing.settings?.currentPeriod.start); + } else { + periodStart = dayjs().utc().startOf("month").toDate(); + periodEnd = dayjs().utc().endOf("month").add(-1, "millisecond").toDate(); + } + const { isLoading, error, data } = useQuery( + ["connector usage", workspace.id], + async () => { + const report = await rpc( + `/api/${workspace.id}/reports/sync-stat?start=${periodStart.toISOString()}&end=${dayjs(periodEnd) + .subtract(1, "millisecond") + .toISOString()}` + ); + return report; + }, + { retry: false, cacheTime: 0, staleTime: 0 } + ); + + if (isLoading) { + return ; + } else if (error) { + return ; + } + + const activeSyncs = data.activeSyncs; + const maxActiveSyncs = billing.settings.dailyActiveSyncs || 1; + const percentage = activeSyncs / maxActiveSyncs; + + return ( +
+ 1 ? "exception" : undefined} /> +
+
+ {activeSyncs} / {maxActiveSyncs} monthly active syncs from{" "} + {dayjs(periodStart).utc().format("MMM DD, YYYY")} to{" "} + {dayjs(periodEnd).utc().format("MMM DD, YYYY")}. The quota will be reset on{" "} + {dayjs(periodEnd).add(1, "day").utc().format("MMM DD")}. + {billing?.settings?.dailyActiveSyncsOverage && ( +
+ Overage fee: ${billing?.settings?.dailyActiveSyncsOverage} per extra daily active sync +
+ )} +
+
+ {percentage > 1 && ( +
+ Overage fee warning} + description={ + <> + Overage fee of at least $ + {(billing.settings.dailyActiveSyncsOverage || 0) * (activeSyncs - maxActiveSyncs)} will be added + to your next invoice. + + } + /> +
+ )} +
+ ); +}; + const CurrentSubscription: React.FC<{}> = () => { const billing = useBilling(); assertTrue(billing.enabled, "Billing is not enabled"); @@ -140,42 +298,56 @@ const CurrentSubscription: React.FC<{}> = () => {
- {(billing.settings.planName || billing.settings.planId).toUpperCase()} + {billing.settings?.customBilling + ? "JITSU SUBSCRIPTION" + : (billing.settings.planName || billing.settings.planId).toUpperCase()}
- {billing.settings.planId !== "free" && ( + {billing.settings.planId !== "free" && !billing.settings?.customBilling && ( - Manage subscription + Manage subscription / download invoices )} + {billing.settings?.futureSubscriptionDate && ( +
+ Your paid subscription starts on{" "} + {new Intl.DateTimeFormat("en-US", { + month: "long", + year: "numeric", + day: "numeric", + }).format(new Date(billing.settings?.futureSubscriptionDate))} +
+ )}
{billing.settings.planId !== "free" && ( -
+
{billing.settings?.renewAfterExpiration ? (
Renews at
) : (
Cancels at
)}
- {new Date(billing.settings?.expiresAt as string).toLocaleString("en-Us", { - month: "long", - day: "numeric", - year: "numeric", - })} + {dayjs(billing.settings?.expiresAt as string).format("MMMM DD, YYYY")}
)}
-

Usage

- +

Events Usage

+ + {billing.settings?.planId !== "free" && ( + <> +

Connectors Usage

+ + + )}
); }; @@ -192,15 +364,21 @@ const AvailablePlans: React.FC<{}> = () => { const { isLoading, error, data } = useQuery( ["availablePlans", workspace.id], async () => { + if (billing.settings?.isLegacyPlan) { + return { + plans: {}, + }; + } const plans = await rpc(`/api/${workspace.id}/ee/billing/plans`); assertDefined(billing.settings.planId, `planId is not defined in ${JSON.stringify(billing.settings)}`); - console.log("plans.products", plans.products); return { plans: { free: { ...BillingSettings.parse({}), monthlyPrice: 0, annualPrice: 0 }, ...plans.products - .filter(p => !p.data.disabled) + //the end-point shouldn't return custom plans anyway, + //but let's double-check that the plan is not custom + .filter(p => !p.data.disabled && !p.data.custom) .reduce( (acc, p) => ({ ...acc, @@ -229,13 +407,25 @@ const AvailablePlans: React.FC<{}> = () => { }, { cacheTime: 0, retry: false } ); + //if (billing.settings?.customBilling) if (isLoading) { return ; } else if (error) { return ; } assertDefined(data, "Data is not defined"); - + if (billing.settings?.isLegacyPlan) { + return ( +
+
+ You're using a legacy plan. To upgrade to a new plan or cancel your subscription, please reach out to our{" "} + + Support Team + +
+
+ ); + } return (
{Object.entries(data.plans).map(([planId, plan]) => ( @@ -280,7 +470,7 @@ const AvailablePlans: React.FC<{}> = () => { />
{planId === billing.settings.planId ? ( - } className="w-full" size="large" type="ghost" disabled={true}> + } className="w-full" size="large" ghost disabled={true}> Current plan ) : planId === "free" ? ( @@ -324,17 +514,31 @@ const AvailablePlans: React.FC<{}> = () => { const BillingManager0: React.FC<{}> = () => { const appConfig = useAppConfig(); + const billing = useBilling(); + return (
-

Available Plans

- -

- Need more information? Learn more about each plan by checking out our{" "} - - pricing page - -

+ {billing.settings?.customBilling || billing.settings?.custom ? ( +
+ You're using a custom plan. To downgrade to standard plan or cancel your supbscription, please reach out to + our{" "} + + Support Team + +
+ ) : ( + <> +

Available Plans

+ +

+ Need more information? Learn more about each plan by checking out our{" "} + + pricing page + +

+ + )}
); }; diff --git a/webapps/console/components/Billing/BillingProvider.tsx b/webapps/console/components/Billing/BillingProvider.tsx index e93c05e8b..b6a4dedcc 100644 --- a/webapps/console/components/Billing/BillingProvider.tsx +++ b/webapps/console/components/Billing/BillingProvider.tsx @@ -7,10 +7,12 @@ import { useJitsu } from "@jitsu/jitsu-react"; export const BillingContext = createContext(null); const log = getLog(`BillingProvider`); -export function useBilling(): +export type UseBillingResult = | { loading: true; enabled: true; settings?: never } - | { loading: false; enabled: false; settings?: never } - | { loading: false; enabled: true; settings: BillingSettings } { + | { loading: false; enabled: false; settings?: never; error?: { message: string } } + | { loading: false; enabled: true; settings: BillingSettings }; + +export function useBilling(): UseBillingResult { const ctx = useContext(BillingContext); const appConfig = useAppConfig(); if (!appConfig.billingEnabled) { @@ -87,7 +89,7 @@ export const BillingProvider: React.FC{children}; } else if (error) { - log.atError().withCause(error).log("Can't connect to billing server. Billing is disabled"); + log.atDebug().withCause(error).log("Can't connect to billing server. Billing is disabled"); return {children}; } else if (billingSettings) { return {children}; diff --git a/webapps/console/components/Billing/UpgradeDialog.tsx b/webapps/console/components/Billing/UpgradeDialog.tsx index 32c721763..52248de95 100644 --- a/webapps/console/components/Billing/UpgradeDialog.tsx +++ b/webapps/console/components/Billing/UpgradeDialog.tsx @@ -36,7 +36,8 @@ export const UpgradeDialog: React.FC<{ featureDescription: string; availableInPl description={
- You are currently subscribed to a {billing.settings.planId} plan. To use{" "} + You are currently subscribed to a{" "} + {billing.settings?.planName || billing.settings.planId} plan. To use{" "} {featureDescription}, please upgrade to a{" "} {availableInPlans ? arrayJoin( diff --git a/webapps/console/components/Billing/use-events-usage.ts b/webapps/console/components/Billing/use-events-usage.ts new file mode 100644 index 000000000..9ee23b08e --- /dev/null +++ b/webapps/console/components/Billing/use-events-usage.ts @@ -0,0 +1,93 @@ +import { useQuery } from "@tanstack/react-query"; +import { assertFalse, assertTrue, rpc } from "juava"; +import { useWorkspace } from "../../lib/context"; +import { useBilling } from "./BillingProvider"; +import dayjs from "dayjs"; +import utc from "dayjs/plugin/utc"; +import { ActiveEventsReport } from "../../lib/shared/reporting"; +dayjs.extend(utc); + +export type Usage = { + events: number; + projectionByTheEndOfPeriod?: number; + maxAllowedDestinatonEvents: number; + usagePercentage: number; + periodStart: Date; + periodEnd: Date; +}; + +export type UseUsageRes = { isLoading: boolean; error?: any; throttle?: number; usage?: Usage }; + +export type DestinationReportDataRow = {}; + +function parseThrottle(val: string): number | undefined { + const match = val.match(/throttle=?(\d+)/); + if (match) { + return parseFloat(match[1]); + } +} + +/** + * @param opts.skipSubscribed - if true, will return bogus data if workspace is subscribed to a paid + * plan. In some cases, we don't really need usage for subscribed workspaces + */ +export function useEventsUsage(opts?: { skipSubscribed?: boolean; cacheSeconds?: number }): UseUsageRes { + const workspace = useWorkspace(); + const billing = useBilling(); + assertTrue(billing.enabled, "Billing is not enabled"); + assertFalse(billing.loading, "Billing must be loaded before using usage hook"); + const cacheSeconds = opts?.cacheSeconds ?? 60 * 5; //5 minutes by default + const throttle = workspace.featuresEnabled + ?.filter(f => f.startsWith("throttle")) + ?.map(parseThrottle) + ?.find(t => t); + + let periodStart: Date; + let periodEnd: Date; + if (billing.settings?.currentPeriod) { + periodEnd = new Date(billing.settings?.currentPeriod.end); + periodStart = new Date(billing.settings?.currentPeriod.start); + } else { + periodStart = dayjs().utc().startOf("month").toDate(); + periodEnd = dayjs().utc().endOf("month").add(-1, "millisecond").toDate(); + } + + const { isLoading, error, data } = useQuery( + ["billing usage", workspace.id, opts?.skipSubscribed, billing.settings.planId], + async () => { + if (opts?.skipSubscribed && billing.settings.planId !== "free") { + //if workspace is subscribed to a paid plan - we don't really need usage in some cases + return { usage: 0 }; + } + const report = (await rpc( + `/api/${workspace.id}/reports/active-events?start=${periodStart.toISOString()}&end=${dayjs(periodEnd) + .subtract(1, "millisecond") + .toISOString()}` + )) as ActiveEventsReport; + const usage = report.totalActiveEvents; + return { usage } as const; + }, + { retry: false, cacheTime: cacheSeconds * 1000, staleTime: cacheSeconds * 1000 } + ); + + const periodDuration = dayjs(new Date()).diff(dayjs(periodStart), "day"); + const projection = + periodDuration > 0 && data + ? (data.usage / periodDuration) * dayjs(periodEnd).diff(dayjs(periodStart), "day") + : undefined; + return { + isLoading, + error, + throttle, + usage: data + ? { + periodStart, + periodEnd, + events: data.usage, + projectionByTheEndOfPeriod: projection, + maxAllowedDestinatonEvents: billing.settings.destinationEvensPerMonth, + usagePercentage: data.usage / billing.settings.destinationEvensPerMonth, + } + : undefined, + }; +} diff --git a/webapps/console/components/Billing/use-usage.ts b/webapps/console/components/Billing/use-usage.ts deleted file mode 100644 index 579570c9f..000000000 --- a/webapps/console/components/Billing/use-usage.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { useQuery } from "@tanstack/react-query"; -import { assertFalse, assertTrue, rpc } from "juava"; -import { useWorkspace } from "../../lib/context"; -import { useBilling } from "./BillingProvider"; - -export type Usage = { - destinationEvents: number; - maxAllowedDestinatonEvents: number; - usagePercentage: number; - periodStart: Date; - periodEnd: Date; -}; - -export type UseUsageRes = { isLoading: boolean; error?: any; usage?: Usage }; - -export function useUsage(): UseUsageRes { - const workspace = useWorkspace(); - const billing = useBilling(); - assertTrue(billing.enabled, "Billing is not enabled"); - assertFalse(billing.loading, "Billing must be loaded before using usage hook"); - - let periodStart: Date | undefined; - let periodEnd: Date | undefined; - if (billing.settings.expiresAt) { - periodEnd = new Date(billing.settings.expiresAt); - periodStart = new Date(billing.settings.expiresAt); - periodStart.setMonth(periodStart.getMonth() - 1); - } else { - periodStart = new Date(); - periodStart.setDate(1); - periodEnd = new Date(new Date().getFullYear(), new Date().getMonth() + 1, 0); - } - - const { isLoading, error, data } = useQuery( - ["billing usage", workspace.id], - async () => { - const report = await rpc(`/api/${workspace.id}/ee/report/destination-stat?start=${periodStart?.toISOString()}`); - const warehouseUsage = report.data.reduce( - (acc, d) => acc + (d.destination_type === "warehouse" ? d.events : 0), - 0 - ); - const destinationUsage = report.data.reduce( - (acc, d) => acc + (d.destination_type === "warehouse" ? 0 : d.events), - 0 - ); - return { warehouseUsage, destinationUsage } as const; - }, - { retry: false, cacheTime: 0 } - ); - - return { - isLoading, - error, - usage: data - ? { - periodStart, - periodEnd, - destinationEvents: data.warehouseUsage + data.destinationUsage, - maxAllowedDestinatonEvents: billing.settings.destinationEvensPerMonth, - usagePercentage: (data.warehouseUsage + data.destinationUsage) / billing.settings.destinationEvensPerMonth, - } - : undefined, - }; -} diff --git a/webapps/console/components/BillingDetails/BillingDetails.tsx b/webapps/console/components/BillingDetails/BillingDetails.tsx new file mode 100644 index 000000000..75ffb2b42 --- /dev/null +++ b/webapps/console/components/BillingDetails/BillingDetails.tsx @@ -0,0 +1,155 @@ +import React, { useEffect } from "react"; +import { Button, DatePicker } from "antd"; +import { AlertTriangle, ArrowLeft, Loader2 } from "lucide-react"; +import { ButtonLabel } from "../ButtonLabel/ButtonLabel"; +import { useWorkspace } from "../../lib/context"; +import dayjs, { Dayjs } from "dayjs"; +import { useQueryStringState } from "../../lib/useQueryStringState"; +import { useQuery } from "@tanstack/react-query"; +import { rpc } from "juava"; +import { ActiveEventsReport } from "../../lib/shared/reporting"; +import { Chart } from "chart.js/auto"; + +export const ChartView: React.FC<{ data: ActiveEventsReport }> = ({ data }) => { + const wrapperRef = React.useRef(null); + useEffect(() => { + if (wrapperRef.current) { + const chart = new Chart(wrapperRef.current.getElementsByTagName("canvas").item(0)!, { + type: "bar", + options: { + //for dev env double animation due to double rendering is just annoying + animation: process.env.NODE_ENV === "development" ? false : undefined, + maintainAspectRatio: false, + plugins: { + legend: { + display: false, + }, + }, + scales: { + x: { + grid: { + display: false, + }, + }, + y: { + grid: { + display: false, + }, + }, + }, + }, + + data: { + labels: data.breakdown.map(row => dayjs(row.period).utc().format("MMM D")), + datasets: [ + { + stack: "main", + backgroundColor: "#009140", + data: data.breakdown.map(row => row.activeEvents), + }, + ], + }, + }); + return () => { + chart.destroy(); + }; + } + }, [data]); + return ( +
+
+

Total Acive Events

+

{data.totalActiveEvents.toLocaleString("en-US")}

+
+ +
+ ); +}; + +export function roundDay(how: "start" | "end", date: Dayjs | Date | string | undefined): Dayjs { + const d = dayjs(date); + if (how === "start") { + return d.utc().startOf("day"); + } else { + return d.utc().endOf("day").add(-1, "millisecond"); + } +} + +export const ChartLoader: React.FC<{ start: Dayjs; end: Dayjs }> = ({ start, end }) => { + const workspace = useWorkspace(); + const { isLoading, error, data } = useQuery( + ["billing usage", workspace.id, start, end], + async () => { + return (await rpc( + `/api/${workspace.id}/reports/active-events?start=${roundDay("start", start).toISOString()}&end=${roundDay( + "end", + end + ).toISOString()}` + )) as ActiveEventsReport; + }, + { retry: false, cacheTime: 0, staleTime: 0 } + ); + if (isLoading) { + return ( +
+ +
Loading...
+
+ ); + } else if (error) { + return ( +
+ +
Error loading data
+
+ ); + } else if (data) { + return ; + } else { + //should never happen + return
Something isn't right
; + } +}; + +export const BillingDetails: React.FC = () => { + const workspace = useWorkspace(); + const [start, setStart] = useQueryStringState("start", { + defaultValue: dayjs().utc().startOf("month"), + parser: val => roundDay("start", val), + serializer: val => roundDay("end", val).toISOString(), + }); + const [end, setEnd] = useQueryStringState("end", { + defaultValue: dayjs().utc().endOf("month").add(-1, "milliseconds"), + parser: val => roundDay("end", val), + serializer: val => roundDay("end", val).toISOString(), + }); + return ( +
+
+

Billing Details

+
+ +
+
+
+
+ { + if (val?.length === 2) { + const [start, end] = val; + setStart(dayjs(start)); + setEnd(dayjs(end)); + } + }} + /> +
+
+
+ +
+
+ ); +}; diff --git a/webapps/console/components/ButtonGroup/ButtonGroup.module.css b/webapps/console/components/ButtonGroup/ButtonGroup.module.css new file mode 100644 index 000000000..2d2527dd0 --- /dev/null +++ b/webapps/console/components/ButtonGroup/ButtonGroup.module.css @@ -0,0 +1,3 @@ +/*.buttonGroup > :global(.ant-btn) {*/ +/* margin-inline-start: 0 !important;*/ +/*}*/ diff --git a/webapps/console/components/ButtonGroup/ButtonGroup.tsx b/webapps/console/components/ButtonGroup/ButtonGroup.tsx new file mode 100644 index 000000000..f9d5a8e12 --- /dev/null +++ b/webapps/console/components/ButtonGroup/ButtonGroup.tsx @@ -0,0 +1,80 @@ +import { Button, Dropdown, MenuProps, Tooltip } from "antd"; +import React from "react"; +import { JitsuButton } from "../JitsuButton/JitsuButton"; +import { BaseButtonProps } from "antd/lib/button/button"; +import styles from "./ButtonGroup.module.css"; +import { useWorkspace, useWorkspaceRole } from "../../lib/context"; +import { MoreHorizontal, MoreVertical } from "lucide-react"; +import Link from "next/link"; +import { hasPermission, WorkspacePermissionsType } from "../../lib/workspace-roles"; + +const AntButtonGroup = Button.Group; + +export type ButtonProps = Omit & { + href?: string; + label?: React.ReactNode; + collapsed?: boolean; + // if label is not a string, tooltip text will be used for Tooltip + tooltip?: string; + onClick?: () => void; + requiredPermission?: WorkspacePermissionsType; +}; + +export type ButtonGroupProps = { + items: ButtonProps[]; + dotsButtonProps?: BaseButtonProps; + dotsOrientation?: "horizontal" | "vertical"; +}; + +export const ButtonGroup: React.FC = ({ items, dotsButtonProps, dotsOrientation = "vertical" }) => { + const w = useWorkspace(); + const workspaceRole = useWorkspaceRole(); + const shownItems = items.filter(item => !item.collapsed); + const dropdownItems: MenuProps["items"] = items + .filter(item => item.collapsed) + .map((item, i) => ({ + label: item.href ? ( + + {item.label} + + ) : ( + item.label + ), + key: i, + icon: item.icon, + disabled: + item.disabled || + (item.requiredPermission ? !hasPermission(workspaceRole.role, item.requiredPermission) : false), + danger: item.danger, + onClick: item.onClick, + })); + + return ( + + {shownItems.map((item, i) => ( + + + + ))} + {dropdownItems.length > 0 && ( + + + ) : ( + + ) + } + onClick={event => { + event.preventDefault(); + event.stopPropagation(); // stop propagation main button + }} + {...dotsButtonProps} + /> + + )} + + ); +}; diff --git a/webapps/console/components/ButtonLabel/ButtonLabel.tsx b/webapps/console/components/ButtonLabel/ButtonLabel.tsx index 1f5a37360..6d9de994f 100644 --- a/webapps/console/components/ButtonLabel/ButtonLabel.tsx +++ b/webapps/console/components/ButtonLabel/ButtonLabel.tsx @@ -1,30 +1,69 @@ import { ReactNode } from "react"; export type ButtonLabelProps = { - icon: ReactNode; + icon?: ReactNode; children?: ReactNode; loading?: boolean; - iconPosition?: "left" | "right"; + iconPosition?: "start" | "end"; className?: string; + iconSize?: "default" | "small"; +}; + +const BouncingDotsLoader = () => { + return ( +
+
+
+
+
+ ); }; export const ButtonLabel: React.FC = ({ children, icon, loading, - iconPosition = "left", + iconPosition = "start", className, + iconSize = "default", }) => { - if (loading) { - return <>{children}; - } + const iconClass = iconSize === "small" ? "scale-75" : ""; return ( -
-
- {iconPosition === "left" && {icon}} - {children && {children}} - {iconPosition === "right" && {icon}} +
+
+ {icon && iconPosition === "start" && !loading && {icon}} + {children && ( + + {children} + + )} + {icon && iconPosition === "end" && !loading && {icon}}
+ {loading && !icon && ( +
+ +
+ )}
); }; diff --git a/webapps/console/components/CodeBlock/CodeBlock.module.css b/webapps/console/components/CodeBlock/CodeBlock.module.css index 5bba7585a..f15400d7d 100644 --- a/webapps/console/components/CodeBlock/CodeBlock.module.css +++ b/webapps/console/components/CodeBlock/CodeBlock.module.css @@ -5,8 +5,3 @@ @apply text-background !important; font-family: var(--font-mono) !important; } - -/* this does not work*/ -.codeBlockLight :global(.hljs) { - background: transparent !important; -} diff --git a/webapps/console/components/CodeBlock/CodeBlock.tsx b/webapps/console/components/CodeBlock/CodeBlock.tsx index eda07853d..118726c4d 100644 --- a/webapps/console/components/CodeBlock/CodeBlock.tsx +++ b/webapps/console/components/CodeBlock/CodeBlock.tsx @@ -7,10 +7,21 @@ import json from "highlight.js/lib/languages/json"; import yaml from "highlight.js/lib/languages/yaml"; import htmlLang from "highlight.js/lib/languages/xml"; import typescript from "highlight.js/lib/languages/typescript"; +import bash from "highlight.js/lib/languages/bash"; +import plaintext from "highlight.js/lib/languages/plaintext"; import styles from "./CodeBlock.module.css"; -export type SupportedLang = "html" | "javascript" | "json" | "yaml" | "typescript" | "tsx" | "jsx"; +export type SupportedLang = + | "html" + | "javascript" + | "json" + | "yaml" + | "typescript" + | "tsx" + | "jsx" + | "bash" + | "plaintext"; const langMap: Record = { html: htmlLang, javascript: javascript, @@ -19,6 +30,8 @@ const langMap: Record = { tsx: typescript, typescript: typescript, yaml: yaml, + bash: bash, + plaintext: plaintext, }; Object.entries(langMap).map(([lang, module]) => hljs.registerLanguage(lang, module)); @@ -29,7 +42,12 @@ import hljs from "highlight.js/lib/common"; import { CopyButton } from "../CopyButton/CopyButton"; import { Copy } from "lucide-react"; -export const CodeBlock: React.FC> = ({ lang, children, className }) => { +export const CodeBlock: React.FC> = ({ + lang, + breaks, + children, + className, +}) => { assertDefined(children, "CodeBlock children must be defined"); const codeRef = useRef(null); useEffect(() => { @@ -44,7 +62,16 @@ export const CodeBlock: React.FC>
-
+      
         {children}
       
diff --git a/webapps/console/components/CodeBlock/CodeBlockLight.tsx b/webapps/console/components/CodeBlock/CodeBlockLight.tsx index 77d55a7e8..4e5f91720 100644 --- a/webapps/console/components/CodeBlock/CodeBlockLight.tsx +++ b/webapps/console/components/CodeBlock/CodeBlockLight.tsx @@ -7,7 +7,6 @@ import json from "highlight.js/lib/languages/json"; import yaml from "highlight.js/lib/languages/yaml"; import htmlLang from "highlight.js/lib/languages/xml"; import typescript from "highlight.js/lib/languages/typescript"; -import styles from "./CodeBlock.module.css"; export type SupportedLang = "html" | "javascript" | "json" | "yaml" | "typescript" | "tsx" | "jsx"; const langMap: Record = { @@ -41,7 +40,8 @@ export const CodeBlockLight: React.FC
         {children}
       
diff --git a/webapps/console/components/CodeEditor/CodeEditor.tsx b/webapps/console/components/CodeEditor/CodeEditor.tsx index 4936a78a1..0cb521b26 100644 --- a/webapps/console/components/CodeEditor/CodeEditor.tsx +++ b/webapps/console/components/CodeEditor/CodeEditor.tsx @@ -1,7 +1,7 @@ import Editor from "@monaco-editor/react"; -import React, { useCallback, useEffect, useRef } from "react"; +import React, { ReactNode, useCallback, useEffect, useRef } from "react"; import { LoadingAnimation } from "../GlobalLoader/GlobalLoader"; -import { debounce } from "lodash"; +import debounce from "lodash/debounce"; import * as monaco from "monaco-editor"; import styles from "./CodeEditor.module.css"; @@ -14,35 +14,68 @@ type CodeEditorProps = { changePosition?: (position: number) => void; ctrlEnterCallback?: (value: string) => void; ctrlSCallback?: (value: string) => void; + foldLevel?: number; + extraSuggestions?: string; + loaderNode?: ReactNode; + autoFit?: boolean; monacoOptions?: Partial; + syntaxCheck?: { + json?: boolean; + }; }; export const CodeEditor: React.FC = ({ language, height, - width = "100%", + width, onChange, value, ctrlEnterCallback, ctrlSCallback, changePosition, monacoOptions, + extraSuggestions, + foldLevel, + loaderNode, + autoFit, + syntaxCheck, }) => { const editorRef = useRef(null); const [mounted, setMounted] = React.useState(false); const handleChange = onChange; - const handleChangePosition = debounce(changePosition ?? (() => {}), 100); + const handleChangePosition = changePosition ? debounce(changePosition, 100) : undefined; const handleEditorDidMount = useCallback( (editor, monaco) => { editorRef.current = editor; - editor.setValue(value); - editor.onDidChangeCursorPosition(e => { - handleChangePosition(editor.getModel().getOffsetAt(e.position)); - }); + if (typeof value !== "undefined") { + editor.setValue(value); + } + if (foldLevel) { + editor.getAction(`editor.foldLevel${foldLevel}`)?.run(); + } + if (extraSuggestions) { + monaco.languages.typescript.javascriptDefaults.setExtraLibs([{ content: extraSuggestions }]); + } + if (syntaxCheck && typeof syntaxCheck.json !== "undefined") { + monaco.languages.json.jsonDefaults.setDiagnosticsOptions({ + validate: syntaxCheck.json, + }); + } + if (handleChangePosition) { + editor.onDidChangeCursorPosition(e => { + handleChangePosition?.(editor.getModel().getOffsetAt(e.position)); + }); + } + if (autoFit) { + editor.layout({ + width: 200, + height: Math.max(editor.getContentHeight(), 50), + }); + } setMounted(true); }, - [handleChangePosition, value] + [autoFit, extraSuggestions, foldLevel, handleChangePosition, syntaxCheck, value] ); useEffect(() => { @@ -71,14 +104,21 @@ export const CodeEditor: React.FC = ({ const editor = editorRef.current; if (editor && editor.getValue() !== value) { const positionShift = value.length - editor.getValue().length; - const position = editor.getPosition(); - editor.setValue(value); - editor.setPosition({ ...position, column: position.column + positionShift }); - //scroll to the end of the line - editor.revealPosition({ ...position, column: position.column + positionShift + 100 }); - editor.focus(); + if (Math.abs(positionShift) > 2 || monacoOptions?.readOnly) { + // we respect prop.value change only if it's more than 2 characters + // otherwise, it's probably user is typing, and we don't want to rollback what he typed due to delay in props update + const position = editor.getPosition(); + editor.setValue(value); + editor.setPosition({ ...position, column: position.column + positionShift }); + //scroll to the end of the line + editor.revealPosition({ ...position, column: position.column + positionShift + 100 }); + editor.focus(); + if (foldLevel) { + editor.getAction(`editor.foldLevel${foldLevel}`)?.run(); + } + } } - }, [value]); + }, [value, foldLevel, monacoOptions?.readOnly]); return (
@@ -86,13 +126,14 @@ export const CodeEditor: React.FC = ({ onChange={v => { handleChange(v || ""); }} - loading={} + loading={loaderNode || } language={language} height={height} width={width} onMount={handleEditorDidMount} className={styles.editor} options={{ + fixedOverflowWidgets: true, automaticLayout: true, glyphMargin: false, scrollBeyondLastLine: false, diff --git a/webapps/console/components/CodeEditor/SnippedEditor.tsx b/webapps/console/components/CodeEditor/SnippedEditor.tsx index 3e85ff2ad..b5b4bc57b 100644 --- a/webapps/console/components/CodeEditor/SnippedEditor.tsx +++ b/webapps/console/components/CodeEditor/SnippedEditor.tsx @@ -1,7 +1,7 @@ -import Editor from "@monaco-editor/react"; import React from "react"; import { Radio } from "antd"; import { editor } from "monaco-editor"; +import { CodeEditor } from "./CodeEditor"; /** * See tagDestination comments, due to limitations of the react-jsonschema-form we can't use @@ -20,9 +20,12 @@ export type SnippedEditorProps = { languages?: SupportedLanguages[]; height?: number; onChange: (value: SnippedEditorValue) => void; - options?: editor.IStandaloneEditorConstructionOptions; + monacoOptions?: editor.IStandaloneEditorConstructionOptions; //automatically fold code on provided level of indentation on editor mount foldLevel?: number; + syntaxCheck?: { + json?: boolean; + }; }; /** @@ -32,7 +35,7 @@ export type SnippedEditorProps = { function parse(val: string) { try { const j = JSON.parse(val); - if (j.lang && j.code) { + if (j.lang) { return j; } else { return { code: val, lang: "json" }; @@ -45,7 +48,9 @@ function parse(val: string) { export const SnippedEditor: React.FC = props => { const [value, setValue] = React.useState(props.value); - const valueParsed = value ? (parse(value) as SnippedEditorParsed) : { lang: "javascript", code: "" }; + const valueParsed = value + ? (parse(value) as SnippedEditorParsed) + : { lang: props.languages?.[0] || "text", code: "" }; const singleLanguage = props.languages && props.languages.length === 1; return (
@@ -66,7 +71,9 @@ export const SnippedEditor: React.FC = props => {
)}
- { if (singleLanguage) { @@ -77,39 +84,11 @@ export const SnippedEditor: React.FC = props => { props.onChange(newValue); } }} - language={valueParsed.lang?.toLowerCase() || "html"} - height={props.height ? `${props.height}px` : "500px"} - onMount={ - props.foldLevel - ? editor => { - editor.getAction(`editor.foldLevel${props.foldLevel}`)?.run(); - } - : undefined - } - className="rounded-lg" - options={{ - renderLineHighlight: "none", - //readOnly: readonly, - automaticLayout: true, - glyphMargin: false, - folding: false, + foldLevel={props.foldLevel} + syntaxCheck={props.syntaxCheck} + monacoOptions={{ lineNumbers: "off", - lineDecorationsWidth: 11, - lineNumbersMinChars: 0, - minimap: { - enabled: false, - }, - scrollbar: { - verticalScrollbarSize: 5, - horizontalScrollbarSize: 5, - }, - padding: { - top: 4, - bottom: 4, - }, - hideCursorInOverviewRuler: true, - overviewRulerLanes: 0, - ...props.options, + ...(props.monacoOptions || {}), }} />
diff --git a/webapps/console/components/ConfigObjectEditor/ConfigEditor.module.css b/webapps/console/components/ConfigObjectEditor/ConfigEditor.module.css index 554cca155..c28ccc5bb 100644 --- a/webapps/console/components/ConfigObjectEditor/ConfigEditor.module.css +++ b/webapps/console/components/ConfigObjectEditor/ConfigEditor.module.css @@ -6,16 +6,24 @@ color: rgb(100, 100, 100); } +.inner:first-child { + padding-top: 4px; +} + +.inner:last-child { + margin-bottom: 0; +} + .invalidInput input { border-color: rgb(255, 0, 0); } -.listTable :global th.ant-table-cell { +.listTable :global(th.ant-table-cell) { @apply text-textLight font-bold; text-transform: uppercase; } -.listTable :global th.ant-table-cell::before { +.listTable :global(th.ant-table-cell::before) { content: none !important; } @@ -27,11 +35,11 @@ text-decoration: underline; } -.nestedObjectField :global .ant-form-item-label { +.nestedObjectField :global(.ant-form-item-label) { max-width: 33.8% !important; text-transform: capitalize; } -.nestedObjectField :global .ant-form-item-control { +.nestedObjectField :global(.ant-form-item-control) { max-width: 66.2% !important; } diff --git a/webapps/console/components/ConfigObjectEditor/ConfigEditor.tsx b/webapps/console/components/ConfigObjectEditor/ConfigEditor.tsx index 5d40434a2..954069b63 100644 --- a/webapps/console/components/ConfigObjectEditor/ConfigEditor.tsx +++ b/webapps/console/components/ConfigObjectEditor/ConfigEditor.tsx @@ -1,17 +1,18 @@ -import React, { createContext, PropsWithChildren, ReactNode, useContext, useEffect, useState } from "react"; -import { Button, Col, Dropdown, Form as AntdForm, Input, MenuProps, Row, Skeleton, Switch, Table } from "antd"; +import React, { createContext, PropsWithChildren, ReactNode, useContext, useEffect, useRef, useState } from "react"; +import { Button, Col, Form as AntdForm, Input, Row, Switch, Table } from "antd"; import { FaCaretDown, FaCaretRight, FaClone, FaPlus } from "react-icons/fa"; import { ZodType } from "zod"; -import { getConfigApi, useApi } from "../../lib/useApi"; +import { getConfigApi } from "../../lib/useApi"; import { useRouter } from "next/router"; -import { asFunction, FunctionLike, getErrorMessage, getLog, randomId, requireDefined } from "juava"; +import { asFunction, FunctionLike, getErrorMessage, getLog, requireDefined, rpc } from "juava"; import zodToJsonSchema from "zod-to-json-schema"; import styles from "./ConfigEditor.module.css"; -import validator from "@rjsf/validator-ajv6"; -import { Form } from "@rjsf/antd/dist/antd.cjs.development"; +import validator from "@rjsf/validator-ajv8"; +import { Form } from "@rjsf/antd"; + import { ADDITIONAL_PROPERTY_FLAG, canExpand, @@ -26,33 +27,49 @@ import { } from "@rjsf/utils"; import { ConfigEntityBase } from "../../lib/schema"; -import { useWorkspace } from "../../lib/context"; -import omitBy from "lodash/omitBy"; -import { GlobalLoader, LoadingAnimation } from "../GlobalLoader/GlobalLoader"; +import { useAppConfig, useWorkspace, useWorkspaceRole } from "../../lib/context"; +import { LoadingAnimation } from "../GlobalLoader/GlobalLoader"; import { WLink } from "../Workspace/WLink"; -import { DeleteOutlined, EditOutlined } from "@ant-design/icons"; -import { ErrorCard, GlobalError } from "../GlobalError/GlobalError"; -import { Action, confirmOp, doAction, feedbackError, feedbackSuccess, useTitle } from "../../lib/ui"; +import { CheckCircleTwoTone, DeleteOutlined, InfoCircleTwoTone } from "@ant-design/icons"; +import { + Action, + confirmOp, + copyTextToClipboard, + doAction, + feedbackError, + feedbackSuccess, + useTitle, +} from "../../lib/ui"; import { branding } from "../../lib/branding"; import { useAntdModal } from "../../lib/modal"; -import { Inbox } from "lucide-react"; -import { createDisplayName, prepareZodObjectForSerialization } from "../../lib/zod"; +import { Copy, Edit3, Inbox } from "lucide-react"; +import { createDisplayName } from "../../lib/zod"; import { JitsuButton } from "../JitsuButton/JitsuButton"; import { EditorTitle } from "./EditorTitle"; import { EditorBase } from "./EditorBase"; import { EditorField } from "./EditorField"; import { EditorButtons } from "./EditorButtons"; +import { ButtonGroup, ButtonProps } from "../ButtonGroup/ButtonGroup"; +import cuid from "cuid"; +import { ObjectTitle } from "../ObjectTitle/ObjectTitle"; +import omitBy from "lodash/omitBy"; +import { asConfigType, useConfigObject, useConfigObjectList, useConfigObjectMutation } from "../../lib/store"; +import { CustomWidgetProps, PasswordEditor } from "./Editors"; +import { WorkspacePermissionsType } from "../../lib/workspace-roles"; +import { oauthDecorators } from "../../lib/server/oauth/destinations"; +import Nango from "@nangohq/frontend"; const log = getLog("ConfigEditor"); export type FieldDisplay = { isId?: boolean; - hidden?: boolean; + hidden?: boolean | ((a: any, isNew?: boolean) => boolean); displayName?: string; editor?: any; advanced?: boolean; documentation?: ReactNode; - constant?: any; + constant?: any | ((a: any, isNew?: boolean) => any); + correction?: any | ((a: any, isNew?: boolean) => any); textarea?: boolean; password?: boolean; }; @@ -63,6 +80,7 @@ export type ConfigEditorProps listTitle?: ReactNode; type: string; listColumns?: { title: ReactNode; render: (o: T) => ReactNode }[]; + icon?: (o: T) => ReactNode; name?: (o: T) => string; objectType: FunctionLike, T>; fields: Record; @@ -78,6 +96,7 @@ export type ConfigEditorProps actions?: { title: ReactNode; icon?: ReactNode; + collapsed?: boolean; key?: string; action?: (o: T) => void; link?: (o: T) => string; @@ -87,44 +106,48 @@ export type ConfigEditorProps newObject?: (meta?: M) => Partial; //for providing custom editor component editorComponent?: EditorComponentFactory; - testConnectionEnabled?: (o: any) => boolean; + testConnectionEnabled?: (o: any) => boolean | "manual"; + testButtonLabel?: string; onTest?: (o: T) => Promise; -}; - -export type CustomWidgetProps = { - value: T | undefined; - onChange: (value: T) => void; + backTo?: string; + pathPrefix?: string; }; type JsonSchema = any; -function getUiWidget(field: FieldDisplay) { - if (field?.constant || field?.hidden) { +function getUiWidget(field: FieldDisplay, obj: any, isNew: boolean) { + if ( + (typeof field?.hidden === "function" && field?.hidden(obj, isNew) === true) || + (typeof field?.hidden === "boolean" && field?.hidden === true) || + (typeof field?.constant === "function" && typeof field?.constant(obj, isNew) !== "undefined") || + (typeof field?.constant !== "function" && typeof field?.constant !== "undefined") + ) { return "hidden"; } else if (field?.editor) { return field?.editor; - } else if (field?.textarea) { - return "textarea"; } else if (field?.password) { return "password"; + } else if (field?.textarea) { + return "textarea"; } else { return undefined; } } -function getUiSchema(schema: JsonSchema, fields: Record): UiSchema { - return { +function getUiSchema(schema: JsonSchema, fields: Record, object: any, isNew: boolean): UiSchema { + const uiSchema = { ...Object.entries((schema as any).properties) .map(([name]) => { const field = fields[name]; const fieldProps = { - "ui:widget": getUiWidget(field), + "ui:widget": getUiWidget(field, object, isNew), "ui:disabled": field?.constant ? true : undefined, "ui:placeholder": field?.constant, "ui:title": field?.displayName || createDisplayName(name), "ui:FieldTemplate": FieldTemplate, "ui:ObjectFieldTemplate": NestedObjectTemplate, "ui:help": field?.documentation || undefined, + "ui:options": field?.password && field?.textarea ? { rows: 4 } : undefined, additionalProperties: { "ui:FieldTemplate": NestedObjectFieldTemplate, }, @@ -139,6 +162,7 @@ function getUiSchema(schema: JsonSchema, fields: Record): norender: true, }, }; + return uiSchema; } export type SingleObjectEditorProps = ConfigEditorProps & { @@ -149,7 +173,7 @@ export type SingleObjectEditorProps = ConfigEditorProps & { export const AdvancedConfiguration: React.FC> = ({ children }) => { const [expanded, setExpanded] = useState(false); return ( -
+
setExpanded(!expanded)} @@ -187,8 +211,25 @@ const FormList: React.FC = props => { ); }; -const CustomCheckbox = function (props) { - return props.onChange(!props.value)} />; +export const CopyConstant: React.FC> = props => { + return ( +
{ + copyTextToClipboard(props.value); + feedbackSuccess("Copied to clipboard"); + }} + > + {props.value} +
+ +
+
+ ); +}; + +export const CustomCheckbox = function (props) { + return props.onChange(!props.value)} />; }; export type ConfigTestResult = { ok: true } | { ok: false; error: string }; @@ -215,7 +256,7 @@ function AddButton(props: IconButtonProps) { type={"primary"} ghost={true} onClick={e => { - btnProps.onClick && btnProps.onClick(e); + btnProps.onClick && btnProps.onClick(e as any); }} > Add parameter @@ -227,6 +268,7 @@ const EditorComponent: React.FC = props => { const { noun, createNew, + type, objectType, meta, fields, @@ -240,19 +282,37 @@ const EditorComponent: React.FC = props => { subtitle, } = props; useTitle(`${branding.productName} : ${createNew ? `Create new ${noun}` : `Edit ${noun}`}`); + const appConfig = useAppConfig(); + const formRef = useRef(); + const role = useWorkspaceRole(); const [loading, setLoading] = useState(false); + const [testing, setTesting] = useState(false); const objectTypeFactory = asFunction(objectType); const schema = zodToJsonSchema(objectTypeFactory(object)); const [formState, setFormState] = useState(undefined); const hasErrors = formState?.errors?.length > 0; - const isTouched = formState !== undefined || !!createNew; - const uiSchema = getUiSchema(schema, fields); - log.atInfo().log("Rendering with schema and props", schema, props); + const [isTouched, setTouched] = useState(!!createNew); + const [testResult, setTestResult] = useState(undefined); + const [nangoLoading, setNangoLoading] = useState(false); + const [nangoError, setNangoError] = useState(undefined); + const oauthConnector = + type === "destination" && appConfig.nango ? oauthDecorators[object.destinationType] : undefined; + + const uiSchema = getUiSchema(schema, fields, formState?.formData || object, isNew); const [submitCount, setSubmitCount] = useState(0); const modal = useAntdModal(); + + useEffect(() => { + if (formRef.current) { + setFormState(formRef.current.state); + } + }, []); + const onFormChange = state => { setFormState(state); + setTestResult(undefined); + setTouched(true); log.atDebug().log(`Updating editor form state`, state); }; const withLoading = (fn: () => Promise) => async () => { @@ -273,31 +333,134 @@ const EditorComponent: React.FC = props => { return ( onCancel(isTouched))} /> + {oauthConnector && ( +
+
+ { + const nango = new Nango({ + publicKey: appConfig.nango!.publicKey, + host: appConfig.nango!.host, + }); + setNangoLoading(true); + const oauthIntegrationId = oauthConnector.nangoIntegrationId(formState?.formData || object); + const oauthConnectionId = `destination.${object?.id}`; + nango + .auth(oauthIntegrationId, oauthConnectionId) + .then(result => { + if (formState) { + formState.formData = { + ...formState.formData, + authorized: true, + oauthIntegrationId, + oauthConnectionId, + }; + setTouched(true); + } + setNangoError(undefined); + }) + .catch(err => { + setNangoError(getErrorMessage(err)); + getLog().atError().log("Failed to add oauth connection", err); + if (formState) { + formState.formData = { ...formState.formData, authorized: false }; + setTouched(true); + } + }) + .finally(() => setNangoLoading(false)); + }} + > + {(formState?.formData || object).authorized ? "Re-Sign In" : "Authorize"} + +
+
+ {nangoError ? ( + OAuth2 error: {nangoError} + ) : (formState?.formData || object).authorized ? ( + <> + + Authorized + + ) : ( + <> + + Click "Authorize" to open OAuth2.0 authorization popup + + )} +
+
+ {/* setManualAuth(!manualAuth)}>*/} + {/* {manualAuth ? "Hide authorization settings" : "Manually setup authorization"}*/} + {/**/} +
+
+ )} 0 }}>
withLoading(() => onSave(formData))()} + onSubmit={async ({ formData }) => { + if ( + onTest && + (typeof testConnectionEnabled === "undefined" || testConnectionEnabled(formData || object) === true) + ) { + setTesting(true); + let testRes: any; + try { + testRes = testResult || (await onTest(formState?.formData || object)); + } finally { + setTesting(false); + } + if (!testRes?.ok) { + modal.confirm({ + title: "Check failed", + content: testRes?.error, + okText: "Save anyway", + okType: "danger", + cancelText: "Cancel", + onOk: () => { + withLoading(() => onSave({ ...formData, testConnectionError: testRes?.error }))(); + }, + }); + return; + } else { + delete formData.testConnectionError; + } + } + withLoading(() => onSave(formData))(); + }} formData={formState?.formData || object} uiSchema={uiSchema} > onTest(formState?.formData || object) + ? async () => { + const testResult = await onTest(formState?.formData || object); + setTestResult(testResult); + return testResult; + } : undefined } onDelete={withLoading(onDelete)} @@ -343,13 +506,39 @@ const SingleObjectEditor: React.FC = props => { newObject = () => ({}), loadMeta, onTest, + backTo, + pathPrefix = "", ...otherProps } = props; + const pref = pathPrefix; const [meta, setMeta] = useState(undefined); const isNew = !!(!otherProps.object || createNew); const workspace = useWorkspace(); + const appConfig = useAppConfig(); const router = useRouter(); + const onSaveMutation = useConfigObjectMutation(type as any, async (newObject: any) => { + if (isNew) { + await getConfigApi(workspace.id, type).create(newObject); + if (type === "stream" && appConfig.ee.available) { + try { + await rpc(`/api/${workspace.id}/ee/s3-init`, { + method: "POST", + query: { workspaceId: workspace.id }, + }); + } catch (e: any) { + console.error("Failed to init S3 bucket", e.message); + } + } + } else { + await getConfigApi(workspace.id, type).update(object.id, newObject); + } + }); + + const onDeleteMutation = useConfigObjectMutation(type as any, async (_: any) => { + await getConfigApi(workspace.id, type).del(object.id); + }); + useEffect(() => { if (loadMeta) { loadMeta(otherProps.object).then(setMeta); @@ -361,44 +550,53 @@ const SingleObjectEditor: React.FC = props => { if (meta === undefined) { return ; } - const object = otherProps.object || { - id: randomId(), + const preObject = otherProps.object || { + id: cuid(), workspaceId: workspace.id, type: type, ...newObject(meta), }; + const constants = Object.fromEntries( + Object.entries(fields) + .filter(([_, v]) => typeof v.constant !== "undefined") + .map(([k, v]) => [k, typeof v.constant === "function" ? v.constant(preObject, isNew) : v.constant]) + ); + const corrections = Object.fromEntries( + Object.entries(fields) + .filter(([_, v]) => typeof v.correction !== "undefined") + .map(([k, v]) => [k, typeof v.correction === "function" ? v.correction(preObject, isNew) : v.correction]) + ); + + const object = { ...preObject, ...constants, ...corrections }; const onCancel = async (confirm: boolean) => { - if (!confirm) { - await router.push(`/${workspace.id}/${type}s`); - } else { - if (await confirmOp("Are you sure you want to close this page? All unsaved changes will be lost.")) { - await router.push(`/${workspace.id}/${type}s`); + if (!confirm || (await confirmOp("Are you sure you want to close this page? All unsaved changes will be lost."))) { + if (backTo) { + router.push(`/${workspace.slugOrId}${backTo}`); + } else { + router.push(`/${workspace.slugOrId}${pref}/${type}s`); } } }; const onDelete = async () => { if (await confirmOp(`Are you sure you want to delete this ${noun}?`)) { try { - await getConfigApi(workspace.id, type).del(object.id); + await onDeleteMutation.mutateAsync(undefined); feedbackSuccess(`Successfully deleted ${noun}`); - router.push(`/${workspace.id}/${type}s`); + router.push(`/${workspace.slugOrId}${pref}/${type}s`); } catch (error) { feedbackError("Failed to delete object", { error }); } } }; const onSave = async newObject => { - newObject = prepareZodObjectForSerialization(newObject); - console.log("Saving", newObject); try { - if (isNew) { - await getConfigApi(workspace.id, type).create(newObject); + await onSaveMutation.mutateAsync(newObject); + if (backTo) { + router.push(`/${workspace.slugOrId}${backTo}`); } else { - await getConfigApi(workspace.id, type).update(object.id, newObject); - //await new Promise(resolve => setTimeout(resolve, 10000000)); + router.push(`/${workspace.slugOrId}${pref}/${type}s`); } - router.push(`/${workspace.id}/${type}s`); } catch (error) { feedbackError("Failed to save object", { error }); } @@ -555,18 +753,12 @@ const FieldTemplate = props => { ); }; -const SingleObjectEditorLoader: React.FC = ({ +const SingleObjectEditorLoader: React.FC = ({ id, clone, ...rest }) => { - const workspace = useWorkspace(); - const { isLoading, data, error } = useApi(`/api/${workspace.id}/config/${rest.type}/${id}`); - if (isLoading) { - return ; - } else if (error) { - return ; - } + const data = requireDefined(useConfigObject(asConfigType(rest.type), id), `Unknown ${rest.type} ${id}`); return ( = props => { const router = useRouter(); const id = router.query.id as string; const clone = router.query.clone as string; + const backTo = router.query.backTo as string; if (id) { if (id === "new") { if (clone) { - return ; + return ; } else { - return ; + return ; } } else { - return ; + return ; } } else { return ; @@ -613,9 +808,13 @@ const ObjectsList: React.FC<{ objects: any[]; onDelete: (id: string) => Promise< listColumns = [], actions = [], noun, + icon, name = (o: any) => o.name, + pathPrefix = "", }) => { + const pref = pathPrefix; const modal = useAntdModal(); + const nameRender = listColumns.find(c => c.title === "name")?.render; useTitle(`${branding.productName} : ${plural(noun)}`); const deleteObject = id => { modal.confirm({ @@ -629,64 +828,60 @@ const ObjectsList: React.FC<{ objects: any[]; onDelete: (id: string) => Promise< const columns = [ { title: "Name", - render: (text, record) => ( - - {name(record)} - - ), + render: + nameRender || + ((text, record) => ( + + + + )), }, - ...listColumns.map(c => ({ - title: c.title, - render: (text, record) => c.render(record), - })), + ...listColumns + .filter(c => c.title !== "name") + .map(c => ({ + title: c.title, + render: (text, record) => c.render(record), + })), { title: "", + className: "text-right", render: (text, record) => { - const items: MenuProps["items"] = [ + const items: ButtonProps[] = [ { - label: Edit, - key: "edit", - icon: , + label: "Edit", + href: `${pref}/${type}s?id=${record.id}`, + icon: , + requiredPermission: "editEntities" as WorkspacePermissionsType, }, + ...actions.map(action => ({ + disabled: !!(action.disabled && action.disabled(record)), + href: action.link ? action.link(record) : undefined, + label: action.title, + collapsed: action.collapsed, + onClick: action.action + ? () => { + (action.action as any)(record); + } + : undefined, + icon:
{action.icon}
, + })), { - label: Clone, - key: "clone", + label: "Clone", + href: `${pref}/${type}s?id=new&clone=${record.id}`, + collapsed: true, icon: , + requiredPermission: "editEntities" as WorkspacePermissionsType, }, { - label: deleteObject(record.id)}>Delete, - key: "del", + label: "Delete", + danger: true, + collapsed: true, + onClick: () => deleteObject(record.id), icon: , + requiredPermission: "deleteEntities" as WorkspacePermissionsType, }, - ...actions.map(action => ({ - disabled: !!(action.disabled && action.disabled(record)), - label: action.link ? ( - {action.title} - ) : ( - { - (action.action as any)(record); - } - : undefined - } - > - {action.title} - - ), - key: - (typeof action.title === "string" ? action.title : action.link ? action.link(record) : action.key) ?? "", - icon:
{action.icon}
, - })), ].filter(i => !!i); - return ( -
- -
-
-
- ); + return ; }, }, ]; @@ -706,22 +901,25 @@ const ObjectsList: React.FC<{ objects: any[]; onDelete: (id: string) => Promise< const ObjectListEditor: React.FC = props => { const workspace = useWorkspace(); - const { isLoading, data, error, reload } = useApi(`/api/${workspace.id}/config/${props.type}`); + const data = useConfigObjectList(asConfigType(props.type)); const router = useRouter(); const pluralNoun = props.nounPlural || plural(props.noun); const addAction = props.addAction || (() => router.push(`${router.asPath}?id=new`)); + + const onDeleteMutation = useConfigObjectMutation(props.type as any, async (id: string) => { + await getConfigApi(workspace.id, props.type).del(id); + }); const onDelete = async (id: string) => { try { - await getConfigApi(workspace.id, props.type).del(id); - reload(); + await onDeleteMutation.mutateAsync(id); } catch (e) { alert(`Failed to delete ${props.noun}: ${getErrorMessage(e)}`); } }; - const list = data?.objects?.filter(props.filter || (() => true)) || []; + const list = data.filter(props.filter || (() => true)) || []; return (
-
+
{props.listTitle || `Edit ${pluralNoun}`}
@@ -730,25 +928,23 @@ const ObjectListEditor: React.FC = props => { onClick={() => doAction(router, addAction)} type="primary" size="large" - disabled={!!(isLoading || error)} icon={} + requiredPermission="editEntities" > Add new {props.noun}
- {isLoading && } - {error && } - {list.length === 0 && !isLoading && !error && ( + {list.length === 0 && (
-
You don't any have {props.noun}s configured.
+
You don't have any {props.noun}s configured.
- +
)} diff --git a/webapps/console/components/ConfigObjectEditor/EditorButtons.tsx b/webapps/console/components/ConfigObjectEditor/EditorButtons.tsx index 405448b4c..f45a5fe8f 100644 --- a/webapps/console/components/ConfigObjectEditor/EditorButtons.tsx +++ b/webapps/console/components/ConfigObjectEditor/EditorButtons.tsx @@ -3,12 +3,16 @@ import { Alert, Button, Popover } from "antd"; import { CheckOutlined, LoadingOutlined } from "@ant-design/icons"; import { getLog } from "juava"; import { ConfigTestResult } from "./ConfigEditor"; +import { useAppConfig } from "../../lib/context"; +import { ButtonLabel } from "../ButtonLabel/ButtonLabel"; +import { JitsuButton } from "../JitsuButton/JitsuButton"; const log = getLog("ConfigEditor"); export type EditorButtonProps = { isNew: boolean; loading: boolean; + testing: boolean; onDelete: () => void; onTest?: () => Promise; onCancel: () => void; @@ -16,19 +20,24 @@ export type EditorButtonProps = { isTouched?: boolean; hasErrors?: boolean; testStatus?: string; + testButtonLabel?: string; }; export const EditorButtons: React.FC = ({ isNew, loading, + testing, onCancel, onDelete, onTest, onSave, isTouched, hasErrors, + testButtonLabel = "Test Connection", }) => { const buttonDivRef = useRef(null); + const appConfig = useAppConfig(); + const readOnly = !!appConfig.readOnlyUntil; const [testStatus, setTestStatus] = useState(""); @@ -66,9 +75,9 @@ export const EditorButtons: React.FC = ({ return ( <> - {testStatus && testStatus !== "success" && testStatus !== "pending" && ( + {!loading && testStatus && testStatus !== "success" && testStatus !== "pending" && ( = ({ closable /> )} + {testing && }
{!isNew && ( - + )}
{onTest && (testStatus === "success" ? ( - - ) : ( ))} - +
diff --git a/webapps/console/components/ConfigObjectEditor/EditorField.tsx b/webapps/console/components/ConfigObjectEditor/EditorField.tsx index 260270cb4..fb3a55e91 100644 --- a/webapps/console/components/ConfigObjectEditor/EditorField.tsx +++ b/webapps/console/components/ConfigObjectEditor/EditorField.tsx @@ -11,7 +11,7 @@ export type EditorFieldProps = { errors?: React.ReactNode; }; -export const EditorField: React.FC> = ({ +const EditorField0: React.FC> = ({ id, required, className, @@ -22,33 +22,70 @@ export const EditorField: React.FC> = }) => { return (
-
-
+
+
-
-
- -
-
{label}
+
{errors} +
{children}
{!!help && ( -
+
{help}
)}
); }; + +const EditorFieldInner: React.FC> = ({ + id, + required, + className, + help, + label, + errors, + children, +}) => { + return ( +
+
+
+ +
+
+ {errors} + +
+
+
+
+
{children}
+
+ {!!help && ( +
+
{help}
+
+ )} +
+
+ ); +}; + +export const EditorField: React.FC> = ({ + inner, + ...props +}) => { + return inner ? : ; +}; diff --git a/webapps/console/components/ConfigObjectEditor/EditorTitle.tsx b/webapps/console/components/ConfigObjectEditor/EditorTitle.tsx index 7c129e233..b7159beca 100644 --- a/webapps/console/components/ConfigObjectEditor/EditorTitle.tsx +++ b/webapps/console/components/ConfigObjectEditor/EditorTitle.tsx @@ -11,7 +11,7 @@ export type EditorTitleProps = { export const EditorTitle: React.FC = ({ title, subtitle, onBack }) => { return ( <> -
+

{title}

} type="link" size="small" onClick={onBack}> diff --git a/webapps/console/components/ConfigObjectEditor/Editors.module.css b/webapps/console/components/ConfigObjectEditor/Editors.module.css new file mode 100644 index 000000000..28a6cd260 --- /dev/null +++ b/webapps/console/components/ConfigObjectEditor/Editors.module.css @@ -0,0 +1,30 @@ +.passwordTextareaMasked { + -webkit-text-security: disc; + text-security: disc; + font-family: monospace; + padding-right: 50px; +} + +.passwordTextareaVisible { + font-family: monospace; + padding-right: 50px; +} + +.passwordExtraButton { + position: absolute !important; + right: 3px; + z-index: 1; +} + +.passwordInput { + padding-right: 50px; +} + +/* Fallback for browsers that don't support text-security */ +@supports not ((-webkit-text-security: disc) or (text-security: disc)) { + .passwordTextareaMasked { + color: transparent; + padding-right: 50px; + text-shadow: 0 0 0 #000; + } +} diff --git a/webapps/console/components/ConfigObjectEditor/Editors.tsx b/webapps/console/components/ConfigObjectEditor/Editors.tsx new file mode 100644 index 000000000..21ca83fa4 --- /dev/null +++ b/webapps/console/components/ConfigObjectEditor/Editors.tsx @@ -0,0 +1,216 @@ +import React, { useState } from "react"; +import { DatePicker, Input, Select, Button } from "antd"; +import { EditOutlined, EyeInvisibleOutlined, EyeOutlined } from "@ant-design/icons"; +import dayjs from "dayjs"; +import utc from "dayjs/plugin/utc"; +import { useWorkspaceRole } from "../../lib/context"; +import { MASKED_SECRET } from "../../lib/schema/destinations"; +import styles from "./Editors.module.css"; +dayjs.extend(utc); + +export type CustomWidgetProps = { + value: T | undefined; + onChange: (value: T) => Promise | void; + disabled?: boolean; +}; +export const DateEditor: React.FC<{ format: string } & CustomWidgetProps> = props => { + return ( + { + props.onChange((v ?? dayjs()).format(props.format)); + }} + /> + ); +}; + +export function SelectEditor( + props: { options: { label: string; value: T }[]; className?: string } & CustomWidgetProps +) { + return ( + ({ label: o, value: o }))} + onChange={v => { + props.onChange(v); + }} + /> + ); +}; + +export const TextEditor: React.FC<{ rows?: number; placeholder?: string } & CustomWidgetProps> = props => { + if (props.rows && props.rows > 1) { + return ( + props.onChange(e.target.value)} + /> + ); + } else { + return ( + props.onChange(e.target.value)} + /> + ); + } +}; + +export const NumberEditor: React.FC & { max?: number; min?: number }> = props => { + return ( + { + const v = parseInt(e.target.value); + if (isNaN(v)) { + props.onChange(undefined); + } else { + props.onChange(v); + } + }} + /> + ); +}; + +export const PasswordEditor: React.FC & { rows?: number; options?: any }> = props => { + const userRole = useWorkspaceRole(); + const canEdit = !props.disabled && userRole.editEntities; + + // Check if the current value is masked + const isMasked = props.value === MASKED_SECRET; + + // Track if we're in edit mode and the local value + const [isEditMode, setIsEditMode] = useState(!isMasked); + const [localValue, setLocalValue] = useState(props.value || ""); + const [isPasswordVisible, setIsPasswordVisible] = useState(false); + + // // Update local value when props change, but only if not in edit mode + // useEffect(() => { + // if (!isEditMode && !hasUserInput) { + // setLocalValue(props.value || ""); + // } + // }, [props.value, isEditMode, hasUserInput]); + + // Get rows from either direct prop or options + const rows = props.rows || props.options?.rows; + + const handleEdit = () => { + if (isMasked) { + setLocalValue(""); + setIsEditMode(true); + } + }; + + const handleChange = (value: string) => { + setLocalValue(value); + props.onChange(value); + }; + + if (rows && rows > 1) { + // For multiline passwords, create a custom implementation + return ( +
+ handleChange(e.target.value)} + className={!isEditMode || !isPasswordVisible ? styles.passwordTextareaMasked : styles.passwordTextareaVisible} + placeholder={ + canEdit && isEditMode + ? isMasked + ? "Enter new password or secret..." + : "Enter password or secret..." + : undefined + } + disabled={props.disabled || !canEdit || !isEditMode} + /> + {canEdit && ( + <> + {isMasked && !isEditMode ? ( +
+ ); + } + + return ( +
+ handleChange(e.target.value)} + visibilityToggle={canEdit && isEditMode} + disabled={props.disabled || !canEdit || !isEditMode} + placeholder={canEdit && isEditMode ? (isMasked ? "Enter new password..." : "Enter password...") : undefined} + className={styles.passwordInput} + /> + {canEdit && isMasked && !isEditMode && ( +
+ ); +}; diff --git a/webapps/console/components/ConfigObjectEditor/SchemaForm.tsx b/webapps/console/components/ConfigObjectEditor/SchemaForm.tsx new file mode 100644 index 000000000..40d532dcc --- /dev/null +++ b/webapps/console/components/ConfigObjectEditor/SchemaForm.tsx @@ -0,0 +1,424 @@ +import React, { useCallback, useMemo, useState } from "react"; +import set from "lodash/set"; +import get from "lodash/get"; +import { EditorField } from "./EditorField"; +import { Switch, Tabs } from "antd"; +import { createDisplayName } from "../../lib/zod"; +import Ajv from "ajv"; +import { PlusIcon } from "lucide-react"; +import { getLog } from "juava"; +import { DateEditor, NumberEditor, PasswordEditor, SelectEditor, StringArray, TextEditor } from "./Editors"; +import { Htmlizer } from "../Htmlizer/Htmlizer"; + +const log = getLog("SchemaForm"); +const ajv = new Ajv({ allErrors: false, strictSchema: false, useDefaults: true, allowUnionTypes: true }); + +/** + * Renders a form based on a JSON schema. Supports nested objects, arrays of objects, and oneOf using recursion. + * + * @param jsonSchema - schema to render (ROOT object schema or schema part of nested object) + * @param path - path for the nested objects. For ROOT object, path must not be provided. + * @param onChange - callback for when the value changes. `path` is always calculated for the ROOT object + * @param obj - object to render (ROOT object or nested object) + * @param hiddenFields - fields to hide. It is mirror of ROOT object where only fields that needed to be hidden are present. + * @param showErrors - enables showing validation errors. It is recommended to disable this when for the fresh form of new object until the first save attempt. + */ +export const SchemaForm: React.FC<{ + path?: string[]; + jsonSchema: any; + hiddenFields?: any; + showErrors?: boolean; + disabled?: boolean; + onChange: (path: string[], v: any) => void; + obj: any; +}> = ({ jsonSchema, path, onChange, obj, hiddenFields, showErrors, disabled }) => { + const required = useMemo(() => (jsonSchema.required as string[]) || [], [jsonSchema.required]); + const properties = useMemo(() => (jsonSchema.properties || {}) as Record, [jsonSchema.properties]); + + const validateSingle = useCallback( + (name: string, fieldSchema: any, value: any) => { + // ajv validates 'oneOf' without determinant against all possible variants and returns error messages for all of them + // airbyte schema seems to uses oneOf without determinants. So we better skip validation for such fields + if (typeof fieldSchema.oneOf === "undefined" && typeof fieldSchema.const === "undefined") { + const req = required.includes(name); + const noValue = typeof value === "undefined" || value === null || value === ""; + if (req && noValue) { + return "Required"; + } else if (!noValue) { + // workaround for buggy schema having null instead of "null" in type array + if (Array.isArray(fieldSchema.type)) { + fieldSchema.type = fieldSchema.type.map(t => (t === null ? "null" : t)); + } + const validate = ajv.compile(fieldSchema); + if (!validate(value)) { + //show only one error message for the field + return validate.errors?.[0].message; + } + } + } + }, + [required] + ); + + // Pre init object with default values and constants + // Rendering will be skipped until all necessary fields will be initialized + // since changed obj value will trigger consequent rerender anyway + const skipRender = useMemo( + () => + Object.entries(properties).reduce((acc: boolean, [n, f]) => { + const value = obj?.[n]; + if (value === undefined && f.default) { + onChange(path ? [...path, n] : [n], f.default); + return true; + } else if (f.const && value !== f.const) { + onChange(path ? [...path, n] : [n], f.const); + return true; + } + return acc; + }, false), + [obj, onChange, path, properties] + ); + + // Validate all object fields + const errors = useMemo(() => { + return skipRender + ? {} + : Object.entries(properties).reduce((acc: any, [n, f]) => { + const value = obj?.[n]; + const error = validateSingle(n, f, value); + if (error) { + acc = set(acc, path ? [...path, n] : n, error); + } + return acc; + }, {}); + }, [obj, path, properties, skipRender, validateSingle]); + + if (skipRender) { + return null; + } + + return ( + <> + {Object.entries(properties) + .sort((a, b) => a[1].order - b[1].order) + .map(([n, f]) => { + const newPath = path ? [...path, n] : [n]; + const error = get(errors, newPath); + const isHidden = !(error && showErrors) && get(hiddenFields, newPath); + + const change = (v: any) => { + onChange(newPath, v); + }; + let value = obj?.[n]; + + const editorProps = { value, onChange: change, disabled }; + + if (f.airbyte_hidden) { + return null; + } + + if (f.const) { + // if (f.description) { + // return ( + //
+ // {f.description} + //
+ // ); + // } else { + return null; + //} + } + + return ( + {f.description}} + required={required.includes(n)} + > + <> + {(function () { + if (f.type === "object") { + if (f.oneOf) { + return ( + + ); + } + return ( + + ); + } + const typ = Array.isArray(f.type) ? f.type.find(t => t !== "null") : f.type; + switch (typ) { + case "integer": + case "number": + return ; + case "string": + if (f.format?.includes("date")) { + let format = "YYYY-MM-DD"; + switch (f.pattern) { + case "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$": + format = "YYYY-MM-DDTHH:mm:ss[Z]"; + break; + case "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}.[0-9]{3}Z$": + format = "YYYY-MM-DDTHH:mm:ss.SSS[Z]"; + break; + case "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}.[0-9]{6}Z$": + format = "YYYY-MM-DDTHH:mm:ss.SSSSSS[Z]"; + break; + case "^\\d{4}-\\d{2}-\\d{2}[T ]\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?Z?$": + case "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?Z?$": + format = "YYYY-MM-DDTHH:mm:ss.SSSSSS[Z]"; + break; + default: + return ; + } + return ; + } + if (f.enum) { + return ( + ({ + value: o, + label: o, + }))} + {...editorProps} + /> + ); + } + if (f.airbyte_secret) { + return ; + } + if (f.multiline) { + return ; + } + + return ; + case "boolean": + return ; + case "array": + if (f.items?.type === "object") { + return ( + + ); + } else { + const options = f.items?.enum ?? []; + return ; + } + default: + return ; + } + })()} + + + ); + })} + + ); +}; +const EditableTabs: React.FC<{ items; onAdd: () => void; onDelete: (k) => void }> = props => { + const [activeKey, setActiveKey] = useState(props.items?.length > 0 ? props.items[0]?.key : undefined); + + const onChange = (key: string) => { + setActiveKey(key); + }; + + const onEdit = (targetKey: React.MouseEvent | React.KeyboardEvent | string, action: "add" | "remove") => { + if (action === "add") { + setActiveKey(props.items.length ?? 1); + props.onAdd(); + } else { + if (activeKey >= props.items.length - 1) { + setActiveKey(props.items.length - 2); + } + props.onDelete(targetKey); + } + }; + + return ( + } + onEdit={onEdit} + items={props.items} + /> + ); +}; + +const ArrayOfObjects: React.FC<{ + name: string; + path: string[]; + fieldSchema: any; + value?: any; + hiddenFields?: any; + showErrors?: boolean; + disabled?: boolean; + onChange: (path: string[], v: any) => void; +}> = ({ fieldSchema, value, hiddenFields, name, path, onChange, showErrors, disabled }) => { + const items = !value + ? [] + : value.map((v, i) => { + const arrPath = [...path, i.toString()]; + return { + label: v?.name || v?.table_name || createDisplayName(name) + ` ${i + 1}`, + key: i, + children: ( +
+ +
+ ), + }; + }); + return ( + { + onChange([...path, value?.length ?? 0], {}); + }} + onDelete={k => onChange([...(path ?? []), k.toString()], undefined)} + items={items} + /> + ); +}; + +const OneOf: React.FC<{ + name: string; + path: string[]; + fieldSchema: any; + value?: any; + hiddenFields?: any; + showErrors?: boolean; + disabled?: boolean; + onChange: (path: string[], v: any) => void; +}> = ({ fieldSchema, value, hiddenFields, name, path, onChange, showErrors, disabled }) => { + const options = (fieldSchema.oneOf as any[]).map((o, i) => ({ + label: o.title, + value: i, + })); + // find discriminator field. This is a field that is present in all oneOf variants and has a const or enum with a single value + // discriminator is used to determine which variant is selected + const consts = (fieldSchema.oneOf as any[]) + .map(o => o.properties) + .map(p => Object.entries(p as Record)) + .flat() + .reduce((acc: any, [k, v]) => { + if (v.const || v.enum?.length === 1) { + acc[k] = (acc[k] ?? 0) + 1; + } + return acc; + }, {}); + const discriminator = Object.entries(consts).find(([k, v]) => v === fieldSchema.oneOf?.length); + + if (!discriminator) { + log.atError().log("No discriminator found for oneOf field", name); + return <>; + } + const discriminatorKey = discriminator[0]; + const discriminatorValue = value?.[discriminatorKey]; + + let selected: any; + let selectedIdx: number = 0; + let removeIdx = -1; + for (let i = 0; i < fieldSchema.oneOf.length; i++) { + const o = fieldSchema.oneOf[i]; + if ( + o.properties[discriminatorKey].const === discriminatorValue || + o.properties[discriminatorKey].enum?.[0] === discriminatorValue + ) { + selected = o; + selectedIdx = i; + break; + } + } + //Hide CDC option for replication_method + if (name === "replication_method") { + for (let i = 0; i < fieldSchema.oneOf.length; i++) { + const o = fieldSchema.oneOf[i]; + if (o.properties[discriminatorKey].const === "CDC" || o.properties[discriminatorKey].enum?.[0] === "CDC") { + removeIdx = i; + break; + } + } + if (selectedIdx === removeIdx) { + for (let i = 0; i < fieldSchema.oneOf.length; i++) { + if (i !== removeIdx) { + selected = fieldSchema.oneOf[i]; + selectedIdx = i; + break; + } + } + } + } + + const filteredOptions = options.filter((o, i) => i !== removeIdx); + + return ( + <> + { + onChange(path, { + [discriminatorKey]: + fieldSchema.oneOf[v].properties[discriminatorKey].const || + fieldSchema.oneOf[v].properties[discriminatorKey].enum?.[0], + }); + }} + disabled={disabled} + options={filteredOptions} + /> + {selected && ( + + )} + + ); +}; diff --git a/webapps/console/components/ConnectionEditorPage/ConnectionEditorPage.tsx b/webapps/console/components/ConnectionEditorPage/ConnectionEditorPage.tsx index b4bc94110..47ef092a8 100644 --- a/webapps/console/components/ConnectionEditorPage/ConnectionEditorPage.tsx +++ b/webapps/console/components/ConnectionEditorPage/ConnectionEditorPage.tsx @@ -1,91 +1,35 @@ -import { useWorkspace } from "../../lib/context"; +import { useWorkspace, useWorkspaceRole } from "../../lib/context"; import { get } from "../../lib/useApi"; import { DestinationConfig, FunctionConfig, StreamConfig } from "../../lib/schema"; -import React, { PropsWithChildren, useEffect, useState } from "react"; +import React, { useEffect, useState } from "react"; import { SomeZodObject, z } from "zod"; import { ConfigurationObjectLinkDbModel } from "../../prisma/schema"; import { useRouter } from "next/router"; import { assertTrue, getLog, requireDefined } from "juava"; -import { Disable } from "../Disable/Disable"; -import { Button, Input, InputNumber, Radio, Select, Space, Switch } from "antd"; -import { WLink } from "../Workspace/WLink"; -import { FaExternalLinkAlt } from "react-icons/fa"; +import { Button, Input, InputNumber, Radio, Switch, Tooltip } from "antd"; import { BaseBulkerConnectionOptions, getCoreDestinationType } from "../../lib/schema/destinations"; -import { confirmOp, feedbackError, feedbackSuccess } from "../../lib/ui"; +import { confirmOp, copyTextToClipboard, feedbackError, feedbackSuccess } from "../../lib/ui"; import FieldListEditorLayout, { EditorItem } from "../FieldListEditorLayout/FieldListEditorLayout"; import { DataLayoutType } from "@jitsu/protocols/analytics"; -import { ChevronLeft } from "lucide-react"; +import { Activity, Copy } from "lucide-react"; import styles from "./ConnectionEditorPage.module.css"; +import { Htmlizer } from "../Htmlizer/Htmlizer"; +import { FunctionsSelector } from "../FunctionsSelector/FunctionsSelector"; +import { Expandable } from "../Expandable/Expandable"; +import { useStoreReload } from "../../lib/store"; +import { DestinationSelector } from "../Selectors/DestinationSelector"; +import { SourceSelector } from "../Selectors/SourceSelector"; +import { FunctionVariables } from "../FunctionsDebugger/FunctionVariables"; +import { EditorToolbar } from "../EditorToolbar/EditorToolbar"; +import JSON5 from "json5"; +import { toURL } from "../../lib/shared/url"; +import { BackButton } from "../BackButton/BackButton"; import { JitsuButton } from "../JitsuButton/JitsuButton"; const log = getLog("ConnectionEditorPage"); -type SelectorProps = { - enabled: boolean; - selected: string; - items: T[]; - onSelect: (value: string) => void; -}; - -const Htmlizer: React.FC> = ({ children }) => { - return typeof children === "string" ? : <>{children}; -}; - type ConnectionOptionsType = Partial & { [key: string]: any }; -function DestinationSelector(props: SelectorProps) { - return ( -
- - - - {!props.enabled && ( -
- - - -
- )} -
- ); -} - -function SourceSelector(props: SelectorProps) { - return ( -
- - - - {!props.enabled && ( -
- - - -
- )} -
- ); -} - type EditorProps = { value?: T; disabled?: boolean; @@ -94,13 +38,26 @@ type EditorProps = { type EditorComponent = React.FC & P>; -const DataLayoutEditor: EditorComponent = props => ( - props.onChange(val.target.value)}> - +const DataLayoutEditor: EditorComponent = props => ( + props.onChange(val.target.value)} + > +
+ {props.fileStorage && ( + +
+
Original
+
Keep original event structure.
+
+
+ )}
- Single Table (recommended) + Single Table{!props.fileStorage ? (recommended) : <>}
The data is written into a table events. Field names and naming convention are same as in @@ -111,7 +68,7 @@ const DataLayoutEditor: EditorComponent = props => (
Segment
-
+
This data layout emulates segment tables: pages, identify, etc
@@ -119,12 +76,12 @@ const DataLayoutEditor: EditorComponent = props => (
Legacy Jitsu
-
+
Table name events. Field names are as in Jitsu 1.0
- +
); @@ -134,41 +91,41 @@ export const BatchOrStreamEditor: EditorComponent { - const [streamModeLocked, setStreamModeLocked] = useState(false); - const [streamModeDisabled, setStreamModeDisabled] = useState(false); + const [streamModeState, setStreamModeState] = useState<"locked" | "disabled" | "allowed">("allowed"); const [streamModeCaveats, setStreamModeCaveats] = useState(); useEffect(() => { if (limitations?.streamModeLocked) { - if (value !== "stream") { - setStreamModeLocked(true); - } + setStreamModeState(value !== "stream" ? "locked" : "allowed"); setStreamModeCaveats(limitations?.streamModeLocked); } else if (limitations?.streamModeDisabled) { - setStreamModeDisabled(true); + setStreamModeState("disabled"); setStreamModeCaveats(limitations?.streamModeDisabled); + } else { + setStreamModeState("allowed"); + setStreamModeCaveats(undefined); } }, [limitations, value]); return ( onChange(e.target.value)} > - - +
+
Stream - {streamModeLocked && ( + {streamModeState === "locked" && (
); }; @@ -207,8 +164,8 @@ export const SyncFrequencyEditor: EditorComponent; type SwitchComponentType = EditorComponent; -export const TextEditor: TextEditorComponent = ({ rows, value, onChange, disabled, className }) => { - if (rows) { +const TextEditor: TextEditorComponent = ({ rows, value, onChange, disabled, className }) => { + if (rows && rows > 1) { return ( ({}); + const reloadStore = useStoreReload(); useEffect(() => { if (connectionOptionsZodType) { const description = connectionOptionsZodType.description; if (description) { - console.log("limitations", description); try { const obj = JSON.parse(description); setLimitations(obj.limitations || {}); } catch (e) { console.error(e); } + } else { + setLimitations({}); } } }, [connectionOptionsZodType]); + const usesBulker = destinationType.usesBulker || destinationType.id === "webhook"; + const configItems: EditorItem[] = [ + { + name: "Enabled", + component: ( + { + updateOptions({ disabled: !enabled }); + }} + /> + ), + }, { name: existingLink ? "Select Source" : "Source", documentation: "Select destination to connect", - component: , + component: ( + + ), }, { name: existingLink ? "Select Destination" : "Destination", @@ -333,7 +316,9 @@ function ConnectionEditor({ { setDstId(id); updateConnectionOptions(id); @@ -359,6 +344,7 @@ function ConnectionEditor({ component: ( updateOptions({ mode })} /> @@ -370,21 +356,66 @@ function ConnectionEditor({ name: "Sync Frequency", component: ( updateOptions({ frequency })} /> ), }); + configItems.push({ + name: "Max Batch Size", + documentation: ( + <> + Maximum number of events to send in one batch. If there are more events in queue than 'Batch Size', events + will be sent as a sequence of batches with provided size. + + ), + component: ( + updateOptions({ batchSize: batchSize ?? 10000 })} + /> + ), + }); } if (hasZodFields(connectionOptionsZodType, "dataLayout")) { configItems.push({ documentation: <>Data layout defines how data is written to database, name: "Data Layout", component: ( - updateOptions({ dataLayout })} - value={connectionOptions.dataLayout || "segment-single-table"} - /> + + {" "} + { + if (existingLink) updateOptions({ dataLayout }); + else { + if (dataLayout === "jitsu-legacy") { + updateOptions({ + dataLayout, + primaryKey: "eventn_ctx_event_id", + timestampColumn: "_timestamp", + keepOriginalNames: true, + }); + } else { + updateOptions({ + dataLayout, + primaryKey: connectionOptionsZodType.parse({}).primaryKey || "message_id", + timestampColumn: "timestamp", + }); + } + } + }} + value={connectionOptions.dataLayout || "segment-single-table"} + /> + ), }); } @@ -397,22 +428,29 @@ function ConnectionEditor({ The unique field that should present in any event. In case of duplicated values, the new event will either replace the old one or be dropped as 'failed' event depending on 'Deduplicate' option. Don't change this field unless you know what you are doing. +
+
+ Keep in mind that for data warehouses, all field names get translated to snake_case. For example,{" "} + messageId get translated to message_id. ), component: ( - { - updateOptions({ primaryKey }); - }} - /> + + {" "} + { + updateOptions({ primaryKey, ...(primaryKey === "" ? { deduplicate: false } : {}) }); + }} + /> + ), }); } if (hasZodFields(connectionOptionsZodType, "deduplicate")) { configItems.push({ - group: "Advanced", name: "Deduplicate", documentation: ( <> @@ -421,17 +459,59 @@ function ConnectionEditor({ ), component: ( - { - let functions = connectionOptions.functions ?? []; - if (!deduplicate) { - // remove user recognition function when deduplication is disabled - functions = functions.filter(f => f.functionId !== "builtin.transformation.user-recognition"); - } - updateOptions({ deduplicate, functions }); - }} +
+ { + let functions = connectionOptions.functions ?? []; + if (!deduplicate) { + // remove user recognition function when deduplication is disabled + functions = functions.filter(f => f.functionId !== "builtin.transformation.user-recognition"); + } + updateOptions({ deduplicate, functions }); + }} + /> + {destinationType.id === "bigquery" && ( +
+ Deduplication relies on reading data from the database. Using the Deduplication feature may therefore lead + to higher BigQuery costs. These additional costs increase linearly with the frequency of syncs. +
+ )} +
+ ), + }); + } + if ( + hasZodFields(connectionOptionsZodType, "deduplicateWindow") && + (destinationType.id === "bigquery" || destinationType.id === "redshift") + ) { + configItems.push({ + group: "Advanced", + documentation: ( + <> + Limits date range on which deduplication is performed by reducing lookup to historic data. That may + significantly reduce cost or duration of data processing during inserting batches with 'Deduplicate' option. + 'Timestamp Column' is used as a parameter that defines date range. + + ), + name: "Deduplicate Window", + component: ( + updateOptions({ deduplicateWindow: deduplicateWindow ?? 31 })} /> ), }); @@ -444,13 +524,17 @@ function ConnectionEditor({ ), name: "Timestamp Column", component: ( - { - updateOptions({ timestampColumn }); - }} - /> + + {" "} + { + updateOptions({ timestampColumn }); + }} + /> + ), }); } @@ -466,7 +550,7 @@ function ConnectionEditor({ name: "Identity Stitching", component: ( + By default, Jitsu automatically creates table columns for any new properties in events. If your table is + already in desired state and you want to control further table schema changes you can enable{" "} + Schema Freeze +
+ With Schema Freeze enabled Jitsu will no longer apply changes to table schema, but incoming data for + any properties that don't have corresponding columns will be stored in _unmapped_data column in + JSON format. + + ), + name: "Schema Freeze", + component: ( + { + updateOptions({ schemaFreeze: sf }); + }} + /> + ), + }); + } + if (hasZodFields(connectionOptionsZodType, "keepOriginalNames") && connectionOptions.dataLayout !== "jitsu-legacy") { + configItems.push({ + group: "Advanced", + documentation: ( + <> + By default, Jitsu translate all event properties to snakeCase, e.g.: myPropertyName is translated + to my_property_name.
+ Enable this option if you want to keep original names + + ), + name: "Keep Original Names", + component: ( + + {" "} + { + const opts = { keepOriginalNames: sf } as any; + if (sf && connectionOptions.primaryKey === "message_id") { + opts.primaryKey = "messageId"; + } else if (!sf && connectionOptions.primaryKey === "messageId") { + opts.primaryKey = "message_id"; + } + updateOptions(opts); + }} + /> + + ), + }); + } + if (hasZodFields(connectionOptionsZodType, "clickhouseSettings") && destinationType.id === "clickhouse") { + configItems.push({ + group: "Advanced", + name: "Clickhouse Settings", + documentation: ( + <> + + Clickhouse Settings + {" "} + are used to configure Clickhouse connection. Format is key=value separated by new line. This + settings will be merged with Parameters configured in destination. + + ), + component: ( + { + updateOptions({ clickhouseSettings }); + }} + /> + ), + }); + } if (hasZodFields(connectionOptionsZodType, "events")) { configItems.push({ documentation: ( @@ -502,6 +675,7 @@ function ConnectionEditor({ component: ( { @@ -526,6 +700,7 @@ function ConnectionEditor({ name: "Hosts Allowlist", component: ( f.functionId === functionId) !== "undefined"} - onChange={enabled => { - const f = (connectionOptions.functions ?? []).filter(f => f.functionId !== functionId); - if (enabled) { - f.push({ - functionId: functionId, - }); - } - updateOptions({ functions: f }); - }} - /> - ), - }); - } - } + // if (hasZodFields(connectionOptionsZodType, "multithreading")) { + // configItems.push({ + // group: "Advanced", + // documentation: ( + // <> + // Use multiple threads to send data to the endpoint. Faster, but doesn't guarantee order of events. Recommended + // on high volumes of data. + // + // ), + // name: "Multithreading", + // component: ( + // { + // updateOptions({ multithreading: m }); + // }} + // /> + // ), + // }); + // } return (
-
+

{(existingLink ? "Edit" : "Create") + " connection"}

- } type="link" size="small" onClick={() => router.back()}> - Back - +
+ {existingLink && ( +
+ , + href: "#", + onClick: () => { + copyTextToClipboard(existingLink.id); + feedbackSuccess("Copied to clipboard"); + }, + }, + { + title: "Logs", + icon: , + href: toURL(`/${workspace.slugOrId}/data`, { + query: JSON5.stringify({ + activeView: usesBulker ? "bulker" : "function", + viewState: { [usesBulker ? "bulker" : "function"]: { actorId: existingLink.id } }, + }), + }), + }, + ].filter(Boolean) as any + } + className="mb-4" + /> +
+ )}
+ Functions} + hideArrow={false} + caretSize="1.5em" + contentLeftPadding={false} + > + s.id === srcId) || streams[0]} + destination={destinations.find(d => d.id === dstId) || destinations[0]} + selectedFunctions={connectionOptions.functions} + onChange={enabledFunctions => { + const nonUdfs = (connectionOptions.functions ?? []).filter(f => !f.functionId.startsWith("udf.")); + const enabledF = enabledFunctions.map(f => ({ functionId: `udf.${f.id}` })); + if (nonUdfs.length > 0) { + enabledF.push(...nonUdfs); + } + updateOptions({ functions: enabledF }); + }} + /> + + Environment Variables} + hideArrow={false} + caretSize="1.5em" + contentLeftPadding={false} + > +
+ Provided variables can be used inside functions via process.env object. For example:{" "} + process.env.DEBUG +
+ updateOptions({ functionsEnv })} + /> +
{existingLink && ( - + )}
- - +
diff --git a/webapps/console/components/ConnectionsDiagram/ConnectionsDiagram.tsx b/webapps/console/components/ConnectionsDiagram/ConnectionsDiagram.tsx new file mode 100644 index 000000000..ddd9b365b --- /dev/null +++ b/webapps/console/components/ConnectionsDiagram/ConnectionsDiagram.tsx @@ -0,0 +1,397 @@ +import React, { ReactNode, RefObject, useEffect } from "react"; +import { branding } from "../../lib/branding"; +import { getLog, requireDefined } from "juava"; +import Link from "next/link"; +import { useAppConfig, useWorkspace } from "../../lib/context"; +import { ArrowRight, ExternalLink, Inbox } from "lucide-react"; +import { Tooltip } from "antd"; +import { PlusOutlined } from "@ant-design/icons"; +import classNames from "classnames"; +import { isTruish } from "../../lib/shared/chores"; +import { useRouter } from "next/router"; +import { WJitsuButton } from "../JitsuButton/JitsuButton"; +import { WLink } from "../Workspace/WLink"; + +export type ConnectorNode = { + id: string; + card: (forceSelect: boolean) => ReactNode; +}; +export type Actions = { + title: ReactNode; + newLink?: string; + editLink: string; + newLinkTooltip?: string; +}; +export type ConnectionDiagramProps = { + sources: ConnectorNode[]; + destinations: ConnectorNode[]; + connectorSources: ConnectorNode[]; + connectorSourcesActions: Actions; + srcActions: Actions; + dstActions: Actions; + connections: { + from: string; + to: string; + }[]; +}; + +function indexArray(arr: T[]): Record { + return arr.reduce((acc, s, idx) => ({ ...acc, [s.id]: idx }), {}); +} + +export type Point = { left: number; top: number }; + +export type ConnectorLine = { + from: Point; + to: Point; + selected: boolean; +}; + +export function getRelativePosition(parent: Element, child: Element): Point { + const parentRect = parent.getBoundingClientRect(); + const childRect = child.getBoundingClientRect(); + return { + left: childRect.left - parentRect.left, + top: childRect.top - parentRect.top, + }; +} + +const Header: React.FC = p => { + return ( +
+

+ {p.title} +

+
+ + View All + + {p.hasData && p.newLink && ( + + } href={p.newLink} disabled={!p.newLink}> + Add + + + )} +
+
+ ); +}; + +export function EmptyList({ + children, + title, + createLink, + footer, +}: { + children: ReactNode; + title: ReactNode; + createLink?: string; + footer?: ReactNode; +}) { + return ( +
+ +

{title}

+

{children}

+ + + Create + + + {footer} +
+ ); +} + +export const ConnectionsDiagram: React.FC = ({ + connections, + sources, + connectorSources, + destinations, + ...p +}) => { + const canvasRef = React.useRef(null); + const srcRefs = React.useRef[]>([]); + const dstRefs = React.useRef[]>([]); + const connectorsRef = React.useRef[]>([]); + const logoRef = React.useRef(null); + const [windowSize, setWindowSize] = React.useState<{ width: number; height: number } | undefined>(); + const [lines, setLines] = React.useState([]); + const [mouseOverSrc, setMouseOverSrc] = React.useState(); + const [mouseOverDst, setMouseOverDst] = React.useState(); + const workspaces = useWorkspace(); + const appConfig = useAppConfig(); + const router = useRouter(); + const [forceSelectDestination, setForceSelectDestination] = React.useState([]); + const [forceSelectSource, setForceSelectSource] = React.useState([]); + const emptySitesRef = React.useRef(null); + const emptyConnectorsRef = React.useRef(null); + const emptyDestinationsRef = React.useRef(null); + useEffect(() => { + const resizeListener = () => setWindowSize({ width: window.innerWidth, height: window.innerHeight }); + window.addEventListener("resize", resizeListener); + + return () => window.removeEventListener("resize", resizeListener); + }); + + useEffect(() => { + getLog().atInfo().log(`Rendering connections diagram with ${connections.length} connections`); + if (canvasRef.current == null || logoRef.current == null) { + return; + } + const logoPosition = getRelativePosition( + requireDefined(canvasRef.current, `Canvas is not here`), + requireDefined(logoRef.current, `Logo is not here`) + ); + const logoBounds = logoRef.current.getBoundingClientRect(); + const newLines: ConnectorLine[] = []; + srcRefs.current + .map(r => r.current) + .filter(r => !!r) + .forEach((r, idx) => { + const rel = getRelativePosition(canvasRef.current!, r!); + const bounds = r!.getBoundingClientRect(); + const source = sources[idx]; + const selected = + mouseOverSrc === source.id || + (!!mouseOverDst && !!connections.find(c => c.from === source.id && c.to === mouseOverDst)); + if (connections.find(c => c.from === source.id)) { + newLines.push({ + from: { top: rel.top + bounds.height / 2, left: rel.left + bounds.width }, + to: { left: logoPosition.left, top: logoPosition.top + logoBounds.height / 2 }, + selected, + }); + } + }); + getLog().atDebug().log(`Rendering ${connectorsRef.current.length} connectors`, connectorsRef.current); + connectorsRef.current + .map(r => r.current) + .filter(r => !!r) + .forEach((r, idx) => { + const rel = getRelativePosition(canvasRef.current!, r!); + const bounds = r!.getBoundingClientRect(); + const source = connectorSources[idx]; + const selected = + mouseOverSrc === source.id || + (!!mouseOverDst && !!connections.find(c => c.from === source.id && c.to === mouseOverDst)); + if (connections.find(c => c.from === source.id)) { + getLog().atInfo().log(`Adding connector from ${source.id} to logo (select=${selected})`); + newLines.push({ + from: { top: rel.top + bounds.height / 2, left: rel.left + bounds.width }, + to: { left: logoPosition.left, top: logoPosition.top + logoBounds.height / 2 }, + selected, + }); + } else { + getLog().atWarn().log(`Source ${source.id} has no connections`); + } + }); + dstRefs.current + .map(r => r.current) + .filter(r => !!r) + .forEach((r, idx) => { + const rel = getRelativePosition(canvasRef.current!, r!); + const bounds = r!.getBoundingClientRect(); + const destination = destinations[idx]; + const selected = + mouseOverDst === destination.id || + (!!mouseOverSrc && !!connections.find(c => c.to === destination.id && c.from === mouseOverSrc)); + if (selected) { + setForceSelectDestination([destination.id]); + setForceSelectSource(connections.filter(c => c.to === destination.id).map(c => c.from)); + } + if (connections.find(c => c.to === destination.id)) { + newLines.push({ + to: { top: rel.top + bounds.height / 2, left: rel.left }, + from: { left: logoPosition.left + logoBounds.width, top: logoPosition.top + logoBounds.height / 2 }, + selected, + }); + } + }); + if (emptySitesRef.current) { + const rel = getRelativePosition(canvasRef.current!, emptySitesRef.current!); + const bounds = emptySitesRef.current!.getBoundingClientRect(); + newLines.push({ + from: { top: rel.top + bounds.height / 2, left: rel.left + bounds.width }, + to: { left: logoPosition.left, top: logoPosition.top + logoBounds.height / 2 }, + selected: false, + }); + } + if (emptyConnectorsRef.current) { + const rel = getRelativePosition(canvasRef.current!, emptyConnectorsRef.current!); + const bounds = emptyConnectorsRef.current!.getBoundingClientRect(); + newLines.push({ + from: { top: rel.top + bounds.height / 2, left: rel.left + bounds.width }, + to: { left: logoPosition.left, top: logoPosition.top + logoBounds.height / 2 }, + selected: false, + }); + } + if (emptyDestinationsRef.current) { + const rel = getRelativePosition(canvasRef.current!, emptyDestinationsRef.current!); + const bounds = emptyDestinationsRef.current!.getBoundingClientRect(); + newLines.push({ + to: { top: rel.top + bounds.height / 2, left: rel.left }, + from: { left: logoPosition.left + logoBounds.width, top: logoPosition.top + logoBounds.height / 2 }, + selected: false, + }); + } + setLines(newLines); + + if (mouseOverSrc) { + setForceSelectSource([mouseOverSrc]); + setForceSelectDestination(connections.filter(c => c.from === mouseOverSrc).map(c => c.to)); + } else if (mouseOverDst) { + setForceSelectDestination([mouseOverDst]); + setForceSelectSource(connections.filter(c => c.to === mouseOverDst).map(c => c.from)); + } else { + setForceSelectDestination([]); + setForceSelectSource([]); + } + }, [connections, sources, destinations, windowSize, mouseOverSrc, mouseOverDst]); + + srcRefs.current = sources.map((_, i) => srcRefs.current[i] ?? React.createRef()); + dstRefs.current = destinations.map((_, i) => dstRefs.current[i] ?? React.createRef()); + connectorsRef.current = connectorSources.map((_, i) => connectorsRef.current[i] ?? React.createRef()); + + const connectorSection = appConfig.syncs.enabled && ( + +
0} /> + {connectorSources?.length > 0 ? ( + <> + {connectorSources.map((s, idx) => ( +
{ + setMouseOverSrc(s.id); + }} + onMouseLeave={() => setMouseOverSrc(undefined)} + > + {s.card(forceSelectSource.includes(s.id))} +
+ ))} + + ) : ( +
+ + Connectors are used to pull data from external sources like databases, APIs, files, etc. + +
+ )} + + ); + const sitesSection = ( + +
0} /> + {sources?.length > 0 ? ( + <> + {sources.map((s, idx) => ( +
{ + setMouseOverSrc(s.id); + }} + onMouseLeave={() => setMouseOverSrc(undefined)} + > + {s.card(forceSelectSource.includes(s.id))} +
+ ))} + + ) : ( +
+ + Site (or stream) is a source of events which are being pushed to Jitsu via SDK. It could be a website, web + application, mobile application or backend server + +
+ )} + + ); + return ( +
+
+
+ {isTruish(router.query.connectorsFirst) ? [connectorSection, sitesSection] : [sitesSection, connectorSection]} +
+
+
+ + {branding.logo} + + + Connections + +
+
+
+
0} /> + {destinations.map((dest, idx) => ( +
{ + setMouseOverDst(dest.id); + }} + onMouseLeave={() => setMouseOverDst(undefined)} + > + {dest.card(forceSelectDestination.includes(dest.id))} +
+ ))} + {destinations.length === 0 && ( +
+ + Destination is a database or service which accepts data coming from sites or connector + +
+ )} +
+
+ + {lines + .sort((a, b) => { + if (a.selected && !b.selected) { + return 1; + } + if (!a.selected && b.selected) { + return -1; + } + return 0; + }) + .map((line, i) => { + const lineCurves = `C${line.from.left + Math.abs(line.from.left - line.to.left) * 0.75},${line.from.top} ${ + line.to.left - Math.abs(line.from.left - line.to.left) * 0.75 + },${line.to.top} ${line.to.left},${line.to.top}`; + + const path = `M${line.from.left},${line.from.top} ${lineCurves}`; + return ( + + ); + })} + +
+ ); +}; diff --git a/webapps/console/components/DataRentionEditor/DataRentionEditor.tsx b/webapps/console/components/DataRentionEditor/DataRentionEditor.tsx new file mode 100644 index 000000000..b33bb9531 --- /dev/null +++ b/webapps/console/components/DataRentionEditor/DataRentionEditor.tsx @@ -0,0 +1,259 @@ +import { Alert, Button, Form, Input, Skeleton, Switch } from "antd"; +import { useWorkspace } from "../../lib/context"; +import { useQuery } from "@tanstack/react-query"; +import { get } from "../../lib/useApi"; +import { ErrorCard } from "../GlobalError/GlobalError"; +import { ReactNode, useState } from "react"; +import { DataRetentionSettings } from "../../lib/shared/data-retention"; +import { getLog, rpc } from "juava"; +import Link from "next/link"; +import merge from "lodash/merge"; +import { z } from "zod"; +import { useBilling } from "../Billing/BillingProvider"; +import { CheckOutlined } from "@ant-design/icons"; +import { feedbackError, feedbackSuccess } from "../../lib/ui"; + +export const ConfigSection: React.FC<{ title: string; documentation: ReactNode; children: ReactNode }> = ({ + title, + documentation, + children, +}) => { + return ( +
+

{title}

+
{documentation}
+
{children}
+
+ ); +}; + +export const DataRetentionEditorLoader: React.FC<{}> = () => { + const workspace = useWorkspace(); + const dataRetention = useQuery( + ["data-retention", workspace.id], + () => get(`/api/workspace/${workspace.id}/data-retention`), + { retry: false, cacheTime: 0, refetchOnWindowFocus: false } + ); + const billing = useBilling(); + return ( +
+ {(dataRetention.isLoading || billing.loading) && } + {dataRetention.isError && } + {dataRetention.data && ( + + )} +
+ ); +}; + +export const UnitEditor: React.FC<{ name: any; unit: string }> = ({ name, unit }) => { + const form = Form.useFormInstance(); + return ( +
+ + + + + {unit} + {unit === "hours" && form.getFieldValue(name) + ? `, equal to ${parseFloat((parseInt(form.getFieldValue(name)) / 24).toFixed(2))} days` + : ``} + +
+ ); +}; + +export const FormValuesType = DataRetentionSettings.omit({ disableS3Archive: true }).and( + z.object({ + mongoEnabled: z.coerce.boolean(), + enableS3Archive: z.coerce.boolean(), + }) +); + +type FormValuesType = z.infer; + +function fromFormValues({ mongoEnabled, enableS3Archive, ...values }: FormValuesType): DataRetentionSettings { + return DataRetentionSettings.parse({ + ...values, + disableS3Archive: !enableS3Archive, + customMongoDb: mongoEnabled ? values.customMongoDb : undefined, + }); +} + +function toFormValues(obj: DataRetentionSettings): FormValuesType { + return { + ...obj, + mongoEnabled: !!obj.customMongoDb, + enableS3Archive: !obj.disableS3Archive, + }; +} + +export const DataRetentionEditor: React.FC<{ + obj: DataRetentionSettings; + readonly?: boolean; + pendingUpdate: boolean; +}> = ({ obj, readonly, pendingUpdate }) => { + const [form] = Form.useForm(); + const initialFormValues = toFormValues(obj); + const workspace = useWorkspace(); + const [pendingUpdateState, setPendingUpdateState] = useState(pendingUpdate); + const [saving, setSaving] = useState(false); + const [currentObj, setCurrentObj] = useState(initialFormValues); + return ( + <> + {readonly && ( +
+ + Your subscription doesn't allow to change data retention settigns. However, you view Jitsu default + setting to understand how your data is being stored. Contact support@jitsu.com if you have any questions + regarding data retention policy.{" "} + + } + type="info" + showIcon + /> +
+ )} + {pendingUpdateState && ( +
+ + You have requested to change retention policy earlier. Allow use a few days to apply the changes. Please + contact support if you have any questions + + } + type="info" + showIcon + /> +
+ )} + { + const merged = FormValuesType.parse(merge(currentObj, newValues)); + setCurrentObj(merged); + getLog().atDebug().log("Settings", merged); + }} + > +
+ + How many hours to keep data in queues. If destnation is down, Jitsu will try to deliver data during + timeframe configured below. After the time is passed, Jitsu will drop events + + } + > + + + + How many days to keep user events to implement{" "} + identity stitching. After the + time is passed, Jitsu will drop data. 0 will disable identity stitching. + + } + > + + + + Live Events and function logs retention policy. OR logic is applied. The events will be dropped if they + stored in the logs more than X days, or the logs size is more than N records + + } + > +
+ +
+ + +
+ + Jitsu users Mongo DB as underneath mechanism for{" "} + + functions persistent storage + {" "} + and identity stitching. You can + use your own Mongo instance to store this data. + + } + > +
+ + + {" "} + +
+
+ + + +
+
+ + Jitsu archives all incoming event into a dedicated S3 bucket{" "} + {workspace.id}.data.use.jitsu.com so they could be replayed later. You can disable this + behavior if you don't need it. + + } + > +
+ + + + +
+
+
+
+ +
+ + + ); +}; diff --git a/webapps/console/components/DataView/DataView.tsx b/webapps/console/components/DataView/DataView.tsx index 2dde4934e..375f5dbfe 100644 --- a/webapps/console/components/DataView/DataView.tsx +++ b/webapps/console/components/DataView/DataView.tsx @@ -1,12 +1,12 @@ import { useQueryStringState } from "../../lib/useQueryStringState"; import JSON5 from "json5"; import { Tabs, TabsProps } from "antd"; -import React from "react"; +import React, { useCallback } from "react"; import { EventsBrowser } from "./EventsBrowser"; export type DataViewState = { - activeView: "incoming" | "functions" | "bulker"; - viewState: Record<"incoming" | "functions" | "bulker", any>; + activeView: "incoming" | "function" | "bulker"; + viewState: Record<"incoming" | "function" | "bulker", any>; }; export function DataView() { @@ -15,7 +15,7 @@ export function DataView() { //state of nested Tab viewState: { incoming: {}, - functions: {}, + function: {}, bulker: {}, }, }; @@ -32,13 +32,22 @@ export function DataView() { const changeActiveView = (activeView: string) => setState({ ...state, activeView: activeView as DataViewState["activeView"] }); - const patchQueryStringState = (key: string, value: any) => { - if (state.viewState[state.activeView][key] === value) return; - setState({ - ...state, - viewState: { ...state.viewState, [state.activeView]: { ...state.viewState[state.activeView], [key]: value } }, - }); - }; + const patchQueryStringState = useCallback( + (key: string, value: any) => { + if (state.viewState[state.activeView]?.[key] === value) return; + if (value === null) { + const newState = { ...state }; + delete newState[key]; + setState(newState); + } else { + setState({ + ...state, + viewState: { ...state.viewState, [state.activeView]: { ...state.viewState[state.activeView], [key]: value } }, + }); + } + }, + [setState, state] + ); const items: TabsProps["items"] = [ { @@ -53,19 +62,19 @@ export function DataView() { ), }, { - key: "functions", - label: `Functions Log`, + key: "function", + label: `API Destinations & Functions Logs`, children: ( ), }, { key: "bulker", - label: `Data Warehouse Events`, + label: `Batches & Data Warehouse Events`, children: ( dayjs(date).utc().format("YYYY-MM-DD HH:mm:ss"); +const formatDate = (date: string | Date | Dayjs) => dayjs(date).utc().format("YYYY-MM-DD HH:mm:ss"); -type StreamType = "incoming" | "functions" | "bulker"; -type EventType = "all" | "error"; -type DatesRange = [number | null, number | null]; +type StreamType = "incoming" | "function" | "bulker"; +type Level = "all" | "error" | "info" | "debug" | "warn"; +type DatesRange = [string | null, string | null]; type EventsBrowserProps = { streamType: StreamType; - eventType: "all" | "error"; + level: Level; actorId: string; dates: DatesRange; + search?: string; patchQueryStringState: (key: string, value: any) => void; }; type EventsBrowserState = { - bulkerMode: "stream" | "batch"; - entitiesLoading: boolean; - entitiesMap?: Record; + bulkerMode?: "stream" | "batch"; eventsLoading: boolean; - events: EventsLogRecord[]; - beforeId: string; + events?: EventsLogRecord[]; + initDate: Date; refreshTime: Date; + previousRefreshTime?: Date; + beforeDate?: Date; error?: string; }; +const defaultState: EventsBrowserState = { + bulkerMode: undefined, + eventsLoading: false, + events: undefined, + beforeDate: undefined, + refreshTime: new Date(), + initDate: new Date(), +}; + +function eventStreamReducer(state: EventsBrowserState, action: any) { + if (action.type === "patch") { + let ev = state.events; + if (action.value.addEvents) { + ev = [...(state.events ?? []), ...action.value.addEvents]; + delete action.value.addEvents; + } else if (action.value.events) { + ev = action.value.events; + } + return { + ...state, + ...action.value, + events: ev, + }; + } else if (action.type === "resetAndPatch") { + return { + ...state, + error: undefined, + events: undefined, + beforeDate: undefined, + refreshTime: state.initDate, + ...action.value, + }; + } + return { + ...state, + [action.type]: action.value, + }; +} + export const UTCHeader: React.FC<{}> = () => { return ( = ({ date }) => { ); }; -export const RelativeDate: React.FC<{ date: string | Date }> = ({ date }) => { +export const RelativeDate: React.FC<{ date: string | Date; fromNow?: boolean }> = ({ date, fromNow = true }) => { return ( - {`${dayjs(date).fromNow(true)} ago`} + {fromNow ? `${dayjs(date).fromNow(true)} ago` : `${dayjs(date).toNow(true)}`} ); }; -export const EventsBrowser = ({ +const useMap = (initialValue: any[]) => { + return useMemo(() => arrayToMap(initialValue), [initialValue]); +}; + +const DebouncedInput = ({ value, onChange, debounceMs, ...props }: any) => { + const [state, setState] = useState(value); + useEffect(() => { + setState(value); + }, [value]); + const debouncedChange = useMemo(() => debounce(onChange, debounceMs || 500), [debounceMs, onChange]); + return ( + { + setState(e.target.value); + debouncedChange(e.target.value); + }} + /> + ); +}; + +const EventsBrowser0 = ({ streamType = "incoming", - eventType = "all", + level = "all", actorId = "", dates, + search, patchQueryStringState, }: EventsBrowserProps) => { const workspace = useWorkspace(); const entityType = streamType === "incoming" ? "stream" : "link"; + const connections = useConfigObjectLinks(); + const streams = useConfigObjectList("stream"); + const streamsMap = useMap(streams); + const services = useConfigObjectList("service"); + const servicesMap = useMap(services); + const destinations = useConfigObjectList("destination"); + const destinationsMap = useMap(destinations); + const profileBuilders = useProfileBuilders(); + const hasActiveProfileBuilder = profileBuilders.some(p => p.version > 0); + const mappedConnections = useMemo( + () => + connections + .filter(c => streamType === "bulker" || c.type === "push") + .map(link => { + const dst = destinationsMap[link.toId]; + const destinationType = coreDestinationsMap[dst?.destinationType]; + return { + id: link.id, + name: `${streamsMap[link.fromId]?.name ?? "DELETED"} → ${destinationsMap[link.toId]?.name ?? "DELETED"}`, + mode: link.type === "sync" ? "batch" : link.data?.mode, + stream: streamsMap[link.fromId], + service: servicesMap[link.fromId], + destination: dst, + usesBulker: destinationType?.usesBulker || false, + hybrid: destinationType?.hybrid || false, + //usesFunctions: Array.isArray(link.data?.functions) && link.data?.functions.length > 0, + }; + }), + [connections, destinationsMap, servicesMap, streamType, streamsMap] + ); + const mappedConnectionsMap = useMap(mappedConnections); + const entities = useMemo(() => { + return streamType == "incoming" + ? streams + : streamType == "function" + ? [ + ...mappedConnections, + ...profileBuilders + .filter(p => p.version > 0) + .map(p => { + return { + ...p, + type: "profile-builder", + }; + }), + ] + : [ + ...mappedConnections.filter(link => link.usesBulker || link.hybrid), + ...(hasActiveProfileBuilder + ? destinations + .filter(dst => { + const destinationType = coreDestinationsMap[dst?.destinationType]; + return destinationType?.usesBulker; + }) + .map(dst => { + return { + id: dst.id, + name: "Profile Builder", + mode: "batch", + destination: dst, + usesBulker: true, + type: "profile-builder", + }; + }) + : []), + ]; + }, [destinations, destinationsMap, mappedConnections, profileBuilders, streamType, streams]); - const defaultState: EventsBrowserState = { - bulkerMode: "stream", - entitiesLoading: false, - entitiesMap: undefined, - eventsLoading: false, - events: [], - beforeId: "", - refreshTime: new Date(), - }; + const entitiesMap = useMemo(() => { + return streamType == "incoming" ? streamsMap : arrayToMap(entities as { id: any }[]); + }, [streamType, streamsMap, entities]); - function eventStreamReducer(state: EventsBrowserState, action: any) { - if (action.type === "addEvents") { - return { - ...state, - events: [...state.events, ...action.value], - }; + const entitiesSelectOptions = useMemo(() => { + if (entitiesMap) { + return Object.entries(entitiesMap).map(entity => ({ + value: entity[0], + label: + entity[1].type === "stream" ? ( + + ) : entity[1].type === "profile-builder" ? ( + + ) : ( + + ), + search: (entity[1].type === "stream" + ? [entity[1].name] + : entity[1].type === "profile-builder" + ? [entity[1].name, entity[1].destination?.name] + : [entity[1].stream?.name, entity[1].service?.name, entity[1].destination?.name] + ) + .filter(s => s !== undefined) + .map(s => s.toLowerCase()), + })); + } else { + return []; } - return { - ...state, - [action.type]: action.value, - }; - } + }, [entitiesMap]); - const [{ bulkerMode, entitiesLoading, entitiesMap, eventsLoading, events, beforeId, refreshTime, error }, dispatch] = - useReducer(eventStreamReducer, defaultState); + const [connection, setConnection] = useState(undefined); + const [ + { bulkerMode, eventsLoading, events, beforeDate, initDate, refreshTime, previousRefreshTime, error }, + dispatch, + ] = useReducer(eventStreamReducer, defaultState, d => { + const initDate = new Date(); + return { ...d, refreshTime: initDate, initDate }; + }); - const eventsLogApi = useEventsLogApi(); + const [shownEvents, setShownEvents] = useState([]); - const loadEvents = useCallback( - async ( - streamType: StreamType, - entitiesMap: any, - eventType: EventType, - actorId: string, - beforeId: string, - dates: DatesRange - ) => { - try { - if (actorId && entitiesMap && entitiesMap[actorId]) { - let eventsLogStream = streamType as string; - if (streamType === "bulker") { - const entity = entitiesMap[actorId]; - if (entity.mode === "stream") { - dispatch({ type: "bulkerMode", value: "stream" }); - eventsLogStream = "bulker_stream"; - } else if (entity.mode === "batch") { - dispatch({ type: "bulkerMode", value: "batch" }); - eventsLogStream = "bulker_batch"; - } - } - dispatch({ type: "eventsLoading", value: true }); - const data = await eventsLogApi.get( - `${eventsLogStream}.${eventType}`, - actorId, - { - beforeId: beforeId, - start: dates && dates[0] ? dayjs.unix(dates[0]).toDate() : undefined, - end: dates && dates[1] ? dayjs.unix(dates[1]).endOf("day").toDate() : undefined, - }, - 100 - ); - if (beforeId) { - dispatch({ type: "addEvents", value: data }); - } else { - dispatch({ type: "events", value: data }); - } - if (data.length > 0) { - dispatch({ type: "beforeId", value: data[data.length - 1].id }); - } - dispatch({ type: "error", value: "" }); - } - } catch (e) { - console.error("Error while loading events", e); - dispatch({ type: "error", value: "Error while loading events" }); - } finally { - dispatch({ type: "eventsLoading", value: false }); - } + const searchFunc = useCallback( + value => { + dispatch({ + type: "resetAndPatch", + value: {}, + }); + patchQueryStringState("search", value); }, - [eventsLogApi] + [patchQueryStringState] ); - //load entities + const [debugEnabled, setDebugEnabled] = useState(false); + + const onSaveMutation = useConfigObjectLinkMutation(async (obj: any) => { + await get(`/api/${workspace.id}/config/link`, { + body: obj, + }); + }); + + const eventsLogApi = useEventsLogApi(); + + const entitySelectRef = React.createRef(); + useEffect(() => { - (async () => { - if (typeof entitiesMap !== "undefined" || entitiesLoading) { - return; + if (!actorId || !entitiesMap[actorId]) { + if (entities.length > 0) { + patchQueryStringState("actorId", entities[0].id); } - try { - let query: () => Promise; - if (streamType === "incoming") { - query = () => getConfigApi(workspace.id, "stream").list(); - } else { - query = async () => { - const data = await linksQuery(workspace.id)(); - const streamsMap = arrayToMap(data[0]); - const dstMap = arrayToMap(data[1]); - return data[2] - .map(link => ({ - id: link.id, - name: `${streamsMap[link.fromId]?.name ?? "DELETED"} → ${dstMap[link.toId]?.name ?? "DELETED"}`, - mode: link.data?.mode, - stream: streamsMap[link.fromId], - destination: dstMap[link.toId], - usesBulker: typeof link.data?.mode === "string", - //usesFunctions: Array.isArray(link.data?.functions) && link.data?.functions.length > 0, - })) - .filter(link => (streamType === "bulker" && link.usesBulker) || streamType === "functions"); - }; - } + } + }, [actorId, entities, patchQueryStringState, entitiesMap]); - dispatch({ type: "entitiesLoading", value: true }); + useEffect(() => { + if (events) { + setShownEvents(events); + } + }, [events]); - const data = await query(); - if (data.length > 0) { - const mp = arrayToMap(data); - dispatch({ type: "entitiesMap", value: mp }); - if (!actorId || !mp[actorId]) { - patchQueryStringState("actorId", data[0].id); - } + useEffect(() => { + if (streamType === "function" && actorId) { + (async () => { + const connection = connections.find(c => c.id === actorId); + if (connection) { + setConnection(connection); + setDebugEnabled(new Date(connection.data.debugTill) > new Date()); } else { - dispatch({ type: "entitiesMap", value: {} }); + setConnection(undefined); } - dispatch({ type: "error", value: "" }); - } catch (e) { - console.error("Error while loading entities objects", e); - dispatch({ type: "error", value: "Error while loading entities objects" }); - } finally { - dispatch({ type: "entitiesLoading", value: false }); + })(); + } + }, [actorId, connections, streamType, workspace.id]); + + useEffect(() => { + const interval = setInterval(() => { + if (connection) { + setDebugEnabled(new Date(connection.data.debugTill) > new Date()); } - })(); - }, [streamType, entitiesMap, actorId, workspace.id, patchQueryStringState, entitiesLoading]); + }, 1000); + return () => clearInterval(interval); + }, [connection]); useEffect(() => { - loadEvents(streamType, entitiesMap, eventType, actorId, "", dates); - }, [loadEvents, streamType, entitiesMap, eventType, actorId, dates, refreshTime]); + if (actorId && entitiesMap && entitiesMap[actorId]) { + // beforeDate set to undefined along with any query changes or "Refresh" button click + // refreshTime !== previousRefreshTime - on "Load previous events button" click + if (!beforeDate || refreshTime !== previousRefreshTime) { + let cancelled = false; + + dispatch({ type: "eventsLoading", value: true }); + (async () => { + let error = ""; + let newBeforeDate: Date | undefined = undefined; + let events: EventsLogRecord[] | undefined = undefined; + let addEvents: EventsLogRecord[] | undefined = undefined; + try { + let eventsLogStream = streamType as string; + if (streamType === "bulker") { + if (!bulkerMode) { + const entity = entitiesMap[actorId]; + dispatch({ type: "bulkerMode", value: entity.mode }); + return; + } + eventsLogStream = "bulker_" + bulkerMode; + } + const data = await eventsLogApi.get( + `${eventsLogStream}`, + level === "all" ? "all" : [level], + actorId, + { + start: dates && dates[0] ? new Date(dates[0]) : undefined, + end: beforeDate || (dates && dates[1] ? new Date(dates[1]) : undefined), + }, + 100, + search + ); + if (beforeDate) { + addEvents = data; + } else { + events = data; + } + if (data.length > 0) { + const d = dayjs(data[data.length - 1].date); + newBeforeDate = d.toDate(); + } + } catch (e) { + console.error("Error while loading events", e); + error = "Error while loading events"; + } finally { + if (!cancelled) { + const patch = { + previousRefreshTime: refreshTime || new Date(), + eventsLoading: false, + } as any; + if (error) { + patch.error = error; + } else { + patch.error = undefined; + if (addEvents) { + patch.addEvents = addEvents; + } else if (events) { + patch.events = events; + } + } + if (newBeforeDate) { + patch.beforeDate = newBeforeDate; + } + dispatch({ type: "patch", value: patch }); + } + } + })(); + return () => { + cancelled = true; + }; + } + } + }, [ + eventsLogApi, + streamType, + entitiesMap, + level, + actorId, + dates, + bulkerMode, + previousRefreshTime, + refreshTime, + beforeDate, + search, + ]); // //load more events on reaching bottom // useEffect(() => { @@ -233,7 +444,7 @@ export const EventsBrowser = ({ // const scrolling_function = e => { // const div = document.getElementsByClassName("global-wrapper")[0]; // if (div.scrollHeight - div.scrollTop == div.clientHeight) { - // if (!eventsLoading && beforeId) { + // if (!eventsLoading && beforeDate) { // force += -e.wheelDeltaY; // document.getElementById("lmore")!.style.transform = `scale(${ // 1 + Math.max(0, Math.min(force / 6000, 0.333)) @@ -241,7 +452,7 @@ export const EventsBrowser = ({ // if (force > 2000) { // force = 0; // window.removeEventListener("wheel", scrolling_function); - // loadEvents(streamType, entitiesMap, eventType, actorId, beforeId, dates); + // loadEvents(streamType, entitiesMap, eventType, actorId, beforeDate, dates); // } // if (force < 0) { // force = 0; @@ -256,33 +467,13 @@ export const EventsBrowser = ({ // return () => { // window.removeEventListener("wheel", scrolling_function); // }; - // }, [loadEvents, eventsLoading, streamType, entitiesMap, eventType, actorId, dates, refreshTime, beforeId]); - - const entitiesSelectOptions = useMemo(() => { - if (entitiesMap) { - return Object.entries(entitiesMap).map(entity => ({ - value: entity[0], - label: - entity[1].type === "stream" ? ( - entity[1].name - ) : ( - - {entity[1].stream.name} - {"→"} - d.name} /> - - ), - })); - } else { - return []; - } - }, [entitiesMap]); + // }, [loadEvents, eventsLoading, streamType, entitiesMap, eventType, actorId, dates, refreshTime, beforeDate]); const TableElement: React.FC = (function () { switch (streamType) { case "incoming": return IncomingEventsTable; - case "functions": + case "function": return FunctionsLogTable; case "bulker": if (bulkerMode === "batch") { @@ -296,90 +487,210 @@ export const EventsBrowser = ({ })(); return ( <> - -
- {entityType == "stream" ? "Sites: " : "Connection: "} - { - dispatch({ type: "events", value: [] }); - dispatch({ type: "beforeId", value: "" }); - patchQueryStringState("eventType", e); - }} - options={[ - { value: "all", label: "All" }, - { value: "error", label: "Errors only" }, - ]} - /> - - - Date range: - (d ? dayjs.unix(d) : null)).slice(0, 2) as [Dayjs | null, Dayjs | null] - } - allowEmpty={[true, true]} - onChange={d => { - if (d) { - patchQueryStringState( - "dates", - d.map(d => (d ? d.unix() : null)) - ); - } else { - patchQueryStringState("dates", [null, null]); +
+
+
+
+ {entityType == "stream" ? "Site: " : "Connection: "} + { + dispatch({ + type: "resetAndPatch", + value: {}, + }); + patchQueryStringState("level", e); + }} + options={ + streamType == "function" + ? [ + { value: "all", label: "All" }, + { value: "error", label: "ERROR" }, + { value: "warn", label: "WARN" }, + { value: "info", label: "INFO" }, + { value: "debug", label: "DEBUG" }, + ] + : [ + { value: "all", label: "All" }, + { value: "error", label: "Errors" }, + ] + } + /> +
+ {streamType === "bulker" && ( +
+ Mode: +
+ ); +}; + +const IncomingEventDrawer = ({ + event, + mappedConnections, +}: { + event: IncomingEvent; + mappedConnections?: Record; +}) => { const drawerColumns: ColumnsType = [ { title: "Name", @@ -702,30 +1099,28 @@ const IncomingEventDrawer = ({ event }: { event: IncomingEvent }) => { const drawerData = useMemo(() => { const drawerData: { name: ReactNode; value: ReactNode }[] = []; if (event) { - const DestinationsList = (props: { - loading: boolean; - destinationsMap: Record; - destinationIds: string[]; - }) => { - return props.loading ? ( - - ) : ( - + const DestinationsList = (props: { mappedConnections: Record; destinationIds: string[] }) => { + return ( +
{props.destinationIds - .map(d => props.destinationsMap[d]?.destination) + .map(d => props.mappedConnections[d]?.destination) .filter(d => typeof d !== "undefined") .map((d, i) => ( - d.name} /> + ))} - +
); }; drawerData.push({ name: , value: }); + drawerData.push({ name: "Source", value: event.ingestType }); drawerData.push({ name: "Message ID", value: event.messageId }); drawerData.push({ name: "Type", value: event.type }); + if (event.event?.event) { + drawerData.push({ name: "Track Event Name", value: event.event.event }); + } drawerData.push({ name: "Status", value: (st => { @@ -754,25 +1149,23 @@ const IncomingEventDrawer = ({ event }: { event: IncomingEvent }) => { drawerData.push({ name: "Page URL", value: ( - <> +
{" "} {event.pageURL} - +
), }); drawerData.push({ name: "Destinations", - value: ( - - ), + value: , }); drawerData.push({ name: "Jitsu Domain", value: event.originDomain, }); - drawerData.push({ name: "Write Key", value: event.writeKey }); + drawerData.push({ name: "Write Key", value: {event.writeKey} }); drawerData.push({ name: "HTTP Headers", value: ( @@ -789,14 +1182,18 @@ const IncomingEventDrawer = ({ event }: { event: IncomingEvent }) => { { dataIndex: "name", width: "14em", className: "font-mono" }, { dataIndex: "value", className: "break-all font-mono" }, ]} - dataSource={Object.entries(event.httpHeaders).map((d, i) => { - let name = d[0]; - let value = d[1]; - if (name.toLowerCase() === "authorization") { - value = "*** MASKED ***"; - } - return { name, value }; - })} + dataSource={ + event.httpHeaders + ? Object.entries(event.httpHeaders).map((d, i) => { + let name = d[0]; + let value = d[1]; + if (name.toLowerCase() === "authorization") { + value = "*** MASKED ***"; + } + return { name, value }; + }) + : undefined + } /> @@ -808,7 +1205,7 @@ const IncomingEventDrawer = ({ event }: { event: IncomingEvent }) => { }); } return drawerData; - }, [event, destinationsMap, loading]); + }, [event, mappedConnections]); return event ? (
{ ); }; +const Flag: React.FC<{ emoji?: string }> = ({ emoji }) => { + return ( + + {emoji || "🇺🇸"} + + ); +}; + +function googleMapsLink(lat: number, lng: number) { + return `https://www.google.com/maps/search/?api=1&query=${lat},${lng}`; +} + +//we should make sure that Geo object is typed in a common module. +//it is typed, but in functions lib only. +export const Geo: React.FC<{ geo?: aGeo }> = ({ geo }) => { + if (geo?.country?.code) { + const flag = countries[geo.country.code]?.flag; + if (!flag) { + return <>; + } + return ( + + {[ + `Country: ${countries[geo.country.code]?.name || geo.country.code}`, + geo.region?.code ? `Region: ${geo.region?.code}` : undefined, + geo.city?.name ? `City: ${geo.city.name}` : undefined, + ] + .filter(s => !!s) + .join("\n")} + {"\n\n"} + {geo.location && geo.location.latitude && geo.location.latitude ? ( + <> + Location:{" "} + + {geo.location.latitude}, {geo.location.longitude} + + + ) : undefined} + + } + > + {/* Without the space after the tag below, tooltip doesn't work. Don't delete it! */} + {" "} + + ); + } + return <>; +}; + type IncomingEvent = { id: string; date: string; + ingestType: string; status: string; error: string; @@ -855,14 +1305,22 @@ type IncomingEvent = { destinations: string[]; }; -const IncomingEventsTable = ({ loadEvents, loading, streamType, entityType, actorId, events }: TableProps) => { +const IncomingEventsTable = ({ + loadEvents, + loading, + streamType, + entityType, + actorId, + events, + mappedConnections, +}: TableProps) => { const appConfig = useAppConfig(); const mapEvents = evs => evs - ? evs.map(ev => { + ? evs.map((ev, i) => { let ingestPayload: any = {}; let unparsedPayload = ""; - if (typeof ev.content.body === "string") { + if (typeof ev.content.body === "string" && ev.content.body.length > 0) { unparsedPayload = ev.content.body; try { ingestPayload = JSON.parse(ev.content.body); @@ -874,8 +1332,10 @@ const IncomingEventsTable = ({ loadEvents, loading, streamType, entityType, acto const context = event?.context; return { - id: ev.id, + id: ev.date + "_" + i, date: ev.date, + ingestType: ingestPayload.ingestType, + status: ev.content.status, error: ev.content.error, @@ -885,7 +1345,10 @@ const IncomingEventsTable = ({ loadEvents, loading, streamType, entityType, acto messageId: ingestPayload.messageId, type: ingestPayload.type, originDomain: - ingestPayload.origin?.domain || `${ingestPayload.origin?.slug}.${appConfig.publicEndpoints.dataHost}`, + ingestPayload.origin?.domain || + (ingestPayload.origin?.slug + ? `${ingestPayload.origin?.slug}.${appConfig.publicEndpoints.dataHost}` + : ingestPayload.httpHeaders?.["x-forwarded-host"] || appConfig.publicEndpoints.dataHost), writeKey: ingestPayload.writeKey, httpHeaders: ingestPayload.httpHeaders, @@ -909,6 +1372,7 @@ const IncomingEventsTable = ({ loadEvents, loading, streamType, entityType, acto const columns: ColumnsType = [ { title: "", + key: "status", width: "2em", dataIndex: "status", render: d => { @@ -927,78 +1391,114 @@ const IncomingEventsTable = ({ loadEvents, loading, streamType, entityType, acto { title: , dataIndex: "date", + key: "date", render: d => , width: "12em", }, { + key: "type", title: "Type", - width: "8em", - dataIndex: "type", + width: "12em", + //dataIndex: "type", + render: (d: IncomingEvent) => { + const eventName = d.type === "track" ? d.event?.event || d.type : d.type; + const isDeviceEvent = d.ingestType === "browser"; + return ( + + (isDeviceEvent ? : )} + /> + } + className={"whitespace-nowrap"} + > + {trimMiddle(`${eventName || ""}`, 16)} + + + ); + }, }, { title: "Page Path", width: "20em", ellipsis: true, key: "pagePath", - render: (d: IncomingEvent) => ( -
- - {" "} - - {d.pagePath} -
- ), + render: (d: IncomingEvent) => + d.pageURL && ( +
+ + {" "} + + {d.pagePath} +
+ ), }, { title: "Summary", ellipsis: true, key: "summary", render: (d: IncomingEvent) => { + if (d.status == "SKIPPED" || d.status == "FAILED") { + return ( +
+ } + className={"whitespace-nowrap"} + > + {d.error} + +
+ ); + } return ( - +
+ {d.host && ( - + } className={"whitespace-nowrap"}> {d.host} )} {d.email && ( - + } className={"whitespace-nowrap"}> {d.email} )} {d.userId && !d.email && ( - + } className={"whitespace-nowrap"}> - {d.userId} + {d.userId.toString()} )} {d.referringDomain && d.host !== d.referringDomain && ( - + } className={"whitespace-nowrap"}> {d.referringDomain} )} {!d.userId && d.anonymousId && ( - + } className={"whitespace-nowrap"}> - {d.anonymousId} - - - )} - {d.messageId && ( - - } className={"whitespace-nowrap"}> - {d.messageId} + {d.anonymousId.toString()} )} - + {/*{d.messageId && (*/} + {/* */} + {/* } className={"whitespace-nowrap"}>*/} + {/* {d.messageId}*/} + {/* */} + {/* */} + {/*)}*/} +
); }, }, @@ -1010,6 +1510,7 @@ const IncomingEventsTable = ({ loadEvents, loading, streamType, entityType, acto loading={loading} loadEvents={loadEvents} events={mapEvents(events)} + mappedConnections={mappedConnections} drawerNode={IncomingEventDrawer} columns={columns} /> diff --git a/webapps/console/components/DataView/JSONView.tsx b/webapps/console/components/DataView/JSONView.tsx index a941877c0..ae503b479 100644 --- a/webapps/console/components/DataView/JSONView.tsx +++ b/webapps/console/components/DataView/JSONView.tsx @@ -1,5 +1,5 @@ import React, { useState } from "react"; -import { Button, Space } from "antd"; +import { Button } from "antd"; import { CopyOutlined, FileTextOutlined } from "@ant-design/icons"; import loadable from "@loadable/component"; import { CodeBlockLight } from "../CodeBlock/CodeBlockLight"; @@ -15,7 +15,9 @@ export const JSONView = (props: { data: any; rawData?: string }) => { const copyToClipboard = () => { if (raw) { - navigator.clipboard.writeText(props.rawData ?? ""); + navigator.clipboard.writeText( + typeof props.rawData === "string" ? props.rawData : JSON.stringify(props.data, undefined, " ") + ); } else { navigator.clipboard.writeText(JSON.stringify(props.data, null, 2)); } @@ -24,7 +26,7 @@ export const JSONView = (props: { data: any; rawData?: string }) => { return (
- +
{!raw ? ( - +
{!raw ? ( diff --git a/webapps/console/components/DataView/TableWithDrawer.tsx b/webapps/console/components/DataView/TableWithDrawer.tsx index 3bec58626..96a801d8d 100644 --- a/webapps/console/components/DataView/TableWithDrawer.tsx +++ b/webapps/console/components/DataView/TableWithDrawer.tsx @@ -7,7 +7,8 @@ type TableWithDrawerProps = { columns: ColumnsType; events?: EventType[]; loadEvents: () => void; - drawerNode: React.FC<{ event: EventType }>; + mappedConnections?: Record; + drawerNode: React.FC<{ event: EventType; mappedConnections?: Record }>; className?: string; }; @@ -16,6 +17,7 @@ export const TableWithDrawer = ({ loading, columns, events, + mappedConnections, drawerNode: DrawerNode, className, }: TableWithDrawerProps) => { @@ -68,7 +70,7 @@ export const TableWithDrawer = ({ onClose={onDrawerClose} open={drawerOpen} > - {selectedEvent && } + {selectedEvent && } ); diff --git a/webapps/console/components/DestinationsCatalog/DestinationsCatalog.tsx b/webapps/console/components/DestinationsCatalog/DestinationsCatalog.tsx index f1f19a9e3..922f3ce6e 100644 --- a/webapps/console/components/DestinationsCatalog/DestinationsCatalog.tsx +++ b/webapps/console/components/DestinationsCatalog/DestinationsCatalog.tsx @@ -1,8 +1,14 @@ -import { useMemo } from "react"; +import React, { useMemo } from "react"; import styles from "./DestinationsCatalog.module.css"; import { coreDestinations, DestinationType } from "../../lib/schema/destinations"; import { FaCloud, FaDatabase } from "react-icons/fa"; +import { useAppConfig, useWorkspace } from "../../lib/context"; +import { useApi } from "../../lib/useApi"; +import { DestinationConfig } from "../../lib/schema"; +import { Skeleton } from "antd"; +import { ProvisionDatabaseButton } from "../ProvisionDatabaseButton/ProvisionDatabaseButton"; +import { useRouter } from "next/router"; function groupDestinationTypes(): Record { const groups: Record = {}; @@ -35,44 +41,80 @@ function groupDestinationTypes(): Record { .reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {}); } -export function getDestinationIcon(destination: DestinationType) { +export function getDestinationIcon(destination?: DestinationType) { + if (!destination) { + return ; + } const tags = ( destination.tags ? (typeof destination.tags === "string" ? [destination.tags] : destination.tags) : [] ).map(t => t.toLowerCase()); return destination.icon || (tags.includes("datawarehouse") ? : ); } -export const DestinationCatalog: React.FC<{ onClick: (destinationType: string) => void }> = ({ onClick }) => { +export const DestinationCatalog: React.FC<{ onClick: (destinationType: string) => void; dismiss: () => any }> = ({ + onClick, + dismiss, +}) => { + const workspace = useWorkspace(); + const router = useRouter(); + const appConfig = useAppConfig(); + const { isLoading, data, error, refetch } = useApi<{ objects: DestinationConfig[] }>( + `/api/${workspace.id}/config/destination` + ); + const provisionedDestinations = data?.objects.filter(d => d.provisioned) || []; + + const loader = ( +
+ +
+ ); const groups = useMemo(() => groupDestinationTypes(), []); return ( -
- {Object.entries(groups).map(([tag, destinations]) => ( -
-
{tag}
-
- {destinations.map(destination => ( -
onClick(destination.id)} - > - {destination.comingSoon && ( -
- Coming soon +
+
+ {Object.entries(groups).map(([tag, destinations]) => ( +
+
{tag}
+ {tag === "Datawarehouse" && + appConfig.ee.available && + provisionedDestinations && + provisionedDestinations.length === 0 && ( +
+ { + dismiss(); + }} + /> +
+ )} +
+ {destinations.map(destination => ( +
onClick(destination.id) : undefined} + > + {destination.comingSoon && ( +
+ Coming soon +
+ )} +
{getDestinationIcon(destination)}
+
+
+ {destination.title} +
+ {destination.description &&
{destination.description}
}
- )} -
{getDestinationIcon(destination)}
-
-
{destination.title}
- {destination.description &&
{destination.description}
}
-
- ))} + ))} +
-
- ))} + ))} +
); }; diff --git a/webapps/console/components/Disable/Disable.tsx b/webapps/console/components/Disable/Disable.tsx index a42eddb5d..5aeb5360e 100644 --- a/webapps/console/components/Disable/Disable.tsx +++ b/webapps/console/components/Disable/Disable.tsx @@ -16,5 +16,5 @@ export const Disable: React.FC> = props => { if (arrayChildren.length !== 1) { throw new Error(` must have exactly one child, found ${arrayChildren.length}`); } - return {props.disabledReason}}>{cloneElement(child as any, { disabled: true })}; + return {cloneElement(child as any, { disabled: true })}; }; diff --git a/webapps/console/components/DomainsEditor/DomainsEditor.tsx b/webapps/console/components/DomainsEditor/DomainsEditor.tsx new file mode 100644 index 000000000..7fc686d24 --- /dev/null +++ b/webapps/console/components/DomainsEditor/DomainsEditor.tsx @@ -0,0 +1,381 @@ +import React, { PropsWithChildren, useState } from "react"; +import { CustomWidgetProps } from "../ConfigObjectEditor/Editors"; +import { useWorkspace } from "../../lib/context"; +import { DomainCheckResponse } from "../../lib/shared/domain-check-response"; +import { get } from "../../lib/useApi"; +import { confirmOp, feedbackError } from "../../lib/ui"; +import { Button, Input, notification, Tag, Tooltip } from "antd"; +import { useQuery } from "@tanstack/react-query"; +import { getAntdModal, useAntdModal } from "../../lib/modal"; +import { Globe } from "lucide-react"; +import { FaExternalLinkAlt, FaSpinner, FaTrash, FaWrench } from "react-icons/fa"; +import { ReloadOutlined } from "@ant-design/icons"; +import { useRouter } from "next/router"; +import { WLink } from "../Workspace/WLink"; +import { JitsuButton } from "../JitsuButton/JitsuButton"; + +const StatusBadge: React.FC< + PropsWithChildren<{ status: "error" | "warning" | "info" | "success" | "loading"; className?: string }> +> = ({ status, children, className }) => { + let color: string | undefined; + let defaultDescription: string; + if (status === "error") { + color = "red"; + defaultDescription = "Error"; + } else if (status === "success") { + color = "cyan"; + defaultDescription = "Success"; + } else if (status === "info") { + color = "geekblue"; + defaultDescription = "Info"; + } else if (status === "warning") { + color = "orange"; + defaultDescription = "Warning"; + } else { + color = undefined; + defaultDescription = "Loading"; + } + return {children || defaultDescription}; +}; + +function displayErrorFeedback(opts?: { message?: string; error?: any }) { + notification.open({ + message: "An error occurred while processing your request. Please try again later.", + description: `Error: ${opts?.message || opts?.error?.message || opts?.error?.toString() || "Unknown error"}`, + onClick: () => { + //console.log("Notification Clicked!"); + }, + }); +} + +const CustomDomain: React.FC<{ domain: string; deleteDomain?: () => Promise; workspaceDomain?: boolean }> = ({ + domain, + deleteDomain, + workspaceDomain, +}) => { + const workspace = useWorkspace(); + const router = useRouter(); + + const [reloadTrigger, setReloadTrigger] = useState(0); + const [deleting, setDeleting] = useState(false); + const { data, isLoading, error, refetch } = useQuery( + ["domain-status", domain.toLowerCase(), reloadTrigger], + async () => { + return await get(`/api/${workspace.id}/domain-check?domain=${domain.toLowerCase()}`); + }, + { cacheTime: 0 } + ); + const m = useAntdModal(); + return ( +
+ <> +
+ {/*
*/} + {/* */} + {/*
*/} +
+ +
+
{domain}
+
+ + + + {data?.reason === "requires_cname_configuration" && ( + + + + )} + + + + {deleteDomain && ( + { + if (await confirmOp(`Are you sure you want to remove domain ${domain}?`)) { + try { + setDeleting(true); + await deleteDomain(); + } catch (e) { + displayErrorFeedback({ message: `Can't remove domain ${domain}`, error: e }); + } finally { + setDeleting(false); + } + } + }} + className="border-0" + > + {!deleting && } + + )} + {workspaceDomain && ( + { + router.push(`/${workspace.slugOrId}/settings/domains`); + }} + > + Workspace Domain + + )} +
+
+
+
Status:
+ {(() => { + if (isLoading) { + return ( + + + + Checking Domain Status + + + ); + } else if (data?.ok) { + return OK; + } else if (data?.reason === "requires_cname_configuration") { + return Configuration Required; + } else if (data?.reason === "pending_ssl") { + return Issuing Certificate; + } else { + return {data?.reason || "ERROR"}; + } + })()} +
+ {error && ( +
+
Description:
+
{`${"Internal error"}`}
+
+ )} + {data?.reason === "requires_cname_configuration" && ( + + )} + {data?.reason === "pending_ssl" && ( +
+
Description:
+
Issuing SSL certificate for the domain. It may take up to 10 minutes.
+
+ )} + +
+ ); +}; +export type DNSRecordTableProps = { + records: { name: string; type: string; value: string; ok: boolean }[]; +}; + +export const DNSRecordTable: React.FC = ({ records }) => { + return ( +
+ + + + + + + + + + {records.map(({ name, type, value, ok }) => ( + + + + + + + ))} + +
TypeNameValue
{ok ? "✅" : "⚠️"}{type}{name}{value}
+ ); +}; + +export type DomainInstructionsProps = { domain: string; status: DomainCheckResponse }; +const DomainConfigurationInstructions: React.FC & { + show: (p: DomainInstructionsProps) => void; +} = ({ domain, status }) => { + if (status.reason === "requires_cname_configuration") { + return ( +
+

Set the following records on your DNS provider to continue

+

+ ({ ...c, type: "CNAME" }))} /> +

+
+ ); + } else { + return
Unknown configuration type
; + } +}; + +DomainConfigurationInstructions.show = p => { + getAntdModal().info({ + width: "80%", + style: { maxWidth: "80%" }, + title: ( +

+ {p.domain} configuration instructions +

+ ), + content: , + }); +}; + +export const DomainsEditor: React.FC< + { context: "site" | "workspace"; workspaceDomains?: string[] } & CustomWidgetProps +> = ({ onChange, value: domains, workspaceDomains, context, disabled }) => { + const [addValue, setAddValue] = useState(); + const [addPending, setAddPending] = useState(false); + const workspace = useWorkspace(); + const add = async () => { + setAddPending(true); + try { + if (addValue?.includes("*") && context !== "workspace") { + feedbackError("Wildcard domains are only allowed on Workspace level"); + return; + } + const available: DomainCheckResponse = await get(`/api/${workspace.id}/domain-check?domain=${addValue}`); + if (!available.ok) { + if (available.reason === "used_by_other_workspace") { + feedbackError( + <> + Domain {addValue} is not available. It is used by other workspace. Contact{" "} + support@jitsu.com if you think this is a mistake + + ); + return; + } else if (available.reason === "invalid_domain_name") { + feedbackError( + <> + Invalid domain name: {addValue} + + ); + return; + } + } + const newVal = [...(domains ?? []), addValue as string]; + await onChange(newVal); + setAddValue(undefined); + } catch (e) { + feedbackError(`Can't add domain ${addValue}`, { error: e }); + } finally { + setAddPending(false); + } + }; + return ( +
+ {(workspaceDomains ?? []).filter(d => d.includes("*")).length > 0 && ( +
+ The following wildcard domains are configured on the Workspace{" "} + level: +
+ {(workspaceDomains ?? []) + .filter(d => d.includes("*")) + .map(d => { + return ( + + {d} + + ); + })} +
+
Subdomains of these domains can be added without additional configuration.
+
+ )} +
+ setAddValue(e.target.value)} + onKeyDown={e => { + if (e.key === "Enter") { + add(); + e.preventDefault(); + } + }} + /> + + Add + +
+
+ {(workspaceDomains ?? []) + .filter(d => !d.includes("*")) + .map(domain => { + return ( +
+ +
+ ); + })} + {(domains ?? []).map(domain => { + return ( +
+ { + const newVal = domains!.filter(d => d !== domain); + await onChange(newVal); + }} + /> +
+ ); + })} +
+
+ ); +}; diff --git a/webapps/console/components/EditorToolbar/EditorToolbar.tsx b/webapps/console/components/EditorToolbar/EditorToolbar.tsx new file mode 100644 index 000000000..ab1a25540 --- /dev/null +++ b/webapps/console/components/EditorToolbar/EditorToolbar.tsx @@ -0,0 +1,31 @@ +import React, { ReactNode } from "react"; +import classNames from "classnames"; +import Link from "next/link"; + +export type EditorToolbarProps = { + items: { + title: ReactNode; + icon: ReactNode; + href: string; + onClick?: () => void; + }[]; + className?: string; +}; + +export const EditorToolbar: React.FC = ({ items, className }) => { + return ( +
+ {items.map(({ href, icon, title, onClick }, index) => ( + +
{icon}
+ {title} + + ))} +
+ ); +}; diff --git a/webapps/console/components/EventStat/EventStatPage.tsx b/webapps/console/components/EventStat/EventStatPage.tsx new file mode 100644 index 000000000..dfb94b881 --- /dev/null +++ b/webapps/console/components/EventStat/EventStatPage.tsx @@ -0,0 +1,311 @@ +import React, { useEffect } from "react"; +import { Checkbox, Radio, Select, Skeleton, Tooltip } from "antd"; +import { QuestionCircleFilled } from "@ant-design/icons"; +import { useQueryStringState } from "../../lib/useQueryStringState"; +import { useQuery } from "@tanstack/react-query"; +import { useWorkspace } from "../../lib/context"; +import { rpc } from "juava"; +import { AlertTriangle, ArrowRight, Loader2 } from "lucide-react"; +import classNames from "classnames"; +import { buildConnectionAggregate, ConnectionAggregate, KnownEventStatus, Report } from "../../lib/shared/reporting"; +import { useConfigObjectLinks, useConfigObjectList, UseConfigObjectLinkResult } from "../../lib/store"; +import { DestinationTitle } from "../../pages/[workspaceId]/destinations"; +import { StreamTitle } from "../../pages/[workspaceId]/streams"; +import { Chart } from "chart.js/auto"; + +const TotalEvents: React.FC<{ val?: number; className?: string }> = ({ val, className }) => ( +
+

Total Events

+
+ {val || val === 0 ? ( +

{val.toLocaleString("en-US", { maximumFractionDigits: 0 })}

+ ) : ( + + )} +
+
+); + +const Period: React.FC<{ value?: string; onChange: (value: string) => void }> = ({ value, onChange }) => { + const [period, setPeriod] = React.useState(value || "24h"); + + return ( +
+ { + if (e.target.value !== "custom") { + setPeriod(e.target.value); + onChange(e.target.value); + } else { + alert("Custom period is not implemented yet"); + } + }} + > + 24H + 7D + 1 Month + + Custom (soon) + + +
+ ); +}; + +export const LabelWithComment: React.FC<{ children: string; comment: string; className?: string }> = ({ + children, + comment, + className, +}) => { + return ( +
+
{children}
+ + + +
+ ); +}; + +const eventStatuses = ["success", "dropped", "error"] as const; +type EventStatus = (typeof eventStatuses)[number]; + +const EventTypes: React.FC<{ value: EventStatus[]; onChange: (val: EventStatus[]) => void }> = props => { + const [value, setValue] = React.useState(props.value); + return ( + { + const newVal = _val as EventStatus[]; + if (_val.length === 0) { + return; + } + setValue(newVal); + props.onChange(newVal); + }} + value={value} + options={[ + { + label: ( + + Errors + + ), + value: "error", + }, + { + label: ( + + Dropped + + ), + value: "dropped", + }, + { + label: ( + + Success + + ), + value: "success", + }, + ]} + /> + ); +}; + +const ConnectionSelector = (props: { onChange: (val: string) => void; value?: string }) => { + const links = useConfigObjectLinks(); + const allConnections = links + .filter(l => l.type === "push") + .reduce((acc, l) => ({ ...acc, [l.id]: l }), {} as Record); + const streams = useConfigObjectList("stream"); + const destinations = useConfigObjectList("destination"); + const [connectionId, setConnectionId] = React.useState(props.value || undefined); + return ( + <> + + + ); +}; + +export const ChartView: React.FC<{ + report: ConnectionAggregate; + dateFormat: "day" | "hour"; + status: KnownEventStatus[]; +}> = ({ report, dateFormat, status }) => { + const wrapperRef = React.useRef(null); + const data = [...report.breakdown].sort((a, b) => a.period.getTime() - b.period.getTime()); + useEffect(() => { + if (wrapperRef.current) { + const chart = new Chart(wrapperRef.current as any, { + type: "bar", + options: { + //for dev env double animation due to double rendering is just annoying + animation: process.env.NODE_ENV === "development" ? false : undefined, + maintainAspectRatio: false, + plugins: { + legend: { + display: false, + }, + }, + }, + data: { + labels: data.map(row => + dateFormat === "day" + ? row.period.toLocaleDateString("en-US", { month: "short", day: "2-digit" }) + : row.period.toLocaleTimeString("en-US", { + hour: "2-digit", + minute: "2-digit", + hour12: false, + }) + ), + datasets: [ + status.includes("error") + ? { + stack: "main", + label: "error", + backgroundColor: "rgb(224, 49, 48)", + data: data.map(row => row.error), + } + : undefined, + status.includes("dropped") + ? { + stack: "main", + label: "dropped", + backgroundColor: "#737373", + data: data.map(row => row.dropped), + } + : undefined, + status.includes("success") + ? { + stack: "main", + backgroundColor: "#009140", + data: data.map(row => row.success), + } + : undefined, + ].filter(Boolean) as any, + }, + }); + return () => { + chart.destroy(); + }; + } + }, [data]); + + return ; +}; + +export const EventStatPage: React.FC = () => { + const workspace = useWorkspace(); + const [period, setPeriod] = useQueryStringState("period", { + defaultValue: "24h", + }); + const [eventTypes, setEventTypes] = useQueryStringState("ev", { + defaultValue: "success,error,dropped", + }); + const [connectionId, setConnectionId] = useQueryStringState("connectionId", { + defaultValue: undefined, + }); + const [start, end, granularity] = (() => { + switch (period) { + case "24h": + return [new Date(Date.now() - 24 * 60 * 60 * 1000), new Date(), "hour"]; + case "7d": + return [new Date(Date.now() - 7 * 24 * 60 * 60 * 1000), new Date(), "day"]; + case "1m": + const monthAgo = new Date(); + monthAgo.setMonth(monthAgo.getMonth() - 1); + return [monthAgo, new Date(), "day"]; + default: + throw new Error(`Unknown period '${period}'`); + } + })(); + const remoteResult = useQuery( + ["event-stat", workspace.slugOrId, period, granularity], + async () => { + return Report.parse( + await rpc( + `/api/${ + workspace.slugOrId + }/reports/event-stat?start=${start.toISOString()}&end=${end.toISOString()}&granularity=${granularity}` + ) + ); + }, + { retry: false, staleTime: 0, cacheTime: 0 } + ); + return ( +
+

Workspace Statistics

+
+
+
+ + +
+
+
+ {!connectionId && ( +
+
Please select connection to display stat
+
+ )} + + {connectionId && remoteResult.isLoading && ( +
+ +
Hang tight, statistics is loading...
+
+ )} + {connectionId && remoteResult.error ? ( +
+ +
+ Failed to load data from the server, see error console for details +
+
+ ) : undefined} + {remoteResult.data && connectionId ? ( + + ) : undefined} +
+
+ setEventTypes(val.join(","))} /> +
+
+
+ ); +}; diff --git a/webapps/console/components/Expandable/Expandable.tsx b/webapps/console/components/Expandable/Expandable.tsx index 0f4c26685..adc686690 100644 --- a/webapps/console/components/Expandable/Expandable.tsx +++ b/webapps/console/components/Expandable/Expandable.tsx @@ -36,7 +36,7 @@ export const Expandable: React.FC> = ) : ( )} -
{typeof title === "function" ? title(expanded) : title}
+ {title &&
{typeof title === "function" ? title(expanded) : title}
}
{
; }; const FieldListEditorLayout: React.FC = props => { const items = props.items.filter(item => !!item) as EditorItem[]; const groups = new Set(items.map(i => i.group)); - if (groups.size === 0) { - return ; + if (groups.size === 0 || (groups.size === 1 && groups.has(undefined))) { + return ( +
+ +
+ ); } else { return ( -
+
{[...groups].map(group => { const grObj = props?.groups?.[group || ""] || {}; const expandable = grObj.expandable; const list = i.group === group)} />; - const title = grObj.title ||

{group}

; + const title = + grObj.title || (group ?

{group}

: undefined); return expandable ? ( []; className?
{item.name && (
- - {item.link && ( - - - - )} +
+ + {item.link && ( + + + + )} +
)}
{item.component}
diff --git a/webapps/console/components/FunctionsDebugger/CodeViewer.tsx b/webapps/console/components/FunctionsDebugger/CodeViewer.tsx new file mode 100644 index 000000000..fe9e18493 --- /dev/null +++ b/webapps/console/components/FunctionsDebugger/CodeViewer.tsx @@ -0,0 +1,22 @@ +import React, { useState } from "react"; +import { CodeBlockLight } from "../CodeBlock/CodeBlockLight"; + +export const CodeViewer: React.FC<{ code: string }> = ({ code }) => { + const [showCode, setShowCode] = useState(false); + + return ( +
+ + {showCode && ( + + {code} + + )} +
+ ); +}; diff --git a/webapps/console/components/FunctionsDebugger/FunctionLogs.tsx b/webapps/console/components/FunctionsDebugger/FunctionLogs.tsx new file mode 100644 index 000000000..aba1b9fe3 --- /dev/null +++ b/webapps/console/components/FunctionsDebugger/FunctionLogs.tsx @@ -0,0 +1,49 @@ +import { logType } from "@jitsu/core-functions"; +import React from "react"; +import dayjs from "dayjs"; + +const localDate = (date: string | Date) => dayjs(date).format("YYYY-MM-DD HH:mm:ss"); + +export const FunctionLogs: React.FC<{ logs: logType[]; className?: string; showDate?: boolean }> = ({ + logs, + className, + showDate, +}) => { + return ( +
+ {logs.map((log, index) => { + const colors = (() => { + switch (log.level) { + case "error": + return { text: "#A4000F", bg: "#FDF3F5", border: "#F8D6DB" }; + case "debug": + return { text: "#646464", bg: "#FBF3F5", border: "#FBF3F5" }; + case "warn": + return { text: "#705100", bg: "#FFFBD6", border: "#F4E89A" }; + default: + return { text: "black", bg: "white", border: "#eaeaea" }; + } + })(); + return ( +
+ {showDate &&
{localDate(log.timestamp)}
} +
+ {log.level.toUpperCase()} +
+
+ {log.message} +
+
+ ); + })} +
+ ); +}; diff --git a/webapps/console/components/FunctionsDebugger/FunctionResult.tsx b/webapps/console/components/FunctionsDebugger/FunctionResult.tsx new file mode 100644 index 000000000..c9d5b2058 --- /dev/null +++ b/webapps/console/components/FunctionsDebugger/FunctionResult.tsx @@ -0,0 +1,58 @@ +import React from "react"; +import { Htmlizer } from "../Htmlizer/Htmlizer"; +import { DropRetryErrorName, RetryErrorName } from "@jitsu/functions-lib"; +import { CodeEditor } from "../CodeEditor/CodeEditor"; +import Convert from "ansi-to-html"; +const convert = new Convert({ newline: true }); + +export const FunctionResult: React.FC<{ resultType: "ok" | "drop" | "error"; result: any; className?: string }> = ({ + result, + resultType, + className, +}) => { + return ( +
+ {resultType === "error" && ( +
+ + {`${result.name}: ` + + convert.toHtml(result.message.replaceAll(" ", " "))} + + {result.name === DropRetryErrorName && ( +
+ If such error will happen on an actual event, it will be SKIPPED and retry will be scheduled in{" "} + {result.retryPolicy?.delays?.[0] ? Math.min(result.retryPolicy.delays[0], 1440) : 5} minutes. +
+ )} + {result.name === RetryErrorName && ( +
+ If such error will happen on an actual event, this function will be scheduled +
+ for retry in {result.retryPolicy?.delays?.[0] ? Math.min(result.retryPolicy.delays[0], 1440) : 5} minutes, + but event will be processed further. +
+ )} +
+ )} + {resultType === "drop" && ( +
+ Further processing will be SKIPPED. Function returned: {JSON.stringify(result)}. +
+ )} + {resultType === "ok" && ( + {}} + monacoOptions={{ + renderLineHighlight: "none", + lineDecorationsWidth: 8, + lineNumbers: "off", + readOnly: true, + folding: false, + }} + /> + )} +
+ ); +}; diff --git a/webapps/console/components/FunctionsDebugger/FunctionVariables.tsx b/webapps/console/components/FunctionsDebugger/FunctionVariables.tsx new file mode 100644 index 000000000..d961e9adc --- /dev/null +++ b/webapps/console/components/FunctionsDebugger/FunctionVariables.tsx @@ -0,0 +1,134 @@ +import React, { useCallback, useEffect } from "react"; +import { CirclePlusIcon, EyeIcon, EyeOffIcon, Trash2 } from "lucide-react"; +import { Button, Input } from "antd"; +import { isEqual } from "juava"; +import { useWorkspaceRole } from "../../lib/context"; + +const Var: React.FC<{ + name: string; + value: string; + disabled?: boolean; + onChange: (name: string, value: string | undefined) => void; +}> = ({ name, value, onChange, disabled }) => { + const [editing, setEditing] = React.useState(false); + + return ( +
+ { + onChange(e.target.value, value); + }} + /> + {editing ? ( + { + onChange(name, e.target.value); + }} + /> + ) : ( +
setEditing(true) : undefined} + className={`flex-auto overflow-auto p-2 basis-1/2 border rounded-md ${ + disabled ? "bg-gray-100 text-gray-300" : "" + } `} + > + {value.replaceAll(/./g, "*")} +
+ )} + {!disabled && ( +
+
+ )} +
+ ); +}; + +export const FunctionVariables: React.FC<{ + value: Record; + onChange: (r: Record) => void; + disabled?: boolean; + className?: string; +}> = ({ value, onChange, className, disabled }) => { + const userRole = useWorkspaceRole(); + const canEdit = userRole.editEntities; + + const [array, setArray] = React.useState<[string, string][]>(Object.entries(value)); + + const change = useCallback( + (index: number, name: string, v: string | undefined) => { + if (typeof v === "undefined") { + setArray(array.filter((_, i) => i !== index)); + } else { + const newArr = [...array]; + newArr[index] = [name, v]; + setArray(newArr); + } + }, + [array] + ); + + useEffect(() => { + const newValue = Object.fromEntries(array); + if (!isEqual(newValue, value)) { + onChange(newValue); + } + }, [array, onChange, value]); + + return ( +
+
+
Name
+
Value
+
+
+ {array.map(([n, v], index) => { + return ( + change(index, nm, vl)} + /> + ); + })} + {canEdit && !disabled && ( +
+ +
+ )} +
+ ); +}; diff --git a/webapps/console/components/FunctionsDebugger/FunctionsDebugger.module.css b/webapps/console/components/FunctionsDebugger/FunctionsDebugger.module.css index dcf019133..ed5312e10 100644 --- a/webapps/console/components/FunctionsDebugger/FunctionsDebugger.module.css +++ b/webapps/console/components/FunctionsDebugger/FunctionsDebugger.module.css @@ -3,7 +3,27 @@ border-color: #d9d9d9; } +.vars { + @apply overflow-hidden; +} + .logs { - @apply border rounded-lg overflow-scroll; + @apply border rounded-lg overflow-auto; border-color: #d9d9d9; } + +.tabsHeightFix { + height: 100%; +} + +.tabsHeightFix :global(.ant-tabs-content) { + height: 100% !important; +} + +.settingsTable :global(.ant-tabs-content) { + height: 100% !important; +} + +.splitterFix :global(.ant-splitter-bar-dragger::before) { + background-color: white !important; +} diff --git a/webapps/console/components/FunctionsDebugger/FunctionsDebugger.tsx b/webapps/console/components/FunctionsDebugger/FunctionsDebugger.tsx index 83868df4a..f3d36657a 100644 --- a/webapps/console/components/FunctionsDebugger/FunctionsDebugger.tsx +++ b/webapps/console/components/FunctionsDebugger/FunctionsDebugger.tsx @@ -1,39 +1,147 @@ import React, { useCallback, useEffect, useReducer, useState } from "react"; import { EditorComponentProps } from "../ConfigObjectEditor/ConfigEditor"; -import FieldListEditorLayout from "../FieldListEditorLayout/FieldListEditorLayout"; -import { TextEditor } from "../ConnectionEditorPage/ConnectionEditorPage"; -import { Badge, Button, Drawer, Dropdown, MenuProps, Select, Table } from "antd"; -import { PlayCircleOutlined } from "@ant-design/icons"; +import { Badge, Button, Descriptions, Drawer, Dropdown, Input, MenuProps, Select, Splitter, Table, Tabs } from "antd"; import { CodeEditor } from "../CodeEditor/CodeEditor"; import styles from "./FunctionsDebugger.module.css"; -import { Settings } from "lucide-react"; -import Link from "antd/lib/typography/Link"; +import { + Braces, + Bug, + Check, + Code2, + Parentheses, + Pencil, + Play, + RefreshCw, + Save, + SearchCode, + Terminal, + Undo2, + X, +} from "lucide-react"; import { getConfigApi, useEventsLogApi } from "../../lib/useApi"; import { EventsLogRecord } from "../../lib/server/events-log"; -import { useWorkspace } from "../../lib/context"; +import { useWorkspace, useWorkspaceRole } from "../../lib/context"; import { arrayToMap } from "../../lib/shared/arrays"; import { AnalyticsServerEvent } from "@jitsu/protocols/analytics"; import { ColumnsType } from "antd/es/table"; import { UTCDate, UTCHeader } from "../DataView/EventsBrowser"; -import { examplePageEvent, exampleTrackEvents, exportIdentifyEvent } from "./example_events"; +import { examplePageEvent, exampleTrackEvents, exampleIdentifyEvent } from "./example_events"; import { rpc } from "juava"; -import { logType } from "../../pages/api/[workspaceId]/function/run"; +import { logType } from "@jitsu/core-functions/src/functions/lib/udf_wrapper"; +import { RetryErrorName, DropRetryErrorName } from "@jitsu/functions-lib"; + import dayjs from "dayjs"; +import utc from "dayjs/plugin/utc"; +dayjs.extend(utc); import { defaultFunctionTemplate } from "./code_templates"; import { FunctionConfig } from "../../lib/schema"; import { useRouter } from "next/router"; -import { feedbackError } from "../../lib/ui"; - -const localDate = (date: string | Date) => dayjs(date).format("YYYY-MM-DD HH:mm:ss"); +import { feedbackError, PropsWithChildrenClassname } from "../../lib/ui"; +import Link from "next/link"; +import { useStoreReload } from "../../lib/store"; +import { FunctionLogs } from "./FunctionLogs"; +import { FunctionResult } from "./FunctionResult"; +import { FunctionVariables } from "./FunctionVariables"; +import { CodeViewer } from "./CodeViewer"; +import classNames from "classnames"; +import { ButtonLabel } from "../ButtonLabel/ButtonLabel"; +import { Dot } from "../ProfileBuilderPage/ProfileBuilderPage"; +import { JitsuButton } from "../JitsuButton/JitsuButton"; type FunctionsDebuggerProps = {} & EditorComponentProps; +export const EditableTitle: React.FC<{ children: string; onUpdate: (str: string) => void; disabled?: boolean }> = ({ + children, + onUpdate, + disabled, +}) => { + const [editing, setEditing] = useState(false); + const [value, setValue] = useState(children); + const [rollbackValue, setRollbackValue] = useState(children); + return ( +
+ {editing ? ( +
+
+ { + setValue(e.target.value); + onUpdate(e.target.value); + }} + onKeyDown={e => { + if (e.key === "Enter") { + setEditing(false); + onUpdate(value); + } else if (e.key == "Escape") { + setEditing(false); + setValue(rollbackValue); + onUpdate(rollbackValue); + } + }} + /> +
+ + +
+ ) : ( +
+

{ + if (!disabled) { + setRollbackValue(value); + setEditing(true); + } + }} + > + {value} +

+ {!disabled && ( + + )} +
+ )} +
+ ); +}; + export const FunctionsDebugger: React.FC = props => { const { push } = useRouter(); const workspace = useWorkspace(); - const [showLogs, setShowLogs] = useState(false); - const [showConfig, setShowConfig] = useState(false); + const role = useWorkspaceRole(); + const canEdit = role.editEntities; + const [activePrimaryTab, setActivePrimaryTab] = useState("code"); + const [activeSecondaryTab, setActiveSecondaryTab] = useState("event"); + const [newResult, setNewResult] = useState(false); const [showEvents, setShowEvents] = useState(false); const [event, setEvent] = useState(JSON.stringify(examplePageEvent(), undefined, 2)); const [obj, setObj] = useState>({ @@ -41,17 +149,30 @@ export const FunctionsDebugger: React.FC = props => { code: props.isNew ? defaultFunctionTemplate() : props.object.code ?? "", }); - const [config, setConfig] = useState("{}"); + const [config, setConfig] = useState({}); const [store, setStore] = useState({}); const [result, setResult] = useState({}); + const [resultType, setResultType] = useState<"ok" | "drop" | "error">("ok"); const [logs, setLogs] = useState([]); const [unreadErrorLogs, setUnreadErrorLogs] = useState(0); const [unreadLogs, setUnreadLogs] = useState(0); - const [loading, setLoading] = useState(false); + const [saving, setSaving] = useState(false); const [running, setRunning] = useState(false); + const reloadStore = useStoreReload(); + + function handleTabChange(key: string) { + setActiveSecondaryTab(key); + if (key === "logs") { + setUnreadLogs(0); + setUnreadErrorLogs(0); + } + if (key === "result") { + setNewResult(false); + } + } const save = useCallback(async () => { - setLoading(true); + setSaving(true); try { if (props.isNew) { await getConfigApi(workspace.id, "function").create(obj); @@ -60,13 +181,14 @@ export const FunctionsDebugger: React.FC = props => { } else { feedbackError(`Can't save function without id`); } - push(`/${workspace.id}/functions`); + await reloadStore(); + push(`/${workspace.slugOrId}/functions`); } catch (error) { feedbackError(`Can't save function`, { error }); } finally { - setLoading(false); + setSaving(false); } - }, [props.isNew, obj, workspace.id, push]); + }, [props.isNew, obj, workspace.id, push, reloadStore]); const runFunction = useCallback(async () => { setRunning(true); @@ -77,9 +199,9 @@ export const FunctionsDebugger: React.FC = props => { functionName: obj.name, code: obj.code, event: JSON.parse(event), - config: JSON.parse(config), + variables: config, store, - workspaceId: workspace.id, + userAgent: navigator.userAgent, }; } catch (e) { feedbackError("Invalid JSON", { error: e }); @@ -91,23 +213,40 @@ export const FunctionsDebugger: React.FC = props => { method: "POST", body, }); + if (activeSecondaryTab !== "result") { + setNewResult(true); + } if (res.error) { setResult(res.error); + setResultType("error"); setLogs([ ...res.logs, { level: "error", type: "log", - message: res.error, + message: `${res.error.name}: ${res.error.message}`, timestamp: new Date(), }, ]); } else { setResult(res.result); - setLogs(res.logs); + setResultType(res.dropped ? "drop" : "ok"); + if (res.dropped) { + setLogs([ + ...res.logs, + { + level: "info", + type: "log", + message: `Further processing will be SKIPPED. Function returned: ${JSON.stringify(res.result)}`, + timestamp: new Date(), + }, + ]); + } else { + setLogs(res.logs); + } } - if (!showLogs) { + if (activeSecondaryTab !== "logs") { setUnreadLogs(res.logs.length); setUnreadErrorLogs(res.logs.filter(l => l.level === "error").length); } @@ -122,269 +261,249 @@ export const FunctionsDebugger: React.FC = props => { timestamp: new Date(), }, ]); - setResult(errorText); + setResult({ + name: "Error", + message: errorText, + }); + setResultType("error"); } finally { setRunning(false); } - }, [workspace.id, obj.code, event, config, store, obj.id, obj.name, showLogs]); + }, [workspace.id, obj.code, config, event, store, obj.id, obj.name, activeSecondaryTab]); return ( -
-
-
-
- ( -
-

{props.isNew ? "Add new function" : `Edit ${obj.name}`}

- {expanded ? "(hide)" : "(edit name)"} -
- ), - }, - Code: { - expandable: false, - className: "overflow-hidden", - title: ( -
-
-

Code:

-
-
- - - - -
+
+ setShowEvents(false)} + open={showEvents} + getContainer={false} + > + setEvent(JSON.stringify(e, undefined, 2))} /> + + { + setObj({ ...obj, name }); + }} + > + {obj.name || "New function"} + + + + + + + }>Save + +
+ } + type={"card"} + activeKey={activePrimaryTab} + size={"small"} + tabBarStyle={{ marginBottom: 0 }} + items={[ + { + key: "code", + style: { height: "100%" }, + label: ( + }> +
+ {obj.origin === "jitsu-cli" ? "Info" : "Code"} + {props.object?.code !== obj.code && }
- ), - }, - }} - items={[ - { - group: "Name", - name: "Name", - component: ( - { - setObj({ ...obj, name }); - }} - /> - ), - }, - { - group: "Name", - name: "Description", - component: ( - { - setObj({ ...obj, description }); - }} - /> - ), - }, - { - group: "Code", - key: "code", - itemClassName: "flex-auto", - component: ( -
-
- setObj({ ...obj, code: value })} - monacoOptions={{ renderLineHighlight: "none" }} - /> -
-
+ ), + children: ( + + {obj.origin === "jitsu-cli" ? ( + -
Config
- -
-
- ), - }, - ]} - /> -
-
-
-
-
-
-

Event:

-
-
+ + {obj.slug} + + This function was created with Jitsu CLI + + {obj.version} + + {obj.description && ( + {obj.description} + )} + { + +
+
+ The function is compiled and deployed with{" "} + + jitsu-cli + {" "} + and can't be edited in the UI. However, you can still run it with different events and + see the results below. And you can view the code +
+ + +
+
+ } + + ) : ( + setObj({ ...obj, code: value })} + monacoOptions={{ renderLineHighlight: "none" }} + /> + )} + + ), + }, + ]} + /> + + + + {activeSecondaryTab === "event" && ( + <> setEvent(JSON.stringify(e, undefined, 2))} /> - -
-
-
- -
+ + )} + + + ) : ( + + ) + } + > + Run + +
-
-
-
-

Result:

-
+ } + type={"card"} + defaultActiveKey="1" + size={"small"} + tabBarStyle={{ marginBottom: 0 }} + activeKey={activeSecondaryTab} + items={[ + { + style: { height: "100%" }, + key: "event", + label: }>Event, + children: ( + + + + ), + }, + { + key: "variables", + style: { height: "100%" }, + label: ( + }> +
+ Test Environment Variables +
+
+ ), + children: ( + +
+ +
+
+ ), + }, + { + style: { height: "100%" }, + key: "result", + label: ( + }> +
+ Last Run Result + {newResult && } +
+
+ ), + children: ( + + + + ), + }, + { + style: { height: "100%" }, + key: "logs", + label: ( - + }>Logs -
-
- {}} - monacoOptions={{ - renderLineHighlight: "none", - lineDecorationsWidth: 8, - lineNumbers: "off", - readOnly: true, - folding: false, - }} - /> -
-
-
-
-
-

Logs:

-
- -
-
- {logs.map((log, index) => { - const colors = (() => { - switch (log.level) { - case "error": - return { text: "#A4000F", bg: "#FDF3F5", border: "#F8D6DB" }; - case "debug": - return { text: "#646464", bg: "#FBF3F5", border: "#FBF3F5" }; - case "warn": - return { text: "#705100", bg: "#FFFBD6", border: "#F4E89A" }; - default: - return { text: "black", bg: "white", border: "#eaeaea" }; - } - })(); - return ( -
- {/*
{localDate(log.timestamp)}
*/} -
- {log.level.toUpperCase()} -
-
- {log.message} -
-
- ); - })} -
-
-
-
- setShowEvents(false)} - open={showEvents} - getContainer={false} - > - setEvent(JSON.stringify(e, undefined, 2))} /> - -
-
+ ), + children: ( + + + + ), + }, + ]} + /> + +
); }; @@ -436,7 +555,7 @@ const EventsSelector = ({ selectEvent }: { selectEvent: (e: any) => void }) => { try { if (actorId && entitiesMap && entitiesMap[actorId]) { dispatch({ type: "eventsLoading", value: true }); - const data = await eventsLogApi.get(`incoming.all`, actorId, {}, 100); + const data = await eventsLogApi.get(`incoming`, "all", actorId, {}, 100); dispatch({ type: "events", value: data }); dispatch({ type: "error", value: "" }); } @@ -531,7 +650,7 @@ const IncomingEventsTable = ({ const mapEvents = events ? events.map(ev => { let ingestPayload: any = {}; - if (typeof ev.content.body === "string") { + if (typeof ev.content.body === "string" && ev.content.body.length > 0) { try { ingestPayload = JSON.parse(ev.content.body); } catch (e) { @@ -614,7 +733,7 @@ const ExamplesDropdown = ({ selectEvent }: { selectEvent: (e: any) => void }) => { key: "identify", onClick: () => { - selectEvent(exportIdentifyEvent()); + selectEvent(exampleIdentifyEvent()); }, label: "Identify", }, @@ -636,9 +755,20 @@ const ExamplesDropdown = ({ selectEvent }: { selectEvent: (e: any) => void }) => return ( - ); }; + +const TabContent: React.FC = ({ children, className }) => { + return ( +
+ {children} +
+ ); +}; diff --git a/webapps/console/components/FunctionsDebugger/code_templates.ts b/webapps/console/components/FunctionsDebugger/code_templates.ts index 95a0e1f93..21e155668 100644 --- a/webapps/console/components/FunctionsDebugger/code_templates.ts +++ b/webapps/console/components/FunctionsDebugger/code_templates.ts @@ -1,7 +1,5 @@ export const defaultFunctionTemplate = () => { - return ` -export default async function(event, { log, fetch, props: config }) { + return `export default async function(event, { log, fetch }) { log.info("Hello world") -} -`; +}`; }; diff --git a/webapps/console/components/FunctionsDebugger/example_events.ts b/webapps/console/components/FunctionsDebugger/example_events.ts index d5c076f26..b2dd6da73 100644 --- a/webapps/console/components/FunctionsDebugger/example_events.ts +++ b/webapps/console/components/FunctionsDebugger/example_events.ts @@ -24,6 +24,7 @@ export const examplePageEvent = () => { name: "jitsu-js", version: "1.0.0", }, + ip: "127.0.0.1", userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/111.0", locale: "en-US", screen: { @@ -44,19 +45,19 @@ export const examplePageEvent = () => { search: "", title: "Example page event", url: "https://example.com/", - enconding: "UTF-8", + encoding: "UTF-8", }, campaign: { name: "example", source: "g", }, }, - request_ip: "127.0.0.1", + requestIp: "127.0.0.1", receivedAt: new Date(), }; }; -export const exportIdentifyEvent = () => { +export const exampleIdentifyEvent = () => { return { type: "identify", userId: "user@example.com", @@ -73,6 +74,7 @@ export const exportIdentifyEvent = () => { name: "jitsu-js", version: "1.0.0", }, + ip: "127.0.0.1", userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/113.0", locale: "en-US", screen: { @@ -90,14 +92,14 @@ export const exportIdentifyEvent = () => { search: "", title: "Example page event", url: "https://example.com/", - enconding: "UTF-8", + encoding: "UTF-8", }, campaign: { name: "example", source: "g", }, }, - request_ip: "127.0.0.1", + requestIp: "127.0.0.1", receivedAt: new Date(), }; }; @@ -120,6 +122,7 @@ export const exampleTrackEvents = () => { name: "jitsu-js", version: "1.0.0", }, + ip: "127.0.0.1", userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/111.0", locale: "en-US", screen: { @@ -140,14 +143,14 @@ export const exampleTrackEvents = () => { search: "", title: "Example page event", url: "https://example.com/", - enconding: "UTF-8", + encoding: "UTF-8", }, campaign: { name: "example", source: "g", }, }, - request_ip: "127.0.0.1", + requestIp: "127.0.0.1", receivedAt: new Date(), }; }; diff --git a/webapps/console/components/FunctionsSelector/FunctionsSelector.tsx b/webapps/console/components/FunctionsSelector/FunctionsSelector.tsx new file mode 100644 index 000000000..7e9e04f5b --- /dev/null +++ b/webapps/console/components/FunctionsSelector/FunctionsSelector.tsx @@ -0,0 +1,217 @@ +import React, { useCallback, useEffect, useState } from "react"; +import { DestinationConfig, FunctionConfig, StreamConfig } from "../../lib/schema"; +import { restrictToVerticalAxis } from "@dnd-kit/modifiers"; +import { DndContext, closestCenter, PointerSensor, useSensor, useSensors } from "@dnd-kit/core"; +import { arrayMove, SortableContext, useSortable, verticalListSortingStrategy } from "@dnd-kit/sortable"; +import { CSS } from "@dnd-kit/utilities"; +import { ArrowDown, GripVertical, X } from "lucide-react"; +import { FunctionTitle } from "../../pages/[workspaceId]/functions"; +import { StreamTitle } from "../../pages/[workspaceId]/streams"; +import { DestinationTitle } from "../../pages/[workspaceId]/destinations"; +import { JitsuButton } from "../JitsuButton/JitsuButton"; +import { WLink } from "../Workspace/WLink"; + +type SelectedFunction = { + functionId: string; + functionOptions?: any; + enabled?: boolean; +}; + +export type FunctionsSelectorProps = { + functions: FunctionConfig[]; + selectedFunctions?: SelectedFunction[]; + onChange: (selectedFunctions: FunctionConfig[]) => void; + split?: "horizontal" | "vertical"; + stream?: StreamConfig; + destination?: DestinationConfig; + disabled?: boolean; +}; + +const FunctionsSelector0: React.FC = ({ + functions, + selectedFunctions, + onChange, + stream, + destination, + disabled, + split = "horizontal", +}) => { + const [enabledFunctions, setEnabledFunctions] = useState( + (selectedFunctions ?? []) + .map(s => functions.find(f => s.functionId === "udf." + f.id)) + .filter(f => typeof f !== "undefined") as FunctionConfig[] + ); + const [disabledFunctions, setDisabledFunctions] = useState([]); + useEffect(() => { + setDisabledFunctions(functions.filter(f => !enabledFunctions.find(e => e.id === f.id))); + }, [enabledFunctions, functions, onChange]); + + const saveEnabledFunctions = useCallback( + f => { + setEnabledFunctions(f); + onChange(f); + }, + [enabledFunctions, onChange] + ); + + const sensors = useSensors(useSensor(PointerSensor)); + + const handleDragEnd = useCallback( + event => { + const { active, over } = event; + if (active.id !== over.id) { + const oldIndex = enabledFunctions.findIndex(i => i.id === active.id); + const newIndex = enabledFunctions.findIndex(i => i.id === over.id); + if (oldIndex === newIndex) return; + const reordered = arrayMove(enabledFunctions, oldIndex, newIndex); + setEnabledFunctions(reordered); + onChange(reordered); + } + }, + [enabledFunctions, onChange] + ); + const Wrapper: React.FC = ({ children }) => { + if (split === "vertical") { + return
{children}
; + } + return <>{children}; + }; + + return ( +
+ {functions && functions.length > 0 && ( + + {enabledFunctions.length > 0 && ( + +
+ Functions pipeline: +
+ {stream && ( + <> +
+ +
+ + + )} + + {enabledFunctions.map(func => ( + saveEnabledFunctions(enabledFunctions.filter(e => e.id !== f.id))} + /> + ))} + + {destination && ( + <> + +
+ +
+ + )} +
+ )} +
+ )} + {!disabled && ( + + {split == "horizontal" &&
} +
+ Choose functions to add to this connection +
+
+ {disabledFunctions.length ? ( + disabledFunctions.map(func => ( + saveEnabledFunctions([...enabledFunctions, f])} + /> + )) + ) : ( +
+ No functions added to workspace.{" "} + + Create Function... + +
+ )} +
+
+ )} +
+ ); +}; + +const FunctionCard: React.FC<{ + func: FunctionConfig; + funcEnabled: boolean; + disabled?: boolean; + listeners?: any; + onAdd?: (f: FunctionConfig) => void; + onDelete?: (f: FunctionConfig) => void; +}> = ({ func, funcEnabled, listeners, onAdd, onDelete, disabled }) => { + const functionId = "udf." + func.id; + + return ( +
+
+ +
+ {/*{enabled && } />}*/} + {!disabled && funcEnabled && ( + (onDelete ? onDelete(func) : undefined)} + icon={} + /> + )} + {!disabled && funcEnabled && } + {!disabled && !funcEnabled && ( + (onAdd ? onAdd(func) : undefined)} + > + Add + + )} +
+ ); +}; + +const SortableItem: React.FC<{ + id: string; + func: FunctionConfig; + onDelete: (f: FunctionConfig) => void; + disabled?: boolean; +}> = ({ id, func, onDelete, disabled }) => { + const { attributes, listeners, setNodeRef, transform, transition } = useSortable({ id }); + + const style = { + transform: CSS.Translate.toString(transform), + transition, + }; + const functionId = "udf." + func.id; + + return ( +
+ +
+ ); +}; + +export const FunctionsSelector = React.memo(FunctionsSelector0); diff --git a/webapps/console/components/GlobalError/GlobalError.tsx b/webapps/console/components/GlobalError/GlobalError.tsx index 359fef115..848747129 100644 --- a/webapps/console/components/GlobalError/GlobalError.tsx +++ b/webapps/console/components/GlobalError/GlobalError.tsx @@ -1,18 +1,18 @@ import React, { ErrorInfo, ReactNode } from "react"; -import { Expandable } from "../Expandable/Expandable"; import { CodeBlock } from "../CodeBlock/CodeBlock"; import { FaExclamationCircle } from "react-icons/fa"; -import { Button } from "antd"; -import { signOut } from "next-auth/react"; +import { Button, Collapse } from "antd"; import { getErrorMessage, getLog } from "juava"; -import { firebaseSignOut } from "../../lib/firebase-client"; import { PropsWithChildrenClassname } from "../../lib/ui"; import { AlertCircle } from "lucide-react"; import classNames from "classnames"; +import { useUserSessionControls } from "../../lib/context"; export type GlobalErrorProps = { - error: any; + error?: any; title?: string; + children?: ReactNode; + hideActions?: boolean; }; export type ErrorProps = { error: Error; errorInfo: ErrorInfo }; @@ -42,33 +42,35 @@ export class ErrorBoundary extends React.Component< } export function ErrorDetails(props: { error: any }) { - const details = [ - props.error?.message && `Message: ${props.error.message}`, - props.error?.stack && `Stack: ${props.error.stack}`, - ] - .filter(x => !!x) - .join("\n"); + const message = props.error?.message && `${props.error.message}`; + const stack = props.error?.stack && `${props.error.stack}`; return ( - <> - {details.length > 0 && ( - <> -
Error
- {details} - + + {message && ( + Error
}> + {message} + )} {props.error.response && ( - <> -
Response
- {JSON.stringify(props.error.response, null, 2)} - + Response
}> + + {JSON.stringify(props.error.response, null, 2)} + + )} {props.error.request && ( - <> -
Request
- {JSON.stringify(props.error.request, null, 2)} - + Request
}> + + {JSON.stringify(props.error.request, null, 2)} + + )} - + {stack && ( + Stack
}> + {stack} + + )} + ); } @@ -97,7 +99,15 @@ function maxLen(str: string, maxLen = 200) { return str.length <= maxLen - 2 ? str : str.substring(0, maxLen) + "..."; } -export function ErrorCard(props: { error?: any; title?: string; hideActions?: boolean }) { +export function SimpleErrorCard(props: { error?: any; title?: string }) { + const title = props.title || (props.error ? maxLen(getErrorMessage(props.error), 60) : "Error"); + const description = ( + <> + {getErrorMessage(props.error) || + `We encountered an error processing your request. Please try refreshing the page, or logging + out and back in. If the problem persists, please contact support.`} + + ); return (
- - {props.title || (props.error ? maxLen(getErrorMessage(props.error), 60) : "Error")} - {!props.hideActions && ( -
+
+
+
{description}
+
+
+ ); +} + +export function ErrorCard(props: { error?: any; title?: string; hideActions?: boolean; children?: ReactNode }) { + const sessionControl = useUserSessionControls(); + + const title = props.title || (props.error ? maxLen(getErrorMessage(props.error), 60) : "Error"); + const description = ( + <> + {props.children || `We encountered an error processing your request`}. Please try refreshing the page, or logging + out and back in. If the problem persists, please contact support. + + ); + const [detailsVisible, setDetailsVisible] = React.useState(false); + return ( +
+
+
+ + {title} +
+
+
+
{description}
+ {!props.hideActions && ( +
+ - )} - {!props.hideActions && ( - - )} -
+ {props.error && ( + + )} +
+ )}
- {props.error && ( + {props.error && detailsVisible && (
- (exp ? "Hide Details" : "Show Details")}> - {(props.error?.stack || props.error?.message) && } - + {(props.error?.stack || props.error?.message) && } + {typeof props.error === "string" && }
)}
@@ -153,8 +186,8 @@ export const GlobalOverlay: React.FC> = ({ childr
); -export const GlobalError: React.FC = ({ error, title }) => ( +export const GlobalError: React.FC = props => ( - + ); diff --git a/webapps/console/components/GlobalLoader/GlobalLoader.tsx b/webapps/console/components/GlobalLoader/GlobalLoader.tsx index 175e4f545..4cc202751 100644 --- a/webapps/console/components/GlobalLoader/GlobalLoader.tsx +++ b/webapps/console/components/GlobalLoader/GlobalLoader.tsx @@ -1,5 +1,8 @@ import React, { ReactNode, useEffect, useState } from "react"; import classNames from "classnames"; +import dayjs from "dayjs"; +import utc from "dayjs/plugin/utc"; +dayjs.extend(utc); export function Spinner(props: { className?: string }) { return ( @@ -24,21 +27,41 @@ export function Spinner(props: { className?: string }) { export function LoadingAnimation({ title, + longLoadingTitle, + longLoadingThresholdSeconds = 3, className, hideTitle, }: { title?: ReactNode; + longLoadingTitle?: ReactNode; + longLoadingThresholdSeconds?: number; iconSize?: number; fontSize?: string; className?: string; hideTitle?: boolean; }) { + const startTime = dayjs(); + const [longLoading, setLongLoading] = useState(false); + + useEffect(() => { + if (longLoadingTitle) { + const id = setInterval(() => { + if (dayjs().diff(startTime, "second") > longLoadingThresholdSeconds) { + setLongLoading(true); + } + }, 1000); + + return () => clearInterval(id); + } + }, [startTime, longLoadingTitle, longLoadingThresholdSeconds]); + return ( -
+
{!hideTitle &&
{title || "Loading..."}
} + {longLoading &&
{longLoadingTitle}
}
); } @@ -48,15 +71,11 @@ export type GlobalLoaderProps = { }; export const GlobalLoader: React.FC = ({ title }) => { - const [showAnimation, setShowAnimation] = useState(false); - useEffect(() => { - setTimeout(() => setShowAnimation(true), 1000); - }, []); return (
- {showAnimation && } +
diff --git a/webapps/console/components/Htmlizer/Htmlizer.tsx b/webapps/console/components/Htmlizer/Htmlizer.tsx new file mode 100644 index 000000000..bd5153b64 --- /dev/null +++ b/webapps/console/components/Htmlizer/Htmlizer.tsx @@ -0,0 +1,5 @@ +import React, { PropsWithChildren } from "react"; + +export const Htmlizer: React.FC> = ({ children }) => { + return typeof children === "string" ? : <>{children}; +}; diff --git a/webapps/console/components/Icons/ExternalLink.tsx b/webapps/console/components/Icons/ExternalLink.tsx index eee01221e..d74f7711a 100644 --- a/webapps/console/components/Icons/ExternalLink.tsx +++ b/webapps/console/components/Icons/ExternalLink.tsx @@ -1,5 +1,4 @@ -import Icon from "@ant-design/icons"; -import { CustomIconComponentProps } from "@ant-design/icons/lib/components/Icon"; +import Icon, { CustomIconComponentProps } from "@ant-design/icons/lib/components/Icon"; function Svg() { return ( diff --git a/webapps/console/components/Icons/FileText.tsx b/webapps/console/components/Icons/FileText.tsx new file mode 100644 index 000000000..1baa1f9cf --- /dev/null +++ b/webapps/console/components/Icons/FileText.tsx @@ -0,0 +1,28 @@ +import Icon, { CustomIconComponentProps } from "@ant-design/icons/lib/components/Icon"; + +function Svg() { + return ( + + + + + + + + ); +} + +export default function FileText(props: Partial) { + return ; +} diff --git a/webapps/console/components/JitsuButton/JitsuButton.tsx b/webapps/console/components/JitsuButton/JitsuButton.tsx index 2eef72bf6..74bbc4f24 100644 --- a/webapps/console/components/JitsuButton/JitsuButton.tsx +++ b/webapps/console/components/JitsuButton/JitsuButton.tsx @@ -1,37 +1,51 @@ import type { ButtonProps } from "antd/es/button/button"; -import Link from "next/link"; -import { Button } from "antd"; +import { Button, Tooltip } from "antd"; import omit from "lodash/omit"; import React from "react"; -import { WLink } from "../Workspace/WLink"; import { ButtonLabel } from "../ButtonLabel/ButtonLabel"; +import { useRouter } from "next/router"; +import { useWorkspace, useWorkspaceRole } from "../../lib/context"; +import { hasPermission, WorkspacePermissionsType } from "../../lib/workspace-roles"; export type JitsuButtonProps = ButtonProps & { - iconPosition?: "left" | "right"; //set to true if href is relative workspace link ws?: boolean; + className?: string; + requiredPermission?: WorkspacePermissionsType; }; export const WJitsuButton: React.FC>> = p => { - return ( - - - - ); + const workspace = useWorkspace(); + const router = useRouter(); + return router.push(`/${workspace.slugOrId}${p.href}`)} />; +}; + +//href button +const HJitsuButton: React.FC>> = p => { + const router = useRouter(); + return router.push(p.href)} />; }; function Button0(props: JitsuButtonProps) { + const workspaceRole = useWorkspaceRole(); + const hasPerm = props.requiredPermission ? hasPermission(workspaceRole.role, props.requiredPermission) : true; return ( - + + + ); } @@ -40,11 +54,7 @@ export const JitsuButton: React.FC = p => { return ; } if (p.ws) { - return ; + return ; } - return ( - - - - ); + return ; }; diff --git a/webapps/console/components/JsonAsTable/JsonAsTable.module.css b/webapps/console/components/JsonAsTable/JsonAsTable.module.css new file mode 100644 index 000000000..b9821288b --- /dev/null +++ b/webapps/console/components/JsonAsTable/JsonAsTable.module.css @@ -0,0 +1,3 @@ +.jsonTable :global(td) { + vertical-align: top; +} diff --git a/webapps/console/components/JsonAsTable/JsonAsTable.tsx b/webapps/console/components/JsonAsTable/JsonAsTable.tsx new file mode 100644 index 000000000..3b8dd9f7a --- /dev/null +++ b/webapps/console/components/JsonAsTable/JsonAsTable.tsx @@ -0,0 +1,96 @@ +import omit from "lodash/omit"; +import Link from "next/link"; +import { ExternalLink } from "lucide-react"; +import { Table } from "antd"; +import hash from "stable-hash"; +import { ReactNode } from "react"; +import styles from "./JsonAsTable.module.css"; + +function makeNiceName(key: string) { + return key + .replace(/([a-z])([A-Z])/g, "$1 $2") + .split(/(?=[A-Z])/) + .map(word => word.charAt(0).toUpperCase() + word.slice(1)) + .join(" "); +} + +export type TypedColumn = + | { + type: "number"; + } + | { + type: "currency"; + currency?: "USD"; + } + | { type: "link"; href: (val, row) => string } + | { type: "custom"; render: (val, row) => ReactNode }; +export type ColumnOption = + | { omit: true; type?: unknown } + | ({ omit?: false | undefined; title?: string } & TypedColumn); +export const JsonAsTable: React.FC<{ rows: any[]; columnOptions: Record }> = ({ + rows, + columnOptions, +}) => { + const columnsMeta: Record = {}; + const omitColumns = columnOptions + ? Object.entries(columnOptions) + .filter(([_, v]) => v.omit) + .map(([k, _]) => k) + : []; + for (const row of rows) { + const displayKeys = Object.keys(omit({ ...row }, omitColumns)); + for (const key of displayKeys) { + if (!columnsMeta[key]) { + const columnType = columnOptions[key]; + const columnName = (columnType as any)?.title || makeNiceName(key); + const isNumber = columnType?.type === "number" || columnType?.type === "currency"; + columnsMeta[key] = { + key: key, + title: {columnName}, + render: (row: any) => { + return ( +
+ {(() => { + const val = row[key]; + if (columnType?.type === "currency") { + return "$" + new Intl.NumberFormat("en-US", { maximumFractionDigits: 0 }).format(val); + } else if (columnType?.type === "number") { + return new Intl.NumberFormat("en-US").format(val); + } else if (columnType?.type === "custom") { + return (columnType as any).render(val, row); + } else if (columnType?.type === "link") { + return ( + + {val} + + ); + } else { + return typeof val === "undefined" ? "" : typeof val === "object" ? JSON.stringify(val) : `${val}`; + } + })()} +
+ ); + }, + sorter: isNumber + ? (a: number, b: number) => a[key] - b[key] + : (a, b) => (a?.[key] ? `${a?.[key]}` : "").localeCompare(b?.[key] ? `${b?.[key]}` : ""), + }; + } + } + } + return ( + ({ key: hash(r), ...r }))} + pagination={{ pageSize: 1000 }} + expandable={{ + expandedRowRender: row =>
{JSON.stringify(omit(row, "key"), null, 2)}
, + }} + /> + ); +}; diff --git a/webapps/console/components/LabelEllipsis/LabelEllipsis.tsx b/webapps/console/components/LabelEllipsis/LabelEllipsis.tsx index a57129dd4..6153a98cf 100644 --- a/webapps/console/components/LabelEllipsis/LabelEllipsis.tsx +++ b/webapps/console/components/LabelEllipsis/LabelEllipsis.tsx @@ -1,15 +1,23 @@ import { Tooltip } from "antd"; -import { trimMiddle } from "../../lib/shared/strings"; +import { trimEnd, trimMiddle } from "juava"; export type LabelEllipsisProps = { maxLen: number; - children: string; + trim?: "middle" | "end"; + children: any; + className?: string; }; -export function LabelEllipsis(props: LabelEllipsisProps) { - if (props.children.length <= props.maxLen) { - return <>{props.children}; +export function LabelEllipsis({ maxLen, children, trim = "middle", className }: LabelEllipsisProps) { + if (children.length <= maxLen) { + return {children}; } else { - return {trimMiddle(props.children, props.maxLen)}; + return ( + + + {trim === "middle" ? trimMiddle(children, maxLen) : trimEnd(children, maxLen)} + + + ); } } diff --git a/webapps/console/components/MultiSelectWithCustomOptions/MultiSelectWithCustomOptions.tsx b/webapps/console/components/MultiSelectWithCustomOptions/MultiSelectWithCustomOptions.tsx index 414f63de5..d6efd2dba 100644 --- a/webapps/console/components/MultiSelectWithCustomOptions/MultiSelectWithCustomOptions.tsx +++ b/webapps/console/components/MultiSelectWithCustomOptions/MultiSelectWithCustomOptions.tsx @@ -28,7 +28,7 @@ export const MultiSelectWithCustomOptions: React.FC<{ value={selectedIds} mode="multiple" placeholder="Select at least one item" - dropdownMatchSelectWidth={true} + popupMatchSelectWidth={true} className="w-full" onSelect={async val => { if (val === "$custom") { diff --git a/webapps/console/components/ObjectTitle/ObjectTitle.tsx b/webapps/console/components/ObjectTitle/ObjectTitle.tsx new file mode 100644 index 000000000..5d49183d4 --- /dev/null +++ b/webapps/console/components/ObjectTitle/ObjectTitle.tsx @@ -0,0 +1,32 @@ +import React from "react"; +import Link from "next/link"; +import { FaExternalLinkAlt } from "react-icons/fa"; + +export const ObjectTitle: React.FC<{ + size?: "small" | "default" | "large"; + icon?: React.ReactNode; + title: string | React.ReactNode; + href?: string; +}> = ({ icon, title, size = "default", href }) => { + const iconClassName = (() => { + switch (size) { + case "small": + return "h-4 w-4 flex-shrink-0"; + case "large": + return "h-10 w-10 flex-shrink-0"; + default: + return "h-6 w-6 flex-shrink-0"; + } + })(); + return ( +
+ {icon &&
{icon}
} +
{title}
+ {href && ( + + + + )} +
+ ); +}; diff --git a/webapps/console/components/OidcAuthorizer/OidcAuthorizer.tsx b/webapps/console/components/OidcAuthorizer/OidcAuthorizer.tsx new file mode 100644 index 000000000..5b7d2a456 --- /dev/null +++ b/webapps/console/components/OidcAuthorizer/OidcAuthorizer.tsx @@ -0,0 +1,151 @@ +import React, { PropsWithChildren, useEffect, useState } from "react"; +import { useRouter } from "next/router"; +import { getLog } from "juava"; +import { ContextApiResponse } from "../../lib/schema"; +import { UserContextProvider } from "../../lib/context"; +import { GlobalLoader } from "../GlobalLoader/GlobalLoader"; + +const log = getLog("oidc-authorizer"); + +export const OidcAuthorizer: React.FC> = ({ children }) => { + const [user, setUser] = useState(null); + const [loading, setLoading] = useState(true); + const router = useRouter(); + + useEffect(() => { + const checkAndRenewSession = async () => { + try { + // Check session via secure API endpoint + const response = await fetch("/api/auth/dynamic-oidc/session", { + method: "GET", + credentials: "include", + }); + + if (!response.ok) { + return false; + } + + const data = await response.json(); + + if (data.authenticated && data.user) { + // Convert to user format + const oidcUser: ContextApiResponse["user"] = { + email: data.user.email, + externalId: data.user.externalId, + externalUsername: data.user.email, + image: null, + internalId: data.user.internalId, + loginProvider: data.user.loginProvider, + name: data.user.name, + }; + + log.atInfo().log("OIDC user authenticated", { email: oidcUser.email, provider: oidcUser.loginProvider }); + setUser(oidcUser); + return true; + } else if (data.needsRefresh) { + // Token needs refresh, try to renew the session + log.atInfo().log("OIDC session needs refresh, attempting renewal"); + + const renewResponse = await fetch("/api/auth/dynamic-oidc/renew", { + method: "POST", + credentials: "include", + }); + + if (renewResponse.ok) { + // After successful renewal, check session again + const secondCheckResponse = await fetch("/api/auth/dynamic-oidc/session", { + method: "GET", + credentials: "include", + }); + + if (secondCheckResponse.ok) { + const secondData = await secondCheckResponse.json(); + + if (secondData.authenticated && secondData.user) { + const oidcUser: ContextApiResponse["user"] = { + email: secondData.user.email, + externalId: secondData.user.externalId, + externalUsername: secondData.user.email, + image: null, + internalId: secondData.user.internalId, + loginProvider: secondData.user.loginProvider, + name: secondData.user.name, + }; + + log.atInfo().log("OIDC session successfully refreshed", { email: oidcUser.email }); + setUser(oidcUser); + return true; + } + } + } + + log.atWarn().log("Failed to refresh OIDC session, user needs to re-authenticate"); + return false; + } else { + log.atWarn().log("OIDC session not authenticated"); + } + } catch (error) { + log.atError().withCause(error).log("Error checking OIDC session"); + } finally { + setLoading(false); + } + return false; + }; + + // Initial check + const okPromis = checkAndRenewSession(); + + // Set up periodic renewal check (every 30 minutes) + const renewalInterval = setInterval(async () => { + let ok = await okPromis; + if (!ok) { + // we don't have a user, so we don't need to renew + clearInterval(renewalInterval); + return; + } + ok = await checkAndRenewSession(); + if (!ok) { + log.atWarn().log("OIDC session renewal failed"); + // renew may be failed do to network issues or server errors, so we don't clear user here + // oidc-session cookie will die after 24 hours anyway + } + }, 30 * 60 * 1000); + + return () => clearInterval(renewalInterval); + }, []); + + useEffect(() => { + if (router.query.projectName) { + localStorage.setItem("projectName", router.query.projectName as string); + } + }, [router.query.projectName]); + + // If we have a user, provide the context + if (loading) { + return ; + } else if (user) { + return ( + { + // Call logout endpoint to clear httpOnly cookie + try { + await fetch("/api/auth/dynamic-oidc/logout", { + method: "POST", + credentials: "include", + }); + setUser(null); + router.push("/signin"); + } catch (error) { + log.atError().withCause(error).log("Error during OIDC logout"); + } + }} + > + {children} + + ); + } + // If loading or no user, return children without user context + // This allows the next component to handle the auth state + return children; +}; diff --git a/webapps/console/components/PageLayout/ClassicProjectProvider.tsx b/webapps/console/components/PageLayout/ClassicProjectProvider.tsx deleted file mode 100644 index fac1b03d7..000000000 --- a/webapps/console/components/PageLayout/ClassicProjectProvider.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { createContext, PropsWithChildren, useContext, useEffect, useMemo, useState } from "react"; -import { getLog, requireDefined } from "juava"; -import { ClassicProjectStatus, getEeClient } from "../../lib/ee-client"; -import { useAppConfig, useWorkspace } from "../../lib/context"; - -const log = getLog(`ClassicProjectProvider`); - -const defaultStatus = { - active: false, - ok: true, - project: null, - uid: null, - name: null, -}; - -export const ClassicProjectContext = createContext(defaultStatus); - -export function useClassicProject(): ClassicProjectStatus { - const ctx = useContext(ClassicProjectContext); - if (!ctx) { - throw new Error(`useClassicProject() must be used inside `); - } - return ctx; -} - -export const ClassicProjectProvider: React.FC> = ({ children }) => { - const appConfig = useAppConfig(); - const [classicProject, setClassicProject] = useState(defaultStatus); - const currentWorkspace = useWorkspace(); - const eeClient = useMemo( - () => - appConfig.ee.available - ? getEeClient(requireDefined(appConfig.ee.host, `EE is not available`), currentWorkspace.id) - : undefined, - [appConfig.ee.available, appConfig.ee.host, currentWorkspace.id] - ); - useEffect(() => { - if (appConfig.auth?.firebasePublic && appConfig.ee.available && eeClient) { - (async () => { - try { - const classicProject = await eeClient.checkClassicProject(); - if (!classicProject.ok) { - log.atError().log("Classic project check error", classicProject); - setClassicProject({ active: false, ok: false, project: null, uid: null, name: null }); - return; - } - setClassicProject(classicProject); - log.atInfo().log("Classic project", classicProject); - } catch (e) { - log.atError().log("Can't check for classic project", e); - setClassicProject({ active: false, ok: false, project: null, uid: null, name: null }); - } - })(); - } - }, [eeClient, appConfig.auth, appConfig.ee.available]); - - return {children}; -}; diff --git a/webapps/console/components/PageLayout/PageLayout.module.css b/webapps/console/components/PageLayout/PageLayout.module.css deleted file mode 100644 index 3ddadad8a..000000000 --- a/webapps/console/components/PageLayout/PageLayout.module.css +++ /dev/null @@ -1,12 +0,0 @@ -.contentWidth { - max-width: 1400px; - min-width: 600px; - flex-grow: 1; -} - -.rootWrapper { - margin: auto; - max-width: 1400px; - min-width: 700px; - min-height: 80vh; -} diff --git a/webapps/console/components/PageLayout/WorkspacePageLayout.module.css b/webapps/console/components/PageLayout/WorkspacePageLayout.module.css new file mode 100644 index 000000000..110a44dc3 --- /dev/null +++ b/webapps/console/components/PageLayout/WorkspacePageLayout.module.css @@ -0,0 +1,40 @@ +.widthControl { + max-width: 1500px; + width: 100%; + min-width: 1024px; +} + +.topMenu { + background-color: rgb(250 250 250) !important; + border-bottom: 0 !important; +} + +.topMenu li :global(.ant-menu-submenu-title) { +} + +.topMenu :global(.ant-menu-item) { + top: 0 !important; + margin-top: 0 !important; +} + +.topMenu :global(.ant-menu-submenu) { + top: 0 !important; + margin-top: 0 !important; +} + +.topMenu :global(li.ant-menu-submenu-selected) :global(.ant-menu-submenu-title) { + /*background-color: white !important;*/ + /*padding-left: 0.5rem;*/ + /*padding-right: 0.5rem;*/ + /*padding-bottom: 0 !important;*/ + /*border-bottom: 0 !important;*/ +} + +.topMenu :global(li.ant-menu-submenu-selected) :global(.ant-menu-submenu::after) { + all: unset; +} + +/*.topMenu :global .ant-menu {*/ +/* background-color: rgb(250 250 250) !important;*/ +/* border-bottom-color: #eb5757 !important;*/ +/*}*/ diff --git a/webapps/console/components/PageLayout/WorkspacePageLayout.tsx b/webapps/console/components/PageLayout/WorkspacePageLayout.tsx index 59f0fd611..3fdac0d72 100644 --- a/webapps/console/components/PageLayout/WorkspacePageLayout.tsx +++ b/webapps/console/components/PageLayout/WorkspacePageLayout.tsx @@ -1,53 +1,69 @@ -import { PropsWithChildren, ReactNode, useEffect, useState } from "react"; +import React, { PropsWithChildren, ReactNode, useEffect, useState } from "react"; import { branding } from "../../lib/branding"; import { HiSelector } from "react-icons/hi"; -import { FaSignOutAlt, FaUserCircle } from "react-icons/fa"; +import { FaDocker, FaSignOutAlt, FaUserCircle } from "react-icons/fa"; import { FiSettings } from "react-icons/fi"; -import { Button, Drawer, Dropdown, Menu } from "antd"; +import { Button, Drawer, Dropdown, Menu, MenuProps } from "antd"; import MenuItem from "antd/lib/menu/MenuItem"; import { ButtonLabel } from "../ButtonLabel/ButtonLabel"; +import styles from "./WorkspacePageLayout.module.css"; import { Activity, - ArrowLeftRight, - BarChart3, + AlertCircle, + ArrowRight, + BellIcon, + ChevronDown, ChevronUp, + CircleDollarSign, + CreditCard, FilePlus, + FolderKanban, Folders, FunctionSquare, Globe, + Hammer, + HelpCircle, LayoutDashboard, - Loader2, + LineChart, + PackageOpen, + PlugZap, + ScrollText, + SearchCode, Server, ServerCog, Settings, Share2, ShieldAlert, + Terminal, + User, + UserRoundPen, + X, + Zap, } from "lucide-react"; import { NextRouter, useRouter } from "next/router"; import Link from "next/link"; -import { WLink } from "../Workspace/WLink"; import { getDomains, useAppConfig, useUser, useUserSessionControls, useWorkspace } from "../../lib/context"; -import { get, useApi } from "../../lib/useApi"; +import { useApi } from "../../lib/useApi"; import { Overlay } from "../Overlay/Overlay"; import { WorkspaceNameAndSlugEditor } from "../WorkspaceNameAndSlugEditor/WorkspaceNameAndSlugEditor"; -import { getLog, requireDefined } from "juava"; +import { assertDefined, assertTrue, getLog } from "juava"; import classNames from "classnames"; -import { getEeClient } from "../../lib/ee-client"; import { BillingBlockingDialog } from "../Billing/BillingBlockingDialog"; -import { signOut } from "next-auth/react"; -import { firebaseSignOut } from "../../lib/firebase-client"; -import { feedbackError } from "../../lib/ui"; -import { MenuItemType } from "antd/es/menu/hooks/useItems"; -import { useClassicProject } from "./ClassicProjectProvider"; import { useJitsu } from "@jitsu/jitsu-react"; import { useSearchParams } from "next/navigation"; import omit from "lodash/omit"; +import { useBilling, UseBillingResult } from "../Billing/BillingProvider"; +import { useEventsUsage, UseUsageRes } from "../Billing/use-events-usage"; +import { MenuItemType } from "antd/lib/menu/interface"; +import { FaGear } from "react-icons/fa6"; export type PageLayoutProps = { fullscreen?: boolean; + screen?: boolean; onClose?: () => void; + contentClassName?: string; className?: string; doNotBlockIfUsageExceeded?: boolean; }; @@ -85,25 +101,9 @@ export const WorkspaceMenu: React.FC<{ closeMenu: () => void; classicProject?: s ); }; -function AdminMenuItems() { - const { data, error } = useApi(`/user/properties`); - if (error) { - log.atWarn().log("Failed to load user properties", error); - } else if (data) { - return ( - }> - Admin Users - - ); - } - return <>; -} - function WorkspacesMenu(props: { jitsuClassicAvailable: boolean }) { const router = useRouter(); const [classicLoading, setClassicLoading] = useState(false); - const [workspacesLoading, setWorkspacesLoading] = useState(false); - const [adding, setAdding] = useState(false); const workspace = useWorkspace(); const appConfig = useAppConfig(); @@ -111,17 +111,43 @@ function WorkspacesMenu(props: { jitsuClassicAvailable: boolean }) { let additionalMenuItems: MenuItemType[] = []; if (error) { log.atWarn().log("Failed to load user properties", error); - } else if (data?.admin) { + } else if (data?.admin && appConfig.auth?.firebasePublic) { additionalMenuItems = [ { key: "admin-users", - label: "Admin Users", - icon: , - onClick: async () => { - await router.push("/admin/users"); - }, + + label: ( + + }> + Admin Users + + + ), + }, + { + label: ( + + }> + Admin Workspaces + + + ), + + key: "admin-workspaces", }, ]; + if (appConfig.ee.available) { + additionalMenuItems.push({ + key: "billing-workspaces", + label: ( + + }> + Billing Administration + + + ), + }); + } } return ( @@ -129,65 +155,27 @@ function WorkspacesMenu(props: { jitsuClassicAvailable: boolean }) { items={[ { key: "all-workspaces", - label: "View all workspaces", - icon: workspacesLoading ? ( - - ) : ( - + label: ( + + }> + All Workspaces + + ), - onClick: async () => { - setWorkspacesLoading(true); - try { - await router.push("/workspaces"); - } finally { - setWorkspacesLoading(false); - } - }, }, { key: "new-workspace", - label: "Create new workspace", - icon: adding ? : , + label: ( +
+ }> + Create new workspace + +
+ ), onClick: async () => { - setAdding(true); - try { - const { id } = await get("/api/workspace", { method: "POST", body: {} }); - await router.push(`/${id}`); - } catch (e) { - feedbackError(`Can't create new workspace`, { error: e }); - } finally { - setAdding(false); - } + await router.push("/new-workspace"); }, }, - ...(props.jitsuClassicAvailable - ? [ - { - key: "switch", - label: "Switch to Jitsu Classic", - icon: classicLoading ? ( - - ) : ( - - ), - onClick: async () => { - setClassicLoading(true); - try { - const eeClient = getEeClient( - requireDefined(appConfig.ee.host, `EE is not available`), - workspace.id - ); - const customToken = await eeClient.createCustomToken(); - window.location.href = `${appConfig.jitsuClassicUrl}/?token=${customToken}`; - } catch (e) { - feedbackError(`Can't navigate to Jitsu.Classic`, { error: e }); - } finally { - //setClassicLoading(false); - } - }, - }, - ] - : []), ...additionalMenuItems, ]} /> @@ -196,13 +184,11 @@ function WorkspacesMenu(props: { jitsuClassicAvailable: boolean }) { export const WorkspaceSelector: React.FC = props => { const [open, setOpen] = useState(false); - const classicProject = useClassicProject(); + //const classicProject = useClassicProject(); return ( ( - - )} + dropdownRender={() => } trigger={["click"]} open={open} onOpenChange={open => setOpen(open)} @@ -219,63 +205,105 @@ type TabsMenuItem = { title: ReactNode; icon: ReactNode; path: string; + globalPath?: boolean; aliases?: string[] | string; hidden?: boolean; + items?: never; +}; + +type TabsMenuGroup = { + title: ReactNode; + icon: ReactNode; + items: (TabsMenuItem | undefined)[]; }; export type TopTabsMenuProps = { - items: TabsMenuItem[]; + items: (TabsMenuItem | TabsMenuGroup)[]; }; -function isSelected(item: { title: React.ReactNode; path: string; aliases?: string[] | string }, router: NextRouter) { +function isSelected(item: string, router: NextRouter) { let workspacePath = router.pathname.replace("/[workspaceId]", ""); if (workspacePath === "") { workspacePath = "/"; } - return item.path === workspacePath || item.aliases === workspacePath || item.aliases?.includes(workspacePath); + return item === workspacePath; +} + +function MenuLabel({ children, icon, hasSubMenu }: { children: ReactNode; icon?: ReactNode; hasSubMenu?: boolean }) { + return ( +
+ {icon &&
{icon}
} +
{children}
+ {hasSubMenu && ( +
+ +
+ )} +
+ ); } export const TopTabsMenu: React.FC = props => { const router = useRouter(); + const workspace = useWorkspace(); + + const items: MenuProps["items"] = props.items.map(item => { + if (item.items) { + return { + label: {item.title}, + key: + "[" + + item.items + .filter(Boolean) + .map(subItem => subItem!.path) + .join("-") + + "]", + selected: true, + children: item.items.filter(Boolean).map(subItem => ({ + key: subItem!.path, + label: ( + + + {subItem!.title} + + + ), + link: subItem!.path, + })), + }; + } else { + return { + label: ( + + {item.title} + + ), + key: item.path, + link: item.path, + }; + } + }); + const allKeys = props.items.map(x => (x.items ? x.items.filter(Boolean).map(i => i!.path) : x.path)).flat(); return ( -
-
- {props.items - .filter(i => !i.hidden) - .map(item => { - const selected = isSelected(item, router); - return ( -
-
- - -
{item.icon}
- {item.title} -
-
-
-
- ); - })} -
-
+ {}} + selectedKeys={allKeys.filter(p => isSelected(p, router))} + mode="horizontal" + items={items} + /> ); }; function Breadcrumbs() { const workspace = useWorkspace(); + return (
-
{branding.logo}
+
+ {branding.logo} +
@@ -290,6 +318,7 @@ function Breadcrumbs() { function UserProfileMenu({ user }: { user: { name: string; email: string } }) { const router = useRouter(); + const { analytics } = useJitsu(); const sessionControl = useUserSessionControls(); return (
@@ -305,7 +334,7 @@ function UserProfileMenu({ user }: { user: { name: string; email: string } }) { { await sessionControl.logout(); - router.push("/", undefined, { shallow: true }); + analytics.reset(); }} > }>Logout @@ -342,51 +371,223 @@ const UserProfileButton: React.FC<{}> = () => { ); }; +const AlertView: React.FC> = ({ children }) => { + const [show, setShow] = useState(false); + const workspaceAlertHiddenAt = "workspaceAlertHiddenAt"; + useEffect(() => { + const hiddenAt = localStorage.getItem(workspaceAlertHiddenAt); + const hideAlertSeconds = 60 * 60; // 1 hour + if (!hiddenAt || new Date().getTime() - new Date(hiddenAt).getTime() > 1000 * hideAlertSeconds) { + setTimeout(() => setShow(true), 1); // show after 1ms to avoid SSR issues and enable animation + } + }, []); + + return ( +
+
+ +
{children}
+
+ +
+
+
+ ); +}; + +function usageIsAboutToExceed(billing: UseBillingResult, usage: UseUsageRes) { + return ( + billing.enabled && + billing.settings && + !usage.isLoading && + !usage.error && + usage.usage?.usagePercentage && + usage.usage?.usagePercentage < 1 && + billing.settings.planId === "free" && + usage.usage?.projectionByTheEndOfPeriod && + usage.usage?.projectionByTheEndOfPeriod > usage.usage.maxAllowedDestinatonEvents + ); +} + +const FreePlanQuotaAlert: React.FC<{}> = () => { + const workspace = useWorkspace(); + const usage = useEventsUsage({ skipSubscribed: true }); + const billing = useBilling(); + const router = useRouter(); + assertTrue(billing.enabled, "Billing should be enabled and loaded"); + assertDefined(billing.settings, "Billing settings should be loaded"); + + if (router.pathname.endsWith("/settings/billing")) { + //don't display second alert on billing page + return <>; + } + + if (usage.throttle) { + return ( + + Your workspace is throttled due to exceeding the free plan quota at rate of {usage.throttle}% + + Go to billing to see more details{" "} + + + + ); + } else if (usageIsAboutToExceed(billing, usage)) { + return ( + + You are projected to exceed your monthly events. Please upgrade your plan to avoid service disruption.{" "} + + Go to billing + + + ); + } + + return <>; +}; + +const WorkspaceAlert: React.FC<{}> = () => { + const billing = useBilling(); + if (billing.loading || !billing.enabled) { + return <>; + } + return ; +}; + function PageHeader() { const appConfig = useAppConfig(); const workspace = useWorkspace(); - const items: TabsMenuItem[] = [ + const billing = useBilling(); + const items: (TabsMenuItem | TabsMenuGroup | undefined | false)[] = [ { title: "Overview", path: "/", aliases: "/overview", icon: }, - { title: "Sites", path: "/streams", icon: }, - { title: "Destinations", path: "/destinations", icon: }, - { title: "Connections", path: "/connections", icon: }, + { + title: "Event Streaming", + icon: , + items: [ + { title: "Sites", path: "/streams", icon: }, + { title: "Connections", path: "/connections", icon: }, + ], + }, + (appConfig.syncs.enabled || workspace.featuresEnabled?.includes("syncs")) && { + title: "Connectors", + icon: , + items: [ + { title: "Service Connections", path: "/services", icon: }, + { title: "Syncs", path: "/syncs", icon: }, + { title: "All Logs", path: "/syncs/tasks", icon: }, + { title: "Custom Images", path: "/custom-images", icon: }, + ], + }, + appConfig.ee?.available && { + title: "Customers", + icon: , + items: [{ title: "Profile Builder", path: "/profile-builder", icon: }], + }, { title: "Functions", path: "/functions", icon: }, - ]; - if (workspace.featuresEnabled && workspace.featuresEnabled.includes("syncs")) { - items.push( - { title: "Services", path: "/services", icon: }, - { title: "Syncs", path: "/syncs", icon: } - ); - } - items.push( - { title: "Live Events", path: "/data", icon: }, - { title: "Query Data", path: "/sql", icon: , hidden: !appConfig?.ee }, + { title: "Destinations", path: "/destinations", icon: }, + { + title: "Data", + icon: , + items: [ + { title: "Live Events", path: "/data", icon: }, + { title: "Query Data", path: "/sql", icon: , hidden: !appConfig?.ee }, + appConfig.ee?.available + ? { + title: "Event Statistics", + path: "/event-stat", + icon: , + hidden: !appConfig?.ee, + } + : undefined, + ], + }, { title: "Settings", - path: "/settings", icon: , - } - ); + items: [ + { title: "Workspace Settings", path: "/settings", icon: }, + appConfig.customDomainsEnabled + ? { title: "Domains", path: "/settings/domains", icon: } + : undefined, + { title: "User Settings", path: "/user", icon: , globalPath: true }, + { title: "Billing Settings", path: "/settings/billing", icon: }, + { + title: "Notification Settings", + path: "/settings/notifications", + icon: , + }, + billing.enabled && billing.settings?.dataRetentionEditorEnabled + ? { + title: "Data Retention", + path: "/settings/data-retention", + icon: , + } + : undefined, + workspace.featuresEnabled?.includes("misc") + ? { + title: "Miscellaneous Settings", + path: "/miscs", + icon: , + } + : undefined, + ], + }, + appConfig.ee?.available && { + title: "Support", + path: "/support", + icon: , + }, + ]; return (
-
-
+
+ +
- + !!i) as (TabsMenuItem | TabsMenuGroup)[]} />
); } +//minimum with of the window +const minWidth = 1024; -const WorkspaceSettingsModal: React.FC<{ onSuccess: () => void }> = ({ onSuccess }) => { - const cfg = useAppConfig(); - const domains = getDomains(cfg); +/** + * @param onboarding if the dialog is shown on onboarding page. For onboarding, + * we should issue an event that onboarding is completed + */ +const WorkspaceSettingsModal: React.FC<{ + onSuccess: (newVals: { name: string; slug: string }) => void; + onboarding: boolean; +}> = ({ onSuccess, onboarding }) => { + const appConfig = useAppConfig(); + const domains = getDomains(appConfig); const { analytics } = useJitsu(); const { push, query } = useRouter(); const searchParams = useSearchParams(); const welcome = searchParams.get("welcome"); + const sessionControl = useUserSessionControls(); + const workspace = useWorkspace(); useEffect(() => { if (welcome) { @@ -397,14 +598,15 @@ const WorkspaceSettingsModal: React.FC<{ onSuccess: () => void }> = ({ onSuccess const dataIngestion = ( <> - {cfg.publicEndpoints.protocol}://yourslug.{cfg.publicEndpoints.dataHost} - {cfg.publicEndpoints.port ? `:${cfg.publicEndpoints.port}` : ""} + {appConfig.publicEndpoints.protocol}://yourslug. + {appConfig.publicEndpoints.dataHost} + {appConfig.publicEndpoints.port ? `:${appConfig.publicEndpoints.port}` : ""} ); return ( -
-
+
+

👋 Let's get started!

Pick a name a slug for your {branding.productName} workspace. Slug will be used in the URLs{" "} @@ -412,22 +614,10 @@ const WorkspaceSettingsModal: React.FC<{ onSuccess: () => void }> = ({ onSuccess {domains.appBase}/your-slug {" "}
- +
Got here by mistake?{" "} - { - //we can't use current session here, since the error can be originated - //from auth layer. Try to logout using all methods - signOut().catch(err => { - log.atWarn().withCause(err).log(`Can't sign ut from next-auth`); - }); - firebaseSignOut().catch(err => { - log.atWarn().withCause(err).log(`Can't sign ut from next-auth`); - }); - }} - > + Sign out {" "} or{" "} @@ -444,20 +634,22 @@ const WorkspaceSettingsModal: React.FC<{ onSuccess: () => void }> = ({ onSuccess const log = getLog("WorkspacePageLayout"); export const VerticalSection: React.FC> = ({ children, className }) => { - return
{children}
; -}; - -export const WidthControl: React.FC> = ({ children, className }) => { return ( -
+
{children}
); }; +export const WidthControl: React.FC> = ({ children, className }) => { + return
{children}
; +}; + export const WorkspacePageLayout: React.FC> = ({ className, + screen, fullscreen, + contentClassName, onClose, children, doNotBlockIfUsageExceeded, @@ -470,27 +662,53 @@ export const WorkspacePageLayout: React.FC> = throw new Error(`${router.asPath} is not a workspace page`); } + useEffect(() => { + if (typeof window !== "undefined") { + const handleKeyDown = (event: KeyboardEvent) => { + if (event.ctrlKey && event.shiftKey && event.key === "M") { + const userConfirmed = window.confirm( + `Do you really want to open a window with minimum width of ${minWidth}px?` + ); + if (userConfirmed) { + window.open(window.location.href, "_blank", `width=${minWidth},height=${window.innerHeight}`); + } + } + }; + window.addEventListener("keydown", handleKeyDown); + return () => window.removeEventListener("keydown", handleKeyDown); + } + }, [window]); + useEffect(() => { setShowDrawer(false); }, [fullscreen]); + if (workspace.deleted) { + router.push("/workspaces"); + return

This workspace was deleted, redirecting...

; + } + const pHeader = ( - - -
- -
+ + + ); + return ( -
+
{!doNotBlockIfUsageExceeded && } -
+
{!workspace.slug && ( { - router.reload(); + onboarding={true} + onSuccess={({ slug }) => { + if (slug) { + router.push(`/${slug}`); + } else { + router.reload(); + } }} /> )} @@ -508,7 +726,7 @@ export const WorkspacePageLayout: React.FC> =
> = ) : ( pHeader )} - + {fullscreen && ( - )} - {children} + {children}
diff --git a/webapps/console/components/PriorityQueueBar/PriorityQueueBar.tsx b/webapps/console/components/PriorityQueueBar/PriorityQueueBar.tsx new file mode 100644 index 000000000..8e0017323 --- /dev/null +++ b/webapps/console/components/PriorityQueueBar/PriorityQueueBar.tsx @@ -0,0 +1,64 @@ +import { Tooltip } from "antd"; + +interface PriorityQueueProps { + queueSizes: number[]; + maxTotalSize?: number; + className?: string; +} + +export default function PriorityQueueBar({ queueSizes = [], className = "", maxTotalSize }: PriorityQueueProps) { + const totalSize = queueSizes.reduce((total, size) => total + size, 0); + const colors = ["bg-red-500", "bg-yellow-500", "bg-green-500", "bg-green-600", "bg-green-700", "bg-green-800"]; + const restPercent = maxTotalSize ? ((maxTotalSize - totalSize) / maxTotalSize) * 100 : 0; + + if (totalSize === 0) { + return
0
; + } + return ( +
+
+
Total Size: {totalSize.toLocaleString()}
+
+ + + {queueSizes.map((size, index) => { + return ( +
+ priority: {index} – items:{" "} + {size.toLocaleString()} +
+ ); + })} + + } + > +
+
+ {queueSizes.map((size, index) => { + const percentage = totalSize > 0 ? (size / Math.max(totalSize, maxTotalSize ?? 0)) * 100 : 0; + return ( +
+ {size} +
+ ); + })} + {restPercent > 0 && ( +
+ )} +
+
+ +
+ ); +} diff --git a/webapps/console/components/ProfileBuilderPage/ProfileBuilderPage.module.css b/webapps/console/components/ProfileBuilderPage/ProfileBuilderPage.module.css new file mode 100644 index 000000000..bd6c64718 --- /dev/null +++ b/webapps/console/components/ProfileBuilderPage/ProfileBuilderPage.module.css @@ -0,0 +1,15 @@ +.tabsHeightFix { + height: 100%; +} + +.tabsHeightFix :global(.ant-tabs-content) { + height: 100% !important; +} + +.settingsTable :global(.ant-tabs-content) { + height: 100% !important; +} + +.splitterFix :global(.ant-splitter-bar-dragger::before) { + background-color: white !important; +} diff --git a/webapps/console/components/ProfileBuilderPage/ProfileBuilderPage.tsx b/webapps/console/components/ProfileBuilderPage/ProfileBuilderPage.tsx new file mode 100644 index 000000000..b28867cc6 --- /dev/null +++ b/webapps/console/components/ProfileBuilderPage/ProfileBuilderPage.tsx @@ -0,0 +1,1257 @@ +import { WorkspacePageLayout } from "../PageLayout/WorkspacePageLayout"; +import React, { useCallback, useEffect, useReducer, useState } from "react"; +import { + Badge, + Button, + Descriptions, + DescriptionsProps, + Input, + Popover, + Splitter, + Statistic, + Tabs, + Tag, + Tooltip, +} from "antd"; +import { + Bug, + Code2, + OctagonAlert, + Play, + Save, + Settings, + Terminal, + Lock, + Hammer, + ThumbsUp, + LoaderCircle, + Braces, + RefreshCw, + History, + Parentheses, + SearchCode, + Rocket, +} from "lucide-react"; +import { CodeEditor } from "../CodeEditor/CodeEditor"; +import { ButtonLabel } from "../ButtonLabel/ButtonLabel"; +import { copyTextToClipboard, feedbackError, feedbackSuccess, PropsWithChildrenClassname } from "../../lib/ui"; +import classNames from "classnames"; +import styles from "./ProfileBuilderPage.module.css"; +import FieldListEditorLayout from "../FieldListEditorLayout/FieldListEditorLayout"; +import Link from "next/link"; +import { useBilling } from "../Billing/BillingProvider"; +import { useWorkspace, useWorkspaceRole } from "../../lib/context"; +import { ErrorCard } from "../GlobalError/GlobalError"; +import type { PresetColorType, PresetStatusColorType } from "antd/es/_util/colors"; +import { get, getConfigApi } from "../../lib/useApi"; +import { useConfigObjectList, useStoreReload } from "../../lib/store"; +import { DestinationSelector } from "../Selectors/DestinationSelector"; +import { NumberEditor } from "../ConfigObjectEditor/Editors"; +import { isEqual, rpc } from "juava"; +import dayjs from "dayjs"; +import utc from "dayjs/plugin/utc"; +import relativeTime from "dayjs/plugin/relativeTime"; +import { JitsuButton } from "../JitsuButton/JitsuButton"; +import { useAntdModal } from "../../lib/modal"; +import { testDataExample } from "./example"; +import { logType } from "@jitsu/core-functions"; +import { FunctionLogs } from "../FunctionsDebugger/FunctionLogs"; +import { FunctionResult } from "../FunctionsDebugger/FunctionResult"; +import { FunctionVariables } from "../FunctionsDebugger/FunctionVariables"; +import omit from "lodash/omit"; +import { WLink } from "../Workspace/WLink"; +import { getCoreDestinationTypeNonStrict } from "../../lib/schema/destinations"; +import { FunctionsSelector } from "../FunctionsSelector/FunctionsSelector"; +import { ButtonGroup, ButtonProps } from "../ButtonGroup/ButtonGroup"; +import PriorityQueueBar from "../PriorityQueueBar/PriorityQueueBar"; + +dayjs.extend(utc); +dayjs.extend(relativeTime); + +export const defaultProfileBuilderFunction = `export default async function(events, user, context) { + context.log.info("Profile userId: " + user.id) + const profile = {} + profile.anonId = user.anonymousId + return { + traits: profile + } +};`; + +const statuses: Record< + ProfileBuilderStatus | "loading" | "locked", + { + title: React.ReactNode; + documentation: React.ReactNode; + icon: React.ReactNode; + color: PresetColorType | PresetStatusColorType; + } +> = { + incomplete: { + color: "lime", + title: "INCOMPLETE", + documentation: <>Profile builder is not fully configured. Go to settings section to finish configuration, + icon: , + }, + locked: { + color: "blue", + title: "LOCKED", + documentation: ( + <> + Profile builder is not enabled for your account. You still can define profiles with JavaScript function, and run + test. To enable profile builder for your account please contact support + + ), + icon: , + }, + building: { + color: "yellow", + title: "BUILDING", + documentation: ( + <> + Profile builder is building initial profiles. In can happen after the first configuration, or after a change has + been made in the code. You can monitor the progress in the progress section + + ), + icon: , + }, + ready: { + color: "green", + title: "READY", + documentation: <>Profile builder is ready to use. You can query profiles, from a configured destination, + icon: , + }, + loading: { + color: "processing", + title: "LOADING", + documentation: <>Settings are being loaded, please wait, + icon: , + }, +}; + +const Header: React.FC<{ status: ProfileBuilderStatus | "loading"; pbEnabled: boolean }> = ({ status, pbEnabled }) => { + let statusDetails = statuses[status]; + if (status !== "loading" && !pbEnabled) { + statusDetails = statuses["locked"]; + } + + return ( +
+

Profile Builder

+ + +
+
{statusDetails.icon}
+
{statusDetails.title}
+
+
+
+
+ ); +}; + +export function Dot(props: { color?: string }) { + return ( + + + + ); +} + +const SettingsTab: React.FC<{ + profileBuilder: ProfileBuilderData; + dispatch: React.Dispatch; + disabled?: boolean; +}> = ({ profileBuilder, disabled, dispatch }) => { + const settings = profileBuilder.settings; + + const destinations = useConfigObjectList("destination").filter(d => { + const dest = getCoreDestinationTypeNonStrict(d.destinationType); + return dest?.usesBulker; + }); + + return ( +
+ { + copyTextToClipboard(profileBuilder.id); + feedbackSuccess("Profile Builder Id copied to clipboard"); + }} + className={"w-80 border rounded-md py-1 px-2.5 cursor-pointer text-textLight"} + > + {profileBuilder.id} +
+ ), + }, + { + key: "destination", + name: settings.destinationId ? ( + "Default Destination" + ) : ( + Default Destination + ), + documentation: ( + <> + Select the destination database where the profiles will be stored. Destination can be assigned in{" "} + Profile Function:
+
+
+ {`return { + destinationId: "cm79123abc" + traits: profile +}`} +
+ + ), + component: ( + dispatch({ type: "settings", value: { ...settings, destinationId: d } })} + /> + ), + }, + { + key: "table-name", + name: "Default Table Name", + documentation: ( + <> + The name of the table where the profiles will be stored. Table Name can be assigned in{" "} + Profile Function:
+
+
+ {`return { + tableName: "my_profiles" + traits: profile +}`} +
+ + ), + component: ( +
+ dispatch({ type: "settings", value: { ...settings, tableName: e.target.value } })} + /> +
+ ), + }, + { + key: "window-days", + name: "Profile Window", + documentation: ( + <>The time window, in days, for querying user activity history during profile generation.. + ), + component: ( +
+ dispatch({ type: "settings", value: { ...settings, profileWindow: n } })} + /> +
+ ), + }, + { + key: "storage", + name: "Storage", + documentation: ( + <> + The host name of the profile storage. To make changes, please{" "} + contact support + + ), + component: ( +
+ +
+ ), + }, + ]} + /> + {/*
*/} + {/* {*/} + {/* modal.confirm({*/} + {/* title: "Publish Profile Builder?",*/} + {/* content:*/} + {/* status === "incomplete" ? (*/} + {/* <>*/} + {/* Saving the settings will lead to publishing the first version of profile builder.*/} + {/*
*/} + {/*
*/} + {/* Are you sure you want to proceed?*/} + {/* */} + {/* ) : (*/} + {/* <>*/} + {/* Saving the settings will lead to publishing the new version of profile builder and rebuilding all*/} + {/* customers profiles.*/} + {/*
*/} + {/*
*/} + {/* Are you sure you want to proceed?*/} + {/* */} + {/* ),*/} + {/* okText: status === "incomplete" ? "Publish the first version" : "Publish",*/} + {/* okType: "primary",*/} + {/* cancelText: "Cancel",*/} + {/* onOk: () => {},*/} + {/* });*/} + {/* }}*/} + {/* type="primary"*/} + {/* className="mt-2"*/} + {/* size={"large"}*/} + {/* >*/} + {/* Save*/} + {/* */} + {/*
*/} +
+ ); +}; + +type ProfileBuilderState = { + status: "ready" | "building" | "unknown" | "error"; + error?: string; + updatedAt?: Date; + fullRebuildInfo?: any; + queuesInfo?: any; + metrics?: any; +}; + +const BuildProgress: React.FC<{ + profileBuilder: ProfileBuilderData; + state?: ProfileBuilderState; + loading?: boolean; + refreshTrigger: () => void; +}> = ({ profileBuilder, state, refreshTrigger, loading }) => { + let status = state?.status; + if (!status || status === "unknown") { + status = "building"; + } + let mappedStatus: PresetStatusColorType = "default"; + switch (status) { + case "ready": + mappedStatus = "success"; + break; + case "building": + mappedStatus = "processing"; + break; + case "error": + mappedStatus = "error"; + } + const items: DescriptionsProps["items"] = [ + { + key: "status", + label: "Status", + children: ( +
+ + } + /> +
+ ), + }, + ]; + if (status === "error") { + items.push({ + key: "error", + label: "Error", + children:
{state?.error}
, + }); + } + if (status === "building" || status === "ready") { + items.push({ + key: "version", + label: "Version", + children:
{profileBuilder.version}
, + }); + items.push({ + key: "published", + label: "Published", + children:
{new Date(profileBuilder.updatedAt!).toLocaleString()}
, + }); + if (state?.fullRebuildInfo?.timestamp) { + items.push({ + key: "rebuilt", + label: "Full Rebuild - Date", + children:
{new Date(state?.fullRebuildInfo?.timestamp!).toLocaleString()}
, + }); + items.push({ + key: "rebuilt", + label: "Full Rebuild - Version", + children:
{state?.fullRebuildInfo?.version ?? "-"}
, + }); + items.push({ + key: "total", + label: "Full Rebuild - Total Users", + children: + typeof state?.fullRebuildInfo?.profilesCount !== "undefined" ? ( + + ) : ( +
Initiating...
+ ), + }); + } + } + items.push({ + key: "queue", + label: "Queue Size", + children: ( + a.priority - b.priority) + .map((q: any) => q.size)} + /> + ), + }); + if (state?.queuesInfo.intervalSec) { + items.push({ + key: "speed", + label: "Processing Speed", + children: ( +
+ {( + (Object.values(state?.queuesInfo.queues) + .map((q: any) => q.processed) + .reduce((a: any, b: any) => a + b, 0) || 0) / state?.queuesInfo.intervalSec + ).toLocaleString()}{" "} + events/sec +
+ ), + }); + } + + return ( +
+ +
+ ); +}; + +const TabContent: React.FC = ({ children, className }) => { + return ( +
+ {children} +
+ ); +}; + +type ProfileBuilderStatus = "incomplete" | "building" | "ready"; +type ProfileBuilderData = { + status: ProfileBuilderStatus; + id: string | undefined; + name: string; + functionId: string | undefined; + draft: string | undefined; + draftUpdatedAt: Date | undefined; + code: string | undefined; + cli: boolean | undefined; + version: number | undefined; + settings: { + storage?: string; + destinationId?: string; + tableName?: string; + profileWindow?: number; + variables: any; + functions?: { functionId: string; functionOptions?: any }[]; + }; + createdAt: Date | undefined; + updatedAt: Date | undefined; +}; + +const defaultProfileBuilderData: ProfileBuilderData = { + id: undefined, + version: 0, + status: "incomplete", + name: "Profile Builder", + functionId: undefined, + code: undefined, + cli: undefined, + draft: undefined, + draftUpdatedAt: undefined, + settings: { + variables: {}, + }, + createdAt: undefined, + updatedAt: undefined, +}; + +type PBDataAction = + | { + [K in keyof ProfileBuilderData]: { + type: K; + value: ProfileBuilderData[K]; + }; + }[keyof ProfileBuilderData] + | { type: "replace"; value: ProfileBuilderData }; + +function pbDataReducer(state: ProfileBuilderData, action: PBDataAction) { + if (action.type === "replace") { + return action.value; + } + return { + ...state, + [action.type]: action?.value, + }; +} + +function useProfileBuilderState( + profileBuilder: ProfileBuilderData, + pbEnabled: boolean, + refreshDate: Date +): { isLoading: boolean; error?: Error; data?: ProfileBuilderState } { + const workspace = useWorkspace(); + const [loading, setLoading] = useState(false); + const [data, setData] = useState(); + + useEffect(() => { + if (pbEnabled) { + setLoading(true); + get(`/api/${workspace.id}/profile-builder/state?profileBuilderId=${profileBuilder.id}`) + .then(res => { + setData(res); + }) + .catch(e => setData({ status: "error", error: e.message })) + .finally(() => setLoading(false)); + } + }, [profileBuilder, refreshDate, workspace.id, pbEnabled]); + return { isLoading: loading, data } as any; +} + +function useProfileBuilderData( + refreshDate: Date +): + | { isLoading: true; error?: never; data?: never; enabled: boolean } + | { isLoading: false; error: Error; data?: never; enabled: boolean } + | { isLoading: false; error?: never; data: ProfileBuilderData; enabled: boolean } { + const workspace = useWorkspace(); + const [enabled, setEnabled] = useState(false); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(); + const [data, setData] = useState(); + const billing = useBilling(); + useEffect(() => { + if (billing.enabled && billing.loading) { + setLoading(true); + return; + } + (async () => { + get(`/api/${workspace.id}/config/profile-builder?init=true`) + .then(res => res.profileBuilders) + .then(profileBuilders => { + let status: ProfileBuilderStatus = "incomplete"; + if (billing.enabled) { + if (billing.settings?.profileBuilderEnabled) { + setEnabled(true); + } + } + if (profileBuilders?.length) { + const pb = profileBuilders[0]; + if (pb.version > 0 && pb.destinationId) { + status = "ready"; + } + setData({ + status: status, + id: pb.id, + name: pb.name, + version: pb.version, + functionId: pb.functions?.length ? pb.functions[0].functionId : undefined, + draft: pb.functions?.length ? pb.functions[0].function.config.draft : defaultProfileBuilderFunction, + draftUpdatedAt: pb.functions?.length ? pb.functions[0].function.updatedAt : undefined, + code: pb.functions?.length ? pb.functions[0].function.config.code : defaultProfileBuilderFunction, + cli: pb.functions?.length ? !!pb.functions[0].function.config.slug : undefined, + settings: { + storage: pb.intermediateStorageCredentials, + destinationId: pb.destinationId, + tableName: pb.connectionOptions?.tableName, + variables: pb.connectionOptions?.variables, + functions: pb.connectionOptions?.functions, + profileWindow: pb.connectionOptions?.profileWindow, + }, + createdAt: pb.createdAt, + updatedAt: pb.updatedAt, + }); + return; + } else { + setData({ + ...defaultProfileBuilderData, + draft: defaultProfileBuilderFunction, + }); + } + }) + .catch(e => setError(e)) + .finally(() => setLoading(false)); + })(); + }, [billing.enabled, billing.loading, workspace, billing.settings, refreshDate.getTime()]); + return { isLoading: loading, error, data, enabled } as any; +} + +const UserIdDialog: React.FC<{ profileBuilderId: string; setter: (v: string) => void; hideCallback: () => void }> = ({ + profileBuilderId, + setter, + hideCallback, +}) => { + const workspace = useWorkspace(); + const [loading, setLoading] = useState(false); + const [userId, setUserId] = useState(""); + + const load = useCallback( + (userId: string) => { + setLoading(true); + get(`/api/${workspace.id}/profile-builder/events?profileBuilderId=${profileBuilderId}&userId=${userId}`) + .then(res => { + if (res.status === "ok") { + setter(JSON.stringify(res.events, null, 2)); + } else { + setter(res.error); + } + }) + .catch(e => setter(e.message)) + .finally(() => { + setLoading(false); + hideCallback(); + }); + }, + [setter, hideCallback, workspace.id, profileBuilderId] + ); + + return ( +
+ Please enter the profileId or userId of the user you want to test the profile builder + for: +
+ setUserId(e.target.value)} /> + +
+
+ ); +}; + +const Overlay: React.FC<{ children?: React.ReactNode; visible: boolean; className?: string }> = ({ + children, + visible, + className, +}) => { + if (!visible) { + return <>; + } + return
{children || ""}
; +}; + +export function ProfileBuilderPage() { + const workspace = useWorkspace(); + const role = useWorkspaceRole(); + const canEdit = role.editEntities; + const functions = useConfigObjectList("function").filter(f => f.kind !== "profile"); + const [pbRefreshDate, setPbRefreshDate] = useState(new Date()); + const [stateRefreshDate, setStateRefreshDate] = useState(new Date()); + const [obj, dispatch] = useReducer(pbDataReducer, defaultProfileBuilderData); + const { data: initialData, error: globalError, isLoading, enabled } = useProfileBuilderData(pbRefreshDate); + const { data: pbState, isLoading: stateLoading } = useProfileBuilderState(initialData!, enabled, stateRefreshDate); + const [saving, setSaving] = useState(false); + const [editorShown, setEditorShown] = useState(false); + + const [activePrimaryTab, setActivePrimaryTab] = useState("code"); + const [activeSecondaryTab, setActiveSecondaryTab] = useState("test"); + const [testData, setTestData] = useState(testDataExample); + const [userIdDialogOpen, setUserIdDialogOpen] = useState(false); + const modal = useAntdModal(); + const [result, setResult] = useState({}); + const [resultType, setResultType] = useState<"ok" | "drop" | "error">("ok"); + const [logs, setLogs] = useState([]); + const [unreadErrorLogs, setUnreadErrorLogs] = useState(0); + const [unreadLogs, setUnreadLogs] = useState(0); + const [running, setRunning] = useState(false); + + const codeChanged = obj.draft !== initialData?.draft; + const hasUnpublishedDraft = obj.code !== obj.draft; + const hasUnpublishedEnvs = !isEqual(obj.settings.variables ?? {}, initialData?.settings.variables ?? {}); + const hasUnpublishedFuncs = !isEqual(obj.settings.functions ?? {}, initialData?.settings.functions ?? {}); + const hasUnpublishedSettings = !isEqual( + omit(obj.settings ?? {}, "variables", "functions"), + omit(initialData?.settings ?? {}, "variables", "functions") + ); + const hasUnpublishedChanges = hasUnpublishedDraft || !isEqual(obj, initialData); + const reloadStore = useStoreReload(); + + function handleTabChange(key: string) { + setActiveSecondaryTab(key); + if (key === "logs") { + setUnreadLogs(0); + setUnreadErrorLogs(0); + } + } + + useEffect(() => { + if (initialData) { + dispatch({ type: "replace", value: initialData }); + setEditorShown(!initialData.cli); + } + }, [initialData, isLoading]); + + const publish = useCallback(async () => { + if (!obj.settings?.destinationId) { + modal.confirm({ + title: "Destination not selected", + content: ( + <> + Please select a destination where the profiles will be stored. +
+ You can do this in the Settings + + ), + okText: "Go to Settings", + okType: "primary", + cancelText: "Cancel", + onOk: () => { + setActivePrimaryTab("settings"); + }, + }); + } else { + setSaving(true); + rpc(`/api/${workspace.id}/config/profile-builder`, { + body: { + profileBuilder: { + id: obj.id, + name: obj.name, + workspaceId: workspace.id, + version: enabled ? (obj.version ?? 0) + 1 : obj.version ?? 0, + destinationId: obj.settings.destinationId, + intermediateStorageCredentials: obj.settings.storage || {}, + connectionOptions: { + tableName: obj.settings.tableName, + profileWindow: obj.settings.profileWindow, + variables: obj.settings.variables, + functions: obj.settings.functions, + }, + createdAt: obj.createdAt || new Date(), + updatedAt: new Date(), + }, + code: obj.draft, + }, + }) + .then(() => { + setPbRefreshDate(new Date()); + }) + .then(async () => reloadStore()) + .catch(e => {}) + .finally(() => setSaving(false)); + } + }, [ + obj.settings.destinationId, + obj.settings.storage, + obj.settings.tableName, + obj.settings.profileWindow, + obj.settings.variables, + obj.settings.functions, + obj.id, + obj.name, + obj.version, + obj.createdAt, + obj.draft, + modal, + workspace.id, + enabled, + reloadStore, + ]); + const save = useCallback(async () => { + setSaving(true); + getConfigApi(workspace.id, "function") + .update(obj.functionId!, { + draft: obj.draft, + type: "function", + }) + .then(() => { + setPbRefreshDate(new Date()); + }) + .catch(e => {}) + .finally(() => setSaving(false)); + }, [obj, workspace.id]); + + const rebuild = useCallback(async () => { + get(`/api/${workspace.id}/profile-builder/state?profileBuilderId=${obj.id}`, { + method: "POST", + }) + .then(() => { + feedbackSuccess("Full rebuild is triggered"); + }) + .catch(e => { + feedbackError("Failed to trigger full rebuild of profile builder", { error: e }); + }); + }, [obj.id, workspace.id]); + + const rollback = useCallback(async () => { + setSaving(true); + getConfigApi(workspace.id, "function") + .update(obj.functionId!, { + draft: obj.code, + type: "function", + }) + .then(() => { + setPbRefreshDate(new Date()); + }) + .catch(e => {}) + .finally(() => setSaving(false)); + }, [obj, workspace.id]); + + const runFunction = useCallback(async () => { + setRunning(true); + let body = {}; + try { + body = { + id: obj.id, + name: obj.name, + version: obj.version, + settings: obj.settings, + code: obj.draft, + events: JSON.parse(testData), + store: {}, + userAgent: navigator.userAgent, + }; + } catch (e) { + feedbackError("Invalid JSON", { error: e }); + setRunning(false); + return; + } + try { + const res = await rpc(`/api/${workspace.id}/profile-builder/run`, { + method: "POST", + body, + }); + if (res.error) { + setResult(res.error); + setResultType("error"); + setLogs([ + ...res.logs, + { + level: "error", + type: "log", + message: `${res.error.name}: ${res.error.message}`, + timestamp: new Date(), + }, + ]); + } else { + setResult(res.result); + setResultType(res.dropped ? "drop" : "ok"); + if (res.dropped) { + setLogs([ + ...res.logs, + { + level: "info", + type: "log", + message: `Further processing will be SKIPPED. Function returned: ${JSON.stringify(res.result)}`, + timestamp: new Date(), + }, + ]); + } else { + setLogs(res.logs); + } + } + + if (activeSecondaryTab !== "logs") { + setUnreadLogs(res.logs.length); + setUnreadErrorLogs(res.logs.filter(l => l.level === "error").length); + } + } catch (e: any) { + const errorText = "Error while calling Function API. Please contact support."; + setLogs([ + { + level: "error", + type: "log", + message: errorText, + timestamp: new Date(), + }, + ]); + setResult({ + name: "Error", + message: errorText, + }); + if (activeSecondaryTab !== "logs") { + setUnreadLogs(1); + setUnreadErrorLogs(1); + } + setResultType("error"); + } finally { + if (activeSecondaryTab !== "logs" && activeSecondaryTab !== "result") { + setActiveSecondaryTab("result"); + } + setRunning(false); + } + }, [obj.id, obj.name, obj.version, obj.settings, obj.draft, testData, workspace.id, activeSecondaryTab]); + + const items: ButtonProps[] = []; + if (activePrimaryTab === "code") { + items.push({ + icon: , + label: "Rollback Draft", + requiredPermission: "editEntities", + onClick: rollback, + disabled: isLoading || !!globalError || !hasUnpublishedDraft, + loading: isLoading, + collapsed: true, + }); + } + if (obj?.version) { + items.push({ + icon: , + label: "Full Rebuild", + requiredPermission: "editEntities", + onClick: rebuild, + disabled: isLoading || !!globalError, + loading: isLoading, + collapsed: true, + }); + } + + return ( + +
+ + +
Configuration loading, please wait...
+
+ + + +
+ + + + {enabled && activePrimaryTab === "code" && ( + <> +
Draft saved: {dayjs(obj.draftUpdatedAt).fromNow()}
+ + + ) : ( + + ) + } + > + Save Draft + + + + )} + + + {enabled ? ( + }>Publish + ) : ( + }>Save + )} + + {enabled && ( + + )} +
+ } + type={"card"} + activeKey={activePrimaryTab} + size={"small"} + tabBarStyle={{ marginBottom: 0 }} + items={[ + { + disabled: isLoading || !!globalError, + key: "code", + style: { height: "100%" }, + label: ( + }> +
+ Profile Function + {enabled && hasUnpublishedDraft && } + {!enabled && codeChanged && } +
+
+ ), + children: ( + + {!isEqual(obj, defaultProfileBuilderData) && + (!editorShown ? ( +
+
+ The function is compiled and deployed with{" "} + + jitsu-cli + {" "} + it is recommended to use the CLI to edit the function code. +
+ However, you can still run it with different events and see the results below. +
+ +
+ ) : ( + dispatch({ type: "draft", value: c })} + /> + ))} +
+ ), + }, + { + disabled: isLoading || !!globalError, + style: { height: "100%" }, + key: "transform", + label: ( + }> +
+ Transformation + {hasUnpublishedFuncs && } +
{" "} +
+ ), + children: ( + +
+ { + const enabledF = enabledFunctions.map(f => ({ functionId: `udf.${f.id}` })); + dispatch({ type: "settings", value: { ...obj.settings, functions: enabledF } }); + }} + /> +
+
+ ), + }, + { + disabled: isLoading || !!globalError, + key: "variables", + style: { height: "100%" }, + label: ( + }> +
+ Environment Variables + {hasUnpublishedEnvs && } +
+
+ ), + children: ( + +
+ dispatch({ type: "settings", value: { ...obj.settings, variables: c } })} + /> +
+
+ ), + }, + { + disabled: isLoading || !!globalError, + style: { height: "100%" }, + key: "settings", + label: ( + }> +
+ Settings + {!obj.settings.destinationId ? ( + + ) : hasUnpublishedSettings ? ( + + ) : ( + <> + )} +
{" "} +
+ ), + children: ( + + + + ), + }, + { + disabled: isLoading || !!globalError || !enabled || obj.status === "incomplete", + style: { height: "100%" }, + key: "build", + label: }>Build Progress, + children: ( + + setStateRefreshDate(new Date())} + /> + + ), + }, + ]} + /> + + + + <> + {obj.status !== "incomplete" && enabled && activeSecondaryTab === "test" && ( + { + setUserIdDialogOpen(false); + }} + /> + } + title="Enter UserId" + open={userIdDialogOpen} + onOpenChange={(newOpen: boolean) => { + setUserIdDialogOpen(newOpen); + }} + trigger="click" + > + + + )} + + + ) : ( + + ) + } + > + Run + + + +
+ } + type={"card"} + defaultActiveKey="1" + size={"small"} + tabBarStyle={{ marginBottom: 0 }} + activeKey={activeSecondaryTab} + items={[ + { + style: { height: "100%" }, + disabled: isLoading || !!globalError, + key: "test", + label: }>Test Data, + children: ( + + + + ), + }, + { + style: { height: "100%" }, + key: "result", + disabled: isLoading || !!globalError, + label: }>Last Run Result, + children: ( + + + + ), + }, + { + style: { height: "100%" }, + key: "logs", + disabled: isLoading || !!globalError, + label: ( + + }>Logs + + ), + children: ( + + + + ), + }, + ]} + /> + + +
+ + ); +} diff --git a/webapps/console/components/ProfileBuilderPage/example.ts b/webapps/console/components/ProfileBuilderPage/example.ts new file mode 100644 index 000000000..723e3eb08 --- /dev/null +++ b/webapps/console/components/ProfileBuilderPage/example.ts @@ -0,0 +1,63 @@ +export const testDataExample = JSON.stringify( + [ + { + _profile_id: "clewyinv10000mo0f1o1a8sg7", + type: "page", + properties: { + title: "Jitsu", + url: "https://use.jitsu.com/test", + path: "/test", + hash: "", + search: "", + width: 2304, + height: 1186, + name: "Workspace Page", + }, + userId: "clewyinv10000mo0f1o1a8sg7", + anonymousId: "65d98584-5fea-4a2d-8988-b02fe4291f25", + timestamp: "2024-10-22T15:39:09.456Z", + sentAt: "2024-10-22T15:39:09.456Z", + messageId: "1t5bykqr4sj1r59fbvstdw", + context: {}, + requestIp: "127.0.0.1", + receivedAt: "2024-10-22T15:39:09.458Z", + }, + { + _profile_id: "clewyinv10000mo0f1o1a8sg7", + type: "track", + event: "workspace_access", + properties: { + workspaceId: "cl9y5kgth0002ccfn3vtqz64g", + workspaceName: "Test Workspace", + workspaceSlug: "test", + }, + userId: "clewyinv10000mo0f1o1a8sg7", + anonymousId: "7efbac34-c4c0-4e84-9458-e58ec184d337", + timestamp: "2024-10-22T15:41:24.351Z", + sentAt: "2024-10-22T15:41:24.351Z", + messageId: "1hllz5alamc17d4x39cyiu", + context: {}, + requestIp: "127.0.0.1", + receivedAt: "2024-10-22T15:41:24.353Z", + }, + { + _profile_id: "clewyinv10000mo0f1o1a8sg7", + type: "identify", + userId: "clewyinv10000mo0f1o1a8sg7", + traits: { + email: "test@example.com", + name: "John Doe", + externalId: "1234", + }, + anonymousId: "7efbac34-c4c0-4e84-9458-e58ec184d337", + timestamp: "2024-10-22T15:41:24.351Z", + sentAt: "2024-10-22T15:41:24.351Z", + messageId: "mj2vr0l9wt1849lq6of5y", + context: {}, + requestIp: "127.0.0.1", + receivedAt: "2024-10-22T15:41:24.353Z", + }, + ], + null, + 2 +); diff --git a/webapps/console/components/ProvisionDatabaseButton/ProvisionDatabaseButton.tsx b/webapps/console/components/ProvisionDatabaseButton/ProvisionDatabaseButton.tsx index 520d834dc..4f6080c32 100644 --- a/webapps/console/components/ProvisionDatabaseButton/ProvisionDatabaseButton.tsx +++ b/webapps/console/components/ProvisionDatabaseButton/ProvisionDatabaseButton.tsx @@ -8,7 +8,7 @@ import { Database, Loader2, Plus } from "lucide-react"; import { JitsuButton } from "../JitsuButton/JitsuButton"; export function ProvisionDatabaseButton(props) { - const { loader = undefined } = props; + const { loader = undefined, createdCallback = undefined } = props; const workspace = useWorkspace(); const url = `/api/${workspace.id}/ee/provision-db`; const { data, isLoading, error } = useApi(url); @@ -19,7 +19,11 @@ export function ProvisionDatabaseButton(props) { try { await rpc(url, { method: "POST", query: { workspaceId: workspace.id } }); feedbackSuccess("Database created"); - router.reload(); + if (createdCallback) { + createdCallback(); + } else { + router.reload(); + } } catch (e) { feedbackError("Error creating database", { error: e }); } finally { @@ -54,6 +58,7 @@ export function ProvisionDatabaseButton(props) { icon={dbCreating ? : } type="default" onClick={createDbHandler} + requiredPermission={"editEntities"} disabled={dbCreating} > Create Database diff --git a/webapps/console/components/QueryResponse/QueryResponse.tsx b/webapps/console/components/QueryResponse/QueryResponse.tsx index ebc66ac71..7ce10848a 100644 --- a/webapps/console/components/QueryResponse/QueryResponse.tsx +++ b/webapps/console/components/QueryResponse/QueryResponse.tsx @@ -24,7 +24,7 @@ export function QueryResponse(props: QueryResponseProps) { }, [result.data, props.prepare, result.isLoading, props]); if (result.isLoading) { - return ; + return ; } if (result.error) { if (props.errorRender) { diff --git a/webapps/console/components/Redirect/Redirect.tsx b/webapps/console/components/Redirect/Redirect.tsx new file mode 100644 index 000000000..838b18ccb --- /dev/null +++ b/webapps/console/components/Redirect/Redirect.tsx @@ -0,0 +1,23 @@ +import { useEffect } from "react"; +import { useRouter } from "next/router"; +import { LoadingAnimation } from "../GlobalLoader/GlobalLoader"; +import { captureReturnUrl, addReturnUrlToSignin } from "../../lib/auth-redirect"; + +export const Redirect: React.FC<{ href: string; title?: string }> = ({ title, href }) => { + const router = useRouter(); + useEffect(() => { + router.push(href); + }, [router, href]); + return ; +}; + +export const RedirectToSignIn: React.FC<{ title?: string }> = ({ title }) => { + const router = useRouter(); + useEffect(() => { + const returnUrl = captureReturnUrl(router); + const signinUrlWithReturn = addReturnUrlToSignin("/signin", returnUrl); + router.push(signinUrlWithReturn); + }, [router]); + + return ; +}; diff --git a/webapps/console/components/SQLViewer/SQLViewer.tsx b/webapps/console/components/SQLViewer/SQLViewer.tsx index f901a3ab1..d272c2156 100644 --- a/webapps/console/components/SQLViewer/SQLViewer.tsx +++ b/webapps/console/components/SQLViewer/SQLViewer.tsx @@ -1,12 +1,11 @@ import { Alert, Button, Empty, Layout, Table, Tooltip, Tree } from "antd"; -import { rpc } from "juava"; +import { rpc, trimMiddle } from "juava"; import React, { useCallback, useEffect, useMemo, useState } from "react"; import { useWorkspace } from "../../lib/context"; import { SQLResultType } from "../../pages/api/[workspaceId]/sql/query"; import { SQLSchemaType } from "../../pages/api/[workspaceId]/sql/schema"; import { BorderOutlined, LeftOutlined, RightOutlined, TableOutlined } from "@ant-design/icons"; import { LoadingAnimation } from "../GlobalLoader/GlobalLoader"; -import { trimMiddle } from "../../lib/shared/strings"; import { LabelEllipsis } from "../LabelEllipsis/LabelEllipsis"; import { SortOrder } from "antd/es/table/interface"; import { ExpandableButton } from "../ExpandableButton/ExpandableButton"; @@ -70,7 +69,7 @@ const SchemaTree: React.FC = ({ onColumnSelect, onTableSelect,

No tables

- The tables are created in few mins after you send your fist events to Jitsu, after the first batch of events + The tables are created in few mins after you send your first events to Jitsu, after the first batch of events is processed
@@ -90,6 +89,7 @@ const SchemaTree: React.FC = ({ onColumnSelect, onTableSelect, isLeaf: true, checkable: false, title: {trimMiddle(column.name, 28)}, + value: column.name, key: `${name}_${column.name}`, }; }), @@ -104,7 +104,7 @@ const SchemaTree: React.FC = ({ onColumnSelect, onTableSelect, { if (e.node.isLeaf) { - onColumnSelect?.(e.node.key); + onColumnSelect?.(e.node["value"]); } }} selectable={false} @@ -151,8 +151,12 @@ const SQLViewer: React.FC = ({ destinationId }) => { query: { destinationId }, body: { query: sql, offset: offset, limit: limit }, }); - setResults(res); - setLastQuery({ sql, offset: res.offset, limit: res.limit }); + if (res.error) { + setError(res.error); + } else { + setResults(res); + setLastQuery({ sql, offset: res.offset, limit: res.limit }); + } } catch (e) { setError(e); } finally { diff --git a/webapps/console/components/Selectors/DestinationSelector.tsx b/webapps/console/components/Selectors/DestinationSelector.tsx new file mode 100644 index 000000000..3bdeaf18a --- /dev/null +++ b/webapps/console/components/Selectors/DestinationSelector.tsx @@ -0,0 +1,54 @@ +import { getCoreDestinationType } from "../../lib/schema/destinations"; +import { DestinationConfig } from "../../lib/schema"; +import { Disable } from "../Disable/Disable"; +import { Select } from "antd"; +import { DestinationTitle } from "../../pages/[workspaceId]/destinations"; +import { WLink } from "../Workspace/WLink"; +import { FaExternalLinkAlt } from "react-icons/fa"; +import React from "react"; + +export type SelectorProps = { + enabled: boolean; + disabledReason?: string; + selected?: string; + items: T[]; + onSelect: (value: string) => void; + showLink?: boolean; +}; + +export function DestinationSelector(props: SelectorProps) { + return ( +
+ + + + {!props.enabled && props.showLink && ( +
+ + + +
+ )} +
+ ); +} diff --git a/webapps/console/components/Selectors/SourceSelector.tsx b/webapps/console/components/Selectors/SourceSelector.tsx new file mode 100644 index 000000000..ad53a3f74 --- /dev/null +++ b/webapps/console/components/Selectors/SourceSelector.tsx @@ -0,0 +1,31 @@ +import { StreamConfig } from "../../lib/schema"; +import { Disable } from "../Disable/Disable"; +import { Select } from "antd"; +import { StreamTitle } from "../../pages/[workspaceId]/streams"; +import { WLink } from "../Workspace/WLink"; +import { FaExternalLinkAlt } from "react-icons/fa"; +import React from "react"; +import { SelectorProps } from "./DestinationSelector"; + +export function SourceSelector(props: SelectorProps) { + return ( +
+ + + + {!props.enabled && props.showLink && ( +
+ + + +
+ )} +
+ ); +} diff --git a/webapps/console/components/ServiceEditor/ServiceEditor.tsx b/webapps/console/components/ServiceEditor/ServiceEditor.tsx index 1c4c77b84..34923ddc1 100644 --- a/webapps/console/components/ServiceEditor/ServiceEditor.tsx +++ b/webapps/console/components/ServiceEditor/ServiceEditor.tsx @@ -1,46 +1,72 @@ import { EditorComponentProps } from "../ConfigObjectEditor/ConfigEditor"; -import React, { useCallback, useEffect, useState } from "react"; -import { getLog, rpc } from "juava"; +import React, { useCallback, useEffect, useMemo, useState } from "react"; +import { getErrorMessage, getLog, rpc } from "juava"; import { EditorTitle } from "../ConfigObjectEditor/EditorTitle"; import { EditorBase } from "../ConfigObjectEditor/EditorBase"; import { LoadingAnimation } from "../GlobalLoader/GlobalLoader"; import { EditorField } from "../ConfigObjectEditor/EditorField"; -import { TextEditor } from "../ConnectionEditorPage/ConnectionEditorPage"; import { ServiceConfig } from "../../lib/schema"; -import { Button, Select } from "antd"; -import { SnippedEditor } from "../CodeEditor/SnippedEditor"; +import { Select } from "antd"; import { EditorButtons } from "../ConfigObjectEditor/EditorButtons"; import { getConfigApi } from "../../lib/useApi"; import { feedbackError } from "../../lib/ui"; -import { useWorkspace } from "../../lib/context"; +import { useAppConfig, useWorkspace, useWorkspaceRole } from "../../lib/context"; import { useRouter } from "next/router"; - -const log = getLog("ServiceEditor"); +import { JitsuButton } from "../JitsuButton/JitsuButton"; +import Nango from "@nangohq/frontend"; +import { oauthDecorators } from "../../lib/server/oauth/services"; +import { CheckCircleTwoTone, InfoCircleTwoTone } from "@ant-design/icons"; +import set from "lodash/set"; +import get from "lodash/get"; +import Ajv from "ajv"; +import unset from "lodash/unset"; +import { SchemaForm } from "../ConfigObjectEditor/SchemaForm"; +import { TextEditor } from "../ConfigObjectEditor/Editors"; +import { useAntdModal } from "../../lib/modal"; +import { useStoreReload } from "../../lib/store"; type ServiceEditorProps = {} & EditorComponentProps; -const VersionSelector: React.FC<{ versions: string[]; onChange: (v: string) => void; value: string }> = ({ - versions, - onChange, - value, -}) => { +const VersionSelector: React.FC<{ + versions: string[]; + onChange: (v: string) => void; + value: string; + disabled?: boolean; +}> = ({ versions, onChange, value, disabled }) => { const options = versions.map(v => ({ label: v, value: v })); - return ; }; export const ServiceEditor: React.FC = props => { const { object, meta, createNew, onCancel, onDelete, onTest, isNew, noun, loadMeta } = props; + const appConfig = useAppConfig(); const workspace = useWorkspace(); - const { push } = useRouter(); + const role = useWorkspaceRole(); + const canEdit = role.editEntities; + const { push, query } = useRouter(); const [obj, setObj] = useState>({ ...props.object, + version: query.version ? query.version.toString() : props.object?.version, }); - const [formState, setFormState] = useState(undefined); - const isTouched = formState !== undefined || !!createNew; + const [credentials, setCredentials] = useState(obj.credentials || {}); + const [isTouched, setIsTouched] = useState(false); + const [testResult, setTestResult] = useState(undefined); + const [showErrors, setShowErrors] = useState(false); const [loading, setLoading] = useState(false); - const [credUserProvided, setCredUserProvided] = useState(!!obj.credentials && obj.credentials !== "{}"); - const [loadingSpecs, setLoadingSpecs] = useState(false); + const [testing, setTesting] = useState(false); + const [nangoLoading, setNangoLoading] = useState(false); + const [nangoError, setNangoError] = useState(undefined); + const [loadingSpecs, setLoadingSpecs] = useState(true); const [specs, setSpecs] = useState(undefined); + const oauthConnector = appConfig.nango ? oauthDecorators.find(d => d.packageId === obj.package) : undefined; + const [manualAuth, setManualAuth] = useState(typeof oauthConnector === "undefined"); + const ajv = useMemo(() => { + const ajv = new Ajv({ allErrors: true, strictSchema: false, useDefaults: true, allowUnionTypes: true }); + ///ajv.addMetaSchema(draft7MetaSchema); + return ajv; + }, []); + const modal = useAntdModal(); + const reloadStore = useStoreReload(); const change = useCallback( (key: string, value: any) => { @@ -48,71 +74,154 @@ export const ServiceEditor: React.FC = props => { ...obj, [key]: value, }); + setIsTouched(true); + setTestResult(undefined); }, [obj] ); useEffect(() => { - if (credUserProvided || specs) { - console.log("No need to load specs. Credentials are already filled."); + if (specs) { return; } (async () => { - console.log("Loading specs"); setLoadingSpecs(true); try { - const firstRes = await rpc(`/api/${workspace.id}/sources/spec?package=${obj.package}&version=${obj.version}`); - if (firstRes.error) { - feedbackError(`Cannot load specs for ${obj.package}:${obj.version} error: ${firstRes.error}`); - } else if (firstRes.ok) { - console.log("Loaded cached specs:", JSON.stringify(firstRes, null, 2)); + const firstRes = await rpc( + `/api/${workspace.id}/sources/spec?package=${obj.package}&version=${obj.version}&force=true` + ); + if (firstRes.ok) { + getLog().atDebug().log("Loaded cached specs", firstRes); setSpecs(firstRes.specs); - change("credentials", JSON.stringify(firstRes.fakeJson, null, 2)); + } else if (firstRes.error) { + feedbackError(`Cannot load specs for ${obj.package}:${obj.version}`, { + error: { message: firstRes.error }, + }); + return; } else { for (let i = 0; i < 60; i++) { await new Promise(resolve => setTimeout(resolve, 2000)); - const resp = await rpc( - `/api/${workspace.id}/sources/spec?package=${obj.package}&version=${obj.version}&after=${firstRes.startedAt}` - ); + const resp = await rpc(`/api/${workspace.id}/sources/spec?package=${obj.package}&version=${obj.version}`); if (!resp.pending) { if (resp.error) { - feedbackError(`Cannot load specs for ${obj.package}:${obj.version} error: ${resp.error}`); + feedbackError(`Cannot load specs for ${obj.package}:${obj.version}`, { + error: { message: resp.error }, + }); return; } else { - console.log("Loaded specs:", JSON.stringify(resp, null, 2)); setSpecs(resp.specs); - change("credentials", JSON.stringify(resp.fakeJson, null, 2)); return; } } } - feedbackError(`Cannot load specs for ${obj.package}:${obj.version} error: Timeout`); + feedbackError(`Cannot load specs for ${obj.package}:${obj.version}`, { + error: { message: "Timeout" }, + }); } } catch (error) { - feedbackError(`Cannot load specs for ${obj.package}:${obj.version} error: ${error}`); + feedbackError(`Cannot load specs for ${obj.package}:${obj.version}`, { error }); } finally { setLoadingSpecs(false); } })(); - }, [workspace.id, credUserProvided, obj.package, obj.version, change, specs]); + }, [workspace.id, obj.package, obj.version, change, specs]); + + const validate = useCallback(() => { + const errors: string[] = []; + if (!specs || !specs.connectionSpecification) { + errors.push("Source specification was not loaded"); + return; + } + // ajv doesn't expect schema identifiers to use https:// prefix. But such identifiers started to appear in connectors specs + if (specs.connectionSpecification.$schema) { + specs.connectionSpecification.$schema = specs.connectionSpecification.$schema.replace("https://", "http://"); + } + const validate = ajv.compile(specs.connectionSpecification); + const valid = validate(credentials); + if (!obj.name) { + errors.push("[required] Object must have required property 'name'"); + } + if (!valid || errors.length > 0) { + errors.push( + ...(validate.errors ?? []).reduce((acc: string[], err) => { + acc.push(`${err.instancePath || "Object"} ${err.message}`); + return acc; + }, []) + ); + setShowErrors(true); + modal.error({ + title: "There are errors in the configuration", + style: { width: "600px" }, + content: ( +
+ Please fix following errors. Fields with errors are marked with red{" "} +
    + {errors.map((e: any, i) => { + return ( +
  • + {e} +
  • + ); + })} +
+
+ ), + }); + return false; + } + return true; + }, [ajv, credentials, modal, obj.name, specs]); const save = useCallback(async () => { setLoading(true); - try { + const _save = async (testConnectionError?: string) => { + obj.credentials = credentials; + obj.testConnectionError = testConnectionError; + let res: any = undefined; if (props.isNew) { - await getConfigApi(workspace.id, "service").create(obj); + res = await getConfigApi(workspace.id, "service").create(obj); } else if (obj.id) { - await getConfigApi(workspace.id, "service").update(obj.id, obj); + res = await getConfigApi(workspace.id, "service").update(obj.id, obj); } else { feedbackError(`Can't save service without id`); } - push(`/${workspace.id}/services`); + if (res?.success || res?.id) { + // trigger loading catalog in background (k8s) + await rpc(`/api/${workspace.id}/sources/discover?serviceId=${res?.id || obj.id}`); + } + await reloadStore(); + push(`/${workspace.slugOrId}/services`); + }; + try { + if (!validate()) { + return; + } + setTesting(true); + const testRes = testResult || (await onTest!({ ...obj, credentials: credentials })); + setTesting(false); + if (!testRes.ok) { + modal.confirm({ + title: "Connection test failed", + content: testRes.error, + okText: "Save anyway", + okType: "danger", + cancelText: "Cancel", + onOk: () => { + _save(testRes.error); + }, + }); + return; + } else { + delete obj.testConnectionError; + } + await _save(); } catch (error) { feedbackError(`Can't save service`, { error }); } finally { + setTesting(false); setLoading(false); } - }, [props.isNew, obj, workspace.id, push]); + }, [obj, credentials, workspace.id, props.isNew, push, validate, testResult, onTest, modal]); if (meta === undefined) { return ; @@ -122,65 +231,155 @@ export const ServiceEditor: React.FC = props => { : isNew ? `Create new ${noun}` : `Edit ${noun}`; + const subtitleComponent = props.subtitle && props.subtitle(object, isNew, meta); + return ( - onCancel(isTouched)} /> - - + onCancel(isTouched)} /> + {oauthConnector && ( +
+
+ { + const nango = new Nango({ + publicKey: appConfig.nango!.publicKey, + host: appConfig.nango!.host, + }); + setNangoLoading(true); + nango + .auth(oauthConnector.nangoIntegrationId ?? "", `sync-source.${obj?.id}`) + .then(result => { + const strippedSchema = oauthConnector.stripSchema(credentials || {}); + setObj({ ...obj, credentials: strippedSchema, authorized: true }); + setCredentials(strippedSchema); + setNangoError(undefined); + }) + .catch(err => { + setNangoError(getErrorMessage(err)); + getLog().atError().log("Failed to add oauth connection", err); + change.bind(null, "authorized")(false); + }) + .finally(() => setNangoLoading(false)); + }} + icon={ + {obj?.package} + } + > + {obj?.authorized ? "Re-Sign In" : "Authorize"} + +
+
+ {nangoError ? ( + OAuth2 error: {nangoError} + ) : obj?.authorized ? ( + <> + + Authorized + + ) : ( + <> + + Click "Authorize" to open OAuth2.0 authorization popup + + )} +
+
+ setManualAuth(!manualAuth)}> + {manualAuth ? "Hide authorization settings" : "Manually setup authorization"} + +
+
+ )} + + { change.bind(null, "version")(v); + setLoadingSpecs(true); setSpecs(undefined); }} versions={meta.versions} /> - - {loadingSpecs ? ( - - ) : ( -
-
- -
- { - change.bind(null, "credentials")(v); - setCredUserProvided(true); + {loadingSpecs ? ( + + ) : ( + !!specs && ( + <> + { + const newCred = { ...credentials }; + const lastPathEl = n[n.length - 1]; + if (v === undefined && lastPathEl.match(/^\d+$/)) { + //remove element from array + get(newCred, n.slice(0, n.length - 1)).splice(parseInt(lastPathEl), 1); + } else if (v === undefined || v === null || v === "") { + //remove element from object + unset(newCred, n); + } else { + set(newCred, n, v); + } + setIsTouched(true); + setTestResult(undefined); + setCredentials(newCred); }} - languages={["json"]} - height={206} + obj={credentials} /> -
- )} -
+ + ) + )} onCancel(isTouched)} onSave={save} - onTest={() => onTest!(obj)} + onTest={async () => { + if (!validate()) { + return Promise.resolve({ ok: false, error: "Config validation failed" }); + } + const testResult = await onTest!({ ...obj, credentials: credentials }); + setTestResult(testResult); + return testResult; + }} />
); diff --git a/webapps/console/components/ServicesCatalog/ServicesCatalog.tsx b/webapps/console/components/ServicesCatalog/ServicesCatalog.tsx index aba05a6b8..2967825ab 100644 --- a/webapps/console/components/ServicesCatalog/ServicesCatalog.tsx +++ b/webapps/console/components/ServicesCatalog/ServicesCatalog.tsx @@ -1,24 +1,23 @@ import styles from "./ServicesCatalog.module.css"; -import { FaCloud, FaDatabase } from "react-icons/fa"; +import { FaDatabase, FaDocker } from "react-icons/fa"; import { useApi } from "../../lib/useApi"; import { SourceType } from "../../pages/api/sources"; -import { capitalize } from "lodash"; +import capitalize from "lodash/capitalize"; import { LoadingAnimation } from "../GlobalLoader/GlobalLoader"; import React from "react"; import { ErrorCard } from "../GlobalError/GlobalError"; import { Input } from "antd"; +import { useAppConfig, useWorkspace } from "../../lib/context"; +import { useConfigObjectList } from "../../lib/store"; function groupByType(sources: SourceType[]): Record { const groups: Record = {}; const otherGroup = "other"; - const sortOrder = ["Datawarehouse", "Product Analytics", "CRM", "Block Storage"]; + const sortOrder = ["api", "database", "file", "custom image"]; sources.forEach(s => { - if (s.meta.license !== "MIT") { - return; - } - if (s.packageId.endsWith("strict-encrypt")) { + if (s.packageId.endsWith("strict-encrypt") || s.packageId === "airbyte/source-file-secure") { return; } const groupName = s.meta.connectorSubtype || otherGroup; @@ -43,63 +42,123 @@ function groupByType(sources: SourceType[]): Record { .reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {}); } -export function getDestinationIcon(source: SourceType) { +export function getServiceIcon(source: SourceType, icons: Record = {}): React.ReactNode { const connectorSubtype = source.meta.connectorSubtype; - return source.logo ? ( - {source.meta.name} + + const logoSvg = (source.logoSvg || icons[source.packageId]) as string; + return logoSvg ? ( + {source.meta.name} ) : connectorSubtype === "database" ? ( - + ) : ( - + ); } -export const ServicesCatalog: React.FC<{ onClick: (packageType, packageId: string) => void }> = ({ onClick }) => { - const { data, isLoading, error } = useApi<{ sources: SourceType[] }>(`/api/sources`); +export const ServicesCatalog: React.FC<{ onClick: (packageType, packageId: string, version?: string) => void }> = ({ + onClick, +}) => { + const { data, isLoading, error } = useApi<{ sources: SourceType[] }>(`/api/sources?mode=meta`); + const customImages = useConfigObjectList("custom-image"); + const sourcesIconsLoader = useApi<{ sources: SourceType[] }>(`/api/sources?mode=icons-only`); + const workspace = useWorkspace(); const [filter, setFilter] = React.useState(""); + const appconfig = useAppConfig(); + const sourcesIcons: Record = sourcesIconsLoader.data + ? sourcesIconsLoader.data.sources.reduce( + (acc, item) => ({ + ...acc, + [item.packageId]: item.logoSvg, + }), + {} + ) + : {}; if (isLoading) { return ; } else if (error) { return ; } - const groups = groupByType(data.sources); + const sources = [ + ...data.sources, + ...customImages.map(c => ({ + id: c.package, + packageId: c.package, + packageType: "airbyte", + meta: { name: c.name, connectorSubtype: "custom image", dockerImageTag: c.version }, + })), + ] as SourceType[]; + + const groups = groupByType(sources); return ( -
+
{ setFilter(e.target.value); }} className="w-full border border-textDisabled rounded-lg px-4 py-4" />
- {Object.entries(groups).map(([group, sources]) => ( -
-
{group === "api" ? "API" : capitalize(group)}
-
- {sources.map(source => { - if (!source.meta.name.toLowerCase().includes(filter.toLowerCase())) { - return null; - } - return ( -
onClick(source.packageType, source.packageId)} - > -
{getDestinationIcon(source)}
-
-
{source.meta.name}
-
-
- ); - })} -
-
- ))} +
+ {Object.entries(groups).map(([group, sources]) => { + const filtered = sources + .filter(source => source.meta.name) + .filter(source => source.meta.name && source.meta.name.toLowerCase().includes(filter.toLowerCase())) + .filter( + source => + !appconfig.mitCompliant || + workspace.featuresEnabled.includes("ignore_sources_licenses") || + source.meta.license?.toLowerCase() === "mit" || + (source.meta.mitVersions && source.meta.mitVersions.length > 0) + ); + if (filtered.length === 0) { + return null; + } + return ( +
+
+ {group === "api" ? "API" : capitalize(group)} +
+
+ {filtered + .sort((a, b) => { + const res = (b.sortIndex || 0) - (a.sortIndex || 0); + return res === 0 ? a.meta.name.localeCompare(b.meta.name) : res; + }) + .map(source => { + return ( +
+ onClick( + source.packageType, + source.packageId, + source.meta?.connectorSubtype === "custom image" ? source.meta?.dockerImageTag : undefined + ) + } + > +
{getServiceIcon(source, sourcesIcons)}
+
+
+
{source.meta.name}
+
+
+ {source.packageId} + {source.meta?.connectorSubtype === "custom image" ? ":" + source.meta?.dockerImageTag : ""} +
+
+
+ ); + })} +
+
+ ); + })} +
); }; diff --git a/webapps/console/components/SignInOrUp/EmailFirstLogin.tsx b/webapps/console/components/SignInOrUp/EmailFirstLogin.tsx new file mode 100644 index 000000000..93e6cfec3 --- /dev/null +++ b/webapps/console/components/SignInOrUp/EmailFirstLogin.tsx @@ -0,0 +1,203 @@ +import React, { useState } from "react"; +import { Alert, Button, Input, Spin } from "antd"; +import { LockOutlined, MailOutlined } from "@ant-design/icons"; +import { rpc } from "juava"; +import { AuthType } from "./SignInOrUp"; + +type AuthMethod = { + type: AuthType; + oidcProviderId?: string; + oidcProviderName?: string; +}; + +interface EmailFirstLoginProps { + onPasswordLogin: (email: string, password: string, type: AuthType) => Promise; + onSSOLogin: (type: AuthType, providerId: string, loginHint?: string) => Promise; + signup?: boolean; // Optional prop to indicate if this is a signup flow +} + +export const EmailFirstLogin: React.FC = ({ onPasswordLogin, onSSOLogin, signup }) => { + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + const [authMethod, setAuthMethod] = useState(null); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + const [checkingEmail, setCheckingEmail] = useState(false); + + const checkAuthMethod = async () => { + if (!email || !email.includes("@")) { + setError("Please enter a valid email address"); + return; + } + + setCheckingEmail(true); + setError(null); + + try { + const result = await rpc("/api/auth/check-email", { + body: { email }, + }); + + const authMethod = result[0]; + setAuthMethod(authMethod); + // Auto-trigger social login if applicable + switch (authMethod.type) { + case "firebase-google": + case "firebase-github": + case "nextauth-github": + case "nextauth-oidc": + await onSSOLogin(authMethod.type, authMethod.type, email); + break; + case "dynamic-oidc": + await onSSOLogin(authMethod.type, authMethod.oidcProviderId, email); + } + } catch (err: any) { + setError("Failed to check authentication method. Please try again."); + console.error("Error checking auth method:", err); + } finally { + setCheckingEmail(false); + } + }; + + const handlePasswordLogin = async () => { + if (!email || !password) { + setError("Please enter both email and password"); + return; + } + + setLoading(true); + setError(null); + + try { + await onPasswordLogin(email, password, authMethod?.type!); + } catch (err: any) { + setError(err.message || "Login failed. Please check your credentials."); + } finally { + setLoading(false); + } + }; + + const renderAuthForm = () => { + if (!authMethod) { + return null; + } + + switch (authMethod.type) { + case "firebase-password": + case "nextauth-credentials": + return ( +
+
+ + Password +
+ setPassword(e.target.value)} + placeholder="Enter your password" + onPressEnter={handlePasswordLogin} + /> + + +
+ ); + + case "firebase-google": + return ( +
+ } /> +
+ ); + + case "firebase-github": + case "nextauth-github": + return ( +
+ } /> +
+ ); + case "nextauth-oidc": + return ( +
+ } /> +
+ ); + case "dynamic-oidc": + return ( +
+ } + /> +
+ ); + case "none": + return ( +
+ +
+ ); + default: + return null; + } + }; + + return ( +
+
+
+
+ + Email +
+ { + setEmail(e.target.value); + setAuthMethod(null); + setError(null); + }} + placeholder="Enter your email address" + onPressEnter={checkAuthMethod} + disabled={checkingEmail || loading} + /> +
Use this for SSO and password based logins
+
+ + {!authMethod && ( + + )} + + {error && ( + setError(null)} /> + )} + + {renderAuthForm()} +
+
+ ); +}; diff --git a/webapps/console/components/SignInOrUp/OAuthButtons.tsx b/webapps/console/components/SignInOrUp/OAuthButtons.tsx new file mode 100644 index 000000000..40134772e --- /dev/null +++ b/webapps/console/components/SignInOrUp/OAuthButtons.tsx @@ -0,0 +1,97 @@ +import React, { useState } from "react"; +import { Button } from "antd"; +import { GithubOutlined, GoogleOutlined, KeyOutlined } from "@ant-design/icons"; +import { getLog } from "juava"; +import { AuthType } from "./SignInOrUp"; + +const log = getLog("oauth-buttons"); + +interface OAuthButtonsProps { + providers?: Array<"firebase-google" | "firebase-github" | "nextauth-github" | "nextauth-oidc">; + prefix?: string; + onSSOLogin: (type: AuthType, providerId: string) => Promise; +} + +export const OAuthButtons: React.FC = ({ + providers = ["firebase-google", "firebase-github", "nextauth-github", "nextauth-oidc"], + prefix = "Sign in", + onSSOLogin, +}) => { + const [loading, setLoading] = useState(); + + const handleLogin = async (provider: AuthType) => { + try { + setLoading(provider); + await onSSOLogin(provider, provider); + } finally { + setLoading(undefined); + } + }; + + const renderButton = (provider: string) => { + switch (provider) { + case "firebase-google": + return ( + + ); + case "firebase-github": + return ( + + ); + case "nextauth-github": + return ( + + ); + case "nextauth-oidc": + return ( + + ); + default: + return null; + } + }; + + return ( +
+
+ {providers.map(renderButton)} +
+
+ ); +}; diff --git a/webapps/console/components/SignInOrUp/SignIn.tsx b/webapps/console/components/SignInOrUp/SignIn.tsx deleted file mode 100644 index bd2ef9337..000000000 --- a/webapps/console/components/SignInOrUp/SignIn.tsx +++ /dev/null @@ -1,274 +0,0 @@ -import { branding } from "../../lib/branding"; -import { Alert, Button, Collapse, Input } from "antd"; -import React, { PropsWithChildren, ReactNode, useState } from "react"; -import { GithubOutlined, GoogleOutlined } from "@ant-design/icons"; -import { useModalPrompt } from "../../lib/modal"; -import { getLog } from "juava"; -import Link from "next/link"; -import { useQueryStringCopy } from "./use-query-string-copy"; - -const theme = require("../../theme.config"); - -function JitsuLogo() { - return ( -
-
{branding.logo}
-
{branding.wordmark}
-
- ); -} - -function Header() { - return ( -
-
- -
-
- ); -} - -export const log = getLog("signin"); - -function handleFirebaseError(error: any): ReactNode { - const code = error?.code; - log - .atError() - .withCause(error) - .log(`Firebase authorization error: ${JSON.stringify(error, null, 2)}`); - if (code === "auth/account-exists-with-different-credential") { - const email = error?.customData?.email; - return ( - <> - The account - {email ? ( - <> - {" "} - for {email} - - ) : ( - "" - )}{" "} - exists, but different sign in method has been used to create this account. Please try to sign in with another - method. - - ); - } else if (code === "auth/popup-closed-by-user") { - return "Auth popup was closed by user. Please try again."; - } else if (code === "auth/invalid-email") { - return "Invalid email"; - } else if (code === "auth/wrong-password") { - return "Invalid password"; - } else if (code === "auth/user-not-found") { - return "User not found"; - } else if (code === "auth/too-many-requests") { - return "Too many signin attempts. Try later, or reset password now"; - } - - return (error?.message || "Unknown auth error").replace("Firebase: ", ""); -} - -export const SocialLogin: React.FC<{ onSocialLogin: (type: string) => Promise; prefix?: string }> = ({ - onSocialLogin, - ...props -}) => { - const [loading, setLoading] = useState<"google.com" | "github.com" | undefined>(); - const prefix = props.prefix || "Sign in"; - const [error, setError] = useState(); - return ( -
-
- - -
- {error && ( -
- setError(undefined)} - /> -
- )} -
- ); -}; - -const PasswordForm: React.FC> = ({ onPasswordLogin }) => { - const prompt = useModalPrompt(); - const [email, setEmail] = useState(undefined); - const [password, setPassword] = useState(undefined); - const [loading, setLoading] = useState(false); - const [error, setError] = useState(); - return ( -
-
Email
-
- setEmail(e.target.value)}> -
-
- Password - - - Reset - - -
-
- setPassword(e.target.value)} placeholder={"•".repeat(10)}> -
- {error && ( -
- setError(undefined)} - /> -
- )} - -
- ); -}; - -export const SigninLayout: React.FC> = ({ - title, - children, - footer, -}) => { - return ( -
-
-
-
{title}
-
{children}
-
- {footer &&
{footer}
} -
-
- ); -}; - -export type SigninProps = { - engine: "firebase" | "nextauth"; - variant: "signin" | "signup"; - enablePassword?: boolean; - onPasswordLogin: (email: string, password: string) => Promise; - onSocialLogin: (type: string) => Promise; -}; - -export const SignIn: React.FC = props => { - const { enablePassword, onSocialLogin } = props; - const [showPasswordLogin, setShowPasswordLogin] = useState(false); - const queryStringCopy = useQueryStringCopy(); - - return ( - - New to {branding.productName}?{" "} - - Create an account! - -
- } - title={ -
- -
- } - > - - {enablePassword && ( - <> - {/*
*/} - {/* */} - {/* Or continue with password*/} - {/* */} - {/*
*/} -
- - - - - -
- {/*{showPasswordLogin && }*/} - - )} - - ); -}; diff --git a/webapps/console/components/SignInOrUp/SignInOrUp.tsx b/webapps/console/components/SignInOrUp/SignInOrUp.tsx new file mode 100644 index 000000000..533f40cea --- /dev/null +++ b/webapps/console/components/SignInOrUp/SignInOrUp.tsx @@ -0,0 +1,270 @@ +import { branding } from "../../lib/branding"; +import React, { PropsWithChildren, ReactNode, useState } from "react"; +import Link from "next/link"; +import { useQueryStringCopy } from "./use-query-string-copy"; +import { useRouter } from "next/router"; +import { EmailFirstLogin } from "./EmailFirstLogin"; +import { OAuthButtons } from "./OAuthButtons"; +import { signIn } from "next-auth/react"; +import { useFirebaseSession } from "../../lib/firebase-client"; +import { useJitsu } from "@jitsu/jitsu-react"; +import { useAppConfig } from "../../lib/context"; +import { safeRedirect } from "../../lib/auth-redirect"; +import { Alert } from "antd"; + +export type AuthType = + | "firebase-password" + | "firebase-google" + | "firebase-github" + | "nextauth-github" + | "nextauth-credentials" + | "nextauth-oidc" + | "dynamic-oidc" + | "none"; + +function JitsuLogo() { + return ( +
+
{branding.logo}
+
{branding.wordmark}
+
+ ); +} + +//TODO: detect that callbackUrl leads to /accept and show appropriate message +export const SigninLayout: React.FC> = ({ + title, + children, + footer, +}) => { + const router = useRouter(); + return ( +
+
+
+
{title}
+ {router.query.invite && ( +
Login to Jitsu to accept the invitation, or create an account
+ )} +
{children}
+
+ {footer &&
{footer}
} +
+
+ ); +}; + +export type SigninProps = { + signup?: boolean; +}; + +export const SignInOrUp: React.FC = ({ signup }) => { + const queryStringCopy = useQueryStringCopy(); + const router = useRouter(); + const appConfig = useAppConfig(); + const firebaseSession = useFirebaseSession(); + const { analytics } = useJitsu(); + const [error, setError] = useState(null); + + const callbackUrl = (router.query.callbackUrl as string) || "/"; + + // Determine available auth methods + const hasFirebase = !!appConfig.auth?.firebasePublic; + const hasDynamicOidc = !!appConfig.auth?.dynamicOidc; + const showSignupLink = !signup && !appConfig.disableSignup; + + // Build the list of available OAuth providers + const ssoProviders: Array<"firebase-google" | "firebase-github" | "nextauth-github" | "nextauth-oidc"> = []; + if (hasFirebase) { + ssoProviders.push("firebase-google", "firebase-github"); + } else { + if (appConfig.auth?.nextauth?.github) { + ssoProviders.push("nextauth-github"); + } + if (appConfig.auth?.nextauth?.oidc) { + ssoProviders.push("nextauth-oidc"); + } + } + + const showEmailLogin = hasFirebase || hasDynamicOidc || (appConfig.auth?.nextauth?.credentials && !signup); + + const handlePasswordLogin = async (email: string, password: string, type: AuthType) => { + try { + if (type === "firebase-password") { + const success = await firebaseSession!.signIn(email, password); + if (!success) { + setError("Invalid email or password"); + return; + } + const user = await firebaseSession!.resolveUser().user; + if (!user) { + setError("Sign in failed"); + } + await analytics.track("login", { + traits: { ...user, type: "password", loginProvider: "firebase/email" }, + }); + safeRedirect(router, callbackUrl); + } else if (type === "nextauth-credentials") { + const result = await signIn("credentials", { + username: email, + password, + redirect: false, + callbackUrl, + }); + + if (result?.error) { + setError(result.error); + } + + if (result?.ok) { + safeRedirect(router, callbackUrl); + } else { + setError("Invalid email or password"); + } + } else { + setError("Unsupported authentication method"); + } + } catch (e: any) { + if (type === "firebase-password") { + setError(handleFirebaseError(e)); + await analytics.track("login_error", { + email, + type: "password", + loginProvider: "firebase/email", + message: e?.message || "Unknown error", + }); + } else { + setError(e.message || "Authentication failed"); + } + } + }; + + const handleSSOLogin = async (provider: AuthType, providerId: string, loginHint?: string) => { + try { + switch (provider) { + case "firebase-google": + case "firebase-github": + await firebaseSession!.signInWith(provider === "firebase-google" ? "google.com" : "github.com"); + const user = await firebaseSession!.resolveUser().user; + if (!user) { + setError("Sign in failed"); + return; + } + await analytics.track("login", { + traits: { ...user, type: "social", loginProvider: `firebase/${provider}` }, + }); + safeRedirect(router, callbackUrl); + break; + case "nextauth-github": + await signIn("github", { callbackUrl }, { ...(loginHint ? { login_hint: loginHint } : {}), prompt: "login" }); + break; + case "nextauth-oidc": + await signIn("oidc", { callbackUrl }, { ...(loginHint ? { login_hint: loginHint } : {}), prompt: "login" }); + break; + case "dynamic-oidc": + window.location.href = `/api/auth/dynamic-oidc/authorize?providerId=${providerId}&callbackUrl=${encodeURIComponent( + callbackUrl + )}${loginHint ? `&loginHint=${encodeURIComponent(loginHint)}` : ""}`; + break; + default: + setError(`Unsupported authentication provider: ${provider}`); + } + } catch (e: any) { + if (provider.startsWith("firebase-")) { + setError(handleFirebaseError(e)); + await analytics.track("login_error", { + type: "social", + loginProvider: `firebase/${provider}`, + message: e?.message || "Unknown error", + }); + } else { + setError(e.message || "Authentication failed"); + } + } + }; + + return ( + <> + + New to {branding.productName}?{" "} + + Create an account! + +
+ ) : null + } + title={ +
+ +
+ } + > + {ssoProviders.length > 0 && ( + + )} + {showEmailLogin && ( + <> + {ssoProviders.length > 0 && ( +
+
+
+
+
+ Or continue with email +
+
+ )} +
+ { + try { + await handlePasswordLogin(email, password, type); + } catch (e: any) { + setError(e.message || "Authentication failed"); + } + }} + onSSOLogin={handleSSOLogin} + /> +
+ + )} + {error && ( +
+ setError(null)} /> +
+ )} + + {router.query.error && ( +
+ +
+ )} + + + ); +}; + +function handleFirebaseError(error: any): string { + const code = error?.code; + if (code === "auth/account-exists-with-different-credential") { + const email = error?.customData?.email; + return `The account ${ + email ? `for '${email}'` : "" + } exists, but different sign in method has been used to create this account. Please try to sign in with another method.`; + } else if (code === "auth/popup-closed-by-user") { + return "Auth popup was closed by user. Please try again."; + } else if (code === "auth/invalid-email") { + return "Invalid email or password"; + } else if (code === "auth/wrong-password") { + return "Invalid email or password"; + } else if (code === "auth/user-not-found") { + return "Invalid email or password"; + } else if (code === "auth/too-many-requests") { + return "Too many signin attempts. Try later, or reset password now"; + } + + return (error?.message || "Unknown auth error").replace("Firebase: ", ""); +} diff --git a/webapps/console/components/SignInOrUp/SignUp.tsx b/webapps/console/components/SignInOrUp/SignUp.tsx deleted file mode 100644 index c86473d5a..000000000 --- a/webapps/console/components/SignInOrUp/SignUp.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import { SocialLogin } from "./SignIn"; -import React, { useEffect } from "react"; -import Link from "next/link"; -import { FirebaseProvider, useFirebaseSession } from "../../lib/firebase-client"; -import { useAppConfig } from "../../lib/context"; -import { ErrorCard } from "../GlobalError/GlobalError"; -import { useRouter } from "next/router"; -import { useQueryStringCopy } from "./use-query-string-copy"; - -export const SignUp: React.FC<{}> = () => { - const appConfig = useAppConfig(); - - if (appConfig?.auth?.firebasePublic) { - return ( - - - - ); - } else { - return ; - } -}; - -export const FirebaseSignUp: React.FC<{}> = () => { - const firebase = useFirebaseSession(); - const router = useRouter(); - const queryStringCopy = useQueryStringCopy(); - const redirect = (router.query.redirect as string) || "/"; - const invite = (router.query.invite as string) || ""; - useEffect(() => { - firebase.resolveUser().user.then(user => { - if (user) { - router.push(redirect); - } - }); - }); - return ( -
-
-
-
Sign up for Jitsu
-
- { - await firebase.signInWith(type); - if (invite) { - router.push(`/accept?invite=${invite}`); - } else { - router.push(redirect); - } - }} - /> -
-
-
- Already have an account?{" "} - - Log in - -
-
-
- ); -}; diff --git a/webapps/console/components/SyncEditorPage/SyncEditorPage.tsx b/webapps/console/components/SyncEditorPage/SyncEditorPage.tsx index 6914377e9..bc3641aea 100644 --- a/webapps/console/components/SyncEditorPage/SyncEditorPage.tsx +++ b/webapps/console/components/SyncEditorPage/SyncEditorPage.tsx @@ -1,95 +1,116 @@ -import { useWorkspace } from "../../lib/context"; +import { useAppConfig, useWorkspace, useWorkspaceRole } from "../../lib/context"; import { get } from "../../lib/useApi"; -import { DestinationConfig, ServiceConfig } from "../../lib/schema"; -import React, { PropsWithChildren, useCallback, useEffect, useState } from "react"; +import { DestinationConfig, SelectedStreamSettings, ServiceConfig, SyncOptionsType } from "../../lib/schema"; +import React, { useCallback, useEffect, useState } from "react"; import { z } from "zod"; import { ConfigurationObjectLinkDbModel } from "../../prisma/schema"; import { useRouter } from "next/router"; -import { assertTrue, getLog, hash as juavaHash, rpc } from "juava"; +import { assertTrue, getLog, requireDefined, rpc } from "juava"; import { Disable } from "../Disable/Disable"; -import { Button, Select } from "antd"; -import { WLink } from "../Workspace/WLink"; -import { FaExternalLinkAlt } from "react-icons/fa"; +import { Button, Checkbox, Input, Select, Switch, Tooltip } from "antd"; import { getCoreDestinationType } from "../../lib/schema/destinations"; -import { confirmOp, feedbackError, feedbackSuccess } from "../../lib/ui"; +import { confirmOp, copyTextToClipboard, feedbackError, feedbackSuccess } from "../../lib/ui"; import FieldListEditorLayout, { EditorItem } from "../FieldListEditorLayout/FieldListEditorLayout"; -import { ChevronLeft } from "lucide-react"; -import { JitsuButton } from "../JitsuButton/JitsuButton"; +import { Copy, ExternalLink, ListMinusIcon } from "lucide-react"; import { LoadingAnimation } from "../GlobalLoader/GlobalLoader"; -import { SnippedEditor } from "../CodeEditor/SnippedEditor"; -import hash from "stable-hash"; +import { ServiceTitle } from "../../pages/[workspaceId]/services"; +import { SwitchComponent } from "../ConnectionEditorPage/ConnectionEditorPage"; +import { SimpleErrorCard } from "../GlobalError/GlobalError"; +import { LabelEllipsis } from "../LabelEllipsis/LabelEllipsis"; +import { createDisplayName } from "../../lib/zod"; +import xor from "lodash/xor"; +import timezones from "timezones-list"; +import { useBilling } from "../Billing/BillingProvider"; +import { WLink } from "../Workspace/WLink"; +import { useStoreReload } from "../../lib/store"; +import capitalize from "lodash/capitalize"; +import { DestinationSelector, SelectorProps } from "../Selectors/DestinationSelector"; +import { EditorToolbar } from "../EditorToolbar/EditorToolbar"; +import JSON5 from "json5"; +import { FaRegFloppyDisk } from "react-icons/fa6"; +import { FunctionVariables } from "../FunctionsDebugger/FunctionVariables"; +import { BackButton } from "../BackButton/BackButton"; +import { JitsuButton } from "../JitsuButton/JitsuButton"; +import { FaExternalLinkAlt } from "react-icons/fa"; +import { initStream } from "../../lib/sources"; const log = getLog("SyncEditorPage"); -type SelectorProps = { - enabled: boolean; - selected: string; - items: T[]; - onSelect: (value: string) => void; -}; +const scheduleOptions = [ + { + label: "Disabled", + value: "", + minuteFrequency: Number.MAX_VALUE, + }, + { + label: "Every day", + value: "0 0 * * *", + minuteFrequency: 60 * 24, + }, + { + label: "Every hour", + value: "0 * * * *", + minuteFrequency: 60, + }, + { + label: "Every 15 minutes", + value: "*/15 * * * *", + minuteFrequency: 15, + }, + { + label: "Every 5 minutes", + value: "*/5 * * * *", + minuteFrequency: 5, + }, + { + label: "Every minute", + value: "* * * * *", + minuteFrequency: 1, + }, + { + label: "Custom", + value: "custom", + minuteFrequency: 0, + }, +]; -const Htmlizer: React.FC> = ({ children }) => { - return typeof children === "string" ? : <>{children}; +const namespaceImplementation: Record = { + clickhouse: { name: "database", field: "database" }, + postgres: { name: "schema", field: "defaultSchema" }, + redshift: { name: "schema", field: "defaultSchema" }, + bigquery: { name: "dataset", field: "bqDataset" }, + snowflake: { name: "schema", field: "defaultSchema" }, + mysql: { name: "database", field: "database" }, + s3: { name: "folder", field: "folder" }, + gcs: { name: "folder", field: "folder" }, }; -type SyncOptionsType = any; - -function DestinationSelector(props: SelectorProps) { - return ( -
- - - - {!props.enabled && ( -
- - - -
- )} -
- ); -} - function ServiceSelector(props: SelectorProps) { return (
- - {props.items.map(service => ( - -
-
- {service.package} -
-
{service.name}
-
- ({service.package.replaceAll(service.protocol + "/", "")}) -
-
+ + { + return ( +
+
{s.name}
+
({s.package.replaceAll(s.protocol + "/", "")})
+
+ ); + }} + />
))}
- {!props.enabled && ( + {!props.enabled && props.showLink && (
- +
@@ -98,12 +119,6 @@ function ServiceSelector(props: SelectorProps) { ); } -type EditorProps = { - value?: T; - disabled?: boolean; - onChange: (value: T) => void; -}; - function SyncEditor({ services, destinations, @@ -114,6 +129,8 @@ function SyncEditor({ links: z.infer[]; }) { const router = useRouter(); + const billing = useBilling(); + const appConfig = useAppConfig(); const existingLink = router.query.id ? links.find(link => link.id === router.query.id) : undefined; assertTrue(services.length > 0, `Services list is empty`); @@ -121,84 +138,269 @@ function SyncEditor({ const [loading, setLoading] = useState(false); const workspace = useWorkspace(); - const [dstId, setDstId] = useState(existingLink?.toId || destinations[0].id); - const [srvId, setSrvId] = useState(existingLink?.fromId || services[0].id); + const role = useWorkspaceRole(); + const canEdit = role.editEntities; + const [dstId, setDstId] = useState( + existingLink?.toId || (router.query.destinationId as string) || destinations[0].id + ); + const [srvId, setSrvId] = useState(existingLink?.fromId || (router.query.serviceId as string) || services[0].id); + const [connectorSubtype, setConnectorSubtype] = useState(undefined); const service = services.find(s => s.id === srvId); + const destination = requireDefined( + destinations.find(d => d.id === dstId), + `Destination ${dstId} not found` + ); + const destinationType = getCoreDestinationType(destination.destinationType); + const namespaceImpl = namespaceImplementation[destinationType.id] ?? { name: "namespace", filed: "namespace" }; + + const [syncOptions, setSyncOptions] = useState( + (existingLink?.data || { namespace: "", deduplicate: true }) as SyncOptionsType + ); + const [catalog, setCatalog] = useState(undefined); + const [catalogError, setCatalogError] = useState(undefined); + const [loadingCatalog, setLoadingCatalog] = useState(true); + const [refreshCatalog, setRefreshCatalog] = useState(0); + const reloadStore = useStoreReload(); - const [syncOptions, setSyncOptions] = useState(existingLink?.data || {}); - const [streamsProvided, setStreamsProvided] = useState(!!syncOptions.streams && syncOptions.streams !== "{}"); - const [loadingCatalog, setLoadingCatalog] = useState(false); + function onlyUnique(value, index, array) { + return array.indexOf(value) === index; + } - const updateOptions: (patch: Partial) => void = useCallback( - patch => { - log.atDebug().log("Patching sync options with", patch, " existing options", syncOptions); + const legacyMode = !!existingLink && typeof syncOptions.namespace === "undefined"; + const sourceNamespaces: string[] = + catalog?.streams + ?.map((s: any) => s.namespace) + .filter(n => !!n) + .filter(onlyUnique) || []; + const legacyPrefix = + legacyMode && sourceNamespaces?.length > 0 + ? sourceNamespaces?.length === 1 + ? syncOptions.tableNamePrefix + sourceNamespaces[0] + "_" + : (syncOptions.tableNamePrefix ?? "") + "${SOURCE_NAMESPACE}_" + : undefined; + + const nameTransformer = syncOptions.toSameCase + ? (s: string) => (destinationType.id === "snowflake" ? s.toUpperCase() : s.toLowerCase()) + : (s: string) => s; + + const destinationNamespaces = (sourceNamespaces?.length > 0 ? sourceNamespaces : [""]) + .map(ns => (syncOptions.namespace ? syncOptions.namespace.replaceAll("${SOURCE_NAMESPACE}", ns ?? "") : "")) + .map(ns => ns?.trim()) + .map(ns => ns || (destination?.[namespaceImpl.field] as string) || "") + .filter(ns => ns !== "") + .filter(onlyUnique); + + const [showCustomSchedule, setShowCustomSchedule] = useState( + syncOptions.schedule && !scheduleOptions.find(o => o.value === syncOptions.schedule) + ); + + const updateOptions = useCallback( + (patch: Partial) => { + // log.atDebug().log("Patching sync options with", patch, " existing options", syncOptions); setSyncOptions({ ...syncOptions, ...patch }); }, [syncOptions] ); - const updateCatalog: (catalog: any) => void = useCallback( - catalog => { - updateOptions({ streams: catalog }); - setStreamsProvided(true); + const updateSelectedStream = useCallback( + (streamName: string, propName: K, value: SelectedStreamSettings[K]) => { + updateOptions({ + streams: { + ...syncOptions.streams, + [streamName]: { + ...syncOptions.streams[streamName], + [propName]: value, + } as SelectedStreamSettings, + }, + }); + }, + [updateOptions, syncOptions.streams] + ); + + const disableSelectedStream = useCallback( + (streamName: string) => { + const stream = catalog.streams.find((s: any) => + streamName === s.namespace ? s.namespace + "." + s.name : s.name + ); + const streams = { ...syncOptions.streams }; + const disabledStream = streams[streamName] || (stream ? initStream(stream) : {}); + delete streams[streamName]; + updateOptions({ + streams: streams, + disabledStreams: { + ...syncOptions.disabledStreams, + [streamName]: disabledStream, + }, + }); + }, + [syncOptions.streams, syncOptions.disabledStreams, updateOptions, catalog] + ); + + const enableStream = useCallback( + (streamName: string) => { + const stream = catalog.streams.find((s: any) => + streamName === s.namespace ? s.namespace + "." + s.name : s.name + ); + if (!stream) { + log.atError().log("Stream not found in catalog", streamName); + return; + } + const disabledStreams = { ...syncOptions.disabledStreams }; + const initedStream = disabledStreams[streamName] || initStream(stream); + delete disabledStreams[streamName]; + + updateOptions({ + streams: { + ...syncOptions.streams, + [streamName]: initedStream, + }, + disabledStreams: disabledStreams, + }); + }, + [syncOptions.streams, syncOptions.disabledStreams, updateOptions, catalog] + ); + + const initSyncOptions = useCallback( + (catalog: any) => { + const streams: Record = {}; + const currentStreams = syncOptions.streams || {}; + const hasIncremental = Object.values(currentStreams).some((s: any) => s.sync_mode === "incremental"); + const disabledStreams = { ...syncOptions.disabledStreams }; + const newSync = typeof syncOptions.streams === "undefined"; + for (const stream of catalog?.streams ?? []) { + const name = stream.namespace ? `${stream.namespace}.${stream.name}` : stream.name; + const currentStream = currentStreams[name]; + const disabledStream = disabledStreams[name]; + if (currentStream) { + streams[name] = currentStream; + } else if (newSync) { + streams[name] = initStream(stream); + } else if (!disabledStream) { + if (syncOptions.schemaChanges === "streams") { + streams[name] = initStream(stream, hasIncremental ? "incremental" : "full_refresh"); + } else { + disabledStreams[name] = initStream(stream, hasIncremental ? "incremental" : "full_refresh"); + } + } + } + if ( + xor(Object.keys(streams), Object.keys(currentStreams)).length > 0 || + xor(Object.keys(disabledStreams), Object.keys(syncOptions.disabledStreams || {})).length > 0 + ) { + updateOptions({ streams, disabledStreams }); + } }, - [updateOptions] + [syncOptions.streams, syncOptions.disabledStreams, syncOptions.schemaChanges, updateOptions] ); + useEffect(() => { + if (catalog) { + initSyncOptions(catalog); + } + }, [catalog, initSyncOptions, syncOptions.schemaChanges]); + + const [runSyncAfterSave, setRunSyncAfterSave] = useState(!existingLink); + + const disableAllStreams = useCallback(() => { + if (catalog) { + const streams: Record = {}; + for (const stream of catalog?.streams ?? []) { + const name = stream.namespace ? `${stream.namespace}.${stream.name}` : stream.name; + const currentStream = syncOptions.streams?.[name] || syncOptions.disabledStreams?.[name]; + if (currentStream) { + streams[name] = currentStream; + } else { + streams[name] = initStream(stream); + } + } + updateOptions({ disabledStreams: streams, streams: {} }); + } + }, [catalog, syncOptions.streams, syncOptions.disabledStreams, updateOptions]); + + const enableAllStreams = useCallback(() => { + if (catalog) { + const streams: Record = {}; + for (const stream of catalog?.streams ?? []) { + const name = stream.namespace ? `${stream.namespace}.${stream.name}` : stream.name; + const currentStream = syncOptions.streams?.[name] || syncOptions.disabledStreams?.[name]; + if (currentStream) { + streams[name] = currentStream; + } else { + streams[name] = initStream(stream); + } + } + updateOptions({ streams: streams, disabledStreams: {} }); + } + }, [catalog, syncOptions.disabledStreams, syncOptions.streams, updateOptions]); + + useEffect(() => { + if (service?.package) { + (async () => { + const sourceType = await rpc(`/api/sources/airbyte/${encodeURIComponent(service.package)}`); + setConnectorSubtype(sourceType?.meta?.connectorSubtype); + })(); + } + }, [service?.package]); + useEffect(() => { if (!service) { console.log("No service."); return; } - if (streamsProvided) { - console.log("No need to load catalog. Specs are already filled."); + if (catalog) { return; } + let cancelled = false; (async () => { - console.log("Loading catalog"); + console.log("Loading catalog for:", service.package, service.version); setLoadingCatalog(true); try { - const h = juavaHash("md5", hash(JSON.parse(service.credentials))); - const storageKey = `${workspace.id}_${service.id}_${h}`; + const force = refreshCatalog > 0; const firstRes = await rpc( - `/api/${workspace.id}/sources/discover?package=${service.package}&version=${service.version}&storageKey=${storageKey}`, - { - body: service, - } + `/api/${workspace.id}/sources/discover?serviceId=${service.id}${force ? "&refresh=true" : ""}` ); - if (firstRes.error) { - updateCatalog(firstRes.error); + if (cancelled) { + return; + } + if (typeof firstRes.error !== "undefined") { + setCatalogError(firstRes.error); } else if (firstRes.ok) { - console.log("Loaded cached catalog:", JSON.stringify(firstRes, null, 2)); - updateCatalog(JSON.stringify(firstRes.catalog, null, 2)); + setCatalog(firstRes.catalog); } else { - for (let i = 0; i < 60; i++) { + for (let i = 0; i < 600; i++) { + if (cancelled) { + return; + } await new Promise(resolve => setTimeout(resolve, 2000)); - const resp = await rpc( - `/api/${workspace.id}/sources/discover?package=${service.package}&version=${service.version}&storageKey=${storageKey}` - ); + const resp = await rpc(`/api/${workspace.id}/sources/discover?serviceId=${service.id}`); if (!resp.pending) { - if (resp.error) { - updateCatalog(resp.error); + if (typeof resp.error !== "undefined") { + setCatalogError(resp.error); return; } else { - console.log("Loaded catalog:", JSON.stringify(resp, null, 2)); - updateCatalog(JSON.stringify(resp.catalog, null, 2)); + setCatalog(resp.catalog); return; } } } - updateCatalog(`Cannot load specs for ${service.package}:${service.version} error: Timeout`); + setCatalogError(`Cannot load catalog for ${service.package}:${service.version} error: Timeout`); } } catch (error) { - updateCatalog(error); + setCatalogError(error); } finally { - setLoadingCatalog(false); + if (!cancelled) { + setLoadingCatalog(false); + } } })(); - }, [workspace.id, service, updateOptions, streamsProvided]); + return () => { + cancelled = true; + }; + }, [workspace.id, service, initSyncOptions, updateOptions, refreshCatalog, catalog]); + const usesBulker = destinationType.usesBulker || destinationType.id === "webhook"; + // @ts-ignore const configItems: EditorItem[] = [ { name: existingLink ? "Select Service" : "Service", @@ -207,11 +409,14 @@ function SyncEditor({ { + setLoadingCatalog(true); setSrvId(v); - updateOptions({ streams: "{}" }); - setStreamsProvided(false); + setRefreshCatalog(0); + setCatalog(undefined); + updateOptions({ streams: undefined }); }} /> ), @@ -225,69 +430,540 @@ function SyncEditor({ { setDstId(id); }} /> ), }, - ]; - if (service) { - configItems.push({ - name: "Selected Streams", - component: ( - <> - {loadingCatalog ? ( - - ) : ( -
-
+ ), + }); + } + if (syncOptions.schedule) { + configItems.push({ + name: "Scheduler Timezone", + documentation: + "Select timezone for scheduler. E.g. 'Every day' setting runs sync at 00:00 in selected timezone", + component: ( + updateSelectedStream(name, "sync_mode", v)} + /> +
+
+ {stream.supported_sync_modes.includes("incremental") && stream.sync_mode === "incremental" && ( + <> + Cursor field:{" "} + + updateSelectedStream(name, "table_name", e.target.value)} + value={nameTransformer( + stream.table_name || + (syncOptions.tableNamePrefix + ? syncOptions.tableNamePrefix.replaceAll("${SOURCE_NAMESPACE}", stream.namespace ?? "") + : "") + tableName + )} + > +
+
+
- )} - - ), - }); + ), + }); + } + } else { + configItems.push({ + group: "Streams", + key: "streams", + component: ( +
+ +
+ ), + }); + } } return (
-
+

{(existingLink ? "Edit" : "Create") + " sync"}

- } type="link" size="small" onClick={() => router.back()}> - Back - +
+ {existingLink && ( +
+ , + href: "#", + onClick: () => { + copyTextToClipboard(existingLink.id); + feedbackSuccess("Copied to clipboard"); + }, + }, + { + title: "Logs", + icon: , + href: `/${workspace.slugOrId}/syncs/tasks?query=${JSON5.stringify({ syncId: existingLink.id })}`, + }, + { + title: "Saved State", + icon: , + href: `/${workspace.slugOrId}/syncs/state?id=${existingLink.id}`, + }, + ].filter(Boolean) as any + } + className="mb-4" + /> +
+ )}
Advanced settings
, + }, + Streams: { + expandable: false, + title: ( +
+
Streams
+
+ {usesBulker && ( + { + setLoadingCatalog(true); + setCatalog(undefined); + setRefreshCatalog(refreshCatalog + 1); + }} + > + Refresh Catalog + + )} +
+ Switch All + 0} + onChange={ch => { + if (ch) { + enableAllStreams(); + } else { + disableAllStreams(); + } + }} + /> +
+
+
+ ), + }, }} items={configItems} /> @@ -295,20 +971,22 @@ function SyncEditor({
{existingLink && ( - + )}
-
- - +
); } +function CursorFieldSelector(props: { onAdd: (value: string) => void }) { + const [name, setName] = useState(""); + return ( +
+ setName(e.target.value)} /> + +
+ ); +} + export default SyncEditor; diff --git a/webapps/console/components/TrackingIntegrationDocumentation/HTTPApi.tsx b/webapps/console/components/TrackingIntegrationDocumentation/HTTPApi.tsx new file mode 100644 index 000000000..177d793fd --- /dev/null +++ b/webapps/console/components/TrackingIntegrationDocumentation/HTTPApi.tsx @@ -0,0 +1,121 @@ +import { CodeBlock } from "../CodeBlock/CodeBlock"; + +export const HTTPManual: React.FC<{ domain: string; writeKey?: string }> = ({ domain, writeKey }) => { + return ( +
+
+

HTTP API

+

+ You can use server to server API to send data to Jitsu. This is useful if you want to send data from your + backend. +

+

In case of using HTTP API you may need to manually ad user's IP address and User Agent to the event.

+

Ingest endpoint (client mode)

+

This endpoint can be used to send events to Jitsu from user devices or browsers:

+ {`${domain}/api/s/{event-type}`} +

+ event-type could be: +

+
    +
  • + page, track, identify or group +
  • +
  • + Use event as event_type if you want server to take actual event type from{" "} + type field of the event payload +
  • +
+

The endpoint accepts POST requests with events payload in JSON format.

+

Ingest endpoint (server mode)

+

This endpoint can be used to send events to Jitsu from servers:

+ {`${domain}/api/s/s2s/{event-type}`} +

+ event-type could be: +

+
    +
  • + page, track, identify or group +
  • +
  • + Use event as event_type if you want server to take actual event type from{" "} + type field of the event payload +
  • +
+

+ The endpoint accepts POST requests with events payload in JSON format. Pay attention to s2s part + of the URL. +
+ The presence of this part indicates that the event is sent from server-to-server. Unlike browser events, s2s + events are processed differently: +

+
    +
  • + Presence of X-Write-Key is mandatory, and the header should contain server write key of the + site +
  • +
  • + context.ip and context.userAgent are not extracted from the request headers, but + should be passed explicitly in the event payload +
  • +
  • + If context.ip is not present, the ip field will be empty to avoid confusion; same applies for{" "} + context.userAgent +
  • +
+

Examples

+

+ Send track event: +

+ {`curl --location '${domain}/api/s/s2s/track' \\ +--header 'Content-Type: application/json' \\ +--header 'X-Write-Key: ${writeKey}' \\ +--data-raw '{ + "type": "track", + "event": "testEvent", + "properties": { + "testProp": "test event properties" + }, + "userId": "user@example.com", + "anonymousId": "bKTtbVZw3yiqCJvCSJgjVeXp", + "timestamp": "2023-04-12T13:29:04.690Z", + "sentAt": "2023-04-12T13:29:04.690Z", + "messageId": "voV6fulcZR4CTVnN89AnxFnC", + "context": { + "library": { + "name": "jitsu-js", + "version": "1.0.0" + }, + "ip": "127.0.0.1", + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/111.0", + "locale": "en-US", + "screen": { + "width": 2304, + "height": 1296, + "innerWidth": 1458, + "innerHeight": 1186, + "density": 2 + }, + "traits": { + "email": "user@example.com" + }, + "page": { + "path": "/", + "referrer": "", + "referring_domain": "", + "host": "example.com", + "search": "", + "title": "Example page event", + "url": "https://example.com/", + "encoding": "UTF-8" + }, + "campaign": { + "name": "example", + "source": "g" + } + }, + "receivedAt": "2023-04-12T13:29:04.690Z" +}'`} +
+
+ ); +}; diff --git a/webapps/console/components/TrackingIntegrationDocumentation/JavaScript.tsx b/webapps/console/components/TrackingIntegrationDocumentation/NPMPackage.tsx similarity index 94% rename from webapps/console/components/TrackingIntegrationDocumentation/JavaScript.tsx rename to webapps/console/components/TrackingIntegrationDocumentation/NPMPackage.tsx index 0952c95d4..185539286 100644 --- a/webapps/console/components/TrackingIntegrationDocumentation/JavaScript.tsx +++ b/webapps/console/components/TrackingIntegrationDocumentation/NPMPackage.tsx @@ -10,7 +10,7 @@ function getExampleJsonConfig() { ); } -export const JavaScriptManual: React.FC<{ domain: string; writeKey?: string }> = ({ domain, writeKey }) => { +export const NPMManual: React.FC<{ domain: string; writeKey?: string }> = ({ domain, writeKey }) => { return (
diff --git a/webapps/console/components/TrackingIntegrationDocumentation/Other.tsx b/webapps/console/components/TrackingIntegrationDocumentation/Other.tsx new file mode 100644 index 000000000..ee5107987 --- /dev/null +++ b/webapps/console/components/TrackingIntegrationDocumentation/Other.tsx @@ -0,0 +1,30 @@ +export const Other: React.FC<{ domain: string }> = ({ domain }) => { + return ( + + ); +}; diff --git a/webapps/console/components/TrackingIntegrationDocumentation/Segment.tsx b/webapps/console/components/TrackingIntegrationDocumentation/Segment.tsx deleted file mode 100644 index 4aa2fc58b..000000000 --- a/webapps/console/components/TrackingIntegrationDocumentation/Segment.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { branding } from "../../lib/branding"; -import { CodeBlock } from "../CodeBlock/CodeBlock"; -import { Callout } from "../Callout/Callout"; -import { WLink } from "../Workspace/WLink"; - -export const Segment: React.FC<{ domain: string }> = ({ domain }) => { - return ( -
-
-

If you're already using Segment

-

- If you're using Segment to collect data from your website, you can use our Segment integration to send data to{" "} - {branding.productName} -

-

For analytics.js

- - {[ - `const { Analytics } = require("analytics");`, - `const segmentPlugin = require('@analytics/segment')`, - `const analytics = Analytics({`, - ` plugins: [`, - ` segmentPlugin({`, - ` host: '${domain}/sg',`, - ` writeKey: '123-xyz'`, - ` })`, - ` ]`, - `})`, - `analytics.page();`, - ].join("\n")} - - {/*

For analytics.js 2.0

- Analytics 2.0 doesn't have a good documentation on plugins, - and it's hard to come up with a good example*/} -

- - If you want {branding.productName} to proxy your Segment calls, you can use{" "} - Segment proxy destination - -

-
-
- ); -}; diff --git a/webapps/console/components/TrackingIntegrationDocumentation/TrackingIntegrationDocumentation.tsx b/webapps/console/components/TrackingIntegrationDocumentation/TrackingIntegrationDocumentation.tsx index b88ed3a29..1604dbec9 100644 --- a/webapps/console/components/TrackingIntegrationDocumentation/TrackingIntegrationDocumentation.tsx +++ b/webapps/console/components/TrackingIntegrationDocumentation/TrackingIntegrationDocumentation.tsx @@ -8,16 +8,18 @@ import { ErrorCard } from "../GlobalError/GlobalError"; import { Center } from "../Center/Center"; import { HtmlManual } from "./Html"; import { ReactManual } from "./React"; -import { useURLPersistedState } from "../../lib/ui"; import { ButtonLabel } from "../ButtonLabel/ButtonLabel"; import { FiMonitor } from "react-icons/fi"; -import { FaReact } from "react-icons/fa"; -import { IoLogoJavascript } from "react-icons/io5"; -import { JavaScriptManual } from "./JavaScript"; -import { Segment } from "./Segment"; +import { FaGlobe, FaNodeJs, FaReact } from "react-icons/fa"; +import { NPMManual } from "./NPMPackage"; +import { Other } from "./Other"; import { useAppConfig } from "../../lib/context"; import { ReactNode, useState } from "react"; import { HiSelector } from "react-icons/hi"; +import { HTTPManual } from "./HTTPApi"; +import { Plug } from "lucide-react"; +import { useQueryStringState } from "../../lib/useQueryStringState"; +import { useConfigObjectList } from "../../lib/store"; function SegmentLogo() { return ( @@ -86,6 +88,8 @@ export const TrackingIntegrationDocumentation: React.FC<{ streamId: string; onCa streamId, onCancel, }) => { + const ds = useConfigObjectList("domain"); + const staticDomains = ds.filter(d => !d.name.includes("*")).map(d => d.name); const appConfig = useAppConfig(); const configApi = useConfigApi("stream"); const [selectedDomain, setSelectedDomain] = useState(); @@ -94,23 +98,43 @@ export const TrackingIntegrationDocumentation: React.FC<{ streamId: string; onCa isLoading, error, } = useQuery(["stream", streamId], () => configApi.get(streamId), { cacheTime: 0, retry: false }); - const [framework, setFramework] = useURLPersistedState("framework", { - defaultVal: "html", + const [framework, setFramework] = useQueryStringState("framework", { + defaultValue: "html", parser: v => v, serializer: v => v, }); - const domains = stream - ? appConfig.publicEndpoints.dataHost || appConfig.ee.available - ? [...(stream?.domains ?? []), `${stream.id}.${appConfig.publicEndpoints.dataHost}`] - : ["{deploy domain}"] - : []; + let domains: string[] = []; + let protocol = appConfig.publicEndpoints.protocol; + if (appConfig.publicEndpoints.ingestUrl) { + //extract host and port + try { + const url = new URL(appConfig.publicEndpoints.ingestUrl); + const host = url.host; + protocol = url.protocol === "https:" ? "https" : "http"; + domains.push(host); + } catch (e) { + console.error("Can't parse ingest URL", appConfig.publicEndpoints.ingestUrl); + } + } else { + domains = stream + ? appConfig.publicEndpoints.dataHost || appConfig.ee.available + ? [ + ...new Set([...staticDomains, ...(stream?.domains ?? [])]), + `${stream.id}.${appConfig.publicEndpoints.dataHost}`, + ] + : ["{deploy domain}"] + : []; + } const writeKey = appConfig.publicEndpoints.dataHost || appConfig.ee.available ? undefined : stream?.publicKeys?.[0] - ? stream.publicKeys[0].hint + ? `${stream.publicKeys[0].id}:${stream.publicKeys[0].hint}` : streamId || "{write-key}"; - const displayDomain = `${appConfig.publicEndpoints.protocol}://${selectedDomain ?? domains?.[0]}`; + const serverWriteKey = stream?.privateKeys?.[0] + ? `${stream.privateKeys[0].id}:${stream.privateKeys[0].hint}` + : "{server-write-key}"; + const displayDomain = `${protocol}://${selectedDomain ?? domains?.[0]}`; const wrap = (r: ReactNode) => { return (
@@ -128,64 +152,73 @@ export const TrackingIntegrationDocumentation: React.FC<{ streamId: string; onCa return ( <> - <> - {isLoading && ( -
- -
- )} - {error && ( -
- -
- )} - {stream && ( - }> - HTML - - ), - key: "html", - children: wrap(), - }, - { - label: ( - }> - React - - ), - key: "react", - children: wrap(), - }, - { - label: }>JavaScript, - key: "js", - children: wrap(), - }, - { - label: ( - - -
- } - > - Segment Proxy - - ), - key: "segment", - children: wrap(), - }, - ]} - /> - )} - +
+ <> + {isLoading && ( +
+ +
+ )} + {error && ( +
+ +
+ )} + {stream && ( + }> + HTML + + ), + key: "html", + children: wrap(), + }, + { + label: ( + }> + React + + ), + key: "react", + children: wrap(), + }, + { + label: ( + }> + NPM Package + + ), + key: "js", + children: wrap(), + }, + { + label: ( + }> + HTTP API + + ), + key: "http", + children: wrap(), + }, + { + label: ( + }> + Apps & Others + + ), + key: "other", + children: wrap(), + }, + ]} + /> + )} + +
); diff --git a/webapps/console/components/UserNotificationSettings/UserNotificationSettings.tsx b/webapps/console/components/UserNotificationSettings/UserNotificationSettings.tsx new file mode 100644 index 000000000..db1903719 --- /dev/null +++ b/webapps/console/components/UserNotificationSettings/UserNotificationSettings.tsx @@ -0,0 +1,139 @@ +import React, { useEffect, useState } from "react"; +import { Button, Form, Input, Switch } from "antd"; +import { feedbackError, feedbackSuccess } from "../../lib/ui"; +import type { UserNotificationsPreferences } from "../../lib/server/user-preferences"; +import { get, useApi } from "../../lib/useApi"; +import { ErrorCard } from "../GlobalError/GlobalError"; +import { LoadingAnimation } from "../GlobalLoader/GlobalLoader"; +import { useUser, WorkspaceContext } from "../../lib/context"; + +export const UserNotificationSettings: React.FC<{ + className?: string; + workspace?: WorkspaceContext; +}> = ({ className, workspace }) => { + const { + isLoading: loading, + data, + error, + } = useApi( + `/api/user/notifications-settings${workspace ? `?workspaceId=${workspace.id}&mergeWithGlobal=true` : ""}` + ); + const [notificationPreference, setNotificationPreference] = useState(data); + const [saving, setSaving] = useState(false); + const [form] = Form.useForm(); + const user = useUser(); + + useEffect(() => { + setNotificationPreference(data); + }, [data]); + + if (error) { + return ; + } else if (loading) { + return ; + } + return ( + // px-8 py-6 border border-textDisabled rounded-lg +
+
{ + console.log("newValues", newValues); + setNotificationPreference({ ...notificationPreference, ...newValues }); + }} + > +
+
+ + + + {" "} +
+
+ + + + {" "} +
+
+ + + + +
+
+
+ {workspace ? ( +
+ Email notification settings are managed separately for each workspace per user. +
+ The current form controls notification settings for{" "} + {user.email}, specifically for events in the{" "} + {workspace!.name || workspace!.slug} + {workspace!.name.includes("workspace") ? "" : " workspace"}. +
+ ) : ( +
+ Email notification settings are managed individually for each workspace in the workspace’s Notification + settings. +
+ This setting will be applied by default to newly created workspaces or workspaces where the user is + invited. +
+ )} + +
+ +
+ ); +}; diff --git a/webapps/console/components/Workspace/WLink.tsx b/webapps/console/components/Workspace/WLink.tsx index 0c49f29cf..c6cb2415b 100644 --- a/webapps/console/components/Workspace/WLink.tsx +++ b/webapps/console/components/Workspace/WLink.tsx @@ -6,12 +6,13 @@ import { useWorkspace } from "../../lib/context"; export type WLinkProps = LinkProps & { target?: string; rel?: string; + className?: string; }; export const WLink: React.FC> = ({ href, target, rel, children, ...props }) => { const workspace = useWorkspace(); return ( - + {children} ); diff --git a/webapps/console/components/WorkspaceNameAndSlugEditor/WorkspaceNameAndSlugEditor.tsx b/webapps/console/components/WorkspaceNameAndSlugEditor/WorkspaceNameAndSlugEditor.tsx index 6b7b11f43..17b08d6ee 100644 --- a/webapps/console/components/WorkspaceNameAndSlugEditor/WorkspaceNameAndSlugEditor.tsx +++ b/webapps/console/components/WorkspaceNameAndSlugEditor/WorkspaceNameAndSlugEditor.tsx @@ -1,9 +1,11 @@ -import { useUser, useWorkspace } from "../../lib/context"; +import { useUser } from "../../lib/context"; import React, { useState } from "react"; -import { Button, Input } from "antd"; +import { Input } from "antd"; import { get } from "../../lib/useApi"; import { copyTextToClipboard, feedbackError, feedbackSuccess } from "../../lib/ui"; import { publicEmailDomains } from "../../lib/shared/email-domains"; +import { JitsuButton } from "../JitsuButton/JitsuButton"; +import type { ContextApiResponse } from "../../lib/schema"; function ensureLength(res): string { return res.length < 5 ? res + "project" : res; @@ -23,95 +25,176 @@ function pickSlug(email, name): string { return ensureLength(username.replace(/[^a-z0-9]/g, "")); } +function capitalize(s: string) { + return s.charAt(0).toUpperCase() + s.slice(1); +} + +function pickWorkspaceName(user: ContextApiResponse["user"]) { + if (!user.email) { + return `${user.name}'s workspace`; + } + const [username, domain] = user.email.split("@"); + if (publicEmailDomains.includes((domain ?? "").toLowerCase())) { + return `${username}'s workspace`; + } else { + const [company, ...rest] = domain.split("."); + return `${capitalize(company)}'s workspace`; + } +} + +/** + * @param onboarding if the dialog is shown on onboarding page. For onboarding, + * we should issue an event that onboarding is completed + */ export function WorkspaceNameAndSlugEditor({ onSuccess, displayId, + onboarding, + workspace, + canEdit = true, }: { - onSuccess?: (newVals: { name: string; slug: string }) => void; + onSuccess?: (newVals: { name: string; slug: string; id?: string }) => void; displayId?: boolean; + offerClassic?: boolean; + onboarding?: boolean; + workspace?: { id?: string; name?: string; slug?: string | null }; + canEdit?: boolean; }) { - const workspace = useWorkspace(); const user = useUser(); - const [name, setName] = useState(workspace.name); - const [slug, setSlug] = useState(workspace.slug || pickSlug(user.email, workspace.name)); + const [name, setName] = useState(workspace?.name || pickWorkspaceName(user)); + const [slug, setSlug] = useState(workspace?.slug || pickSlug(user.email, workspace?.name || name)); const [changed, setChanged] = useState(false); const [loading, setLoading] = useState(false); const [slugError, setSlugError] = useState(); + const [nameError, setNameError] = useState(); return ( -
-
Workspace Name
- { - setName(e.target.value); - setChanged(true); - }} - /> -
Workspace Slug
- { - setSlug(e.target.value); - setChanged(true); - }} - /> - {displayId && ( - <> -
Workspace Id
-
{ - copyTextToClipboard(workspace.id); - feedbackSuccess("Workspace id copied to clipboard"); +
+
+

Workspace Configuration

+
+ +
+
+ + { + setName(e.target.value); + setChanged(true); + setNameError(undefined); // Clear error on change }} - > - {workspace.id} -
-
- You'll need this id for making{" "} - - API calls - {" "} + /> + {nameError &&
{nameError}
} +
+ +
+ + { + setSlug(e.target.value); + setChanged(true); + setSlugError(undefined); // Clear error on change + }} + /> + {slugError &&
{slugError}
} +
+ + {displayId && workspace?.id && ( +
+ +
{ + copyTextToClipboard(workspace.id!); + feedbackSuccess("Workspace ID copied to clipboard"); + }} + > + {workspace.id} +
+

+ You'll need this ID for making{" "} + + API calls + +

- - )} -
- + {workspace?.id ? "Save Changes" : "Create Workspace"} +
); diff --git a/webapps/console/emails/connection-status-failed.tsx b/webapps/console/emails/connection-status-failed.tsx new file mode 100644 index 000000000..e99184406 --- /dev/null +++ b/webapps/console/emails/connection-status-failed.tsx @@ -0,0 +1,115 @@ +import { EmailTemplate } from "@jitsu-internal/webapps-shared"; +import { Body, Container, Html, Preview, Section, Text } from "@react-email/components"; + +import React from "react"; +import dayjs from "dayjs"; +import utc from "dayjs/plugin/utc"; +import { main } from "./styles"; +import { CheckJobStatusButton, Details, Footer, MetaList } from "./shared"; +import capitalize from "lodash/capitalize"; +import { ConnectionStatusNotificationProps } from "../pages/api/admin/notifications"; + +dayjs.extend(utc); + +export const ConnectionStatusFailedEmail: EmailTemplate = props => { + let { + name, + workspaceName, + entityType, + entityName, + entityFrom, + entityTo, + tableName, + incidentDetails, + incidentStatus, + incidentStartedAt, + queueSize, + recurringAlertsPeriodHours, + detailsUrl, + unsubscribeLink, + } = props; + + if (!workspaceName?.toLowerCase().endsWith(" workspace")) { + workspaceName += " workspace"; + } + + return ( + + + 🚨 {capitalize(entityType)} job "{entityName}" has FAILED in the {workspaceName} + + + +
+ + 🚨 {capitalize(entityType)} job of the connection {entityName} has FAILED + +
+ Hi {name || "there"}! + + The last job of the connection from {entityFrom} to {entityTo} has FAILED in the{" "} + {workspaceName} + + + +
+ + {recurringAlertsPeriodHours ? ( + + No additional reports will be sent for this connection in {recurringAlertsPeriodHours} hours unless the + status changes. + + ) : ( + <> + )} + +
+ + + + ); +}; + +ConnectionStatusFailedEmail.subject = ({ workspaceName, entityType, entityName }) => { + if (!workspaceName?.toLowerCase().endsWith(" workspace")) { + workspaceName += " workspace"; + } + return `[${workspaceName || "Your Jitsu Workspace"}] 🚨 ${capitalize(entityType)} job failed: ${entityName}`; +}; + +ConnectionStatusFailedEmail.isMarketingEmail = false; + +ConnectionStatusFailedEmail.PreviewProps = { + status: "FAILED", + timestamp: "2025-03-31T12:06:43.161Z", + name: "John", + entityId: "entity-id", + entityType: "batch", + entityName: "Entrypoint to Redshift", + entityFrom: "Entrypoint", + entityTo: "Redshift", + tableName: "events", + incidentDetails: + "2025-03-31T12:06:43.161Z [FAILED] failed to setup s3 client:\nS3 bucket access error: operation error S3: HeadBucket, https response error StatusCode: 0, RequestID: , HostID: , canceled, context deadline exceeded", + incidentStatus: "FAILED", + incidentStartedAt: dayjs().subtract(5, "minute").toISOString(), + queueSize: 13222, + workspaceSlug: "workspace-slug", + workspaceName: "Integration Tests", + recurringAlertsPeriodHours: 24, + recurring: false, + flappingWindowHours: 2, + changesPerHours: 0, + flappingSince: "", + streamsFailed: "", + detailsUrl: "http://localhost:3000/data", + baseUrl: "http://localhost:3000", + unsubscribeLink: "https://example.com/unsubscribe", +}; + +export default ConnectionStatusFailedEmail; diff --git a/webapps/console/emails/connection-status-firstrun.tsx b/webapps/console/emails/connection-status-firstrun.tsx new file mode 100644 index 000000000..02de9a532 --- /dev/null +++ b/webapps/console/emails/connection-status-firstrun.tsx @@ -0,0 +1,101 @@ +import { EmailTemplate } from "@jitsu-internal/webapps-shared"; +import { Body, Container, Html, Preview, Section, Text } from "@react-email/components"; +import React from "react"; +import dayjs from "dayjs"; +import utc from "dayjs/plugin/utc"; +import { main } from "./styles"; +import { CheckJobStatusButton, Footer, MetaList } from "./shared"; +import { ConnectionStatusNotificationProps } from "../pages/api/admin/notifications"; + +dayjs.extend(utc); + +export const ConnectionStatusFirstRunEmail: EmailTemplate = props => { + let { + name, + workspaceName, + entityType, + entityName, + entityFrom, + entityTo, + tableName, + recurringAlertsPeriodHours, + detailsUrl, + unsubscribeLink, + } = props; + + if (!workspaceName?.toLowerCase().endsWith(" workspace")) { + workspaceName += " workspace"; + } + + return ( + + + 🎉 Successful initial run of {entityType} job "{entityName}" in the {workspaceName} + + + +
+ + 🎉️ The initial job of the connection {entityName} has been SUCCESSFUL +
+
+
+ Hi {name || "there"}! + + + Congratulations! The initial job of the connection from {entityFrom} to {entityTo} in the{" "} + {workspaceName} has been SUCCESSFUL + + + + + + + {recurringAlertsPeriodHours && ( + No additional reports will be sent for this connection unless the status changes. + )} + +
+ + + + ); +}; + +ConnectionStatusFirstRunEmail.subject = ({ workspaceName, entityType, entityName }) => { + if (!workspaceName?.toLowerCase().endsWith(" workspace")) { + workspaceName += " workspace"; + } + return `[${workspaceName || "Your Jitsu Workspace"}] 🎉 Successful initial run of ${entityType} job: ${entityName}`; +}; + +ConnectionStatusFirstRunEmail.isMarketingEmail = false; + +ConnectionStatusFirstRunEmail.PreviewProps = { + status: "FIRST_RUN", + timestamp: "2025-03-31T12:06:43.161Z", + name: "John", + entityId: "entity-id", + entityType: "batch", + entityName: "Entrypoint to Redshift", + entityFrom: "Entrypoint", + entityTo: "Redshift", + tableName: "events", + queueSize: 120, + incidentStatus: "", + incidentDetails: "", + incidentStartedAt: "", + workspaceSlug: "workspace-slug", + workspaceName: "Integration Tests", + recurringAlertsPeriodHours: 24, + recurring: false, + flappingWindowHours: 2, + changesPerHours: 0, + flappingSince: "", + streamsFailed: "", + detailsUrl: "http://localhost:3000/data", + baseUrl: "http://localhost:3000", + unsubscribeLink: "https://example.com/unsubscribe", +}; + +export default ConnectionStatusFirstRunEmail; diff --git a/webapps/console/emails/connection-status-flapping.tsx b/webapps/console/emails/connection-status-flapping.tsx new file mode 100644 index 000000000..a8c6f599a --- /dev/null +++ b/webapps/console/emails/connection-status-flapping.tsx @@ -0,0 +1,111 @@ +import { EmailTemplate } from "@jitsu-internal/webapps-shared"; +import { Body, Container, Html, Preview, Section, Text } from "@react-email/components"; +import React from "react"; +import dayjs from "dayjs"; +import utc from "dayjs/plugin/utc"; +import { main } from "./styles"; +import { CheckJobStatusButton, Details, Footer, MetaList } from "./shared"; +import capitalize from "lodash/capitalize"; +import { ConnectionStatusNotificationProps } from "../pages/api/admin/notifications"; + +dayjs.extend(utc); + +export const ConnectionStatusFlappingEmail: EmailTemplate = props => { + let { + name, + workspaceName, + entityType, + entityName, + entityFrom, + entityTo, + tableName, + incidentDetails, + incidentStatus, + changesPerHours, + flappingWindowHours, + queueSize, + recurringAlertsPeriodHours, + detailsUrl, + unsubscribeLink, + } = props; + + if (!workspaceName?.toLowerCase().endsWith(" workspace")) { + workspaceName += " workspace"; + } + + return ( + + + ⚠️ {capitalize(entityType)} job "{entityName}" status fluctuating between success and failure in the{" "} + {workspaceName} + + + +
+ + ⚠️ Intermittent {entityType} job failures on the connection {entityName} + +
+ Hi {name || "there"}! + + {capitalize(entityType)} processing status changes between success and failure on the connection from{" "} + {entityFrom} to {entityTo} in the {workspaceName} + + + It has changed status {changesPerHours} times in the last {flappingWindowHours} hours. + + + +
+ + {recurringAlertsPeriodHours && ( + + No additional reports will be sent for this connection in {recurringAlertsPeriodHours} hours unless the + status changes. + + )} +
+ + + + ); +}; + +ConnectionStatusFlappingEmail.subject = ({ workspaceName, entityType, entityName }) => { + if (!workspaceName?.toLowerCase().endsWith(" workspace")) { + workspaceName += " workspace"; + } + return `[${workspaceName || "Your Jitsu Workspace"}] ⚠️ Intermittent failures of ${entityType} job: ${entityName}`; +}; + +ConnectionStatusFlappingEmail.isMarketingEmail = false; + +ConnectionStatusFlappingEmail.PreviewProps = { + status: "FLAPPING", + timestamp: "2025-03-31T12:06:43.161Z", + name: "John", + entityId: "entity-id", + entityType: "batch", + entityName: "Entrypoint to Redshift", + entityFrom: "Entrypoint", + entityTo: "Redshift", + tableName: "events", + incidentDetails: + "2025-03-31T12:06:43.161Z [FAILED] failed to setup s3 client: s3 bucket access error: operation error S3: HeadBucket, https response error StatusCode: 0, RequestID: , HostID: , canceled, context deadline exceeded", + incidentStatus: "FAILED", + incidentStartedAt: dayjs().subtract(2, "hour").toISOString(), + queueSize: 2422, + changesPerHours: 5, + flappingWindowHours: 6, + workspaceSlug: "workspace-slug", + workspaceName: "Integration Tests", + recurringAlertsPeriodHours: 24, + recurring: true, + flappingSince: "", + streamsFailed: "", + detailsUrl: "http://localhost:3000/data", + baseUrl: "http://localhost:3000", + unsubscribeLink: "https://example.com/unsubscribe", +}; + +export default ConnectionStatusFlappingEmail; diff --git a/webapps/console/emails/connection-status-ongoing.tsx b/webapps/console/emails/connection-status-ongoing.tsx new file mode 100644 index 000000000..eaea2112a --- /dev/null +++ b/webapps/console/emails/connection-status-ongoing.tsx @@ -0,0 +1,118 @@ +import { EmailTemplate } from "@jitsu-internal/webapps-shared"; +import { Body, Container, Html, Preview, Section, Text } from "@react-email/components"; +import React from "react"; +import dayjs from "dayjs"; +import utc from "dayjs/plugin/utc"; +import { main } from "./styles"; +import { CheckJobStatusButton, Details, Footer, MetaList } from "./shared"; +import capitalize from "lodash/capitalize"; +import { ConnectionStatusNotificationProps } from "../pages/api/admin/notifications"; + +dayjs.extend(utc); + +export const ConnectionStatusOngoingEmail: EmailTemplate = props => { + let { + name, + workspaceName, + entityType, + entityName, + entityFrom, + entityTo, + tableName, + incidentDetails, + incidentStatus, + incidentStartedAt, + queueSize, + streamsFailed, + recurringAlertsPeriodHours, + detailsUrl, + unsubscribeLink, + } = props; + + if (!workspaceName?.toLowerCase().endsWith(" workspace")) { + workspaceName += " workspace"; + } + const partial = incidentStatus === "PARTIAL" || incidentStatus === "TIME_EXCEEDED"; + + return ( + + + {partial ? "⚠️" : "🚨"} Ongoing {entityType} processing issues with "{entityName}" in the {workspaceName} + + + +
+ + {partial ? "⚠️" : "🚨"} Ongoing {entityType} processing issues with the connection {entityName} + +
+ Hi {name || "there"}! + + {capitalize(entityType)} processing issues persist with the connection from {entityFrom} to{" "} + {entityTo} in the {workspaceName} + + + +
+ + {recurringAlertsPeriodHours && ( + + No additional reports will be sent for this connection in {recurringAlertsPeriodHours} hours unless the + status changes. + + )} +
+ + + + ); +}; + +ConnectionStatusOngoingEmail.subject = ({ workspaceName, entityType, entityName, incidentStatus }) => { + if (!workspaceName?.toLowerCase().endsWith(" workspace")) { + workspaceName += " workspace"; + } + const partial = incidentStatus === "PARTIAL" || incidentStatus === "TIME_EXCEEDED"; + + return `[${workspaceName || "Your Jitsu Workspace"}] ${ + partial ? "⚠️" : "🚨" + } Ongoing ${entityType} processing issues: ${entityName}`; +}; + +ConnectionStatusOngoingEmail.isMarketingEmail = false; + +ConnectionStatusOngoingEmail.PreviewProps = { + status: "ONGOING", + timestamp: "2025-03-31T12:06:43.161Z", + name: "John", + entityId: "entity-id", + entityType: "batch", + entityName: "Entrypoint to Redshift", + entityFrom: "Entrypoint", + entityTo: "Redshift", + tableName: "events", + incidentDetails: + "2025-03-31T12:06:43.161Z [FAILED] failed to setup s3 client: s3 bucket access error: operation error S3: HeadBucket, https response error StatusCode: 0, RequestID: , HostID: , canceled, context deadline exceeded", + incidentStatus: "FAILED", + incidentStartedAt: dayjs().subtract(2, "hour").toISOString(), + queueSize: 132422, + workspaceSlug: "workspace-slug", + workspaceName: "Integration Tests", + recurringAlertsPeriodHours: 24, + recurring: true, + flappingWindowHours: 2, + changesPerHours: 0, + flappingSince: "", + streamsFailed: "", + detailsUrl: "http://localhost:3000/data", + baseUrl: "http://localhost:3000", + unsubscribeLink: "https://example.com/unsubscribe", +}; + +export default ConnectionStatusOngoingEmail; diff --git a/webapps/console/emails/connection-status-partial.tsx b/webapps/console/emails/connection-status-partial.tsx new file mode 100644 index 000000000..b7c10d0b3 --- /dev/null +++ b/webapps/console/emails/connection-status-partial.tsx @@ -0,0 +1,113 @@ +import { EmailTemplate } from "@jitsu-internal/webapps-shared"; +import { Body, Container, Html, Preview, Section, Text } from "@react-email/components"; + +import React from "react"; +import dayjs from "dayjs"; +import utc from "dayjs/plugin/utc"; +import { main } from "./styles"; +import { CheckJobStatusButton, Details, Footer, MetaList } from "./shared"; +import capitalize from "lodash/capitalize"; +import { ConnectionStatusNotificationProps } from "../pages/api/admin/notifications"; + +dayjs.extend(utc); + +export const ConnectionStatusPartialEmail: EmailTemplate = props => { + let { + name, + workspaceName, + entityType, + entityName, + entityFrom, + entityTo, + incidentDetails, + incidentStatus, + incidentStartedAt, + streamsFailed, + recurringAlertsPeriodHours, + detailsUrl, + unsubscribeLink, + } = props; + + if (!workspaceName?.toLowerCase().endsWith(" workspace")) { + workspaceName += " workspace"; + } + + return ( + + + ⚠️ {capitalize(entityType)} job "{entityName}" has run with PARTIAL success in the {workspaceName} + + + +
+ + ⚠️ {capitalize(entityType)} job of the connection {entityName} has run with PARTIAL success + +
+ Hi {name || "there"}! + + The last job of the connection from {entityFrom} to {entityTo} has run with PARTIAL{" "} + success in the {workspaceName} + + + +
+ + {recurringAlertsPeriodHours ? ( + + No additional reports will be sent for this connection in {recurringAlertsPeriodHours} hours unless the + status changes. + + ) : ( + <> + )} + +
+ + + + ); +}; + +ConnectionStatusPartialEmail.subject = ({ workspaceName, entityType, entityName }) => { + if (!workspaceName?.toLowerCase().endsWith(" workspace")) { + workspaceName += " workspace"; + } + return `[${workspaceName || "Your Jitsu Workspace"}] ⚠️ ${capitalize(entityType)} had partial success: ${entityName}`; +}; + +ConnectionStatusPartialEmail.isMarketingEmail = false; + +ConnectionStatusPartialEmail.PreviewProps = { + status: "FAILED", + timestamp: "2025-03-31T12:06:43.161Z", + name: "John", + entityId: "entity-id", + entityType: "sync", + entityName: "Entrypoint to Redshift", + entityFrom: "Entrypoint", + entityTo: "Redshift", + tableName: "", + incidentDetails: + "2025-03-31T12:06:43.161Z [FAILED] failed to setup s3 client:\nS3 bucket access error: operation error S3: HeadBucket, https response error StatusCode: 0, RequestID: , HostID: , canceled, context deadline exceeded", + incidentStatus: "PARTIAL", + incidentStartedAt: dayjs().subtract(5, "minute").toISOString(), + queueSize: 0, + workspaceSlug: "workspace-slug", + workspaceName: "Integration Tests", + recurringAlertsPeriodHours: 24, + recurring: false, + flappingWindowHours: 2, + changesPerHours: 0, + flappingSince: "", + streamsFailed: "2 of 13", + detailsUrl: "http://localhost:3000/data", + baseUrl: "http://localhost:3000", + unsubscribeLink: "https://example.com/unsubscribe", +}; + +export default ConnectionStatusPartialEmail; diff --git a/webapps/console/emails/connection-status-recovered.tsx b/webapps/console/emails/connection-status-recovered.tsx new file mode 100644 index 000000000..277439261 --- /dev/null +++ b/webapps/console/emails/connection-status-recovered.tsx @@ -0,0 +1,111 @@ +import { EmailTemplate } from "@jitsu-internal/webapps-shared"; +import { Body, Container, Html, Preview, Section, Text } from "@react-email/components"; +import React from "react"; +import dayjs from "dayjs"; +import utc from "dayjs/plugin/utc"; +import { main } from "./styles"; +import capitalize from "lodash/capitalize"; +import { CheckJobStatusButton, Footer, MetaList } from "./shared"; +import { ConnectionStatusNotificationProps } from "../pages/api/admin/notifications"; + +dayjs.extend(utc); + +export const ConnectionStatusRecoveredEmail: EmailTemplate = props => { + let { + name, + workspaceName, + entityType, + entityName, + entityFrom, + entityTo, + tableName, + incidentStatus, + incidentStartedAt, + queueSize, + recurringAlertsPeriodHours, + detailsUrl, + unsubscribeLink, + } = props; + + if (!workspaceName?.toLowerCase().endsWith(" workspace")) { + workspaceName += " workspace"; + } + + return ( + + + ✅ {capitalize(entityType)} job "{entityName}" processing restored in the {workspaceName} + + + +
+ + ✅ {capitalize(entityType)} job processing of the connection {entityName} has been RESTORED + +
+ Hi {name || "there"}! + + + The last job of the connection from {entityFrom} to {entityTo} has been SUCCESSFUL in + the {workspaceName} + + + + + + {recurringAlertsPeriodHours && ( + No additional reports will be sent for this connection unless the status changes. + )} + +
+ + + + ); +}; + +ConnectionStatusRecoveredEmail.subject = ({ workspaceName, entityType, entityName }) => { + if (!workspaceName?.toLowerCase().endsWith(" workspace")) { + workspaceName += " workspace"; + } + return `[${workspaceName || "Your Jitsu Workspace"}] ✅ ${capitalize( + entityType + )} job processing restored: ${entityName}`; +}; + +ConnectionStatusRecoveredEmail.isMarketingEmail = false; + +ConnectionStatusRecoveredEmail.PreviewProps = { + status: "RECOVERED", + timestamp: "2025-03-31T12:06:43.161Z", + name: "John", + entityId: "entity-id", + entityType: "batch", + entityName: "Entrypoint to Redshift", + entityFrom: "Entrypoint", + entityTo: "Redshift", + tableName: "events", + queueSize: 6120, + incidentDetails: + "2025-03-31T12:06:43.161Z [FAILED] failed to setup s3 client: s3 bucket access error: operation error S3: HeadBucket, https response error StatusCode: 0, RequestID: , HostID: , canceled, context deadline exceeded", + incidentStatus: "FAILED", + incidentStartedAt: dayjs().subtract(5, "day").toISOString(), + workspaceSlug: "workspace-slug", + workspaceName: "Integration Tests", + recurringAlertsPeriodHours: 24, + recurring: false, + flappingWindowHours: 2, + changesPerHours: 0, + flappingSince: "", + streamsFailed: "", + detailsUrl: "http://localhost:3000/data", + baseUrl: "http://localhost:3000", + unsubscribeLink: "https://example.com/unsubscribe", +}; + +export default ConnectionStatusRecoveredEmail; diff --git a/webapps/console/emails/shared.tsx b/webapps/console/emails/shared.tsx new file mode 100644 index 000000000..ba6ba4010 --- /dev/null +++ b/webapps/console/emails/shared.tsx @@ -0,0 +1,160 @@ +import React from "react"; +import { Button, Container, Hr, Text } from "@react-email/components"; +import dayjs from "dayjs"; + +type MetaListProps = { + tableName?: string; + streamsFailed?: string; + incidentStartedAt?: string; + incidentStatus?: string; + recoveredFrom?: string; + queueSize?: number; +}; + +export const MetaList: React.FC = ({ + tableName, + incidentStartedAt, + incidentStatus, + recoveredFrom, + streamsFailed, + queueSize, +}) => { + return ( + + {tableName ? ( + + Table Name: {tableName} +
+
+ ) : ( + <> + )} + {recoveredFrom ? ( + + Recovered From: {recoveredFrom.toLowerCase()} +
+
+ ) : ( + <> + )} + {incidentStatus ? ( + + Last Status: {incidentStatus} +
+
+ ) : ( + <> + )} + {streamsFailed ? ( + + Streams Failed: {streamsFailed} +
+
+ ) : ( + <> + )} + {incidentStartedAt && (Date.now() - new Date(incidentStartedAt).getTime() > 5 * 60 * 1000 || recoveredFrom) ? ( + + Incident Started At: {dayjs(incidentStartedAt).toLocaleString()} +
+
+ ) : ( + <> + )} + {queueSize ? ( + + Events Queue Size: {queueSize.toLocaleString()} +
+
+ ) : ( + <> + )} +
+ ); +}; + +export const CheckJobStatusButton: React.FC<{ url?: string; label?: string; color?: string }> = ({ + url, + label = "Check Job Status", + color = "#a21caf", +}) => { + if (!url) { + return <>; + } + return ( + + + + ); +}; + +export const Details: React.FC<{ details?: string }> = ({ details }) => { + if (!details) { + return <>; + } + return ( + + Details: + + {details.split("\n").map((line, index) => ( + + {line} +
+
+ ))} +
+
+ ); +}; + +export const Footer: React.FC<{ unsubscribeLink?: string }> = ({ unsubscribeLink }) => { + return ( + <> + + Best Regards, +
+ Jitsu Team +
+ + jitsu.com + +
+
+ {unsubscribeLink ? ( + + + Manage your email notification preferences + + + ) : ( + <> + )} + + Jitsu Labs Inc. 2261 Market Street #4109, San Francisco, CA 94114 + + + ); +}; diff --git a/webapps/console/emails/styles.ts b/webapps/console/emails/styles.ts new file mode 100644 index 000000000..31b080ad5 --- /dev/null +++ b/webapps/console/emails/styles.ts @@ -0,0 +1,5 @@ +export const main = { + fontFamily: + '-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif', + backgroundColor: "transparent", +}; diff --git a/webapps/console/lib/api.ts b/webapps/console/lib/api.ts index 83e3b27ab..2c542fa87 100644 --- a/webapps/console/lib/api.ts +++ b/webapps/console/lib/api.ts @@ -1,7 +1,7 @@ import { ZodType } from "zod"; import { NextApiHandler, NextApiRequest, NextApiResponse } from "next"; -import { assertDefined, checkHash, getErrorMessage, requireDefined, tryJson } from "juava"; -import { Session, getServerSession } from "next-auth"; +import { assertDefined, checkHash, checkRawToken, getErrorMessage, isTruish, requireDefined, tryJson } from "juava"; +import { getServerSession, Session } from "next-auth"; import { nextAuthConfig } from "./nextauth.config"; import { SessionUser } from "./schema"; import { db } from "./server/db"; @@ -9,7 +9,25 @@ import { prepareZodObjectForDeserialization, safeParseWithDate } from "./zod"; import { ApiError } from "./shared/errors"; import { getServerLog } from "./server/log"; import { getFirebaseUser, isFirebaseEnabled } from "./server/firebase-server"; -import { ReactNode } from "react"; +import jwt from "jsonwebtoken"; +import { serialize } from "cookie"; +import { + validateJwtToken, + introspectToken, + isJwtToken, + isTokenExpired, + refreshAccessToken, +} from "./server/oidc-token-service"; +import { OidcSessionData } from "./server/oidc-types"; +import { isSecure } from "./server/origin"; +import { + WorkspaceRoleType, + hasPermission, + WorkspacePermissionsType, + WorkspaceRoleWithPermissions, + WorkspaceRolePermissions, +} from "./workspace-roles"; +const adminServiceAccountEmail = "admin-service-account@jitsu.com"; type HandlerOpts = { body?: Req; @@ -44,6 +62,8 @@ export type ApiMethod; query?: ZodType; }; + // indicates that handler uses write method for outputting response content. This is useful for streaming responses. + streaming?: boolean; handle: (ctx: HandlerOpts) => Promise; }; @@ -75,14 +95,171 @@ function parseIfNeeded(o: any): any { export function getAuthBearerToken(req: NextApiRequest): string | undefined { if (req.headers.authorization && req.headers.authorization.toLowerCase().indexOf("bearer ") === 0) { return req.headers.authorization.substring("bearer ".length); + } else if (req.query?.__unsafe_token) { + //very unsafe, but some tools we use can't set headers, so we need to allow this + return req.query.__unsafe_token as string; } return undefined; } -export async function getUser(res: NextApiResponse, req: NextApiRequest): Promise { +function findServiceAccount({ keyId, secret }): SessionUser | undefined { + let tokens: string[] = []; + let checkFunction: (token: string, secret: string) => boolean = () => false; + if (process.env.CONSOLE_AUTH_TOKENS) { + tokens = process.env.CONSOLE_AUTH_TOKENS.split(","); + checkFunction = checkHash; + } else if (process.env.CONSOLE_RAW_AUTH_TOKENS) { + tokens = process.env.CONSOLE_RAW_AUTH_TOKENS.split(","); + checkFunction = checkRawToken; + } + if (tokens.length > 0) { + for (const tokenHashOrPlain of tokens) { + if (checkFunction(tokenHashOrPlain, secret)) { + return { + internalId: adminServiceAccountEmail, + externalUsername: adminServiceAccountEmail, + externalId: adminServiceAccountEmail, + loginProvider: "admin/token", + email: adminServiceAccountEmail, + name: adminServiceAccountEmail, + }; + } + } + } +} + +async function getUserFromOidcSession(req: NextApiRequest, res?: NextApiResponse): Promise { + const dynamicOidcEnabled = isTruish(process.env.DYNAMIC_OIDC_ENABLED); + if (!dynamicOidcEnabled) { + return undefined; + } + + // Check for OIDC session cookie (for API requests from OIDC-authenticated users) + const oidcSessionCookie = req.cookies?.["oidc-session"]; + if (!oidcSessionCookie) { + return undefined; + } + + try { + // Verify the OIDC session token + let sessionData: OidcSessionData = jwt.verify(oidcSessionCookie, nextAuthConfig.secret) as OidcSessionData; + let tokensRefreshed = false; + + // Validate OIDC tokens if present + const providerId = sessionData.providerId; + if (sessionData.tokens && providerId) { + const { accessToken, refreshToken, expiresAt } = sessionData.tokens; + + // Check if access token is expired + if (isTokenExpired(expiresAt)) { + log.atInfo().log("Access token expired in API request, attempting refresh", { userId: sessionData.userId }); + + // Try to refresh the token if we have a refresh token + if (refreshToken) { + const refreshResult = await refreshAccessToken(refreshToken, providerId); + + if (refreshResult.success && refreshResult.tokens) { + log.atInfo().log("Successfully refreshed access token in API request", { userId: sessionData.userId }); + + // Update session data with new tokens + sessionData = { + ...sessionData, + timestamp: Date.now(), + tokens: refreshResult.tokens, + }; + tokensRefreshed = true; + } else { + log.atWarn().log("Failed to refresh access token in API request", { + userId: sessionData.userId, + error: refreshResult.error, + }); + return undefined; // Cannot refresh, need re-authentication + } + } else { + log.atWarn().log("No refresh token available for expired access token", { userId: sessionData.userId }); + return undefined; // No refresh token, need re-authentication + } + } + + // Validate the (possibly refreshed) access token + const currentAccessToken = sessionData.tokens!.accessToken; + let tokenValid = false; + + if (isJwtToken(currentAccessToken)) { + // Validate JWT token using JWKS + const validation = await validateJwtToken(currentAccessToken, providerId); + tokenValid = validation.valid; + + if (!tokenValid) { + log.atWarn().log("JWT token validation failed in API request", { + userId: sessionData.userId, + error: validation.error, + }); + } + } else { + // Use token introspection for opaque tokens + const introspection = await introspectToken(currentAccessToken, providerId); + tokenValid = introspection.valid; + + if (!tokenValid) { + log.atWarn().log("Token introspection failed in API request", { + userId: sessionData.userId, + error: introspection.error, + }); + } + } + + if (!tokenValid) { + log.atWarn().log("OIDC token validation failed in API request", { userId: sessionData.userId }); + return undefined; // Invalid token, need re-authentication + } + + // If tokens were refreshed, update the session cookie + if (tokensRefreshed && res) { + const newSessionToken = jwt.sign(sessionData, nextAuthConfig.secret); + + res.setHeader( + "Set-Cookie", + serialize("oidc-session", newSessionToken, { + httpOnly: true, + secure: isSecure(req), + sameSite: "strict", + maxAge: 7 * 24 * 60 * 60, // 7 days + path: "/", + }) + ); + + log.atInfo().log("Updated session cookie with refreshed tokens", { userId: sessionData.userId }); + } + } + + // Convert OIDC session to SessionUser format + return { + internalId: sessionData.userId, + externalUsername: sessionData.email || sessionData.name || "unknown", + externalId: sessionData.externalId, + loginProvider: sessionData.loginProvider, + email: sessionData.email || "unknown", + name: sessionData.name || "Unknown", + }; + } catch (error) { + log.atWarn().withCause(error).log("Invalid OIDC session cookie"); + return undefined; + } +} + +export async function getUser( + res: NextApiResponse, + req: NextApiRequest, + checkRevoked?: boolean +): Promise { const bearerToken = getAuthBearerToken(req); if (bearerToken) { const [keyId, secret] = bearerToken.split(":"); + const serviceAccount = findServiceAccount({ keyId, secret }); + if (serviceAccount) { + return serviceAccount; + } if (keyId && secret) { //auth based on an API key const token = await db.prisma().userApiToken.findUnique({ where: { id: keyId } }); @@ -96,6 +273,7 @@ export async function getUser(res: NextApiResponse, req: NextApiRequest): Promis await db.prisma().userProfile.findUnique({ where: { id: token.userId } }), `Can't find user ${token.userId} for API key ${keyId}` ); + await db.prisma().userApiToken.update({ where: { id: keyId }, data: { lastUsed: new Date() } }); return { internalId: user.id, externalUsername: user.externalUsername, @@ -107,8 +285,13 @@ export async function getUser(res: NextApiResponse, req: NextApiRequest): Promis } } + const oidcUser = await getUserFromOidcSession(req, res); + if (oidcUser) { + return oidcUser; + } + if (isFirebaseEnabled()) { - return await getFirebaseUser(req); + return await getFirebaseUser(req, checkRevoked); } const session = await getServerSession(req, res, nextAuthConfig); return session ? getUserFromSession(session) : undefined; @@ -135,8 +318,7 @@ export function nextJsApiHandler(api: Api): NextApiHandler { } let body = undefined; if (req.body && handler.types?.body) { - const parseResult = safeParseWithDate( - handler.types?.body, + const parseResult = handler.types?.body.safeParse( req.body ? prepareZodObjectForDeserialization(parseIfNeeded(req.body)) : undefined ); @@ -149,7 +331,7 @@ export function nextJsApiHandler(api: Api): NextApiHandler { body = parseResult.data; } else if (req.body) { try { - body = JSON.parse(req.body); + body = parseIfNeeded(req.body); } catch (e) { throw new ApiError(`Body ${req.method} ${req.url} is not a JSON object: ${getErrorMessage(e)}`, { body: req.body, @@ -170,21 +352,31 @@ export function nextJsApiHandler(api: Api): NextApiHandler { } const result = await handler.handle({ body, req, res, query, user: currentUser as any }); + if (handler.streaming) { + //we cannot do anything with the result, it is the responsibility of the handler to write the response + return; + } if (handler.types?.result) { const parseResult = handler.types?.result.safeParse(result); if (!parseResult.success) { log .atDebug() .log( - `Zod mismatch. Obj: ${JSON.stringify(result, null, 2)}. Zod error: ${JSON.stringify(parseResult.error)}` + `Api method zod mismatch at ${req.method} ${req.url}. Obj: ${JSON.stringify( + result, + null, + 2 + )}. Zod error: ${JSON.stringify(parseResult.error)}` ); throw new ApiError(`Response for ${req.method} ${req.url} doesn't match required schema`, { zodError: parseResult.error, }); } - res.status(200).json(parseResult.data); + //do not set explicit 200 status here. If the status has been set by the handler, we should respect it. There's + //no way to check if the status has been set. If status is not set here and in handler, Next.js will set 200 by default. + res.json(parseResult.data); } else { - res.status(200).json(result || { success: true }); + res.json(result || { success: true }); } } catch (e: any) { if (isApiError(e)) { @@ -224,8 +416,50 @@ function stackToArray(stack?: string) { const lines = stack.split("\n"); return lines.length > 0 ? lines.map(s => s.trim()) : undefined; } +export async function verifyAdmin(user: SessionUser) { + if (user.internalId === adminServiceAccountEmail && user.loginProvider === "admin/token") { + return; + } + const userId = requireDefined(user.internalId, `internalId is not defined`); + if ((await db.prisma().userProfile.findFirst({ where: { id: user.internalId } }))?.admin) { + return; + } + throw new ApiError(`User ${userId} is not an admin`, { status: 403 }); +} + +export function looksLikeCuid(id: string) { + return id.length === 25 && id.charAt(0) === "c"; +} + +export async function getWorkspace(workspaceId: string | undefined) { + return requireDefined( + await db.prisma().workspace.findFirst({ + where: { + OR: [ + { + id: workspaceId, + }, + { + slug: workspaceId, + }, + ], + deleted: false, + }, + }), + `Workspace ${workspaceId} not found` + ); +} export async function verifyAccess(user: SessionUser, workspaceId: string) { + if (user.internalId === adminServiceAccountEmail && user.loginProvider === "admin/token") { + return; + } + if (!looksLikeCuid(workspaceId)) { + const w = await db.prisma().workspace.findFirst({ where: { slug: workspaceId } }); + if (w) { + workspaceId = w.id; + } + } const userId = requireDefined(user.internalId, `internalId is not defined`); if ((await db.prisma().workspaceAccess.count({ where: { userId, workspaceId } })) === 0) { if ((await db.prisma().userProfile.findFirst({ where: { id: user.internalId } }))?.admin) { @@ -239,6 +473,60 @@ export async function verifyAccess(user: SessionUser, workspaceId: string) { } } +export async function verifyAccessWithRole( + user: SessionUser, + workspaceId: string, + requiredPermission: WorkspacePermissionsType +): Promise { + if (user.internalId === adminServiceAccountEmail && user.loginProvider === "admin/token") { + return { + role: "owner", + ...WorkspaceRolePermissions["owner"], + }; + } + + if (!looksLikeCuid(workspaceId)) { + const w = await db.prisma().workspace.findFirst({ where: { slug: workspaceId } }); + if (w) { + workspaceId = w.id; + } + } + + const userId = requireDefined(user.internalId, `internalId is not defined`); + const access = await db.prisma().workspaceAccess.findFirst({ + where: { userId, workspaceId }, + }); + + if (!access) { + // Check if user is admin + if ((await db.prisma().userProfile.findFirst({ where: { id: user.internalId } }))?.admin) { + return { + role: "owner", + ...WorkspaceRolePermissions["owner"], + }; + } + throw new ApiError( + `User ${userId} doesn't have access to workspace ${workspaceId}`, + { workspaceId, userId }, + { status: 403 } + ); + } + + const role = (access.role || "owner") as WorkspaceRoleType; + + if (!hasPermission(role, requiredPermission)) { + throw new ApiError( + `User ${userId} doesn't have permission '${requiredPermission}' in workspace ${workspaceId}. Required role: owner or editor`, + { workspaceId, userId, role, requiredPermission }, + { status: 403 } + ); + } + + return { + role, + ...WorkspaceRolePermissions[role], + }; +} //new type-safe route builder export type RouteBuilderBase = { @@ -248,11 +536,12 @@ export type RouteBuilderBase = { ResultZodType extends ZodType = any, RequireAuth extends undefined | boolean = false >(spec: { - description?: ReactNode; + description?: string; query?: QueryZodType; body?: BodyZodType; result?: ResultZodType; auth?: RequireAuth; + streaming?: boolean; }) => { handler: ( handler: (params: { @@ -272,13 +561,14 @@ export function createRoute(): RouteBuilder { const legacyApiInstance: Api = {}; const builder: any = {}; for (const method of httpMethods) { - builder[method] = ({ query, body, result, auth }) => { + builder[method] = ({ query, body, result, auth, streaming }) => { return { handler: handler => { legacyApiInstance[method] = { auth: !!auth, types: { query, body, result }, handle: handler, + streaming: streaming, }; return builder; }, diff --git a/webapps/console/lib/auth-redirect.ts b/webapps/console/lib/auth-redirect.ts new file mode 100644 index 000000000..c38268ba7 --- /dev/null +++ b/webapps/console/lib/auth-redirect.ts @@ -0,0 +1,96 @@ +import { NextRouter } from "next/router"; +import { getServerLog } from "./server/log"; + +const log = getServerLog("auth-redirect"); + +/** + * Utility functions for handling return URLs during authentication flows + */ + +/** + * Captures the current URL and returns it as a callback URL for authentication + * @param router Next.js router instance + * @returns callback URL to redirect to after authentication + */ +export function captureReturnUrl(router: NextRouter): string { + // Don't redirect back to auth-related pages + const authPaths = ["/signin", "/signup", "/reset-password"]; + const currentPath = router.asPath; + + if (authPaths.some(path => currentPath.startsWith(path))) { + return "/"; // Redirect to home if on auth page + } + + // Use the full current path including query parameters + return currentPath; +} + +/** + * Extracts return URL from various sources (query params, headers, etc.) + * @param req Next.js API request + * @returns return URL or null if none found + */ +export function extractReturnUrl(req: any): string | null { + // Check for explicit callbackUrl parameter + if (req.query?.callbackUrl) { + return req.query.callbackUrl as string; + } + return null; +} + +/** + * Validates and sanitizes a return URL + * we do not allow absolute URLs to prevent open redirects + * @param returnUrl URL to validate + * @returns sanitized URL or null if invalid + */ +export function validateReturnUrl(returnUrl: string): string | undefined { + if (!returnUrl) { + return; + } + + // Allow relative URLs + if (returnUrl.startsWith("/") && !returnUrl.startsWith("//")) { + // Ensure it's not an auth page + const authPaths = ["/signin", "/signup", "/reset-password"]; + if (authPaths.some(path => returnUrl.startsWith(path))) { + return "/"; + } + return returnUrl; + } +} + +/** + * Performs a safe redirect to the return URL + * @param router Next.js router instance + * @param returnUrl URL to redirect to + */ +export function safeRedirect(router: NextRouter, returnUrl: string | null): void { + const validatedUrl = validateReturnUrl(returnUrl || "/"); + const finalUrl = validatedUrl || "/"; + + log.atInfo().log(`Redirecting to: ${finalUrl}`); + + router.push(finalUrl); +} + +/** + * Adds return URL parameter to a signin URL + * @param signinUrl Base signin URL + * @param returnUrl Return URL to preserve + * @returns signin URL with return URL parameter + */ +export function addReturnUrlToSignin(signinUrl: string, returnUrl: string | null): string { + if (!returnUrl) { + return signinUrl; + } + + const validatedUrl = validateReturnUrl(returnUrl); + if (!validatedUrl || validatedUrl === "/") { + return signinUrl; + } + + const url = new URL(signinUrl, window.location.origin); + url.searchParams.set("callbackUrl", validatedUrl); + return url.toString(); +} diff --git a/webapps/console/lib/context.tsx b/webapps/console/lib/context.tsx index 5a6b856b6..0f411ac3a 100644 --- a/webapps/console/lib/context.tsx +++ b/webapps/console/lib/context.tsx @@ -1,19 +1,30 @@ -import { createContext, PropsWithChildren, useContext } from "react"; +import React, { createContext, PropsWithChildren, useContext } from "react"; import { z } from "zod"; import { AppConfig, ContextApiResponse } from "./schema"; import { WorkspaceDbModel } from "../prisma/schema"; -import { omit } from "lodash"; +import omit from "lodash/omit"; +import { Analytics } from "../pages/_app"; +import type { WorkspaceRoleWithPermissions } from "./workspace-roles"; -export type WorkspaceContext = z.infer; +export type WorkspaceContext = z.infer & { + slugOrId: string; + oidcLoginGroups?: any[]; +}; const WorkspaceContext0 = createContext(null); +const WorkspaceRoleContext0 = createContext(null); -export const WorkspaceContextProvider: React.FC<{ workspace: WorkspaceContext; children: React.ReactNode }> = ({ - children, - workspace, -}) => { +export const WorkspaceContextProvider: React.FC<{ + workspace: WorkspaceContext; + userRole: WorkspaceRoleWithPermissions; + children: React.ReactNode; +}> = ({ children, workspace, userRole }) => { const Context = WorkspaceContext0; - return {children}; + return ( + + {children} + + ); }; export function useWorkspace(): WorkspaceContext { @@ -63,7 +74,12 @@ const UserContext0 = createContext(null!); export const UserContextProvider: React.FC> = ({ children, ...props }) => { const Context = UserContext0; - return {children}; + return ( + + {props.user && } + {children} + + ); }; export function useUser(): ContextApiResponse["user"] { @@ -74,10 +90,29 @@ export function useUser(): ContextApiResponse["user"] { return props.user; } +export function useWorkspaceRole(): WorkspaceRoleWithPermissions { + const context = useContext(WorkspaceRoleContext0); + if (!context) { + return { + role: "analyst", + editEntities: false, + deleteEntities: false, + manageUsers: false, + readEntities: true, + }; + } + return context; +} + +export function useUserSafe(): ContextApiResponse["user"] | undefined | null { + const props = useContext(UserContext0); + return props?.user; +} + export function useUserSessionControls(): { logout: () => Promise } { const props = useContext(UserContext0); if (!props) { - throw new Error(`useUserSessionControls() should be called inside `); + return { logout: async () => {} }; } return omit(props, "user"); } diff --git a/webapps/console/lib/domains.ts b/webapps/console/lib/domains.ts index 205cfb0db..278f1a898 100644 --- a/webapps/console/lib/domains.ts +++ b/webapps/console/lib/domains.ts @@ -1,11 +1,13 @@ import { NextApiRequest } from "next"; -import { getErrorMessage, getLog } from "juava"; -import { MergeExclusive, Simplify } from "type-fest"; +import { getErrorMessage } from "juava"; import { IngestType } from "@jitsu/protocols/async-request"; +import { AnalyticsServerEvent } from "@jitsu/protocols/analytics"; +import { getServerLog } from "./server/log"; +import { mainDataDomain } from "./server/data-domains"; export type HttpProtocolVariant = "https" | "http"; -export const log = getLog("domains"); +const log = getServerLog("domains"); export type PublicEndpoint = { //always without port @@ -17,16 +19,7 @@ export type PublicEndpoint = { baseUrl: string; }; -export function getDataDomain({ hostname }: PublicEndpoint): string | undefined { - if (process.env.DATA_DOMAIN) { - return process.env.DATA_DOMAIN; - } - return undefined; -} - -export type StreamLocator = Simplify< - MergeExclusive, { writeKey: string; keyType: IngestType }> ->; +export type StreamCredentials = { domain?: string; slug?: string; writeKey?: string; ingestType: IngestType }; /** * Example: @@ -35,18 +28,26 @@ export type StreamLocator = Simplify< * @param req * @param keyType type of a key */ -export function getDataLocator(req: NextApiRequest, keyType: IngestType): StreamLocator { +export function getDataLocator( + req: NextApiRequest, + ingestType: IngestType, + event: AnalyticsServerEvent +): StreamCredentials { const requestEndpoint = getReqEndpoint(req); - const [dataHost] = getDataDomain(requestEndpoint)?.split(":") || [undefined]; //ignore port, port can be used only in dev env - if (requestEndpoint.hostname === dataHost) { - throw new Error(`Cannot get data slug from data hostname ${requestEndpoint.hostname}`); + const [dataHost] = mainDataDomain?.split(":") || [undefined]; //ignore port, port can be used only in dev env + const loc: Partial = { ingestType }; + if (req.headers["authorization"]) { + const auth = Buffer.from(req.headers["authorization"].replace("Basic ", ""), "base64").toString("utf-8"); + loc.writeKey = auth; } else if (req.headers["x-write-key"]) { - return { writeKey: req.headers["x-write-key"] as string, keyType }; - } else if (dataHost && requestEndpoint.hostname.endsWith(`.${dataHost}`)) { - return { slug: requestEndpoint.hostname.replace(`.${dataHost}`, "") }; + loc.writeKey = req.headers["x-write-key"] as string; + } + if (dataHost && requestEndpoint.hostname.endsWith(`.${dataHost}`)) { + loc.slug = requestEndpoint.hostname.replace(`.${dataHost}`, ""); } else { - return { domain: requestEndpoint.hostname }; + loc.domain = requestEndpoint.hostname; } + return loc as StreamCredentials; } export function getDefaultPort(protocol: HttpProtocolVariant) { @@ -54,7 +55,7 @@ export function getDefaultPort(protocol: HttpProtocolVariant) { } export function getAppEndpoint(req: NextApiRequest): PublicEndpoint { - let envUrl = process.env.PUBLIC_URL || process.env.VERCEL_URL; + let envUrl = process.env.JITSU_PUBLIC_URL || process.env.VERCEL_URL; if (envUrl) { if (envUrl.indexOf("https://") !== 0 && envUrl.indexOf("http://") !== 0) { envUrl = "https://" + envUrl; @@ -77,7 +78,7 @@ export function getAppEndpoint(req: NextApiRequest): PublicEndpoint { .atError() .withCause(e) .log( - `Can't parse url ${envUrl}. PUBLIC_URL=${process.env.PUBLIC_URL}. VERCEL_URL=${ + `Can't parse url ${envUrl}. JITSU_PUBLIC_URL=${process.env.JITSU_PUBLIC_URL}. VERCEL_URL=${ process.env.VERCEL_URL }: ${getErrorMessage(e)}` ); diff --git a/webapps/console/lib/ee-client.ts b/webapps/console/lib/ee-client.ts deleted file mode 100644 index e76fe0911..000000000 --- a/webapps/console/lib/ee-client.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { get } from "./useApi"; -import { DomainStatus } from "./server/ee"; -import * as auth from "firebase/auth"; - -export type ClassicProjectStatus = { - ok: boolean; - uid: string | null; - project: string | null; - name: string | null; - active: boolean; -}; - -export interface EeClient { - attachDomain(domain: string): Promise; - checkClassicProject(): Promise; - createCustomToken(): Promise; -} - -type CachedToken = { - token: string; - expiresAt: Date; -}; - -function removeDoubleSlashes(s: string) { - return s.replace(/([^:]\/)\/+/g, "$1"); -} - -export function getEeClient(host: string, workspaceId: string): EeClient { - let cachedToken: CachedToken | undefined = undefined; - const refreshTokenIfNeeded = async () => { - //get short-lived (10m) - if (!cachedToken || cachedToken.expiresAt < new Date()) { - const refreshed = await get(`/api/ee/jwt`, { query: { workspaceId } }); - cachedToken = { - token: refreshed.jwt, - expiresAt: new Date(refreshed.expiresAt), - }; - } - return cachedToken; - }; - return { - attachDomain: async domain => { - cachedToken = await refreshTokenIfNeeded(); - return await get(removeDoubleSlashes(`${host}/api/domain`), { - query: { domain }, - headers: { - Authorization: `Bearer ${cachedToken.token}`, - }, - }); - }, - checkClassicProject: async () => { - const fbToken = await auth.getAuth().currentUser?.getIdToken(); - return await get(removeDoubleSlashes(`${host}/api/is-active`), { - credentials: "include", - cache: "default", - mode: "cors", - headers: { - Authorization: `Bearer ${fbToken}`, - }, - }); - }, - createCustomToken: async () => { - const fbToken = await auth.getAuth().currentUser?.getIdToken(); - const res = await get(removeDoubleSlashes(`${host}/api/custom-token`), { - headers: { - Authorization: `Bearer ${fbToken}`, - }, - }); - return res.customToken; - }, - }; -} diff --git a/webapps/console/lib/firebase-client.tsx b/webapps/console/lib/firebase-client.tsx index 1086a00fd..0d65527ea 100644 --- a/webapps/console/lib/firebase-client.tsx +++ b/webapps/console/lib/firebase-client.tsx @@ -3,6 +3,7 @@ import { initializeApp } from "firebase/app"; import * as auth from "firebase/auth"; import { AppConfig, ContextApiResponse } from "./schema"; import { getLog, randomId, requireDefined, rpc } from "juava"; +import { useJitsu } from "@jitsu/jitsu-react"; type FirebaseClientSettings = Record; export type FirebaseProviderInstance = @@ -106,6 +107,7 @@ async function getUserFromFirebase(currentUser: auth.User): Promise { + throw new Error("Firebase auth is not enabled"); + }, + signInWith: async () => { + throw new Error("Firebase auth is not enabled"); + }, + signOut: async () => { + throw new Error("Firebase auth is not enabled"); + }, + resetPassword: async () => { + throw new Error("Firebase auth is not enabled"); + }, + resolveUser: () => { + throw new Error("Firebase auth is not enabled"); + }, + }; } const a = getFirebaseAuth(config); @@ -128,7 +147,9 @@ export function useFirebaseSession(): FirebaseSession { } else { user = await a.signInWithPopup(a.getAuth(), new auth.GoogleAuthProvider()); } - await getUserFromFirebase(a.getAuth().currentUser!); + const firebaseUser = await getUserFromFirebase(a.getAuth().currentUser!); + await analytics.identify(firebaseUser.internalId, { email: firebaseUser.email, name: firebaseUser.name }); + await analytics.track("login"); } catch (e) { log.atError().withCause(e).log(`Can't sign in with ${type}`); throw e; @@ -161,7 +182,7 @@ export function useFirebaseSession(): FirebaseSession { }; }, async signOut(): Promise { - await a.signOut(a.getAuth()); + await firebaseSignOut(); }, //user: () => (currentUser ? getUserFromFirebase(currentUser) : undefined), async signIn(username: string, password): Promise { diff --git a/webapps/console/lib/modal.tsx b/webapps/console/lib/modal.tsx index b841ef5a2..4433dff02 100644 --- a/webapps/console/lib/modal.tsx +++ b/webapps/console/lib/modal.tsx @@ -49,7 +49,7 @@ const PromptModal: React.FC<{ footer={<>} destroyOnClose={true} onCancel={() => handleResult(null)} - maskStyle={{ backdropFilter: "blur(10px)" }} + styles={{ mask: { backdropFilter: "blur(10px)" } }} >

{question || "Dummy question. Would you mind providing an answer?"}

@@ -115,8 +115,6 @@ export function PromptContextProvider({ children }) { const [okText, setOkText] = useState(null); const [initialValue, setInitialValue] = useState(); - log.atDebug().log(`render - `); - return ( {context => { - log.atDebug().log(`functional child of - . Context:`, context); return ( s.trim().split(":")) - .find(([user]) => user.toLowerCase() === credentials.username.toLowerCase()); - if (!userRecord) { - log.atWarn().log(`Failed attempt to login with ${credentials.username}: no such user`); - return null; + const username = credentials.username; + if (!username) { + throw new ApiError("Username is not defined"); } - if (!checkHash(userRecord[1], credentials.password)) { - log.atWarn().log(`Failed attempt to login with ${credentials.username}: invalid password`); - return null; + const user = await db.prisma().userProfile.findFirst({ where: { email: username }, include: { password: true } }); + if (!user) { + log.atDebug().log(`Attempt to login with unknown user: ${username}`); + const profileCount = await db.prisma().userProfile.count(); + if (profileCount === 0 && process.env.SEED_USER_EMAIL && process.env.SEED_USER_PASSWORD) { + log.atDebug().log(`There're no user profiles in DB, checking ${username} against seed user config`); + if (process.env.SEED_USER_EMAIL === username && process.env.SEED_USER_PASSWORD === credentials.password) { + const userId = toId(process.env.SEED_USER_EMAIL); + log.atDebug().log(`Adding a seed admin user with id ${userId} and email ${username}`); + await db.prisma().userProfile.create({ + data: { + id: userId, + email: username, + name: username, + externalId: userId, + loginProvider: "credentials", + admin: true, + password: { + create: { + hash: createHash(credentials.password), + changeAtNextLogin: true, + }, + }, + }, + }); + return { + id: userId, + externalId: userId, + email: process.env.SEED_USER_EMAIL, + name: process.env.SEED_USER_EMAIL, + }; + } else { + log.atWarn().log(`Attempt to login with unknown user: ${username} and invalid password`); + } + } + } else if (user.password && checkHash(user.password.hash, credentials.password)) { + log.atDebug().log(`User ${username} logged in successfully with password`); + return { + id: user.id, + externalId: user.externalId, + email: user.email, + name: user.name, + }; } - const userId = "id-" + credentials.username.toLowerCase().replace("@", "-").replace(".", "-"); - return { - id: userId, - externalId: userId, - email: credentials.username.toLowerCase(), - name: credentials.username.toLowerCase(), - }; + log.atDebug().log(`Unsuccessful login attempt: user ${username} exists, but password is invalid`); + return null; }, credentials: { username: { label: "Email", type: "text" }, @@ -68,17 +104,21 @@ export async function getOrCreateUser(opts: { loginProvider: string; name?: string; email: string; - admin?: boolean; -}): Promise { - const { externalId, loginProvider, email, name = email, admin = false } = opts; - log.atDebug().log(`Signing in user ${JSON.stringify(opts)}`); + // we only need this for product analytics, so it's optional + req?: NextApiRequest; +}): Promise { + const { externalId, loginProvider, email, name = email } = opts; let user = await db.prisma().userProfile.findFirst({ where: { externalId, loginProvider } }); if (!user) { if (process.env.DISABLE_SIGNUP === "true" || process.env.DISABLE_SIGNUP === "1") { throw new ApiError("Sign up is disabled", { code: "signup-disabled" }); } + //first user is admin + const admin = !(await db.prisma().userProfile.count()); user = await db.prisma().userProfile.create({ data: { + //we need this to be consistent with id generated by .authorize() call + id: loginProvider === "credentials" ? externalId : undefined, email, name, externalId: externalId, @@ -86,6 +126,11 @@ export async function getOrCreateUser(opts: { admin, }, }); + await withProductAnalytics(p => p.track("user_created"), { + user: { email, name, internalId: user.id, externalId, loginProvider }, + req: opts.req, + }); + await onUserCreated({ email, name }); } else if (user.name !== name || user.email !== email) { await db.prisma().userProfile.update({ where: { id: user.id }, data: { name, email } }); } @@ -96,20 +141,22 @@ function generateSecret(base: (string | undefined)[]) { const hash = crypto.createHash("sha256"); hash.update(base.map(s => s || "empty").join(":")); const secretKey = hash.digest("hex"); - getLog().atInfo().log("Using autogenerated JWT key", secretKey); + log.atInfo().log("Using autogenerated JWT key", secretKey); return secretKey; } export const nextAuthConfig: NextAuthOptions = { // Configure one or more authentication providers - providers: [githubProvider, googleProvider, credentialsProvider].filter(provider => !!provider) as any, + providers: [githubProvider, oidcProvider, credentialsProvider].filter(provider => !!provider) as any, pages: { error: "/error/auth", // Error code passed in query string as ?error= + signIn: "/signin", // Displays signin buttons }, secret: process.env.JWT_SECRET || generateSecret([ + "v2", process.env.GITHUB_CLIENT_ID, process.env.GOOGLE_CLIENT_ID, process.env.DATABASE_URL, @@ -117,7 +164,7 @@ export const nextAuthConfig: NextAuthOptions = { ]), callbacks: { jwt: async props => { - const loginProvider = (props.account?.provider || "credentials") as string; + const loginProvider = (props.account?.provider || props.token.loginProvider || "credentials") as string; const externalId = requireDefined(props.token.sub, `JWT token .sub is not defined`); const email = requireDefined(props.token.email, `JWT token .email is not defined`); const user = await getOrCreateUser({ @@ -125,8 +172,6 @@ export const nextAuthConfig: NextAuthOptions = { loginProvider, email, name: props.token.name || email, - //credentials login are only for test only, so we make all of them admins - admin: false, }); return { internalId: user.id, diff --git a/webapps/console/lib/oidc.ts b/webapps/console/lib/oidc.ts new file mode 100644 index 000000000..127933e79 --- /dev/null +++ b/webapps/console/lib/oidc.ts @@ -0,0 +1,68 @@ +import type { OAuthConfig, OAuthUserConfig } from "next-auth/providers/oauth"; +import { ApiError } from "./shared/errors"; + +export interface OIDCProfile extends Record { + sub: string; + name: string; + preferred_username: string; + nickname: string; + email: string; + picture: string; +} + +export type OIDCConfig

= OAuthUserConfig

& Required, "issuer">>; + +/** + * Creates an OAuth configuration for an OpenID Connect (OIDC) Discovery compliant provider. + * + * @template P - The type of the profile, extending `OIDCProfile`. + * + * @param {OIDCConfig

} options - The user configuration options for OAuth authentication. + * + * @returns {OAuthConfig

} - An OIDC provider NextAuthJS valid configuration. + * + * @throws {ApiError} - Throws an error if the required fields `issuer`, `clientId`, or `clientSecret` + * are not provided in the options parameter. + * + * @description + * Initializes an OAuth configuration object for a generic OIDC provider that is compliant with the OIDC Discovery. It requires + * the `issuer` (the issuer domain in valid URL format), `clientId`, and `clientSecret` fields in the options. This configuration + * includes default settings for handling the PKCE and state checks and provides + * a profile extraction mechanism. + * + * The well-known configuration endpoint for the provider is automatically set based on the issuer, and + * the default authorization request includes scopes for OpenID, email, and profile information. + */ +export function OIDCProvider

(options: OIDCConfig

): OAuthConfig

{ + if (!options.issuer || !options.clientId || !options.clientSecret) { + throw new ApiError("Malformed OIDC config: issuer, clientId, and clientSecret are required"); + } + + return { + id: "oidc", + name: "OIDC", + wellKnown: `${options.issuer}/.well-known/openid-configuration`, + type: "oauth", + authorization: { params: { scope: "openid email profile" } }, + checks: ["pkce", "state"], + idToken: true, + profile(profile) { + return { + id: profile.sub, + name: profile.name ?? profile.preferred_username ?? profile.nickname, + email: profile.email, + image: profile.picture, + }; + }, + options, + }; +} + +export function ParseJSONConfigFromEnv

(env: string): OIDCConfig

| undefined { + try { + return env && env != '""' ? (JSON.parse(env) as OIDCConfig

) : undefined; + } catch (error: unknown) { + console.error("Failed to parse JSON config from env", error); + return undefined; + } +} diff --git a/webapps/console/lib/previous-route.tsx b/webapps/console/lib/previous-route.tsx new file mode 100644 index 000000000..84e4de322 --- /dev/null +++ b/webapps/console/lib/previous-route.tsx @@ -0,0 +1,28 @@ +import { useRouter } from "next/router"; +import { useEffect, useRef, useState, createContext, FC, useContext } from "react"; + +const PreviousRouteContext = createContext(null); + +export const PreviousRouteContextProvider: FC<{ children: React.ReactNode }> = ({ children }) => { + const router = useRouter(); + const [previousRoute, setPreviousRoute] = useState(null); + const routeRef = useRef(router.asPath); + + useEffect(() => { + const handleRouteChange = (url: string) => { + setPreviousRoute(routeRef.current); + routeRef.current = url; + }; + + router.events.on("routeChangeStart", handleRouteChange); + return () => { + router.events.off("routeChangeStart", handleRouteChange); + }; + }, [router]); + + return {children}; +}; + +export function usePreviousRoute() { + return useContext(PreviousRouteContext); +} diff --git a/webapps/console/lib/queries.ts b/webapps/console/lib/queries.ts deleted file mode 100644 index 43eec0783..000000000 --- a/webapps/console/lib/queries.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { get, getConfigApi } from "./useApi"; -import { DestinationConfig, ServiceConfig, StreamConfig } from "./schema"; -import { useQuery } from "@tanstack/react-query"; - -export function linksQuery(workspaceId: string, type: "push" | "sync" = "push") { - return async () => { - return await Promise.all([ - type === "sync" - ? getConfigApi(workspaceId, "service").list() - : getConfigApi(workspaceId, "stream").list(), - getConfigApi(workspaceId, "destination").list(), - get(`/api/${workspaceId}/config/link`).then(res => - res.links.filter(l => l.type === type || (type === "push" && !l.type)) - ), - ]); - }; -} - -export function useLinksQuery( - workspaceId: string, - type: "push" | "sync" = "push", - options?: { - cacheTime?: number; - retry?: boolean; - } -) { - return useQuery( - ["links", workspaceId, type], - linksQuery(workspaceId, type), - options - ? { - cacheTime: options.cacheTime, - retry: options.retry, - } - : {} - ); -} diff --git a/webapps/console/lib/schema/config-objects.ts b/webapps/console/lib/schema/config-objects.ts index 0af24ddcf..12de49c78 100644 --- a/webapps/console/lib/schema/config-objects.ts +++ b/webapps/console/lib/schema/config-objects.ts @@ -1,11 +1,24 @@ import { coreDestinationsMap } from "./destinations"; import { safeParseWithDate } from "../zod"; import { ApiError } from "../shared/errors"; -import { ApiKey, ConfigObjectType, DestinationConfig, FunctionConfig, ServiceConfig, StreamConfig } from "./index"; -import { assertDefined, createHash, requireDefined } from "juava"; -import { isDomainAvailable } from "../server/custom-domains"; +import { + ApiKey, + ConfigObjectType, + ConnectorImageConfig, + DestinationConfig, + FunctionConfig, + MiscEntity, + NotificationChannel, + ServiceConfig, + StreamConfig, + WorkspaceDomain, +} from "./index"; +import { assertDefined, createHash, deepMerge, requireDefined } from "juava"; +import { checkDomain, checkOrAddToIngress, isDomainAvailable } from "../server/custom-domains"; import { ZodType, ZodTypeDef } from "zod"; import { getServerLog } from "../server/log"; +import { getWildcardDomains } from "../../pages/api/[workspaceId]/domain-check"; +import { getDestinationSecretPaths, getServiceSecretPaths, maskSecrets, removeMaskedValues } from "./secrets"; const log = getServerLog("config-objects"); @@ -58,10 +71,10 @@ export const getConfigObjectType: (type: string) => Required = inputFilter: async function (val: any) { return val; }, - merge: function (original: any, patch: Partial) { - return { ...original, ...patch }; + merge: async function (original: any, patch: Partial) { + return deepMerge(original, patch); }, - outputFilter: function (original: any) { + outputFilter: async function (original: any) { return original; }, }; @@ -72,25 +85,34 @@ export const getConfigObjectType: (type: string) => Required = const configObjectTypes: Record = { destination: { schema: DestinationConfig, - outputFilter: (obj: DestinationConfig) => { + outputFilter: async (obj: DestinationConfig) => { const newObject = { ...obj }; if (newObject.provisioned) { delete (newObject as any).credentials; + } else { + // Mask secrets for non-provisioned destinations + const secretPaths = getDestinationSecretPaths(obj.destinationType); + return maskSecrets(newObject, secretPaths); } return newObject; }, - merge(original: DestinationConfig, patch: Partial): any { + merge: async (original: DestinationConfig, patch: Partial): Promise => { if (patch.provisioned) { throw new ApiError(`Can't set destination to provisioned destination through API (${original.id})`); } - return { ...original, ...patch }; + // Remove masked values before merge + const secretPaths = getDestinationSecretPaths(original.destinationType); + const cleanedPatch = removeMaskedValues(patch, secretPaths); + return deepMerge(original, cleanedPatch); }, inputFilter: async (obj: DestinationConfig, context) => { if (context === "create" && obj.provisioned) { throw new ApiError(`Can't create provisioned destination through API (${obj.id})`); } - return obj; + // Remove masked values + const secretPaths = getDestinationSecretPaths(obj.destinationType); + return removeMaskedValues(obj, secretPaths); }, narrowSchema: obj => { const type = obj.destinationType; @@ -102,7 +124,7 @@ const configObjectTypes: Record = { stream: { schema: StreamConfig, merge(original: any, patch: Partial): any { - return { + const merged = { ...original, ...patch, privateKeys: patch.privateKeys @@ -112,31 +134,62 @@ const configObjectTypes: Record = { ? hashKeys(patch.publicKeys, original.publicKeys || []) : original.publicKeys || [], }; + // TODO: dirty workaround for not be able to clear authorizedJavaScriptDomains + if (!patch.authorizedJavaScriptDomains) { + delete merged.authorizedJavaScriptDomains; + } + return merged; }, - inputFilter: async obj => { - const workspaceId = obj.workspaceId; - for (const domain of obj.domains || []) { - const domainAvailability = await isDomainAvailable(domain, workspaceId); + inputFilter: async (obj, _, workspace) => { + const workspaceId = workspace.id; + outer: for (const domain of obj.domains || []) { + const domainToCheck = domain.trim().toLowerCase(); + if (!checkDomain(domainToCheck)) { + log.atWarn().log(`Domain '${domainToCheck}' is not a valid domain name`); + throw new ApiError(`Domain ${domainToCheck} is not a valid domain name`); + } + const domainAvailability = await isDomainAvailable(domainToCheck, workspace); if (!domainAvailability.available) { log .atWarn() .log( - `Domain ${domain} can't be added to workspace ${workspaceId}, it is already in use by other workspaces: ${domainAvailability.usedInWorkspace}` + `Domain ${domainToCheck} can't be added to workspace ${workspaceId}, it is already in use by other workspaces: ${domainAvailability.usedInWorkspace}` ); - throw new ApiError(`Domain ${domain} is already in use by other workspace`); + throw new ApiError(`Domain ${domainToCheck} is already in use by other workspace`); + } + const wildcardDomains = await getWildcardDomains(workspaceId); + for (const wildcardDomain of wildcardDomains) { + if (domainToCheck.endsWith(wildcardDomain.toLowerCase().replace("*", ""))) { + log + .atInfo() + .log( + `No need to check ingress status for ${domainToCheck} since it is under wildcard domain: ${wildcardDomain}` + ); + continue outer; + } + } + try { + const ingressStatus = await checkOrAddToIngress(domainToCheck); + log.atInfo().log(`Ingress status for ${domainToCheck}: ${JSON.stringify(ingressStatus)}`); + if (!ingressStatus) { + log.atWarn().log(`Incorrect ingress status ${domainToCheck} is not valid`); + } + } catch (e) { + log.atError().withCause(e).log(`Error checking ingress status for ${domainToCheck}`); } } return { ...obj, + domains: obj.domains?.map(d => d.trim().toLowerCase()) || [], privateKeys: hashKeys(obj.privateKeys || [], []), publicKeys: hashKeys(obj.publicKeys || [], []), }; }, - outputFilter: (original: StreamConfig) => { + outputFilter: async (original: StreamConfig) => { return { ...original, - domains: original.domains?.map(d => d.toLowerCase()), + domains: original.domains?.map(d => d.trim().toLowerCase()), privateKeys: (original.privateKeys || []).map(k => ({ ...k, plaintext: undefined, hash: undefined })), publicKeys: (original.publicKeys || []).map(k => ({ ...k, plaintext: undefined, hash: undefined })), }; @@ -147,5 +200,50 @@ const configObjectTypes: Record = { }, service: { schema: ServiceConfig, + outputFilter: async (obj: ServiceConfig) => { + // Mask secrets for services + const secretPaths = await getServiceSecretPaths(obj.package, obj.version); + return maskSecrets(obj, secretPaths); + }, + merge: async (original: ServiceConfig, patch: Partial): Promise => { + // Remove masked values before merge + const secretPaths = await getServiceSecretPaths(original.package, original.version); + const cleanedPatch = removeMaskedValues(patch, secretPaths); + return deepMerge(original, cleanedPatch); + }, + inputFilter: async (obj: ServiceConfig) => { + // Remove masked values + const secretPaths = await getServiceSecretPaths(obj.package, obj.version); + return removeMaskedValues(obj, secretPaths); + }, + }, + "custom-image": { + schema: ConnectorImageConfig, + }, + domain: { + schema: WorkspaceDomain, + inputFilter: async obj => { + const domainToCheck = obj.name.trim().toLowerCase(); + if (!checkDomain(domainToCheck)) { + log.atWarn().log(`Domain '${domainToCheck}' is not a valid domain name`); + throw new ApiError(`Domain ${domainToCheck} is not a valid domain name`); + } + return { + ...obj, + name: domainToCheck, + }; + }, + outputFilter: async (original: WorkspaceDomain) => { + return { + ...original, + name: original.name.trim().toLowerCase(), + }; + }, + }, + misc: { + schema: MiscEntity, + }, + notification: { + schema: NotificationChannel, }, } as const; diff --git a/webapps/console/lib/schema/destinations.tsx b/webapps/console/lib/schema/destinations.tsx index 831b2c215..f4ddb3721 100644 --- a/webapps/console/lib/schema/destinations.tsx +++ b/webapps/console/lib/schema/destinations.tsx @@ -8,13 +8,17 @@ import devnullIcon from "./icons/devnull"; import gcsIcon from "./icons/gcs"; import hubspotIcon from "./icons/hubspot"; import mixpanelIcon from "./icons/mixpanel"; +import facebookIcon from "./icons/facebook"; import juneIcon from "./icons/june"; +import blazeIcon from "./icons/blaze"; +import salesforceIcon from "./icons/salesforce"; import mongodbIcon from "./icons/mongodb"; import ga4Icon from "./icons/ga4"; import gtmIcon from "./icons/gtm"; import postgresIcon from "./icons/postgres"; import mysqlIcon from "./icons/mysql"; +import motherduckIcon from "./icons/motherduck"; import redshiftIcon from "./icons/redshift"; import posthogIcon from "./icons/posthog"; import segmentIcon from "./icons/segment"; @@ -22,10 +26,11 @@ import s3Icon from "./icons/s3"; import tagIcon from "./icons/tag"; import snowflakeIcon from "./icons/snowflake"; import logRocketIcon from "./icons/logrocket"; +import intercomIcon from "./icons/intercom"; import webhookIcon from "./icons/webhook"; import { branding } from "../branding"; import * as meta from "@jitsu/core-functions/src/meta"; -import { SegmentCredentials } from "@jitsu/core-functions/src/meta"; +import { HubspotCredentials } from "@jitsu/core-functions/src/meta"; const s3Regions = [ "us-west-1", @@ -53,6 +58,8 @@ const s3Regions = [ "us-gov-west-1", ] as const; +export const MASKED_SECRET = "__MASKED_BY_JITSU__"; + /** * UI for property */ @@ -72,7 +79,16 @@ export type PropertyUI = { /** * If the field should not be displayed. That field must have a default value */ - hidden?: boolean; + hidden?: boolean | ((obj: any) => boolean); + /** + * If the field should be a constant + */ + constant?: any | ((obj: any) => any); + /** + * correction to field value. e.g: set default value for property that was missing before + */ + correction?: any | ((obj: any) => any); + /** * Documentation for the field */ @@ -89,48 +105,77 @@ export type PropertyUI = { export type SchemaUI = Record; -//Options of any source -> destination connection that are not specific to any particular destination -export const CloudDestinationsConnectionOptions = z.object({ +export const FunctionsConnectionOptions = z.object({ functions: z.array(z.object({ functionId: z.string(), functionOptions: z.any() })).optional(), + functionsEnv: z.record(z.string()).optional(), + debugTill: z.string().optional(), }); + +//Options of any source -> destination connection that are not specific to any particular destination +export const CloudDestinationsConnectionOptions = z + .object({ + multithreading: z.boolean().optional(), + threadsCount: z.number().optional(), + disabled: z.boolean().optional(), + noretry: z.boolean().optional(), + }) + .merge(FunctionsConnectionOptions); export type CloudDestinationsConnectionOptions = z.infer; //Auxiliary type for batch mode options export const BatchModeOptions = z.object({ - batchSize: z.number().min(1).optional(), + batchSize: z.number().min(1).default(10000), frequency: z .number() .int() .min(1) .max(60 * 24) - .default(60), + .default(5) + .nullish(), }); export type BatchModeOptions = z.infer; /** * Common settings for device destination connections */ -export const DeviceDestinationsConnectionOptions = z.object({ - events: z.string().optional().default("*"), - hosts: z.string().optional().default("*"), -}); +export const DeviceDestinationsConnectionOptions = z + .object({ + events: z.string().optional().default("*"), + hosts: z.string().optional().default("*"), + }) + .merge(FunctionsConnectionOptions); export type DeviceDestinationsConnectionOptions = z.infer; //All possible options for Bulker based source -> destination connection export const BaseBulkerConnectionOptions = z .object({ + disabled: z.boolean().optional(), mode: z.enum(["stream", "batch"]).default("batch"), primaryKey: z.string().default("message_id"), deduplicate: z.boolean().default(true), + deduplicateWindow: z.number().default(31), timestampColumn: z.string().default("timestamp"), - dataLayout: z.enum(["segment", "jitsu-legacy", "segment-single-table"]).default("segment-single-table"), + dataLayout: z + .enum(["segment", "jitsu-legacy", "segment-single-table", "passthrough"]) + .default("segment-single-table"), + schemaFreeze: z.boolean().default(false), + keepOriginalNames: z.boolean().default(false), + multithreading: z.boolean().optional(), + threadsCount: z.number().optional(), + spreadTablesSchedule: z.boolean().optional(), }) .merge(BatchModeOptions) - .merge(CloudDestinationsConnectionOptions); + .merge(FunctionsConnectionOptions); export type BaseBulkerConnectionOptions = z.infer; +export const AllConnectionOptions = BaseBulkerConnectionOptions.merge(DeviceDestinationsConnectionOptions).merge( + CloudDestinationsConnectionOptions +); + +export type AllConnectionOptions = Partial>; + /** * There's a little copy-paste between here and jitsu-js */ @@ -154,6 +199,8 @@ export type DestinationType = { title: string; isSynchronous?: boolean; usesBulker?: boolean; + // destinations that relies on both rotor for flexible logic in typescript and bulker for batching + hybrid?: boolean; tags: string | string[]; credentials: SomeZodObject; connectionOptions: SomeZodObject; @@ -161,11 +208,23 @@ export type DestinationType = { comingSoon?: boolean; icon?: ReactNode; description: ReactNode; + documentation?: ReactNode; //For cloud (=server side) destinations - name builtin of the function that implements it implementingFunction?: string; //For device destinations - how this destination should be invoked? Information such as analytics plugin name, package name //etc. Not typed yet since so far each destination has its own settings deviceOptions?: DeviceOptions; + + /* + * If destination support sync from connector packages, here's a place to define it + * key is a FQN of the connector package, li + */ + syncs?: { + [key: string]: { + syncOptions: SomeZodObject; + description: ReactNode; + }; + }; }; export const blockStorageSettings = z.object({ @@ -174,13 +233,13 @@ export const blockStorageSettings = z.object({ .enum(["ndjson", "ndjson_flat", "csv"]) .default("ndjson") .describe( - "Format of the files stored in the block storage: ndjson - Newline Delimited JSON, ndjson_flat - Newline Delimited JSON flattened, csv - CSV" + "Format of the files stored in the block storage: ndjson - Newline Delimited JSON, ndjson_flat - Newline Delimited JSON flattened, csv - CSV" ), compression: z .enum(["gzip", "none"]) - .default("none") + .default("gzip") .describe( - "Compression algorithm used for the files stored in the block storage: gzip - GZIP, none - no compression." + "Compression mode used for the files stored in the block storage:
gzip - files will be compressed and have .gz filename suffix and Content-Type: application/gzip
none - no compression, Content-Type and file extension will be set according to the format" ), }); @@ -194,6 +253,10 @@ export function getCoreDestinationType(typeId: string): DestinationType { return destinationType; } +export function getCoreDestinationTypeNonStrict(typeId: string): DestinationType | undefined { + return coreDestinationsMap[typeId]; +} + export const ClickhouseCredentials = z.object({ protocol: z .enum(["http", "https", "clickhouse", "clickhouse-secure"]) @@ -210,8 +273,8 @@ export const ClickhouseCredentials = z.object({ password: z.string().describe("Password for ClickHouse connection"), cluster: z .string() - .default("default") - .describe("Name of cluster to use. If clickhouse works in single node mode, leave this field empty"), + .optional() + .describe("Name of cluster to use.
For ClickHouse Cloud or single-node setups, leave this field empty."), database: z.string().default("default").describe("Name of the database to use"), parameters: z .object({}) @@ -220,6 +283,12 @@ export const ClickhouseCredentials = z.object({ .describe( "Additional parameters for ClickHouse driver. See Clickhouse documentation" ), + loadAsJson: z + .boolean() + .default(false) + .describe( + "Use JSONEachRow format::Load data in the JSONEachRow format. This method offers better performance but may not work correctly on older ClickHouse versions." + ), }); export type ClickhouseCredentials = z.infer; @@ -261,7 +330,10 @@ const tagDestination = { name: "tag", } as DeviceOptions, credentialsUi: { - code: { editor: "SnippedEditor", editorProps: { languages: ["html", "javascript"] } }, + code: { + editor: "SnippedEditor", + editorProps: { languages: ["html", "javascript"] }, + }, }, connectionOptions: DeviceDestinationsConnectionOptions, }; @@ -277,14 +349,22 @@ const gaDeviceDestination = { measurementIds: z .string() .describe( - "Measurement IDs of your Google Analytics 4 properties. How to find" + "Measurement ID::Measurement ID of your Google Analytics 4 properties. How to find" + ), + autoPageView: z + .boolean() + .default(false) + .describe( + "Rely on Enhanced event measurement to track page views. Jitsu page event will be ignored." ), }), deviceOptions: { - type: "analytics-plugin", - packageCdn: - "https://cdn.jsdelivr.net/npm/@analytics/google-analytics@1.0.5/dist/@analytics/google-analytics.min.js", - moduleVarName: "analyticsGa", + type: "internal-plugin", + name: "ga4-tag", + // type: "analytics-plugin", + // packageCdn: + // "https://cdn.jsdelivr.net/npm/@analytics/google-analytics@1.0.7/dist/@analytics/google-analytics.min.js", + // moduleVarName: "analyticsGa", } as DeviceOptions, connectionOptions: DeviceDestinationsConnectionOptions, }; @@ -312,43 +392,20 @@ export const coreDestinations: DestinationType[] = [ gaDeviceDestination, gtmDeviceDestination, logRocketDestination, - - { - id: "postgres", - usesBulker: true, - icon: postgresIcon, - title: "Postgres", - tags: "Datawarehouse", - connectionOptions: BaseBulkerConnectionOptions, - credentials: z.object({ - host: z.string().describe("Postgres host"), - port: z.number().default(5432).describe("Postgres port"), - sslMode: z - .enum(["disable", "require"]) - .default("require") - .describe("SSL Mode::SSL mode for Postgres connection: disable or require"), - database: z.string().describe("Postgres database name"), - username: z.string().describe("Postgres username"), - password: z.string().describe("Postgres password"), - defaultSchema: z.string().default("public").describe("Schema::Postgres schema"), - }), - credentialsUi: { - password: { - password: true, - }, - }, - description: "Postgres is a powerful, open source object-relational database system.", - }, - { id: "clickhouse", usesBulker: true, icon: , - connectionOptions: BaseBulkerConnectionOptions.describe( + connectionOptions: BaseBulkerConnectionOptions.merge( + z.object({ + primaryKey: z.string().default("timestamp,message_id"), + clickhouseSettings: z.string().default(""), + }) + ).describe( JSON.stringify({ limitations: { streamModeLocked: - 'Stream mode in ClickHouse is limited by MergeTree engine capabilities.
ClickHouse is not designed to handle a large number of individual inserts.
Use it only for testing purposes.', + 'Stream mode in ClickHouse requires async inserts enabled both on client and server level. Read more about async inserts here.
Also, make sure to set async inserts parameters in the advanced section', }, }) ), @@ -361,10 +418,92 @@ export const coreDestinations: DestinationType[] = [ hosts: { editor: "StringArrayEditor", }, + loadAsJson: { + hidden: true, + }, + password: { + password: true, + }, + }, + }, + { + id: "postgres", + usesBulker: true, + icon: postgresIcon, + title: "Postgres", + tags: "Datawarehouse", + connectionOptions: BaseBulkerConnectionOptions, + credentials: z.object({ + authenticationMethod: z + .enum(["password", "google-psc"]) + .optional() + .default("password") + .describe( + "Authentication Method::Standard username/password or Private Service Connect for Google-managed postgres instances only." + ), + instanceConnectionName: z + .string() + .optional() + .describe( + "Instance Connection Name::Google SQL instance Connection Name in the format project-name:region:instance-name. How to obtain." + ), + host: z.string().optional().describe("Postgres host"), + port: z.number().optional().default(5432).describe("Postgres port"), + sslMode: z + .enum(["disable", "require", "verify-ca", "verify-full"]) + .optional() + .default("require") + .describe( + "SSL Mode::SSL mode for Postgres connection: disable,require,verify-ca,verify-full" + ), + sslServerCA: z.string().optional().describe("SSL Server CA::"), + sslClientCert: z.string().optional().describe("SSL Client Cert::"), + sslClientKey: z.string().optional().describe("SSL Client Key::"), + database: z.string().describe("Postgres database name"), + username: z.string().optional().describe("Postgres username"), + password: z.string().optional().describe("Postgres password"), + defaultSchema: z.string().default("public").describe("Schema::Postgres schema"), + }), + credentialsUi: { + authenticationMethod: { + correction: obj => obj.authenticationMethod || "password", + }, + instanceConnectionName: { + hidden: obj => obj.authenticationMethod !== "google-psc", + }, + username: { + hidden: obj => obj.authenticationMethod === "google-psc", + }, password: { password: true, + hidden: obj => obj.authenticationMethod === "google-psc", + }, + host: { + hidden: obj => obj.authenticationMethod === "google-psc", + }, + port: { + hidden: obj => obj.authenticationMethod === "google-psc", + }, + sslMode: { + hidden: obj => obj.authenticationMethod === "google-psc", + }, + sslServerCA: { + textarea: true, + password: true, + hidden: obj => obj.sslMode === "disable" || obj.sslMode === "require", + }, + sslClientCert: { + textarea: true, + password: true, + hidden: obj => obj.sslMode === "disable" || obj.sslMode === "require", + }, + sslClientKey: { + textarea: true, + password: true, + hidden: obj => obj.sslMode === "disable" || obj.sslMode === "require", }, }, + description: "Postgres is a powerful, open source object-relational database system.", }, { id: "bigquery", @@ -377,6 +516,10 @@ export const coreDestinations: DestinationType[] = [ "It's possible to implement stream mode for BigQuery, but data Deduplication cannot be supported in this mode. So it is currently disabled in Jitsu.", }, }) + ).merge( + z.object({ + deduplicate: z.boolean().default(false), + }) ), title: "BigQuery", tags: "Datawarehouse", @@ -400,8 +543,8 @@ export const coreDestinations: DestinationType[] = [ }), credentialsUi: { keyFile: { - editor: "SnippedEditor", - editorProps: { languages: ["json"], height: 250 }, + password: true, + textarea: true, }, }, }, @@ -411,12 +554,26 @@ export const coreDestinations: DestinationType[] = [ title: "Snowflake", tags: "Datawarehouse", credentials: z.object({ + authenticationMethod: z + .enum(["key-pair", "password"]) + .optional() + .default("key-pair") + .describe( + "Authentication Method::Snowflake authentication method: Key-pair or username/password" + ), + username: z.string().optional().describe("Snowflake username"), + password: z.string().optional().describe("Snowflake password"), + privateKey: z + .string() + .optional() + .describe( + "Private Key::Snowflake private key. Generate the private key" + ), + privateKeyPassphrase: z.string().optional(), account: z.string().describe("Snowflake account name"), + warehouse: z.string().describe("Snowflake warehouse name"), database: z.string().describe("Snowflake database name"), defaultSchema: z.string().default("PUBLIC").describe("Schema::Snowflake schema"), - username: z.string().describe("Snowflake username"), - password: z.string().describe("Snowflake password"), - warehouse: z.string().describe("Snowflake warehouse name"), parameters: z .object({}) .catchall(z.string().default("")) @@ -424,8 +581,21 @@ export const coreDestinations: DestinationType[] = [ .describe("Additional Snowflake connection parameters"), }), credentialsUi: { + authenticationMethod: { + correction: (obj, isNew) => (isNew ? "key-pair" : obj.authenticationMethod || "password"), + }, password: { password: true, + hidden: obj => obj.authenticationMethod === "key-pair", + }, + privateKey: { + hidden: obj => obj.authenticationMethod !== "key-pair", + textarea: true, + password: true, + }, + privateKeyPassphrase: { + hidden: obj => obj.authenticationMethod !== "key-pair", + password: true, }, }, connectionOptions: BaseBulkerConnectionOptions, @@ -447,23 +617,99 @@ export const coreDestinations: DestinationType[] = [ ), tags: "Datawarehouse", credentials: z.object({ - host: z.string().describe("Redshift host"), + authenticationMethod: z + .enum(["iam", "password"]) + .optional() + .default("password") + .describe( + "Authentication Method::Redshift authentication method: IAM Role based or database username/password" + ), + serverless: z.boolean().default(false).optional().describe("Redshift Serverless::"), + region: z.enum(s3Regions).describe("Region::Aws Region"), + clusterIdentifier: z.string().optional().describe("Cluster Identifier::Redshift cluster identifier"), + workgroupName: z.string().optional().describe("Workgroup name::Redshift Serverless workgroup name"), + roleARN: z + .string() + .optional() + .describe( + "Role ARN::IAM role ARN. How to create" + ), + externalID: z.string().optional().describe("External ID::IAM external ID"), + host: z.string().optional().describe("Redshift host"), database: z.string().describe("Redshift database name"), defaultSchema: z.string().default("PUBLIC").describe("Schema::Redshift schema"), - username: z.string().describe("Redshift username"), - password: z.string().describe("Redshift password"), + username: z.string().optional().describe("Redshift username"), + password: z.string().optional().describe("Redshift password"), + bucket: z.string().describe("S3 Bucket Name::S3 Bucket Name"), accessKeyId: z .string() + .optional() .describe( "S3 Access Key Id::S3 Access Key Id. Create access key" ), - secretAccessKey: z.string().describe("S3 Secret Access Key::S3 Secret Access Key"), - region: z.enum(s3Regions).describe("S3 Region::S3 Region"), - bucket: z.string().describe("S3 Bucket Name::S3 Bucket Name"), + secretAccessKey: z.string().optional().describe("S3 Secret Access Key::S3 Secret Access Key"), }), + credentialsUi: { + authenticationMethod: { + correction: obj => obj.authenticationMethod || "password", + }, + username: { + hidden: obj => obj.authenticationMethod === "iam" && obj.serverless === true, + }, + password: { + password: true, + hidden: obj => obj.authenticationMethod === "iam", + }, + secretAccessKey: { + hidden: obj => obj.authenticationMethod === "iam", + password: true, + }, + accessKeyId: { + hidden: obj => obj.authenticationMethod === "iam", + }, + host: { + hidden: obj => obj.authenticationMethod === "iam", + }, + clusterIdentifier: { + hidden: obj => obj.authenticationMethod !== "iam" || obj.serverless === true, + }, + workgroupName: { + hidden: obj => obj.authenticationMethod !== "iam" || obj.serverless === false, + }, + roleARN: { + hidden: obj => obj.authenticationMethod !== "iam", + }, + externalID: { + // constants are not yet refreshed on form state change. so it is unconditional here + constant: obj => obj.workspaceId, + }, + }, description: "Amazon Redshift is a cloud data warehouse that is optimized for the analytical workloads of business intelligence (BI) and data warehousing (DWH). Jitsu supports both Serverless and Classic Redshift", }, + { + id: "duckdb", + usesBulker: true, + icon: motherduckIcon, + title: "MotherDuck (DuckDB)", + tags: "Datawarehouse", + connectionOptions: BaseBulkerConnectionOptions, + credentials: z.object({ + database: z.string().describe("MotherDuck database name"), + motherduckToken: z + .string() + .describe( + "MotherDuck token::MotherDuck token can be obtained in the MotherDuck's Settings -> Access Tokens section" + ), + defaultSchema: z.string().default("main").describe("Schema::Database schema"), + }), + credentialsUi: { + motherduckToken: { + password: true, + }, + }, + description: "DuckDB-powered cloud data warehouse scaling to terabytes with ease.", + }, { id: "mysql", usesBulker: true, @@ -508,17 +754,54 @@ export const coreDestinations: DestinationType[] = [ description: "S3 is a cloud file storage service by Amazon", credentials: z .object({ + authenticationMethod: z + .enum(["iam", "accessKey"]) + .optional() + .default("accessKey") + .describe( + "Authentication Method::S3 authentication method: IAM Role based or Access Key" + ), + region: z.enum(s3Regions).default(s3Regions[0]).describe("S3 Region::S3 Region"), + roleARN: z + .string() + .optional() + .describe( + "Role ARN::IAM role ARN. How to create" + ), + externalID: z.string().optional().describe("External ID::IAM external ID"), accessKeyId: z .string() + .optional() .describe( "S3 Access Key Id::S3 Access Key Id. Create access key" ), - secretAccessKey: z.string().describe("S3 Secret Access Key::S3 Secret Access Key"), + secretAccessKey: z.string().optional().describe("S3 Secret Access Key::S3 Secret Access Key"), bucket: z.string().describe("S3 Bucket Name::S3 Bucket Name"), - region: z.enum(s3Regions).default(s3Regions[0]).describe("S3 Region::S3 Region"), endpoint: z.string().optional().describe("Custom endpoint of S3-compatible server"), }) .merge(blockStorageSettings), + credentialsUi: { + authenticationMethod: { + correction: obj => obj.authenticationMethod || "accessKey", + }, + secretAccessKey: { + hidden: obj => obj.authenticationMethod === "iam", + password: true, + }, + accessKeyId: { + hidden: obj => obj.authenticationMethod === "iam", + }, + endpoint: { + hidden: obj => obj.authenticationMethod === "iam", + }, + roleARN: { + hidden: obj => obj.authenticationMethod !== "iam", + }, + externalID: { + // constants are not yet refreshed on form state change. so it is unconditional here + constant: obj => obj.workspaceId, + }, + }, }, { id: "gcs", @@ -540,17 +823,88 @@ export const coreDestinations: DestinationType[] = [ bucket: z.string().describe("GCS Bucket Name::GCS Bucket Name"), }) .merge(blockStorageSettings), + credentialsUi: { + accessKey: { + textarea: true, + password: true, + }, + }, description: "Google Cloud Storage is a cloud file storage service by Google", }, { id: "mixpanel", icon: mixpanelIcon, title: "Mixpanel", + hybrid: true, tags: "Product Analytics", - connectionOptions: CloudDestinationsConnectionOptions, + connectionOptions: CloudDestinationsConnectionOptions.merge(BatchModeOptions).merge( + z.object({ + batchSize: z.number().min(1).max(2000).default(1000), + batchSizeBytes: z.number().min(1).max(10_000_000).default(9_000_000), + mode: z.enum(["stream", "batch"]).default("stream"), + }) + ), credentials: meta.MixpanelCredentials, credentialsUi: meta.MixpanelCredentialsUi, description: "Mixpanel is a product analytics platform that provides insights into user behavior.", + syncs: { + "airbyte/source-google-ads": { + syncOptions: z.object({}), + description: ( + <> + Jitsu exports ad spend data to from Google Ads to Mixpanel to measure return on ad spend (ROAS). Learn more + about Mixpanel ROAS tracking{" "} + + here + + + ), + }, + "airbyte/source-facebook-marketing": { + syncOptions: z.object({}), + description: ( + <> + Jitsu exports ad spend data to from Facebook to Mixpanel to measure return on ad spend (ROAS). Learn more + about Mixpanel ROAS tracking{" "} + + here + + + ), + }, + }, + }, + { + id: "intercom", + icon: intercomIcon, + title: "Intercom", + tags: "Product Analytics", + credentials: meta.IntercomDestinationCredentials, + connectionOptions: CloudDestinationsConnectionOptions, + description: ( + <> + Jitsu updates intercom companies and users on each .group() and .identify() calls. For + other events Jitsu sends them as custom events to Intercom + + ), + }, + { + id: "facebook-conversions", + icon: facebookIcon, + title: "Facebook Conversions API", + tags: "Product Analytics", + connectionOptions: CloudDestinationsConnectionOptions, + credentials: meta.FacebookConversionApiCredentials, + credentialsUi: meta.FacebookConversionApiCredentialsUi, + description: "Facebook Conversion API is a tool for sending events to Facebook Ads Manager.", }, { id: "june", @@ -561,6 +915,35 @@ export const coreDestinations: DestinationType[] = [ credentials: meta.JuneCredentials, description: "June.so is a product analytics platform that provides insights into user behavior.", }, + { + id: "braze", + icon: blazeIcon, + title: "Braze", + tags: "Product Analytics", + connectionOptions: CloudDestinationsConnectionOptions, + credentials: meta.BrazeCredentials, + description: "Braze is a customer engagement platform used by businesses for multichannel marketing.", + }, + { + id: "salesforce", + icon: salesforceIcon, + title: "Salesforce", + tags: "Product Analytics", + connectionOptions: CloudDestinationsConnectionOptions, + credentials: meta.SalesforceCredentials, + credentialsUi: { + authorized: { + hidden: true, + }, + oauthIntegrationId: { + hidden: true, + }, + oauthConnectionId: { + hidden: true, + }, + }, + description: "Salesforce is the world's leading customer relationship management technology.", + }, { id: "mongodb", icon: mongodbIcon, @@ -575,7 +958,7 @@ export const coreDestinations: DestinationType[] = [ { id: "ga4", icon: ga4Icon, - title: "Google Analytics 4", + title: "Google Analytics 4 (GA4 Measurement Protocol)", tags: "Product Analytics", connectionOptions: CloudDestinationsConnectionOptions, credentials: meta.Ga4Credentials, @@ -598,24 +981,46 @@ export const coreDestinations: DestinationType[] = [ connectionOptions: CloudDestinationsConnectionOptions, title: "Amplitude", tags: "Product Analytics", - comingSoon: true, - credentials: z.object({ - apiKey: z.string().describe("API Key::Amplitude API Key"), - }), + credentials: meta.AmplitudeDestinationConfig, description: "Amplitude is a product analytics platform", }, { id: "hubspot", icon: hubspotIcon, - comingSoon: true, connectionOptions: CloudDestinationsConnectionOptions, title: "Hubspot", tags: "CRM", - credentials: z.object({ - apiKey: z.string().describe("API Key::Hubspot API Key"), - hubId: z.string().describe("Hub ID::Hubspot Hub ID"), - }), + credentials: HubspotCredentials, description: "Hubspot is a CRM. Jitsu sends data to Hubspot API and updates contacts and company records", + documentation: ( + <> + The integration performs several functions: +

    +
  • + For each .identify() event, it either creates or updates a contact in the CRM. Jitsu utilizes a + custom property named jitsu_user_id, which is automatically generated, as the unique identifier + for the contact object. This identifier corresponds to the .userId property within the{" "} + identify event. +
  • +
  • + For each .group() event, it either creates or updates a company profile in the CRM. Here, Jitsu + employs a custom property called jitsu_group_id, which is also automatically generated, to + serve as the unique identifier for the company object. This identifier is derived from the{" "} + .groupId property within the group event. +
  • +
  • + If an event includes both groupId and userId, Jitsu will establish a linkage + between the two identifiers, effectively associating the user with the company. +
  • +
  • + When the sendPageViews feature is activated, Jitsu will forward page events, along + with other related events, to HubSpot for any identified user. Important: To utilize this + functionality, ensure that your HubSpot plan includes access to the{" "} + Events API. +
  • +
+ + ), }, { id: "devnull", @@ -633,7 +1038,7 @@ export const coreDestinations: DestinationType[] = [ icon: segmentIcon, title: "Segment", tags: "Special", - credentials: SegmentCredentials, + credentials: meta.SegmentCredentials, description: ( <> Forward events for to Segment-compatible endpoint. It's useful if you want to use {branding.productName} for @@ -643,15 +1048,26 @@ export const coreDestinations: DestinationType[] = [ }, { id: "webhook", - connectionOptions: CloudDestinationsConnectionOptions, icon: webhookIcon, title: "Webhook", tags: "Special", + hybrid: true, + connectionOptions: CloudDestinationsConnectionOptions.merge(BatchModeOptions).merge( + z.object({ + batchSize: z.number().min(1).max(1000).default(100), + mode: z.enum(["stream", "batch"]).default("stream"), + }) + ), credentials: meta.WebhookDestinationConfig, credentialsUi: { headers: { editor: "StringArrayEditor", }, + payload: { + editor: "SnippedEditor", + editorProps: { languages: ["json", "text"], height: "300", syntaxCheck: { json: false } }, + hidden: obj => !obj.customPayload, + }, }, description: "Send data to any HTTP endpoint. You can use this destination to send data to Slack, Discord, or any other service that accepts HTTP requests. ", diff --git a/webapps/console/lib/schema/icons/blaze.tsx b/webapps/console/lib/schema/icons/blaze.tsx new file mode 100644 index 000000000..8d39547ae --- /dev/null +++ b/webapps/console/lib/schema/icons/blaze.tsx @@ -0,0 +1,11 @@ +export default ( + + + + + +); diff --git a/webapps/console/lib/schema/icons/facebook.tsx b/webapps/console/lib/schema/icons/facebook.tsx new file mode 100644 index 000000000..79328c1ad --- /dev/null +++ b/webapps/console/lib/schema/icons/facebook.tsx @@ -0,0 +1,46 @@ +export default ( + + + + + + + + + + + + + + + +); diff --git a/webapps/console/lib/schema/icons/intercom.tsx b/webapps/console/lib/schema/icons/intercom.tsx new file mode 100644 index 000000000..c487b9d89 --- /dev/null +++ b/webapps/console/lib/schema/icons/intercom.tsx @@ -0,0 +1,14 @@ +export default ( + + + +); diff --git a/webapps/console/lib/schema/icons/motherduck.tsx b/webapps/console/lib/schema/icons/motherduck.tsx new file mode 100644 index 000000000..bcc0bdb44 --- /dev/null +++ b/webapps/console/lib/schema/icons/motherduck.tsx @@ -0,0 +1,12 @@ +export default ( + + + + +); diff --git a/webapps/console/lib/schema/icons/salesforce.tsx b/webapps/console/lib/schema/icons/salesforce.tsx new file mode 100644 index 000000000..7e131c523 --- /dev/null +++ b/webapps/console/lib/schema/icons/salesforce.tsx @@ -0,0 +1,63 @@ +export default ( + + + + + + + + + + + + + + + +); diff --git a/webapps/console/lib/schema/index.ts b/webapps/console/lib/schema/index.ts index 8cdf86df1..45954da7d 100644 --- a/webapps/console/lib/schema/index.ts +++ b/webapps/console/lib/schema/index.ts @@ -1,5 +1,6 @@ import { z } from "zod"; -import { UserProfileDbModel } from "../../prisma/schema"; +import { UserProfileDbModel, WorkspaceDbModel } from "../../prisma/schema"; +import { WorkspaceRolesZodType } from "../workspace-roles"; export const SessionUser = z.object({ name: z.string(), @@ -16,18 +17,44 @@ export const ContextApiResponse = z.object({ user: SessionUser, firstWorkspaceId: z.string().nullish().optional(), firstWorkspaceSlug: z.string().nullish().optional(), - newUser: z.boolean().optional(), + redirect: z.string().optional(), }); export type ContextApiResponse = z.infer; +//Default values are for "free" (default) plan export const BillingSettings = z.object({ planId: z.string().default("free"), + //if plan has a custom pricing prepared for a particular workspace + customBilling: z.boolean().default(false).optional(), + pastDue: z.boolean().default(false).optional(), + //Can be "self-service" or "enterprise". Enterprise plans doesn't block workspace on overage, but requires manual billing. + planKind: z.string().default("self-service").optional(), + //similar to customBilling, but indicates that plan is custom. custom flag comes from stripe plan metadata + custom: z.boolean().default(false).optional(), + dailyActiveSyncs: z.number().default(1).optional(), + dailyActiveSyncsOverage: z.number().default(20).optional(), + maximumSyncFrequency: z.number().optional(), //minutes planName: z.string().optional(), //if not set - will be taken from planId overagePricePer100k: z.number().optional(), canShowProvisionDbCredentials: z.boolean().default(false), + dataRetentionEditorEnabled: z.boolean().default(false).optional(), destinationEvensPerMonth: z.number().default(200_000), expiresAt: z.string().optional(), + /** + * Subscription period. For monthly subscriptions it will be [expiresAt - 1 month, expiresAt]. For annual subscriptions - current + * month adjusted to a correct billing start date + */ + currentPeriod: z + .object({ + end: z.string(), + start: z.string(), + }) + .optional(), renewAfterExpiration: z.boolean().default(false).optional(), + //if subscription starts some time in the future, for enterprise plans only + futureSubscriptionDate: z.string().optional(), + profileBuilderEnabled: z.boolean().default(false).optional(), + isLegacyPlan: z.boolean().default(false).optional(), }); export type BillingSettings = z.infer; @@ -36,14 +63,20 @@ export const noRestrictions: BillingSettings = { planId: "$admin", overagePricePer100k: undefined, canShowProvisionDbCredentials: true, + maximumSyncFrequency: 0, + dailyActiveSyncs: 100, + dailyActiveSyncsOverage: 100, destinationEvensPerMonth: 100_000_000_000, + profileBuilderEnabled: true, }; export const AppConfig = z.object({ docsUrl: z.string().optional(), websiteUrl: z.string().optional(), - credentialsLoginEnabled: z.boolean(), + //iso date + readOnlyUntil: z.string().optional(), disableSignup: z.boolean().optional(), + customDomainsEnabled: z.boolean().optional(), ee: z.object({ available: z.boolean(), host: z.string().optional(), @@ -54,6 +87,7 @@ export const AppConfig = z.object({ protocol: z.enum(["http", "https"]), host: z.string(), dataHost: z.string().optional(), + ingestUrl: z.string().optional(), cname: z.string().optional(), //if differs from standard protocol port - 80 or 443 port: z.number().optional(), @@ -61,14 +95,29 @@ export const AppConfig = z.object({ auth: z .object({ firebasePublic: z.any(), + nextauth: z + .object({ + github: z.boolean().optional(), + credentials: z.boolean().optional(), + oidc: z.boolean().optional(), + }) + .optional(), + dynamicOidc: z.boolean().optional(), }) .optional(), - telemetry: z.object({ + frontendTelemetry: z.object({ enabled: z.boolean(), host: z.string().optional(), - writeKey: z.string().optional(), }), logLevel: z.enum(["debug", "info", "warn", "error"]), + syncs: z.object({ + enabled: z.boolean(), + scheduler: z.object({ + enabled: z.boolean(), + provider: z.enum(["google-cloud-scheduler"]).optional(), + }), + }), + mitCompliant: z.boolean().optional(), nango: z .object({ publicKey: z.string(), @@ -82,6 +131,8 @@ export const ConfigEntityBase = z.object({ id: z.string(), type: z.string(), workspaceId: z.string(), + name: z.string(), + cloneId: z.string().optional(), }); export type ConfigEntityBase = z.infer; @@ -89,17 +140,19 @@ export const ApiKey = z.object({ plaintext: z.string().nullish(), hash: z.string().nullish(), hint: z.string().nullish(), + createdAt: z.coerce.date().nullish(), + lastUsed: z.coerce.date().nullish(), id: z.string(), }); export type ApiKey = z.infer; export const StreamConfig = ConfigEntityBase.merge( z.object({ - name: z.string().min(5).optional(), domains: z.array(z.string()).optional(), authorizedJavaScriptDomains: z.string().optional(), publicKeys: z.array(ApiKey).optional(), privateKeys: z.array(ApiKey).optional(), + strict: z.boolean().optional(), }) ); export type StreamConfig = z.infer; @@ -109,7 +162,7 @@ export const DestinationConfig = ConfigEntityBase.merge( .object({ destinationType: z.string(), provisioned: z.boolean().optional(), - name: z.string().min(5), + testConnectionError: z.string().optional(), }) .passthrough() ); @@ -117,31 +170,71 @@ export type DestinationConfig = z.infer; export const FunctionConfig = ConfigEntityBase.merge( z.object({ - name: z.string().min(5), - description: z.string().optional(), code: z.string(), + draft: z.string().optional(), + kind: z.enum(["profile", "event"]).optional(), + description: z.string().optional(), + version: z.string().optional(), + origin: z.string().optional(), + slug: z.string().optional(), }) ); export type FunctionConfig = z.infer; export const ServiceConfig = ConfigEntityBase.merge( z.object({ - name: z.string().min(5), protocol: z.enum(["airbyte"]).default("airbyte"), + authorized: z.boolean().optional(), package: z.string(), version: z.string(), - credentials: z.string(), + credentials: z.object({}).passthrough(), + testConnectionError: z.string().optional(), }) ); export type ServiceConfig = z.infer; +export const ConnectorImageConfig = ConfigEntityBase.merge( + z.object({ + package: z.string(), + version: z.string(), + }) +); +export type ConnectorImageConfig = z.infer; + +export const WorkspaceDomain = ConfigEntityBase.merge(z.object({})); +export type WorkspaceDomain = z.infer; + +export const MiscEntity = ConfigEntityBase.merge( + z.object({ + objectType: z.enum(["classic-mapping"]).default("classic-mapping"), + value: z.string(), + }) +); +export type MiscEntity = z.infer; + +export const NotificationChannel = ConfigEntityBase.merge( + z.object({ + events: z.array(z.enum(["all", "sync", "batch"])).default(["all"]), + channel: z.enum(["email", "slack"]).default("slack"), + slackWebhookUrl: z.string().optional(), + // allWorkspaceEmails: z.boolean().default(true).optional(), + emails: z.array(z.string()).optional(), + recurringAlertsPeriodHours: z.number().max(720).min(0).default(24), + }) +); +export type NotificationChannel = z.infer; + /** * What happens to an object before it is saved to DB. * * opts.original — original of the object, if object is being updated * opts.patch — patch of the object, if object is being updated. Or full object, if object is being created */ -export type InputFilter = (val: T, context: "create" | "update") => Promise; +export type InputFilter = ( + val: T, + context: "create" | "update", + workspace: z.infer +) => Promise; export type OutputFilter = (original: T) => T; /** @@ -162,12 +255,12 @@ export type ConfigObjectType = { /** * Custom merge logic. By default, it's just shallow merge - {...original, ...patch}. */ - merge?: (original: T, patch: Partial) => T; + merge?: (original: T, patch: Partial) => T | Promise; /** * Clean object before sending to client. Can remove fields, hide values etc */ - outputFilter?: OutputFilter; + outputFilter?: OutputFilter | ((original: T) => Promise); }; const SafeUserProfile = UserProfileDbModel.pick({ @@ -187,6 +280,33 @@ export const UserWorkspaceRelation = z.object({ invitationLink: z.string().optional(), invitationEmail: z.string().optional(), canSendEmail: z.boolean().optional(), + role: WorkspaceRolesZodType, }); export type UserWorkspaceRelation = z.infer; + +export const BaseLinkType = z.object({ fromId: z.string(), toId: z.string() }); + +export const SelectedStreamSettings = z.object({ + sync_mode: z.enum(["full_refresh", "incremental"]), + table_name: z.string().optional(), + cursor_field: z.array(z.string()).optional(), +}); + +export type SelectedStreamSettings = z.infer; + +export const SyncOptionsType = z.object({ + streams: z.record(SelectedStreamSettings), + disabledStreams: z.record(SelectedStreamSettings), + namespace: z.string().optional(), + tableNamePrefix: z.string().optional(), + toSameCase: z.boolean().optional(), + addMeta: z.boolean().optional(), + deduplicate: z.boolean().optional().default(true), + schemaChanges: z.enum(["manual", "fields", "streams"]).optional(), + functionsEnv: z.any().optional(), + schedule: z.union([z.string(), z.enum(["0 0 * * *", "0 * * * *", "*/15 * * * *", "*/5 * * * *", "* * * * *"])]), + timezone: z.string().optional(), +}); + +export type SyncOptionsType = z.infer; diff --git a/webapps/console/lib/schema/secrets.ts b/webapps/console/lib/schema/secrets.ts new file mode 100644 index 000000000..7a15f24ad --- /dev/null +++ b/webapps/console/lib/schema/secrets.ts @@ -0,0 +1,223 @@ +import { coreDestinationsMap, MASKED_SECRET } from "./destinations"; +import { PropertyUI } from "./destinations"; +import { db } from "../server/db"; +import { getServerLog } from "../server/log"; +import get from "lodash/get"; +import set from "lodash/set"; +import unset from "lodash/unset"; + +import isPlainObject from "lodash/isPlainObject"; + +const log = getServerLog("secrets"); + +/** + * Get all secret field paths for a destination type + */ +export function getDestinationSecretPaths(destinationType: string): string[] { + const destination = coreDestinationsMap[destinationType]; + if (!destination) { + return []; + } + + const secretPaths: string[] = []; + const credentialsUi = destination.credentialsUi || {}; + + // Check each field in credentialsUi for password: true + Object.entries(credentialsUi).forEach(([fieldName, fieldUi]) => { + const ui = fieldUi as PropertyUI; + if (ui.password) { + secretPaths.push(fieldName); + } + }); + + return secretPaths; +} + +/** + * Get all secret field paths for a service (Airbyte source) + */ +export async function getServiceSecretPaths(packageName: string, version: string): Promise { + try { + const sourceSpec = await db.prisma().source_spec.findUnique({ + where: { + package_version: { + package: packageName, + version: version, + }, + }, + }); + + if (!sourceSpec || !sourceSpec.specs) { + return []; + } + + const specs = sourceSpec.specs as any; + const connectionSpec = specs.connectionSpecification; + if (!connectionSpec || !connectionSpec.properties) { + return []; + } + + const secretPaths: string[] = []; + + // Recursively find all fields with airbyte_secret: true + function findSecretFields(properties: any, basePath: string = "") { + Object.entries(properties).forEach(([key, schema]: [string, any]) => { + const currentPath = basePath ? `${basePath}.${key}` : key; + + if (schema.airbyte_secret === true) { + secretPaths.push(`credentials.${currentPath}`); + } + + // Handle oneOf/anyOf schemas + if (schema.oneOf || schema.anyOf) { + const options = schema.oneOf || schema.anyOf; + options.forEach((option: any) => { + if (option.properties) { + findSecretFields(option.properties, currentPath); + } + }); + } + + // Recurse into nested objects + if (schema.type === "object" && schema.properties) { + findSecretFields(schema.properties, currentPath); + } + }); + } + + findSecretFields(connectionSpec.properties); + return secretPaths; + } catch (error) { + log.atError().withCause(error).log(`Failed to get service secret paths for ${packageName}@${version}`); + return []; + } +} + +/** + * Mask secret values in an object based on the field paths + */ +export function maskSecrets(obj: any, secretPaths: string[]): any { + if (!obj || typeof obj !== "object") { + return obj; + } + + const result = JSON.parse(JSON.stringify(obj)); // Deep clone + + for (const path of secretPaths) { + const value = get(obj, path); + if (typeof value !== "undefined") { + set(result, path, MASKED_SECRET); // We don't mask non-set values + } + } + + return result; +} + +/** + * Remove masked values from an object before merge + */ +export function removeMaskedValues(obj: any, secretPaths: string[]): any { + if (!obj || typeof obj !== "object") { + return obj; + } + + const result = JSON.parse(JSON.stringify(obj)); // Deep clone + + for (const path of secretPaths) { + const value = get(obj, path); + if (value === MASKED_SECRET) { + unset(result, path); // Remove the masked value + } + } + + return result; +} + +/** + * Recursively replaces all occurrences of MASKED_SECRET in object with actual values from dbEntity. + * + * @param object - The d object being sent + * @param dbEntity - The original entity from database containing real secret values + * @returns A new object with masked secrets replaced by values from db + */ +export function unmaskSecretsFromOriginal(object: any, dbEntity: any): any { + if (!object || !dbEntity) { + return object; + } + + // Deep clone object to avoid mutating the original + const result = JSON.parse(JSON.stringify(object)); + + // Recursively find and replace masked secrets + replaceMaskedSecrets(result, dbEntity, []); + + return result; +} + +/** + * Recursively walks through an object and replaces MASKED_SECRET values with actual values from dbEntity + * + * @param obj - Current object being processed + * @param dbEntity - Original entity from database + * @param path - Current path in the object structure + */ +function replaceMaskedSecrets(obj: any, dbEntity: any, path: (string | number)[]): void { + if (!isPlainObject(obj) && !Array.isArray(obj)) { + return; + } + + if (Array.isArray(obj)) { + obj.forEach((item, index) => { + if (item === MASKED_SECRET) { + // Replace masked value with value from db at the same path + const dbValue = get(dbEntity, [...path, index]); + if (dbValue !== undefined) { + obj[index] = dbValue; + } + } else if (isPlainObject(item) || Array.isArray(item)) { + replaceMaskedSecrets(item, dbEntity, [...path, index]); + } + }); + } else { + Object.keys(obj).forEach(key => { + const value = obj[key]; + const currentPath = [...path, key]; + + if (value === MASKED_SECRET) { + // Replace masked value with value from db at the same path + const dbValue = get(dbEntity, currentPath); + if (dbValue !== undefined) { + obj[key] = dbValue; + } + } else if (isPlainObject(value) || Array.isArray(value)) { + replaceMaskedSecrets(value, dbEntity, currentPath); + } + }); + } +} + +/** + * Checks if an object contains any masked secrets + * + * @param obj - Object to check for masked secrets + * @returns true if object contains MASKED_SECRET values + */ +export function containsMaskedSecrets(obj: any): boolean { + if (!obj) { + return false; + } + + if (obj === MASKED_SECRET) { + return true; + } + + if (Array.isArray(obj)) { + return obj.some(item => containsMaskedSecrets(item)); + } + + if (isPlainObject(obj)) { + return Object.values(obj).some(value => containsMaskedSecrets(value)); + } + + return false; +} diff --git a/webapps/console/lib/server/audit-log.ts b/webapps/console/lib/server/audit-log.ts new file mode 100644 index 000000000..a2f5f11b6 --- /dev/null +++ b/webapps/console/lib/server/audit-log.ts @@ -0,0 +1,3 @@ +import { isTruish } from "../shared/chores"; + +export const enableAuditLog = isTruish(process.env.CONSOLE_ENABLE_AUDIT_LOG); diff --git a/webapps/console/lib/server/clickhouse.ts b/webapps/console/lib/server/clickhouse.ts new file mode 100644 index 000000000..cb4c23dec --- /dev/null +++ b/webapps/console/lib/server/clickhouse.ts @@ -0,0 +1,29 @@ +import { createClient } from "@clickhouse/client"; +import { isTruish } from "juava"; +import { getServerLog } from "./log"; + +const log = getServerLog("clickhouse"); + +function clickhouseHost() { + if (process.env.CLICKHOUSE_URL) { + return process.env.CLICKHOUSE_URL; + } + if (!process.env.CLICKHOUSE_HOST) { + log.atError().log("env CLICKHOUSE_HOST is not defined, using default 'localhost'"); + return "http://localhost"; + } + return `${isTruish(process.env.CLICKHOUSE_SSL) ? "https://" : "http://"}${process.env.CLICKHOUSE_HOST}`; +} + +export const clickhouse = createClient({ + url: clickhouseHost(), + username: process.env.CLICKHOUSE_USERNAME || "default", + password: process.env.CLICKHOUSE_PASSWORD || "", + compression: { + response: true, + }, +}); + +export function dateToClickhouse(date: Date): string { + return date.toISOString().replace("T", " ").replace("Z", "").split(".")[0]; +} diff --git a/webapps/console/lib/server/custom-domains.ts b/webapps/console/lib/server/custom-domains.ts index fae3870ba..3a6e652a9 100644 --- a/webapps/console/lib/server/custom-domains.ts +++ b/webapps/console/lib/server/custom-domains.ts @@ -1,23 +1,58 @@ import { db } from "./db"; -import { StreamConfig } from "../schema"; +import dns from "dns"; +import { getLog, requireDefined } from "juava"; +import { httpAgent, httpsAgent } from "./http-agent"; +import nodeFetch from "node-fetch-commonjs"; +import { z } from "zod"; +import { WorkspaceDbModel } from "../../prisma/schema"; +import { Prisma } from "@prisma/client"; type DomainAvailability = { available: true; usedInWorkspaces?: never } | { available: false; usedInWorkspace: string }; +export const customDomainCnames = process.env.CUSTOM_DOMAIN_CNAMES?.split(","); + +export function checkDomain(domain: string): boolean { + return !!domain.match(/^(?:[*][.])?(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,63}$/); +} /** * Tells if the given domain is used in other workspaces. */ -export async function isDomainAvailable(domain: string, workspaceId: string): Promise { - const pattern = `%${domain.toLowerCase()}%`; +export async function isDomainAvailable( + domain: string, + workspace: z.infer +): Promise { + const workspaceId = workspace.id; + const siblings = (workspace.featuresEnabled ?? []) + .filter(f => f.startsWith("sibling=")) + .flatMap(f => f.replace("sibling=", "").split(",")); + siblings.push(workspaceId); + + const domainSuffix = domain.replace(/^[*]/, ""); + const fullTextPattern = `%${domainSuffix.toLowerCase()}%`; + const pattern = `%${domainSuffix.toLowerCase()}`; const dirtyList = (await db.prisma().$queryRaw` - select "id", "workspaceId", "config" - from "ConfigurationObject" + select id,type, "workspaceId", config->'domains' as domains + from newjitsu."ConfigurationObject" where type = 'stream' - and config::TEXT ilike ${pattern} - and "workspaceId" <> ${workspaceId} - `) as { id: string; workspaceId: string; config: string }[]; - const list = dirtyList.filter(({ config, ...props }) => { - const stream = StreamConfig.parse({ ...(config as any), ...props }); - return (stream.domains || []).map(d => d.toLowerCase()).includes(domain.toLowerCase()); + and config::TEXT ilike ${fullTextPattern} + and "workspaceId" not in (${Prisma.join(siblings)}) + and deleted = false + union + select id,type, "workspaceId", jsonb_build_array(config->'name') as domains + from newjitsu."ConfigurationObject" + where type = 'domain' + and (config->>'name' ilike ${pattern} or ${domain.toLowerCase()} ilike REPLACE(config->>'name','*','%') ) + and "workspaceId" not in (${Prisma.join(siblings)}) + and deleted = false + `) as { id: string; workspaceId: string; domains: string[] }[]; + + const list = dirtyList.filter(({ domains }) => { + return ( + (domains || []).filter(d => d.toLowerCase().endsWith(domainSuffix)).length > 0 || + (domains || []).filter(d => { + return d.startsWith("*") && domain.toLowerCase().endsWith(d.toLowerCase().replace(/^[*]/, "")); + }).length > 0 + ); }); if (list.length > 0) { @@ -26,3 +61,66 @@ export async function isDomainAvailable(domain: string, workspaceId: string): Pr return { available: true }; } } + +export async function resolveCname(domain: string): Promise { + try { + return await new Promise((resolve, reject) => { + dns.resolveCname(domain, (err, addresses) => { + if (err) { + reject(err); + } else { + if (addresses.length === 1) { + resolve(addresses[0]); + } else if (!addresses || addresses.length === 0) { + resolve(undefined); + } else { + getLog() + .atWarn() + .log(`Domain ${domain} has multiple CNAME records: ${addresses.join(", ")}. Using first one`); + resolve(addresses[0]); + } + } + }); + }); + } catch (e) { + getLog().atError().withCause(e).log(`Domain ${domain} has no CNAME records`); + return undefined; + } +} + +export async function isDomainCnameValid(domain: string): Promise { + let cnameRecord: string | undefined; + try { + cnameRecord = await resolveCname(domain); + return await checkCname(cnameRecord); + } catch (e) { + getLog().atError().withCause(e).log(`Domain ${domain} has no CNAME records`); + return false; + } +} + +export async function checkCname(cname?: string): Promise { + if (!customDomainCnames || customDomainCnames.length == 0) { + throw new Error(`CUSTOM_DOMAIN_CNAMES is not set. isCnameValid() should not be called`); + } + return !!(cname && customDomainCnames.includes(cname.toLowerCase())); +} + +export async function checkOrAddToIngress(domain: string): Promise { + const ingmgrURLEnv = requireDefined(process.env.INGMGR_URL, "env INGMGR_URL is not defined"); + const ingmgrAuthKey = process.env.INGMGR_AUTH_KEY ?? ""; + const isHttps = ingmgrURLEnv.startsWith("https://"); + const options = { + agent: (isHttps ? httpsAgent : httpAgent)(), + headers: {}, + }; + if (ingmgrAuthKey) { + options.headers["Authorization"] = `Bearer ${ingmgrAuthKey}`; + } + try { + const response = await nodeFetch(ingmgrURLEnv + "/api/domain?name=" + domain, options); + return await response.json(); + } catch (e: any) { + return { error: e.message }; + } +} diff --git a/webapps/console/lib/server/data-domains.ts b/webapps/console/lib/server/data-domains.ts new file mode 100644 index 000000000..71774be96 --- /dev/null +++ b/webapps/console/lib/server/data-domains.ts @@ -0,0 +1,7 @@ +export const dataDomains: Set | undefined = process.env.DATA_DOMAIN + ? new Set(process.env.DATA_DOMAIN.split(",")) + : undefined; + +export const mainDataDomain: string | undefined = process.env.DATA_DOMAIN + ? process.env.DATA_DOMAIN.split(",")[0] + : undefined; diff --git a/webapps/console/lib/server/db.ts b/webapps/console/lib/server/db.ts index defa3705a..2672dd1ef 100644 --- a/webapps/console/lib/server/db.ts +++ b/webapps/console/lib/server/db.ts @@ -1,10 +1,12 @@ import { PrismaClient } from "@prisma/client"; -import { Pool } from "pg"; -import { namedParameters, newError, requireDefined, stopwatch } from "juava"; -import { getSingleton } from "juava"; +import { Pool, PoolClient } from "pg"; +import Cursor from "pg-cursor"; +import { getSingleton, namedParameters, newError, requireDefined, stopwatch, hideSensitiveInfo } from "juava"; import { getServerLog } from "./log"; +import { isReadOnly } from "./read-only-mode"; +import { isTruish } from "../shared/chores"; -export type Handler = (row: Record) => Promise | void; +export type Handler = (row: Record) => Promise | (boolean | undefined | void); //we will need to support named params in future export type ParamValues = any[] | Record; @@ -30,44 +32,41 @@ const pgHelper: PgHelper = { ); const { query: processedQuery, values: processedParams } = namedParameters(query, values || []); const sw = stopwatch(); - let queryResult; + let totalRows = 0; + let cursor: Cursor = undefined; + const client: PoolClient = await db.pgPool().connect(); try { - queryResult = await db.pgPool().query(processedQuery, processedParams); + cursor = client.query(new Cursor(processedQuery, processedParams)); + let rows = await cursor.read(100); + while (rows.length > 0) { + for (let i = 0; i < rows.length; i++) { + const abort = await handler(rows[i]); + if (abort) { + return { rows: totalRows }; + } + totalRows++; + } + rows = await cursor.read(100); + } } catch (e) { log .atError() .withCause(e) .log("Error executing query: \n" + processedQuery + "\n with params: " + JSON.stringify(processedParams)); throw newError("Error executing the query. See query in logs", e); + } finally { + if (cursor) { + await cursor.close(() => { + client.release(); + }); + } else if (client) { + client.release(); + } } - for (let i = 0; i < queryResult.rows.length; i++) { - const row = queryResult.rows[i]; - await handler(row); - } log.atDebug().log(`Query executed in ${sw.elapsedMs()}ms: ${processedQuery}${processedParams}`); - return { rows: queryResult.rowCount }; - - // const queryStream = new QueryStream(processedQuery, processedParams); - // - // const stream = await db.pg().query(queryStream); - // - // let rows = 0; - // console.log('Start streaming') - // return new Promise((resolve, reject) => { - // stream.on("error", err => { - // reject(err); - // }); - // stream.on("end", () => { - // resolve({ rows }); - // }); - // stream.on("data", async row => { - // rows++; - // console.log('Got row', row) - // await handler(row); - // }); - // }); + return { rows: totalRows }; }, }; @@ -82,28 +81,46 @@ export type DatabaseConnection = typeof db; export type PrismaSSLMode = "disable" | "prefer" | "require" | "no-verify"; export function createPg(): Pool { - const connectionUrl = requireDefined(process.env.DATABASE_URL, "env DATABASE_URL is not defined"); + const connectionUrl = getApplicationDatabaseUrl(); const parsedUrl = new URL(connectionUrl); const schema = parsedUrl.searchParams.get("schema"); + if (schema !== "newjitsu") { + const tBorder = `┌─────────────────────────────────────────────────────────────────────┐`; + const bBorder = `└─────────────────────────────────────────────────────────────────────┘`; + const msg = [ + "\n", + tBorder, + `│ Jitsu requires to connect to the database with "newjitsu" schema`.padEnd(tBorder.length - 2, " ") + "│", + bBorder, + ].join("\n"); + log.atError().log(msg); + throw new Error(`Invalid schema ${schema} in database connection URL. Expected 'newjitsu' schema.`); + } const sslMode = parsedUrl.searchParams.get("sslmode") || ("disable" as PrismaSSLMode); if (sslMode === "require" || sslMode === "prefer") { throw new Error(`sslmode=${sslMode} is not supported`); } const pool = new Pool({ + max: 20, + idleTimeoutMillis: 600000, connectionString: requireDefined(process.env.DATABASE_URL, "env.DATABASE_URL is not defined"), ssl: sslMode === "no-verify" ? { rejectUnauthorized: false } : undefined, + application_name: (parsedUrl.searchParams.get("application_name") || "console") + "-raw-pg", }); pool.on("connect", async client => { log .atInfo() .log( - `Connecting new client ${connectionUrl}. Pool stat: idle=${pool.idleCount}, waiting=${pool.waitingCount}, total=${pool.totalCount}` + - (schema ? `. Default schema: ${schema}` : "") + `Connecting new client ${hideSensitiveInfo(connectionUrl)}. Pool stat: idle=${pool.idleCount}, waiting=${ + pool.waitingCount + }, total=${pool.totalCount}` + (schema ? `. Default schema: ${schema}` : "") ); - if (schema) { - await client.query(`SET search_path TO "${schema}"`); - } + //this is commented on purpose, it won't work for pgbouncer in transaction mode https://www.pgbouncer.org/features.html + //let's leave it commented for information purposes + // if (schema) { + // await client.query(`SET search_path TO "${schema}"`); + // } }); pool.on("error", error => { log.atError().withCause(error).log("Pool error"); @@ -111,6 +128,29 @@ export function createPg(): Pool { return pool; } +const mutationActions = ["create", "update", "upsert", "delete"]; + +function blockPrismaMutations() { + return async (params, next) => { + const action = params.action; + if (mutationActions.find(candidate => action.indexOf(candidate) === 0)) { + throw new Error(`Prisma operation ${action} is not allowed in read-only mode`); + } + return await next(params); + }; +} + +export function getApplicationDatabaseUrl(): string { + return requireDefined( + process.env.APP_DATABASE_URL || process.env.DATABASE_URL, + "neither env.DATABASE_URL, nor env.APP_DATABASE_URL is not defined" + ); +} + +export function isUsingPgBouncer() { + return isTruish(new URL(getApplicationDatabaseUrl()).searchParams.get("pgbouncer")); +} + export function createPrisma(): PrismaClient { // @ts-ignore if (typeof window !== "undefined") { @@ -129,7 +169,13 @@ export function createPrisma(): PrismaClient { } ) as PrismaClient; } + log.atInfo().log("Initializing prisma"); const prisma = new PrismaClient({ + datasources: { + db: { + url: getApplicationDatabaseUrl(), + }, + }, log: [ { emit: "event", @@ -170,13 +216,19 @@ export function createPrisma(): PrismaClient { // return result // }) prisma.$on("query", e => { - log - .atInfo() - .log( - `SQL executed ${queryCounter++}. Duration: ${e.duration}ms: ${e.query.replaceAll("\n", " ").trim()}${ - e.params !== "[]" ? " " + e.params : "" - }` - ); + if (isTruish(process.env.CONSOLE_DATABASE_DEBUG)) { + log + .atDebug() + .log( + `SQL executed ${queryCounter++}. Duration: ${e.duration}ms: ${e.query.replaceAll("\n", " ").trim()}${ + e.params !== "[]" ? " " + e.params : "" + }` + ); + } }); + if (isReadOnly) { + prisma.$use(blockPrismaMutations()); + } + return prisma; } diff --git a/webapps/console/lib/server/ee.ts b/webapps/console/lib/server/ee.ts index a83dbaddd..d60ae4336 100644 --- a/webapps/console/lib/server/ee.ts +++ b/webapps/console/lib/server/ee.ts @@ -1,4 +1,4 @@ -import { requireDefined } from "juava"; +import { getErrorMessage, getLog, requireDefined, rpc } from "juava"; import jwt from "jsonwebtoken"; export function isEEAvailable(): boolean { @@ -26,17 +26,18 @@ export function getEeConnection(): EeConnection { }; } -export function createJwt( - userId: string, - email: string, - workspaceId: string, - expireInSecs: number -): { +type JWTResult = { //JWT token, jwt: string; //Token expiration date as ISO UTC expiresAt: string; -} { +}; + +export function createSystemJwt(): JWTResult { + return createJwt("admin-service-account@jitsu.com", "admin-service-account@jitsu.com", "$all", 60); +} + +export function createJwt(userId: string, email: string, workspaceId: string, expireInSecs: number): JWTResult { if (!isEEAvailable()) { throw new Error("EE is not available"); } @@ -46,11 +47,23 @@ export function createJwt( return { jwt: token, expiresAt: new Date(expiresSecondsTimestamp * 1000).toISOString() }; } -export type DomainStatus = - | { needsConfiguration: false } - | { needsConfiguration: true; configurationType: "cname"; cnameValue: string } - | { - needsConfiguration: true; - configurationType: "verification"; - verification: { type: string; domain: string; value: string }[]; - }; +export async function onUserCreated(opts: { email: string; name?: string }) { + if (!isEEAvailable()) { + return; + } + const url = `${getEeConnection().host}api/user-created`; + try { + await rpc(url, { + method: "POST", + body: opts, + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${createSystemJwt().jwt}`, + }, + }); + } catch (e: any) { + getLog() + .atError() + .log(`Error sending user (${JSON.stringify(opts)}) created event to EE: ${getErrorMessage(e)}`, e); + } +} diff --git a/webapps/console/lib/server/events-log.ts b/webapps/console/lib/server/events-log.ts index e4e08c7a6..178cdb98c 100644 --- a/webapps/console/lib/server/events-log.ts +++ b/webapps/console/lib/server/events-log.ts @@ -1,7 +1,6 @@ export type EventsLogFilter = { start?: Date; end?: Date; - beforeId?: string; filter?: (any) => boolean; }; diff --git a/webapps/console/lib/server/fast-store.ts b/webapps/console/lib/server/fast-store.ts deleted file mode 100644 index d212606ae..000000000 --- a/webapps/console/lib/server/fast-store.ts +++ /dev/null @@ -1,444 +0,0 @@ -import Redis from "ioredis"; -import { StreamConfig } from "../schema"; -import { DatabaseConnection, db, Handler } from "./db"; -import { omit, pick } from "lodash"; -import { randomId, requireDefined, stopwatch } from "juava"; -import { z } from "zod"; -import { ConfigurationObjectDbModel, ConfigurationObjectLinkDbModel, WorkspaceDbModel } from "../../prisma/schema"; -import { zParse } from "../shared/zod"; -import { BaseBulkerConnectionOptions, DestinationType, getCoreDestinationType } from "../schema/destinations"; -import { redis } from "./redis"; -import { getServerLog } from "./log"; -import hash from "object-hash"; -import { IngestType } from "@jitsu/protocols/async-request"; - -type RedisKey = { tmp(): string; used(): boolean; rename(redis: Redis): Promise; name(): string }; - -function redisKey(name: string): RedisKey { - let tmp: string | undefined = undefined; - return { - tmp: () => (tmp ? tmp : (tmp = `${name}_tmp_${randomId()}`)), - rename: async (redis: Redis) => { - if (tmp) { - await redis.rename(tmp, name); - } - }, - used() { - return !!tmp; - }, - name: () => name, - }; -} - -/** - * Redis map: - * * config:links - ${connectionId} -> ConfigurationObjectLinkDbModel - * * config:${type} - ${objectId} -> ConfigurationObjectDbModel - * * config:stream - ${streamId} -> StreamConfig - * * config:destination - ${destinationId} -> DestinationConfig - * - */ - -const redisKeyRoutes = { - configurationObjectsLink: () => redisKey(`config:links`), - configurationObject: (type: string) => redisKey(`config:${type}`), - streamIds: () => redisKey(`streamIds`), - apiKeys: () => redisKey(`apiKeys`), - streamDomain: () => redisKey(`streamDomains`), - enrichedConnections: () => redisKey(`enrichedConnections`), -}; - -/** - * Shortened destination config for that is saved to store. It contains only information needed - * to serve destination on the edge. - * - * See {@link DestinationConfig} for full destination config. - */ -export type ShortDestinationConfig = SyncShortDestinationConfig | AsyncShortDestinationConfig; - -export type CommonShortDestinationConfig = { - id: string; - connectionId: string; - destinationType: string; - credentials: any; - options: BaseBulkerConnectionOptions; -}; - -export type SyncShortDestinationConfig = { isSynchronous: true } & CommonShortDestinationConfig; - -export type AsyncShortDestinationConfig = { isSynchronous: false } & CommonShortDestinationConfig; - -export type StreamWithDestinations = { - stream: StreamConfig; - backupEnabled: boolean; - synchronousDestinations: ShortDestinationConfig[]; - asynchronousDestinations: ShortDestinationConfig[]; -}; - -export type EnrichedConnectionConfig = { - id: string; - workspaceId: string; - updatedAt: Date; - destinationId: string; - streamId: string; - metricsKeyPrefix: string; - usesBulker: boolean; - //destinationType - type: string; - options: BaseBulkerConnectionOptions; - - credentials: { - [key: string]: any; - }; - credentialsHash: string; -}; - -type ConfigurationObjectLinkDbModel = z.infer; - -type ApiKeyBinding = { hash: string; keyType: IngestType; streamId: string }; - -export type FastStore = { - getStreamsByDomain: (domain: string) => Promise; - getStreamById: (slug: string) => Promise; - getStreamByKeyId: (keyId: string) => Promise; - getConfig: (type: string, id: string) => Promise; - getConnection: (id: string) => Promise; - getEnrichedConnection: (id: string) => Promise; - - fullRefresh(); -}; - -export type ConfigObjectHandler = { - onObject(opts: { - obj: z.infer; - workspace: z.infer; - }): Promise; - end(hasData: boolean): Promise; - [k: string]: any; -}; - -function flatten(obj: z.infer) { - return { - ...((obj.config as any) || {}), - ...omit(obj, "config"), - }; -} - -function createDefaultHandler(type: string): ConfigObjectHandler { - const configTmpKey = redisKeyRoutes.configurationObject(type) + `_tmp_${randomId()}`; - return { - async onObject({ obj }) { - const serialisedObject = JSON.stringify(flatten(obj)); - await redis().hset(configTmpKey, obj.id, serialisedObject); - }, - async end(hasData) { - if (hasData) { - await redis().rename(configTmpKey, `config:${type}`); - } - }, - }; -} - -function prefixedWith(row: Record, pefix: string): Record { - return Object.fromEntries( - Object.entries(row) - .filter(([k]) => k.indexOf(pefix) === 0) - .map(([k, v]) => [k.substring(pefix.length), v]) - ); -} - -async function selectConfigObjectRows(type: string, db: DatabaseConnection, handler: Handler) { - return db.pgHelper().streamQuery( - `SELECT - obj.id as "obj_id", - obj."workspaceId" as "obj_workspaceId", - obj."type" as "obj_type", - obj."config" as "obj_config", - obj."createdAt" as "obj_createdAt", - obj."updatedAt" as "obj_updatedAt", - workspace.id as "workspace_id", - workspace.name as "workspace_name", - workspace."slug" as "workspace_slug", - workspace."createdAt" as "workspace_createdAt", - workspace."updatedAt" as "workspace_updatedAt", - workspace."deleted" as "workspace_deleted" - FROM "ConfigurationObject" obj - join "Workspace" workspace on obj."workspaceId" = workspace.id - WHERE workspace."deleted" = false AND - obj."type" = :type AND - obj."deleted" = false`, - { type }, - handler - ); -} - -async function saveConfigObjectsToRedis(types: string[], db: DatabaseConnection) { - for (const type of types) { - const handlers = [createDefaultHandler(type)]; - const { rows } = await selectConfigObjectRows(type, db, async row => { - const obj = ConfigurationObjectDbModel.parse(prefixedWith(row, "obj_")); - const workspace = WorkspaceDbModel.parse(prefixedWith(row, "workspace_")); - await Promise.all(handlers.map(h => h.onObject({ obj, workspace }))); - }); - await Promise.all(handlers.map(h => h.end(rows > 0))); - } -} - -function createDestinationConfig(type: DestinationType, row: any): ShortDestinationConfig { - return { - id: row.toId, - isSynchronous: !!type.isSynchronous, - destinationType: type.id, - credentials: omit(row.dstConfig, "name", "type", "destinationType"), - connectionId: row.id, - options: row.connectionData as BaseBulkerConnectionOptions, - } as ShortDestinationConfig; -} - -export function createEnrichedConnectionConfig( - connectionData: ConfigurationObjectLinkDbModel, - destinationConfig: ShortDestinationConfig, - src: StreamConfig, - usesBulker: boolean -): EnrichedConnectionConfig { - return { - id: connectionData.id, - workspaceId: connectionData.workspaceId, - destinationId: destinationConfig.id, - streamId: src.id, - metricsKeyPrefix: connectionData.workspaceId, - usesBulker: usesBulker, - type: destinationConfig.destinationType, - options: destinationConfig.options, - updatedAt: connectionData.updatedAt, - credentials: destinationConfig.credentials, - credentialsHash: hash(destinationConfig.credentials), - }; -} - -function createStreamWithDestinations( - streamConfig: StreamConfig, - destinations: ShortDestinationConfig[], - backupEnabled: boolean -): StreamWithDestinations { - return { - stream: streamConfig, - backupEnabled: backupEnabled, - asynchronousDestinations: destinations.filter(d => !getCoreDestinationType(d.destinationType).isSynchronous), - synchronousDestinations: destinations.filter(d => getCoreDestinationType(d.destinationType).isSynchronous), - }; -} - -async function saveConnectionsToRedis(db: DatabaseConnection) { - const backupSupported = process.env.S3_REGION && process.env.S3_ACCESS_KEY_ID && process.env.S3_SECRET_ACCESS_KEY; - const bulkerRedisKey = redisKeyRoutes.enrichedConnections(); - const domainsRedisKey = redisKeyRoutes.streamDomain(); - const idsRedisKey = redisKeyRoutes.streamIds(); - const apiKeys = redisKeyRoutes.apiKeys(); - const linksKey = redisKeyRoutes.configurationObjectsLink(); - const query = ` - select - greatest(link."updatedAt", src."updatedAt", dst."updatedAt") as "updatedAt", - link."createdAt" as "createdAt", - ws.id as "workspaceId", - link."id" as "id", - link.data as "connectionData", - src.id as "fromId", - src."config" as "srcConfig", - dst.id as "toId", - dst."config" as "dstConfig", - true as "backupEnabled" - from "ConfigurationObjectLink" link - join "ConfigurationObject" src on link."fromId" = src.id - join "ConfigurationObject" dst on link."toId" = dst.id - join "Workspace" ws on link."workspaceId" = ws.id - where link.deleted = false and - src."type" in ('stream', 'service') and - src."deleted" = false and - dst.type='destination' and - dst."deleted" = false and - ws."deleted" = false and src."workspaceId" = link."workspaceId" and src."workspaceId" = link."workspaceId" - order by "fromId", "toId" - `; - let destinationsBuffer: ShortDestinationConfig[] = []; - let lastStreamConfig: StreamConfig | undefined = undefined; - let lastBackupEnabled: boolean = false; - let streamsByDomain: Record> = {}; - const noDomainKey = "no-domain"; - const addStreamByDomain = ( - streamConfig: StreamConfig, - destinationsBuffer: ShortDestinationConfig[], - backupEnabled: boolean - ) => { - for (const domain of streamConfig.domains?.length ? streamConfig.domains : [noDomainKey]) { - let streams = streamsByDomain[domain.toLowerCase()]; - const stream = createStreamWithDestinations(streamConfig, destinationsBuffer, backupEnabled); - streams = streams ? { ...streams, [stream.stream.id]: stream } : { [stream.stream.id]: stream }; - streamsByDomain[domain.toLowerCase()] = streams; - } - }; - //to make sure we have all the streams in redis (even with no destinations) fill the buffer with all the streams first - await selectConfigObjectRows("stream", db, async row => { - const obj = ConfigurationObjectDbModel.parse(prefixedWith(row, "obj_")); - const streamConfig = flatten(obj); - addStreamByDomain(streamConfig, [], row.backupEnabled); - }); - await db.pgHelper().streamQuery(query, async row => { - const workspaceId = row.workspaceId; - const linkId = row.id; - const destinationType = requireDefined( - getCoreDestinationType(row.dstConfig.destinationType), - `Unknown destination type ${row.dstConfig.destinationType}` - ); - const destinationConfig = createDestinationConfig(destinationType, row); - const streamConfig = zParse(StreamConfig, { workspaceId, id: row.fromId, type: "stream", ...row.srcConfig }); - const backupDestCreated: Record = {}; - - const allConnectionParameters: ConfigurationObjectLinkDbModel = { - deleted: false, - id: linkId, - workspaceId, - ...pick(row, "createdAt", "updatedAt", "fromId", "toId"), - data: row.connectionData, - }; - - redis().hset(linksKey.tmp(), linkId, JSON.stringify(allConnectionParameters)); - - await redis().hset( - bulkerRedisKey.tmp(), - linkId, - JSON.stringify( - createEnrichedConnectionConfig( - allConnectionParameters, - destinationConfig, - streamConfig, - !!destinationType.usesBulker - ) - ) - ); - - if (backupSupported && row.backupEnabled && !backupDestCreated[row.workspaceId]) { - const backupConn: EnrichedConnectionConfig = { - id: `${row.workspaceId}_backup`, - workspaceId: row.workspaceId, - destinationId: `${row.workspaceId}_backup`, - streamId: "backup", - metricsKeyPrefix: row.workspaceId, - usesBulker: true, - type: "s3", - options: { - dataLayout: "segment-single-table", - deduplicate: false, - primaryKey: "", - timestampColumn: "", - frequency: 60, - batchSize: 1_000_000, - mode: "batch", - }, - updatedAt: new Date(), - credentials: { - region: process.env.S3_REGION, - accessKeyId: process.env.S3_ACCESS_KEY_ID, - secretAccessKey: process.env.S3_SECRET_ACCESS_KEY, - bucket: `${workspaceId}.data.use.jitsu.com`, - compression: "gzip", - format: "ndjson", - folder: "[DATE]", - }, - credentialsHash: "", - }; - await redis().hset(bulkerRedisKey.tmp(), `${row.workspaceId}_backup`, JSON.stringify(backupConn)); - backupDestCreated[row.workspaceId] = true; - } - - // when we reach new stream, we need to save destinationsBuffer for the previous stream - if (lastStreamConfig && streamConfig.id !== lastStreamConfig.id && destinationsBuffer.length > 0) { - addStreamByDomain(lastStreamConfig, destinationsBuffer, row.backupEnabled); - destinationsBuffer = []; - } - destinationsBuffer.push(destinationConfig); - lastStreamConfig = streamConfig; - lastBackupEnabled = row.backupEnabled; - }); - - // save the last stream - if (lastStreamConfig && destinationsBuffer.length > 0) { - addStreamByDomain(lastStreamConfig, destinationsBuffer, lastBackupEnabled); - } - const apiKeysTmp = apiKeys.tmp(); - for (const [domain, streamDsts] of Object.entries(streamsByDomain)) { - //store array of StreamWithDestinations by domain - await redis().hset(domainsRedisKey.tmp(), domain.toLowerCase(), JSON.stringify(Object.values(streamDsts))); - - for (const id in streamDsts) { - const strm = streamDsts[id]; - //store each StreamWithDestinations by stream id separately - await redis().hset(idsRedisKey.tmp(), id, JSON.stringify(strm)); - - await Promise.all( - (strm.stream.publicKeys ?? []) - .filter(k => !!k.hash) - .map(k => - redis().hset(apiKeysTmp, k.id, JSON.stringify({ hash: k.hash as string, streamId: id, keyType: "browser" })) - ) - ); - await Promise.all( - (strm.stream.privateKeys ?? []) - .filter(k => !!k.hash) - .map(k => - redis().hset(apiKeysTmp, k.id, JSON.stringify({ hash: k.hash as string, streamId: id, keyType: "s2s" })) - ) - ); - } - } - - await apiKeys.rename(redis()); - await idsRedisKey.rename(redis()); - await linksKey.rename(redis()); - await domainsRedisKey.rename(redis()); - await bulkerRedisKey.rename(redis()); -} - -export function createFastStore({ db }: { db: DatabaseConnection }): FastStore { - return { - getStreamByKeyId(keyId: string): Promise { - return redis() - .hget(redisKeyRoutes.apiKeys().name(), keyId) - .then(raw => (raw ? JSON.parse(raw) : undefined)); - }, - async getStreamById(slug: string): Promise { - const raw = await redis().hget(redisKeyRoutes.streamIds().name(), slug); - if (!raw) { - return undefined; - } - return JSON.parse(raw); - }, - async getStreamsByDomain(domain: string): Promise { - const raw = await redis().hget(redisKeyRoutes.streamDomain().name(), domain.toLowerCase()); - if (!raw) { - return undefined; - } - return JSON.parse(raw); - }, - async getConfig(type: string, id: string): Promise { - const config = await redis().hget(redisKeyRoutes.configurationObject(type).name(), id); - return config ? JSON.parse(config) : undefined; - }, - async getConnection(id: string): Promise { - const config = await redis().hget(redisKeyRoutes.configurationObjectsLink().name(), id); - return config ? JSON.parse(config) : undefined; - }, - async getEnrichedConnection(id: string): Promise { - const config = await redis().hget(redisKeyRoutes.enrichedConnections().name(), id); - return config ? JSON.parse(config) : undefined; - }, - async fullRefresh() { - const sw = stopwatch(); - await saveConfigObjectsToRedis(["stream", "destination", "function"], db); - await saveConnectionsToRedis(db); - getServerLog().atInfo().log("Export to redis took", sw.elapsedPretty()); - }, - }; -} - -export const fastStore = createFastStore({ db }); diff --git a/webapps/console/lib/server/firebase-server.ts b/webapps/console/lib/server/firebase-server.ts index e6668d3a3..a30e8e511 100644 --- a/webapps/console/lib/server/firebase-server.ts +++ b/webapps/console/lib/server/firebase-server.ts @@ -1,9 +1,10 @@ import { SessionUser } from "../schema"; import { NextApiRequest } from "next"; -import * as admin from "firebase-admin"; +import admin from "firebase-admin"; import * as JSON5 from "json5"; import { getErrorMessage, getSingleton, requireDefined, Singleton } from "juava"; +import { getServerLog } from "./log"; export type FirebaseOptions = { admin: any; @@ -40,6 +41,10 @@ export function isFirebaseEnabled(): boolean { return !!(process.env.FIREBASE_AUTH || (process.env.FIREBASE_ADMIN && process.env.FIREBASE_CLIENT_CONFIG)); } +export function isGithubEnabled(): boolean { + return !!(process.env.GITHUB_CLIENT_ID && process.env.GITHUB_CLIENT_SECRET); +} + const bearerPrefix = "bearer "; const firebaseService: Singleton = getSingleton("firebase-service", () => { @@ -57,7 +62,7 @@ export function firebase(): admin.app.App { return requireDefined(firebaseService(), `Something went wrong, firebaseService is not initialized`); } -export const firebaseAuthCookieName = "fb-auth"; +export const firebaseAuthCookieName = "jitsu-auth"; export type FirebaseToken = { idToken: string; cookieToken?: never } | { idToken?: never; cookieToken: string }; @@ -82,7 +87,11 @@ export async function createSessionCookie(idToken: string): Promise<{ cookie; ex return { cookie, expiresIn }; } -export async function getFirebaseUser(req: NextApiRequest): Promise { +export async function signOut(firebaseUserId: string): Promise { + await firebase().auth().revokeRefreshTokens(firebaseUserId); +} + +export async function getFirebaseUser(req: NextApiRequest, checkRevoked?: boolean): Promise { const authToken = getFirebaseToken(req); if (!authToken) { return undefined; @@ -90,11 +99,20 @@ export async function getFirebaseUser(req: NextApiRequest): Promise("http-agent", createHTTPAgent); -export const httpsAgent = getSingleton("https-agent", createHTTPSAgent); +export const httpAgent = getSingleton("http-agent", createHTTPAgent, { silent: true }); +export const httpsAgent = getSingleton("https-agent", createHTTPSAgent, { silent: true }); async function createHTTPAgent(): Promise { const agent = new Agent({ timeout: 300000, freeSocketTimeout: 30000, maxSockets: 1024 }); diff --git a/webapps/console/lib/server/kafka-config.ts b/webapps/console/lib/server/kafka-config.ts deleted file mode 100644 index 92628a200..000000000 --- a/webapps/console/lib/server/kafka-config.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { Kafka, logLevel, CompressionCodecs, CompressionTypes } from "kafkajs"; -import SnappyCodec from "kafkajs-snappy"; -import "@sensejs/kafkajs-zstd-support"; - -import { LogMessageBuilder, requireDefined, randomId } from "juava"; -import JSON5 from "json5"; -import { getServerLog } from "./log"; - -CompressionCodecs[CompressionTypes.Snappy] = SnappyCodec; - -const log = getServerLog("kafka"); - -function translateLevel(l: logLevel): LogMessageBuilder { - switch (l) { - case logLevel.ERROR: - return log.atError(); - case logLevel.WARN: - return log.atWarn(); - case logLevel.INFO: - return log.atDebug(); - case logLevel.DEBUG: - return log.atDebug(); - default: - return log.atInfo(); - } -} - -export type KafkaCredentials = { - brokers: string[] | string; - ssl?: boolean; - sasl?: { - mechanism: "scram-sha-256" | "scram-sha-512"; - username: string; - password: string; - }; -}; - -export function getCredentialsFromEnv(): KafkaCredentials { - return { - brokers: requireDefined(process.env.KAFKA_BOOTSTRAP_SERVERS, "env KAFKA_BOOTSTRAP_SERVERS is required").split(","), - ssl: process.env.KAFKA_SSL === "true" || process.env.KAFKA_SSL === "1", - sasl: process.env.KAFKA_SASL ? JSON5.parse(process.env.KAFKA_SASL) : undefined, - }; -} - -export function connectToKafka(opts: { defaultAppId: string } & KafkaCredentials): Kafka { - const sasl = opts.sasl - ? { - sasl: opts.sasl as any, - } - : {}; - log.atDebug().log("SASL config", JSON.stringify(opts.sasl)); - return new Kafka({ - logLevel: logLevel.ERROR, - // logCreator: logLevel => log => { - // translateLevel(logLevel).log( - // `${log.namespace ? `${log.namespace} # ` : ""}${JSON.stringify(omit(log.log, "timestamp", "logger"))}` - // ); - // }, - clientId: process.env.APPLICATION_ID || opts.defaultAppId, - brokers: typeof opts.brokers === "string" ? opts.brokers.split(",") : opts.brokers, - ssl: opts.ssl - ? { - rejectUnauthorized: false, - checkServerIdentity: () => undefined, - } - : undefined, - - ...sasl, - }); -} - -export function destinationMessagesTopic(): string { - return process.env.KAFKA_DESTINATIONS_TOPIC_NAME || "destination-messages"; -} - -export function rotorConsumerGroupId(): string { - return process.env.KAFKA_CONSUMER_GROUP_ID !== undefined - ? process.env.KAFKA_CONSUMER_GROUP_ID.replace("$random", randomId(5)) - : "rotor"; -} diff --git a/webapps/console/lib/server/kafka-sink.ts b/webapps/console/lib/server/kafka-sink.ts deleted file mode 100644 index 34745ca8e..000000000 --- a/webapps/console/lib/server/kafka-sink.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { getSingleton } from "juava"; -import { connectToKafka, getCredentialsFromEnv } from "./kafka-config"; - -export type KafkaSink = { - send( - topic: { name: string }, - message: { body: Record; key?: string; headers?: Record } - ): Promise; -}; - -export const kafkaSink = getSingleton("kafka", createKafka); - -async function createKafka(): Promise { - const kafka = connectToKafka({ defaultAppId: "jitsu-sink", ...getCredentialsFromEnv() }); - const producer = kafka.producer(); - await producer.connect(); - return { - send: async (topic, { body, key, headers }) => { - await producer.send({ - topic: topic.name, - messages: [{ value: JSON.stringify(body), key, headers }], - }); - }, - }; -} diff --git a/webapps/console/lib/server/log.ts b/webapps/console/lib/server/log.ts index 340431c5c..249a0474b 100644 --- a/webapps/console/lib/server/log.ts +++ b/webapps/console/lib/server/log.ts @@ -1,10 +1,11 @@ import { getLog, LoggerOpts, LogLevel, setGlobalLogLevel, setServerJsonFormat, setServerLogColoring } from "juava"; +setGlobalLogLevel((process.env.LOG_LEVEL || "info") as LogLevel); +setServerLogColoring( + process.env.DISABLE_SERVER_LOGS_ANSI_COLORING !== "true" && process.env.DISABLE_SERVER_LOGS_ANSI_COLORING !== "1" +); +setServerJsonFormat(process.env.LOG_FORMAT === "json"); + export function getServerLog(_opts?: LoggerOpts | string) { - setGlobalLogLevel((process.env.LOG_LEVEL || "info") as LogLevel); - setServerLogColoring( - process.env.DISABLE_SERVER_LOGS_ANSI_COLORING !== "true" && process.env.DISABLE_SERVER_LOGS_ANSI_COLORING !== "1" - ); - setServerJsonFormat(process.env.LOG_FORMAT === "json"); return getLog(_opts); } diff --git a/webapps/console/lib/server/mail.tsx b/webapps/console/lib/server/mail.tsx index 17adcd21e..b458f553d 100644 --- a/webapps/console/lib/server/mail.tsx +++ b/webapps/console/lib/server/mail.tsx @@ -2,14 +2,47 @@ import nodemailer from "nodemailer"; import Mail from "nodemailer/lib/mailer"; import { getErrorMessage } from "juava"; import { db } from "./db"; +import mjml2html from "mjml"; -import { render } from "mjml-react"; -import { getSingleton } from "juava"; +import { randomUUID } from "crypto"; +import { renderToString } from "react-dom/server"; -async function init() { +function parseConnectionString(connectionString: string) { + if (connectionString.startsWith("smtp://")) { + connectionString = connectionString.substring("smtp://".length); + } + const atIndex = connectionString.lastIndexOf("@"); + if (atIndex < 0) { + throw new Error(`Invalid SMTP connection string ${connectionString}`); + } + const auth = connectionString.substring(0, atIndex); + const hostAndPort = connectionString.substring(atIndex + 1); + const [host, port = "587"] = hostAndPort.split(":"); + const colonIndex = auth.lastIndexOf(":"); + const [user, password] = + colonIndex < 0 ? [auth, ""] : [auth.substring(0, colonIndex), auth.substring(colonIndex + 1)]; + return { host, port, user, password }; +} + +function initNodeMailer() { if (process.env.SMTP_CONNECTION_STRING) { + const { host, port, user, password } = parseConnectionString(process.env.SMTP_CONNECTION_STRING); + try { - return nodemailer.createTransport(process.env.SMTP_CONNECTION_STRING); + const credentials = { + host, + port: parseInt(port), + auth: { + user, + pass: password, + }, + secure: parseInt(port) === 465, + tls: { + rejectUnauthorized: false, + }, + }; + //console.log("SMTP credentials", credentials) + return nodemailer.createTransport(credentials); } catch (e) { console.error( `Error initializing SMTP transport ${process.env.SMTP_CONNECTION_STRING}: ${getErrorMessage(e)}`, @@ -18,31 +51,50 @@ async function init() { throw new Error(`Can't connect to SMTP server`); } } else { - const testAccount = await nodemailer.createTestAccount(); - - return nodemailer.createTransport({ - host: "smtp.ethereal.email", - port: 587, - secure: false, // true for 465, false for other ports - auth: { - user: testAccount.user, // generated ethereal user - pass: testAccount.pass, // generated ethereal password - }, - }); + return undefined; + + // const testAccount = await nodemailer.createTestAccount(); + // + // return nodemailer.createTransport({ + // host: "smtp.ethereal.email", + // port: 587, + // secure: false, // true for 465, false for other ports + // auth: { + // user: testAccount.user, // generated ethereal user + // pass: testAccount.pass, // generated ethereal password + // }, + // }); } } +const transport = initNodeMailer(); + export function isMailAvailable() { return !!process.env.SMTP_CONNECTION_STRING; } export async function sendEmail(mailOptions: Mail.Options) { - const transporter = await getSingleton("nodeMailer", init)(); + if (!mailOptions.from) { + mailOptions.from = process.env.EMAIL_TRANSACTIONAL_SENDER + ? process.env.EMAIL_TRANSACTIONAL_SENDER.replace("Jitsu Support", "Jitsu Team") + : undefined; + } + if (!mailOptions.replyTo) { + mailOptions.replyTo = process.env.EMAIL_TRANSACTIONAL_REPLY_TO + ? process.env.EMAIL_TRANSACTIONAL_REPLY_TO.replace("Jitsu Support", "Jitsu Team") + : undefined; + } const logEntry = await db.prisma().emailLog.create({ - data: { status: "PENDING", email: JSON.parse(JSON.stringify(mailOptions)) }, + data: { id: randomUUID(), status: "PENDING", email: JSON.parse(JSON.stringify(mailOptions)) }, }); + if (!transport) { + await db + .prisma() + .emailLog.update({ where: { id: logEntry.id }, data: { status: "SKIPPED", error: "SMTP is not configured" } }); + return; + } try { - const info = await transporter.sendMail(mailOptions); + const info = await transport.sendMail(mailOptions); await db.prisma().emailLog.update({ where: { id: logEntry.id }, data: { status: "SENT", messageId: info.messageId, previewUrl: nodemailer.getTestMessageUrl(info) || null }, @@ -50,10 +102,14 @@ export async function sendEmail(mailOptions: Mail.Options) { } catch (e: any) { const errorText = `${getErrorMessage(e)}\n${e?.stack}`; await db.prisma().emailLog.update({ where: { id: logEntry.id }, data: { status: "FAILED", error: errorText } }); - throw e; + throw new Error(`Error sending email to ${mailOptions.to}: ${errorText}`, e); } } -export function renderEmailTemplate(Component: React.FC<{ data: any }>, data: any) { - return render().html; +export async function renderEmailTemplate(Component: React.FC<{ data: any }>, data: any) { + const str = renderToString(); + const renderedEmail = await mjml2html(str, { + validationLevel: "soft", + }); + return renderedEmail.html; } diff --git a/webapps/console/lib/server/oauth/destinations.ts b/webapps/console/lib/server/oauth/destinations.ts new file mode 100644 index 000000000..21eb4614b --- /dev/null +++ b/webapps/console/lib/server/oauth/destinations.ts @@ -0,0 +1,26 @@ +export type OptionsObject = Record; + +export type OauthDecorator = { + /** + * ID of the package. Same as ConnectorPackage.packageId + */ + destinationType: string; + + /** + * Provider id for nango (github, googlesheets, etc). See https://github.com/NangoHQ/nango/blob/master/packages/shared/providers.yaml + */ + nangoProvider: (cred: any) => string; + + /** + * Integration id for nango. + */ + nangoIntegrationId: (cred: any) => string; +}; + +export const oauthDecorators: Record = { + salesforce: { + destinationType: "salesforce", + nangoProvider: cred => (cred.isSandbox ? "salesforce-sandbox" : "salesforce"), + nangoIntegrationId: cred => (cred.isSandbox ? "jitsu-cloud-dst-salesforce-sandbox" : "jitsu-cloud-dst-salesforce"), + }, +}; diff --git a/webapps/console/lib/server/oauth/services.ts b/webapps/console/lib/server/oauth/services.ts new file mode 100644 index 000000000..20cf40851 --- /dev/null +++ b/webapps/console/lib/server/oauth/services.ts @@ -0,0 +1,286 @@ +import { ServiceConfig } from "../../schema"; +import { requireDefined, rpc } from "juava"; +import { nangoConfig } from "./nango-config"; + +export type PackageId = `airbyte/${string}`; +/** + * Decorates services with OAuth authentication + */ + +export const JITSU_MANAGED = "JITSU_MANAGED"; + +function manage(original: string, provided: string) { + if (original === JITSU_MANAGED) { + return provided; + } else { + return original; + } +} + +export type OptionsObject = Record; + +export type OauthDecorator = { + /** + * ID of the package. Same as ConnectorPackage.packageId + */ + packageId: PackageId; + /** + * Type of the package. Same as ConnectorPackage.packageType + */ + packageType: "airbyte"; + + /** + * Provider id for nango (github, googlesheets, etc). See https://github.com/NangoHQ/nango/blob/master/packages/shared/providers.yaml + */ + nangoProvider: string; + + /** + * Integration id for nango. If not set, `jitsu-cloud-sync-${nangoProvider}` + */ + nangoIntegrationId?: string; + + /** + * @param original connector config, as defined by airbyte + * @param integrationObj nango integration object, it usually contains client id and secret, sometimes scopes + * @param connectionObj connection object, containing auth and refresh tokens + */ + merge: (opts: OptionsObject, integrationObj: OptionsObject, connectionObj: OptionsObject) => OptionsObject; + + /** + * Removes credentials fields from schema + * @param schema + */ + stripSchema: (schema: any) => any; +}; + +function fillDefaults(dec: OauthDecorator): Required { + return { + nangoIntegrationId: `jitsu-cloud-sync-${dec.nangoProvider}`, + ...dec, + }; +} + +const github: OauthDecorator = { + stripSchema: (schema: any) => { + return { + ...schema, + credentials: { + access_token: JITSU_MANAGED, + client_id: JITSU_MANAGED, + client_secret: JITSU_MANAGED, + option_title: "OAuth Credentials", + }, + }; + }, + packageId: "airbyte/source-github", + packageType: "airbyte", + nangoProvider: "github", + merge: (opts, integrationObj, connectionObj) => { + const mCred = { ...opts.credentials }; + if (mCred.option_title === "OAuth Credentials") { + mCred.access_token = manage(mCred.access_token, connectionObj.access_token); + mCred.client_id = manage(mCred.client_id, integrationObj.client_id); + mCred.client_secret = manage(mCred.client_secret, integrationObj.client_secret); + } + return { + ...opts, + credentials: { + ...opts.credentials, + ...mCred, + }, + }; + }, +}; + +const salesforce: OauthDecorator = { + stripSchema: (schema: any) => { + return { + ...schema, + refresh_token: JITSU_MANAGED, + client_id: JITSU_MANAGED, + client_secret: JITSU_MANAGED, + }; + }, + packageId: "airbyte/source-salesforce", + packageType: "airbyte", + nangoProvider: "salesforce", + merge: (opts, integrationObj, connectionObj) => { + const mCred = { ...opts }; + mCred.refresh_token = manage(mCred.refresh_token, connectionObj.refresh_token); + mCred.client_id = manage(mCred.client_id, integrationObj.client_id); + mCred.client_secret = manage(mCred.client_secret, integrationObj.client_secret); + return mCred; + }, +}; + +const salesforceSinger: OauthDecorator = { + stripSchema: (schema: any) => { + return { + ...schema, + refresh_token: JITSU_MANAGED, + client_id: JITSU_MANAGED, + client_secret: JITSU_MANAGED, + }; + }, + packageId: "airbyte/source-salesforce-singer", + packageType: "airbyte", + nangoProvider: "salesforce", + merge: (opts, integrationObj, connectionObj) => { + const mCred = { ...opts }; + mCred.refresh_token = manage(mCred.refresh_token, connectionObj.refresh_token); + mCred.client_id = manage(mCred.client_id, integrationObj.client_id); + mCred.client_secret = manage(mCred.client_secret, integrationObj.client_secret); + return mCred; + }, +}; + +const _googleBase: Omit = { + stripSchema: (schema: any) => { + return { + ...schema, + credentials: { + access_token: JITSU_MANAGED, + refresh_token: JITSU_MANAGED, + client_id: JITSU_MANAGED, + client_secret: JITSU_MANAGED, + auth_type: "Client", + }, + }; + }, + packageType: "airbyte", + nangoProvider: "google", + merge: (opts, integrationObj, connectionObj) => { + const mCred = { ...opts.credentials }; + if (mCred.auth_type === "Client") { + mCred.access_token = manage(mCred.access_token, connectionObj.access_token); + mCred.refresh_token = manage(mCred.refresh_token, connectionObj.refresh_token); + mCred.client_id = manage(mCred.client_id, integrationObj.client_id); + mCred.client_secret = manage(mCred.client_secret, integrationObj.client_secret); + } + return { + ...opts, + credentials: { + ...opts.credentials, + ...mCred, + }, + }; + }, +}; + +const googleAnalytics: OauthDecorator = { + ..._googleBase, + packageId: "airbyte/source-google-analytics-v4", + nangoIntegrationId: "jitsu-cloud-sync-google-analytics", +}; + +const googleAnalyticsGA4: OauthDecorator = { + ..._googleBase, + packageId: "airbyte/source-google-analytics-data-api", + nangoIntegrationId: "jitsu-cloud-sync-google-analytics", +}; + +const googleAds: OauthDecorator = { + ..._googleBase, + stripSchema: (schema: any) => { + return { + ...schema, + credentials: { + access_token: JITSU_MANAGED, + refresh_token: JITSU_MANAGED, + developer_token: JITSU_MANAGED, + client_id: JITSU_MANAGED, + client_secret: JITSU_MANAGED, + }, + }; + }, + merge: (opts, integrationObj, connectionObj) => { + const mCred = { ...opts.credentials }; + mCred.developer_token = manage( + mCred.developer_token, + requireDefined( + process.env.GOOGLE_ADS_DEVELOPER_TOKEN, + "GOOGLE_ADS_DEVELOPER_TOKEN is not configured, google ads integration will not work" + ) + ); + mCred.access_token = manage(mCred.access_token, connectionObj.access_token); + mCred.refresh_token = manage(mCred.refresh_token, connectionObj.refresh_token); + mCred.client_id = manage(mCred.client_id, integrationObj.client_id); + mCred.client_secret = manage(mCred.client_secret, integrationObj.client_secret); + return { + ...opts, + credentials: { + ...opts.credentials, + ...mCred, + }, + }; + }, + packageId: "airbyte/source-google-ads", + nangoIntegrationId: "jitsu-cloud-sync-google-ads", +}; + +const googleSheets: OauthDecorator = { + ..._googleBase, + packageId: "airbyte/source-google-sheets", + nangoIntegrationId: "jitsu-cloud-sync-google-sheets", +}; + +const facebookMarketing: OauthDecorator = { + stripSchema: (schema: any) => { + return { + ...schema, + access_token: JITSU_MANAGED, + client_id: JITSU_MANAGED, + client_secret: JITSU_MANAGED, + }; + }, + packageId: "airbyte/source-facebook-marketing", + packageType: "airbyte", + nangoProvider: "facebook", + nangoIntegrationId: "jitsu-cloud-sync-facebook", + merge: (opts, integrationObj, connectionObj) => { + const mCred = { ...opts }; + mCred.access_token = manage(mCred.access_token, connectionObj.access_token); + mCred.client_id = manage(mCred.client_id, integrationObj.client_id); + mCred.client_secret = manage(mCred.client_secret, integrationObj.client_secret); + return mCred; + }, +}; + +export const oauthDecorators = [ + github, + salesforce, + salesforceSinger, + googleAnalytics, + googleAnalyticsGA4, + googleAds, + googleSheets, + facebookMarketing, +].map(fillDefaults); + +// If service supports Jitsu OAuth - returns decorated credentials part of service config +// otherwise returns original credentials part of config +export const tryManageOauthCreds = async (service: ServiceConfig): Promise => { + if (service.authorized && nangoConfig.enabled) { + const oauthDecorator = requireDefined( + oauthDecorators.find(d => d.packageId === service.package), + `Package ${service.package} for service ${service.id} not found in catalog` + ); + const credentials = service.credentials; + const integrationId = oauthDecorator.nangoIntegrationId; + + const integrationSettings = await rpc(`${nangoConfig.nangoApiHost}/config/${integrationId}?include_creds=true`, { + headers: { Authorization: `Bearer ${nangoConfig.secretKey}` }, + }); + const nangoConnectionObject = await rpc( + `${nangoConfig.nangoApiHost}/connection/sync-source.${service.id}?provider_config_key=${integrationId}&refresh_token=true`, + { headers: { Authorization: `Bearer ${nangoConfig.secretKey}` } } + ); + + // getLog().atInfo().log("Integration settings", JSON.stringify(integrationSettings, null, 2)); + // getLog().atInfo().log("Configuration object", JSON.stringify(nangoConnectionObject, null, 2)); + + return oauthDecorator.merge(credentials, integrationSettings.config, nangoConnectionObject.credentials); + } else { + return service.credentials; + } +}; diff --git a/webapps/console/lib/server/oauth/services.tsx b/webapps/console/lib/server/oauth/services.tsx deleted file mode 100644 index 474fc3ffc..000000000 --- a/webapps/console/lib/server/oauth/services.tsx +++ /dev/null @@ -1,68 +0,0 @@ -export type PackageId = `airbyte/${string}`; -/** - * Decorates services with OAuth authentication - */ - -export type OptionsObject = Record; - -export type OauthDecorator = { - /** - * ID of the package. Same as ConnectorPackage.packageId - */ - packageId: PackageId; - /** - * Type of the package. Same as ConnectorPackage.packageType - */ - packageType: "airbyte"; - - /** - * Provider id for nango (github, googlesheets, etc). See https://github.com/NangoHQ/nango/blob/master/packages/shared/providers.yaml - */ - nangoProvider: string; - - /** - * Integration id for nango. If not set, `jitsu-cloud-sync-${nangoProvider}` - */ - nangoIntegrationId?: string; - - /** - * @param original connector config, as defined by airbyte - * @param integrationObj nango integration object, it usually contains client id and secret, sometimes scopes - * @param connectionObj connection object, containing auth and refresh tokens - */ - merge: (opts: OptionsObject, integrationObj: OptionsObject, connectionObj: OptionsObject) => OptionsObject; - - /** - * Removes credentials fields from schema - * @param schema - */ - stripSchema(schema: any): any; -}; - -export function fillDefaults(dec: OauthDecorator): Required { - return { - nangoIntegrationId: `jitsu-cloud-sync-${dec.nangoProvider}`, - ...dec, - }; -} - -export const github: OauthDecorator = { - stripSchema(schema: any): any { - return schema; - }, - packageId: "airbyte/source-github", - packageType: "airbyte", - nangoProvider: "github", - merge: (opts, integrationObj, connectionObj) => { - return { - ...opts, - ...integrationObj, - credentials: { - access_token: connectionObj.access_token, - option_title: "OAuth Credentials", - }, - }; - }, -}; - -export const oauthDecorators = [github]; diff --git a/webapps/console/lib/server/oidc-discovery.ts b/webapps/console/lib/server/oidc-discovery.ts new file mode 100644 index 000000000..aba1474cd --- /dev/null +++ b/webapps/console/lib/server/oidc-discovery.ts @@ -0,0 +1,110 @@ +import { getServerLog } from "./log"; +import { db } from "./db"; +import { OidcProviderDbModel } from "./oidc-token-service"; + +const log = getServerLog("oidc-discovery"); + +export interface OidcDiscoveryDocument { + issuer: string; + authorization_endpoint: string; + token_endpoint: string; + userinfo_endpoint?: string; + jwks_uri: string; + introspection_endpoint?: string; + scopes_supported?: string[]; + response_types_supported?: string[]; + grant_types_supported?: string[]; + [key: string]: any; +} + +/** + * Fetches OIDC discovery document from the well-known endpoint + * @param issuer The OIDC issuer URL + * @returns The discovery document or null if failed + */ +export async function fetchOidcDiscovery(issuer: string): Promise { + try { + // Ensure issuer doesn't end with a slash for the well-known URL + const normalizedIssuer = issuer.replace(/\/$/, ""); + const wellKnownUrl = `${normalizedIssuer}/.well-known/openid-configuration`; + + log.atInfo().log("Fetching OIDC discovery document", { wellKnownUrl }); + + const response = await fetch(wellKnownUrl); + + if (!response.ok) { + log.atWarn().log("Failed to fetch OIDC discovery document", { + status: response.status, + statusText: response.statusText, + issuer, + }); + return null; + } + + const discovery: OidcDiscoveryDocument = await response.json(); + + // Validate that the discovery document has required fields + if (!discovery.authorization_endpoint || !discovery.token_endpoint) { + log.atWarn().log("Invalid OIDC discovery document - missing required endpoints", { + issuer, + hasAuthEndpoint: !!discovery.authorization_endpoint, + hasTokenEndpoint: !!discovery.token_endpoint, + hasJwksUri: !!discovery.jwks_uri, + }); + return null; + } + + log.atInfo().log("Successfully fetched OIDC discovery document", { issuer }); + return discovery; + } catch (error) { + log.atError().withCause(error).log("Error fetching OIDC discovery document", { issuer }); + return null; + } +} + +/** + * Updates OIDC provider with discovered endpoints + * @param providerId The provider ID in database + * @param discovery The discovery document + * @returns true if updated successfully + */ +export async function updateProviderWithDiscovery( + providerId: string, + discovery: OidcDiscoveryDocument +): Promise { + try { + await db.prisma().oidcProvider.update({ + where: { id: providerId }, + data: { + authorizationEndpoint: discovery.authorization_endpoint, + tokenEndpoint: discovery.token_endpoint, + userinfoEndpoint: discovery.userinfo_endpoint, + jwksUri: discovery.jwks_uri, + introspectionEndpoint: discovery.introspection_endpoint, + }, + }); + + log.atInfo().log("Updated OIDC provider with discovered endpoints", { providerId }); + return true; + } catch (error) { + log.atError().withCause(error).log("Failed to update OIDC provider with discovery", { providerId }); + return false; + } +} + +/** + * Performs OIDC auto-discovery and updates provider if needed + * @param provider The OIDC provider configuration + * @returns The discovery document or null if auto-discovery is disabled or failed + */ +export async function performAutoDiscovery(provider: OidcProviderDbModel): Promise { + if (!provider.autoDiscovery) { + return null; + } + const discovery = await fetchOidcDiscovery(provider.issuer); + if (discovery) { + await updateProviderWithDiscovery(provider.id, discovery); + } + + return discovery; +} diff --git a/webapps/console/lib/server/oidc-error-handler.ts b/webapps/console/lib/server/oidc-error-handler.ts new file mode 100644 index 000000000..0a802a9df --- /dev/null +++ b/webapps/console/lib/server/oidc-error-handler.ts @@ -0,0 +1,61 @@ +import { NextApiResponse } from "next"; +import { getServerLog } from "./log"; + +const log = getServerLog("oidc-error-handler"); + +export interface OidcErrorRedirectOptions { + error: string; + message?: string; + returnUrl?: string; +} + +/** + * Centralized error redirect handler for OIDC flows + * @param res NextApiResponse object + * @param options Error details and optional return URL + */ +export function redirectWithOidcError(res: NextApiResponse, options: OidcErrorRedirectOptions): void { + const { error, message, returnUrl } = options; + + // Build query parameters + const params = new URLSearchParams({ + error, + ...(message && { message: message }), + ...(returnUrl && { callbackUrl: returnUrl }), + }); + + const redirectUrl = `/signin?${params.toString()}`; + + log.atWarn().log("Redirecting with OIDC error", { + error, + message, + hasReturnUrl: !!returnUrl, + redirectUrl, + }); + + res.redirect(redirectUrl); +} + +/** + * Common OIDC error types + */ +export const OidcErrors = { + AUTH_ERROR: "oidc_auth_error", + MISSING_PARAMS: "missing_params", + INVALID_STATE: "invalid_state", + STATE_EXPIRED: "state_expired", + PROVIDER_NOT_FOUND: "provider_not_found", + TOKEN_EXCHANGE_FAILED: "token_exchange_failed", + INVALID_ID_TOKEN: "invalid_id_token", + INVALID_ISSUER: "invalid_issuer", + INVALID_AUDIENCE: "invalid_audience", + INVALID_NONCE: "invalid_nonce", + TOKEN_EXPIRED: "token_expired", + USERINFO_FETCH_FAILED: "userinfo_fetch_failed", + NO_USER_INFO: "no_user_info", + NO_EMAIL: "no_email", + NO_WORKSPACE_ACCESS: "no_workspace_access", + INTERNAL_ERROR: "internal_error", +} as const; + +export type OidcErrorType = (typeof OidcErrors)[keyof typeof OidcErrors]; diff --git a/webapps/console/lib/server/oidc-token-service.ts b/webapps/console/lib/server/oidc-token-service.ts new file mode 100644 index 000000000..31e7a6664 --- /dev/null +++ b/webapps/console/lib/server/oidc-token-service.ts @@ -0,0 +1,261 @@ +import jwt from "jsonwebtoken"; +import jwksClient from "jwks-rsa"; +import NodeCache from "node-cache"; +import { getServerLog } from "./log"; +import { db } from "./db"; +import { OidcTokens } from "./oidc-types"; +import { OidcProviderDbModel } from "../../prisma/schema"; +import { z } from "zod"; + +const log = getServerLog("oidc-token-service"); + +interface TokenClaims { + sub: string; + email?: string; + name?: string; + preferred_username?: string; + groups?: string[]; + exp: number; + iat: number; + iss: string; + aud: string | string[]; + [key: string]: any; +} + +interface TokenIntrospectionResponse { + active: boolean; + client_id?: string; + username?: string; + scope?: string; + exp?: number; + iat?: number; + sub?: string; + aud?: string; + iss?: string; + token_type?: string; +} + +// Cache for JWKS clients with 5-minute TTL +const jwksClientCache = new NodeCache({ stdTTL: 300, checkperiod: 60 }); // 5 minutes TTL, check every minute + +// Cache for introspection results with 2-minute TTL +const introspectionCache = new NodeCache({ stdTTL: 120, checkperiod: 30 }); // 2 minutes TTL, check every 30 seconds + +const oidcProviderCache = new NodeCache({ stdTTL: 300, checkperiod: 60 }); // 5 minutes TTL for OIDC provider data + +export type OidcProviderDbModel = z.infer; + +export async function getOidcProvider(providerId: string): Promise { + const cached = oidcProviderCache.get(providerId); + + if (cached) { + return cached as OidcProviderDbModel; + } + + return await db + .prisma() + .oidcProvider.findUnique({ + where: { id: providerId, enabled: true }, + }) + .then(provider => { + if (provider) { + oidcProviderCache.set(providerId, provider); + } + return provider; + }); +} +function getJwksClient(jwksUri: string): jwksClient.JwksClient { + const cached = jwksClientCache.get(jwksUri); + + if (cached) { + return cached; + } + + const client = jwksClient({ + jwksUri, + cache: true, + cacheMaxEntries: 5, + cacheMaxAge: 300000, // 5 minutes + rateLimit: true, + jwksRequestsPerMinute: 10, + }); + + jwksClientCache.set(jwksUri, client); + return client; +} + +export async function validateJwtToken( + token: string, + providerId: string +): Promise<{ valid: boolean; claims?: TokenClaims; error?: string }> { + try { + const oidcProvider = await getOidcProvider(providerId); + + if (!oidcProvider) { + return { valid: false, error: "OIDC provider not found" }; + } + + // Decode token header to get key ID + const decoded = jwt.decode(token, { complete: true }); + if (!decoded || typeof decoded === "string") { + return { valid: false, error: "Invalid token format" }; + } + + const { header, payload } = decoded; + + // Get JWKS URI from provider configuration or discovery + let jwksUri = oidcProvider.jwksUri; + if (!jwksUri) { + return { valid: false, error: "JWKS URI not available" }; + } + + // Get signing key + const client = getJwksClient(jwksUri); + const key = await client.getSigningKey(header.kid); + const publicKey = key.getPublicKey(); + + // Verify the token + const claims = jwt.verify(token, publicKey, { + ...(oidcProvider.audience ? { audience: oidcProvider.audience } : {}), + issuer: oidcProvider.issuer, + algorithms: ["RS256", "RS384", "RS512", "ES256", "ES384", "ES512"], + }) as TokenClaims; + + return { valid: true, claims }; + } catch (error) { + log.atWarn().withCause(error).log("JWT validation failed", { providerId }); + return { valid: false, error: error instanceof Error ? error.message : "Token validation failed" }; + } +} + +export async function introspectToken( + token: string, + providerId: string +): Promise<{ valid: boolean; claims?: TokenIntrospectionResponse; error?: string }> { + try { + // Check cache first + const cacheKey = `${providerId}:${token.substring(0, 16)}`; // Use first 16 chars to avoid storing full token + const cached = introspectionCache.get(cacheKey); + + if (cached) { + return { valid: cached.active, claims: cached }; + } + + const oidcProvider = await getOidcProvider(providerId); + + if (!oidcProvider) { + return { valid: false, error: "OIDC provider not found" }; + } + + let introspectionEndpoint = oidcProvider.introspectionEndpoint; + if (!introspectionEndpoint) { + return { valid: false, error: "Token introspection endpoint not available" }; + } + + // Perform token introspection + const response = await fetch(introspectionEndpoint, { + method: "POST", + headers: { + "Content-Type": "application/x-www-form-urlencoded", + Authorization: `Basic ${Buffer.from(`${oidcProvider.clientId}:${oidcProvider.clientSecret}`).toString( + "base64" + )}`, + }, + body: new URLSearchParams({ + token, + token_type_hint: "access_token", + }).toString(), + }); + + if (!response.ok) { + return { valid: false, error: "Token introspection request failed" }; + } + + const result: TokenIntrospectionResponse = await response.json(); + + // Cache the result with TTL based on token expiration + let ttl = 120; // Default 2 minutes + if (result.exp) { + const expiresInSeconds = result.exp - Math.floor(Date.now() / 1000); + if (expiresInSeconds > 0) { + // Cache until token expires, but with a minimum of 30 seconds and maximum of 300 seconds (5 minutes) + ttl = Math.max(30, Math.min(300, expiresInSeconds)); + } + } + + introspectionCache.set(cacheKey, result, ttl); + + return { valid: result.active, claims: result }; + } catch (error) { + log.atWarn().withCause(error).log("Token introspection failed", { providerId }); + return { valid: false, error: error instanceof Error ? error.message : "Token introspection failed" }; + } +} + +export async function refreshAccessToken( + refreshToken: string, + providerId: string +): Promise<{ success: boolean; tokens?: OidcTokens; error?: string }> { + try { + // Get OIDC provider configuration + const oidcProvider = await getOidcProvider(providerId); + + if (!oidcProvider) { + return { success: false, error: "OIDC provider not found" }; + } + + const tokenEndpoint = oidcProvider.tokenEndpoint || `${oidcProvider.issuer}/token`; + + // Refresh the access token + const response = await fetch(tokenEndpoint, { + method: "POST", + headers: { + "Content-Type": "application/x-www-form-urlencoded", + Authorization: `Basic ${Buffer.from(`${oidcProvider.clientId}:${oidcProvider.clientSecret}`).toString( + "base64" + )}`, + }, + body: new URLSearchParams({ + grant_type: "refresh_token", + refresh_token: refreshToken, + client_id: oidcProvider.clientId, + }).toString(), + }); + + if (!response.ok) { + const errorText = await response.text(); + log.atWarn().log("Failed to refresh access token", { providerId, error: errorText }); + return { success: false, error: "Token refresh failed" }; + } + + const tokenResponse = await response.json(); + + const tokens: OidcTokens = { + accessToken: tokenResponse.access_token, + refreshToken: tokenResponse.refresh_token || refreshToken, // Use new refresh token if provided, otherwise keep the old one + idToken: tokenResponse.id_token, + expiresAt: Date.now() + (tokenResponse.expires_in || 3600) * 1000, + }; + + return { success: true, tokens }; + } catch (error) { + log.atWarn().withCause(error).log("Token refresh failed", { providerId }); + return { success: false, error: error instanceof Error ? error.message : "Token refresh failed" }; + } +} + +export function isTokenExpired(expiresAt: number): boolean { + return Date.now() >= expiresAt - 60000; // Consider expired if less than 1 minute remaining +} + +export function isJwtToken(token: string): boolean { + try { + const header = token.split(".")[0]; + const json = Buffer.from(header, "base64url").toString("utf8"); + const jwtHeader = JSON.parse(json); + return jwtHeader && jwtHeader.alg; + } catch (e: any) { + log.atWarn().withCause(e).log("Failed to decode JWT token", { token }); + } + return false; +} diff --git a/webapps/console/lib/server/oidc-types.ts b/webapps/console/lib/server/oidc-types.ts new file mode 100644 index 000000000..e70ea96a4 --- /dev/null +++ b/webapps/console/lib/server/oidc-types.ts @@ -0,0 +1,40 @@ +export interface OidcSessionData { + userId: string; + email?: string | null; + name?: string | null; + loginProvider: string; + externalId: string; + providerId?: string; + timestamp: number; + exp: number; + tokens?: { + accessToken: string; + refreshToken?: string; + idToken?: string; + expiresAt: number; + }; +} + +export interface OidcTokens { + accessToken: string; + refreshToken?: string; + idToken?: string; + expiresAt: number; +} + +export interface OidcTokenResponse { + access_token: string; + token_type: string; + id_token?: string; + refresh_token?: string; + expires_in?: number; +} + +export interface OidcUserInfo { + sub: string; + email?: string; + name?: string; + preferred_username?: string; + groups?: string[]; + [key: string]: any; +} diff --git a/webapps/console/lib/server/origin.ts b/webapps/console/lib/server/origin.ts new file mode 100644 index 000000000..6e9c3ce0a --- /dev/null +++ b/webapps/console/lib/server/origin.ts @@ -0,0 +1,21 @@ +import { NextApiRequest } from "next"; + +export function getRequestHost(req: NextApiRequest) { + return (req.headers["x-forwarded-host"] || req.headers.host) as string; +} + +export function getTopLevelDomain(requestDomain: string): string { + const parts = requestDomain.split("."); + if (parts.length < 2) { + return parts[0]; + } + return parts.slice(-2).join("."); +} + +export function isSecure(req: NextApiRequest) { + const forwardedProto = req.headers["x-forwarded-proto"]; + if (forwardedProto === "https") { + return true; + } + return !!((req.socket || {}) as any).encrypted; +} diff --git a/webapps/console/lib/server/read-only-mode.ts b/webapps/console/lib/server/read-only-mode.ts new file mode 100644 index 000000000..d5de785a2 --- /dev/null +++ b/webapps/console/lib/server/read-only-mode.ts @@ -0,0 +1,24 @@ +import { getLog } from "juava"; + +export const isReadOnly = !!process.env.JITSU_CONSOLE_READ_ONLY_UNTIL; + +export const readOnlyUntil = getReadOnlyUntil(); + +function getReadOnlyUntil(): Date | undefined { + if (!isReadOnly) { + return undefined; + } + let readOnlyUntil; + try { + readOnlyUntil = new Date(process.env.JITSU_CONSOLE_READ_ONLY_UNTIL!); + } catch (e) { + getLog() + .atWarn() + .log( + `Read only until is not a valid date: ${process.env.JITSU_CONSOLE_READ_ONLY_UNTIL}. Setting a date 2 hours from now` + ); + readOnlyUntil = new Date(Date.now() + 2 * 60 * 60 * 1000); + } + getLog().atInfo().log(`Read only mode enabled until: ${readOnlyUntil}`); + return readOnlyUntil; +} diff --git a/webapps/console/lib/server/redis.ts b/webapps/console/lib/server/redis.ts deleted file mode 100644 index a7f29ab99..000000000 --- a/webapps/console/lib/server/redis.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { getSingleton } from "juava"; -import Redis from "ioredis"; -import { requireDefined } from "juava"; -import { getServerLog } from "./log"; - -export const log = getServerLog("redis"); - -export const redis = getSingleton("redis", createRedis); - -function hideSensitiveInfoFromURL(url: string) { - let parsed: URL; - try { - parsed = new URL(url); - } catch (e) { - //if URL is not parseable, we just return it as is. We can't fail and - //rethrow error - return url; - } - if (parsed.password) { - parsed.password = "****"; - } - return parsed.toString(); -} - -function createRedis(): Redis { - const redisUrl = requireDefined(process.env.REDIS_URL, "env REDIS_URL is not defined"); - log.atDebug().log(`Building redis client for ${hideSensitiveInfoFromURL(redisUrl)}`); - let tls: any = undefined; - if (redisUrl.startsWith("rediss://")) { - tls = { - rejectUnauthorized: false, - }; - } - const redisClient = new Redis(requireDefined(process.env.REDIS_URL, "env REDIS_URL is not defined"), { - maxRetriesPerRequest: 3, - tls: tls, - lazyConnect: false, - }); - redisClient.on("error", err => { - log - .atWarn() - .withCause(err) - .log(`Redis @ ${hideSensitiveInfoFromURL(redisUrl)} - failed to connect`); - }); - redisClient.on("connect", () => { - log.atInfo().log(`Redis @ ${hideSensitiveInfoFromURL(redisUrl)} - successfully connected`); - }); - return redisClient; -} diff --git a/webapps/console/lib/server/sync.ts b/webapps/console/lib/server/sync.ts new file mode 100644 index 000000000..8d40e1862 --- /dev/null +++ b/webapps/console/lib/server/sync.ts @@ -0,0 +1,1046 @@ +import { CloudSchedulerClient } from "@google-cloud/scheduler"; +import { db } from "./db"; +import { ConfigurationObject, ConfigurationObjectLink } from "@prisma/client"; +import { hash as juavaHash, LogFactory, randomId, requireDefined, rpc, stopwatch } from "juava"; +import { google } from "@google-cloud/scheduler/build/protos/protos"; +import { difference } from "lodash"; +import { getServerLog } from "./log"; +import { getAppEndpoint } from "../domains"; +import { NextApiRequest } from "next"; +import { createJwt, getEeConnection, isEEAvailable } from "./ee"; +import { DestinationConfig, ServiceConfig, SessionUser } from "../schema"; +import { randomUUID } from "crypto"; +import { tryManageOauthCreds } from "./oauth/services"; +import { DestinationType, getCoreDestinationType } from "../schema/destinations"; +import omit from "lodash/omit"; +import { FunctionLogger, SetOpts, Store, SyncFunction } from "@jitsu/protocols/functions"; +import { mixpanelFacebookAdsSync, mixpanelGoogleAdsSync } from "./syncs/mixpanel"; +import hash from "stable-hash"; +import { clickhouse } from "./clickhouse"; +import { SyncDbModel } from "../../pages/api/[workspaceId]/config/link"; +import IJob = google.cloud.scheduler.v1.IJob; +import { initStream } from "../sources"; + +const metricsSchema = process.env.CLICKHOUSE_METRICS_SCHEMA || process.env.CLICKHOUSE_DATABASE || "newjitsu_metrics"; +const clickhouseUploadS3Bucket = process.env.CLICKHOUSE_UPLOAD_S3_BUCKET; +const s3Region = process.env.S3_REGION; +const s3AccessKeyId = process.env.S3_ACCESS_KEY_ID; +const s3SecretAccessKey = process.env.S3_SECRET_ACCESS_KEY; +const clickhouseS3Configured = clickhouseUploadS3Bucket && s3Region && s3AccessKeyId && s3SecretAccessKey; + +const log = getServerLog("sync-scheduler"); + +const googleSchedulerLocation = process.env.GOOGLE_SCHEDULER_LOCATION || "us-central1"; +const googleScheduler = createGoogleSchedulerClient(); + +export type ScheduleSyncError = { ok: false; error: string; [key: string]: any }; +export type ScheduleSyncSuccess = { ok: true; taskId: string; [key: string]: any }; +export type ScheduleSyncResult = ScheduleSyncError | ScheduleSyncSuccess; + +export const syncError = ( + log: LogFactory, + message: string, + error: any, + mask: boolean = false, + ...privateArgs: any[] +): ScheduleSyncError => { + const errorId = randomId(8); + const publicMessage = mask + ? `Internal server error. Please contact support. Error ID: ${errorId}` + : `${message}. Error ${errorId}: ${error}.`; + log + .atError() + .withCause(error) + .log(message, `Error ID: ${errorId}`, ...privateArgs); + return { + ok: false, + error: publicMessage, + }; +}; + +async function dbLog({ + taskId, + syncId, + message, + level, +}: { + taskId: string; + message: string; + syncId: string; + level: string; +}) { + log.at(level).log(`Task ${taskId} sync ${syncId}: ${message}`); + await clickhouse.insert({ + table: metricsSchema + ".task_log", + format: "JSON", + clickhouse_settings: { + async_insert_busy_timeout_ms: 1000, + async_insert: 1, + wait_for_async_insert: 0, + }, + values: { + timestamp: new Date().getTime(), + logger: "sync", + task_id: taskId, + sync_id: syncId, + message, + level, + }, + }); +} + +async function createOrUpdateTask({ + taskId, + syncId, + status, + startedBy, + description, + pkg = "jitsu", + version = "0.0.1", +}: { + taskId: string; + syncId: string; + status: string; + startedBy: any; + description: string; + pkg?: string; + version?: string; +}) { + const taskData = { + sync_id: syncId, + task_id: taskId, + status, + started_at: new Date(), + updated_at: new Date(), + started_by: startedBy, + description, + package: pkg, + version: version, + }; + await db.prisma().source_task.upsert({ + where: { task_id: taskId }, + create: taskData, + update: omit(taskData, "task_id"), + }); +} + +export async function checkQuota(opts: { + user?: SessionUser; + trigger: "manual" | "scheduled"; + workspaceId: string; + syncId: string; + package: string; + version: string; + startedBy: any; +}): Promise { + try { + const quotaCheck = `${getEeConnection().host}api/quotas/sync`; + let eeAuthToken: string | undefined; + if (opts.user) { + eeAuthToken = createJwt(opts.user.internalId, opts.user.email, opts.workspaceId, 60).jwt; + } else { + //automatic run, authorized via syncctl auth key. Authorize as admin + eeAuthToken = createJwt("admin-service-account@jitsu.com", "admin-service-account@jitsu.com", "$all", 60).jwt; + } + const quotaCheckResult = await rpc(quotaCheck, { + method: "POST", + query: { workspaceId: opts.workspaceId, trigger: opts.trigger }, //db is created, so the slug won't be really used + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${eeAuthToken}`, + }, + }); + if (!quotaCheckResult.ok) { + if (!opts.user) { + const taskId = randomUUID(); + //scheduled run. We need to create a failed task so user can see the error + await createOrUpdateTask({ + taskId, + syncId: opts.syncId, + status: "SKIPPED", + startedBy: opts.startedBy, + description: `Quota exceeded: ${quotaCheckResult.error}`, + pkg: opts.package, + version: opts.version, + }); + await dbLog({ + taskId, + syncId: opts.syncId, + message: `Quota exceeded: ${quotaCheckResult.error}`, + level: "ERROR", + }); + } + return { + ok: false, + error: `Quota exceeded: ${quotaCheckResult.error}`, + errorType: "quota_exceeded", + }; + } + } catch (e) { + log.atError().log("Error checking quota", e); + //ignore this error and proceed with the run. If billing server is down, we don't want to spoil the user experience + } +} + +export async function catalogFromDb(packageName: string, version: string, storageKey: string) { + const res = await db.pgPool().query( + `select catalog + from newjitsu.source_catalog + where key = $1 + and package = $2 + and version = $3`, + [storageKey, packageName, version] + ); + if (res.rowCount === 1) { + return res.rows[0].catalog; + } else { + return null; + } +} + +export function selectStreamsFromCatalog(catalog: any, syncOptions: any): any { + const selectedStreams: Record = syncOptions?.streams || {}; + const disabledStreams: Record = syncOptions?.disabledStreams || {}; + const schemaChanges = syncOptions?.schemaChanges; + const hasIncremental = Object.values(selectedStreams).some((s: any) => s.sync_mode === "incremental"); + + const streams = catalog.streams + .filter((s: any) => { + const name = s.namespace ? s.namespace + "." + s.name : s.name; + return !!selectedStreams[name] || (schemaChanges === "streams" && !disabledStreams[name]); + }) + .map((s: any) => { + const name = s.namespace ? s.namespace + "." + s.name : s.name; + let stream = selectedStreams[name]; + if (!stream) { + stream = initStream(s, hasIncremental ? "incremental" : "full_refresh"); + } + return { + ...omit(stream, "table_name"), + destination_sync_mode: "overwrite", + stream: { + ...s, + table_name: stream.table_name, + }, + }; + }); + return { streams }; +} + +export type SyncDatabaseModel = ConfigurationObjectLink & { from: ConfigurationObject; to: ConfigurationObject }; + +export async function getSyncById(syncId: string, workspaceId: string): Promise { + return ( + (await db.prisma().configurationObjectLink.findFirst({ + where: { + id: syncId, + workspaceId: workspaceId, + deleted: false, + type: "sync", + workspace: { deleted: false }, + from: { deleted: false, workspaceId: workspaceId }, + to: { deleted: false, workspaceId: workspaceId }, + }, + include: { + from: true, + to: true, + }, + })) || undefined + ); +} + +function createDatabaseLogger(taskId: string, syncId: string): FunctionLogger { + return { + debug: async (message: string) => { + await dbLog({ + taskId, + syncId, + message, + level: "DEBUG", + }); + }, + info: async (message: string) => { + await dbLog({ + taskId, + syncId, + message, + level: "INFO", + }); + }, + error: async (message: string) => { + await dbLog({ + taskId, + syncId, + message, + level: "ERROR", + }); + }, + warn: async (message: string) => { + await dbLog({ + taskId, + syncId, + message, + level: "WARN", + }); + }, + }; +} + +type SaasSyncState = { + dict: Record< + string, + { + value: any; + //ISO date + expireAt?: string; + } + >; +}; + +function createDatabaseStore(taskId: string, syncId: string): Store { + const stream = "cloud-sync"; + + async function getSaasSyncState(): Promise { + return ((await db.prisma().source_state.findFirst({ where: { sync_id: syncId, stream } }))?.state || { + dict: {}, + }) as SaasSyncState; + } + + async function saveSaasSyncState(state: SaasSyncState) { + await db.prisma().source_state.upsert({ + where: { sync_id_stream: { sync_id: syncId, stream } }, + create: { + sync_id: syncId, + stream, + state, + }, + update: { + state, + }, + }); + } + + return { + del: async (key: string): Promise => { + const state = await getSaasSyncState(); + delete state.dict[key]; + await saveSaasSyncState(state); + }, + get: async (key: string): Promise => { + return (await getSaasSyncState()).dict[key]?.value; + }, + set: async (key: string, value: any, opts?: SetOpts): Promise => { + if (opts) { + throw new Error("Custom TTLs are not supported for Cloud Syncs sync"); + } + const state = await getSaasSyncState(); + state.dict[key] = { value }; + await saveSaasSyncState(state); + }, + ttl: (key: string): Promise => Promise.reject(new Error("Not implemented")), + }; +} + +function getImplemetingFunction(pkg: string, destinationType: DestinationType): SyncFunction { + if (destinationType.id === "mixpanel" && pkg === "airbyte/source-google-ads") { + return mixpanelGoogleAdsSync as any; + } else if (destinationType.id === "mixpanel" && pkg === "airbyte/source-facebook-marketing") { + return mixpanelFacebookAdsSync as any; + } + + throw new Error(`${pkg} -> ${destinationType.id} sync doesn't exist`); +} + +async function runSyncSynchronously({ + syncId, + taskId, + destinationConfig, + destinationType, + sourceConfig, + startedBy, +}: { + syncId: string; + taskId: string; + destinationType: DestinationType; + destinationConfig: DestinationConfig; + sourceConfig: ServiceConfig; + startedBy: any; +}) { + await createOrUpdateTask({ + taskId, + syncId, + startedBy, + status: "RUNNING", + description: "Started", + pkg: sourceConfig.package, + version: sourceConfig.version, + }); + const syncConfig = destinationType?.syncs?.[sourceConfig.package]; + if (!syncConfig) { + await createOrUpdateTask({ + taskId, + syncId, + startedBy, + status: "FAILED", + description: `Sync function not found for package ${sourceConfig.package}`, + pkg: sourceConfig.package, + version: sourceConfig.version, + }); + return; + } + await dbLog({ + taskId, + syncId, + message: `Running sync from ${sourceConfig.package} -> ${destinationType.title} (#${destinationType.id})`, + level: "INFO", + }); + const credentials = await tryManageOauthCreds(sourceConfig); + + const implementingFunction = getImplemetingFunction(sourceConfig.package, destinationType); + await dbLog({ + taskId, + syncId, + level: "INFO", + message: `Successfully connected to to ${sourceConfig.package}. Running sync`, + }); + + await implementingFunction({ + source: { + package: sourceConfig.package, + credentials, + syncProps: sourceConfig, + }, + destination: destinationConfig, + ctx: { + log: createDatabaseLogger(taskId, syncId), + store: createDatabaseStore(taskId, syncId), + }, + }); + + await createOrUpdateTask({ + taskId, + syncId, + startedBy, + status: "SUCCESS", + description: "Successfully finished", + pkg: sourceConfig.package, + version: sourceConfig.version, + }); +} + +function safeStringify(e: any) { + try { + return JSON.stringify(e, null, 2); + } catch (e) { + return e?.toString(); + } +} + +export async function scheduleSync({ + workspaceId, + syncIdOrModel, + user, + trigger = "manual", + req, + fullSync, + ignoreRunning, + skipRefresh, + nodelay, + taskId, +}: { + workspaceId: string; + syncIdOrModel: string | SyncDatabaseModel; + trigger?: "manual" | "scheduled"; + user?: SessionUser; + req: NextApiRequest; + fullSync?: boolean; + ignoreRunning?: boolean; + skipRefresh?: boolean; + nodelay?: boolean; + taskId?: string; +}): Promise { + const syncAuthKey = process.env.SYNCCTL_AUTH_KEY ?? ""; + taskId = taskId || randomUUID(); + const syncURL = requireDefined( + process.env.SYNCCTL_URL, + `env SYNCCTL_URL is not set. Sync Controller is required to run sources` + ); + const startedBy = + trigger === "manual" ? (user ? { trigger: "manual", ...user } : { trigger: "manual" }) : { trigger: "scheduled" }; + const authHeaders: any = {}; + if (syncAuthKey) { + authHeaders["Authorization"] = `Bearer ${syncAuthKey}`; + } + try { + const appBase = getAppEndpoint(req).baseUrl; + const sync = typeof syncIdOrModel === "string" ? await getSyncById(syncIdOrModel, workspaceId) : syncIdOrModel; + if (!sync) { + return { + ok: false, + error: `Sync ${syncIdOrModel} not found`, + }; + } + const service = sync.from; + if (!service) { + return { + ok: false, + error: `Service ${sync.from} not found`, + }; + } + const destinationConfig = sync.to.config as DestinationConfig; + const destinationType = getCoreDestinationType(destinationConfig.destinationType); + const serviceConfig = { ...(service.config as any), ...service }; + const runSynchronously = !(destinationType.usesBulker || destinationType.id === "webhook") && destinationType.syncs; + // for normal scheduled syncs syncctl handles 'already running' case + if (trigger === "manual" || runSynchronously) { + const running = await db.prisma().source_task.findFirst({ + where: { + sync_id: syncIdOrModel as string, + status: "RUNNING", + }, + }); + + if (running) { + const msInMin = 1000 * 60; + if (ignoreRunning || (runSynchronously && Date.now() - running.updated_at.getTime() >= 2 * msInMin)) { + await dbLog({ + taskId: running.task_id, + syncId: sync.id, + message: `Synchronous task ${running.task_id} was running due to timeout`, + level: "ERROR", + }); + await db.prisma().source_task.update({ + where: { + task_id: running.task_id, + }, + data: { + status: "FAILED", + updated_at: new Date(), + }, + }); + } else { + return { + ok: false, + error: `Sync is already running`, + runningTask: { + taskId: running.task_id, + status: `${appBase}/api/${workspaceId}/sources/tasks?taskId=${running.task_id}&syncId=${syncIdOrModel}`, + logs: `${appBase}/api/${workspaceId}/sources/logs?taskId=${running.task_id}&syncId=${syncIdOrModel}`, + }, + }; + } + } + } + + if (isEEAvailable()) { + const checkResult = await checkQuota({ + user, + trigger, + workspaceId, + syncId: sync.id, + package: (service.config as any).package, + version: (service.config as any).version, + startedBy, + }); + if (checkResult) { + return checkResult; + } + } + let stateObj: any = undefined; + if (fullSync) { + await db.prisma().source_state.deleteMany({ + where: { + sync_id: sync.id, + }, + }); + } else { + stateObj = await loadState(sync); + } + if (runSynchronously) { + const started = Date.now(); + try { + await runSyncSynchronously({ + taskId, + syncId: sync.id, + destinationConfig, + destinationType, + sourceConfig: serviceConfig, + startedBy, + }); + const time = Date.now() - started; + await dbLog({ + taskId, + syncId: sync.id, + message: `Sync finished in ${time}ms`, + level: "INFO", + }); + } catch (e: any) { + log + .atError() + .log(`Error running task ${taskId}, sync ${sync.id}. Message : ${e?.message}`, JSON.stringify(e, null, 2)); + const syncError = `${e?.message || safeStringify(e)}`; + await createOrUpdateTask({ + taskId, + syncId: sync.id, + status: "FAILED", + startedBy, + description: `Error running sync: ${syncError}`, + pkg: serviceConfig.package, + version: serviceConfig.version, + }); + await dbLog({ + taskId, + syncId: sync.id, + message: `Error running sync: ${syncError}${e?.stack ? `\n${e.stack}` : ""}`, + level: "ERROR", + }); + } + return { + ok: true, + taskId, + status: `${appBase}/api/${workspaceId}/sources/tasks?taskId=${taskId}&syncId=${syncIdOrModel}`, + logs: `${appBase}/api/${workspaceId}/sources/logs?taskId=${taskId}&syncId=${syncIdOrModel}`, + }; + } + if (destinationType.id === "clickhouse" && !destinationConfig.provisioned) { + destinationConfig.loadAsJson = false; + } + + const h = juavaHash("md5", hash(serviceConfig.credentials)); + const versionHash = `${workspaceId}_${serviceConfig.id}_${h}`; + + const catalog = await catalogFromDb(serviceConfig.package, serviceConfig.version, versionHash); + if (!catalog) { + return { + ok: false, + error: `Source catalog not found or outdated. Please run Refresh Catalog in Sync settings`, + }; + } + const configuredCatalog = selectStreamsFromCatalog(catalog, sync.data); + if ( + serviceConfig.package === "airbyte/source-postgres" || + serviceConfig.package === "airbyte/source-mssql" || + serviceConfig.package === "airbyte/source-singlestore" + ) { + // default value 10000 is too low for big tables - leading to very slow syncs + serviceConfig.credentials.sync_checkpoint_records = 200000; + } + let res: any; + const schemaChanges = (sync.data as any).schemaChanges; + if (!skipRefresh && (schemaChanges === "fields" || schemaChanges === "streams")) { + res = await rpc(syncURL + "/discover", { + method: "POST", + body: { + config: await tryManageOauthCreds({ ...serviceConfig, id: sync.fromId }), + }, + headers: { + "Content-Type": "application/json", + ...authHeaders, + }, + query: { + package: serviceConfig.package, + version: serviceConfig.version, + storageKey: versionHash, + thenRun: "true", + taskId, + syncId: sync.id, + workspaceId: workspaceId, + fullSync: fullSync ? "true" : "false", + startedBy: JSON.stringify(startedBy), + }, + }); + } else { + res = await rpc(syncURL + "/read", { + method: "POST", + headers: { + "Content-Type": "application/json", + ...authHeaders, + }, + query: { + package: serviceConfig.package, + version: serviceConfig.version, + taskId, + syncId: sync.id, + fullSync: fullSync ? "true" : "false", + startedBy: JSON.stringify(startedBy), + namespace: typeof sync.data?.["namespace"] !== "undefined" ? sync.data?.["namespace"] : "${LEGACY}", + toSameCase: sync.data?.["toSameCase"] ? "true" : "false", + addMeta: sync.data?.["addMeta"] ? "true" : "false", + deduplicate: sync.data?.["deduplicate"] ?? true ? "true" : "false", + nodelay: nodelay ? "true" : "false", + tableNamePrefix: sync.data?.["tableNamePrefix"] ?? "", + }, + body: { + config: await tryManageOauthCreds({ ...serviceConfig, id: sync.fromId }), + catalog: configuredCatalog, + ...(stateObj ? { state: stateObj } : {}), + destinationConfig, + functionsEnv: sync.data?.["functionsEnv"], + }, + }); + } + if (!res.ok) { + return { ok: false, error: res.error ?? "unknown error", taskId }; + } else { + if (trigger === "manual") { + await createOrUpdateTask({ + taskId, + syncId: sync.id, + startedBy, + status: "RUNNING", + description: "Started", + pkg: serviceConfig.package, + version: serviceConfig.version, + }); + } + return { + ok: true, + taskId, + status: `${appBase}/api/${workspaceId}/sources/tasks?taskId=${taskId}&syncId=${syncIdOrModel}`, + logs: `${appBase}/api/${workspaceId}/sources/logs?taskId=${taskId}&syncId=${syncIdOrModel}`, + }; + } + } catch (e: any) { + return syncError(log, `Error running sync`, e, false, `sync: ${syncIdOrModel} workspace: ${workspaceId}`); + } +} + +async function loadState(sync: SyncDatabaseModel): Promise { + //load state from db + const stateRows = await db.prisma().source_state.findMany({ + where: { + sync_id: sync.id, + }, + }); + if (stateRows.length > 0) { + if (stateRows.length === 1 && stateRows[0].stream === "_LEGACY_STATE") { + //legacy state + return stateRows[0].state; + } else if (stateRows.length === 1 && stateRows[0].stream === "_GLOBAL_STATE") { + //v2 global state + return [ + { + type: "GLOBAL", + global: stateRows[0].state, + }, + ]; + } else { + //v2 multi-stream states + return stateRows + .filter(r => r.stream !== "_LEGACY_STATE" && r.stream != "_GLOBAL_STATE") + .filter(r => ((sync.data as any).streams || {})[r.stream]?.sync_mode !== "full_refresh") + .map(r => { + const descr = r.stream.split("."); + let namespace: string | undefined = undefined; + let name: string | undefined = undefined; + if (descr.length === 1) { + name = descr[0]; + } else if (descr.length === 2) { + namespace = descr[0]; + name = descr[1]; + } else { + throw new Error(`Invalid stream name ${r.stream}`); + } + return { + type: "STREAM", + stream: { + stream_descriptor: { name: name, namespace: namespace }, + stream_state: r.state, + }, + }; + }); + } + } + return undefined; +} + +export async function updateScheduler(baseUrl: string, sync: SyncDbModel) { + if (!googleScheduler) { + return; + } + const sw = stopwatch(); + const parent = googleScheduler.locationPath(await googleScheduler.getProjectId(), googleSchedulerLocation); + const job: IJob = { + name: googleScheduler.jobPath(await googleScheduler.getProjectId(), googleSchedulerLocation, sync.id), + schedule: sync.data?.schedule, + timeZone: sync.data?.timezone ?? "Etc/UTC", + httpTarget: { + uri: `${baseUrl}/api/${sync.workspaceId}/sources/run?syncId=${sync.id}`, + headers: { + Authorization: `Bearer ${process.env.SYNCCTL_AUTH_KEY}`, + }, + httpMethod: "GET", + }, + }; + log.atInfo().log(`Updating job ${job.name}`); + try { + await googleScheduler.updateJob({ job }); + log.atInfo().log("Update scheduler took", sw.elapsedPretty()); + } catch (e: any) { + if (e.message.includes("NOT_FOUND") || e.message.includes("INVALID_ARGUMENT:")) { + log.atInfo().log(`Creating job ${job.name}`); + await googleScheduler.createJob({ job, parent }); + log.atInfo().log("Create scheduler took", sw.elapsedPretty()); + } else { + log.atError().log(`Error updating job ${job.name}`, e); + throw new Error(`Error updating scheduler`, { cause: e }); + } + } +} + +export async function createScheduler(baseUrl: string, sync: SyncDbModel) { + if (!googleScheduler) { + return; + } + const sw = stopwatch(); + const parent = googleScheduler.locationPath(await googleScheduler.getProjectId(), googleSchedulerLocation); + const job: IJob = { + name: googleScheduler.jobPath(await googleScheduler.getProjectId(), googleSchedulerLocation, sync.id), + schedule: sync.data?.schedule, + timeZone: sync.data?.timezone ?? "Etc/UTC", + httpTarget: { + uri: `${baseUrl}/api/${sync.workspaceId}/sources/run?syncId=${sync.id}`, + headers: { + Authorization: `Bearer ${process.env.SYNCCTL_AUTH_KEY}`, + }, + httpMethod: "GET", + }, + }; + log.atInfo().log(`Creating job ${job.name}`); + try { + await googleScheduler.createJob({ job, parent }); + log.atInfo().log("Create scheduler took", sw.elapsedPretty()); + } catch (e: any) { + if (e.message.includes("ALREADY_EXISTS")) { + log.atInfo().log(`Updating job ${job.name}`); + await googleScheduler.updateJob({ job }); + log.atInfo().log("Updating scheduler took", sw.elapsedPretty()); + } else { + log.atError().log(`Error creating job ${job.name}`, e); + throw new Error(`Error creating scheduler`, { cause: e }); + } + } +} + +export async function deleteScheduler(syncId: string) { + if (!googleScheduler) { + return; + } + const sw = stopwatch(); + + const jobName = googleScheduler.jobPath(await googleScheduler.getProjectId(), googleSchedulerLocation, syncId); + log.atInfo().log(`Deleting job ${jobName}`); + try { + await googleScheduler.deleteJob({ name: jobName }); + log.atInfo().log("Delete scheduler took", sw.elapsedPretty()); + } catch (e: any) { + if (!e.message.includes("NOT_FOUND")) { + log.atError().log(`Error deleting job ${jobName}`, e); + throw new Error(`Error deleting scheduler`, { cause: e }); + } + } +} + +export async function syncWithScheduler(baseUrl: string) { + const sw = stopwatch(); + if (!googleScheduler) { + log.atInfo().log(`GoogleCloudScheduler sync: GOOGLE_SCHEDULER_KEY is not defined, skipping`); + return; + } + const gsParent = googleScheduler.locationPath(await googleScheduler.getProjectId(), googleSchedulerLocation); + + const allSyncs = await db.prisma().configurationObjectLink.findMany({ + where: { + type: "sync", + deleted: false, + workspace: { deleted: false }, + from: { deleted: false }, + to: { deleted: false }, + }, + }); + const syncs = allSyncs.filter(sync => !!(sync.data as any).schedule); + const syncsById = syncs.reduce((acc, sync) => { + acc[sync.id] = sync; + return acc; + }, {} as Record); + + const iterable = googleScheduler.listJobsAsync({ + parent: gsParent, + }); + const jobsById: Record = {}; + for await (const response of iterable) { + jobsById[(response.name ?? "").replace(`${gsParent}/jobs/`, "")] = response; + } + + const syncsIds = Object.keys(syncsById); + const jobsIds = Object.keys(jobsById); + const idsToCreate = difference(syncsIds, jobsIds); + const idsToDelete = difference(jobsIds, syncsIds); + const idsToUpdate = difference(syncsIds, idsToCreate); + log + .atInfo() + .log( + `GoogleCloudScheduler sync: ${idsToCreate.length} to create, ${idsToDelete.length} to delete, ${idsToUpdate.length} to update` + ); + for (const id of idsToCreate) { + const sync = syncsById[id]; + const schedule = (sync.data as any).schedule; + const job: IJob = { + name: `${gsParent}/jobs/${id}`, + schedule: schedule, + timeZone: (sync.data as any).timezone ?? "Etc/UTC", + httpTarget: { + uri: `${baseUrl}/api/${sync.workspaceId}/sources/run?syncId=${sync.id}`, + headers: { + Authorization: `Bearer ${process.env.SYNCCTL_AUTH_KEY}`, + }, + httpMethod: "GET", + }, + }; + log.atInfo().log(`Creating job ${job.name}`); + try { + await googleScheduler.createJob({ + parent: gsParent, + job: job, + }); + } catch (e: any) { + log.atError().log(`Error creating job ${job.name}`, e); + if (e.message.includes("ALREADY_EXISTS")) { + await googleScheduler.updateJob({ + job, + }); + } else { + throw e; + } + } + } + for (const id of idsToDelete) { + const job = jobsById[id]; + log.atInfo().log(`Deleting job ${job.name}`); + try { + await googleScheduler.deleteJob({ + name: job.name ?? "", + }); + } catch (e: any) { + log.atError().log(`Error deleting job ${job.name}`, e); + if (!e.message.includes("NOT_FOUND")) { + throw e; + } + } + } + for (const id of idsToUpdate) { + const sync = syncsById[id]; + const schedule = (sync.data as any).schedule; + const job = jobsById[id]; + const syncTimezone = (sync.data as any).timezone ?? "Etc/UTC"; + if (job.schedule !== schedule || job.timeZone !== syncTimezone) { + log.atInfo().log(`Updating job ${job.name}`); + await googleScheduler.updateJob({ + job: { + ...job, + schedule: schedule, + timeZone: syncTimezone, + }, + }); + } + } + getServerLog().atInfo().log("Sync with GoogleCloudScheduler took", sw.elapsedPretty()); +} + +function createGoogleSchedulerClient(): CloudSchedulerClient | undefined { + const googleSchedulerKeyJson = process.env.GOOGLE_SCHEDULER_KEY; + if (!googleSchedulerKeyJson) { + log.atWarn().log(`GoogleCloudScheduler sync: GOOGLE_SCHEDULER_KEY is not defined. Sync scheduler is disabled`); + return; + } + const googleSchedulerKey = JSON.parse(googleSchedulerKeyJson); + const googleSchedulerProjectId = googleSchedulerKey.project_id; + + const client = new CloudSchedulerClient({ + credentials: googleSchedulerKey, + projectId: googleSchedulerProjectId, + }); + // client.getProjectId(); + // client.locationPath(); + return client; +} + +// export async function syncWithK8SCronJob(baseUrl: string) { +// const sw = stopwatch(); +// const allSyncs = await db.prisma().configurationObjectLink.findMany({ +// where: { type: "sync", deleted: false }, +// }); +// const syncs = allSyncs.filter(sync => !!(sync.data as any).schedule); +// const syncsById = syncs.reduce((acc, sync) => { +// acc[sync.id] = sync; +// return acc; +// }, {} as Record); +// +// const client = new CloudSchedulerClient({ +// credentials: googleSchedulerKey, +// projectId: googleSchedulerProjectId, +// }); +// const iterable = client.listJobsAsync({ +// parent: googleSchedulerParent, +// }); +// const jobsById: Record = {}; +// for await (const response of iterable) { +// jobsById[(response.name ?? "").replace(`${googleSchedulerParent}/jobs/`, "")] = response; +// } +// +// const syncsIds = Object.keys(syncsById); +// const jobsIds = Object.keys(jobsById); +// const idsToCreate = difference(syncsIds, jobsIds); +// const idsToDelete = difference(jobsIds, syncsIds); +// const idsToUpdate = difference(syncsIds, idsToCreate); +// log +// .atInfo() +// .log( +// `GoogleCloudScheduler sync: ${idsToCreate.length} to create, ${idsToDelete.length} to delete, ${idsToUpdate.length} to update` +// ); +// for (const id of idsToCreate) { +// const sync = syncsById[id]; +// const schedule = (sync.data as any).schedule; +// const job: IJob = { +// name: `${googleSchedulerParent}/jobs/${id}`, +// schedule: schedule, +// timeZone: (sync.data as any).timezone ?? "Etc/UTC", +// httpTarget: { +// uri: `${baseUrl}/api/${sync.workspaceId}/sources/run?syncId=${sync.id}`, +// headers: { +// Authorization: `Bearer ${process.env.SYNCCTL_AUTH_KEY}`, +// }, +// httpMethod: "GET", +// }, +// }; +// log.atInfo().log(`Creating job ${job.name}`); +// await client.createJob({ +// parent: googleSchedulerParent, +// job: job, +// }); +// } +// for (const id of idsToDelete) { +// const job = jobsById[id]; +// log.atInfo().log(`Deleting job ${job.name}`); +// await client.deleteJob({ +// name: job.name ?? "", +// }); +// } +// for (const id of idsToUpdate) { +// const sync = syncsById[id]; +// const schedule = (sync.data as any).schedule; +// const job = jobsById[id]; +// const syncTimezone = (sync.data as any).timezone ?? "Etc/UTC"; +// if (job.schedule !== schedule || job.timeZone !== syncTimezone) { +// log.atInfo().log(`Updating job ${job.name}`); +// await client.updateJob({ +// job: { +// ...job, +// schedule: schedule, +// timeZone: syncTimezone, +// }, +// }); +// } +// } +// getServerLog().atInfo().log("Sync with GoogleCloudScheduler took", sw.elapsedPretty()); +// } diff --git a/webapps/console/lib/server/syncs/mixpanel.ts b/webapps/console/lib/server/syncs/mixpanel.ts new file mode 100644 index 000000000..bcf6b2acd --- /dev/null +++ b/webapps/console/lib/server/syncs/mixpanel.ts @@ -0,0 +1,387 @@ +import { AnyProps, FunctionLogger, Store, SyncFunction } from "@jitsu/protocols/functions"; +import { GoogleAdsApi } from "google-ads-api"; +import dayjs from "dayjs"; +import utc from "dayjs/plugin/utc"; +import { assertTrue, requireDefined, rpc } from "juava"; + +dayjs.extend(utc); + +export type GoogleAdsCredentials = { + credentials: { + client_id: string; + access_token: string; + client_secret: string; + refresh_token: string; + developer_token: string; + }; + customer_id: string; +}; + +export type FacebookCredentials = { + client_id: string; + page_size?: number; + account_ids: string[]; + access_token: string; + client_secret: string; + insights_lookback_window?: number; +}; + +export type MixpanelAdReportRaw = { + time: Date; + source: string; + campaign_id: string; + utm_source: string; + utm_campaign: string; + cost: number; + impressions: number; + clicks: number; +}; + +const maxRunTimeSeconds = 60; + +function base64(str: string) { + return btoa(str); +} + +function getAuth(props: any) { + return base64(`${props.serviceAccountUserName}:${props.serviceAccountPassword}`); +} + +async function sendMixpanelMessage(props: any, payload: any) { + const auth = getAuth(props); + await rpc(`https://api.mixpanel.com/import?project_id=${props.projectId}`, { + method: "POST", + headers: { + Authorization: `Basic ${auth}`, + "Content-Type": "application/json", + }, + body: payload, + }); + //console.log(`Successfully sent to mixpanel ${JSON.stringify(message)}`); +} + +function sortByKey(result: Record): Record { + return Object.keys(result) + .sort() + .reduce((acc, key) => { + acc[key] = result[key]; + return acc; + }, {} as Record); +} + +async function getDaysToSync(opts: { + lookbackWindow: number; + initialSyncDays: number; + store: Store; +}): Promise> { + const now = dayjs().utc(); + const result: Record = {}; + //Those days we always need to sync + const alwaysSync = Array.from({ length: opts.lookbackWindow }, (_, i) => now.add(-i, "day").format("YYYY-MM-DD")); + alwaysSync.forEach(day => (result[day] = null)); + const syncIfNotSynced = Array.from({ length: opts.initialSyncDays - opts.lookbackWindow }, (_, i) => + now.add(-i - opts.lookbackWindow, "day").format("YYYY-MM-DD") + ); + for (const day of syncIfNotSynced) { + const syncStatus = await opts.store.get(`day-synced.${day}`); + result[day] = syncStatus || null; + } + return sortByKey(result); +} + +function describeDaysToSync(daysToSync: Record) { + return Object.entries(daysToSync) + .map( + ([day, syncStatus]) => + `\t${day} → ${syncStatus === null ? "🚀WILL SYNC" : `✂️WONT SYNC (${JSON.stringify(syncStatus)})`}` + ) + .join("\n"); +} + +export const mixpanelFacebookAdsSync: SyncFunction = async props => { + const started = Date.now(); + const { + source, + destination, + ctx: { log, store }, + } = props; + if (source.credentials.account_ids.length === 0) { + throw new Error("No account ids provided"); + } else if (source.credentials.account_ids.length > 1) { + await log.warn( + `Multiple account ids provided - ${JSON.stringify(source.credentials.account_ids)}. Using the first one: ${ + source.credentials.account_ids[0] + }` + ); + } + const daysToSync = await getDaysToSync({ lookbackWindow: 2, initialSyncDays: 30, store }); + await log.info( + `Following days will be synced (${Object.keys(daysToSync).length})\n${describeDaysToSync(daysToSync)}` + ); + const reportFields = [ + "campaign_id", + "campaign_name", + "spend", + "impressions", + "clicks", + "ad_name", + "ad_id", + "adset_name", + "adset_id", + ]; + const baseUrl = `https://graph.facebook.com/v23.0/act_${source.credentials.account_ids[0]}/insights`; + let nextPageUrl: string | undefined = undefined; + for (const day of Object.entries(daysToSync) + .filter(([day, val]) => val === null) + .map(([day, val]) => day)) { + if (Date.now() - started > maxRunTimeSeconds * 1000) { + await log.info( + `Syncing took more than ${maxRunTimeSeconds} seconds. Stopping. Rest of the days will be synced next time.` + ); + break; + } + await log.info(`Fetching Facebook Ads data for the day: ${day}`); + let totalDailyReportRows = 0; + while (true) { + const headers = { + "Content-Type": "application/json", + }; + + const data = nextPageUrl + ? await rpc(nextPageUrl, { headers }) + : await rpc(baseUrl, { + query: { + access_token: source.credentials.access_token, + level: "ad", + fields: reportFields.join(","), + time_range: JSON.stringify({ since: day, until: day }), + limit: 1000, + filtering: JSON.stringify([ + { + field: "spend", + operator: "GREATER_THAN", + value: 0, + }, + ]), + }, + headers, + }); + const { paging } = data; + nextPageUrl = paging?.next; + const reports = data.data as any[]; + totalDailyReportRows += reports.length; + await log.debug( + `Fetched ${ + reports.length + } rows. Total rows for ${day}: ${totalDailyReportRows}. Has next page: ${!!nextPageUrl}. Sending data to mixpanel` + ); + const mixpanelMessages: any[] = []; + for (const row of reports) { + const campaignDay = dayjs(day).utc().startOf("day").toDate(); + const campaignId = requireDefined(row.campaign_id); + const insertId = `G-${campaignDay.toISOString()}-${campaignId}`; + const mixPanelMessage = { + event: "Ad Data", + properties: { + $insert_id: insertId, + // distinct_id: insertId, + // We need to turn the date into a Unix timestamp + time: campaignDay.getTime(), + source: "facebook", + campaign_id: campaignId, + + // metadata about the campaign; matches client side events + utm_source: "facebook", + utm_campaign: requireDefined(row.campaign_name), + utm_content: requireDefined(row.adset_name), + utm_term: requireDefined(row.ad_name), + + // Google's cost metric is 1 millionth of the fundamental currency specified by your Ads Account. + cost: parseFloat(requireDefined(row.spend)), + impressions: parseInt(requireDefined(row.impressions)), + clicks: parseInt(requireDefined(row.clicks)), + }, + }; + mixpanelMessages.push(mixPanelMessage); + } + if (mixpanelMessages.length > 0) { + await log.info(`Sending ${mixpanelMessages.length} rows to mixpanel`); + await sendMixpanelMessage(destination, mixpanelMessages); + } + if (!nextPageUrl) { + break; + } + } + await store.set(`day-synced.${day}`, { totalDailyReportRows, time: new Date().toISOString() }); + } +}; + +async function getSubAccountsIfManagerAccount( + googleAdsProps: GoogleAdsCredentials, + customerId: string, + { log }: { log: FunctionLogger } +): Promise { + const client = new GoogleAdsApi({ + client_id: googleAdsProps.credentials.client_id, + client_secret: googleAdsProps.credentials.client_secret, + developer_token: googleAdsProps.credentials.developer_token, + }); + + const customer = client.Customer({ + customer_id: googleAdsProps.customer_id, + refresh_token: googleAdsProps.credentials.refresh_token, + }); + const customerInfo = await customer.query( + `SELECT customer.manager, customer.descriptive_name FROM customer WHERE customer.id = ${googleAdsProps.customer_id}` + ); + if (customerInfo[0]?.customer?.manager) { + const subAccounts = await customer.query(` + SELECT + customer_client.id, + customer_client.status, + customer_client.manager, + customer_client.descriptive_name + FROM + customer_client + WHERE + customer_client.level = 1 AND customer_client.status = 'ENABLED' and customer_client.manager = false + `); + await log.info( + `Google Ads account ${googleAdsProps.customer_id} ${ + customerInfo[0]?.customer.descriptive_name + } is a manager account with ${subAccounts.length} sub-accounts. Following accounts will be used:\n${subAccounts + .map((a: any) => `${a.customer_client.id} ${a.customer_client.descriptive_name}`) + .join("\t\n")}` + ); + return subAccounts.map((row: any) => row.customer_client.id); + } +} + +async function getCustomersWithActiveCampaigns( + client: GoogleAdsApi, + refhresToken: string, + day: string, + managerCustomerId: string +): Promise { + const customer = client.Customer({ + customer_id: managerCustomerId, + refresh_token: refhresToken, + // login_customer_id: managerCustomerId, // Set this to the manager account ID + }); + const query = ` + SELECT + customer.id, + customer.descriptive_name, + campaign.id, + campaign.name, + campaign.status, + campaign.start_date, + campaign.end_date + FROM + campaign + WHERE + AND campaign.start_date <= '${day}' + AND (campaign.end_date >= '${day}' OR campaign.end_date IS NULL) + `; + const result = await customer.query(query); + console.log(result); + return result.map((row: any) => row.customer.id); +} + +export const mixpanelGoogleAdsSync: SyncFunction = async props => { + const { + source, + destination, + ctx: { log, store }, + } = props; + const googleAdsProps = source.credentials; + const started = Date.now(); + + let customerIds: string[] = googleAdsProps.customer_id.split(","); + assertTrue(customerIds.length > 0, "No customer ids provided"); + + const client = new GoogleAdsApi({ + client_id: googleAdsProps.credentials.client_id, + client_secret: googleAdsProps.credentials.client_secret, + developer_token: googleAdsProps.credentials.developer_token, + }); + + let loginCustomer: string | undefined = undefined; + + if (customerIds.length === 1) { + const subAccounts = await getSubAccountsIfManagerAccount(googleAdsProps, customerIds[0], { + log, + }); + if (subAccounts) { + loginCustomer = customerIds[0]; + customerIds = subAccounts as string[]; + } + } + + const daysToSync = await getDaysToSync({ lookbackWindow: 2, initialSyncDays: 30, store }); + await log.info( + `Following days will be synced (${Object.keys(daysToSync).length})\n${describeDaysToSync(daysToSync)}` + ); + for (const day of Object.entries(daysToSync) + .filter(([day, val]) => val === null) + .map(([day]) => day)) { + await log.info(`Fetching Google Ads data for the day: ${day}`); + let activeCustomers = customerIds; + if (loginCustomer) { + // activeCustomers = await getCustomersWithActiveCampaigns(client, day, googleAdsProps.credentials.refresh_token, loginCustomer); + // await log.info(`Active customers for the day: ${day} are: ${activeCustomers.length} / ${customerIds.length}`); + } + + for (const customerId of activeCustomers) { + await log.info(`Fetching Google Ads data for the day: ${day} and customer: ${customerId}`); + const customer = client.Customer({ + customer_id: customerId, + login_customer_id: loginCustomer || customerId, + refresh_token: googleAdsProps.credentials.refresh_token, + }); + const campaigns = await customer.query(` + SELECT + segments.date, + campaign.id, + campaign.name, + metrics.cost_micros, + metrics.clicks, + metrics.impressions + FROM + campaign + WHERE + metrics.cost_micros > 0 + AND + segments.date BETWEEN '${day}' AND '${day}'`); + await log.info( + `Fetched Google Ads data for the day: ${day} and customer: ${customerId}. Results size: ${campaigns.length}` + ); + + if (campaigns.length > 0) { + const mixpanelEvents = campaigns.map((campaign: any) => ({ + event: "Ad Data", + properties: { + $insert_id: `G-${campaign.segments.date}-${campaign.campaign.id}`, + time: new Date(campaign.segments.date).getTime(), + source: "Google", + campaign_id: campaign.campaign.id, + + utm_source: "google", + utm_campaign: campaign.campaign.name, + cost: campaign.metrics.cost_micros / 1_000_000, + impressions: campaign.metrics.impressions, + clicks: campaign.metrics.clicks, + }, + })); + await sendMixpanelMessage(destination, mixpanelEvents); + } + } + await store.set(`day-synced.${day}`, { time: new Date().toISOString() }); + if (Date.now() - started > maxRunTimeSeconds * 1000) { + await log.info( + `Syncing took more than ${maxRunTimeSeconds} seconds. Stopping. Rest of the days will be synced next time.` + ); + break; + } + } +}; diff --git a/webapps/console/lib/server/telemetry.ts b/webapps/console/lib/server/telemetry.ts new file mode 100644 index 000000000..d378bbc36 --- /dev/null +++ b/webapps/console/lib/server/telemetry.ts @@ -0,0 +1,188 @@ +import { db } from "./db"; +import { getLog, randomId } from "juava"; +import { isTruish } from "../shared/chores"; +import { AnalyticsInterface, emptyAnalytics, jitsuAnalytics } from "@jitsu/js/compiled/src"; +import { SessionUser } from "../schema"; +import { Workspace } from "@prisma/client"; +import { NextApiRequest } from "next"; +import { AnalyticsContext } from "@jitsu/protocols/analytics"; + +/** + * Server telemetry is enabled by default. We need it to see the usage + * of self-hosted instance. It's disabled for Jitsu Cloud. + */ +export const anonymousTelemetryEnabled = !isTruish(process.env.JITSU_DISABLE_ANONYMOUS_TELEMETRY); +export const anonymousTelemetryJitsuKey = + process.env.JITSU_SERVER_ANONYMOUS_TELEMETRY_KEY || + "mxPhV4KmCQasdEsGr98TzX4hq12VuEaN:etEKNhKx5Gy2Ib8gVu4CSWGF6oRgyckG"; + +/** + * Frontend telemetry is opposite from server telemetry. It's disabled by default, + * since we need it ONLY for Jitsu Cloud. We don't need to track UI usage of self-hosted instances + */ +export const productTelemetryHost = process.env.JITSU_PRODUCT_TELEMETRY_HOST; //support old and new env vars +export const productTelemetryEnabled = !!productTelemetryHost; +export const productTelemetryBackendKey = process.env.JITSU_PRODUCT_BACKEND_TELEMETRY_WRITE_KEY; + +const log = getLog("telemetry"); + +const anonymousTelemetry = anonymousTelemetryEnabled + ? jitsuAnalytics({ + host: "https://ingest.g.jitsu.com", + writeKey: anonymousTelemetryJitsuKey, + //debug: true, + }) + : emptyAnalytics; + +function createAnalytics() { + return productTelemetryEnabled + ? jitsuAnalytics({ + host: productTelemetryHost, + writeKey: productTelemetryBackendKey, + s2s: true, + fetchTimeoutMs: 1000, + // debug: true, + }) + : emptyAnalytics; +} + +type WorkspaceProps = { slug?: string; name?: string }; +type WorkspaceIdAndProps = { id: string } & WorkspaceProps; + +type TrackEvents = + | "user_created" + | "workspace_created" + | "workspace_onboarded" + | "workspace_ping" + | "workspace_access" + | "create_object"; + +export interface ProductAnalytics extends AnalyticsInterface { + identifyUser(sessionUser: TrackedUser): Promise; + + workspace(workspaceId: string, opts?: WorkspaceProps): Promise; + + workspace(workspace: Workspace): Promise; + + /** + * Typed version of track method + */ + track(event: TrackEvents, props?: any): Promise; +} + +function createProductAnalytics(analytics: AnalyticsInterface, req?: NextApiRequest): ProductAnalytics { + return { + ...analytics, + identifyUser(sessionUser: SessionUser): Promise { + return analytics.identify(sessionUser.internalId, { + email: sessionUser.email, + name: sessionUser.name, + externalId: sessionUser.externalId, + }); + }, + workspace(idOrObject: string | Workspace, opts?: WorkspaceProps) { + if (typeof idOrObject === "string") { + return analytics.group( + idOrObject, + opts ? { workspaceSlug: opts.slug, workspaceName: opts.name, name: opts.name, workspaceId: idOrObject } : {} + ); + } else { + return analytics.group(idOrObject.id, { + workspaceSlug: idOrObject.slug, + workspaceName: idOrObject.name, + workspaceId: idOrObject.id, + }); + } + }, + track(event: TrackEvents, props?: any): Promise { + const context: AnalyticsContext = { + ip: (req?.headers["x-forwarded-for"] as string)?.split(",")[0]?.trim() || req?.socket?.remoteAddress, + //userAgent: req?.headers["user-agent"] as string, + }; + return analytics.track(event, { ...(props || {}), context }); + }, + }; +} + +export type TrackedUser = Pick; + +/** + * Entry point for all analytics events. The method makes sure that all identify events + * are properly sent, and calls a `callback` on configured analytics instance. + */ +export function withProductAnalytics( + callback: (p: ProductAnalytics) => Promise, + opts: { + user: TrackedUser; + workspace?: Workspace | WorkspaceIdAndProps; + req?: NextApiRequest; + } +): Promise { + //we create new instance every time since analytics.js saves state in props and not thread safe + //creating of an instance is cheap operation + const instance = createProductAnalytics(createAnalytics(), opts?.req); + const allPromises: Promise[] = []; + if (opts.user) { + allPromises.push(instance.identifyUser(opts.user)); + } + if (opts.workspace) { + allPromises.push( + instance.workspace(opts.workspace.id, { + slug: opts.workspace.slug || undefined, + name: opts.workspace.name || undefined, + }) + ); + } + allPromises.push( + (async () => { + try { + return await callback(instance); + } catch (e) { + log.atWarn().withCause(e).log(`Failed to send product analytics event`); + } + return {}; + })() + ); + return Promise.all(allPromises); +} + +let deploymentId: string | undefined = undefined; + +export async function initTelemetry(): Promise<{ deploymentId: string } | undefined> { + if (anonymousTelemetryEnabled) { + if (!deploymentId) { + try { + const instanceIdVal = await db.prisma().globalProps.findFirst({ where: { name: "deploymentId" } }); + if (instanceIdVal) { + deploymentId = (instanceIdVal.value as any).id; + log.atInfo().log(`Deployment id is going to be used for telemetry: ${deploymentId}`); + } else { + deploymentId = randomId(); + log.atInfo().log(`Creating new deployment id ${deploymentId}`); + await db.prisma().globalProps.create({ data: { name: "deploymentId", value: { id: deploymentId } } }); + await trackTelemetryEvent("deployment_created"); + } + } catch (e) { + log.atWarn().withCause(e).log("Failed to initialize telemetry"); + } + } + } + return deploymentId ? { deploymentId } : undefined; +} + +export async function trackTelemetryEvent(event: string, props: any = {}): Promise { + try { + anonymousTelemetry.setAnonymousId(deploymentId || "unknown"); + const result = await anonymousTelemetry.track(`console.${event}`, { + ...props, + deploymentId, + source: "console", + nodeVersion: process.versions.node, + host: process.env.HOST, + onVercel: isTruish(process.env.VERCEL), + }); + log.atDebug().log(`Sent ${event} to telemetry server. Result`, JSON.stringify(result, null, 2)); + } catch (e) { + log.atWarn().withCause(e).log(`Failed to send ${event} to telemetry server`); + } +} diff --git a/webapps/console/lib/server/user-preferences.ts b/webapps/console/lib/server/user-preferences.ts index b5e1994cc..e43a0a76a 100644 --- a/webapps/console/lib/server/user-preferences.ts +++ b/webapps/console/lib/server/user-preferences.ts @@ -1,7 +1,27 @@ import { PrismaClient } from "@prisma/client"; -import { merge } from "lodash"; +import merge from "lodash/merge"; +import { z } from "zod"; -export type PreferencesObj = Record; +export const UserNotificationsPreferences = z.object({ + batches: z.boolean().default(true), + syncs: z.boolean().default(true), + recurringAlertsPeriodHours: z.coerce.number().max(720).min(0).default(24), + subscriptionCode: z.string().optional(), +}); + +export type UserNotificationsPreferences = z.infer; + +export const DefaultUserNotificationsPreferences: UserNotificationsPreferences = { + batches: true, + syncs: true, + recurringAlertsPeriodHours: 24, +}; + +export type PreferencesObj = { + lastUsedWorkspaceId?: string; + notifications?: UserNotificationsPreferences; + [key: string]: any; +}; export type PreferenceOpts = { userId: string; @@ -18,7 +38,7 @@ export function getUserPreferenceService(prisma: PrismaClient): UserPreferencesS const allPreferences = await prisma.userPreferences.findMany({ where: { userId, - workspaceId: workspaceId || undefined, + workspaceId: workspaceId ?? null, }, }); return merge({}, ...allPreferences.map(p => p.preferences)); @@ -27,14 +47,14 @@ export function getUserPreferenceService(prisma: PrismaClient): UserPreferencesS const currentPreferences = await prisma.userPreferences.findMany({ where: { userId, - workspaceId: workspaceId || undefined, + workspaceId: workspaceId ?? null, }, }); if (currentPreferences.length === 0) { await prisma.userPreferences.create({ data: { userId, - workspaceId: workspaceId || undefined, + workspaceId: workspaceId ?? null, preferences: obj, }, }); @@ -44,7 +64,7 @@ export function getUserPreferenceService(prisma: PrismaClient): UserPreferencesS await prisma.userPreferences.updateMany({ where: { userId, - workspaceId: workspaceId || undefined, + workspaceId: workspaceId ?? null, }, data: { preferences: newValue, diff --git a/webapps/console/lib/shared/arrays.ts b/webapps/console/lib/shared/arrays.ts index 80f6e93ef..b82fcf5d3 100644 --- a/webapps/console/lib/shared/arrays.ts +++ b/webapps/console/lib/shared/arrays.ts @@ -1,4 +1,6 @@ -export function arrayToMap(arr: any[]): any { +export function arrayToMap>( + arr: T[] +): Record { if (!arr || arr.length === 0) { return {}; } diff --git a/webapps/console/lib/shared/chores.ts b/webapps/console/lib/shared/chores.ts new file mode 100644 index 000000000..b4b218dfa --- /dev/null +++ b/webapps/console/lib/shared/chores.ts @@ -0,0 +1,3 @@ +export function isTruish(val: any) { + return val === "true" || val === "1" || val === "yes" || val === true || val === 1; +} diff --git a/webapps/console/lib/shared/countries.ts b/webapps/console/lib/shared/countries.ts new file mode 100644 index 000000000..7dea45f99 --- /dev/null +++ b/webapps/console/lib/shared/countries.ts @@ -0,0 +1,194 @@ +export const countries = { + US: { flag: "🇺🇸", name: "United States" }, + GB: { flag: "🇬🇧", name: "United Kingdom" }, + CA: { flag: "🇨🇦", name: "Canada" }, + DE: { flag: "🇩🇪", name: "Germany" }, + FR: { flag: "🇫🇷", name: "France" }, + IT: { flag: "🇮🇹", name: "Italy" }, + ES: { flag: "🇪🇸", name: "Spain" }, + RU: { flag: "🇷🇺", name: "Russia" }, + CN: { flag: "🇨🇳", name: "China" }, + JP: { flag: "🇯🇵", name: "Japan" }, + IN: { flag: "🇮🇳", name: "India" }, + BR: { flag: "🇧🇷", name: "Brazil" }, + AU: { flag: "🇦🇺", name: "Australia" }, + ZA: { flag: "🇿🇦", name: "South Africa" }, + AR: { flag: "🇦🇷", name: "Argentina" }, + MX: { flag: "🇲🇽", name: "Mexico" }, + NL: { flag: "🇳🇱", name: "Netherlands" }, + SE: { flag: "🇸🇪", name: "Sweden" }, + NO: { flag: "🇳🇴", name: "Norway" }, + FI: { flag: "🇫🇮", name: "Finland" }, + DK: { flag: "🇩🇰", name: "Denmark" }, + PL: { flag: "🇵🇱", name: "Poland" }, + GR: { flag: "🇬🇷", name: "Greece" }, + IE: { flag: "🇮🇪", name: "Ireland" }, + PT: { flag: "🇵🇹", name: "Portugal" }, + BE: { flag: "🇧🇪", name: "Belgium" }, + CH: { flag: "🇨🇭", name: "Switzerland" }, + AT: { flag: "🇦🇹", name: "Austria" }, + TR: { flag: "🇹🇷", name: "Turkey" }, + SA: { flag: "🇸🇦", name: "Saudi Arabia" }, + AE: { flag: "🇦🇪", name: "United Arab Emirates" }, + IL: { flag: "🇮🇱", name: "Israel" }, + EG: { flag: "🇪🇬", name: "Egypt" }, + NG: { flag: "🇳🇬", name: "Nigeria" }, + KE: { flag: "🇰🇪", name: "Kenya" }, + GH: { flag: "🇬🇭", name: "Ghana" }, + TZ: { flag: "🇹🇿", name: "Tanzania" }, + UG: { flag: "🇺🇬", name: "Uganda" }, + ZW: { flag: "🇿🇼", name: "Zimbabwe" }, + MZ: { flag: "🇲🇿", name: "Mozambique" }, + AO: { flag: "🇦🇴", name: "Angola" }, + CM: { flag: "🇨🇲", name: "Cameroon" }, + BJ: { flag: "🇧🇯", name: "Benin" }, + TG: { flag: "🇹🇬", name: "Togo" }, + CI: { flag: "🇨🇮", name: "Ivory Coast" }, + SN: { flag: "🇸🇳", name: "Senegal" }, + ML: { flag: "🇲🇱", name: "Mali" }, + BF: { flag: "🇧🇫", name: "Burkina Faso" }, + NE: { flag: "🇳🇪", name: "Niger" }, + TD: { flag: "🇹🇩", name: "Chad" }, + MR: { flag: "🇲🇷", name: "Mauritania" }, + SD: { flag: "🇸🇩", name: "Sudan" }, + ER: { flag: "🇪🇷", name: "Eritrea" }, + DJ: { flag: "🇩🇯", name: "Djibouti" }, + ET: { flag: "🇪🇹", name: "Ethiopia" }, + SO: { flag: "🇸🇴", name: "Somalia" }, + GM: { flag: "🇬🇲", name: "Gambia" }, + GW: { flag: "🇬🇼", name: "Guinea-Bissau" }, + GN: { flag: "🇬🇳", name: "Guinea" }, + SL: { flag: "🇸🇱", name: "Sierra Leone" }, + LR: { flag: "🇱🇷", name: "Liberia" }, + CF: { flag: "🇨🇫", name: "Central African Republic" }, + GA: { flag: "🇬🇦", name: "Gabon" }, + CG: { flag: "🇨🇬", name: "Congo - Brazzaville" }, + CD: { flag: "🇨🇩", name: "Congo - Kinshasa" }, + IO: { flag: "🇮🇴", name: "British Indian Ocean Territory" }, + AC: { flag: "🇦🇨", name: "Ascension Island" }, + CP: { flag: "🇨🇵", name: "Clipperton Island" }, + EA: { flag: "🇪🇦", name: "Ceuta & Melilla" }, + DG: { flag: "🇩🇬", name: "Diego Garcia" }, + IC: { flag: "🇮🇨", name: "Canary Islands" }, + TA: { flag: "🇹🇦", name: "Tristan da Cunha" }, + HK: { flag: "🇭🇰", name: "Hong Kong" }, + UA: { flag: "🇺🇦", name: "Ukraine" }, + BY: { flag: "🇧🇾", name: "Belarus" }, + MD: { flag: "🇲🇩", name: "Moldova" }, + LV: { flag: "🇱🇻", name: "Latvia" }, + LT: { flag: "🇱🇹", name: "Lithuania" }, + EE: { flag: "🇪🇪", name: "Estonia" }, + GE: { flag: "🇬🇪", name: "Georgia" }, + AM: { flag: "🇦🇲", name: "Armenia" }, + XK: { flag: "🇽🇰", name: "Kosovo" }, + RS: { flag: "🇷🇸", name: "Serbia" }, + ME: { flag: "🇲🇪", name: "Montenegro" }, + MK: { flag: "🇲🇰", name: "North Macedonia" }, + AL: { flag: "🇦🇱", name: "Albania" }, + RO: { flag: "🇷🇴", name: "Romania" }, + BG: { flag: "🇧🇬", name: "Bulgaria" }, + CY: { flag: "🇨🇾", name: "Cyprus" }, + MT: { flag: "🇲🇹", name: "Malta" }, + TH: { flag: "🇹🇭", name: "Thailand" }, + PK: { flag: "🇵🇰", name: "Pakistan" }, + KR: { flag: "🇰🇷", name: "South Korea" }, + ID: { flag: "🇮🇩", name: "Indonesia" }, + MY: { flag: "🇲🇾", name: "Malaysia" }, + PH: { flag: "🇵🇭", name: "Philippines" }, + SG: { flag: "🇸🇬", name: "Singapore" }, + VN: { flag: "🇻🇳", name: "Vietnam" }, + NZ: { flag: "🇳🇿", name: "New Zealand" }, + CL: { flag: "🇨🇱", name: "Chile" }, + CO: { flag: "🇨🇴", name: "Colombia" }, + PE: { flag: "🇵🇪", name: "Peru" }, + VE: { flag: "🇻🇪", name: "Venezuela" }, + IR: { flag: "🇮🇷", name: "Iran" }, + KZ: { flag: "🇰🇿", name: "Kazakhstan" }, + UZ: { flag: "🇺🇿", name: "Uzbekistan" }, + BD: { flag: "🇧🇩", name: "Bangladesh" }, + LK: { flag: "🇱🇰", name: "Sri Lanka" }, + NP: { flag: "🇳🇵", name: "Nepal" }, + BT: { flag: "🇧🇹", name: "Bhutan" }, + MM: { flag: "🇲🇲", name: "Myanmar" }, + AF: { flag: "🇦🇫", name: "Afghanistan" }, + IS: { flag: "🇮🇸", name: "Iceland" }, + CZ: { flag: "🇨🇿", name: "Czech Republic" }, + HU: { flag: "🇭🇺", name: "Hungary" }, + SK: { flag: "🇸🇰", name: "Slovakia" }, + SI: { flag: "🇸🇮", name: "Slovenia" }, + HR: { flag: "🇭🇷", name: "Croatia" }, + BA: { flag: "🇧🇦", name: "Bosnia and Herzegovina" }, + LU: { flag: "🇱🇺", name: "Luxembourg" }, + MC: { flag: "🇲🇨", name: "Monaco" }, + LI: { flag: "🇱🇮", name: "Liechtenstein" }, + SM: { flag: "🇸🇲", name: "San Marino" }, + VA: { flag: "🇻🇦", name: "Vatican City" }, + AD: { flag: "🇦🇩", name: "Andorra" }, + MA: { flag: "🇲🇦", name: "Morocco" }, + DZ: { flag: "🇩🇿", name: "Algeria" }, + TN: { flag: "🇹🇳", name: "Tunisia" }, + LY: { flag: "🇱🇾", name: "Libya" }, + TW: { flag: "🇹🇼", name: "Taiwan" }, + UY: { flag: "🇺🇾", name: "Uruguay" }, + PY: { flag: "🇵🇾", name: "Paraguay" }, + BO: { flag: "🇧🇴", name: "Bolivia" }, + EC: { flag: "🇪🇨", name: "Ecuador" }, + CR: { flag: "🇨🇷", name: "Costa Rica" }, + PA: { flag: "🇵🇦", name: "Panama" }, + GT: { flag: "🇬🇹", name: "Guatemala" }, + HN: { flag: "🇭🇳", name: "Honduras" }, + SV: { flag: "🇸🇻", name: "El Salvador" }, + NI: { flag: "🇳🇮", name: "Nicaragua" }, + DO: { flag: "🇩🇴", name: "Dominican Republic" }, + HT: { flag: "🇭🇹", name: "Haiti" }, + JM: { flag: "🇯🇲", name: "Jamaica" }, + TT: { flag: "🇹🇹", name: "Trinidad and Tobago" }, + BB: { flag: "🇧🇧", name: "Barbados" }, + BS: { flag: "🇧🇸", name: "Bahamas" }, + CU: { flag: "🇨🇺", name: "Cuba" }, + AG: { flag: "🇦🇬", name: "Antigua and Barbuda" }, + AQ: { flag: "🇦🇶", name: "Antarctica" }, + AS: { flag: "🇦🇸", name: "American Samoa" }, + AW: { flag: "🇦🇼", name: "Aruba" }, + AX: { flag: "🇦🇽", name: "Åland Islands" }, + AZ: { flag: "🇦🇿", name: "Azerbaijan" }, + BH: { flag: "🇧🇭", name: "Bahrain" }, + BI: { flag: "🇧🇮", name: "Burundi" }, + SW: { flag: "🇸🇿", name: "Eswatini" }, +}; + +const _euCountryCodes = [ + "AT", // Austria + "BE", // Belgium + "BG", // Bulgaria + "CY", // Cyprus + "CZ", // Czech Republic + "DE", // Germany + "DK", // Denmark + "EE", // Estonia + "ES", // Spain + "FI", // Finland + "FR", // France + "GR", // Greece + "HR", // Croatia + "HU", // Hungary + "IE", // Ireland + "IT", // Italy + "LT", // Lithuania + "LU", // Luxembourg + "LV", // Latvia + "MT", // Malta + "NL", // Netherlands + "PL", // Poland + "PT", // Portugal + "RO", // Romania + "SE", // Sweden + "SI", // Slovenia + "SK", // Slovakia +]; + +export const euCountryCodes = new Set(_euCountryCodes); + +export function isEU(countryCode: string): boolean { + return euCountryCodes.has(countryCode.toUpperCase()); +} diff --git a/webapps/console/lib/shared/data-retention.ts b/webapps/console/lib/shared/data-retention.ts new file mode 100644 index 000000000..60bbdd1ad --- /dev/null +++ b/webapps/console/lib/shared/data-retention.ts @@ -0,0 +1,26 @@ +import { z } from "zod"; +import { Simplify } from "type-fest"; + +export const DataRetentionSettings = z.object({ + kafkaRetentionHours: z.coerce.number(), + identityStitchingRetentionDays: z.coerce.number(), + logsRetentionDays: z.object({ + maxRecords: z.coerce.number(), + maxHours: z.coerce.number(), + }), + customMongoDb: z.string().optional(), + disableS3Archive: z.coerce.boolean().default(false).optional(), + pendingUpdate: z.coerce.boolean().default(false).optional(), +}); + +// Example of type derived from Zod schema +export type DataRetentionSettings = Simplify>; + +export const defaultDataRetentionSettings: DataRetentionSettings = { + kafkaRetentionHours: 7 * 24, + identityStitchingRetentionDays: 30, + logsRetentionDays: { + maxRecords: 1000, + maxHours: 7 * 24, + }, +}; diff --git a/webapps/console/lib/shared/domain-check-response.ts b/webapps/console/lib/shared/domain-check-response.ts new file mode 100644 index 000000000..628cc8092 --- /dev/null +++ b/webapps/console/lib/shared/domain-check-response.ts @@ -0,0 +1,26 @@ +import { z } from "zod"; +import { Simplify } from "type-fest"; + +export const DomainCheckResponse = z.union([ + z.object({ + ok: z.literal(true), + reason: z.never().optional(), + }), + z.object({ + ok: z.literal(false), + reason: z.union([ + z.literal("used_by_other_workspace"), + z.literal("invalid_domain_name"), + z.literal("pending_ssl"), + z.literal("internal_error"), + ]), + cnames: z.never().optional(), + }), + z.object({ + ok: z.literal(false), + reason: z.literal("requires_cname_configuration"), + cnames: z.array(z.object({ name: z.string(), value: z.string(), ok: z.boolean() })), + }), +]); + +export type DomainCheckResponse = Simplify>; diff --git a/webapps/console/lib/shared/errors.tsx b/webapps/console/lib/shared/errors.ts similarity index 100% rename from webapps/console/lib/shared/errors.tsx rename to webapps/console/lib/shared/errors.ts diff --git a/webapps/console/lib/shared/json.ts b/webapps/console/lib/shared/json.ts new file mode 100644 index 000000000..51dace43f --- /dev/null +++ b/webapps/console/lib/shared/json.ts @@ -0,0 +1,17 @@ +/** + * Sometime JSON.stringify() throws an exception, if the object contains circular references. + * + * This method fixes that by catching the exception and returning stringifies option instead. It's intended to be used + * for debugging logging + */ +export function safeJsonStringify(obj: any, space?: number): string { + try { + if (!space) { + return JSON.stringify(obj); + } else { + return JSON.stringify(obj, null, space); + } + } catch (e) { + return obj + ""; + } +} diff --git a/webapps/console/lib/shared/reporting.ts b/webapps/console/lib/shared/reporting.ts new file mode 100644 index 000000000..e4cbda138 --- /dev/null +++ b/webapps/console/lib/shared/reporting.ts @@ -0,0 +1,134 @@ +import { z } from "zod"; +import { Simplify } from "type-fest"; + +export const KnownEventStatus = z.enum([ + "success", + "dropped", + "error", + "processed", + "function_success", + "builtin_function_success", + "builtin_function_error", +]); +export type KnownEventStatus = z.infer; + +const EventStatus = KnownEventStatus + //don't fail if we have a new status + .and(z.string()); +export type EventStatus = z.infer; + +export const ReportRow = z.object({ + // day as being the day in UTC + period: z.coerce.date(), + workspaceId: z.string(), + connectionId: z.string(), + streamId: z.string(), + destinationId: z.string(), + status: z.string(), + events: z.coerce.number(), + /** + * Size (rows) of an underlying chunk, do not use in UI + */ + srcSize: z.coerce.number(), +}); + +export type ReportRow = z.infer; + +export const Report = z.object({ + rows: z.array(ReportRow), +}); + +export type Report = z.infer; + +export const AggregatedReportRow = ReportRow.omit({ events: true, srcSize: true }).and( + z.object({ + events: z.record(EventStatus, z.number()), + }) +); + +export type AggregatedReportRow = z.infer; + +export const AggregatedReport = z.object({ + rows: z.array(AggregatedReportRow), +}); + +export type AggregatedReport = z.infer; + +export function aggregateReport(report: Report): AggregatedReport { + //We will need it for CSV export + throw new Error("Not implemented"); +} + +export const ActiveEventsReportRow = z.object({ + period: z.coerce.date(), + activeEvents: z.coerce.number(), +}); +export type ActiveEventsReportRow = z.infer; +export const ActiveEventsReport = z.object({ + workspaceId: z.string(), + //totals might be calculated differently than a sun of breakdown, because we take into account unique events + totalActiveEvents: z.number(), + breakdown: z.array(ActiveEventsReportRow), +}); + +export type ActiveEventsReport = z.infer; + +const ConnectionStat = z + .record(KnownEventStatus, z.number()) + .transform(x => x as typeof x extends Partial ? T : never); +export type ConnectionStat = z.infer; + +export const ConnectionAggregate = z.object({ + workspaceId: z.string(), + connectionId: z.string(), + breakdown: z.array( + z + .object({ + period: z.coerce.date(), + }) + //see https://github.com/colinhacks/zod/issues/2623#issuecomment-1880845969. Otherwise it makes a partial record + .and(ConnectionStat) + ), +}); + +export type ConnectionAggregate = Simplify>; + +function getWorkspaceId(report: Report): string { + if (report.rows.length === 0) { + return ""; + } + const workspaceId = report.rows[0].workspaceId; + + const otherWorkspace = report.rows.find(row => row.workspaceId !== workspaceId); + if (otherWorkspace) { + throw new Error(`Report contains rows from multiple workspaces: ${workspaceId} and ${otherWorkspace.workspaceId}`); + } + return workspaceId; +} + +function agg(rec1: Record, rec2: Record): Record { + const res = { ...rec1 }; + for (const [key, value] of Object.entries(rec2)) { + res[key] = (res[key] || 0) + value; + } + return res; +} + +export function buildConnectionAggregate(report: Report, connectionId: string): ConnectionAggregate { + const rowsOfInterest = report.rows.filter(row => row.connectionId === connectionId); + const preAgg = rowsOfInterest.reduce((byDay, row) => { + byDay[row.period.toISOString()] = agg( + byDay[row.period.toISOString()] || Object.fromEntries(KnownEventStatus.options.map(st => [st, 0])), + { [row.status]: row.events } + ); + return byDay; + }, {} as Record); + return { + workspaceId: getWorkspaceId(report), + connectionId, + breakdown: Object.entries(preAgg).map(([day, values]) => ({ + period: new Date(day), + ...values, + })), + }; +} diff --git a/webapps/console/lib/shared/strings.ts b/webapps/console/lib/shared/strings.ts deleted file mode 100644 index f25afc894..000000000 --- a/webapps/console/lib/shared/strings.ts +++ /dev/null @@ -1,7 +0,0 @@ -export function trimMiddle(str: string, maxLen: number, ellisis = "...") { - if (str.length <= maxLen) { - return str; - } else { - return str.substring(0, maxLen / 2 - (ellisis.length - 1)) + ellisis + str.substring(str.length - maxLen / 2 + 1); - } -} diff --git a/webapps/console/lib/shared/url.ts b/webapps/console/lib/shared/url.ts new file mode 100644 index 000000000..ac29ac6ef --- /dev/null +++ b/webapps/console/lib/shared/url.ts @@ -0,0 +1,8 @@ +export function toURL(url: string, params: Record = {}): string { + if (Object.keys(params).length) { + const urlParams = new URLSearchParams(params); + return `${url}?${urlParams.toString()}`; + } else { + return url; + } +} diff --git a/webapps/console/lib/sources.ts b/webapps/console/lib/sources.ts new file mode 100644 index 000000000..1a7e042f0 --- /dev/null +++ b/webapps/console/lib/sources.ts @@ -0,0 +1,35 @@ +import { SelectedStreamSettings } from "./schema"; + +export const initStream = (stream: any, mode?: "full_refresh" | "incremental"): SelectedStreamSettings => { + const supportedModes = stream.supported_sync_modes; + let sync_mode: "full_refresh" | "incremental" = "full_refresh"; + let cursor_field: string[] | undefined = undefined; + if (supportedModes.includes("incremental") && mode !== "full_refresh") { + if (stream.source_defined_cursor) { + sync_mode = "incremental"; + } + if (stream.default_cursor_field?.length > 0) { + sync_mode = "incremental"; + cursor_field = stream.default_cursor_field; + } else { + const props = Object.entries(stream.json_schema.properties as Record); + const dateProps = props.filter(([_, p]) => p.format === "date-time"); + const cursorField = + dateProps.find(([name, _]) => name.startsWith("updated")) || + dateProps.find(([name, _]) => name.startsWith("created")) || + dateProps.find(([name, _]) => name === "timestamp") || + props.find( + ([name, p]) => + name === "id" && (p.type === "integer" || (Array.isArray(p.type) && p.type.includes("integer"))) + ); + if (cursorField) { + sync_mode = "incremental"; + cursor_field = [cursorField[0]]; + } + } + } + return { + sync_mode, + cursor_field, + }; +}; diff --git a/webapps/console/lib/store/index.tsx b/webapps/console/lib/store/index.tsx new file mode 100644 index 000000000..91eac18a7 --- /dev/null +++ b/webapps/console/lib/store/index.tsx @@ -0,0 +1,483 @@ +import type { + ConnectorImageConfig, + MiscEntity, + DestinationConfig, + FunctionConfig, + ServiceConfig, + StreamConfig, + WorkspaceDomain, + NotificationChannel, +} from "../schema"; +import { useCallback, useEffect, useMemo, useState } from "react"; +import { getLog, requireDefined, rpc } from "juava"; +import { useWorkspace } from "../context"; +import { QueryClient, useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; +import { z } from "zod"; +import { ConfigurationObjectLinkDbModel, ProfileBuilderDbModel, WorkspaceDbModel } from "../../prisma/schema"; +import { UseMutationResult } from "@tanstack/react-query/src/types"; + +export const allConfigTypes = [ + "stream", + "service", + "function", + "destination", + "custom-image", + "domain", + "misc", + "notification", +] as const; + +export type ConfigType = (typeof allConfigTypes)[number]; + +export type ConfigTypes = { + stream: StreamConfig; + service: ServiceConfig; + function: FunctionConfig; + destination: DestinationConfig; + "custom-image": ConnectorImageConfig; + domain: WorkspaceDomain; + misc: MiscEntity; + notification: NotificationChannel; +}; + +export function asConfigType(type: string): ConfigType { + if (!allConfigTypes.includes(type as any)) { + throw new Error(`Unknown config type ${type}`); + } + return type as ConfigType; +} + +export type Result = + | { + isLoading: true; + data?: never; + error?: never; + } + | { isLoading: false; data: T; error?: never } + | { isLoading: false; data?: never; error: Error }; + +export function useConfigObject(type: T, id: string): ConfigTypes[T] | undefined { + const list = useConfigObjectList(type); + return list.find(o => o.id === id); +} + +export function getConfigObjectCacheKey(workspaceId: string, type: ConfigType) { + return [`workspaceId=${workspaceId}`, "config-object-type", type]; +} + +export function getLinksCacheKey(workspaceId: string, opts?: UseConfigObjectLinksParams) { + //so far we have one store that always contains data, so withData is always true. We remove data + //further from hook result. It's just a placeholder for future optimization + return [`workspaceId=${workspaceId}`, "links", "withData=true"]; +} + +export function getProfileBuilderCacheKey(workspaceId: string) { + //so far we have one store that always contains data, so withData is always true. We remove data + //further from hook result. It's just a placeholder for future optimization + return [`workspaceId=${workspaceId}`, "profile-builder", "withData=true"]; +} + +type UseConfigObjectsUpdaterResult = { loading: true; error?: never } | { loading: false; error?: Error }; + +function toError(e: any) { + return e instanceof Error ? e : new Error(e?.message || "Unknown error"); +} + +export function getWorkspaceCacheKey(workspaceIdOrSlug: string) { + return ["workspace", workspaceIdOrSlug]; +} + +export const foreverCache = { + retry: false, + staleTime: Infinity, + cacheTime: Infinity, + //initialData: [] +}; + +async function initialDataLoad( + workspaceIdOrSlug: string, + queryClient: QueryClient, + signal: AbortSignal +): Promise<{ workspaceId: string }> { + const loaders: Promise[] = []; + + const data = await rpc(`/api/workspace/${workspaceIdOrSlug}`, { signal }); + const workspaceDbModel = WorkspaceDbModel.parse(data) as any; + workspaceDbModel.oidcLoginGroups = data.oidcLoginGroups; + + await queryClient.prefetchQuery(getWorkspaceCacheKey(workspaceIdOrSlug), async () => workspaceDbModel, foreverCache); + const workspace = requireDefined( + queryClient.getQueryData(getWorkspaceCacheKey(workspaceIdOrSlug)), + `No data for workspace ${workspaceIdOrSlug} was prefetched` + ) as z.infer; + + for (const type of allConfigTypes) { + loaders.push( + queryClient.prefetchQuery( + getConfigObjectCacheKey(workspace.id, type), + async ({ signal }) => { + getLog().atDebug().log(`/api/${workspace.id}/config/${type}`); + const { objects } = await rpc(`/api/${workspace.id}/config/${type}`, { signal }); + getLog().atDebug().log(`Loaded ${objects.length} config objects of type ${type}`); + return objects; + }, + foreverCache + ) + ); + } + loaders.push( + queryClient.prefetchQuery( + getLinksCacheKey(workspace.id), + async ({ signal }) => { + const { links } = await rpc(`/api/${workspace.id}/config/link`, { signal }); + getLog().atDebug().log(`Loaded ${links.length} config links`); + return links; + }, + foreverCache + ) + ); + loaders.push( + queryClient.prefetchQuery( + getProfileBuilderCacheKey(workspace.id), + async ({ signal }) => { + const { profileBuilders } = await rpc(`/api/${workspace.id}/config/profile-builder`, { signal }); + getLog().atDebug().log(`Loaded ${profileBuilders.length} config profileBuilders`); + return profileBuilders; + }, + foreverCache + ) + ); + try { + getLog().atInfo().log("@@@@@@@@@@@@@@@@@@@ Loaders", loaders); + await Promise.all(loaders); + } catch (e) { + getLog().atError().log("@@@@@@@@@@@@@@@@@@@ Failed to load initial data", e); + throw e; + } + + return { workspaceId: workspace.id }; +} + +function fullDataRefresh(workspaceId: string, queryClient: QueryClient) { + const loaders: Promise[] = []; + for (const type of allConfigTypes) { + loaders.push( + rpc(`/api/${workspaceId}/config/${type}`).then(({ objects }) => { + getLog().atDebug().log(`Refreshed ${objects.length} config objects of type ${type}`); + queryClient.setQueriesData(getConfigObjectCacheKey(workspaceId, type), objects); + }) + ); + } + loaders.push( + rpc(`/api/${workspaceId}/config/link`).then(({ links }) => { + getLog().atDebug().log(`Refreshed ${links.length} config links`); + queryClient.setQueriesData(getLinksCacheKey(workspaceId), links); + }) + ); + loaders.push( + rpc(`/api/${workspaceId}/config/profile-builder`).then(({ profileBuilders }) => { + getLog().atDebug().log(`Refreshed ${profileBuilders.length} config profileBuilders`); + queryClient.setQueriesData(getProfileBuilderCacheKey(workspaceId), profileBuilders); + }) + ); + return loaders; +} + +function getDebugQueryClient() { + if (typeof window !== "undefined") { + return (window as any).queryClient; + } +} + +export function useLoadedWorkspace(workspaceIdOrSlug: string): z.infer { + const { isLoading, error, data } = useQuery(getWorkspaceCacheKey(workspaceIdOrSlug), noopLoader, foreverCache); + if (isLoading) { + getLog() + .atError() + .log( + "useLoadedWorkspace() assumes that workspace is already loaded, but it is loading. See window.queryClient", + getDebugQueryClient() + ); + throw new Error(`useLoadedWorkspace() assumes that workspace is already loaded, but it is loading`); + } + if (error) { + throw error; + } + return data! as z.infer; +} + +const listenConfig = { + checkInterval: 10_000, +} as const; + +/** + * This method loads all config object and stores them in a cache. Subsequent calls useConfigObjectList() will be + * non-blocking and return the cached data. + * + * It also sets up a background task to update the cache. + * + * And it provides a method to signal an update to the cache. + */ +export function useConfigObjectsUpdater(workspaceIdOrSlug: string): UseConfigObjectsUpdaterResult { + getLog().atInfo().log("useConfigObjectsUpdater() called with workspaceIdOrSlug=", workspaceIdOrSlug); + const queryClient = useQueryClient(); + const [loadedWorkspace, setLoadedWorkspace] = useState(undefined); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(); + useEffect(() => { + let modifiedSince = new Date(); + let interval; + const abortController = new AbortController(); + //reload data after every 5 seconds; + initialDataLoad(workspaceIdOrSlug, queryClient, abortController.signal) + .then(res => { + getLog().atDebug().log("Initial version of workspace config has been loaded"); + //setup background task to reload data + interval = setInterval(async () => { + if (!document.hidden) { + try { + const ifModified = await fetch( + `/api/${res.workspaceId}/listen?maxWaitMs=0&ifModifiedSince=${modifiedSince.toISOString()}`, + { + signal: abortController.signal, + headers: { + "If-Modified-Since": modifiedSince.toUTCString(), + }, + } + ); + if (ifModified.status === 304) { + //do nothing + } else if (ifModified.status === 200) { + modifiedSince = new Date(); + getLog().atDebug().log("Workspace config has been modified, reloading"); + await Promise.all(fullDataRefresh(res.workspaceId, queryClient)); + } else { + getLog().atWarn().log(`Unexpected response from listen: ${ifModified.status} ${ifModified.statusText}`); + } + } catch (e) { + getLog().atWarn().log("Failed to check workspace config freshness"); + } + } + }, listenConfig.checkInterval); + setLoadedWorkspace(workspaceIdOrSlug); + }) + .catch(e => { + setError(e); + }) + .finally(() => setLoading(false)); + + return () => { + abortController.abort(); + if (interval) { + try { + clearTimeout(interval); + } catch (e) {} + } + }; + }, [queryClient, workspaceIdOrSlug, loading, loadedWorkspace]); + if (error) { + return { loading: false, error: error! }; + } else if (loading || loadedWorkspace != workspaceIdOrSlug) { + return { loading: true }; + } else { + return { loading: false }; + } + return loading || loadedWorkspace != workspaceIdOrSlug ? { loading: true } : { loading: false, error: error! }; +} + +export type UseConfigObjectLinksParams = { withData?: boolean; type?: "push" | "sync" }; + +export type ConfigurationObjectLinkType = z.infer; +export type UseConfigObjectLinkResult = Omit & { data?: any }; +export type ProfileBuilderType = z.infer; + +export function useConfigObjectLinksLoader(opts?: UseConfigObjectLinksParams): Result { + const workspace = useWorkspace(); + const queryRes = useQuery(getLinksCacheKey(workspace.id, opts), noopLoader, { + retry: false, + staleTime: Infinity, + cacheTime: Infinity, + }); + //reserved for future use, due to noopLoader, the loading will be always false + return useMemo(() => { + if (queryRes.isLoading) { + return { isLoading: true }; + } else if (queryRes.error) { + return { isLoading: false, error: toError(queryRes.error) }; + } else { + return { + isLoading: false, + data: (queryRes.data! as UseConfigObjectLinkResult[]).filter(link => !opts?.type || link.type === opts?.type), + }; + } + }, [queryRes.isLoading, queryRes.error, queryRes.data, opts?.type]); +} + +export function useProfileBuildersLoader(): Result { + const workspace = useWorkspace(); + const queryRes = useQuery(getProfileBuilderCacheKey(workspace.id), noopLoader, { + retry: false, + staleTime: Infinity, + cacheTime: Infinity, + }); + //reserved for future use, due to noopLoader, the loading will be always false + return useMemo(() => { + if (queryRes.isLoading) { + return { isLoading: true }; + } else if (queryRes.error) { + return { isLoading: false, error: toError(queryRes.error) }; + } else { + return { + isLoading: false, + data: queryRes.data! as ProfileBuilderType[], + }; + } + }, [queryRes.isLoading, queryRes.error, queryRes.data]); +} + +export function useProfileBuilders(): ProfileBuilderType[] { + const loader = useProfileBuildersLoader(); + if (loader.isLoading) { + getLog() + .atError() + .log( + "useProfileBuilders() assumes that workspace is already loaded, but it is loading. See window.queryClient", + getDebugQueryClient() + ); + throw new Error( + "useProfileBuilders() assumes that all config objects are already loaded, but they are loading. use useConfigObjectListLoader() instead." + ); + } else if (loader.error) { + throw loader.error; + } + return loader.data; +} + +/** + * Indicates that data in the store has been updated and should be reloaded + * + * `opts` reserved for future use, if we ever need to support partial reloads. + * + * The usage is as follows: + * + * ```ts + * const reloadStore = useStoreReload(); + * + * const save = useCallback(async () => { + * try { + * await postDataToServer(); + * await reloadStore(); + * } catch (e) { + * //handle error + * } finally { + * setLoading(false); + * } + * }), []); + * + * Alternatively, you can use useConfigObjectMutation() w + */ +export function useStoreReload(): (opts?: {}) => Promise { + const queryClient = useQueryClient(); + const workspace = useWorkspace(); + return useCallback( + () => Promise.all(fullDataRefresh(workspace.id, queryClient)).then(() => {}), + [workspace.id, queryClient] + ); +} + +export function useConfigObjectLinks(opts?: UseConfigObjectLinksParams): UseConfigObjectLinkResult[] { + const loader = useConfigObjectLinksLoader(opts); + if (loader.isLoading) { + getLog() + .atError() + .log( + "useConfigObjectLinks() assumes that workspace is already loaded, but it is loading. See window.queryClient", + getDebugQueryClient() + ); + throw new Error( + "useConfigObjectLinks() assumes that all config objects are already loaded, but they are loading. use useConfigObjectListLoader() instead." + ); + } else if (loader.error) { + throw loader.error; + } + return loader.data; +} + +/** + * For useQuery where data should be inserted externally, so the loader should never be called + */ +export function noopLoader() { + throw new Error("The loader should not be called"); +} + +export function useConfigObjectListLoader(type: T): Result { + const workspace = useWorkspace(); + const queryRes = useQuery(getConfigObjectCacheKey(workspace.id, type), noopLoader, { + retry: false, + staleTime: Infinity, + cacheTime: Infinity, + }); + //reserved for future use, due to noopLoader, the loading will be always false + return useMemo(() => { + if (queryRes.isLoading) { + return { isLoading: true }; + } else if (queryRes.error) { + return { isLoading: false, error: toError(queryRes.error) }; + } else { + return { isLoading: false, data: queryRes.data! }; + } + }, [queryRes.isLoading, queryRes.error, queryRes.data]); +} + +export function useConfigObjectList(type: T): ConfigTypes[T][] { + const loader = useConfigObjectListLoader(type); + if (loader.isLoading) { + getLog() + .atError() + .log( + "useConfigObjectList() assumes that workspace is already loaded, but it is loading. See window.queryClient", + getDebugQueryClient() + ); + throw new Error( + `useConfigObjectList() assumes that instance of ${type} is already loaded, but it is loading. use useConfigObjectListLoader() instead.` + ); + } + if (loader.error) { + throw loader.error; + } + return loader.data; +} + +export function useConfigObjectMutation( + type: ConfigType, + fn: (variables: FParams) => Promise +): UseMutationResult { + const queryClient = useQueryClient(); + const workspace = useWorkspace(); + return useMutation(async params => { + try { + await fn(params); + queryClient.setQueriesData( + getConfigObjectCacheKey(workspace.id, type), + (await rpc(`/api/${workspace.id}/config/${type}`)).objects + ); + } catch (e) { + throw toError(e); + } + }); +} + +export function useConfigObjectLinkMutation( + fn: (variables: FParams) => Promise +): UseMutationResult { + const queryClient = useQueryClient(); + const workspace = useWorkspace(); + return useMutation(async params => { + try { + await fn(params); + queryClient.setQueriesData(getLinksCacheKey(workspace.id), (await rpc(`/api/${workspace.id}/config/link`)).links); + } catch (e) { + throw toError(e); + } + }); +} diff --git a/webapps/console/lib/ui.tsx b/webapps/console/lib/ui.tsx index 0326aa336..b7b44b515 100644 --- a/webapps/console/lib/ui.tsx +++ b/webapps/console/lib/ui.tsx @@ -1,11 +1,12 @@ -import { PropsWithChildren, ReactNode, useCallback, useEffect, useState } from "react"; +import { PropsWithChildren, ReactNode, useCallback, useEffect } from "react"; import { notification } from "antd"; import { NextRouter, useRouter } from "next/router"; -import { assertTrue } from "juava"; import { ErrorDetails } from "../components/GlobalError/GlobalError"; import { getAntdModal } from "./modal"; +import { Input } from "antd"; import * as _useTitle from "react-use/lib/useTitle"; +import { NotificationPlacement } from "antd/es/notification/interface"; export type KeyboardKey = "Escape" | "Enter"; @@ -76,6 +77,34 @@ export function confirmOp(message: ReactNode) { }); } +export function confirmOpWithInput(message: ReactNode, messageToMatch: string) { + return new Promise(resolve => { + const modal = getAntdModal().confirm({ + title: message, + content: ( + { + modal.update({ + okButtonProps: { + disabled: e.target.value !== messageToMatch, + }, + }); + }} + style={{ width: "100%", marginTop: "1rem" }} + /> + ), + + okButtonProps: { + disabled: true, + }, + + onOk: () => resolve(true), + onCancel: () => resolve(false), + }); + }); +} + export type Serialization = { parser?: (val: string) => T; serializer?: (val: T) => string }; export const serialization = { @@ -84,28 +113,6 @@ export const serialization = { bool: { parser: (val: string) => val === "true", serializer: (val: boolean) => val.toString() }, }; -export function useURLPersistedState( - paramName: string, - opts: Serialization & { defaultVal?: T; type?: Serialization } = { type: serialization.json } -): [T, (val: T) => void] { - const router = useRouter(); - const parser = opts.parser || opts.type?.parser || serialization.json.parser; - const serializer = opts?.serializer || opts.type?.serializer || serialization.json.serializer; - const paramValue = router.query[paramName]; - assertTrue(typeof paramValue !== "object", `Invalid param '${paramName}' type: ${paramValue}`); - const [value, setValue] = useState(paramValue ? parser(paramValue as string) : opts?.defaultVal); - - const setPersistedValue = val => { - setValue(val); - router.replace({ - pathname: router.pathname, - query: { ...(router.query || {}), [paramName]: serializer(val) }, - }); - }; - - return [value, setPersistedValue]; -} - export function feedbackSuccess(message: ReactNode) { notification.success({ message, @@ -113,10 +120,10 @@ export function feedbackSuccess(message: ReactNode) { }); } -export function feedbackError(message: ReactNode, opts?: { error?: any }) { +export function feedbackError(message: ReactNode, opts?: { error?: any; placement?: NotificationPlacement }) { notification.error({ message: message, - placement: "bottomRight", + placement: opts?.placement || "bottomRight", description: opts?.error ? ( <>
= { get(id: string): Promise; - create(obj: Omit): Promise; + create(obj: T): Promise; update(id: string, obj: Partial): Promise; test(obj: Partial): Promise; del(id: string): Promise; @@ -17,7 +17,14 @@ export type ConfigApi = { }; export type EventsLogApi = { - get(eventType: string, actorId: string, filter: EventsLogFilter, limit: number): Promise; + get( + eventType: string, + levels: ("warn" | "info" | "error" | "debug")[] | "all", + actorId: string, + filter: EventsLogFilter, + limit: number, + search?: string + ): Promise; }; export function useEventsLogApi(): EventsLogApi { @@ -27,11 +34,20 @@ export function useEventsLogApi(): EventsLogApi { export function getEventsLogApi(workspaceId: string): EventsLogApi { return { - get(eventType: string, actorId: string, filter: EventsLogFilter, limit: number): Promise { + get( + eventType: string, + levels: ("warn" | "info" | "error" | "debug")[] | "all", + actorId: string, + filter: EventsLogFilter, + limit: number, + search?: string + ): Promise { return rpc( - `/api/${workspaceId}/log/${eventType}/${actorId}?start=${filter.start?.getTime() ?? ""}&end=${ - filter.end?.getTime() ?? "" - }&beforeId=${filter.beforeId ?? ""}&limit=${limit}` + `/api/${workspaceId}/log/${eventType}/${actorId}?limit=${limit}${ + filter.start ? "&start=" + filter.start.toISOString() : "" + }${filter.end ? "&end=" + filter.end.toISOString() : ""}${ + levels !== "all" ? `&levels=${levels.join(",")}` : "" + }${search ? `&search=${encodeURIComponent(search)}` : ""}` ); }, }; @@ -123,7 +139,7 @@ export function useApi = } return zodParsed.data; }, - { retry: false, cacheTime: 0 } + { retry: false, cacheTime: 0, staleTime: 0, refetchOnWindowFocus: false, refetchOnMount: false } ); return { ...queryResult, reload: () => setVersion(version + 1) }; } diff --git a/webapps/console/lib/useQueryStringState.ts b/webapps/console/lib/useQueryStringState.ts index c97e824a1..f3585fc88 100644 --- a/webapps/console/lib/useQueryStringState.ts +++ b/webapps/console/lib/useQueryStringState.ts @@ -1,5 +1,7 @@ import { useRouter } from "next/router"; import { useCallback, useState } from "react"; +import omit from "lodash/omit"; +import { isEqual } from "juava"; export type Serde = { parser: (val: string) => T; @@ -18,13 +20,14 @@ export const jsonSerializationBase64: Serde = { export function useQueryStringState( param: string, - opt: { defaultValue?: T } & Partial> = { + opt: { defaultValue?: T; skipHistory?: boolean } & Partial> = { defaultValue: undefined as T, parser: (val: string) => val as T, serializer: (val: T) => (val || "").toString(), } -): [T, (val: T) => void] { - const { push, query } = useRouter(); +): [T, (val: T) => Promise] { + const { push, replace, query } = useRouter(); + const transition = opt.skipHistory ? replace : push; const initialValueStr = query[param] as string | undefined; const initialValue = initialValueStr ? opt.parser @@ -33,13 +36,22 @@ export function useQueryStringState( : opt.defaultValue; const [state, setState] = useState(initialValue as T); + const updateState = useCallback( (val: T) => { - setState(val); - const newQuery = { ...query, [param]: opt.serializer ? opt.serializer(val) : (val as string) }; - return push({ query: newQuery }, undefined, { shallow: true }); + if (val !== state && !isEqual(val, state)) { + setState(val); + if (val) { + const newQuery = { ...query, [param]: opt.serializer ? opt.serializer(val) : (val as string) }; + return transition({ query: newQuery }, undefined, { shallow: true }); + } else { + return transition({ query: omit(query, param) }, undefined, { shallow: true }); + } + } else { + return Promise.resolve(false); + } }, - [opt, param, query, push] + [state, opt, param, query, transition] ); return [state, updateState]; } diff --git a/webapps/console/lib/version.ts b/webapps/console/lib/version.ts new file mode 100644 index 000000000..ae1830a68 --- /dev/null +++ b/webapps/console/lib/version.ts @@ -0,0 +1,43 @@ +export type ApplicationVersion = { + /** + * Version as 3.2.1 or "dev" + */ + version: string; + /** + * "latest" or "beta" or "dev" + */ + stream: string; + + git?: { + //Id of latest commit + commitId: string; + }; +}; + +export type EnvVars = { + JITSU_VERSION_COMMIT_SHA?: string; + JITSU_VERSION_DOCKER_TAG?: string; + JITSU_VERSION_STRING?: string; + VERCEL_GIT_COMMIT_SHA?: string; +}; + +function getGit(env: EnvVars): ApplicationVersion["git"] { + if (env.JITSU_VERSION_COMMIT_SHA) { + return { + commitId: env.JITSU_VERSION_COMMIT_SHA, + }; + } else if (env.JITSU_VERSION_COMMIT_SHA) { + return { + commitId: env.JITSU_VERSION_COMMIT_SHA, + }; + } +} + +export function getApplicationVersion(): ApplicationVersion { + const env = process.env as EnvVars; + return { + version: env.JITSU_VERSION_STRING || "dev", + stream: env.JITSU_VERSION_DOCKER_TAG || "dev", + git: getGit(env), + }; +} diff --git a/webapps/console/lib/workspace-roles.ts b/webapps/console/lib/workspace-roles.ts new file mode 100644 index 000000000..c15b4da9a --- /dev/null +++ b/webapps/console/lib/workspace-roles.ts @@ -0,0 +1,97 @@ +import { z } from "zod"; + +export type WorkspaceRoleType = "owner" | "editor" | "analyst"; + +export const WorkspaceRolesZodType = z.enum(["owner", "editor", "analyst"]); + +export type WorkspacePermissionsType = "readEntities" | "editEntities" | "deleteEntities" | "manageUsers"; + +export type WorkspaceRoleWithPermissions = { + role: WorkspaceRoleType; +} & Record; + +export const WorkspaceRolePermissions: Record> = { + owner: { + editEntities: true, + deleteEntities: true, + manageUsers: true, + readEntities: true, + }, + editor: { + editEntities: true, + deleteEntities: true, + manageUsers: false, + readEntities: true, + }, + analyst: { + editEntities: false, + deleteEntities: false, + manageUsers: false, + readEntities: true, + }, +} as const; + +export function hasPermission(role: WorkspaceRoleType, permission: WorkspacePermissionsType): boolean { + return WorkspaceRolePermissions[role]?.[permission] ?? false; +} + +export const WorkspaceRoleLabels: Record = { + owner: "Owner", + editor: "Editor", + analyst: "Analyst", +}; + +export const WorkspaceRoleDescriptions: Record = { + owner: "Can create, edit workspace entities and manage workspace users", + editor: "Can create and edit workspace entities", + analyst: "Read-only access to workspace data. Can trigger Syncs tasks runs.", +}; + +export const WorkspaceRoleConfig: Record< + WorkspaceRoleType, + { + color: string; + bgColor: string; + borderColor: string; + icon: string; + style: { + color: string; + backgroundColor: string; + borderColor: string; + }; + } +> = { + owner: { + color: "text-gray-600", + bgColor: "bg-gray-50", + borderColor: "border-gray-200", + icon: "Crown", + style: { + color: "#4b5563", // gray-600 + backgroundColor: "#f9fafb", // gray-50 + borderColor: "#e5e7eb", // gray-200 + }, + }, + editor: { + color: "text-gray-600", + bgColor: "bg-gray-50", + borderColor: "border-gray-200", + icon: "Edit3", + style: { + color: "#4b5563", // gray-600 + backgroundColor: "#f9fafb", // gray-50 + borderColor: "#e5e7eb", // gray-200 + }, + }, + analyst: { + color: "text-gray-600", + bgColor: "bg-gray-50", + borderColor: "border-gray-200", + icon: "BarChart3", + style: { + color: "#4b5563", // gray-600 + backgroundColor: "#f9fafb", // gray-50 + borderColor: "#e5e7eb", // gray-200 + }, + }, +}; diff --git a/webapps/console/lib/zod.ts b/webapps/console/lib/zod.ts index 83ed7e50d..40a21f379 100644 --- a/webapps/console/lib/zod.ts +++ b/webapps/console/lib/zod.ts @@ -51,6 +51,7 @@ export function prepareZodObjectForDeserialization(obj: any) { } export function createDisplayName(name: string) { - const result = name.replace(/([A-Z])/g, " $1"); - return result.charAt(0).toUpperCase() + result.slice(1); + return name + .replace(/([A-Z])/g, " $1") + .replace(/([-_][a-z]|^[a-z])/g, group => group.toUpperCase().replace("-", " ").replace("_", " ")); } diff --git a/webapps/console/middleware.ts b/webapps/console/middleware.ts deleted file mode 100644 index e7390a5d3..000000000 --- a/webapps/console/middleware.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { NextResponse } from "next/server"; -import type { NextRequest } from "next/server"; - -export function middleware(request: NextRequest) { - if (request.nextUrl.pathname.startsWith("/p.js")) { - return NextResponse.rewrite(new URL("/api/s/javascript-library", request.url)); - } -} - -export const config = { - matcher: "/p.js", -}; diff --git a/webapps/console/middleware.ts_ b/webapps/console/middleware.ts_ new file mode 100644 index 000000000..58d850c55 --- /dev/null +++ b/webapps/console/middleware.ts_ @@ -0,0 +1,74 @@ +import type { NextRequest } from "next/server"; +import { NextResponse } from "next/server"; +import { getServerLog } from "./lib/server/log"; +import { isTruish } from "./lib/shared/chores"; + +const logRequests = isTruish(process.env.CONSOLE_DEBUG_REQUESTS); + +function prettifyBody(body: string) { + try { + const obj = JSON.parse(body); + return JSON.stringify(obj, null, 2); + } catch (e) { + return body; + } +} +const segmentsSettings = (writeKey: string) => ({ + integrations: { + "Segment.io": { + apiKey: writeKey, + unbundledIntegrations: [], + addBundledMetadata: true, + maybeBundledConfigIds: {}, + versionSettings: { version: "4.4.7", componentTypes: ["browser"] }, + }, + }, + plan: { + track: { __default: { enabled: true, integrations: {} } }, + identify: { __default: { enabled: true } }, + group: { __default: { enabled: true } }, + }, + edgeFunction: {}, + analyticsNextEnabled: true, + middlewareSettings: {}, + enabledMiddleware: {}, + metrics: { sampleRate: 0.1 }, + legacyVideoPluginsEnabled: false, + remotePlugins: [], +}); +const log = getServerLog("request-logger"); + +export async function middleware(request: NextRequest) { + let response; + if (request.nextUrl.pathname.startsWith("/p.js")) { + response = NextResponse.rewrite(new URL("/api/s/javascript-library", request.url)); + } else if (request.nextUrl.pathname.startsWith("/v1/batch")) { + response = NextResponse.rewrite(new URL("/api/s/batch", request.url)); + } else if (request.nextUrl.pathname.startsWith("/v1/b")) { + response = NextResponse.rewrite(new URL("/api/s/batch", request.url)); + } else if (request.nextUrl.pathname.startsWith("/v1/projects")) { + // mimic segments setting endpoint + // https://cdn-settings.segment.com/v1/projects//settings + response = NextResponse.json(segmentsSettings(request.nextUrl.pathname.split("/")[3])); + } else { + response = NextResponse.next(); + } + if (logRequests) { + if (request.nextUrl.pathname.startsWith("/_next") || request.nextUrl.pathname.startsWith("/favicon.ico")) { + return response; + } + const method = request.method; + const body = request.body; + if (body) { + log.atInfo().log(`${method} ${request.nextUrl.pathname}`, prettifyBody(await request.text())); + } else { + log.atInfo().log(`${method} ${request.nextUrl.pathname}`); + } + } + + return response; +} + +export const config = { + matcher: !logRequests ? ["/p.js", "/v1/batch*", "/api/s/batch*", "/v1/projects*"] : undefined, +}; diff --git a/webapps/console/next-env.d.ts b/webapps/console/next-env.d.ts index 4f11a03dc..254b73c16 100644 --- a/webapps/console/next-env.d.ts +++ b/webapps/console/next-env.d.ts @@ -1,5 +1,6 @@ /// /// +/// // NOTE: This file should not be edited -// see https://nextjs.org/docs/basic-features/typescript for more information. +// see https://nextjs.org/docs/pages/api-reference/config/typescript for more information. diff --git a/webapps/console/next.config.js b/webapps/console/next.config.js index 0263c27b7..8d08c727e 100644 --- a/webapps/console/next.config.js +++ b/webapps/console/next.config.js @@ -1,31 +1,72 @@ /** @type {import("next").NextConfig} */ -const withTM = require("next-transpile-modules")(["juava", "@jitsu/protocols", "@jitsu/core-functions"]); -const nextConfig = withTM({}); -const path = require("path"); -module.exports = nextConfig; -const prevWebpack = module.exports.webpack; +const withBundleAnalyzer = require("@next/bundle-analyzer")({ + enabled: process.env.ANALYZE === "true", +}); -const packageRoot = path.join(__dirname, "../../"); -console.log("packageRoot", packageRoot); -module.exports = { - experimental: { - outputFileTracingExcludes: { - "*": [ - "./**/node_modules/@swc/core-linux-x64-gnu", - "./**/node_modules/@swc/core-linux-x64-musl", - "./**/node_modules/esbuild/linux", - "./**/node_modules/webpack", - "./**/node_modules/rollup", - "./**/node_modules/terser", - ], +module.exports = withBundleAnalyzer({ + transpilePackages: ["juava", "@jitsu/protocols", "@jitsu/core-functions", "@jitsu-internal/webapps-shared"], + turbopack: { + rules: { + "*.txt": { + loaders: ["raw-loader"], + as: "*.js", + }, }, }, - outputFileTracing: true, - webpack: (config, opts) => { - if (prevWebpack) { - prevWebpack(config, opts); + // modularizeImports: { + // // "lucide-react": { + // // transform: "Use instead of importing from 'lucide-react'", + // // preventFullImport: true, + // // }, + // lodash: { + // transform: "lodash/{{member}}", + // preventFullImport: true, + // }, + // "@ant-design/icons": { + // transform: "@ant-design/icons/{{member}}", + // preventFullImport: true, + // }, + // "react-icons/(\\w+)": { + // transform: "@react-icons/all-files/{{ matches.[1] }}/{{member}}", + // preventFullImport: true, + // skipDefaultConversion: true, + // }, + // }, + async headers() { + //set cors headers + return [ + { + source: "/:path*{/}?", + headers: [ + { + key: "X-Frame-Options", + value: "DENY", + }, + { + key: "X-Content-Type-Options", + value: "nosniff", + }, + ], + }, + ]; + }, + outputFileTracingExcludes: { + "*": [ + "./**/node_modules/@swc/core-linux-x64-gnu", + "./**/node_modules/@swc/core-linux-x64-musl", + "./**/node_modules/esbuild/linux", + "./**/node_modules/webpack", + "./**/node_modules/rollup", + "./**/node_modules/terser", + ], + }, + ...(process.env.NEXTJS_STANDALONE_BUILD === "1" + ? { + output: "standalone", } + : {}), + webpack: (config, opts) => { // Fixes npm packages that depend on `fs` and 'dns' module if (!opts.isServer) { config.resolve.fallback = { @@ -38,7 +79,14 @@ module.exports = { config.plugins.push(new opts.webpack.IgnorePlugin({ resourceRegExp: /^mongodb$/ })); config.plugins.push(new opts.webpack.IgnorePlugin({ resourceRegExp: /^posthog-node$/ })); } - config.externals.vm2 = "require('vm2')"; + if (!opts.dev) { + config.devtool = "source-map"; + } + config.externals["isolated-vm"] = "require('isolated-vm')"; + config.module.rules.push({ + test: /\.sql$/, + use: "raw-loader", + }); config.module.rules.push({ test: /\.txt$/, use: "raw-loader", @@ -50,4 +98,4 @@ module.exports = { config.resolve.extensions.push(".node"); return config; }, -}; +}); diff --git a/webapps/console/package.json b/webapps/console/package.json index 914b49013..50ab9541d 100644 --- a/webapps/console/package.json +++ b/webapps/console/package.json @@ -12,110 +12,130 @@ "prisma": "prisma", "lint": "next lint", "console:dev": "next dev", - "dev:rotor": "ts-node service/rotor.ts", "console:start": "next start", + "email:dev": "email dev -p 3901", + "dev:rotor": "ts-node service/rotor.ts", "build": "prisma generate && next build", "clean": "rm -rf ./.next ./.turbo", "compile": "tsc -p .", "tool:hash": "ts-node scripts/password-hash.ts", "db:code-gen": "prisma generate", - "db:update-schema": "dotenv -e ../../.env.local -- prisma db push" + "db:update-schema": "dotenv -e ../../.env.local -- prisma db push", + "db:format-schema": "dotenv -e ../../.env.local -- prisma format", + "db:update-schema-force": "dotenv -e ../../.env.local -- prisma db push --accept-data-loss" }, "dependencies": { - "@ant-design/cssinjs": "^1.5.6", - "@clickhouse/client": "^0.0.11", + "@ant-design/cssinjs": "^1.21.1", + "@clickhouse/client": "^1.10.1", + "@dnd-kit/core": "^6.0.8", + "@dnd-kit/modifiers": "^6.0.1", + "@dnd-kit/sortable": "^7.0.2", + "@dnd-kit/utilities": "^3.2.1", "@fontsource/inter": "^4.5.13", "@fontsource/roboto-mono": "^4.5.8", + "@google-cloud/scheduler": "^4.0.0", + "@jitsu-internal/webapps-shared": "workspace:*", "@jitsu/core-functions": "workspace:*", + "@jitsu/functions-lib": "workspace:*", "@jitsu/jitsu-react": "workspace:*", "@jitsu/js": "workspace:*", "@jitsu/protocols": "workspace:*", "@loadable/component": "^5.15.3", - "@monaco-editor/react": "4.5.0", + "@monaco-editor/react": "^4.6.0", "@nangohq/frontend": "^0.21.11", - "@prisma/client": "^4.12.0", - "@rjsf/antd": "5.3.1", - "@rjsf/core": "5.3.1", - "@rjsf/utils": "5.3.1", - "@rjsf/validator-ajv6": "5.3.1", - "@sensejs/kafkajs-zstd-support": "^0.9.2", - "@tanstack/react-query": "^4.2.1", - "@types/cookie": "^0.5.1", + "@prisma/client": "^6.5.0", + "@react-email/components": "^0.3.1", + "@react-email/render": "^1.1.3", + "@rjsf/antd": "^5.22.4", + "@rjsf/core": "^5.22.4", + "@rjsf/utils": "^5.22.4", + "@rjsf/validator-ajv8": "^5.22.4", + "@tanstack/react-query": "^4.36.1", + "@types/cookie": "^1.0.0", "@types/pg-promise": "^5.4.3", "agentkeepalive": "^4.2.1", + "ajv": "^8.17.1", + "chart.js": "^4.4.1", "classnames": "^2.3.1", - "cookie": "^0.5.0", + "cookie": "1.0.1", "cuid": "^2.1.8", - "dayjs": "^1.11.7", - "firebase": "^9.17.1", - "firebase-admin": "^11.5.0", - "googleapis": "^100.0.0", + "dayjs": "^1.11.10", + "firebase": "^11.4.0", + "firebase-admin": "^13.2.0", + "google-ads-api": "^17.1.0-rest-beta", + "googleapis": "^126.0.1", "highlight.js": "^11.6.0", - "ioredis": "^5.2.3", + "ioredis": "^5.3.2", "js-yaml": "^4.1.0", - "json-schema-faker": "~0.5.3", "json5": "^2.2.3", - "jsonwebtoken": "^8.5.1", + "jsondiffpatch": "^0.5.0", + "jsonwebtoken": "^9.0.2", "juava": "workspace:*", - "kafkajs": "^2.2.4", - "kafkajs-snappy": "^1.1.0", "lodash": "^4.17.21", - "lucide-react": "^0.125.0", - "mjml": "^4.13.0", + "lucide-react": "^0.447.0", + "mjml": "^5.0.0-alpha.6", "mjml-react": "^2.0.8", - "monaco-editor": "0.37.1", - "next": "^13.3.0", - "next-auth": "^4.22.0", - "node-fetch-commonjs": "^3.2.4", - "node-sql-parser": "^4.6.5", - "nodemailer": "^6.8.0", + "monaco-editor": "^0.52.0", + "next": "^15.5.4", + "next-auth": "^4.24.11", + "node-fetch-commonjs": "^3.3.2", + "node-sql-parser": "^5.3.8", + "nodemailer": "^6.10.1", "object-hash": "^3.0.0", - "pg": "^8.7.3", - "pg-query-stream": "^4.2.4", - "prisma": "^4.12.0", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "react-icons": "^4.8.0", + "pg": "~8.11.6", + "pg-cursor": "~2.10.1", + "prisma": "^6.5.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-icons": "^4.10.1", "react-json-view": "^1.19.1", "react-use": "^17.4.0", + "recharts": "^2.10.4", "stable-hash": "0.0.3", "stopwatch-node": "^1.1.0", - "tslib": "^2.4.0", - "zod": "3.21.4", - "zod-to-json-schema": "3.20.4" + "timezones-list": "^3.0.2", + "tslib": "^2.6.3", + "use-debounce": "^10.0.4", + "zod": "^3.23.8", + "zod-to-json-schema": "^3.23.2", + "node-cache": "^5.1.2", + "jwks-rsa": "^3.2.0" }, "devDependencies": { - "@ant-design/icons": "^5.0.1", - "@tailwindcss/typography": "^0.5.7", - "@testing-library/jest-dom": "^5.16.4", + "@ant-design/icons": "^5.5.1", + "@next/bundle-analyzer": "^15.5.4", + "@tailwindcss/typography": "^0.5.15", + "@testing-library/jest-dom": "^6.1.3", "@testing-library/react": "^13.1.1", "@types/cli-progress": "^3.11.0", - "@types/firebase": "^3.2.1", "@types/js-yaml": "^4.0.5", "@types/lodash": "^4.14.185", "@types/node": "^18.15.3", - "@types/nodemailer": "^6.4.6", - "@types/pg": "^8.6.5", + "@types/nodemailer": "^6.4.17", + "@types/pg": "~8.11.15", "@types/pg-query-stream": "^3.4.0", - "@types/react": "18.0.28", - "@types/react-dom": "18.0.11", - "antd": "^5.2.1", - "autoprefixer": "^10.4.5", + "@types/react": "18.3.3", + "@types/react-dom": "18.3.0", + "ansi-to-html": "^0.7.2", + "antd": "^5.22.0", + "autoprefixer": "^10.4.20", "dotenv-cli": "^6.0.0", - "eslint": "8.24.0", - "eslint-config-next": "^13.3.0", - "eslint-plugin-unused-imports": "^2.0.0", + "elliptic": "^6.6.1", + "eslint": "^8.57.0", + "eslint-config-next": "^15.5.4", + "eslint-plugin-unused-imports": "^4.0.1", + "http-proxy-middleware": "^2.0.7", "jest": "^28.0.0", "less": "^4.1.2", "less-loader": "^10.2.0", "minimist": "^1.2.7", - "next-transpile-modules": "^10.0.0", - "postcss": "^8.4.12", + "postcss": "^8.4.47", "raw-loader": "^4.0.2", - "tailwindcss": "^3.0.24", - "ts-node": "~10.8.1", + "react-email": "^4.1.3", + "tailwindcss": "^3.4.14", + "ts-node": "^10.9.2", "type-fest": "^3.5.7", - "typescript": "^4.8.4", - "zod-prisma": "0.5.4" + "typescript": "^5.6.3", + "zod-prisma": "^0.5.4" } } diff --git a/webapps/console/pages/403.tsx b/webapps/console/pages/403.tsx new file mode 100644 index 000000000..31cc8ed0a --- /dev/null +++ b/webapps/console/pages/403.tsx @@ -0,0 +1,5 @@ +import { GlobalError } from "../components/GlobalError/GlobalError"; + +export default function Custom403() { + return ; +} diff --git a/webapps/console/pages/404.tsx b/webapps/console/pages/404.tsx new file mode 100644 index 000000000..630f5f9e7 --- /dev/null +++ b/webapps/console/pages/404.tsx @@ -0,0 +1,22 @@ +import { useRouter } from "next/router"; +import { Button } from "antd"; + +export default function Custom404() { + const router = useRouter(); + return ( +
+
+

Error 404

+

The page does not exist

+

+ URL: {window.location.pathname} +

+
+ +
+
+
+ ); +} diff --git a/webapps/console/pages/405.tsx b/webapps/console/pages/405.tsx new file mode 100644 index 000000000..eb0ebaf35 --- /dev/null +++ b/webapps/console/pages/405.tsx @@ -0,0 +1,5 @@ +import { GlobalError } from "../components/GlobalError/GlobalError"; + +export default function Custom405() { + return ; +} diff --git a/webapps/console/pages/500.tsx b/webapps/console/pages/500.tsx new file mode 100644 index 000000000..0812fc1c9 --- /dev/null +++ b/webapps/console/pages/500.tsx @@ -0,0 +1,5 @@ +import { GlobalError } from "../components/GlobalError/GlobalError"; + +export default function Custom405() { + return ; +} diff --git a/webapps/console/pages/[workspaceId]/connections/edit.tsx b/webapps/console/pages/[workspaceId]/connections/edit.tsx index b5d47c2a6..d1ea8bbd6 100644 --- a/webapps/console/pages/[workspaceId]/connections/edit.tsx +++ b/webapps/console/pages/[workspaceId]/connections/edit.tsx @@ -1,12 +1,10 @@ import { WorkspacePageLayout } from "../../../components/PageLayout/WorkspacePageLayout"; -import { useWorkspace } from "../../../lib/context"; -import React, { useEffect, useState } from "react"; -import { LoadingAnimation } from "../../../components/GlobalLoader/GlobalLoader"; -import { GlobalError } from "../../../components/GlobalError/GlobalError"; +import React from "react"; import ConnectionEditorPage from "../../../components/ConnectionEditorPage/ConnectionEditorPage"; -import { useLinksQuery } from "../../../lib/queries"; -import { useConfigApi } from "../../../lib/useApi"; import { FunctionConfig } from "../../../lib/schema"; +import { useConfigObjectLinks, useConfigObjectList } from "../../../lib/store"; +import { z } from "zod"; +import { ConfigurationObjectLinkDbModel } from "../../../prisma/schema"; type FunctionAPIResult = { functions: FunctionConfig[]; @@ -14,37 +12,17 @@ type FunctionAPIResult = { error: any; }; const Loader = () => { - const workspace = useWorkspace(); - const functionsApi = useConfigApi("function"); - const [functions, setFunctions] = useState({ - functions: [], - isLoading: true, - error: null, - }); - const result = useLinksQuery(workspace.id, "push", { - cacheTime: 0, - retry: false, - }); - useEffect(() => { - (async () => { - try { - setFunctions({ functions: [], isLoading: true, error: null }); - setFunctions({ functions: await functionsApi.list(), isLoading: false, error: null }); - } catch (e) { - setFunctions({ functions: [], isLoading: false, error: e }); - } - })(); - }, [functionsApi]); - - if (result.isLoading || functions.isLoading) { - return ; - } - if (result.error || functions.error) { - return ; - } - const [streams, destinations, links] = result.data; + const links = useConfigObjectLinks({ withData: true }); + const streams = useConfigObjectList("stream"); + const destinations = useConfigObjectList("destination"); + const functions = useConfigObjectList("function").filter(f => f.kind !== "profile"); return ( - + []} + functions={functions} + /> ); }; diff --git a/webapps/console/pages/[workspaceId]/connections/index.tsx b/webapps/console/pages/[workspaceId]/connections/index.tsx index e611c9b84..9f464efb0 100644 --- a/webapps/console/pages/[workspaceId]/connections/index.tsx +++ b/webapps/console/pages/[workspaceId]/connections/index.tsx @@ -1,11 +1,10 @@ import { WorkspacePageLayout } from "../../../components/PageLayout/WorkspacePageLayout"; import { useWorkspace } from "../../../lib/context"; import { get } from "../../../lib/useApi"; -import { DestinationConfig, StreamConfig } from "../../../lib/schema"; +import { DestinationConfig, FunctionConfig, ServiceConfig, StreamConfig } from "../../../lib/schema"; import { ConfigurationObjectLinkDbModel } from "../../../prisma/schema"; -import { QueryResponse } from "../../../components/QueryResponse/QueryResponse"; import { z } from "zod"; -import { Table, Tooltip } from "antd"; +import { Table } from "antd"; import { confirmOp, feedbackError, feedbackSuccess } from "../../../lib/ui"; import React, { useState } from "react"; import Link from "next/link"; @@ -13,14 +12,29 @@ import { FaExternalLinkAlt, FaPlus, FaTrash } from "react-icons/fa"; import { index } from "juava"; import { getCoreDestinationType } from "../../../lib/schema/destinations"; import { useRouter } from "next/router"; -import { FiEdit2 } from "react-icons/fi"; -import { useLinksQuery } from "../../../lib/queries"; import { jsonSerializationBase64, useQueryStringState } from "../../../lib/useQueryStringState"; import { TableProps } from "antd/es/table/InternalTable"; import { ColumnType, SortOrder } from "antd/es/table/interface"; -import { Inbox } from "lucide-react"; +import { Activity, Edit3, Inbox, UserRoundPen, XCircle, Power, PowerOff } from "lucide-react"; import { PlusOutlined } from "@ant-design/icons"; -import { JitsuButton, WJitsuButton } from "../../../components/JitsuButton/JitsuButton"; +import { WJitsuButton } from "../../../components/JitsuButton/JitsuButton"; +import { DestinationTitle } from "../destinations"; +import { ButtonGroup, ButtonProps } from "../../../components/ButtonGroup/ButtonGroup"; +import { StreamTitle } from "../streams"; +import { FunctionTitle } from "../functions"; +import omit from "lodash/omit"; +import { toURL } from "../../../lib/shared/url"; +import JSON5 from "json5"; +import { + useConfigObjectLinks, + useConfigObjectList, + useStoreReload, + useConfigObjectLinkMutation, +} from "../../../lib/store"; +import { ServiceTitle } from "../services"; +import { ObjectTitle } from "../../../components/ObjectTitle/ObjectTitle"; +import { WLink } from "../../../components/Workspace/WLink"; +import { useAntdModal } from "../../../lib/modal"; function EmptyLinks() { const workspace = useWorkspace(); @@ -29,8 +43,8 @@ function EmptyLinks() {
{" "} - You don't have any links between sites and{" "} - destinations + You don't have any links between sites and{" "} + destinations
@@ -41,12 +55,72 @@ function EmptyLinks() { ); } +export const ConnectionTitle: React.FC<{ + connectionId: string; + stream?: StreamConfig; + service?: ServiceConfig; + destination: DestinationConfig; + showLink?: boolean; +}> = ({ connectionId, stream, service, destination, showLink = false }) => { + return ( +
+ {service && } + {stream && } + {!stream && !service && "DELETED "} + {"➞"} + + {showLink && ( + } + /> + )} +
+ ); +}; + +export const ProfileBuilderTitle: React.FC<{ + profileBuilder: any; + destination?: DestinationConfig; + showLink?: boolean; +}> = ({ profileBuilder, destination, showLink = false }) => { + return ( +
+ } + size={"small"} + // href={stream && link ? `/${stream.workspaceId}/streams?id=${stream?.id}` : undefined} + title={profileBuilder ? profileBuilder.name : "Unknown stream"} + /> + {destination && ( + <> + {"→"} + + + )} + {/*{showLink && (*/} + {/* }*/} + {/* />*/} + {/*)}*/} +
+ ); +}; + type ConfigurationLinkDbModel = z.infer; type RemoteEntitiesProps = { streams: StreamConfig[]; destinations: DestinationConfig[]; links: Omit[]; + functions: FunctionConfig[]; reloadCallback: () => void; }; @@ -54,24 +128,37 @@ type SortingSettings = { columns: { order: SortOrder; field: string }[]; }; -function ConnectionsTable({ links, streams, destinations, reloadCallback }: RemoteEntitiesProps) { +function ConnectionsTable({ links, streams, destinations, functions, reloadCallback }: RemoteEntitiesProps) { const streamsById = index(streams, "id"); const destinationsById = index(destinations, "id"); + const functionsById = index(functions, "id"); + const modal = useAntdModal(); + const workspace = useWorkspace(); const router = useRouter(); + const [showId, setShowId] = React.useState(false); const [loading, setLoading] = React.useState(false); const [sorting, setSorting] = useQueryStringState("sorting", { defaultValue: { columns: [] }, ...jsonSerializationBase64, }); + const reloadStore = useStoreReload(); + + const onSaveMutation = useConfigObjectLinkMutation(async (obj: any) => { + await get(`/api/${workspace.id}/config/link`, { + body: obj, + }); + }); + const deleteConnection = async (link: Omit) => { if (await confirmOp("Are you sure you want to unlink this site from this destination?")) { setLoading(true); try { await get(`/api/${workspace.id}/config/link`, { method: "DELETE", - query: { fromId: link.fromId, toId: link.toId }, + query: { id: link.id }, }); + await reloadStore(); feedbackSuccess("Successfully unliked"); reloadCallback(); } catch (e) { @@ -93,9 +180,18 @@ function ConnectionsTable({ links, streams, destinations, reloadCallback }: Remo console.log("sorter", newVal); }; const columns: ColumnType[] = [ + { + title: "ID", + dataIndex: "id", + width: "33%", + className: showId ? "" : "hidden", + render: (text, link) => { + return
{link.id}
; + }, + }, { title: "Source", - width: "60%", + width: "33%", sortOrder: sorting.columns?.find(s => s.field === "Source")?.order, sorter: (a, b) => { const streamA = streamsById[a.fromId]; @@ -108,24 +204,15 @@ function ConnectionsTable({ links, streams, destinations, reloadCallback }: Remo return
Stream not found
; } return ( -
-
{stream.name}
-
- } - /> -
-
+ + + ); }, }, { title: "Destination", - width: "40%", + width: "33%", sortOrder: sorting.columns?.find(s => s.field === "Destination")?.order, sorter: (a, b) => { @@ -138,43 +225,127 @@ function ConnectionsTable({ links, streams, destinations, reloadCallback }: Remo if (!destination) { return
Destination not found
; } - const coreDestinationType = getCoreDestinationType(destination.destinationType); return ( -
-
-
- {coreDestinationType.icon} -
-
{destination.name}
-
- } - className="link" - size="small" - href={`/${workspace.id}/destinations?id=${link.toId}`} - /> -
-
+ + + + ); + }, + }, + { + title: "Functions", + width: "33%", + render: (text, link) => { + return ( +
+ {functions && + (link.data?.functions || []) + .filter(f => f.functionId.startsWith("udf.")) + .map((f, i) => { + const id = f.functionId.replace("udf.", ""); + const func = functionsById[id]; + if (!func) { + return f.functionId} />; + } + return ( +
router.push(`/${workspace.slugOrId}/functions?id=${id}`)} + key={i} + > + +
+ ); + })}
); }, }, { - title: "Actions", - render: (text, link) => ( -
- } /> - } - onClick={() => { + title: setShowId(!showId)}>Actions, + key: "actions", + render: (text, link) => { + const stream = streamsById[link.fromId]; + const dst = destinationsById[link.toId]; + const isEnabled = !link.data?.disabled; + let type = "function"; + try { + if (getCoreDestinationType(dst.destinationType).usesBulker) { + type = "bulker"; + } + } catch (e) {} + const items: ButtonProps[] = [ + { + icon: isEnabled ? : , + label: isEnabled ? "Disable connection" : "Enable connection", + collapsed: true, + onClick: async () => { + modal.confirm({ + title: `Are you sure you want to ${!isEnabled ? "enable" : "disable"} this connection?`, + content: ( + <> + This will {isEnabled ? "disable" : "enable"} the connection between +
+ {stream.name} and {dst.name} + + ), + okType: isEnabled ? "danger" : "primary", + okText: !isEnabled ? "Enable" : "Disable", + cancelText: "Cancel", + onOk: async () => { + setLoading(true); + try { + const updatedLink = { + ...link, + data: { + ...link.data, + disabled: isEnabled, + }, + }; + await onSaveMutation.mutateAsync(updatedLink); + await reloadStore(); + feedbackSuccess(`Connection ${!isEnabled ? "enabled" : "disabled"}`); + reloadCallback(); + } catch (e) { + feedbackError(`Failed to ${!isEnabled ? "enable" : "disable"} connection`, { error: e }); + } finally { + setLoading(false); + } + }, + }); + }, + requiredPermission: "editEntities", + }, + { + icon: , + label: "Edit", + href: `/connections/edit?id=${link.id}`, + requiredPermission: "editEntities", + }, + { + icon: , + //collapsed: true, + href: toURL("/data", { + query: JSON5.stringify({ + activeView: type, + viewState: { [type]: { actorId: link.id } }, + }), + }), + label: "Live Events", + }, + { + icon: , + collapsed: true, + onClick: async () => { deleteConnection(link); - }} - /> -
- ), + }, + danger: true, + label: "Delete", + requiredPermission: "deleteEntities", + }, + ]; + return ; + }, }, ]; return ( @@ -184,7 +355,10 @@ function ConnectionsTable({ links, streams, destinations, reloadCallback }: Remo dataSource={links} sortDirections={["ascend", "descend"]} columns={columns} - className="border border-backgroundDark rounded-lg" + rowClassName={link => { + return link.data?.disabled ? "opacity-50" : ""; + }} + className="border border-backgroundDark rounded-lg " pagination={false} loading={loading} onChange={onChange} @@ -194,7 +368,10 @@ function ConnectionsTable({ links, streams, destinations, reloadCallback }: Remo } function Connections(props: RemoteEntitiesProps) { - const { streams, destinations, links } = props; + const { streams, destinations, links, functions } = props; + const router = useRouter(); + const destinationFilter = router.query.destination as string | undefined; + const srcFilter = router.query.source as string | undefined; const workspace = useWorkspace(); if (props.streams.length == 0 || props.destinations.length == 0) { return ( @@ -203,20 +380,20 @@ function Connections(props: RemoteEntitiesProps) {
In order to connect site to destination please create at least one destination and one stream. Currently, you have{" "} - + {props.destinations.length} destination{props.destinations.length === 1 ? "" : "s"} {" "} and{" "} - + {props.streams.length} site{props.streams.length === 1 ? "" : "s"} {" "} configured
- }> + } requiredPermission="editEntities"> Create Destination - }> + } requiredPermission="editEntities"> Create Site
@@ -225,12 +402,36 @@ function Connections(props: RemoteEntitiesProps) { } return (
-
+
Edit connections
+ {destinationFilter && ( +
+ {/*to*/} + d.id === destinationFilter)} /> + + + +
+ )} + {srcFilter && ( +
+ {/*to*/} + d.id === srcFilter)} /> + + + +
+ )}
- }> + } + requiredPermission="editEntities" + > Connect site and destination
@@ -239,7 +440,10 @@ function Connections(props: RemoteEntitiesProps) { {links.length === 0 && } {links.length > 0 && ( !destinationFilter || l.toId === destinationFilter) + .filter(l => !srcFilter || l.fromId === srcFilter)} + functions={functions} streams={streams} destinations={destinations} reloadCallback={props.reloadCallback} @@ -251,23 +455,18 @@ function Connections(props: RemoteEntitiesProps) { } function ConnectionsLoader(props: { reloadCallback: () => void }) { - const workspace = useWorkspace(); - const data = useLinksQuery(workspace.id, "push", { - cacheTime: 0, - retry: false, - }); + const functions = useConfigObjectList("function"); + const streams = useConfigObjectList("stream"); + const destinations = useConfigObjectList("destination"); + const links = useConfigObjectLinks(); return ( - ( - - )} + l.type === "push")} + reloadCallback={() => {}} /> ); } diff --git a/webapps/console/pages/[workspaceId]/custom-images.tsx b/webapps/console/pages/[workspaceId]/custom-images.tsx new file mode 100644 index 000000000..831565645 --- /dev/null +++ b/webapps/console/pages/[workspaceId]/custom-images.tsx @@ -0,0 +1,134 @@ +import { WorkspacePageLayout } from "../../components/PageLayout/WorkspacePageLayout"; +import { ConfigEditor, ConfigEditorProps } from "../../components/ConfigObjectEditor/ConfigEditor"; +import { ConnectorImageConfig } from "../../lib/schema"; +import { useAppConfig, useWorkspace } from "../../lib/context"; +import React from "react"; +import { SourceType } from "../api/sources"; +import { ErrorCard } from "../../components/GlobalError/GlobalError"; +import { ServerCog } from "lucide-react"; +import { FaDocker } from "react-icons/fa"; +import { Htmlizer } from "../../components/Htmlizer/Htmlizer"; +import { rpc } from "juava"; +import { UpgradeDialog } from "../../components/Billing/UpgradeDialog"; +import { useBilling } from "../../components/Billing/BillingProvider"; +import { LoadingAnimation } from "../../components/GlobalLoader/GlobalLoader"; + +const CustomImages: React.FC = () => { + return ( + + + + ); +}; + +const CustomImagesList: React.FC<{}> = () => { + const workspace = useWorkspace(); + const appconfig = useAppConfig(); + const billing = useBilling(); + + if (billing.loading) { + return ; + } + if (billing.enabled && billing.settings?.planId === "free") { + return ; + } + + if (!(appconfig.syncs.enabled || workspace.featuresEnabled.includes("syncs"))) { + return ( + + ); + } + + const config: ConfigEditorProps = { + listColumns: [ + { + title: "Package", + render: (s: ConnectorImageConfig) => {`${s.package}:${s.version}`}, + }, + ], + objectType: ConnectorImageConfig, + fields: { + type: { constant: "custom-image" }, + workspaceId: { constant: workspace.id }, + cloneId: { hidden: true }, + package: { + documentation: ( + + { + "Docker image name. Images can also include a registry hostname, e.g.: fictional.registry.example/imagename, and possibly a port number as well." + } + + ), + }, + version: { + documentation: "Docker image tag", + }, + }, + noun: "custom image", + type: "custom-image", + explanation: "Custom connector images that can be used to setup Service connector", + icon: () => , + testConnectionEnabled: () => true, + testButtonLabel: "Check Image", + onTest: async obj => { + try { + const firstRes = await rpc( + `/api/${workspace.id}/sources/spec?package=${obj.package}&version=${obj.version}&force=true` + ); + if (firstRes.ok) { + return { ok: true }; + } else if (firstRes.error) { + return { ok: false, error: `Cannot load specs for ${obj.package}:${obj.version}: ${firstRes.error}` }; + } else { + for (let i = 0; i < 60; i++) { + await new Promise(resolve => setTimeout(resolve, 2000)); + const resp = await rpc(`/api/${workspace.id}/sources/spec?package=${obj.package}&version=${obj.version}`); + if (!resp.pending) { + if (resp.error) { + return { ok: false, error: `Cannot load specs for ${obj.package}:${obj.version}: ${resp.error}` }; + } else { + return { ok: true }; + } + } + } + return { ok: false, error: `Cannot load specs for ${obj.package}:${obj.version}: Timeout` }; + } + } catch (error: any) { + return { ok: false, error: `Cannot load specs for ${obj.package}:${obj.version}: ${error.message}` }; + } + }, + editorTitle: (_: ConnectorImageConfig, isNew: boolean) => { + const verb = isNew ? "New" : "Edit"; + return ( +
+
+ +
+ {verb} custom image +
+ ); + }, + actions: [ + { + icon: , + title: "Setup Connector", + collapsed: false, + link: s => + `/services?id=new&packageType=airbyte&packageId=${encodeURIComponent(s.package)}&version=${encodeURIComponent( + s.version + )}`, + }, + ], + }; + return ( + <> + + + ); +}; + +export default CustomImages; diff --git a/webapps/console/pages/[workspaceId]/data.tsx b/webapps/console/pages/[workspaceId]/data.tsx index 4bdda3cc0..5e3ccfe71 100644 --- a/webapps/console/pages/[workspaceId]/data.tsx +++ b/webapps/console/pages/[workspaceId]/data.tsx @@ -10,10 +10,10 @@ const DataViewPage: React.FC = () => { return (
-
+

Live Events

-
+
diff --git a/webapps/console/pages/[workspaceId]/destinations.tsx b/webapps/console/pages/[workspaceId]/destinations.tsx index 10d2e9593..f9eb515cd 100644 --- a/webapps/console/pages/[workspaceId]/destinations.tsx +++ b/webapps/console/pages/[workspaceId]/destinations.tsx @@ -1,13 +1,8 @@ import { WorkspacePageLayout } from "../../components/PageLayout/WorkspacePageLayout"; import { Button, Modal, Popover, Skeleton, Table, Tabs, Tooltip } from "antd"; -import { - ConfigEditor, - ConfigEditorProps, - CustomWidgetProps, - FieldDisplay, -} from "../../components/ConfigObjectEditor/ConfigEditor"; +import { ConfigEditor, ConfigEditorProps, FieldDisplay } from "../../components/ConfigObjectEditor/ConfigEditor"; import { DestinationConfig } from "../../lib/schema"; -import { confirmOp, feedbackError, serialization, useURLPersistedState } from "../../lib/ui"; +import { confirmOp, copyTextToClipboard, feedbackError, feedbackSuccess, serialization } from "../../lib/ui"; import { coreDestinations, coreDestinationsMap, @@ -16,12 +11,12 @@ import { PropertyUI, } from "../../lib/schema/destinations"; import { DestinationCatalog, getDestinationIcon } from "../../components/DestinationsCatalog/DestinationsCatalog"; -import { useWorkspace } from "../../lib/context"; +import { useAppConfig, useWorkspace } from "../../lib/context"; import { useRouter } from "next/router"; import { assertDefined, getLog, requireDefined, rpc } from "juava"; import React, { PropsWithChildren, useState } from "react"; import TextArea from "antd/lib/input/TextArea"; -import { getConfigApi, useApi } from "../../lib/useApi"; +import { get, getConfigApi, useApi } from "../../lib/useApi"; import { EmbeddedErrorMessage, ErrorCard } from "../../components/GlobalError/GlobalError"; import { branding } from "../../lib/branding"; import styles from "../../components/ConfigObjectEditor/ConfigEditor.module.css"; @@ -30,13 +25,32 @@ import { SnippedEditor } from "../../components/CodeEditor/SnippedEditor"; import { MultiSelectWithCustomOptions } from "../../components/MultiSelectWithCustomOptions/MultiSelectWithCustomOptions"; import { LoadingAnimation } from "../../components/GlobalLoader/GlobalLoader"; import { useQuery } from "@tanstack/react-query"; -import { Copy, Eye, FileKey, Loader2, TerminalSquare, XCircle } from "lucide-react"; +import { + AlertTriangle, + Check, + Copy, + Eye, + FileKey, + Loader2, + Share2, + ShieldAlert, + TerminalSquare, + XCircle, + Zap, +} from "lucide-react"; import { ClickhouseConnectionCredentials } from "../../lib/schema/clickhouse-connection-credentials"; import { CodeBlock } from "../../components/CodeBlock/CodeBlock"; import { useBilling } from "../../components/Billing/BillingProvider"; import { UpgradeDialog } from "../../components/Billing/UpgradeDialog"; import { ProvisionDatabaseButton } from "../../components/ProvisionDatabaseButton/ProvisionDatabaseButton"; import Link from "next/link"; +import { CodeEditor } from "../../components/CodeEditor/CodeEditor"; +import { ObjectTitle } from "../../components/ObjectTitle/ObjectTitle"; +import { useQueryStringState } from "../../lib/useQueryStringState"; +import { CustomWidgetProps } from "../../components/ConfigObjectEditor/Editors"; +import { Htmlizer } from "../../components/Htmlizer/Htmlizer"; +import omit from "lodash/omit"; +import { EditorToolbar } from "../../components/EditorToolbar/EditorToolbar"; const log = getLog("destinations"); const Loader: React.FC<{}> = () => { @@ -143,6 +157,20 @@ export const ArrayTextareaEditor: React.FC> = props ); }; +export const TextareaEditor: React.FC> = props => { + const [value, setValue] = useState(props.value || ""); + return ( +