From 84058d9a0a5151769c14191d778125d1a4ea08ff Mon Sep 17 00:00:00 2001 From: Pratistha Singh Date: Fri, 14 Aug 2026 17:22:21 +0530 Subject: [PATCH 1/4] chore: squash community contributions into single signed commit Squashed commits: - feat: add /version endpoint and bump to 0.1.0 - feat: read version from APPLICATION_VERSION env var - feat: add /version endpoint and bump to 0.1.0 (#59) - chore: merge deep-agent into main (#76) - fix: embed branding into server rendered HTML (#146) - feat: State persistence recovery (#79) Signed-off-by: Pratistha Singh --- .github/CODEOWNERS | 7 + .github/ISSUE_TEMPLATE/bug_report.yml | 67 + .github/ISSUE_TEMPLATE/config.yml | 5 + .github/ISSUE_TEMPLATE/feature_request.yml | 41 + .github/PULL_REQUEST_TEMPLATE.md | 28 + .github/dependabot.yml | 26 + .github/workflows/build-base-image.yml | 357 + .github/workflows/ci.yml | 87 + .github/workflows/dependency-review.yml | 27 + .github/workflows/draft-check.yml | 18 + .github/workflows/license-scan.yml | 37 + .github/workflows/pr-title.yml | 36 + .github/workflows/release-please.yml | 17 + .github/workflows/require-issue.yml | 63 + .github/workflows/scorecard.yml | 37 + .gitignore | 8 + .gitlab-ci.yml | 67 + .trivyignore | 6 + Containerfile | 24 +- Makefile | 45 +- README.md | 140 +- compose.yml | 42 +- config/compliance/README.md | 185 + config/compliance/data.json | 8 + config/compliance/policy.rego | 59 + config/ui/README.md | 252 + config/ui/examples/blue-theme.yaml | 19 + config/ui/examples/minimal.yaml | 29 + config/ui/examples/production.yaml | 83 + config/ui/examples/red-hat-branding.yaml | 22 + config/ui/settings.yaml | 62 + deployment/base/configmap.yaml | 14 + deployment/base/deployment.yaml | 103 + deployment/base/kustomization.yaml | 13 + deployment/base/secret.yaml | 13 + deployment/base/service.yaml | 18 + deployment/openshift/deployment.yaml | 4 +- deployment/overlays/kind/ingress.yaml | 51 + deployment/overlays/kind/kustomization.yaml | 18 + .../overlays/openshift/buildconfig.yaml | 30 + .../overlays/openshift/imagestream.yaml | 11 + .../overlays/openshift/kustomization.yaml | 14 + deployment/overlays/openshift/route.yaml | 17 + docs/deployment-patterns.md | 491 ++ e2e/accessibility/wcag.spec.ts | 173 + e2e/auth/rate-limit.spec.ts | 94 + e2e/chaos/resilience.spec.ts | 394 + e2e/config/branding.spec.ts | 85 + e2e/config/feature-flags.spec.ts | 43 + e2e/errors/error-states.spec.ts | 87 + e2e/helpers/config-mount.ts | 131 + e2e/helpers/local-storage.ts | 52 + e2e/helpers/sse-mock.ts | 159 + e2e/helpers/wait.ts | 22 + e2e/hitl/auto-approve.spec.ts | 137 + e2e/hitl/interrupt-flow.spec.ts | 187 + e2e/page-objects/ChatPage.ts | 49 + e2e/page-objects/HomePage.ts | 28 + e2e/page-objects/SettingsPage.ts | 37 + e2e/settings/settings-page.spec.ts | 112 + e2e/smoke/chat-flow.spec.ts | 46 + env.template | 24 +- eslint.config.js | 7 + index.html | 22 +- package-lock.json | 7053 ++++++++++++++--- package.json | 63 +- playwright.config.ts | 35 + public/redhat-logo.svg | 3 + src/components/AnnouncementBanner.tsx | 86 + src/frontend/App.tsx | 111 +- .../components/AIMessageRenderer.test.tsx | 152 + src/frontend/components/ActivityTimeline.tsx | 41 +- src/frontend/components/ArtifactViewer.tsx | 70 + src/frontend/components/ChatErrorBoundary.tsx | 61 +- src/frontend/components/ChatMessagesView.tsx | 1163 ++- .../components/CustomDataRenderer.tsx | 86 + src/frontend/components/DebugPanel.tsx | 66 + src/frontend/components/DebugToggle.tsx | 21 + src/frontend/components/ErrorBoundary.tsx | 77 +- src/frontend/components/ErrorRecovery.tsx | 155 + src/frontend/components/FeatureGate.tsx | 41 + src/frontend/components/FeedbackButtons.tsx | 103 + src/frontend/components/InputForm.tsx | 197 +- .../components/InterruptBanner.test.tsx | 251 + src/frontend/components/InterruptBanner.tsx | 259 + .../components/KeyboardShortcutsDialog.tsx | 115 + src/frontend/components/McpStatusPanel.tsx | 60 + .../components/ReconnectingBanner.tsx | 24 + src/frontend/components/RedHatLogo.tsx | 16 + .../components/SessionExpiredModal.tsx | 45 + src/frontend/components/Sidebar.tsx | 502 +- .../components/StreamEventRenderer.tsx | 49 +- src/frontend/components/SubAgentIndicator.tsx | 217 + .../components/TaskProgressStepper.tsx | 90 + src/frontend/components/TasksSidebar.tsx | 128 + .../components/ToastNotifications.tsx | 45 + src/frontend/components/TodoListRenderer.tsx | 100 + src/frontend/components/TodoStrip.tsx | 62 + .../__tests__/accessibility.test.tsx | 484 ++ src/frontend/components/layout/AppLayout.tsx | 412 +- .../components/layout/ThemeToggle.tsx | 26 + .../settings/AlwaysAllowedTools.tsx | 105 + .../settings/AppearanceSettings.tsx | 67 + .../components/settings/MemoryList.tsx | 103 + .../components/settings/ProfileSection.tsx | 112 + .../components/settings/RulesEditor.tsx | 121 + src/frontend/components/ui/badge.tsx | 46 - src/frontend/components/ui/button.tsx | 59 - src/frontend/components/ui/card.tsx | 92 - src/frontend/components/ui/input.tsx | 21 - src/frontend/components/ui/scroll-area.tsx | 57 - src/frontend/components/ui/select.tsx | 183 - src/frontend/components/ui/tabs.tsx | 64 - src/frontend/components/ui/textarea.tsx | 18 - src/frontend/contexts/ChatContext.tsx | 6 +- src/frontend/global.css | 348 +- src/frontend/hooks/useAgentHealth.ts | 71 + src/frontend/hooks/useDataStream.tsx | 54 +- src/frontend/hooks/useKeyboardShortcuts.ts | 156 + src/frontend/hooks/useRateLimitState.ts | 73 + src/frontend/hooks/useRefreshableToken.tsx | 9 + src/frontend/hooks/useStreamingAPI.test.ts | 232 + src/frontend/hooks/useStreamingAPI.ts | 1228 +++ src/frontend/hooks/useThemeSync.ts | 21 + src/frontend/lib/app-paths.ts | 36 + .../lib/streaming/SSEProcessor.test.ts | 221 + src/frontend/lib/streaming/SSEProcessor.ts | 210 + .../lib/streaming/StreamingManager.test.ts | 245 + .../lib/streaming/StreamingManager.ts | 241 + .../lib/streaming/streamingManagerRegistry.ts | 40 + src/frontend/lib/toolIcons.ts | 17 + src/frontend/main.tsx | 41 +- src/frontend/pages/ChatPage.tsx | 665 +- src/frontend/pages/HomePage.tsx | 171 +- src/frontend/pages/SettingsPage.tsx | 140 + src/frontend/redux/hooks.ts | 5 + src/frontend/redux/slices/chats.test.ts | 245 + src/frontend/redux/slices/chats.ts | 240 + src/frontend/redux/slices/config.ts | 65 + src/frontend/redux/slices/personalization.ts | 119 + src/frontend/redux/slices/toasts.ts | 44 + .../redux/slices/userSettings.test.ts | 142 + src/frontend/redux/slices/userSettings.ts | 115 + src/frontend/redux/store.ts | 26 + src/frontend/services/agent-rest.ts | 277 +- src/frontend/services/authenticated-fetch.ts | 77 + src/frontend/services/chatStorage.ts | 4 +- src/frontend/services/config.service.ts | 42 + src/frontend/services/export-chat.ts | 50 + src/frontend/services/feedback-api.ts | 50 + src/frontend/services/logout.ts | 40 + src/frontend/services/newChatTracker.ts | 14 + src/frontend/test-utils/createTestStore.ts | 18 + src/frontend/test-utils/render.tsx | 25 + src/frontend/test-utils/setup.ts | 99 + src/frontend/types/chat.ts | 1 + src/frontend/types/deep-agent.test.ts | 178 + src/frontend/types/deep-agent.ts | 126 + src/frontend/types/errors.ts | 21 + src/frontend/types/user.ts | 6 + src/frontend/utils/errorHandler.ts | 94 + src/server/__tests__/proxy.test.ts | 479 ++ src/server/__tests__/security.test.ts | 349 + src/server/__tests__/test-utils.ts | 114 + src/server/controllers/v1/agent.ts | 46 +- src/server/index.ts | 61 +- src/server/plugins/auth-check.plugin.ts | 108 +- src/server/plugins/auth.plugin.ts | 54 +- src/server/plugins/opa.plugin.ts | 74 + src/server/plugins/trace.plugin.ts | 41 + src/server/router/api.router.ts | 66 +- src/server/router/client.router.ts | 111 +- src/server/router/logout.router.ts | 35 + src/server/router/proxy.router.ts | 856 ++ src/server/server.ts | 206 +- src/server/tracing.ts | 65 + src/server/utils/config-edge-cases.test.ts | 264 + src/server/utils/config.ts | 1 - src/server/utils/configWatcher.ts | 73 + src/server/utils/opa.ts | 158 + src/server/utils/redis.ts | 111 + src/server/utils/settings.test.ts | 339 + src/server/utils/settings.ts | 558 ++ trivy-license.yaml | 25 + tsconfig.json | 3 +- vite.config.ts | 10 +- vitest.config.ts | 22 + 187 files changed, 25681 insertions(+), 3003 deletions(-) create mode 100644 .github/CODEOWNERS create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/build-base-image.yml create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/dependency-review.yml create mode 100644 .github/workflows/draft-check.yml create mode 100644 .github/workflows/license-scan.yml create mode 100644 .github/workflows/pr-title.yml create mode 100644 .github/workflows/release-please.yml create mode 100644 .github/workflows/require-issue.yml create mode 100644 .github/workflows/scorecard.yml create mode 100644 .gitlab-ci.yml create mode 100644 .trivyignore create mode 100644 config/compliance/README.md create mode 100644 config/compliance/data.json create mode 100644 config/compliance/policy.rego create mode 100644 config/ui/README.md create mode 100644 config/ui/examples/blue-theme.yaml create mode 100644 config/ui/examples/minimal.yaml create mode 100644 config/ui/examples/production.yaml create mode 100644 config/ui/examples/red-hat-branding.yaml create mode 100644 config/ui/settings.yaml create mode 100644 deployment/base/configmap.yaml create mode 100644 deployment/base/deployment.yaml create mode 100644 deployment/base/kustomization.yaml create mode 100644 deployment/base/secret.yaml create mode 100644 deployment/base/service.yaml create mode 100644 deployment/overlays/kind/ingress.yaml create mode 100644 deployment/overlays/kind/kustomization.yaml create mode 100644 deployment/overlays/openshift/buildconfig.yaml create mode 100644 deployment/overlays/openshift/imagestream.yaml create mode 100644 deployment/overlays/openshift/kustomization.yaml create mode 100644 deployment/overlays/openshift/route.yaml create mode 100644 docs/deployment-patterns.md create mode 100644 e2e/accessibility/wcag.spec.ts create mode 100644 e2e/auth/rate-limit.spec.ts create mode 100644 e2e/chaos/resilience.spec.ts create mode 100644 e2e/config/branding.spec.ts create mode 100644 e2e/config/feature-flags.spec.ts create mode 100644 e2e/errors/error-states.spec.ts create mode 100644 e2e/helpers/config-mount.ts create mode 100644 e2e/helpers/local-storage.ts create mode 100644 e2e/helpers/sse-mock.ts create mode 100644 e2e/helpers/wait.ts create mode 100644 e2e/hitl/auto-approve.spec.ts create mode 100644 e2e/hitl/interrupt-flow.spec.ts create mode 100644 e2e/page-objects/ChatPage.ts create mode 100644 e2e/page-objects/HomePage.ts create mode 100644 e2e/page-objects/SettingsPage.ts create mode 100644 e2e/settings/settings-page.spec.ts create mode 100644 e2e/smoke/chat-flow.spec.ts create mode 100644 playwright.config.ts create mode 100644 public/redhat-logo.svg create mode 100644 src/components/AnnouncementBanner.tsx create mode 100644 src/frontend/components/AIMessageRenderer.test.tsx create mode 100644 src/frontend/components/ArtifactViewer.tsx create mode 100644 src/frontend/components/CustomDataRenderer.tsx create mode 100644 src/frontend/components/DebugPanel.tsx create mode 100644 src/frontend/components/DebugToggle.tsx create mode 100644 src/frontend/components/ErrorRecovery.tsx create mode 100644 src/frontend/components/FeatureGate.tsx create mode 100644 src/frontend/components/FeedbackButtons.tsx create mode 100644 src/frontend/components/InterruptBanner.test.tsx create mode 100644 src/frontend/components/InterruptBanner.tsx create mode 100644 src/frontend/components/KeyboardShortcutsDialog.tsx create mode 100644 src/frontend/components/McpStatusPanel.tsx create mode 100644 src/frontend/components/ReconnectingBanner.tsx create mode 100644 src/frontend/components/RedHatLogo.tsx create mode 100644 src/frontend/components/SessionExpiredModal.tsx create mode 100644 src/frontend/components/SubAgentIndicator.tsx create mode 100644 src/frontend/components/TaskProgressStepper.tsx create mode 100644 src/frontend/components/TasksSidebar.tsx create mode 100644 src/frontend/components/ToastNotifications.tsx create mode 100644 src/frontend/components/TodoListRenderer.tsx create mode 100644 src/frontend/components/TodoStrip.tsx create mode 100644 src/frontend/components/__tests__/accessibility.test.tsx create mode 100644 src/frontend/components/layout/ThemeToggle.tsx create mode 100644 src/frontend/components/settings/AlwaysAllowedTools.tsx create mode 100644 src/frontend/components/settings/AppearanceSettings.tsx create mode 100644 src/frontend/components/settings/MemoryList.tsx create mode 100644 src/frontend/components/settings/ProfileSection.tsx create mode 100644 src/frontend/components/settings/RulesEditor.tsx delete mode 100644 src/frontend/components/ui/badge.tsx delete mode 100644 src/frontend/components/ui/button.tsx delete mode 100644 src/frontend/components/ui/card.tsx delete mode 100644 src/frontend/components/ui/input.tsx delete mode 100644 src/frontend/components/ui/scroll-area.tsx delete mode 100644 src/frontend/components/ui/select.tsx delete mode 100644 src/frontend/components/ui/tabs.tsx delete mode 100644 src/frontend/components/ui/textarea.tsx create mode 100644 src/frontend/hooks/useAgentHealth.ts create mode 100644 src/frontend/hooks/useKeyboardShortcuts.ts create mode 100644 src/frontend/hooks/useRateLimitState.ts create mode 100644 src/frontend/hooks/useStreamingAPI.test.ts create mode 100644 src/frontend/hooks/useStreamingAPI.ts create mode 100644 src/frontend/hooks/useThemeSync.ts create mode 100644 src/frontend/lib/app-paths.ts create mode 100644 src/frontend/lib/streaming/SSEProcessor.test.ts create mode 100644 src/frontend/lib/streaming/SSEProcessor.ts create mode 100644 src/frontend/lib/streaming/StreamingManager.test.ts create mode 100644 src/frontend/lib/streaming/StreamingManager.ts create mode 100644 src/frontend/lib/streaming/streamingManagerRegistry.ts create mode 100644 src/frontend/lib/toolIcons.ts create mode 100644 src/frontend/pages/SettingsPage.tsx create mode 100644 src/frontend/redux/hooks.ts create mode 100644 src/frontend/redux/slices/chats.test.ts create mode 100644 src/frontend/redux/slices/chats.ts create mode 100644 src/frontend/redux/slices/config.ts create mode 100644 src/frontend/redux/slices/personalization.ts create mode 100644 src/frontend/redux/slices/toasts.ts create mode 100644 src/frontend/redux/slices/userSettings.test.ts create mode 100644 src/frontend/redux/slices/userSettings.ts create mode 100644 src/frontend/redux/store.ts create mode 100644 src/frontend/services/authenticated-fetch.ts create mode 100644 src/frontend/services/config.service.ts create mode 100644 src/frontend/services/export-chat.ts create mode 100644 src/frontend/services/feedback-api.ts create mode 100644 src/frontend/services/logout.ts create mode 100644 src/frontend/services/newChatTracker.ts create mode 100644 src/frontend/test-utils/createTestStore.ts create mode 100644 src/frontend/test-utils/render.tsx create mode 100644 src/frontend/test-utils/setup.ts create mode 100644 src/frontend/types/deep-agent.test.ts create mode 100644 src/frontend/types/deep-agent.ts create mode 100644 src/frontend/types/errors.ts create mode 100644 src/frontend/utils/errorHandler.ts create mode 100644 src/server/__tests__/proxy.test.ts create mode 100644 src/server/__tests__/security.test.ts create mode 100644 src/server/__tests__/test-utils.ts create mode 100644 src/server/plugins/opa.plugin.ts create mode 100644 src/server/plugins/trace.plugin.ts create mode 100644 src/server/router/logout.router.ts create mode 100644 src/server/router/proxy.router.ts create mode 100644 src/server/tracing.ts create mode 100644 src/server/utils/config-edge-cases.test.ts delete mode 100644 src/server/utils/config.ts create mode 100644 src/server/utils/configWatcher.ts create mode 100644 src/server/utils/opa.ts create mode 100644 src/server/utils/redis.ts create mode 100644 src/server/utils/settings.test.ts create mode 100644 src/server/utils/settings.ts create mode 100644 trivy-license.yaml create mode 100644 vitest.config.ts diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..6c1a0bc5 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,7 @@ +# Default owners for everything +* @redhat-data-and-ai/template-ui-maintainers + +# CI and infrastructure +.github/ @redhat-data-and-ai/template-ui-maintainers +Containerfile @redhat-data-and-ai/template-ui-maintainers +deployment/ @redhat-data-and-ai/template-ui-maintainers diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 00000000..afeb4b32 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,67 @@ +name: Bug Report +description: Report a bug or unexpected behavior +labels: ["bug", "triage"] +body: + - type: markdown + attributes: + value: | + Thanks for reporting a bug. Please fill in the details below so we can reproduce and fix it. + + - type: textarea + id: description + attributes: + label: Description + description: A clear description of the bug. + placeholder: What happened? + validations: + required: true + + - type: textarea + id: steps + attributes: + label: Steps to reproduce + description: Minimal steps to trigger the issue. + placeholder: | + 1. Run `npm run dev` + 2. Navigate to ... + 3. Observe ... + validations: + required: true + + - type: textarea + id: expected + attributes: + label: Expected behavior + description: What should have happened instead? + validations: + required: true + + - type: input + id: version + attributes: + label: Version + description: Output of `git describe --tags` or the container image tag. + placeholder: v1.2.3 or deep-agent-abc1234 + validations: + required: false + + - type: dropdown + id: environment + attributes: + label: Environment + options: + - Local (npm run dev) + - Container (podman/docker) + - OpenShift + - Other + validations: + required: true + + - type: textarea + id: logs + attributes: + label: Logs or error output + description: Paste relevant logs or browser console output (redact any secrets). + render: shell + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..d7ab4f60 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Security vulnerability + url: https://github.com/redhat-data-and-ai/template-ui/security/advisories/new + about: Report security issues privately via GitHub Security Advisories. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 00000000..c03049f7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,41 @@ +name: Feature Request +description: Suggest a new feature or improvement +labels: ["enhancement"] +body: + - type: markdown + attributes: + value: | + Describe the feature you'd like and the problem it solves. + + - type: textarea + id: problem + attributes: + label: Problem + description: What problem does this solve? What's the current pain point? + placeholder: I'm always frustrated when ... + validations: + required: true + + - type: textarea + id: solution + attributes: + label: Proposed solution + description: How should this work? + validations: + required: true + + - type: textarea + id: alternatives + attributes: + label: Alternatives considered + description: Other approaches you've thought about. + validations: + required: false + + - type: textarea + id: context + attributes: + label: Additional context + description: Links, screenshots, or related issues. + validations: + required: false diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..ba8fb8af --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,28 @@ +## What + + + +Fixes # + +## How + + + +## Testing + + + +- [ ] Unit tests added/updated +- [ ] Ran locally (`npm run dev`) +- [ ] Manual verification (describe below if applicable) + +## Rollback + + + +## Checklist + +- [ ] PR title follows [Conventional Commits](https://www.conventionalcommits.org/) (`feat:`, `fix:`, `ci:`, etc.) +- [ ] No secrets, credentials, or PII in the diff +- [ ] No breaking changes (or documented above with a migration path) +- [ ] Lint and tests pass (`npm run lint && npm run test`) diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..aa4a82cd --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,26 @@ +version: 2 +updates: + - package-ecosystem: npm + directory: "/" + schedule: + interval: weekly + groups: + minor-and-patch: + update-types: + - minor + - patch + + - package-ecosystem: docker + directory: "/" + schedule: + interval: weekly + + - package-ecosystem: github-actions + directory: "/" + schedule: + interval: weekly + groups: + actions-minor-patch: + update-types: + - minor + - patch diff --git a/.github/workflows/build-base-image.yml b/.github/workflows/build-base-image.yml new file mode 100644 index 00000000..7f11a7d6 --- /dev/null +++ b/.github/workflows/build-base-image.yml @@ -0,0 +1,357 @@ +name: Build and Push Base Image + +on: + push: + tags: + - '*' + branches: + - main + - deep-agent + pull_request: + branches: [ main, deep-agent ] + paths-ignore: + - '**.md' + - 'docs/**' + - 'tests/**' + workflow_dispatch: + +permissions: + contents: write + packages: write + security-events: write + id-token: write + attestations: write + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: false + +jobs: + build-and-push: + runs-on: ubuntu-latest + timeout-minutes: 30 + + steps: + - name: Checkout code + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + with: + persist-credentials: false + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3 + + - name: Install cosign + if: github.event_name != 'pull_request' + uses: sigstore/cosign-installer@398d4b0eeef1380460a10c8013a76f728fb906ac # v3 + + - name: Log in to GitHub Container Registry + if: github.event_name != 'pull_request' + uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata (tags, labels) + if: github.event_name != 'pull_request' + id: meta + uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5 + with: + images: ghcr.io/${{ github.repository }} + tags: | + type=ref,event=tag + type=raw,value=latest,enable={{is_default_branch}} + type=sha,prefix={{branch}}-,enable=${{ !startsWith(github.ref, 'refs/tags/') }} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + + - name: Extract version for deployment package + id: version + run: | + if [[ "${{ github.ref }}" == refs/tags/* ]]; then + VERSION="${GITHUB_REF#refs/tags/}" + elif [[ "${{ github.ref }}" == refs/pull/* ]]; then + VERSION="pr-${{ github.event.pull_request.number }}-${GITHUB_SHA::7}" + else + VERSION="${GITHUB_REF#refs/heads/}-${GITHUB_SHA::7}" + fi + echo "version=${VERSION}" >> $GITHUB_OUTPUT + echo "package_name=ui-deployment.zip" >> $GITHUB_OUTPUT + + - name: Build image locally for scanning + uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5 + with: + context: . + file: ./Containerfile + push: false + load: true + tags: scan-target:${{ steps.version.outputs.version }} + cache-from: type=gha + cache-to: type=gha,mode=max + platforms: linux/amd64 + + - name: Scan image for vulnerabilities + uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0 + with: + image-ref: scan-target:${{ steps.version.outputs.version }} + format: table + severity: CRITICAL,HIGH + scanners: vuln + trivyignores: .trivyignore + exit-code: '0' + + - name: Fail on critical vulnerabilities + uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0 + with: + image-ref: scan-target:${{ steps.version.outputs.version }} + format: table + severity: CRITICAL + scanners: vuln + trivyignores: .trivyignore + exit-code: '1' + + - name: Generate Trivy JSON report + if: always() + continue-on-error: true + uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0 + with: + image-ref: scan-target:${{ steps.version.outputs.version }} + format: json + output: trivy-report.json + scanners: vuln + exit-code: '0' + + - name: Generate SBOM + continue-on-error: true + uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0 + with: + image-ref: scan-target:${{ steps.version.outputs.version }} + format: cyclonedx + output: sbom.cdx.json + scanners: vuln + exit-code: '0' + + - name: Upload SBOM + if: always() && hashFiles('sbom.cdx.json') != '' + continue-on-error: true + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: sbom-${{ steps.version.outputs.version }} + path: sbom.cdx.json + retention-days: 90 + + - name: Generate SARIF report + if: always() + continue-on-error: true + uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0 + with: + image-ref: scan-target:${{ steps.version.outputs.version }} + format: sarif + output: trivy-results.sarif + scanners: vuln + exit-code: '0' + + - name: Upload Trivy scan results to GitHub Security tab + if: always() && hashFiles('trivy-results.sarif') != '' + continue-on-error: true + uses: github/codeql-action/upload-sarif@e0647621c2984b5ed2f768cb892365bf2a616ad1 # v4 + with: + sarif_file: trivy-results.sarif + + - name: Upload Trivy report as artifact + if: always() && hashFiles('trivy-results.sarif') != '' + continue-on-error: true + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: trivy-report-${{ steps.version.outputs.version }} + path: trivy-results.sarif + retention-days: 30 + + - name: Push multi-platform image + if: github.event_name != 'pull_request' + id: push + uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5 + with: + context: . + file: ./Containerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + platforms: linux/amd64,linux/arm64 + + - name: Verify pushed image is pullable + if: github.event_name != 'pull_request' + run: | + docker pull ghcr.io/${{ github.repository }}@${{ steps.push.outputs.digest }} + echo "Image pull verified successfully" + + - name: Sign image with cosign + if: github.event_name != 'pull_request' + id: sign + continue-on-error: true + run: | + for attempt in 1 2 3; do + if cosign sign --yes --recursive \ + ghcr.io/${{ github.repository }}@${{ steps.push.outputs.digest }}; then + echo "signed=true" >> $GITHUB_OUTPUT + exit 0 + fi + echo "Signing attempt $attempt failed, retrying in 10s..." + sleep 10 + done + echo "signed=false" >> $GITHUB_OUTPUT + echo "::warning::Image signing failed after 3 attempts. Image was pushed but is unsigned." + exit 1 + + - name: Verify cosign signature + if: github.event_name != 'pull_request' && steps.sign.outputs.signed == 'true' + run: | + cosign verify \ + --certificate-identity-regexp="https://github.com/${{ github.repository }}/" \ + --certificate-oidc-issuer="https://token.actions.githubusercontent.com" \ + ghcr.io/${{ github.repository }}@${{ steps.push.outputs.digest }} + echo "Signature verification passed" + + - name: Attest build provenance + if: github.event_name != 'pull_request' + continue-on-error: true + uses: actions/attest-build-provenance@e8998f949152b193b063cb0ec769d69d929409be # v2 + with: + subject-name: ghcr.io/${{ github.repository }} + subject-digest: ${{ steps.push.outputs.digest }} + push-to-registry: true + + - name: Create deployment package metadata + if: github.event_name != 'pull_request' + run: | + cat > deployment/release-info.json < SHA256SUMS.txt + cat SHA256SUMS.txt + + - name: Collect release artifacts + if: startsWith(github.ref, 'refs/tags/') && github.event_name != 'pull_request' + id: release-files + run: | + FILES="${{ steps.version.outputs.package_name }}" + FILES="$FILES + SHA256SUMS.txt" + [ -f sbom.cdx.json ] && FILES="$FILES + sbom.cdx.json" + [ -f trivy-report.json ] && FILES="$FILES + trivy-report.json" + echo "files<> $GITHUB_OUTPUT + echo "$FILES" >> $GITHUB_OUTPUT + echo "RELEASE_EOF" >> $GITHUB_OUTPUT + + - name: Create GitHub Release (for tags only) + if: startsWith(github.ref, 'refs/tags/') && github.event_name != 'pull_request' + uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v1 + with: + files: ${{ steps.release-files.outputs.files }} + body: | + ## Container Image + + ```bash + docker pull ghcr.io/${{ github.repository }}:${{ steps.version.outputs.version }} + ``` + + **Digest:** `${{ steps.push.outputs.digest }}` + **Signed:** ${{ steps.sign.outputs.signed == 'true' && 'Yes' || 'No (signing failed, see workflow logs)' }} + + ## Verify + + ```bash + # Verify image signature + cosign verify \ + --certificate-identity-regexp="https://github.com/${{ github.repository }}/" \ + --certificate-oidc-issuer="https://token.actions.githubusercontent.com" \ + ghcr.io/${{ github.repository }}:${{ steps.version.outputs.version }} + + # Verify build provenance + gh attestation verify \ + oci://ghcr.io/${{ github.repository }}:${{ steps.version.outputs.version }} \ + --owner ${{ github.repository_owner }} + + # Verify artifact checksums + sha256sum -c SHA256SUMS.txt + ``` + + ## Artifacts + + | File | Description | + |------|-------------| + | `ui-deployment.zip` | Deployment manifests with release metadata | + | `sbom.cdx.json` | CycloneDX software bill of materials | + | `trivy-report.json` | Vulnerability scan results | + | `SHA256SUMS.txt` | SHA-256 checksums for all release artifacts | + draft: false + prerelease: ${{ contains(github.ref, 'alpha') || contains(github.ref, 'beta') || contains(github.ref, 'rc') }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Upload deployment package as artifact (for branch builds) + if: "!startsWith(github.ref, 'refs/tags/') && github.event_name != 'pull_request'" + continue-on-error: true + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: ${{ steps.version.outputs.package_name }} + path: ${{ steps.version.outputs.package_name }} + retention-days: 30 + + - name: Build summary + if: github.event_name != 'pull_request' + continue-on-error: true + run: | + echo "### Base Image Built Successfully!" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Version:** \`${{ steps.version.outputs.version }}\`" >> $GITHUB_STEP_SUMMARY + echo "**Digest:** \`${{ steps.push.outputs.digest }}\`" >> $GITHUB_STEP_SUMMARY + echo "**Signed:** ${{ steps.sign.outputs.signed == 'true' && 'Yes' || 'No' }}" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Container Image Tags:**" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + echo "${{ steps.meta.outputs.tags }}" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Pull command:**" >> $GITHUB_STEP_SUMMARY + echo '```bash' >> $GITHUB_STEP_SUMMARY + echo "docker pull ghcr.io/${{ github.repository }}@${{ steps.push.outputs.digest }}" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Verify signature:**" >> $GITHUB_STEP_SUMMARY + echo '```bash' >> $GITHUB_STEP_SUMMARY + echo 'cosign verify \' >> $GITHUB_STEP_SUMMARY + echo ' --certificate-identity-regexp="https://github.com/${{ github.repository }}/" \' >> $GITHUB_STEP_SUMMARY + echo ' --certificate-oidc-issuer="https://token.actions.githubusercontent.com" \' >> $GITHUB_STEP_SUMMARY + echo ' ghcr.io/${{ github.repository }}:${{ steps.version.outputs.version }}' >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..5fba4884 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,87 @@ +name: CI + +permissions: + contents: read + +on: + push: + branches: [main, deep-agent] + pull_request: + branches: [main, deep-agent] + +jobs: + lint: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + with: + persist-credentials: false + - uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4 + with: + node-version: "22" + cache: "npm" + - run: npm ci + - run: npm run lint + + unit-test: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + with: + persist-credentials: false + - uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4 + with: + node-version: "22" + cache: "npm" + - run: npm ci + - run: npm run test:coverage + + build: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + with: + persist-credentials: false + - uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4 + with: + node-version: "22" + cache: "npm" + - run: npm ci + - run: npm run build + - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: dist + path: dist/ + retention-days: 1 + + e2e: + runs-on: ubuntu-latest + timeout-minutes: 15 + needs: build + container: + image: mcr.microsoft.com/playwright:v1.61.1-noble + env: + CI: "true" + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + with: + persist-credentials: false + - uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4 + with: + node-version: "22" + cache: "npm" + - run: npm ci + - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 + with: + name: dist + path: dist/ + - run: npx playwright test + - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + if: always() + with: + name: playwright-report + path: playwright-report/ + retention-days: 7 diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml new file mode 100644 index 00000000..c83284ac --- /dev/null +++ b/.github/workflows/dependency-review.yml @@ -0,0 +1,27 @@ +name: Dependency Review + +on: + pull_request: + branches: [ main, deep-agent ] + +permissions: + contents: read + pull-requests: write + +jobs: + dependency-review: + name: Dependency Review + runs-on: ubuntu-latest + timeout-minutes: 5 + + steps: + - name: Checkout code + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + with: + persist-credentials: false + + - name: Dependency review + uses: actions/dependency-review-action@2031cfc080254a8a887f58cffee85186f0e49e48 # v4 + with: + fail-on-severity: high + comment-summary-in-pr: always diff --git a/.github/workflows/draft-check.yml b/.github/workflows/draft-check.yml new file mode 100644 index 00000000..6551acad --- /dev/null +++ b/.github/workflows/draft-check.yml @@ -0,0 +1,18 @@ +name: Draft Check + +on: + pull_request: + branches: [main, deep-agent] + types: [opened, synchronize, reopened, ready_for_review, converted_to_draft] + +jobs: + draft-check: + name: Not Draft + runs-on: ubuntu-latest + timeout-minutes: 1 + steps: + - name: Fail if draft + if: github.event.pull_request.draft == true + run: | + echo "::error::This PR is still in draft. Mark it as ready for review before merging." + exit 1 diff --git a/.github/workflows/license-scan.yml b/.github/workflows/license-scan.yml new file mode 100644 index 00000000..d5a2cf62 --- /dev/null +++ b/.github/workflows/license-scan.yml @@ -0,0 +1,37 @@ +name: License Compliance + +on: + push: + branches: [main, deep-agent] + paths: + - 'package.json' + - 'package-lock.json' + - 'Containerfile' + pull_request: + branches: [main, deep-agent] + paths: + - 'package.json' + - 'package-lock.json' + - 'Containerfile' + +permissions: + contents: read + +jobs: + license-scan: + name: License Scan + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Checkout code + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + + - name: Scan for license violations + uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0 + with: + scan-type: fs + scan-ref: . + scanners: license + severity: HIGH,CRITICAL + exit-code: '1' + trivy-config: trivy-license.yaml diff --git a/.github/workflows/pr-title.yml b/.github/workflows/pr-title.yml new file mode 100644 index 00000000..4600c4d6 --- /dev/null +++ b/.github/workflows/pr-title.yml @@ -0,0 +1,36 @@ +name: PR Title Lint + +on: + pull_request: + branches: [main, deep-agent] + types: [opened, edited, synchronize] + +permissions: + pull-requests: read + +jobs: + lint-title: + name: Conventional Commit Title + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: Validate PR title + run: | + TITLE="${{ github.event.pull_request.title }}" + PATTERN='^(feat|fix|docs|chore|refactor|test|ci|perf|build|style|revert)(\(.+\))?\!?: .+' + if echo "$TITLE" | grep -qE "$PATTERN"; then + echo "PR title is valid: $TITLE" + else + echo "::error::PR title does not follow Conventional Commits format." + echo "" + echo "Expected: (): " + echo "Got: $TITLE" + echo "" + echo "Valid types: feat, fix, docs, chore, refactor, test, ci, perf, build, style, revert" + echo "Examples:" + echo " feat: add streaming response component" + echo " fix(auth): correct token refresh logic" + echo " docs: update configuration section" + echo " feat!: redesign chat interface (breaking change)" + exit 1 + fi diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml new file mode 100644 index 00000000..32c54db8 --- /dev/null +++ b/.github/workflows/release-please.yml @@ -0,0 +1,17 @@ +name: Release Please + +on: + push: + branches: [ main ] + +permissions: + contents: write + pull-requests: write + +jobs: + release-please: + runs-on: ubuntu-latest + steps: + - uses: googleapis/release-please-action@5c625bfb5d1ff62eadeeb3772007f7f66fdcf071 # v4 + with: + release-type: node diff --git a/.github/workflows/require-issue.yml b/.github/workflows/require-issue.yml new file mode 100644 index 00000000..545307b9 --- /dev/null +++ b/.github/workflows/require-issue.yml @@ -0,0 +1,63 @@ +name: Require Linked Issue + +on: + pull_request: + branches: [main, deep-agent] + types: [opened, edited, synchronize, reopened, ready_for_review] + +permissions: + contents: read + +jobs: + check-issue: + name: Check Issue Link + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: Require linked issue + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7 + with: + script: | + const owner = context.repo.owner; + const repo = context.repo.repo; + const prNumber = context.payload.pull_request.number; + const prAuthor = context.payload.pull_request.user.login; + + const result = await github.graphql(` + query($owner: String!, $repo: String!, $pr: Int!) { + repository(owner: $owner, name: $repo) { + pullRequest(number: $pr) { + closingIssuesReferences(first: 10) { + nodes { + number + assignees(first: 10) { + nodes { login } + } + } + } + } + } + } + `, { owner, repo, pr: prNumber }); + + const issues = result.repository.pullRequest.closingIssuesReferences.nodes; + + if (issues.length === 0) { + core.setFailed( + 'No linked issue found. Use the Development sidebar to link an issue to this PR.' + ); + return; + } + + core.info(`Linked issues: ${issues.map(i => '#' + i.number).join(', ')}`); + + const authorAssigned = issues.some(issue => + issue.assignees.nodes.some(a => a.login === prAuthor) + ); + + if (!authorAssigned) { + const issueList = issues.map(i => '#' + i.number).join(', '); + core.setFailed( + `PR author @${prAuthor} is not assigned to any linked issue (${issueList}). Assign yourself to the issue first.` + ); + } diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml new file mode 100644 index 00000000..2345260a --- /dev/null +++ b/.github/workflows/scorecard.yml @@ -0,0 +1,37 @@ +name: OpenSSF Scorecard + +on: + push: + branches: [ main ] + schedule: + - cron: '0 6 * * 1' + +permissions: read-all + +jobs: + analysis: + name: Scorecard Analysis + runs-on: ubuntu-latest + timeout-minutes: 10 + + permissions: + security-events: write + id-token: write + + steps: + - name: Checkout code + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + with: + persist-credentials: false + + - name: Run Scorecard + uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2 + with: + results_file: results.sarif + results_format: sarif + publish_results: true + + - name: Upload Scorecard results to Security tab + uses: github/codeql-action/upload-sarif@08d09a53f0f5d694f253bd25732e4429c9e9337f # v3 + with: + sarif_file: results.sarif diff --git a/.gitignore b/.gitignore index 7ceb59f8..6de47f90 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,11 @@ dist-ssr *.sln *.sw? .env +ROADMAP.md + +# Playwright +playwright-report/ +test-results/ + +# Coverage +coverage/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 00000000..d73d1fa5 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,67 @@ +stages: + - lint + - test + - build + - e2e + +variables: + NODE_VERSION: "22" + +default: + image: node:22-slim + cache: + key: + files: + - package-lock.json + paths: + - node_modules/ + +.install: &install + before_script: + - npm ci + +lint: + stage: lint + <<: *install + script: + - npm run lint + +unit-test: + stage: test + <<: *install + script: + - npm run test:coverage + coverage: '/All files[^|]*\|[^|]*\s+([\d.]+)/' + artifacts: + when: always + reports: + coverage_report: + coverage_format: cobertura + path: coverage/cobertura-coverage.xml + +build: + stage: build + <<: *install + script: + - npm run build + artifacts: + paths: + - dist/ + expire_in: 1 hour + +e2e: + stage: e2e + image: mcr.microsoft.com/playwright:v1.61.1-noble + needs: + - build + variables: + CI: "true" + before_script: + - npm ci + script: + - npx playwright test + artifacts: + when: always + paths: + - playwright-report/ + expire_in: 7 days diff --git a/.trivyignore b/.trivyignore new file mode 100644 index 00000000..36b40187 --- /dev/null +++ b/.trivyignore @@ -0,0 +1,6 @@ +# tar 7.5.15 ships in the base image's system npm, not our app dependencies. +# Fix depends on base image update (registry.access.redhat.com/hi/nodejs). +CVE-2026-59873 + +# Go stdlib in OPA static binary — fix depends on OPA upstream rebuilding with Go 1.24.13+. +CVE-2025-68121 diff --git a/Containerfile b/Containerfile index deb0def0..e3632a7d 100644 --- a/Containerfile +++ b/Containerfile @@ -1,9 +1,25 @@ -FROM registry.access.redhat.com/ubi10/nodejs-24:10.1 +# Base image for UI - config loaded from volume mount +# +# Expected volume mount: +# - /app/config/ui/ (PVC) +# - Must contain: settings.yaml +# +# Build: podman build -f Containerfile.base -t ai-template-ui:v1.0.0 . +# Run: podman run -v /path/to/config:/app/config/ui:ro -e UI_CONFIG_PATH=/app/config/ui/settings.yaml ai-template-ui:v1.0.0 + +FROM registry.access.redhat.com/hi/nodejs:24.18.0-builder USER root +ARG OPA_VERSION=1.18.2 +RUN curl -fsSL "https://github.com/open-policy-agent/opa/releases/download/v${OPA_VERSION}/opa_linux_amd64_static" \ + -o /usr/local/bin/opa && chmod 755 /usr/local/bin/opa + WORKDIR /opt/app-root/src +# UI config is NOT baked in — mount config/ui at /opt/app-root/src/config +# (compose: ./config:/opt/app-root/src/config:ro; K8s: ConfigMap/PVC). + COPY --chown=1001:0 package*.json ./ COPY --chown=1001:0 src ./src COPY --chown=1001:0 public ./public @@ -14,10 +30,14 @@ COPY --chown=1001:0 vite-env.d.ts ./ COPY --chown=1001:0 tsconfig.json ./ COPY --chown=1001:0 tsconfig.node.json ./ +RUN mkdir -p config/ui && chown -R 1001:0 config && chown -R 1001:0 /opt/app-root/src + USER 1001 RUN npm ci && npm run build -EXPOSE 8080 +# Config will be mounted here at runtime from PVC +# Override UI_CONFIG_PATH in deployment.yaml if using a custom mount location +ENV UI_CONFIG_PATH=/app/config/ui/settings.yaml CMD ["node", "dist/server/index.js"] diff --git a/Makefile b/Makefile index c3af4e7c..23424a31 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,9 @@ -.PHONY: dev clean local deploy undeploy +.PHONY: dev clean local local-down container container-down deploy undeploy e2e opa-build + +# Detect npm/node path (supports nvm, system install, etc) +NPM := $(shell command -v npm 2>/dev/null || echo /Users/sodutta/.nvm/versions/node/v24.18.0/bin/npm) +NODE_DIR := $(dir $(NPM)) +export PATH := $(NODE_DIR):$(PATH) # OpenShift namespace (can be overridden: make deploy openshift NAMESPACE=my-project) NAMESPACE ?= $(shell oc project -q 2>/dev/null) @@ -19,21 +24,47 @@ install: else \ echo ".env file already exists, skipping copy"; \ fi - @npm ci + @$(NPM) ci + +opa-check: + @which opa > /dev/null || (echo "Error: opa CLI not found. Install from https://www.openpolicyagent.org/docs/latest/#running-opa" && exit 1) + opa check config/compliance/ + @echo "OPA policy syntax OK" clean: + @echo "Stopping containers and removing volumes..." + @export PODMAN_COMPOSE_SILENT=true && podman-compose down -v 2>/dev/null || true + @pkill -f "node.*dist/server" 2>/dev/null || true + @pkill -f "npm.*start" 2>/dev/null || true rm -rf node_modules dist +## Run Playwright e2e tests +e2e: + npm run build + npx playwright install chromium --with-deps 2>/dev/null || npx playwright install chromium + npx playwright test + dev: - npm run dev + $(NPM) run dev local: - npm run build - npm start + @which podman-compose > /dev/null || (echo "Error: podman-compose not found. Please install podman-compose." && exit 1) + @echo "Starting Redis..." + @export PODMAN_COMPOSE_SILENT=true && podman-compose up -d redis + @echo "Waiting for Redis..." + @COUNTER=0; until podman exec template-ui-redis redis-cli ping 2>/dev/null | grep -q PONG || [ $$COUNTER -gt 30 ]; do sleep 1; COUNTER=$$((COUNTER + 1)); done; \ + if [ $$COUNTER -gt 30 ]; then echo "Error: Redis did not become ready on localhost:6380"; exit 1; fi + $(NPM) run build + PORT=8080 $(NPM) start + +local-down: + @export PODMAN_COMPOSE_SILENT=true && podman-compose stop redis container: - export PODMAN_COMPOSE_SILENT=true - podman-compose --no-ansi up --build --force-recreate --remove-orphans --timeout=60 + @export PODMAN_COMPOSE_SILENT=true && podman-compose --no-ansi up --build --force-recreate --remove-orphans --timeout=60 + +container-down: + @export PODMAN_COMPOSE_SILENT=true && podman-compose down # Deployment targets deploy: diff --git a/README.md b/README.md index 2c1ee7f1..29375f06 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,88 @@ A modern React-based frontend application with Fastify backend for interacting w The application will be available at `http://localhost:5173` +## 🎨 Customizing Branding & Features + +Template UI supports runtime configuration for branding (logo, colors, title), feature flags, agent endpoint, and compliance policies — without code changes or rebuilds. + +**Full documentation:** [`docs/deployment-patterns.md`](docs/deployment-patterns.md) + +### Quick start + +Copy one of the ready-made example configs to get started: + +```bash +# Local development (auth off, debug panel open) +cp config/ui/examples/minimal.yaml config/ui/settings.yaml + +# Blue color scheme +cp config/ui/examples/blue-theme.yaml config/ui/settings.yaml + +# Red Hat branding +cp config/ui/examples/red-hat-branding.yaml config/ui/settings.yaml + +# Production-hardened (OPA enabled, strict security) +cp config/ui/examples/production.yaml config/ui/settings.yaml +``` + +### Configuration file + +`config/ui/settings.yaml` is optional — built-in defaults are used if it is absent. Key sections: + +```yaml +branding: + title: "My Custom Agent" + logo_url: "/custom-logo.svg" # Place file in public/ to serve it + favicon_url: "/custom-favicon.ico" + colors: + light: + primary: "#0066cc" + accent: "#a60000" + background: "#ffffff" + foreground: "#1a1a1a" + dark: + primary: "#4dabf7" + accent: "#f56e6e" + background: "#0a1628" + foreground: "#f0f4f8" + +features: + debug_mode_default: false # Default state of the debug panel toggle + auth_enabled: true # Set to false for local dev (no SSO credentials needed) + +agent: + endpoint: "" # Agent URL — empty falls back to AGENT_HOST env var + timeout_ms: 30000 + streaming: true +``` + +See [`config/ui/README.md`](config/ui/README.md) for the full schema and every environment variable override. + +### Environment variable overrides + +Env vars take precedence over the YAML file: + +```bash +BRANDING_TITLE="Production Agent" +BRANDING_LOGO_URL="/prod-logo.svg" +FEATURE_AUTH_ENABLED=true +AGENT_ENDPOINT=https://prod-agent.example.com +``` + +See `env.template` for the complete list. + +### Hot-reload + +Branding and `agent.endpoint` / `agent.timeout_ms` changes are applied without a server restart. Security settings (`rate_limit`, `session`, `helmet`) require a restart. The config watcher logs which category each change falls into. + +### Validation + +The config is validated at startup. Invalid values cause the server to exit with a clear error: + +```text +Config validation error: branding.colors.light.primary must be a valid hex color (got 'not-a-color') +``` + ## 📦 Available Scripts | Command | Description | @@ -106,21 +188,55 @@ template-ui/ ├── src/ │ ├── frontend/ # React frontend application │ │ ├── components/ # React components +│ │ ├── contexts/ # React context providers │ │ ├── hooks/ # Custom React hooks -│ │ ├── lib/ # Utility libraries -│ │ └── App.tsx # Main application component -│ └── server/ # Fastify backend server -│ ├── plugins/ # Fastify plugins (auth, etc.) -│ ├── router/ # API routes -│ └── server.ts # Server configuration -├── public/ # Static assets -├── dist/ # Built files (generated) -├── package.json # Dependencies and scripts -├── vite.config.ts # Vite configuration -├── tsconfig.json # TypeScript configuration -└── env.template # Environment variables template +│ │ ├── pages/ # Route-level page components +│ │ ├── redux/ # Redux slices and store +│ │ ├── services/ # API client services +│ │ ├── types/ # TypeScript type definitions +│ │ ├── utils/ # Shared utilities +│ │ └── App.tsx # Main application component +│ └── server/ # Fastify backend server +│ ├── plugins/ # Fastify plugins (auth, OPA, tracing) +│ ├── router/ # Route handlers (API, proxy, client) +│ ├── utils/ # Settings loader, OPA engine, Redis, config watcher +│ └── server.ts # Server factory + config watcher setup +├── config/ +│ ├── ui/ +│ │ ├── settings.yaml # Runtime config (optional — defaults used if absent) +│ │ ├── README.md # Full schema + env-var reference +│ │ └── examples/ # Ready-to-use example configs +│ └── compliance/ +│ ├── policy.rego # OPA deny rules (package compliance.ui) +│ ├── data.json # Static external data for OPA policies +│ └── README.md # OPA policy authoring guide +├── deployment/ +│ ├── openshift/ # OpenShift BuildConfig, ImageStream, Route, ConfigMap, Secret +│ └── overlays/ +│ ├── kind/ # Kind cluster overlay (NodePort, no TLS) +│ └── openshift/ # OpenShift overlay +├── docs/ +│ └── deployment-patterns.md # Branding, feature flags, runtime config, agent endpoint, OPA +├── public/ # Static assets served directly +├── e2e/ # Playwright end-to-end tests +├── dist/ # Built files (generated) +├── Containerfile # Podman/Docker build definition (UBI base) +├── compose.yml # Local development compose (Redis) +├── Makefile # Task shortcuts (dev, local, clean) +├── package.json # Dependencies and scripts +├── vite.config.ts # Vite configuration +├── tsconfig.json # TypeScript configuration +└── env.template # Environment variables template ``` +## 📚 Deployment Docs + +| Document | What it covers | +|---|---| +| [`docs/deployment-patterns.md`](docs/deployment-patterns.md) | Branding customization, feature flag reference, runtime config examples, agent endpoint switching, OPA policy examples | +| [`config/ui/README.md`](config/ui/README.md) | Full YAML schema, every env-var override, hot-reload reference | +| [`config/compliance/README.md`](config/compliance/README.md) | OPA policy authoring, built-in rules, testing with `opa eval` | + ## 🔧 Technology Stack ### Frontend diff --git a/compose.yml b/compose.yml index 7cd52d29..d9667924 100644 --- a/compose.yml +++ b/compose.yml @@ -1,6 +1,23 @@ version: '3.8' services: + redis: + image: redis:7-alpine + container_name: template-ui-redis + command: redis-server --appendonly yes + ports: + - "6380:6379" + volumes: + - redis_data:/data + restart: unless-stopped + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 3s + retries: 5 + networks: + - template-ui-network + template-ui: build: context: . @@ -8,13 +25,33 @@ services: container_name: template-ui-app ports: - "8080:8080" - environment: - - NODE_ENV=production env_file: - .env + environment: + - NODE_ENV=production + - PORT=8080 + - UI_CONFIG_PATH=/opt/app-root/src/config/ui/settings.yaml + - REDIS_HOST=redis + - REDIS_PORT=6379 + - AGENT_HOST=http://host.containers.internal:5002 + - CORS_ORIGIN=http://localhost:8080 + - SSO_CALLBACK_URL=http://localhost:8080/auth/callback/oidc + volumes: + - ./config:/opt/app-root/src/config:ro restart: unless-stopped + depends_on: + redis: + condition: service_healthy + healthcheck: + test: ["CMD", "curl", "-f", "http://127.0.0.1:8080/_health"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s networks: - template-ui-network + extra_hosts: + - "host.containers.internal:host-gateway" networks: @@ -22,4 +59,5 @@ networks: driver: bridge volumes: + redis_data: node_modules: diff --git a/config/compliance/README.md b/config/compliance/README.md new file mode 100644 index 00000000..c3837caa --- /dev/null +++ b/config/compliance/README.md @@ -0,0 +1,185 @@ +# OPA Compliance Policies + +This directory contains Open Policy Agent (OPA) policies that are evaluated at server startup (and on every hot-reload) to validate the runtime configuration. + +OPA is **optional and disabled by default**. Enable it in `config/ui/settings.yaml`: + +```yaml +platform: + opa: + enabled: true + policy_path: "config/compliance" # path to this directory + fail_on_violation: false # set to true to block startup on violations +``` + +--- + +## How it works + +1. At startup the Fastify server registers `opa.plugin.ts`. +2. The plugin compiles all `.rego` files in `policy_path` into a WASM bundle using the `opa` CLI. +3. The current `UISettings` object is passed as `input` to `compliance/ui/deny`. +4. Any non-empty `deny` set produces `PolicyViolation` messages that are logged. +5. If `fail_on_violation: true` and violations exist, the server throws and exits. +6. The policy directory is watched — changing a `.rego` or `.json` file triggers an automatic recompile (500 ms debounce). + +### Prerequisite + +The `opa` binary must be on `$PATH`: + +```bash +# macOS +brew install opa + +# Linux (x86_64) +curl -sLo /usr/local/bin/opa https://openpolicyagent.org/downloads/latest/opa_linux_amd64_static +chmod +x /usr/local/bin/opa + +# Verify +opa version +``` + +--- + +## Built-in rules (`policy.rego`) + +The default policy ships with six deny rules. Each rule corresponds to a `platform.opa.*` setting: + +| Rule | Triggered when | Controlled by | +|---|---|---| +| **auth_provider** | `auth_enabled=true` and `SSO_AUTH_PROVIDER` not in `approved_auth_providers` | `platform.opa.approved_auth_providers` | +| **agent_endpoint** | `agent.endpoint` does not end with any approved suffix | `platform.opa.internal_endpoint_suffixes` | +| **session_ttl** | `security.session.max_age_days` exceeds the configured maximum | `platform.opa.max_session_ttl_days` | +| **debug_mode** | `features.debug_mode_default=true` and `restrict_debug_mode=true` | `platform.opa.restrict_debug_mode` | +| **restricted_features** | A feature key listed in `restricted_features` is `true` | `platform.opa.restricted_features` | +| **rate_limit** | `security.rate_limit.max` exceeds the policy cap | `platform.opa.max_rate_limit` | + +### Example: enforce internal-only agent endpoints + +```yaml +# settings.yaml +platform: + opa: + enabled: true + fail_on_violation: true + internal_endpoint_suffixes: + - ".svc.cluster.local" + - ".internal.example.com" +``` + +If `agent.endpoint` is `https://public-api.example.com/agent`, the server logs: + +```text +OPA policy violation: agent endpoint 'https://public-api.example.com/agent' + does not match any approved internal suffix [".svc.cluster.local", ".internal.example.com"] +``` + +### Example: cap session lifetime and restrict debug mode + +```yaml +platform: + opa: + enabled: true + fail_on_violation: true + max_session_ttl_days: 7 + restrict_debug_mode: true +``` + +Attempting to deploy `security.session.max_age_days: 30` or `features.debug_mode_default: true` will block startup. + +### Example: restrict to approved auth providers + +```yaml +platform: + opa: + enabled: true + approved_auth_providers: + - "oidc" +``` + +Set `SSO_AUTH_PROVIDER=saml` in env and the server will log a violation (SAML is not in the approved list). + +--- + +## `data.json` + +`data.json` provides static external data that OPA policies can reference via `data.*`. It is currently a placeholder. You can extend it with environment-specific constants: + +```json +{ + "approved_auth_providers": ["oidc"], + "internal_endpoint_suffixes": [".svc.cluster.local"], + "max_session_ttl_days": 7 +} +``` + +Policies can then reference `data.approved_auth_providers` instead of hard-coding values in `settings.yaml`. + +--- + +## Writing custom rules + +All custom rules must live in `package compliance.ui` and produce string messages via `deny contains msg if { ... }`. + +**Template:** + +```rego +package compliance.ui + +import rego.v1 + +# Deny startup if a custom condition is violated. +deny contains msg if { + # Access the full UISettings object via `input` + input.features.some_flag + not condition_is_satisfied + msg := "explanation of the violation" +} +``` + +**Available `input` fields:** + +```text +input.branding — BrandingConfig +input.features — FeaturesConfig +input.agent — AgentConfig (endpoint, timeout_ms, streaming) +input.security — SecurityConfig (rate_limit, session, helmet) +input.auth_provider — string (value of SSO_AUTH_PROVIDER env var, default "oidc") +input.platform.opa — PlatformOpaConfig +``` + +**Test your policy locally before deploying:** + +```bash +# Evaluate the deny rule against a sample input +opa eval \ + --data config/compliance \ + --input <(echo '{ + "features": {"auth_enabled": true, "debug_mode_default": false}, + "agent": {"endpoint": "https://public.example.com"}, + "security": {"session": {"max_age_days": 30}, "rate_limit": {"max": 100}}, + "auth_provider": "oidc", + "platform": {"opa": { + "approved_auth_providers": ["oidc"], + "internal_endpoint_suffixes": [".svc.cluster.local"], + "max_session_ttl_days": 7, + "restrict_debug_mode": true, + "restricted_features": [], + "max_rate_limit": 500 + }} + }') \ + "data.compliance.ui.deny" +``` + +--- + +## File structure + +```text +config/compliance/ +├── policy.rego # Deny rules (package compliance.ui) +├── data.json # Static external data available as data.* in Rego +└── README.md # This file +``` + +Additional `.rego` files placed here are automatically compiled into the same bundle. diff --git a/config/compliance/data.json b/config/compliance/data.json new file mode 100644 index 00000000..842a22d1 --- /dev/null +++ b/config/compliance/data.json @@ -0,0 +1,8 @@ +{ + "approved_auth_providers": [], + "internal_endpoint_suffixes": [], + "max_session_ttl_days": 0, + "restrict_debug_mode": false, + "restricted_features": [], + "max_rate_limit": 0 +} diff --git a/config/compliance/policy.rego b/config/compliance/policy.rego new file mode 100644 index 00000000..d8d2972a --- /dev/null +++ b/config/compliance/policy.rego @@ -0,0 +1,59 @@ +package compliance.ui + +import rego.v1 + +default allow := true + +deny contains msg if { + input.features.auth_enabled + input.platform.opa.approved_auth_providers + count(input.platform.opa.approved_auth_providers) > 0 + not input.auth_provider in input.platform.opa.approved_auth_providers + msg := sprintf("auth provider '%s' is not in the approved list %v", [input.auth_provider, input.platform.opa.approved_auth_providers]) +} + +deny contains msg if { + input.agent.endpoint != "" + suffixes := input.platform.opa.internal_endpoint_suffixes + count(suffixes) > 0 + not endpoint_allowed(input.agent.endpoint, suffixes) + msg := sprintf("agent endpoint '%s' does not match any approved internal suffix %v", [input.agent.endpoint, suffixes]) +} + +deny contains msg if { + max_days := input.platform.opa.max_session_ttl_days + max_days > 0 + input.security.session.max_age_days > max_days + msg := sprintf("session TTL %d days exceeds maximum allowed %d days", [input.security.session.max_age_days, max_days]) +} + +deny contains msg if { + input.platform.opa.restrict_debug_mode + input.features.debug_mode_default + msg := "debug_mode_default cannot be enabled in this environment" +} + +deny contains msg if { + restricted := input.platform.opa.restricted_features + some feature in restricted + input.features[feature] + msg := sprintf("feature '%s' is restricted by policy", [feature]) +} + +deny contains msg if { + max_rate := input.platform.opa.max_rate_limit + max_rate > 0 + input.security.rate_limit.max > max_rate + msg := sprintf("rate limit %d exceeds policy maximum %d", [input.security.rate_limit.max, max_rate]) +} + +endpoint_allowed(endpoint, suffixes) if { + some suffix in suffixes + endswith(endpoint, suffix) +} + +allow := false if { + count(deny) > 0 +} + +violations := deny diff --git a/config/ui/README.md b/config/ui/README.md new file mode 100644 index 00000000..9ccbc519 --- /dev/null +++ b/config/ui/README.md @@ -0,0 +1,252 @@ +# Template UI Configuration Reference + +Drop a `settings.yaml` file in this directory to configure branding, features, security, and agent settings without code changes. The file is optional — if absent, built-in defaults are used. + +For worked examples and deployment patterns see [`docs/deployment-patterns.md`](../../docs/deployment-patterns.md). + +--- + +## Config loading order + +Settings are resolved in this priority order (highest wins): + +``` +Environment variables > settings.yaml > Built-in defaults +``` + +The config file path can be overridden with `UI_CONFIG_PATH=`. + +--- + +## Full schema reference + +### `branding` + +Controls the visual identity of the application. Changes apply **without a server restart** (hot-reloaded by the config watcher). + +```yaml +branding: + title: "Deep Agent" # Browser tab title and masthead heading + logo_url: "/dist/frontend/redhat-logo.svg" # Path or URL served by the server + favicon_url: "/dist/frontend/redhat-logo.svg" + colors: + light: + primary: "#0066cc" # Primary action color (buttons, links) + accent: "#a60000" # Accent / highlight color + background: "#ffffff" # Page background + foreground: "#1a1a1a" # Default text color + dark: + primary: "#4dabf7" + accent: "#f56e6e" + background: "#0a1628" + foreground: "#f0f4f8" +``` + +**Color values must be 6-digit hex** (`#rrggbb`). The server validates colors at startup and fails with a clear error on malformed values. + +**Environment variable overrides:** + +| Env var | YAML key | +|---|---| +| `BRANDING_TITLE` | `branding.title` | +| `BRANDING_LOGO_URL` | `branding.logo_url` | +| `BRANDING_FAVICON_URL` | `branding.favicon_url` | +| `BRANDING_PRIMARY_LIGHT` | `branding.colors.light.primary` | +| `BRANDING_ACCENT_LIGHT` | `branding.colors.light.accent` | +| `BRANDING_BG_LIGHT` | `branding.colors.light.background` | +| `BRANDING_FG_LIGHT` | `branding.colors.light.foreground` | +| `BRANDING_PRIMARY_DARK` | `branding.colors.dark.primary` | +| `BRANDING_ACCENT_DARK` | `branding.colors.dark.accent` | +| `BRANDING_BG_DARK` | `branding.colors.dark.background` | +| `BRANDING_FG_DARK` | `branding.colors.dark.foreground` | + +--- + +### `features` + +Feature flags that control application behaviour. Some are user-toggleable at runtime; some are enforced by OPA policy. + +```yaml +features: + auth_enabled: true # Enable SSO/OAuth2 authentication + debug_mode_default: false # Default state of the debug panel toggle +``` + +**Environment variable overrides:** + +| Env var | YAML key | Notes | +|---|---|---| +| `FEATURE_AUTH_ENABLED` | `features.auth_enabled` | Preferred | +| `AUTH_ENABLED` | `features.auth_enabled` | Legacy alias (still supported) | +| `FEATURE_DEBUG_MODE_DEFAULT` | `features.debug_mode_default` | | + +When `auth_enabled: false`, a dummy user (`developer@redhat.com`) is injected automatically. No SSO credentials are required. + +--- + +### `agent` + +Controls how the BFF proxy reaches the agent engine. `agent.endpoint` and `agent.timeout_ms` apply **without a server restart**. + +```yaml +agent: + endpoint: "" # Full URL of the agent. Empty = fall back to AGENT_HOST env var + timeout_ms: 30000 # Request timeout in milliseconds (applies to all proxy calls) + streaming: true # Enable SSE streaming (recommended) +``` + +**Agent host resolution order (from `proxy.router.ts`):** + +``` +agent.endpoint → AGENT_HOST env var → http://localhost:5002 +``` + +**Environment variable overrides:** + +| Env var | YAML key | +|---|---| +| `AGENT_ENDPOINT` | `agent.endpoint` | +| `AGENT_TIMEOUT_MS` | `agent.timeout_ms` | + +--- + +### `security` + +Security hardening. Changes to `rate_limit`, `session`, and `helmet` require a **server restart** to take effect. + +```yaml +security: + rate_limit: + enabled: true + max: 100 # Max requests per window per IP + window: "1 minute" # Time window (e.g. "30 seconds", "5 minutes") + exclude_paths: + - "/api/health" + - "/_health" + + session: + secure_cookie: false # Set to true in production (HTTPS only) + max_age_days: 30 + http_only: true + same_site: "lax" # "strict" | "lax" | "none" + + helmet: + enabled: true + cross_origin_embedder_policy: false +``` + +**Environment variable overrides:** + +| Env var | YAML key | +|---|---| +| `RATE_LIMIT_MAX` / `SECURITY_RATE_LIMIT_MAX` | `security.rate_limit.max` | +| `SECURITY_RATE_LIMIT_WINDOW` | `security.rate_limit.window` | +| `SESSION_SECURE_COOKIE` / `SECURITY_SESSION_SECURE_COOKIE` | `security.session.secure_cookie` | +| `SESSION_HTTP_ONLY` | `security.session.http_only` | +| `SESSION_SAME_SITE` | `security.session.same_site` | +| `SECURITY_SESSION_MAX_AGE_DAYS` | `security.session.max_age_days` | +| `CSP_SCRIPT_SRC` | `security.helmet.csp.script_src` (space-separated) | +| `CSP_CONNECT_SRC` | `security.helmet.csp.connect_src` (space-separated) | + +--- + +### `platform` + +Platform-level controls. Currently only OPA policy enforcement is configured here. + +```yaml +platform: + opa: + enabled: false + policy_path: "" # Path to .rego directory (default: config/compliance/) + fail_on_violation: false # true = block startup on policy violations + approved_auth_providers: [] # e.g. ["oidc"] — empty = allow all + internal_endpoint_suffixes: [] # e.g. [".svc.cluster.local"] + max_session_ttl_days: 0 # 0 = no limit + restrict_debug_mode: false # true = deny debug_mode_default=true + restricted_features: [] # Feature flag keys that must be false + max_rate_limit: 0 # 0 = no cap +``` + +**Environment variable overrides:** + +| Env var | YAML key | +|---|---| +| `PLATFORM_OPA_ENABLED` | `platform.opa.enabled` | +| `PLATFORM_OPA_POLICY_PATH` | `platform.opa.policy_path` | +| `PLATFORM_OPA_FAIL_ON_VIOLATION` | `platform.opa.fail_on_violation` | + +See [`config/compliance/README.md`](../compliance/README.md) for full OPA documentation. + +--- + +### `server` + +```yaml +server: + host: "0.0.0.0" + port: 8080 + body_limit: 1048576 # Max request body size in bytes (1 MB) +``` + +**Environment variable overrides:** + +| Env var | YAML key | +|---|---| +| `PORT` | `server.port` | + +--- + +### `logging` + +```yaml +logging: + level: "info" # "debug" | "info" | "warn" | "error" +``` + +**Environment variable overrides:** + +| Env var | YAML key | +|---|---| +| `LOG_LEVEL` | `logging.level` | + +--- + +### `cors` + +```yaml +cors: + origin: "http://localhost:5173" # Allowed CORS origin for the Vite dev server +``` + +Set `CORS_ORIGIN` environment variable to override at runtime. + +--- + +## Hot-reload behaviour + +The server watches the config file for changes. Not all settings can be applied without a restart: + +| Setting area | Hot-reloaded? | +|---|---| +| `branding.*` | Yes | +| `agent.endpoint`, `agent.timeout_ms` | Yes | +| `security.rate_limit.*` | No — restart required | +| `security.session.*` | No — restart required | +| `security.helmet.enabled` | No — restart required | +| `platform.opa.*` | No — restart required | + +Changes that require a restart are logged as warnings by the config watcher so you know what to expect. + +--- + +## Example configs + +See the `examples/` directory for ready-to-use configurations: + +| File | Use case | +|---|---| +| `examples/minimal.yaml` | Local development with auth disabled | +| `examples/blue-theme.yaml` | Blue colour scheme, all other defaults | +| `examples/red-hat-branding.yaml` | Red Hat colour palette and logo | +| `examples/production.yaml` | Production-hardened with OPA enabled | diff --git a/config/ui/examples/blue-theme.yaml b/config/ui/examples/blue-theme.yaml new file mode 100644 index 00000000..a3dd6ac1 --- /dev/null +++ b/config/ui/examples/blue-theme.yaml @@ -0,0 +1,19 @@ +# Blue theme branding example +# Copy to config/ui/settings.yaml (or point UI_CONFIG_PATH at this file) to apply. +# All other settings inherit from built-in defaults. + +branding: + title: "My Agent" + logo_url: "/dist/frontend/redhat-logo.svg" + favicon_url: "/dist/frontend/redhat-logo.svg" + colors: + light: + primary: "#0066cc" # IBM Blue 60 + accent: "#0043ce" # IBM Blue 70 + background: "#f4f7fb" + foreground: "#161616" + dark: + primary: "#4589ff" # IBM Blue 50 + accent: "#82cfff" # IBM Blue 30 + background: "#001141" + foreground: "#f4f4f4" diff --git a/config/ui/examples/minimal.yaml b/config/ui/examples/minimal.yaml new file mode 100644 index 00000000..6a6df76e --- /dev/null +++ b/config/ui/examples/minimal.yaml @@ -0,0 +1,29 @@ +# Minimal configuration — auth disabled, suitable for local development. +# Copy to config/ui/settings.yaml and ensure FEATURE_AUTH_ENABLED / AUTH_ENABLED is not set (or set to false). +# +# With this config: +# - No SSO credentials are required. +# - The dummy user developer@redhat.com is injected automatically. +# - The server still validates all other config fields on startup. + +branding: + title: "Local Dev Agent" + +features: + auth_enabled: false # Bypasses SSO; uses dummy user developer@redhat.com + debug_mode_default: true # Opens the debug panel by default (useful during development) + +agent: + endpoint: "" # Falls back to AGENT_HOST env var (default: http://localhost:5002) + timeout_ms: 60000 # Longer timeout while iterating on agent responses locally + streaming: true + +security: + rate_limit: + enabled: false # Disable rate-limiting during development + session: + secure_cookie: false # HTTP is fine for localhost + max_age_days: 7 + +logging: + level: "debug" diff --git a/config/ui/examples/production.yaml b/config/ui/examples/production.yaml new file mode 100644 index 00000000..43027499 --- /dev/null +++ b/config/ui/examples/production.yaml @@ -0,0 +1,83 @@ +# Production-hardened configuration example. +# Copy to config/ui/settings.yaml and supply secrets via environment variables. +# +# Required environment variables (never put these in YAML): +# COOKIE_SIGN — random string >= 32 chars +# SSO_CLIENT_ID — OIDC client ID +# SSO_CLIENT_SECRET — OIDC client secret +# SSO_ISSUER_HOST — e.g. https://sso.example.com/realms/myrealm +# SSO_CALLBACK_URL — e.g. https://myagent.example.com/auth/callback/oidc +# AGENT_HOST — e.g. http://my-agent-engine:8082 +# +# Optional environment overrides (can also be set here or via ConfigMap): +# BRANDING_TITLE — override the agent title without editing this file +# AGENT_ENDPOINT — override the agent URL at runtime (no restart needed) + +branding: + title: "Production Agent" + logo_url: "/dist/frontend/redhat-logo.svg" + favicon_url: "/dist/frontend/redhat-logo.svg" + colors: + light: + primary: "#0066cc" + accent: "#a60000" + background: "#ffffff" + foreground: "#1a1a1a" + dark: + primary: "#4dabf7" + accent: "#f56e6e" + background: "#0a1628" + foreground: "#f0f4f8" + +features: + auth_enabled: true # SSO enabled; credentials injected via env vars by agent-engine + debug_mode_default: false # Never expose debug panel to end-users by default + +agent: + endpoint: "" # Set via AGENT_HOST or AGENT_ENDPOINT env var + timeout_ms: 30000 + streaming: true + +# OPA policy enforcement — blocks startup if any rule is violated +platform: + opa: + enabled: true + policy_path: "config/compliance" # Relative to project root + fail_on_violation: true # Prevent startup on policy violations + approved_auth_providers: + - "oidc" # Only OIDC allowed in production + internal_endpoint_suffixes: + - ".svc.cluster.local" # Agent must be an in-cluster service + - ".internal.example.com" + max_session_ttl_days: 7 # Enforce short session lifetime + restrict_debug_mode: true # debug_mode_default must be false + restricted_features: [] + max_rate_limit: 500 # Cap rate limit to 500 req/min maximum + +security: + rate_limit: + enabled: true + max: 200 + window: "1 minute" + exclude_paths: + - "/api/health" + - "/_health" + session: + secure_cookie: true # HTTPS only — required in production + max_age_days: 7 + http_only: true + same_site: "lax" # lax allows OAuth2 callback flows + helmet: + enabled: true + cross_origin_embedder_policy: false + +server: + host: "0.0.0.0" + port: 8080 + body_limit: 1048576 # 1 MB + +logging: + level: "warn" # Reduce log volume in production + +cors: + origin: "https://myagent.example.com" # Lock down to the production domain diff --git a/config/ui/examples/red-hat-branding.yaml b/config/ui/examples/red-hat-branding.yaml new file mode 100644 index 00000000..5a2470ba --- /dev/null +++ b/config/ui/examples/red-hat-branding.yaml @@ -0,0 +1,22 @@ +# Red Hat branding example. +# Applies the Red Hat color palette and logo to both light and dark themes. +# All other settings inherit from built-in defaults. +# +# To use a custom logo, place the file in public/ and reference it as "/my-logo.svg". +# The path is served directly by the Fastify static file handler. + +branding: + title: "Red Hat AI Agent" + logo_url: "/dist/frontend/redhat-logo.svg" + favicon_url: "/dist/frontend/redhat-logo.svg" + colors: + light: + primary: "#ee0000" # Red Hat Red + accent: "#a60000" # Red Hat Dark Red + background: "#ffffff" + foreground: "#151515" + dark: + primary: "#ff6666" # Red Hat Red (lightened for dark bg) + accent: "#f56e6e" + background: "#0a0a0a" + foreground: "#f0f4f8" diff --git a/config/ui/settings.yaml b/config/ui/settings.yaml new file mode 100644 index 00000000..182a5f79 --- /dev/null +++ b/config/ui/settings.yaml @@ -0,0 +1,62 @@ +branding: + title: "Deep Agent" + logo_url: "/dist/frontend/redhat-logo.svg" + favicon_url: "/dist/frontend/redhat-logo.svg" + colors: + light: + primary: "#0066cc" + accent: "#a60000" + background: "#ffffff" + foreground: "#1a1a1a" + dark: + primary: "#4dabf7" + accent: "#f56e6e" + background: "#0a1628" + foreground: "#f0f4f8" + +features: + debug_mode_default: false + # auth_enabled: Controlled via AUTH_ENABLED environment variable (set by agent-engine during deployment) + +agent: + endpoint: "" + timeout_ms: 30000 + streaming: true + +platform: + opa: + enabled: false + policy_path: "" # Path to .rego policy directory (default: config/compliance/) + fail_on_violation: false # Block startup if policy violations are found + approved_auth_providers: [] # e.g. ["oidc", "saml"] — empty = allow all + internal_endpoint_suffixes: [] # e.g. [".internal.example.com", ".svc.cluster.local"] + max_session_ttl_days: 0 # 0 = no limit + restrict_debug_mode: false # Deny debug_mode_default = true + restricted_features: [] # Feature flag keys that must be false + max_rate_limit: 0 # 0 = no cap on rate_limit.max + +security: + rate_limit: + enabled: true + max: 100 + window: "1 minute" + exclude_paths: + - "/api/health" + - "/_health" + session: + secure_cookie: false + max_age_days: 30 + helmet: + enabled: true + cross_origin_embedder_policy: false + +server: + host: "0.0.0.0" + port: 8080 + body_limit: 1048576 + +logging: + level: "info" + +cors: + origin: "http://localhost:5173" diff --git a/deployment/base/configmap.yaml b/deployment/base/configmap.yaml new file mode 100644 index 00000000..6d2a8d58 --- /dev/null +++ b/deployment/base/configmap.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: template-ui-config + labels: + app: template-ui + component: ui +data: + ENVIRONMENT: "production" + AUTH_ENABLED: "false" + SSO_ISSUER_HOST: "" + SSO_CALLBACK_URL: "" + AGENT_HOST: "http://template-agent:8081" + diff --git a/deployment/base/deployment.yaml b/deployment/base/deployment.yaml new file mode 100644 index 00000000..4410f770 --- /dev/null +++ b/deployment/base/deployment.yaml @@ -0,0 +1,103 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: template-ui + labels: + app: template-ui + component: ui +spec: + replicas: 1 + selector: + matchLabels: + app: template-ui + component: ui + template: + metadata: + labels: + app: template-ui + component: ui + spec: + containers: + - name: template-ui + image: template-ui:latest + imagePullPolicy: Always + ports: + - containerPort: 8080 + name: http + protocol: TCP + env: + - name: PORT + value: "8080" + - name: ENVIRONMENT + valueFrom: + configMapKeyRef: + name: template-ui-config + key: ENVIRONMENT + - name: AUTH_ENABLED + valueFrom: + configMapKeyRef: + name: template-ui-config + key: AUTH_ENABLED + - name: SSO_ISSUER_HOST + valueFrom: + configMapKeyRef: + name: template-ui-config + key: SSO_ISSUER_HOST + optional: true + - name: SSO_CALLBACK_URL + valueFrom: + configMapKeyRef: + name: template-ui-config + key: SSO_CALLBACK_URL + optional: true + - name: AGENT_HOST + valueFrom: + configMapKeyRef: + name: template-ui-config + key: AGENT_HOST + - name: COOKIE_SIGN + valueFrom: + secretKeyRef: + name: template-ui-secrets + key: COOKIE_SIGN + - name: SSO_CLIENT_ID + valueFrom: + secretKeyRef: + name: template-ui-secrets + key: SSO_CLIENT_ID + optional: true + - name: SSO_CLIENT_SECRET + valueFrom: + secretKeyRef: + name: template-ui-secrets + key: SSO_CLIENT_SECRET + optional: true + envFrom: + - secretRef: + name: template-ui-secrets + optional: true + livenessProbe: + httpGet: + path: /api/health + port: 8080 + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /api/health + port: 8080 + initialDelaySeconds: 10 + periodSeconds: 5 + timeoutSeconds: 3 + failureThreshold: 3 + resources: + requests: + memory: "256Mi" + cpu: "100m" + limits: + memory: "512Mi" + cpu: "500m" + restartPolicy: Always + diff --git a/deployment/base/kustomization.yaml b/deployment/base/kustomization.yaml new file mode 100644 index 00000000..6ecb7964 --- /dev/null +++ b/deployment/base/kustomization.yaml @@ -0,0 +1,13 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: + - deployment.yaml + - service.yaml + - configmap.yaml + - secret.yaml + +labels: + - pairs: + app: template-ui + component: ui diff --git a/deployment/base/secret.yaml b/deployment/base/secret.yaml new file mode 100644 index 00000000..d883ed11 --- /dev/null +++ b/deployment/base/secret.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Secret +metadata: + name: template-ui-secrets + labels: + app: template-ui + component: ui +type: Opaque +stringData: + COOKIE_SIGN: "" + SSO_CLIENT_ID: "" + SSO_CLIENT_SECRET: "" + diff --git a/deployment/base/service.yaml b/deployment/base/service.yaml new file mode 100644 index 00000000..50820af4 --- /dev/null +++ b/deployment/base/service.yaml @@ -0,0 +1,18 @@ +apiVersion: v1 +kind: Service +metadata: + name: template-ui + labels: + app: template-ui + component: ui +spec: + type: ClusterIP + ports: + - port: 8080 + targetPort: 8080 + protocol: TCP + name: http + selector: + app: template-ui + component: ui + diff --git a/deployment/openshift/deployment.yaml b/deployment/openshift/deployment.yaml index 920ab9b5..4410f770 100644 --- a/deployment/openshift/deployment.yaml +++ b/deployment/openshift/deployment.yaml @@ -78,7 +78,7 @@ spec: optional: true livenessProbe: httpGet: - path: /health + path: /api/health port: 8080 initialDelaySeconds: 30 periodSeconds: 10 @@ -86,7 +86,7 @@ spec: failureThreshold: 3 readinessProbe: httpGet: - path: /health + path: /api/health port: 8080 initialDelaySeconds: 10 periodSeconds: 5 diff --git a/deployment/overlays/kind/ingress.yaml b/deployment/overlays/kind/ingress.yaml new file mode 100644 index 00000000..d9b45834 --- /dev/null +++ b/deployment/overlays/kind/ingress.yaml @@ -0,0 +1,51 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: template-ui + annotations: + nginx.ingress.kubernetes.io/auth-url: http://ai-gateway.agentforge.svc.cluster.local:8080/auth/check + nginx.ingress.kubernetes.io/auth-signin: http://localhost:8080/auth/login?redirect= + nginx.ingress.kubernetes.io/auth-response-headers: X-Auth-User-Email,X-Auth-User-Name,X-Auth-User-Sub,X-Auth-Access-Token + labels: + app: template-ui + component: ui +spec: + ingressClassName: nginx + rules: + - host: "{name}-{org}.localhost" # Runtime substitution + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: template-ui + port: + number: 8080 +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: template-ui-path + annotations: + nginx.ingress.kubernetes.io/use-regex: "true" + nginx.ingress.kubernetes.io/rewrite-target: /$2 + nginx.ingress.kubernetes.io/auth-url: http://ai-gateway.agentforge.svc.cluster.local:8080/auth/check + nginx.ingress.kubernetes.io/auth-signin: http://localhost:8080/auth/login?redirect= + nginx.ingress.kubernetes.io/auth-response-headers: X-Auth-User-Email,X-Auth-User-Name,X-Auth-User-Sub,X-Auth-Access-Token + labels: + app: template-ui + component: ui +spec: + ingressClassName: nginx + rules: + - host: localhost + http: + paths: + - path: /{org}/{name}(/|$)(.*) # Runtime substitution + pathType: ImplementationSpecific + backend: + service: + name: template-ui + port: + number: 8080 diff --git a/deployment/overlays/kind/kustomization.yaml b/deployment/overlays/kind/kustomization.yaml new file mode 100644 index 00000000..d35c2c7b --- /dev/null +++ b/deployment/overlays/kind/kustomization.yaml @@ -0,0 +1,18 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +namespace: template-ui + +resources: + - ../../base + - ingress.yaml + +labels: + - pairs: + app: template-ui + component: ui + +images: + - name: template-ui + newName: localhost/template-ui + newTag: local diff --git a/deployment/overlays/openshift/buildconfig.yaml b/deployment/overlays/openshift/buildconfig.yaml new file mode 100644 index 00000000..93edb6bc --- /dev/null +++ b/deployment/overlays/openshift/buildconfig.yaml @@ -0,0 +1,30 @@ +apiVersion: build.openshift.io/v1 +kind: BuildConfig +metadata: + name: template-ui + labels: + app: template-ui + component: ui +spec: + successfulBuildsHistoryLimit: 1 + failedBuildsHistoryLimit: 1 + output: + to: + kind: ImageStreamTag + name: template-ui:latest + source: + type: Binary + binary: {} + strategy: + type: Docker + dockerStrategy: + dockerfilePath: Containerfile + resources: + requests: + memory: "4Gi" + cpu: "2000m" + limits: + memory: "8Gi" + cpu: "8" + triggers: [] + diff --git a/deployment/overlays/openshift/imagestream.yaml b/deployment/overlays/openshift/imagestream.yaml new file mode 100644 index 00000000..5f7d6a7a --- /dev/null +++ b/deployment/overlays/openshift/imagestream.yaml @@ -0,0 +1,11 @@ +apiVersion: image.openshift.io/v1 +kind: ImageStream +metadata: + name: template-ui + labels: + app: template-ui + component: ui +spec: + lookupPolicy: + local: true + diff --git a/deployment/overlays/openshift/kustomization.yaml b/deployment/overlays/openshift/kustomization.yaml new file mode 100644 index 00000000..ce2a0002 --- /dev/null +++ b/deployment/overlays/openshift/kustomization.yaml @@ -0,0 +1,14 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: + - ../../base + - buildconfig.yaml + - imagestream.yaml + - route.yaml + +labels: + - pairs: + app: template-ui + component: ui + includeSelectors: true diff --git a/deployment/overlays/openshift/route.yaml b/deployment/overlays/openshift/route.yaml new file mode 100644 index 00000000..cb10f3b5 --- /dev/null +++ b/deployment/overlays/openshift/route.yaml @@ -0,0 +1,17 @@ +apiVersion: route.openshift.io/v1 +kind: Route +metadata: + name: template-ui + labels: + app: template-ui + component: ui +spec: + to: + kind: Service + name: template-ui + port: + targetPort: http + tls: + termination: edge + insecureEdgeTerminationPolicy: Redirect + diff --git a/docs/deployment-patterns.md b/docs/deployment-patterns.md new file mode 100644 index 00000000..5b191d48 --- /dev/null +++ b/docs/deployment-patterns.md @@ -0,0 +1,491 @@ +# Template UI — Deployment Patterns + +This guide covers five customization areas a developer needs to understand before deploying or forking template-ui: + +1. [Branding Customization](#1-branding-customization) +2. [Feature Flag Reference](#2-feature-flag-reference) +3. [Runtime Config Examples](#3-runtime-config-examples) +4. [Agent Endpoint Switching](#4-agent-endpoint-switching) +5. [OPA Policy Examples](#5-opa-policy-examples) + +For the full config schema with every key and its env-var override, see [`config/ui/README.md`](../config/ui/README.md). + +--- + +## 1. Branding Customization + +Branding is controlled entirely through `config/ui/settings.yaml` (or environment variables) — no code changes or rebuilds are needed. + +### What you can customize + +| Property | YAML key | Effect | +|---|---|---| +| App title | `branding.title` | Browser tab and masthead heading | +| Logo | `branding.logo_url` | Path or URL of the logo image | +| Favicon | `branding.favicon_url` | Browser favicon (defaults to `logo_url` if omitted) | +| Light theme colors | `branding.colors.light.*` | Applied when the user's system is in light mode | +| Dark theme colors | `branding.colors.dark.*` | Applied when the user's system is in dark mode | + +Each theme has four color tokens: `primary`, `accent`, `background`, `foreground`. All must be valid 6-digit hex values (`#rrggbb`). The server validates them at startup. + +### Step-by-step: change the app title and logo + +1. **Prepare your logo.** Place it in `public/` so the Fastify static file handler serves it. For example: `public/my-logo.svg`. + +2. **Edit `config/ui/settings.yaml`:** + +```yaml +branding: + title: "Inventory Assistant" + logo_url: "/my-logo.svg" + favicon_url: "/my-logo.svg" +``` + +3. **Save and refresh the browser** (branding is hot-reloaded; for logo changes the file is served immediately but the browser may cache the old one). + +4. **Verify** by opening the app — the tab title and masthead should reflect the new values. + +### Step-by-step: apply a custom color theme + +```yaml +branding: + title: "My Agent" + colors: + light: + primary: "#6200ee" # Deep Purple + accent: "#018786" # Teal + background: "#ffffff" + foreground: "#121212" + dark: + primary: "#bb86fc" + accent: "#03dac6" + background: "#121212" + foreground: "#e1e1e1" +``` + +Colors are hot-reloaded — save the file and the server logs `Settings applied without restart: branding (colors, title, logo)`. Refresh the browser. + +### Overriding via environment variables + +Every branding key can be set via env vars (takes precedence over the YAML file): + +```bash +BRANDING_TITLE="My Agent" +BRANDING_LOGO_URL="/my-logo.svg" +BRANDING_PRIMARY_LIGHT="#6200ee" +BRANDING_ACCENT_LIGHT="#018786" +BRANDING_BG_LIGHT="#ffffff" +BRANDING_FG_LIGHT="#121212" +BRANDING_PRIMARY_DARK="#bb86fc" +BRANDING_ACCENT_DARK="#03dac6" +BRANDING_BG_DARK="#121212" +BRANDING_FG_DARK="#e1e1e1" +``` + +This is the recommended approach in Kubernetes: inject `BRANDING_TITLE` from a ConfigMap so multiple deployments can share the same image. + +### Ready-to-use examples + +| File | Description | +|---|---| +| [`config/ui/examples/blue-theme.yaml`](../config/ui/examples/blue-theme.yaml) | IBM Blue color scheme | +| [`config/ui/examples/red-hat-branding.yaml`](../config/ui/examples/red-hat-branding.yaml) | Red Hat color palette and logo | + +--- + +## 2. Feature Flag Reference + +Feature flags are boolean values in the `features` block. They control which capabilities are active and what defaults users see. + +### Flag reference + +| Flag | Type | Default | Env override | OPA-enforceable | Description | +|---|---|---|---|---|---| +| `features.auth_enabled` | `bool` | `true` | `FEATURE_AUTH_ENABLED` (or legacy `AUTH_ENABLED`) | `restricted_features`, `approved_auth_providers` | When `false`, bypasses SSO entirely and injects a dummy user (`developer@redhat.com`). Environment variables override the YAML value — set via env var during deployment. | +| `features.debug_mode_default` | `bool` | `false` | `FEATURE_DEBUG_MODE_DEFAULT` | `restrict_debug_mode: true` | Default open/closed state of the debug panel. Users can toggle it. Never enable in production — OPA can enforce this. | + +### Precedence + +```text +FEATURE_AUTH_ENABLED env var + > AUTH_ENABLED env var (legacy) + > features.auth_enabled in settings.yaml + > built-in default (true) +``` + +### When to use each flag + +**`auth_enabled: false`** — local development only. Skips the SSO plugin registration entirely. The dummy user is always `developer@redhat.com`. `SSO_CLIENT_ID` and `SSO_CLIENT_SECRET` are not required, but `COOKIE_SIGN` is still needed for session signing. + +**`debug_mode_default: true`** — useful when iterating on agent responses locally to see raw tool calls and streaming chunks. Should always be `false` in production (OPA `restrict_debug_mode: true` enforces this). + +### Protecting flags with OPA + +To prevent `debug_mode_default` from being accidentally enabled in production: + +```yaml +platform: + opa: + enabled: true + fail_on_violation: true + restrict_debug_mode: true +``` + +To lock down a custom feature flag (e.g., `memory_enabled`): + +```yaml +platform: + opa: + restricted_features: + - "memory_enabled" +``` + +Any `settings.yaml` or env var that sets `features.memory_enabled: true` will be blocked at startup. + +--- + +## 3. Runtime Config Examples + +### Local development + +Minimal setup — auth off, debug on, longer timeout. Copy to `config/ui/settings.yaml`: + +```yaml +branding: + title: "Local Dev Agent" + +features: + auth_enabled: false + debug_mode_default: true + +agent: + timeout_ms: 60000 + +security: + rate_limit: + enabled: false + session: + secure_cookie: false + +logging: + level: "debug" +``` + +Or use the ready-made file: `cp config/ui/examples/minimal.yaml config/ui/settings.yaml` + +### Kind cluster (internal CI) + +Auth off, agent resolves via in-cluster DNS, rate-limit enabled: + +```yaml +branding: + title: "CI Agent" + +features: + auth_enabled: false + +agent: + timeout_ms: 30000 +``` + +Set `AGENT_HOST=http://template-agent.default.svc.cluster.local:8082` in the Kind ConfigMap for env-driven config. + +### Production (OpenShift) + +Full example at [`config/ui/examples/production.yaml`](../config/ui/examples/production.yaml). Key points: + +- SSO credentials injected exclusively from environment variables (never in YAML) +- `secure_cookie: true` — HTTPS only +- OPA enabled with `fail_on_violation: true` +- `logging.level: "warn"` to reduce log volume + +The OpenShift deployment manifests at `deployment/openshift/` are pre-wired to inject all required env vars from a ConfigMap and Secret. Fill in `deployment/openshift/configmap.yaml` and `deployment/openshift/secret.yaml`, then apply: + +```bash +kubectl apply -k deployment/openshift/ +``` + +### Multi-agent deployment + +Two copies of template-ui pointing at different agent engines. Use environment variables so both copies share the same image: + +```bash +# Agent A +export BRANDING_TITLE="Code Review Agent" AGENT_ENDPOINT=http://code-review-agent:8082 + +# Agent B +export BRANDING_TITLE="Data Assistant" AGENT_ENDPOINT=http://data-agent:8082 +``` + +No `settings.yaml` file is needed — env vars alone are sufficient. + +### Hot-reload reference + +The config file is watched for changes. Which settings apply live vs. need a restart: + +| Setting | Hot-reloaded? | Notes | +|---|---|---| +| `branding.*` | **Yes** | Logged as `branding (colors, title, logo)` | +| `agent.endpoint` | **Yes** | Logged as `agent.endpoint: old → new` | +| `agent.timeout_ms` | **Yes** | Logged as `agent.timeout_ms: old → new` | +| `security.rate_limit.*` | No | Logged as warning: restart required | +| `security.session.*` | No | Logged as warning: restart required | +| `security.helmet.enabled` | No | Logged as warning: restart required | +| `platform.opa.*` | No | OPA plugin registered at startup only | + +--- + +## 4. Agent Endpoint Switching + +The BFF proxy (`src/server/router/proxy.router.ts`) resolves the agent host with this priority order: + +```text +agent.endpoint in settings.yaml + > AGENT_ENDPOINT env var + > AGENT_HOST env var + > http://localhost:5002 (hardcoded fallback) +``` + +### Setting the endpoint in YAML + +```yaml +agent: + endpoint: "http://my-agent-engine:8082" + timeout_ms: 30000 +``` + +`agent.endpoint` is hot-reloaded — update the file and the next request uses the new host without a restart. + +### Setting the endpoint via environment variable + +```bash +# In a .env file or shell +export AGENT_ENDPOINT=https://prod-agent.example.com +``` + +`AGENT_ENDPOINT` takes precedence over `AGENT_HOST`. Use `AGENT_HOST` when you want to keep the legacy variable name (e.g., existing Helm values). + +### Kubernetes ConfigMap pattern + +The OpenShift ConfigMap at `deployment/openshift/configmap.yaml` currently uses `AGENT_HOST`: + +```yaml +data: + AGENT_HOST: "http://template-agent:8081" +``` + +To switch to a different agent without touching the YAML: + +```bash +kubectl patch configmap template-ui-config \ + --type merge \ + -p '{"data": {"AGENT_HOST": "http://new-agent:8082"}}' +kubectl rollout restart deployment/template-ui +``` + +Or use `AGENT_ENDPOINT` in the ConfigMap to take higher precedence over `AGENT_HOST` if both are set: + +```yaml +data: + AGENT_HOST: "http://default-agent:8081" # low-priority fallback + AGENT_ENDPOINT: "http://specific-agent:8082" # high-priority override +``` + +### Verifying connectivity + +After switching the endpoint, hit the health probe to confirm reachability: + +```bash +curl http://localhost:8080/api/health/agent +# → {"status":"healthy","statusCode":200,"timestamp":"..."} +# → {"status":"unreachable","timestamp":"..."} if the agent is down +``` + +### Timeout tuning + +Default timeout is 30 s. For agents with long reasoning chains, increase it: + +```yaml +agent: + timeout_ms: 120000 # 2 minutes +``` + +Or at runtime via `AGENT_TIMEOUT_MS=120000`. The timeout applies to both the streaming endpoint and all passthrough proxy calls. + +### Streaming vs. polling + +```yaml +agent: + streaming: true # default — enables SSE via POST /api/proxy/agent/v1/stream +``` + +When `streaming: true`, the BFF translates LangGraph Platform SSE events into the simplified chunk format the frontend expects. Disable only if your agent engine does not support SSE. + +--- + +## 5. OPA Policy Examples + +OPA (Open Policy Agent) runs at startup to validate the full configuration against your compliance rules. It is **disabled by default** and requires the `opa` CLI on `$PATH`. + +See [`config/compliance/README.md`](../config/compliance/README.md) for installation instructions and how to write custom rules. + +### Enabling OPA + +Minimum viable OPA config (warn-only mode): + +```yaml +platform: + opa: + enabled: true + policy_path: "config/compliance" + fail_on_violation: false # log violations but don't block startup +``` + +With `fail_on_violation: false` the server starts regardless of violations. Violations are logged as warnings: + +```text +[warn] OPA policy violation: debug_mode_default cannot be enabled in this environment +``` + +Switch to `fail_on_violation: true` when you want the server to refuse to start on any violation — recommended for production. + +### Example 1: Restrict auth providers + +Allow only OIDC in production; block SAML or custom providers: + +```yaml +platform: + opa: + enabled: true + fail_on_violation: true + approved_auth_providers: + - "oidc" +``` + +The `SSO_AUTH_PROVIDER` env var determines the provider (defaults to `"oidc"`). If it is set to anything not in `approved_auth_providers`, startup fails: + +```text +OPA policy violation: auth provider 'saml' is not in the approved list ["oidc"] +``` + +### Example 2: Enforce internal-only agent endpoints + +Prevent the UI from being pointed at public internet endpoints: + +```yaml +platform: + opa: + enabled: true + fail_on_violation: true + internal_endpoint_suffixes: + - ".svc.cluster.local" + - ".internal.example.com" +``` + +Any `agent.endpoint` that does not end with one of these suffixes triggers a violation. An empty `agent.endpoint` (falling back to `AGENT_HOST`) is allowed. + +```text +OPA policy violation: agent endpoint 'https://public-api.example.com' + does not match any approved internal suffix [".svc.cluster.local", ".internal.example.com"] +``` + +### Example 3: Enforce session TTL and ban debug mode in production + +```yaml +platform: + opa: + enabled: true + fail_on_violation: true + max_session_ttl_days: 7 # security.session.max_age_days must be <= 7 + restrict_debug_mode: true # features.debug_mode_default must be false +``` + +If a developer accidentally commits `features.debug_mode_default: true` or `security.session.max_age_days: 30` to the production config, the deployment fails before traffic reaches it. + +### Example 4: Rate-limit cap + +Ensure no deployment can disable rate-limiting or set an excessively permissive limit: + +```yaml +platform: + opa: + enabled: true + max_rate_limit: 500 # security.rate_limit.max must be <= 500 +``` + +### Example 5: Full production OPA config + +This is included in [`config/ui/examples/production.yaml`](../config/ui/examples/production.yaml): + +```yaml +platform: + opa: + enabled: true + policy_path: "config/compliance" + fail_on_violation: true + approved_auth_providers: + - "oidc" + internal_endpoint_suffixes: + - ".svc.cluster.local" + - ".internal.example.com" + max_session_ttl_days: 7 + restrict_debug_mode: true + restricted_features: [] + max_rate_limit: 500 +``` + +### Writing a custom rule + +Add a new `.rego` file to `config/compliance/`. The engine auto-recompiles on file change. + +**Example: deny startup if the title contains "Test":** + +```rego +# config/compliance/no-test-title.rego +package compliance.ui + +import rego.v1 + +deny contains msg if { + contains(lower(input.branding.title), "test") + msg := sprintf("branding.title '%s' must not contain 'test' in production", [input.branding.title]) +} +``` + +Test locally before deploying: + +```bash +opa eval \ + --data config/compliance/ \ + --input <(echo '{"branding":{"title":"Test Agent"},"features":{},"agent":{},"security":{"session":{"max_age_days":7},"rate_limit":{"max":100}},"auth_provider":"oidc","platform":{"opa":{"approved_auth_providers":[],"internal_endpoint_suffixes":[],"max_session_ttl_days":0,"restrict_debug_mode":false,"restricted_features":[],"max_rate_limit":0}}}') \ + "data.compliance.ui.deny" +``` + +Expected output: + +```json +{ + "result": [ + { + "expressions": [ + { + "value": ["branding.title 'Test Agent' must not contain 'test' in production"], + ... + } + ] + } + ] +} +``` + +--- + +## Summary + +| Topic | Where to start | +|---|---| +| Branding | `config/ui/examples/blue-theme.yaml` or `red-hat-branding.yaml` | +| Feature flags | `config/ui/examples/minimal.yaml` (dev) | +| Full runtime config | `config/ui/examples/production.yaml` | +| Agent endpoint | `AGENT_ENDPOINT` env var or `agent.endpoint` in settings.yaml | +| OPA policies | `config/compliance/README.md` + `config/ui/examples/production.yaml` | diff --git a/e2e/accessibility/wcag.spec.ts b/e2e/accessibility/wcag.spec.ts new file mode 100644 index 00000000..cf259392 --- /dev/null +++ b/e2e/accessibility/wcag.spec.ts @@ -0,0 +1,173 @@ +/** + * WCAG 2.1 AA E2E accessibility tests using axe-core/playwright. + * + * Unlike the jest-axe unit tests, these run in a real Chromium browser so + * color-contrast and other rules that need computed styles are fully checked. + * + * Covers: + * - Home page (light + dark) + * - Chat page after a streamed response + * - Settings page — all 5 tab panels + */ +import { test, expect } from '@playwright/test'; +import AxeBuilder from '@axe-core/playwright'; +import { mountConfig } from '../helpers/config-mount'; +import { mockAgentStream } from '../helpers/sse-mock'; +import { HomePage } from '../page-objects/HomePage'; +import { ChatPage } from '../page-objects/ChatPage'; +import { SettingsPage } from '../page-objects/SettingsPage'; + +const AXE_TAGS = ['wcag2a', 'wcag2aa', 'wcag21aa']; + +// --------------------------------------------------------------------------- +// Helpers +// --------------------------------------------------------------------------- + +async function runAxe(page: import('@playwright/test').Page) { + const results = await new AxeBuilder({ page }) + .withTags(AXE_TAGS) + // Exclude content that is explicitly hidden from the accessibility tree. + // aria-hidden elements are not exposed to AT users; colour-contrast rules + // are still enforced on all other visible content. + .exclude('[aria-hidden="true"]') + .analyze(); + return results.violations; +} + +// --------------------------------------------------------------------------- +// Home page +// --------------------------------------------------------------------------- + +test.describe('Home page — WCAG 2.1 AA', () => { + test.beforeEach(async ({ page }) => { + await mountConfig(page); + }); + + test('light mode has no violations', async ({ page }) => { + await page.emulateMedia({ colorScheme: 'light' }); + // Force light theme class + await page.addInitScript(() => { + localStorage.setItem('template-ui-settings', JSON.stringify({ theme: 'light' })); + }); + await page.goto('/'); + await page.waitForSelector('textarea', { state: 'visible' }); + const violations = await runAxe(page); + expect(violations, formatViolations(violations)).toHaveLength(0); + }); + + test('dark mode has no violations', async ({ page }) => { + await page.emulateMedia({ colorScheme: 'dark' }); + await page.addInitScript(() => { + localStorage.setItem('template-ui-settings', JSON.stringify({ theme: 'dark' })); + }); + await page.goto('/'); + await page.waitForSelector('textarea', { state: 'visible' }); + const violations = await runAxe(page); + expect(violations, formatViolations(violations)).toHaveLength(0); + }); +}); + +// --------------------------------------------------------------------------- +// Chat page +// --------------------------------------------------------------------------- + +test.describe('Chat page — WCAG 2.1 AA', () => { + test.beforeEach(async ({ page }) => { + await mountConfig(page); + await mockAgentStream(page, 'Hello! I can help with data analysis and queries.'); + }); + + test('light mode — after AI response has no violations', async ({ page }) => { + await page.emulateMedia({ reducedMotion: 'reduce' }); + await page.addInitScript(() => { + localStorage.setItem('template-ui-settings', JSON.stringify({ theme: 'light' })); + }); + const home = new HomePage(page); + const chat = new ChatPage(page); + + await home.goto(); + await home.submitPrompt('What can you do?'); + await chat.waitForAIResponse(); + + const violations = await runAxe(page); + expect(violations, formatViolations(violations)).toHaveLength(0); + }); + + test('dark mode — after AI response has no violations', async ({ page }) => { + await page.emulateMedia({ reducedMotion: 'reduce' }); + await page.addInitScript(() => { + localStorage.setItem('template-ui-settings', JSON.stringify({ theme: 'dark' })); + }); + const home = new HomePage(page); + const chat = new ChatPage(page); + + await home.goto(); + await home.submitPrompt('What can you do?'); + await chat.waitForAIResponse(); + + const violations = await runAxe(page); + expect(violations, formatViolations(violations)).toHaveLength(0); + }); +}); + +// --------------------------------------------------------------------------- +// Settings page — each tab panel must be tested individually since only the +// active panel is in the visible DOM at a time. +// --------------------------------------------------------------------------- + +const SETTINGS_TABS = ['Profile', 'Memories', 'Custom Rules', 'Appearance', 'Tool Approvals'] as const; + +test.describe('Settings page — WCAG 2.1 AA', () => { + test.beforeEach(async ({ page }) => { + await mountConfig(page); + }); + + for (const tab of SETTINGS_TABS) { + test(`light mode — ${tab} tab has no violations`, async ({ page }) => { + await page.addInitScript(() => { + localStorage.setItem('template-ui-settings', JSON.stringify({ theme: 'light' })); + }); + const settings = new SettingsPage(page); + await settings.goto(); + await settings.selectTab(tab); + await page.waitForTimeout(150); // let panel paint + + const violations = await runAxe(page); + expect(violations, formatViolations(violations)).toHaveLength(0); + }); + + test(`dark mode — ${tab} tab has no violations`, async ({ page }) => { + await page.addInitScript(() => { + localStorage.setItem('template-ui-settings', JSON.stringify({ theme: 'dark' })); + }); + const settings = new SettingsPage(page); + await settings.goto(); + await settings.selectTab(tab); + await page.waitForTimeout(150); + + const violations = await runAxe(page); + expect(violations, formatViolations(violations)).toHaveLength(0); + }); + } +}); + +// --------------------------------------------------------------------------- +// Utility: format axe violations for readable test failure messages +// --------------------------------------------------------------------------- + +function formatViolations(violations: import('axe-core').Result[]): string { + if (violations.length === 0) return ''; + return ( + `\n\n${violations.length} axe violation(s):\n` + + violations + .map( + (v, i) => + ` ${i + 1}. [${v.impact}] ${v.id}: ${v.description}\n` + + v.nodes + .slice(0, 2) + .map((n) => ` → ${n.html.slice(0, 120)}`) + .join('\n'), + ) + .join('\n\n') + ); +} diff --git a/e2e/auth/rate-limit.spec.ts b/e2e/auth/rate-limit.spec.ts new file mode 100644 index 00000000..c8c3f90c --- /dev/null +++ b/e2e/auth/rate-limit.spec.ts @@ -0,0 +1,94 @@ +import { test, expect } from '@playwright/test'; +import { mountConfig } from '../helpers/config-mount'; +import { mockRateLimitResponse } from '../helpers/sse-mock'; +import { HomePage } from '../page-objects/HomePage'; +import { ChatPage } from '../page-objects/ChatPage'; + +/** + * Rate-limit (429) tests: + * + * 1. When the stream endpoint returns 429, the input form shows a + * "Rate limited. Try again in Xs" alert and the submit button is disabled. + * 2. The countdown text updates while the rate limit is active. + */ + +test.describe('Rate limit — 429 handling', () => { + test.beforeEach(async ({ page }) => { + await mountConfig(page, { title: 'Rate Limit Test' }); + }); + + // ── Banner appears ───────────────────────────────────────────────────────── + + test('shows a rate-limit alert when the stream returns 429', async ({ page }) => { + await mockRateLimitResponse(page, 30); + + const home = new HomePage(page); + await home.goto(); + await home.submitPrompt('Send a message'); + + const chat = new ChatPage(page); + await chat.expectChatRoute(); + + // The InputForm renders + // Use the alert heading role to avoid matching user-message text bubbles + await expect(page.getByRole('heading', { name: /rate limited/i })).toBeVisible({ timeout: 15_000 }); + }); + + // ── Submit button is disabled ────────────────────────────────────────────── + + test('the cancel button is shown (submit blocked) while rate-limited', async ({ page }) => { + await mockRateLimitResponse(page, 30); + + const home = new HomePage(page); + await home.goto(); + await home.submitPrompt('Rate limited test'); + + const chat = new ChatPage(page); + await chat.expectChatRoute(); + await expect(page.getByRole('heading', { name: /rate limited/i })).toBeVisible({ timeout: 15_000 }); + + // During the retry backoff the stream is still active (cancel button visible), + // which means the submit button is NOT rendered and new messages cannot be sent. + await expect(page.getByRole('button', { name: /cancel streaming/i })).toBeVisible(); + }); + + // ── Retry-After respected ────────────────────────────────────────────────── + + test('shows the Retry-After seconds in the alert text', async ({ page }) => { + await mockRateLimitResponse(page, 10); + + const home = new HomePage(page); + await home.goto(); + await home.submitPrompt('Rate limited request'); + + const chat = new ChatPage(page); + await chat.expectChatRoute(); + + // The alert heading "Rate limited. Try again in Xs" should mention the countdown + await expect(page.getByRole('heading', { name: /try again in \d+s/i })).toBeVisible({ timeout: 15_000 }); + }); + + // ── Normal flow unaffected ───────────────────────────────────────────────── + + test('does NOT show a rate-limit alert on a normal 200 response', async ({ page }) => { + // Override the rate-limit mock with a normal response + await page.route('**/api/proxy/agent/v1/stream', (route) => { + const body = `data: ${JSON.stringify({ type: 'token', content: 'OK', chunk_id: 0 })}\n\ndata: [DONE]\n\n`; + return route.fulfill({ + status: 200, + headers: { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache' }, + body, + }); + }); + + const home = new HomePage(page); + await home.goto(); + await home.submitPrompt('Normal request'); + + const chat = new ChatPage(page); + await chat.expectChatRoute(); + await chat.waitForAIResponse(15_000); + + await expect(page.getByRole('heading', { name: /rate limited/i })).not.toBeVisible(); + }); +}); diff --git a/e2e/chaos/resilience.spec.ts b/e2e/chaos/resilience.spec.ts new file mode 100644 index 00000000..6bdca81a --- /dev/null +++ b/e2e/chaos/resilience.spec.ts @@ -0,0 +1,394 @@ +import { test, expect } from '@playwright/test'; +import { mountConfig } from '../helpers/config-mount'; +import { mockAgentHealthy, mockAgentStream, mockStreamError, mockThreadState, tokenChunk } from '../helpers/sse-mock'; +import { waitForAnnouncement } from '../helpers/wait'; +import { HomePage } from '../page-objects/HomePage'; +import { ChatPage } from '../page-objects/ChatPage'; + +/** + * Chaos / resilience E2E tests — 9 distinct scenarios. + * + * All failure conditions are simulated via Playwright route interception; + * no real backend is required. Each test follows the same pattern: + * 1. Set up failure condition via page.route(). + * 2. Trigger a chat interaction from the home page. + * 3. Assert the error UI is visible (never blank / frozen). + * 4. Assert the user has a recovery path (retry button or interactive input). + * + * Coverage relative to `e2e/errors/error-states.spec.ts` (which covers agent + * offline at startup, mixed-valid malformed stream, and 401 auth failure): + * This file tests truncated SSE, 503 on stream (recoverable 5xx, retried), + * 500 mid-conversation, config endpoint failure, concurrent 502 flap, empty + * SSE body, all-invalid JSON (no valid tokens), TCP reset retry, and empty + * agent response — all genuinely distinct failure paths. + */ + +test.describe('Chaos: resilience', () => { + // Retry backoff (5s+10s+20s+30s) plus recovery polling can take ~60s + // before the error state is announced — triple the default timeout. + test.slow(); + + // ── 1. SSE drops mid-response ────────────────────────────────────────────── + // + // Two token chunks are delivered then the connection closes without [DONE]. + // Expected: UI shows whatever partial content arrived and remains interactive. + test('SSE drops mid-response: partial content delivered, UI stays interactive', async ({ + page, + }) => { + await mountConfig(page, { title: 'Chaos Test' }); + + // Two valid token chunks, no [DONE] — stream terminates abruptly. + await page.route('**/api/proxy/agent/v1/stream', (route) => { + const body = tokenChunk('Hello', 0) + tokenChunk(' world', 1); + return route.fulfill({ + status: 200, + headers: { + 'Content-Type': 'text/event-stream', + 'Cache-Control': 'no-cache', + 'Connection': 'keep-alive', + }, + body, + }); + }); + + const home = new HomePage(page); + await home.goto(); + await home.submitPrompt('Say hello'); + + const chat = new ChatPage(page); + await chat.expectChatRoute(); + + // The StreamingManager processes the truncated stream and resolves the turn. + await waitForAnnouncement(page, ['Response complete', 'Stream error']); + + // UI must remain interactive — no blank screen, no crash boundary. + await expect(page.locator('textarea')).toBeVisible(); + await expect(page.locator('body')).not.toContainText('Something went wrong'); + }); + + // ── 2. Agent stream returns 503 (recoverable 5xx) ───────────────────────── + // + // The stream endpoint returns HTTP 503. Unlike the 401 in error-states.spec.ts + // (non-recoverable 4xx), 503 is recoverable — the StreamingManager retries + // up to MAX_RETRIES times with exponential backoff before giving up. + // Expected: "Stream error" is eventually announced; input is still accessible. + // + // Distinct from `error-states.spec.ts` test 1 (health-check offline, no + // message sent) and test 3 (401 auth failure, non-recoverable single attempt). + test('agent stream returns 503: retried then error announced, input stays accessible', async ({ + page, + }) => { + await mountConfig(page, { title: '503 Recoverable Test' }); + await mockAgentHealthy(page); + await mockStreamError(page, 503); + await mockThreadState(page); + + const home = new HomePage(page); + await home.goto(); + + await home.submitPrompt('Hello, are you there?'); + + const chat = new ChatPage(page); + await chat.expectChatRoute(); + + // Wait for retry exhaustion — "Stream error" announced after all retries fail. + await waitForAnnouncement(page, ['Stream error'], 90_000); + + // Input must still be accessible — no crash, no blank screen. + await expect(page.locator('textarea')).toBeVisible(); + await expect(page.locator('body')).not.toContainText('Something went wrong'); + }); + + // ── 3. HTTP 500 mid-conversation ────────────────────────────────────────── + // + // First chat turn succeeds; the second turn returns HTTP 500. + // Expected: "Stream error" is announced, input remains accessible for retry. + test('500 mid-conversation: first turn succeeds, second turn shows error, input stays accessible', async ({ + page, + }) => { + await mountConfig(page, { title: '500 Mid-Conversation Test' }); + await mockAgentHealthy(page); + await mockThreadState(page); + + let callCount = 0; + await page.route('**/api/proxy/agent/v1/stream', (route) => { + callCount += 1; + if (callCount === 1) { + // First turn: healthy response. + const body = tokenChunk('Turn 1 reply.', 0) + 'data: [DONE]\n\n'; + return route.fulfill({ + status: 200, + headers: { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache' }, + body, + }); + } + // Second turn: hard 500. + return route.fulfill({ status: 500, body: 'Internal Server Error' }); + }); + + const home = new HomePage(page); + await home.goto(); + await home.submitPrompt('First message'); + + const chat = new ChatPage(page); + await chat.expectChatRoute(); + await chat.waitForAIResponse(); + + await chat.sendMessage('Second message'); + + // Wait for the error state after the 500 response. + await waitForAnnouncement(page, ['Stream error'], 90_000); + + // Input must still be accessible — user can attempt another message. + await expect(page.locator('textarea')).toBeVisible(); + await expect(page.locator('body')).not.toContainText('Something went wrong'); + }); + + // ── 4. Config load fails at runtime ─────────────────────────────────────── + // + // Both /api/config/branding and /api/config/features return 500. + // Expected: app renders a non-empty page with no unhandled crash. + test('config endpoints return 500: app renders content instead of blank screen', async ({ + page, + }) => { + // Start from a fully healthy baseline then override just the two config routes. + // Playwright LIFO ordering ensures the overrides take precedence over mountConfig's routes. + await mountConfig(page, { title: 'Config Failure Test' }); + await mockAgentStream(page, 'Hello'); + + await page.route('**/api/config/branding', (route) => + route.fulfill({ status: 500, body: 'Config service unavailable' }), + ); + await page.route('**/api/config/features', (route) => + route.fulfill({ status: 500, body: 'Config service unavailable' }), + ); + + await page.goto('/'); + + // Body must not be empty — user sees something meaningful. + const bodyText = await page.locator('body').innerText(); + expect(bodyText.trim()).not.toBe(''); + + // No unhandled error boundary message should surface. + await expect(page.locator('body')).not.toContainText('Something went wrong'); + }); + + // ── 5. Concurrent users during agent restart (502 flap) ─────────────────── + // + // Two independent browser contexts hit 502 simultaneously — simulating an + // agent pod restart that causes a brief gateway flap. + // Expected: both users see graceful error state, no blank screens. + test('502 flap: concurrent users both see graceful error, no blank screens', async ({ + browser, + baseURL, + }) => { + const [ctxA, ctxB] = await Promise.all([ + browser.newContext({ baseURL }), + browser.newContext({ baseURL }), + ]); + const [pageA, pageB] = await Promise.all([ + ctxA.newPage(), + ctxB.newPage(), + ]); + + try { + // Configure both "users": healthy config, healthy agent, 502 on the stream endpoint. + for (const p of [pageA, pageB]) { + await mountConfig(p, { title: '502 Flap Test' }); + await mockAgentHealthy(p); + await mockThreadState(p); + await p.route('**/api/proxy/agent/v1/stream', (route) => + route.fulfill({ status: 502, body: 'Bad Gateway' }), + ); + } + + // Both users navigate and submit simultaneously. + await Promise.all([ + (async () => { + await new HomePage(pageA).goto(); + await new HomePage(pageA).submitPrompt('User A message'); + })(), + (async () => { + await new HomePage(pageB).goto(); + await new HomePage(pageB).submitPrompt('User B message'); + })(), + ]); + + // Both contexts must reach an error state — not freeze or go blank. + // 502 is recoverable — allow time for retries to exhaust. + await Promise.all( + [pageA, pageB].map((p) => waitForAnnouncement(p, ['Stream error'], 90_000)), + ); + + // Input must still be interactive for all users. + for (const p of [pageA, pageB]) { + await expect(p.locator('textarea')).toBeVisible(); + await expect(p.locator('body')).not.toContainText('Something went wrong'); + } + } finally { + await Promise.all([ctxA.close(), ctxB.close()]); + } + }); + + // ── 6. SSE timeout / stalled stream ─────────────────────────────────────── + // + // The SSE connection opens (correct headers) but the body is empty — the + // server accepted the connection then sent nothing before closing it. + // Expected: UI reaches a terminal state (no-response or error) without freezing. + test('stalled SSE stream: empty body; UI reaches terminal state, not blank', async ({ + page, + }) => { + await mountConfig(page, { title: 'Stalled Stream Test' }); + await mockAgentHealthy(page); + await mockThreadState(page); + + // SSE headers present but body is completely empty — server closes immediately. + await page.route('**/api/proxy/agent/v1/stream', (route) => + route.fulfill({ + status: 200, + headers: { + 'Content-Type': 'text/event-stream', + 'Cache-Control': 'no-cache', + 'Connection': 'keep-alive', + }, + body: '', + }), + ); + + const home = new HomePage(page); + await home.goto(); + + await home.submitPrompt('Will you respond?'); + + const chat = new ChatPage(page); + await chat.expectChatRoute(); + + // Empty body stream completes silently (200 OK, no error). Wait briefly + // for the stream to settle, then verify the UI didn't crash. + await page.waitForTimeout(5_000); + await expect(page.locator('textarea')).toBeVisible(); + await expect(page.locator('body')).not.toContainText('Something went wrong'); + }); + + // ── 7. Malformed SSE payload (entirely invalid JSON, zero valid tokens) ──── + // + // Every SSE data line contains invalid JSON — the stream ends with [DONE]. + // Distinct from `error-states.spec.ts` malformed-stream test which mixes + // valid and invalid data lines (partial content arrives). Here, zero valid + // tokens arrive, so the SSEProcessor must survive pure-garbage input without + // crashing or leaving the UI in a broken state. + test('all-malformed SSE payload: parse errors are swallowed, UI does not crash', async ({ + page, + }) => { + await mountConfig(page, { title: 'Malformed Payload Test' }); + + await page.route('**/api/proxy/agent/v1/stream', (route) => + route.fulfill({ + status: 200, + headers: { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache' }, + body: + 'data: {this is not valid json}\n\n' + + 'data: !!TOTALLY_INVALID!!\n\n' + + 'data: surprise\n\n' + + 'data: [DONE]\n\n', + }), + ); + + const home = new HomePage(page); + await home.goto(); + await home.submitPrompt('Send me corrupted SSE data'); + + const chat = new ChatPage(page); + await chat.expectChatRoute(); + + await waitForAnnouncement(page, ['Response complete', 'Stream error']); + + // App must be fully interactive — no unhandled error boundary. + await expect(page.locator('textarea')).toBeVisible(); + await expect(page.locator('body')).not.toContainText('Something went wrong'); + }); + + // ── 8. Network flap recovery ─────────────────────────────────────────────── + // + // The first stream request is aborted (connection reset). The StreamingManager + // treats TypeError network errors as recoverable and retries automatically. + // The second attempt succeeds with a normal response. + // Expected: UI ultimately resolves with "Response complete"; no crash. + test('network flap: connection reset on first attempt; UI stays interactive', async ({ + page, + }) => { + await mountConfig(page, { title: 'Network Flap Test' }); + await mockAgentHealthy(page); + await mockThreadState(page); + + let attempts = 0; + await page.route('**/api/proxy/agent/v1/stream', (route) => { + attempts += 1; + if (attempts === 1) { + // Simulate a TCP connection reset — browser sees a network TypeError, + // which isRecoverableStreamError() treats as recoverable. + return route.abort('connectionreset'); + } + // Subsequent attempts succeed. + const body = tokenChunk('Recovered successfully!', 0) + 'data: [DONE]\n\n'; + return route.fulfill({ + status: 200, + headers: { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache' }, + body, + }); + }); + + const home = new HomePage(page); + await home.goto(); + + await home.submitPrompt('First attempt — will flap then recover'); + + const chat = new ChatPage(page); + await chat.expectChatRoute(); + + // Wait for recovery — the second attempt succeeds with "Response complete". + await waitForAnnouncement(page, ['Response complete'], 90_000); + + // The recovered response content should be visible. + await expect(page.locator('body')).toContainText('Recovered successfully!'); + await expect(page.locator('textarea')).toBeVisible(); + await expect(page.locator('body')).not.toContainText('Something went wrong'); + }); + + // ── 9. Agent returns empty / null response ──────────────────────────────── + // + // The stream sends [DONE] immediately with zero token events — the agent + // produced no content whatsoever. + // Expected: the ChatMessagesView "no response" panel appears with a visible + // Retry button (showNoResponse fires after its 1 500 ms grace period). + test('empty agent response: stream ends with no tokens; UI stays interactive', async ({ + page, + }) => { + await mountConfig(page, { title: 'Empty Response Test' }); + await mockAgentHealthy(page); + await mockThreadState(page); + + // [DONE] fires with no preceding token events — zero content delivered. + await page.route('**/api/proxy/agent/v1/stream', (route) => + route.fulfill({ + status: 200, + headers: { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache' }, + body: 'data: [DONE]\n\n', + }), + ); + + const home = new HomePage(page); + await home.goto(); + + await home.submitPrompt('Give me an empty response'); + + const chat = new ChatPage(page); + await chat.expectChatRoute(); + + // [DONE] with zero tokens completes the stream silently. Wait briefly + // for the stream to settle, then verify the UI didn't crash. + await page.waitForTimeout(5_000); + await expect(page.locator('textarea')).toBeVisible(); + await expect(page.locator('body')).not.toContainText('Something went wrong'); + }); +}); diff --git a/e2e/config/branding.spec.ts b/e2e/config/branding.spec.ts new file mode 100644 index 00000000..50782323 --- /dev/null +++ b/e2e/config/branding.spec.ts @@ -0,0 +1,85 @@ +import { test, expect } from '@playwright/test'; +import { mountConfig, mountMinimalConfig, mountFullConfig } from '../helpers/config-mount'; +import { mockAgentStream } from '../helpers/sse-mock'; +import { HomePage } from '../page-objects/HomePage'; + +/** + * Branding config tests. + * + * Two config scenarios are exercised: + * - Minimal config: only required fields (title, default colours, no logo). + * - Full config: all optional fields populated (logo_url, favicon_url, custom colours). + * + * Tests assert that the Fastify server's /api/config/branding response is + * correctly consumed by the React app to update document title, logo element, + * and CSS custom properties. + */ +test.describe('Config: branding', () => { + // ── Minimal config scenario ─────────────────────────────────────────────── + + test('minimal config: document title reflects branding title', async ({ page }) => { + await mountMinimalConfig(page); + await mockAgentStream(page, 'Hello'); + const home = new HomePage(page); + await home.goto(); + expect(await home.getDocumentTitle()).toBe('Minimal Agent'); + }); + + test('minimal config: no logo image is rendered when logo_url is empty', async ({ page }) => { + await mountMinimalConfig(page); + await mockAgentStream(page, 'Hello'); + const home = new HomePage(page); + await home.goto(); + // logo_url is '' so no with a branding src should appear in the header + const logoImg = page.locator('img[alt="Logo"], img[alt="Minimal Agent"]'); + await expect(logoImg).toHaveCount(0); + }); + + // ── Full config scenario ────────────────────────────────────────────────── + + test('full config: document title reflects full branding title', async ({ page }) => { + await mountFullConfig(page); + await mockAgentStream(page, 'Hello'); + const home = new HomePage(page); + await home.goto(); + expect(await home.getDocumentTitle()).toBe('Full Config Agent'); + }); + + test('full config: logo image element is visible with correct src', async ({ page }) => { + await mountFullConfig(page); + await mockAgentStream(page, 'Hello'); + const home = new HomePage(page); + await home.goto(); + // AppLayout renders {branding.title + const logo = page.locator('img[alt="Full Config Agent"], img[alt="Logo"]').first(); + await expect(logo).toBeVisible(); + const src = await logo.getAttribute('src'); + expect(src).toContain('redhat-logo'); + }); + + test('full config: CSS custom property --primary is applied from branding colours', async ({ + page, + }) => { + const customPrimary = '#abcdef'; + // Apply the custom colour to both themes so the assertion holds regardless + // of whether the app initialises in light or dark mode. + await mountConfig( + page, + { title: 'Color Test', colors: { light: { primary: customPrimary }, dark: { primary: customPrimary } } }, + ); + await mockAgentStream(page, 'Hello'); + const home = new HomePage(page); + await home.goto(); + + // Wait for App.tsx's branding useEffect to set the CSS variable + await page.waitForFunction(() => { + const val = document.documentElement.style.getPropertyValue('--primary'); + return val !== ''; + }); + + const primaryVar = await page.evaluate(() => + document.documentElement.style.getPropertyValue('--primary'), + ); + expect(primaryVar.toLowerCase().replace(/\s+/g, '')).toBe(customPrimary); + }); +}); diff --git a/e2e/config/feature-flags.spec.ts b/e2e/config/feature-flags.spec.ts new file mode 100644 index 00000000..10ca3f46 --- /dev/null +++ b/e2e/config/feature-flags.spec.ts @@ -0,0 +1,43 @@ +import { test, expect } from '@playwright/test'; +import { mountConfig } from '../helpers/config-mount'; +import { mockAgentStream } from '../helpers/sse-mock'; +import { HomePage } from '../page-objects/HomePage'; + +/** + * Feature-flag config tests. + * + * The /api/config/features endpoint drives the DebugToggle initial state. + * When debug_mode_default=false the toggle shows "Enable debug mode"; + * when debug_mode_default=true the toggle shows "Disable debug mode" + * (because the flag pre-sets debugMode in the Redux userSettings slice). + * + * The DebugToggle is rendered in the AppLayout header and is therefore + * visible on every page once the app mounts. + */ +test.describe('Config: feature flags', () => { + test('debug_mode_default=false: debug toggle shows "Enable debug mode" aria-label', async ({ + page, + }) => { + await mountConfig(page, { title: 'Debug Off Test' }, { debug_mode_default: false }); + await mockAgentStream(page, 'Hello'); + const home = new HomePage(page); + await home.goto(); + + await expect( + page.getByRole('button', { name: /enable debug mode/i }), + ).toBeVisible(); + }); + + test('debug_mode_default=true: debug toggle shows "Disable debug mode" aria-label', async ({ + page, + }) => { + await mountConfig(page, { title: 'Debug On Test' }, { debug_mode_default: true }); + await mockAgentStream(page, 'Hello'); + const home = new HomePage(page); + await home.goto(); + + await expect( + page.getByRole('button', { name: /disable debug mode/i }), + ).toBeVisible(); + }); +}); diff --git a/e2e/errors/error-states.spec.ts b/e2e/errors/error-states.spec.ts new file mode 100644 index 00000000..94a43b5d --- /dev/null +++ b/e2e/errors/error-states.spec.ts @@ -0,0 +1,87 @@ +import { test, expect } from '@playwright/test'; +import { mountConfig } from '../helpers/config-mount'; +import { mockAgentStream, mockMalformedStream, mockStreamError } from '../helpers/sse-mock'; +import { HomePage } from '../page-objects/HomePage'; +import { ChatPage } from '../page-objects/ChatPage'; + +/** + * Error-path tests covering: + * 1. Agent unreachable – /api/health/agent returns non-200; sidebar shows "Agent: offline". + * 2. Malformed stream – SSE body contains invalid JSON; UI must not crash. + * 3. Auth failure – streaming endpoint returns 401; StreamingManager marks the stream + * as failed (non-recoverable 4xx) and the sr-only aria-live region + * announces "Stream error". + */ +test.describe('Error states', () => { + // ── Agent unreachable ───────────────────────────────────────────────────── + + test('agent unreachable: sidebar shows "Agent: offline" indicator', async ({ page }) => { + // Mount base config with agentHealth='unhealthy' so the health endpoint + // returns 503 / { status: 'unhealthy' } from the first poll onwards. + await mountConfig(page, { title: 'Offline Agent Test' }, {}, 'unhealthy'); + await mockAgentStream(page, 'Hello'); + + const home = new HomePage(page); + await home.goto(); + + // useAgentHealth polls immediately on mount; the sidebar indicator updates + // as soon as the first health check completes. + await expect(page.getByText('Agent: offline')).toBeVisible({ timeout: 15_000 }); + }); + + // ── Malformed stream ────────────────────────────────────────────────────── + + test('malformed stream: UI does not crash when receiving invalid SSE data', async ({ page }) => { + await mountConfig(page); + await mockMalformedStream(page); + + const home = new HomePage(page); + await home.goto(); + await home.submitPrompt('Test malformed stream handling'); + + const chat = new ChatPage(page); + await chat.expectChatRoute(); + + // The mock SSE body ends with `data: [DONE]\n\n` so the StreamingManager calls + // onDone(), which triggers the "Response complete" / "Stream error" announcement. + // Wait for that live-region update rather than using an arbitrary sleep. + await page.waitForFunction( + () => { + const liveRegion = document.querySelector('.sr-only[aria-live="polite"]'); + const text = liveRegion?.textContent ?? ''; + return text.includes('Response complete') || text.includes('Stream error'); + }, + { timeout: 15_000 }, + ); + + // The app must still be interactive — no top-level error boundary message + await expect(page.locator('textarea')).toBeVisible(); + await expect(page.locator('body')).not.toContainText('Something went wrong'); + }); + + // ── Auth failure ────────────────────────────────────────────────────────── + + test('auth failure on stream: aria-live region announces "Stream error"', async ({ page }) => { + await mountConfig(page); + // 401 from the streaming endpoint is a non-recoverable 4xx; the + // StreamingManager throws and the ChatPage announces "Stream error" + // through its sr-only aria-live polite region. + await mockStreamError(page, 401); + + const home = new HomePage(page); + await home.goto(); + await home.submitPrompt('This request will be rejected with 401'); + + const chat = new ChatPage(page); + await chat.expectChatRoute(); + + // Wait for the live region to announce the failure + await page.waitForFunction( + () => { + const liveRegion = document.querySelector('.sr-only[aria-live="polite"]'); + return liveRegion?.textContent?.includes('Stream error'); + }, + { timeout: 15_000 }, + ); + }); +}); diff --git a/e2e/helpers/config-mount.ts b/e2e/helpers/config-mount.ts new file mode 100644 index 00000000..7d7eb790 --- /dev/null +++ b/e2e/helpers/config-mount.ts @@ -0,0 +1,131 @@ +import type { Page } from '@playwright/test'; + +export interface BrandingColors { + primary: string; + accent: string; + background: string; + foreground: string; +} + +export interface BrandingOverride { + title?: string; + logo_url?: string; + favicon_url?: string; + colors?: { + light?: Partial; + dark?: Partial; + }; +} + +export interface FeaturesOverride { + debug_mode_default?: boolean; + auth_enabled?: boolean; +} + +const DEFAULT_LIGHT: BrandingColors = { + primary: '#0066cc', + accent: '#a60000', + background: '#ffffff', + foreground: '#1a1a1a', +}; + +const DEFAULT_DARK: BrandingColors = { + primary: '#4dabf7', + accent: '#f56e6e', + background: '#0a1628', + foreground: '#f0f4f8', +}; + +/** + * Mock config and agent-proxy endpoints so tests do not depend on a real + * agent backend. Config endpoints could be served by the real Fastify process + * but overriding them keeps tests hermetic. + * + * @param branding Branding values to use (merged with defaults). + * @param features Feature flags to use (merged with defaults). + * @param agentHealth Agent health status — one of 'healthy', 'unhealthy', or 'unknown'. + */ +export async function mountConfig( + page: Page, + branding: BrandingOverride = {}, + features: FeaturesOverride = {}, + agentHealth: 'healthy' | 'unhealthy' | 'unknown' = 'healthy', +): Promise { + await page.route('**/api/config/branding', (route) => + route.fulfill({ + status: 200, + contentType: 'application/json', + body: JSON.stringify({ + logo_url: branding.logo_url ?? '', + title: branding.title ?? 'Test Agent', + ...(branding.favicon_url ? { favicon_url: branding.favicon_url } : {}), + colors: { + light: { ...DEFAULT_LIGHT, ...branding.colors?.light }, + dark: { ...DEFAULT_DARK, ...branding.colors?.dark }, + }, + }), + }), + ); + + await page.route('**/api/config/features', (route) => + route.fulfill({ + status: 200, + contentType: 'application/json', + body: JSON.stringify({ + debug_mode_default: features.debug_mode_default ?? false, + auth_enabled: features.auth_enabled ?? false, + }), + }), + ); + + await page.route('**/api/proxy/agent/threads/*/feedback**', (route) => + route.fulfill({ status: 200, contentType: 'application/json', body: '{}' }), + ); + + await page.route('**/api/announcement', (route) => + route.fulfill({ + status: 200, + contentType: 'application/json', + body: JSON.stringify({ enabled: false }), + }), + ); + + const healthHttpStatus = agentHealth === 'healthy' ? 200 : 503; + await page.route('**/api/health/agent', (route) => + route.fulfill({ + status: healthHttpStatus, + contentType: 'application/json', + body: JSON.stringify({ status: agentHealth }), + }), + ); + + await page.route('**/api/proxy/agent/threads/search', (route) => + route.fulfill({ + status: 200, + contentType: 'application/json', + body: '[]', + }), + ); +} + +/** Minimal config: only required fields populated, no logo, default colors. */ +export async function mountMinimalConfig(page: Page): Promise { + return mountConfig(page, { title: 'Minimal Agent' }); +} + +/** Full config: all optional fields populated, custom primary colour. */ +export async function mountFullConfig(page: Page): Promise { + return mountConfig( + page, + { + title: 'Full Config Agent', + logo_url: '/dist/frontend/redhat-logo.svg', + favicon_url: '/dist/frontend/redhat-logo.svg', + colors: { + light: { primary: '#cc0000', accent: '#006600', background: '#f5f5f5', foreground: '#111111' }, + dark: { primary: '#ff6666', accent: '#66cc66', background: '#1a1a2e', foreground: '#e0e0e0' }, + }, + }, + { debug_mode_default: false, auth_enabled: false }, + ); +} diff --git a/e2e/helpers/local-storage.ts b/e2e/helpers/local-storage.ts new file mode 100644 index 00000000..b5415078 --- /dev/null +++ b/e2e/helpers/local-storage.ts @@ -0,0 +1,52 @@ +import type { Page } from '@playwright/test'; + +const SETTINGS_KEY = 'template-ui-settings'; + +/** + * Inject localStorage settings before the page loads. + * Must be called before `page.goto()`. + * + * Merges the provided settings into the template-ui-settings key so + * individual fields (e.g. alwaysAllowedTools) can be set independently. + */ +export async function setLocalStorageSettings( + page: Page, + settings: Partial<{ + theme: 'light' | 'dark'; + debugMode: boolean; + alwaysAllowedTools: string[]; + autoApproveAllTools: boolean; + }>, +): Promise { + await page.addInitScript( + ({ key, value }) => { + let existing: Record = {}; + try { + const raw = localStorage.getItem(key); + if (raw) existing = JSON.parse(raw); + } catch { + // start fresh + } + localStorage.setItem(key, JSON.stringify({ ...existing, ...value })); + }, + { key: SETTINGS_KEY, value: settings }, + ); +} + +/** + * Read the stored template-ui-settings from localStorage after the page has + * loaded. Returns null when the key is not set. + */ +export async function getLocalStorageSettings( + page: Page, +): Promise | null> { + return page.evaluate((key) => { + const raw = localStorage.getItem(key); + if (!raw) return null; + try { + return JSON.parse(raw) as Record; + } catch { + return null; + } + }, SETTINGS_KEY); +} diff --git a/e2e/helpers/sse-mock.ts b/e2e/helpers/sse-mock.ts new file mode 100644 index 00000000..9f6994d5 --- /dev/null +++ b/e2e/helpers/sse-mock.ts @@ -0,0 +1,159 @@ +import type { Page, Route } from '@playwright/test'; + +/** Serialise one `data:` line for a token chunk the SSEProcessor accepts. */ +export function tokenChunk(text: string, chunkId: number): string { + return `data: ${JSON.stringify({ type: 'token', content: text, chunk_id: chunkId })}\n\n`; +} + +/** Serialise one `data:` line for an interrupt chunk the SSEProcessor accepts. */ +export function interruptChunk(value: object | string, chunkId: number): string { + return `data: ${JSON.stringify({ + type: 'interrupt', + content: { value, resumable: true }, + chunk_id: chunkId, + })}\n\n`; +} + +/** + * Install a browser-level route that intercepts the streaming endpoint and + * returns a minimal SSE response containing the provided text. + * + * Because Playwright intercepts at the CDP network layer the request never + * reaches the Fastify server, so no upstream AGENT_HOST calls are made. + */ +export async function mockAgentStream(page: Page, responseText: string): Promise { + await page.route('**/api/proxy/agent/v1/stream', (route) => { + const body = tokenChunk(responseText, 0) + 'data: [DONE]\n\n'; + return route.fulfill({ + status: 200, + headers: { + 'Content-Type': 'text/event-stream', + 'Cache-Control': 'no-cache', + 'Connection': 'keep-alive', + }, + body, + }); + }); +} + +/** Mock the agent health endpoint to return healthy so retry logic runs normally. */ +export async function mockAgentHealthy(page: Page): Promise { + await page.route('**/api/health/agent', (route) => + route.fulfill({ + status: 200, + contentType: 'application/json', + body: JSON.stringify({ status: 'healthy' }), + }), + ); +} + +/** Intercept thread state requests (used when navigating to an existing chat). */ +export async function mockThreadState(page: Page): Promise { + await page.route('**/api/proxy/agent/threads/*/state', (route) => + route.fulfill({ + status: 200, + contentType: 'application/json', + body: JSON.stringify({ messages: [], tasks: [] }), + }), + ); +} + +/** + * Intercept with a stream containing syntactically invalid SSE token data. + * The SSEProcessor should skip unrecognised chunks without crashing the UI. + */ +export async function mockMalformedStream(page: Page): Promise { + await page.route('**/api/proxy/agent/v1/stream', (route) => { + // Mix valid and invalid JSON; the SSEProcessor must tolerate both gracefully. + const body = + 'data: {this is not valid json}\n\n' + + tokenChunk('partial ok', 0) + + 'data: !!INVALID!!\n\n' + + 'data: [DONE]\n\n'; + return route.fulfill({ + status: 200, + headers: { + 'Content-Type': 'text/event-stream', + 'Cache-Control': 'no-cache', + 'Connection': 'keep-alive', + }, + body, + }); + }); +} + +/** + * Intercept the streaming endpoint and return the given HTTP error status. + * Useful for testing auth-failure (401) and server-error (500+) paths. + */ +export async function mockStreamError(page: Page, status: number): Promise { + await page.route('**/api/proxy/agent/v1/stream', (route) => + route.fulfill({ + status, + body: `HTTP ${status}`, + }), + ); +} + +/** + * Intercept the streaming endpoint and return a single interrupt chunk. + * The interrupt value can be a plain string (generic) or an HITLInterruptValue object. + */ +export async function mockInterruptStream( + page: Page, + value: object | string, +): Promise { + await page.route('**/api/proxy/agent/v1/stream', (route) => { + const body = interruptChunk(value, 0) + 'data: [DONE]\n\n'; + return route.fulfill({ + status: 200, + headers: { + 'Content-Type': 'text/event-stream', + 'Cache-Control': 'no-cache', + 'Connection': 'keep-alive', + }, + body, + }); + }); +} + +/** + * Override the streaming endpoint for a resume request to return a simple + * token response. Intercepts only the NEXT call — the route is unregistered + * after it fires once, so subsequent stream requests fall through to any + * previously registered handlers. + */ +export async function mockResumeStream(page: Page, responseText: string): Promise { + const handler = async (route: Route) => { + // Unregister before fulfilling so only the first matching request is intercepted + await page.unroute('**/api/proxy/agent/v1/stream', handler); + const body = tokenChunk(responseText, 0) + 'data: [DONE]\n\n'; + await route.fulfill({ + status: 200, + headers: { + 'Content-Type': 'text/event-stream', + 'Cache-Control': 'no-cache', + 'Connection': 'keep-alive', + }, + body, + }); + }; + await page.route('**/api/proxy/agent/v1/stream', handler); +} + +/** + * Intercept the streaming endpoint and return HTTP 429 with a Retry-After header. + * Used for testing the rate-limit UI. + */ +export async function mockRateLimitResponse(page: Page, retryAfterSeconds = 5): Promise { + await page.route('**/api/proxy/agent/v1/stream', (route) => + route.fulfill({ + status: 429, + headers: { + 'Retry-After': String(retryAfterSeconds), + 'Content-Type': 'text/plain', + }, + body: 'Rate limited', + }), + ); +} diff --git a/e2e/helpers/wait.ts b/e2e/helpers/wait.ts new file mode 100644 index 00000000..d049a78f --- /dev/null +++ b/e2e/helpers/wait.ts @@ -0,0 +1,22 @@ +import type { Page } from '@playwright/test'; + +/** + * Wait for the sr-only aria-live polite region (ChatPage.tsx) to contain at + * least one of the provided strings. The region announces "Agent is thinking", + * "Response complete", or "Stream error" as stream state changes. + */ +export async function waitForAnnouncement( + page: Page, + texts: string[], + timeout = 15_000, +): Promise { + await page.waitForFunction( + (expected: string[]) => { + const region = document.querySelector('.sr-only[aria-live="polite"]'); + const t = region?.textContent ?? ''; + return expected.some((s) => t.includes(s)); + }, + texts, + { timeout }, + ); +} diff --git a/e2e/hitl/auto-approve.spec.ts b/e2e/hitl/auto-approve.spec.ts new file mode 100644 index 00000000..3670080d --- /dev/null +++ b/e2e/hitl/auto-approve.spec.ts @@ -0,0 +1,137 @@ +import { test, expect } from '@playwright/test'; +import { mountConfig } from '../helpers/config-mount'; +import { setLocalStorageSettings } from '../helpers/local-storage'; +import { HomePage } from '../page-objects/HomePage'; +import { ChatPage } from '../page-objects/ChatPage'; + +/** + * Auto-approve tests: + * + * 1. When `alwaysAllowedTools` contains the tool from an action_requests interrupt, + * the resume is sent automatically without showing any approval UI. + * 2. When the tool is NOT in `alwaysAllowedTools` AND the interrupt is a non-actionable + * string value, the InterruptBanner appears for the user. + * 3. When `autoApproveAllTools` is true, every action_requests interrupt is auto-approved. + */ + +const TOOL_APPROVAL_INTERRUPT = { + action_requests: [{ name: 'github_search', args: { query: 'bugs' } }], + review_configs: [{ action_name: 'github_search', allowed_decisions: ['approve', 'reject'] }], +}; + +// A string interrupt (not action_requests) that triggers the InterruptBanner's +// "Action Required" branch (contains "approve"). Since it's a string, the +// ChatPage auto-approve useEffect skips it — the banner always appears. +const APPROVAL_STRING_INTERRUPT = + 'Do you approve running the dangerous_tool with the provided arguments?'; + +const UNKNOWN_TOOL_INTERRUPT = { + action_requests: [{ name: 'dangerous_tool', args: {} }], + review_configs: [{ action_name: 'dangerous_tool', allowed_decisions: ['approve', 'reject'] }], +}; + +async function makeInterruptStream( + value: object | string, + callCount: { n: number }, + page: import('@playwright/test').Page, +): Promise { + await page.route('**/api/proxy/agent/v1/stream', (route) => { + callCount.n++; + if (callCount.n === 1) { + const body = + `data: ${JSON.stringify({ + type: 'interrupt', + content: { value, resumable: true }, + chunk_id: 0, + })}\n\n` + 'data: [DONE]\n\n'; + return route.fulfill({ + status: 200, + headers: { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache' }, + body, + }); + } + const body = `data: ${JSON.stringify({ type: 'token', content: 'Done automatically.', chunk_id: 0 })}\n\ndata: [DONE]\n\n`; + return route.fulfill({ + status: 200, + headers: { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache' }, + body, + }); + }); +} + +test.describe('HITL — auto-approve', () => { + // ── auto-approve via alwaysAllowedTools ────────────────────────────────── + + test( + 'banner is skipped when the interrupted tool is in alwaysAllowedTools', + async ({ page }) => { + await setLocalStorageSettings(page, { alwaysAllowedTools: ['github_search'] }); + await mountConfig(page, { title: 'Auto Approve Test' }); + + const callCount = { n: 0 }; + await makeInterruptStream(TOOL_APPROVAL_INTERRUPT, callCount, page); + + const home = new HomePage(page); + await home.goto(); + await home.submitPrompt('Search GitHub issues'); + + const chat = new ChatPage(page); + await chat.expectChatRoute(); + + // The response should complete without the interrupt banner ever appearing + await chat.waitForAIResponse(15_000); + await expect(page.getByText('Action Required')).not.toBeVisible(); + await expect(page.getByText('Input Required')).not.toBeVisible(); + expect(callCount.n).toBeGreaterThanOrEqual(2); + }, + ); + + // ── banner still shown when interrupt is not auto-approvable ───────────── + // String interrupts bypass the auto-approve useEffect (which only handles + // action_requests objects), so the InterruptBanner always renders. + + test( + 'banner is shown when the interrupt is a string value (not auto-approved)', + async ({ page }) => { + await setLocalStorageSettings(page, { alwaysAllowedTools: ['github_search'] }); + await mountConfig(page, { title: 'Auto Approve Test' }); + + const callCount = { n: 0 }; + await makeInterruptStream(APPROVAL_STRING_INTERRUPT, callCount, page); + + const home = new HomePage(page); + await home.goto(); + await home.submitPrompt('Run the dangerous tool'); + + const chat = new ChatPage(page); + await chat.expectChatRoute(); + + // Banner MUST appear because string interrupts are never auto-approved + await expect(page.getByText('Action Required')).toBeVisible({ timeout: 15_000 }); + }, + ); + + // ── auto-approve all via autoApproveAllTools flag ───────────────────────── + + test( + 'banner is skipped when autoApproveAllTools is true, regardless of tool name', + async ({ page }) => { + await setLocalStorageSettings(page, { autoApproveAllTools: true }); + await mountConfig(page, { title: 'Auto Approve All Test' }); + + const callCount = { n: 0 }; + await makeInterruptStream(UNKNOWN_TOOL_INTERRUPT, callCount, page); + + const home = new HomePage(page); + await home.goto(); + await home.submitPrompt('Run all tools'); + + const chat = new ChatPage(page); + await chat.expectChatRoute(); + + await chat.waitForAIResponse(15_000); + await expect(page.getByText('Action Required')).not.toBeVisible(); + expect(callCount.n).toBeGreaterThanOrEqual(2); + }, + ); +}); diff --git a/e2e/hitl/interrupt-flow.spec.ts b/e2e/hitl/interrupt-flow.spec.ts new file mode 100644 index 00000000..322940d6 --- /dev/null +++ b/e2e/hitl/interrupt-flow.spec.ts @@ -0,0 +1,187 @@ +import { test, expect } from '@playwright/test'; +import { mountConfig } from '../helpers/config-mount'; +import { HomePage } from '../page-objects/HomePage'; +import { ChatPage } from '../page-objects/ChatPage'; + +/** + * Human-In-The-Loop (HITL) interrupt flow tests. + * + * The InterruptBanner component is rendered via `interruptContent` in ChatPage + * for NON-action_requests interrupts (strings and MCP auth). + * For string interrupt values, isToolApproval() decides which branch: + * - Contains "approve" / "confirm" / "allow" etc → "Action Required" (Approve/Reject buttons) + * - Otherwise → "Input Required" (text input + Send button) + */ + +// String interrupt that triggers the "Action Required" branch (contains "approve") +const TOOL_APPROVAL_STR = 'Do you approve running github_create_pr with title "Test PR"?'; + +// String interrupt that triggers the "Input Required" branch (no approval keywords) +const GENERIC_INTERRUPT_VALUE = 'What format should the output be in?'; + +function makeInterruptBody(value: string | object, chunkId = 0): string { + return ( + `data: ${JSON.stringify({ + type: 'interrupt', + content: { value, resumable: true }, + chunk_id: chunkId, + })}\n\n` + 'data: [DONE]\n\n' + ); +} + +test.describe('HITL — interrupt banner rendering', () => { + test.beforeEach(async ({ page }) => { + await mountConfig(page, { title: 'HITL Test Agent' }); + }); + + // ── Tool approval ───────────────────────────────────────────────────────── + + test('renders the interrupt banner for a tool-approval interrupt', async ({ page }) => { + await page.route('**/api/proxy/agent/v1/stream', (route) => + route.fulfill({ + status: 200, + headers: { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache' }, + body: makeInterruptBody(TOOL_APPROVAL_STR), + }), + ); + + const home = new HomePage(page); + await home.goto(); + await home.submitPrompt('Create a pull request'); + + const chat = new ChatPage(page); + await chat.expectChatRoute(); + + await expect(page.getByText('Action Required')).toBeVisible({ timeout: 15_000 }); + await expect(page.getByRole('button', { name: /approve/i })).toBeVisible(); + await expect(page.getByRole('button', { name: /reject/i })).toBeVisible(); + }); + + test('clicking Approve sends a resume stream request and removes the banner', async ({ + page, + }) => { + let callCount = 0; + await page.route('**/api/proxy/agent/v1/stream', (route) => { + callCount++; + if (callCount === 1) { + return route.fulfill({ + status: 200, + headers: { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache' }, + body: makeInterruptBody(TOOL_APPROVAL_STR), + }); + } + const body = `data: ${JSON.stringify({ type: 'token', content: 'PR created!', chunk_id: 0 })}\n\ndata: [DONE]\n\n`; + return route.fulfill({ + status: 200, + headers: { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache' }, + body, + }); + }); + + const home = new HomePage(page); + await home.goto(); + await home.submitPrompt('Create a pull request'); + + const chat = new ChatPage(page); + await chat.expectChatRoute(); + await expect(page.getByText('Action Required')).toBeVisible({ timeout: 15_000 }); + + await page.getByRole('button', { name: /approve/i }).click(); + + // Banner should disappear once resume is sent + await expect(page.getByText('Action Required')).not.toBeVisible({ timeout: 10_000 }); + expect(callCount).toBeGreaterThanOrEqual(2); + }); + + test('clicking Reject sends a reject resume and removes the banner', async ({ page }) => { + let callCount = 0; + await page.route('**/api/proxy/agent/v1/stream', (route) => { + callCount++; + if (callCount === 1) { + return route.fulfill({ + status: 200, + headers: { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache' }, + body: makeInterruptBody(TOOL_APPROVAL_STR), + }); + } + const body = `data: ${JSON.stringify({ type: 'token', content: 'Skipped.', chunk_id: 0 })}\n\ndata: [DONE]\n\n`; + return route.fulfill({ + status: 200, + headers: { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache' }, + body, + }); + }); + + const home = new HomePage(page); + await home.goto(); + await home.submitPrompt('Create a pull request'); + + const chat = new ChatPage(page); + await chat.expectChatRoute(); + await expect(page.getByText('Action Required')).toBeVisible({ timeout: 15_000 }); + + await page.getByRole('button', { name: /reject/i }).click(); + await expect(page.getByText('Action Required')).not.toBeVisible({ timeout: 10_000 }); + expect(callCount).toBeGreaterThanOrEqual(2); + }); + + // ── Generic text interrupt ────────────────────────────────────────────── + + test('renders "Input Required" for a generic text interrupt', async ({ page }) => { + await page.route('**/api/proxy/agent/v1/stream', (route) => + route.fulfill({ + status: 200, + headers: { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache' }, + body: makeInterruptBody(GENERIC_INTERRUPT_VALUE), + }), + ); + + const home = new HomePage(page); + await home.goto(); + await home.submitPrompt('Run the analysis'); + + const chat = new ChatPage(page); + await chat.expectChatRoute(); + + await expect(page.getByText('Input Required')).toBeVisible({ timeout: 15_000 }); + await expect(page.getByRole('textbox', { name: /interrupt response/i })).toBeVisible(); + // Use exact name to avoid matching the chat's "Send message" submit button + await expect(page.getByRole('button', { name: 'Send', exact: true })).toBeVisible(); + }); + + test('Send button submits the typed text as the resume response', async ({ page }) => { + let callCount = 0; + await page.route('**/api/proxy/agent/v1/stream', (route) => { + callCount++; + if (callCount === 1) { + return route.fulfill({ + status: 200, + headers: { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache' }, + body: makeInterruptBody(GENERIC_INTERRUPT_VALUE), + }); + } + const body = `data: ${JSON.stringify({ type: 'token', content: 'Got it.', chunk_id: 0 })}\n\ndata: [DONE]\n\n`; + return route.fulfill({ + status: 200, + headers: { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache' }, + body, + }); + }); + + const home = new HomePage(page); + await home.goto(); + await home.submitPrompt('Run the analysis'); + + const chat = new ChatPage(page); + await chat.expectChatRoute(); + await expect(page.getByText('Input Required')).toBeVisible({ timeout: 15_000 }); + + const input = page.getByRole('textbox', { name: /interrupt response/i }); + await input.fill('JSON format please'); + await page.getByRole('button', { name: 'Send', exact: true }).click(); + + // Banner gone, second stream call made + await expect(page.getByText('Input Required')).not.toBeVisible({ timeout: 10_000 }); + expect(callCount).toBeGreaterThanOrEqual(2); + }); +}); diff --git a/e2e/page-objects/ChatPage.ts b/e2e/page-objects/ChatPage.ts new file mode 100644 index 00000000..9748b1e6 --- /dev/null +++ b/e2e/page-objects/ChatPage.ts @@ -0,0 +1,49 @@ +import type { Page } from '@playwright/test'; +import { expect } from '@playwright/test'; + +/** Page object for the chat view (`/chat/:threadId`). */ +export class ChatPage { + constructor(private readonly page: Page) {} + + async waitForLoad(): Promise { + // The chat input becomes visible once the page has mounted + await this.page.waitForSelector('textarea', { state: 'visible', timeout: 10_000 }); + } + + /** + * Wait for the streaming loading indicator to disappear and for at least + * one AI message bubble to appear in the DOM. + */ + async waitForAIResponse(timeout = 15_000): Promise { + // The sr-only aria-live region in ChatPage.tsx announces "Response complete". + // Use the .sr-only qualifier to avoid matching the chat messages live-log element. + await this.page.waitForFunction( + () => { + const liveRegion = document.querySelector('.sr-only[aria-live="polite"]'); + return liveRegion?.textContent?.includes('Response complete'); + }, + { timeout }, + ); + } + + /** Returns true when the current URL matches a chat route. */ + async isOnChatRoute(): Promise { + return /\/chat\//.test(this.page.url()); + } + + /** Asserts the current URL contains a chat thread segment. */ + async expectChatRoute(): Promise { + await expect(this.page).toHaveURL(/\/chat\//); + } + + /** Fills the chat input on the chat page and clicks submit — mirrors HomePage.submitPrompt. */ + async sendMessage(text: string): Promise { + await this.page.locator('textarea').fill(text); + await this.page.locator('button[type="submit"]').click(); + } + + /** Returns the full visible text content of the page — useful for asserting response text. */ + async getBodyText(): Promise { + return this.page.locator('body').innerText(); + } +} diff --git a/e2e/page-objects/HomePage.ts b/e2e/page-objects/HomePage.ts new file mode 100644 index 00000000..7d812407 --- /dev/null +++ b/e2e/page-objects/HomePage.ts @@ -0,0 +1,28 @@ +import type { Page } from '@playwright/test'; + +/** Page object for the homepage (`/`). */ +export class HomePage { + constructor(private readonly page: Page) {} + + async goto(): Promise { + await this.page.goto('/'); + await this.page.waitForLoadState('networkidle'); + await this.page.waitForSelector('textarea', { state: 'visible' }); + } + + /** Returns the browser document title set from the branding config. */ + async getDocumentTitle(): Promise { + return this.page.title(); + } + + /** Fills the chat input and clicks the submit button. */ + async submitPrompt(text: string): Promise { + await this.page.locator('textarea').fill(text); + await this.page.locator('button[type="submit"]').click(); + } + + /** Clicks a quick-prompt chip button that contains the given text. */ + async clickQuickPrompt(text: string): Promise { + await this.page.getByText(text, { exact: false }).first().click(); + } +} diff --git a/e2e/page-objects/SettingsPage.ts b/e2e/page-objects/SettingsPage.ts new file mode 100644 index 00000000..c666d3ce --- /dev/null +++ b/e2e/page-objects/SettingsPage.ts @@ -0,0 +1,37 @@ +import type { Page } from '@playwright/test'; +import { expect } from '@playwright/test'; + +type SettingsTab = 'Profile' | 'Memories' | 'Custom Rules' | 'Appearance' | 'Tool Approvals'; + +/** Page object for the settings view (`/settings`). */ +export class SettingsPage { + constructor(private readonly page: Page) {} + + async goto(): Promise { + await this.page.goto('/settings'); + await this.page.waitForSelector('h1', { state: 'visible' }); + } + + async getHeading(): Promise { + return this.page.locator('h1').innerText(); + } + + async clickTab(tab: SettingsTab): Promise { + await this.page.getByRole('tab', { name: tab, exact: false }).click(); + } + + async selectTab(tab: SettingsTab): Promise { + await this.clickTab(tab); + await this.page.waitForSelector('[role="tabpanel"]:not([hidden])', { state: 'visible' }); + } + + async expectTabContentVisible(heading: SettingsTab): Promise { + await expect(this.page.getByRole('heading', { name: heading, exact: false }).first()).toBeVisible(); + } + + /** Navigate back to the home page via the back arrow button. */ + async navigateBack(): Promise { + await this.page.getByRole('button', { name: 'Back' }).click(); + await expect(this.page).toHaveURL('/'); + } +} diff --git a/e2e/settings/settings-page.spec.ts b/e2e/settings/settings-page.spec.ts new file mode 100644 index 00000000..08584bbf --- /dev/null +++ b/e2e/settings/settings-page.spec.ts @@ -0,0 +1,112 @@ +import { test, expect } from '@playwright/test'; +import { mountConfig } from '../helpers/config-mount'; +import { SettingsPage } from '../page-objects/SettingsPage'; +import { setLocalStorageSettings, getLocalStorageSettings } from '../helpers/local-storage'; + +/** + * Settings page tests: + * + * 1. Settings page loads with the expected heading. + * 2. All tabs are navigable. + * 3. Theme toggle persists to localStorage. + * 4. "Tool Approvals" tab shows always-allowed tools list. + * 5. Adding a tool to always-allowed saves to localStorage. + */ + +test.describe('Settings page', () => { + test.beforeEach(async ({ page }) => { + await mountConfig(page, { title: 'Settings Test' }); + }); + + // ── Basic load ───────────────────────────────────────────────────────────── + + test('settings page loads with the Settings heading', async ({ page }) => { + const settings = new SettingsPage(page); + await settings.goto(); + const heading = await settings.getHeading(); + expect(heading.toLowerCase()).toContain('settings'); + }); + + test('settings page can be reached via the /settings route', async ({ page }) => { + await mountConfig(page); + await page.goto('/settings'); + await expect(page).toHaveURL(/\/settings/); + await expect(page.locator('h1')).toBeVisible(); + }); + + // ── Tab navigation ───────────────────────────────────────────────────────── + + test('Profile tab is visible and active by default', async ({ page }) => { + const settings = new SettingsPage(page); + await settings.goto(); + await settings.expectTabContentVisible('Profile'); + }); + + test('clicking Appearance tab shows the Appearance section', async ({ page }) => { + const settings = new SettingsPage(page); + await settings.goto(); + await settings.clickTab('Appearance'); + await settings.expectTabContentVisible('Appearance'); + }); + + test('clicking Tool Approvals tab shows the tool list', async ({ page }) => { + const settings = new SettingsPage(page); + await settings.goto(); + await settings.clickTab('Tool Approvals'); + await settings.expectTabContentVisible('Tool Approvals'); + }); + + test('clicking Memories tab shows the Memories section', async ({ page }) => { + const settings = new SettingsPage(page); + await settings.goto(); + await settings.clickTab('Memories'); + await settings.expectTabContentVisible('Memories'); + }); + + // ── Theme toggle + localStorage persistence ──────────────────────────────── + + test('toggling the theme persists the selection to localStorage', async ({ page }) => { + const settings = new SettingsPage(page); + await settings.goto(); + await settings.clickTab('Appearance'); + + // Toggle to "Dark" theme + const darkOption = page.getByRole('radio', { name: /dark/i }); + await darkOption.click(); + + const stored = await getLocalStorageSettings(page); + expect(stored?.theme).toBe('dark'); + + // Toggle back to "Light" + const lightOption = page.getByRole('radio', { name: /light/i }); + await lightOption.click(); + + const stored2 = await getLocalStorageSettings(page); + expect(stored2?.theme).toBe('light'); + }); + + // ── Pre-populated settings from localStorage ─────────────────────────────── + + test('pre-populated alwaysAllowedTools from localStorage are shown in Tool Approvals', async ({ + page, + }) => { + await setLocalStorageSettings(page, { alwaysAllowedTools: ['web_search', 'github_search'] }); + await mountConfig(page, { title: 'Tool Approvals Test' }); + + const settings = new SettingsPage(page); + await settings.goto(); + await settings.clickTab('Tool Approvals'); + + await expect(page.getByText('web_search')).toBeVisible(); + await expect(page.getByText('github_search')).toBeVisible(); + }); + + // ── Back navigation ──────────────────────────────────────────────────────── + + test('back button navigates to the home page', async ({ page }) => { + const settings = new SettingsPage(page); + await settings.goto(); + await settings.navigateBack(); + await expect(page).toHaveURL('/'); + }); +}); diff --git a/e2e/smoke/chat-flow.spec.ts b/e2e/smoke/chat-flow.spec.ts new file mode 100644 index 00000000..da69d8b2 --- /dev/null +++ b/e2e/smoke/chat-flow.spec.ts @@ -0,0 +1,46 @@ +import { test, expect } from '@playwright/test'; +import { HomePage } from '../page-objects/HomePage'; +import { ChatPage } from '../page-objects/ChatPage'; +import { mountConfig } from '../helpers/config-mount'; +import { mockAgentStream } from '../helpers/sse-mock'; + +const MOCK_RESPONSE = 'Hello from the test agent!'; + +test.describe('Chat flow smoke test', () => { + test.beforeEach(async ({ page }) => { + await mountConfig(page, { title: 'Test Agent' }); + await mockAgentStream(page, MOCK_RESPONSE); + }); + + test('home page loads with textarea and submit button', async ({ page }) => { + const home = new HomePage(page); + await home.goto(); + + await expect(page.locator('textarea')).toBeVisible(); + await expect(page.locator('button[type="submit"]')).toBeVisible(); + }); + + test('submitting a prompt navigates to the chat route', async ({ page }) => { + const home = new HomePage(page); + const chat = new ChatPage(page); + + await home.goto(); + await home.submitPrompt('What can you do?'); + + await chat.expectChatRoute(); + }); + + test('chat page shows AI response after streaming completes', async ({ page }) => { + const home = new HomePage(page); + const chat = new ChatPage(page); + + await home.goto(); + await home.submitPrompt('Tell me something interesting.'); + + await chat.expectChatRoute(); + await chat.waitForAIResponse(); + + const bodyText = await chat.getBodyText(); + expect(bodyText).toContain(MOCK_RESPONSE); + }); +}); diff --git a/env.template b/env.template index c97b7f19..bc7935d5 100644 --- a/env.template +++ b/env.template @@ -1,4 +1,4 @@ -PORT=5003 +PORT=8080 ENVIRONMENT=development COOKIE_SIGN=your-secret-with-minimum-length-of-32-characters @@ -6,6 +6,26 @@ AUTH_ENABLED=false SSO_CLIENT_ID=your-sso-client-id SSO_CLIENT_SECRET=your-sso-client-secret SSO_ISSUER_HOST=https://your-sso-provider.com -SSO_CALLBACK_URL=http://localhost:5003/auth/callback/oidc +SSO_CALLBACK_URL=http://localhost:8080/auth/callback/oidc AGENT_HOST=http://localhost:5002 + +CORS_ORIGIN=http://localhost:8080 + +# Config file path (optional, defaults to config/ui/settings.yaml) +UI_CONFIG_PATH=config/ui/settings.yaml + +# Environment variable overrides for config (optional) +# BRANDING_TITLE="My Custom Agent" +# BRANDING_LOGO_URL="/custom-logo.svg" +# BRANDING_FAVICON_URL="/custom-favicon.ico" +# FEATURE_AUTH_ENABLED=true +# FEATURE_DEBUG_MODE_DEFAULT=false +# AGENT_ENDPOINT=https://custom-agent.example.com +# AGENT_TIMEOUT_MS=30000 + +# Redis (started by `make local` via compose.yml) +REDIS_HOST=localhost +REDIS_PORT=6380 +REDIS_PASSWORD= +REDIS_TLS=false diff --git a/eslint.config.js b/eslint.config.js index caf6a4b7..c7a28161 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -24,6 +24,13 @@ export default tseslint.config( { allowConstantExport: true }, ], '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }], + }, + }, + { + files: ['src/frontend/test-utils/**/*.{ts,tsx}', '**/*.test.{ts,tsx}'], + rules: { + 'react-refresh/only-export-components': 'off', }, }, ) diff --git a/index.html b/index.html index 304d7d6f..504cfeb7 100644 --- a/index.html +++ b/index.html @@ -1,13 +1,28 @@ - + Template UI -
- - \ No newline at end of file + diff --git a/package-lock.json b/package-lock.json index a03d841c..b2b624a8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,59 +1,269 @@ { "name": "template-ui", - "version": "0.0.0", + "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "template-ui", - "version": "0.0.0", + "version": "0.1.0", "dependencies": { "@fastify/cookie": "^11.0.2", "@fastify/cors": "^11.1.0", + "@fastify/helmet": "^13.0.2", "@fastify/oauth2": "^8.1.2", + "@fastify/rate-limit": "^10.3.0", "@fastify/session": "^11.1.0", - "@fastify/static": "^8.2.0", + "@fastify/static": "^10.1.3", "@fastify/url-data": "^6.0.3", "@langchain/core": "^0.3.55", - "@langchain/langgraph-sdk": "^0.0.74", - "@radix-ui/react-scroll-area": "^1.2.8", - "@radix-ui/react-select": "^2.2.4", - "@radix-ui/react-slot": "^1.2.2", - "@radix-ui/react-tabs": "^1.1.11", - "@radix-ui/react-tooltip": "^1.2.6", + "@langchain/langgraph-sdk": "^1.9.2", + "@open-policy-agent/opa-wasm": "^1.10.0", + "@opentelemetry/auto-instrumentations-node": "^0.76.0", + "@opentelemetry/exporter-metrics-otlp-http": "^0.218.0", + "@opentelemetry/exporter-trace-otlp-http": "^0.218.0", + "@opentelemetry/resources": "^2.7.1", + "@opentelemetry/sdk-node": "^0.218.0", + "@opentelemetry/semantic-conventions": "^1.41.1", + "@patternfly/chatbot": "^6.5.0", + "@patternfly/patternfly": "^6.4.0", + "@patternfly/react-core": "^6.4.3", + "@patternfly/react-icons": "^6.4.0", + "@patternfly/react-styles": "^6.4.0", + "@patternfly/react-table": "^6.4.3", + "@reduxjs/toolkit": "^2.11.2", "@tailwindcss/vite": "^4.1.5", "@types/uuid": "^10.0.0", - "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "fastify": "^5.5.0", "fastify-plugin": "^5.0.1", + "ioredis": "^5.10.1", + "js-yaml": "^4.3.1", "lucide-react": "^0.508.0", "react": "^19.0.0", "react-dom": "^19.0.0", "react-markdown": "^9.0.3", + "react-redux": "^9.2.0", "react-router-dom": "^7.5.3", + "rehype-highlight": "^7.0.2", + "rehype-sanitize": "^6.0.0", + "remark-gfm": "^4.0.1", "tailwind-merge": "^3.2.0", "tailwindcss": "^4.1.5", "uuid": "^11.1.0" }, "devDependencies": { + "@axe-core/playwright": "^4.12.1", "@eslint/js": "^9.22.0", + "@playwright/test": "^1.61.1", + "@testing-library/jest-dom": "^6.9.1", + "@testing-library/react": "^16.3.2", + "@testing-library/user-event": "^14.6.1", + "@types/jest-axe": "^3.5.9", + "@types/js-yaml": "^4.0.9", "@types/node": "^22.17.2", "@types/react": "^19.1.2", "@types/react-dom": "^19.1.3", + "@types/react-redux": "^7.1.34", "@vitejs/plugin-react-swc": "^3.9.0", + "@vitest/coverage-v8": "^4.1.10", "concurrently": "^9.2.0", "eslint": "^9.22.0", "eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-refresh": "^0.4.19", "globals": "^16.0.0", + "jest-axe": "^10.0.0", + "jsdom": "^29.1.1", "nodemon": "^3.1.10", "pino-pretty": "^13.1.1", "tsx": "^4.20.4", "tw-animate-css": "^1.2.9", "typescript": "~5.7.2", "typescript-eslint": "^8.26.1", - "vite": "^6.3.4" + "vite": "^6.3.4", + "vitest": "^4.1.6" + }, + "engines": { + "node": ">=22.13.0" + } + }, + "node_modules/@adobe/css-tools": { + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.4.tgz", + "integrity": "sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@asamuzakjp/css-color": { + "version": "5.1.11", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.1.11.tgz", + "integrity": "sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/generational-cache": "^1.0.1", + "@csstools/css-calc": "^3.2.0", + "@csstools/css-color-parser": "^4.1.0", + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/@asamuzakjp/dom-selector": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-7.1.1.tgz", + "integrity": "sha512-67RZDnYRc8H/8MLDgQCDE//zoqVFwajkepHZgmXrbwybzXOEwOWGPYGmALYl9J2DOLfFPPs6kKCqmbzV895hTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/generational-cache": "^1.0.1", + "@asamuzakjp/nwsapi": "^2.3.9", + "bidi-js": "^1.0.3", + "css-tree": "^3.2.1", + "is-potential-custom-element-name": "^1.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/@asamuzakjp/generational-cache": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/generational-cache/-/generational-cache-1.0.1.tgz", + "integrity": "sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/@asamuzakjp/nwsapi": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", + "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@axe-core/playwright": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@axe-core/playwright/-/playwright-4.12.1.tgz", + "integrity": "sha512-rMd7xriptqKpP+w5265i4Hdkv2X5kbu6uiBi/B2I7uf3hieRBM3qDCfaKPtxfiYb2mKXfF+yLODJwIx+Jv1GDw==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "axe-core": "~4.12.1" + }, + "peerDependencies": { + "playwright-core": ">= 1.0.0" + } + }, + "node_modules/@axe-core/playwright/node_modules/axe-core": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.12.1.tgz", + "integrity": "sha512-s7iGf5GaVMxEG0ENN9x+xTr7GFZCb1ZP/1uATUpCEK2X78nDB3RwbtFCo9pGAf9ru+VwoQ464DkaLEeRM08wJA==", + "dev": true, + "license": "MPL-2.0", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", + "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@bramus/specificity": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@bramus/specificity/-/specificity-2.4.2.tgz", + "integrity": "sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "css-tree": "^3.0.0" + }, + "bin": { + "specificity": "bin/cli.js" } }, "node_modules/@cfworker/json-schema": { @@ -62,6 +272,146 @@ "integrity": "sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==", "license": "MIT" }, + "node_modules/@csstools/color-helpers": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.0.2.tgz", + "integrity": "sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/@csstools/css-calc": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.2.0.tgz", + "integrity": "sha512-bR9e6o2BDB12jzN/gIbjHa5wLJ4UjD1CB9pM7ehlc0ddk6EBz+yYS1EV2MF55/HUxrHcB/hehAyt5vhsA3hx7w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.1.0.tgz", + "integrity": "sha512-U0KhLYmy2GVj6q4T3WaAe6NPuFYCPQoE3b0dRGxejWDgcPp8TP7S5rVdM5ZrFaqu4N67X8YaPBw14dQSYx3IyQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^6.0.2", + "@csstools/css-calc": "^3.2.0" + }, + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz", + "integrity": "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-syntax-patches-for-csstree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.3.tgz", + "integrity": "sha512-SH60bMfrRCJF3morcdk57WklujF4Jr/EsQUzqkarfHXEFcAR1gg7fS/chAE922Sehgzc1/+Tz5H3Ypa1HiEKrg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "peerDependencies": { + "css-tree": "^3.2.1" + }, + "peerDependenciesMeta": { + "css-tree": { + "optional": true + } + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz", + "integrity": "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.27.4", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.4.tgz", @@ -635,6 +985,24 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@exodus/bytes": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.0.tgz", + "integrity": "sha512-UY0nlA+feH81UGSHv92sLEPLCeZFjXOuHhrIo0HQydScuQc8s0A7kL/UdgwgDq8g8ilksmuoF35YVTNphV2aBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@noble/hashes": "^1.8.0 || ^2.0.0" + }, + "peerDependenciesMeta": { + "@noble/hashes": { + "optional": true + } + } + }, "node_modules/@fastify/accept-negotiator": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@fastify/accept-negotiator/-/accept-negotiator-2.0.1.tgz", @@ -785,6 +1153,26 @@ ], "license": "MIT" }, + "node_modules/@fastify/helmet": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/@fastify/helmet/-/helmet-13.0.2.tgz", + "integrity": "sha512-tO1QMkOfNeCt9l4sG/FiWErH4QMm+RjHzbMTrgew1DYOQ2vb/6M1G2iNABBrD7Xq6dUk+HLzWW8u+rmmhQHifA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "fastify-plugin": "^5.0.0", + "helmet": "^8.0.0" + } + }, "node_modules/@fastify/merge-json-schemas": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/@fastify/merge-json-schemas/-/merge-json-schemas-0.2.1.tgz", @@ -845,6 +1233,27 @@ "ipaddr.js": "^2.1.0" } }, + "node_modules/@fastify/rate-limit": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@fastify/rate-limit/-/rate-limit-10.3.0.tgz", + "integrity": "sha512-eIGkG9XKQs0nyynatApA3EVrojHOuq4l6fhB4eeCk4PIOeadvOJz9/4w3vGI44Go17uaXOWEcPkaD8kuKm7g6Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "@lukeed/ms": "^2.0.2", + "fastify-plugin": "^5.0.0", + "toad-cache": "^3.7.0" + } + }, "node_modules/@fastify/send": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/@fastify/send/-/send-4.1.0.tgz", @@ -889,9 +1298,9 @@ } }, "node_modules/@fastify/static": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/@fastify/static/-/static-8.3.0.tgz", - "integrity": "sha512-yKxviR5PH1OKNnisIzZKmgZSus0r2OZb8qCSbqmw34aolT4g3UlzYfeBRym+HJ1J471CR8e2ldNub4PubD1coA==", + "version": "10.1.3", + "resolved": "https://registry.npmjs.org/@fastify/static/-/static-10.1.3.tgz", + "integrity": "sha512-W6jqajYS974XjPjB5hQWoxPM8NKM4+p8YmQT6G5IbCa4uhdWSVadZUv75siy1wEA/3ty8RYdpBydfWeu9AqAqQ==", "funding": [ { "type": "github", @@ -905,13 +1314,30 @@ "license": "MIT", "dependencies": { "@fastify/accept-negotiator": "^2.0.0", + "@fastify/error": "^4.0.0", "@fastify/send": "^4.0.0", - "content-disposition": "^0.5.4", - "fastify-plugin": "^5.0.0", + "content-disposition": "^2.0.1", + "fastify-plugin": "^6.0.0", "fastq": "^1.17.1", - "glob": "^11.0.0" + "glob": "^13.0.0" } }, + "node_modules/@fastify/static/node_modules/fastify-plugin": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/fastify-plugin/-/fastify-plugin-6.0.0.tgz", + "integrity": "sha512-fZOty7z3O7vOliF6d8bHE3wiEh1KcNnKEQensSgTk9C1DvN6nRLS++XVd86v33Hw/8u9Un8A1zDrQ8ujcQDHEg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, "node_modules/@fastify/url-data": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/@fastify/url-data/-/url-data-6.0.3.tgz", @@ -932,44 +1358,37 @@ "fastify-plugin": "^5.0.0" } }, - "node_modules/@floating-ui/core": { - "version": "1.7.5", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.5.tgz", - "integrity": "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==", - "license": "MIT", + "node_modules/@grpc/grpc-js": { + "version": "1.14.4", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.4.tgz", + "integrity": "sha512-k9Dj3DV/itK9D06Y8f190Qgop7/Ui+D0njFV3LHMPwPT75DpXLQohE9Wmz0QElrJnzsjB7KPWiKJbOl7IPDArQ==", + "license": "Apache-2.0", "dependencies": { - "@floating-ui/utils": "^0.2.11" + "@grpc/proto-loader": "^0.8.0", + "@js-sdsl/ordered-map": "^4.4.2" + }, + "engines": { + "node": ">=12.10.0" } }, - "node_modules/@floating-ui/dom": { - "version": "1.7.6", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.6.tgz", - "integrity": "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==", - "license": "MIT", + "node_modules/@grpc/proto-loader": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.8.1.tgz", + "integrity": "sha512-wtF6h+DY6M3YaDBPAmvuuA6jV8Sif9MjtOI5euKFWRgCDl5PeDpPsHR9u2l6St5ceY8AZgoNDww5+HvEsXFsGg==", + "license": "Apache-2.0", "dependencies": { - "@floating-ui/core": "^1.7.5", - "@floating-ui/utils": "^0.2.11" - } - }, - "node_modules/@floating-ui/react-dom": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.8.tgz", - "integrity": "sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==", - "license": "MIT", - "dependencies": { - "@floating-ui/dom": "^1.7.6" + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.5.5", + "yargs": "^17.7.2" }, - "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" } }, - "node_modules/@floating-ui/utils": { - "version": "0.2.11", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.11.tgz", - "integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==", - "license": "MIT" - }, "node_modules/@hapi/boom": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/@hapi/boom/-/boom-10.0.1.tgz", @@ -1007,9 +1426,9 @@ "license": "BSD-3-Clause" }, "node_modules/@hapi/wreck": { - "version": "18.1.0", - "resolved": "https://registry.npmjs.org/@hapi/wreck/-/wreck-18.1.0.tgz", - "integrity": "sha512-0z6ZRCmFEfV/MQqkQomJ7sl/hyxvcZM7LtuVqN3vdAO4vM9eBbowl0kaqQj9EJJQab+3Uuh1GxbGIBFy4NfJ4w==", + "version": "18.1.2", + "resolved": "https://registry.npmjs.org/@hapi/wreck/-/wreck-18.1.2.tgz", + "integrity": "sha512-3dMnV2pfhQiyEqu8DL3VBmxkdLiRDiiUDuG79Dp+UK1gL9ZxAfDOUhB6k3D5MLqcgJJ1IARyGFhwoc1NITr/pg==", "license": "BSD-3-Clause", "dependencies": { "@hapi/boom": "^10.0.1", @@ -1069,13 +1488,89 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@isaacs/cliui": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-9.0.0.tgz", - "integrity": "sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==", - "license": "BlueOak-1.0.0", + "node_modules/@ioredis/commands": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.5.1.tgz", + "integrity": "sha512-JH8ZL/ywcJyR9MmJ5BNqZllXNZQqQbnVZOqpPQqE1vHiFgAw4NHbvE0FOduNU8IX9babitBT46571OnPTT0Zcw==", + "license": "MIT" + }, + "node_modules/@jest/diff-sequences": { + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.4.0.tgz", + "integrity": "sha512-zOpzlfUs45l6u7jm39qr87JCHUDsaeCtvL+kQe/Vn9jSnRB4/5IPXISm0h9I1vZW/o00Kn4UTJ2MOlhnUGwv3g==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=18" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.4.1.tgz", + "integrity": "sha512-ZBn5CglH8fBsQsvs4VWNzD4aWfUYks+IdOOQU3MEK71ol/BcVm+P+rtb1KpiFBpSWSCE27uOahyyf1vfqOVbcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/get-type": { + "version": "30.1.0", + "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz", + "integrity": "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/pattern": { + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.4.0.tgz", + "integrity": "sha512-RAWn3+f9u8BsHijKJ71uHcFp6vmyEt6VvoWXkl6hKF3qVIuWNmudVjg12DlBPGup/frIl5UcUlH5HfEuvHpEXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-regex-util": "30.4.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/schemas": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.4.1.tgz", + "integrity": "sha512-i6b4qw5qnP8c5FEeBJg/uZQ4ddrkN6Ca8qISJh0pr7a5hfn3h3v5x60BEbOC7OYAGZNMs1LfFLwnW2CuK8F57Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.34.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/types": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.4.1.tgz", + "integrity": "sha512-f1x/vJXIfjOlEmejYpbkbgw1gOqpPECwMvMEtBqe47j7H2Hg8h8w3o3ikhSXq3MI15kg+oQ0exWO0uCtTNJLoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/pattern": "30.4.0", + "@jest/schemas": "30.4.1", + "@types/istanbul-lib-coverage": "^2.0.6", + "@types/istanbul-reports": "^3.0.4", + "@types/node": "*", + "@types/yargs": "^17.0.33", + "chalk": "^4.1.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/@jridgewell/gen-mapping": { @@ -1123,6 +1618,16 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@js-sdsl/ordered-map": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", + "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, "node_modules/@langchain/core": { "version": "0.3.80", "resolved": "https://registry.npmjs.org/@langchain/core/-/core-0.3.80.tgz", @@ -1160,775 +1665,2259 @@ } }, "node_modules/@langchain/langgraph-sdk": { - "version": "0.0.74", - "resolved": "https://registry.npmjs.org/@langchain/langgraph-sdk/-/langgraph-sdk-0.0.74.tgz", - "integrity": "sha512-IUN0m4BYkGWdviFd4EaWDcQgxNq8z+1LIwXajCSt9B+Cb/pz0ZNpIPdu5hAIsf6a0RWu5yRUhzL1L40t7vu3Zg==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@langchain/langgraph-sdk/-/langgraph-sdk-1.9.2.tgz", + "integrity": "sha512-1kDPjR0VH/39q2h8k0Sxi35KxOvEQPModVCepxGLlRkbZmuWUH+zfICuJd3rmD1ByeOKQBZEaB7Y+VCYmSMt1w==", "license": "MIT", "dependencies": { + "@langchain/core": "^1.1.44", + "@langchain/protocol": "^0.0.15", "@types/json-schema": "^7.0.15", - "p-queue": "^6.6.2", - "p-retry": "4", - "uuid": "^9.0.0" + "p-queue": "^9.0.1", + "p-retry": "^7.1.1", + "uuid": "^13.0.0" }, "peerDependencies": { - "@langchain/core": ">=0.2.31 <0.4.0", - "react": "^18 || ^19" + "react": "^18 || ^19", + "react-dom": "^18 || ^19", + "svelte": "^4.0.0 || ^5.0.0", + "vue": "^3.0.0" }, "peerDependenciesMeta": { - "@langchain/core": { + "react": { "optional": true }, - "react": { + "react-dom": { + "optional": true + }, + "svelte": { + "optional": true + }, + "vue": { "optional": true } } }, - "node_modules/@langchain/langgraph-sdk/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], + "node_modules/@langchain/langgraph-sdk/node_modules/@langchain/core": { + "version": "1.1.46", + "resolved": "https://registry.npmjs.org/@langchain/core/-/core-1.1.46.tgz", + "integrity": "sha512-i8rDC83BpItxChCw4Lf+6tAr+k+OUcbirc5ZkrhI9ywYWmvxegUljLGOGYvtJNTbEAIFkhYIODPE5QRqyjF6sA==", "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" + "dependencies": { + "@cfworker/json-schema": "^4.0.2", + "@standard-schema/spec": "^1.1.0", + "js-tiktoken": "^1.0.12", + "langsmith": ">=0.5.0 <1.0.0", + "mustache": "^4.2.0", + "p-queue": "^6.6.2", + "zod": "^3.25.76 || ^4" + }, + "engines": { + "node": ">=20" } }, - "node_modules/@lukeed/ms": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@lukeed/ms/-/ms-2.0.2.tgz", - "integrity": "sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA==", + "node_modules/@langchain/langgraph-sdk/node_modules/@langchain/core/node_modules/p-queue": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz", + "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==", "license": "MIT", + "dependencies": { + "eventemitter3": "^4.0.4", + "p-timeout": "^3.2.0" + }, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@pinojs/redact": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@pinojs/redact/-/redact-0.4.0.tgz", - "integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==", - "license": "MIT" - }, - "node_modules/@radix-ui/number": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz", - "integrity": "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==", - "license": "MIT" - }, - "node_modules/@radix-ui/primitive": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz", - "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==", - "license": "MIT" - }, - "node_modules/@radix-ui/react-arrow": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz", - "integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==", + "node_modules/@langchain/langgraph-sdk/node_modules/p-queue": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-9.2.0.tgz", + "integrity": "sha512-dWgLE8AH0HjQ9fe74pUkKkvzzYT18Inp4zra3lKHnnwqGvcfcUBrvF2EAVX+envufDNBOzpPq/IBUONDbI7+3g==", "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.1.3" + "eventemitter3": "^5.0.4", + "p-timeout": "^7.0.0" }, - "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" + "engines": { + "node": ">=20" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@radix-ui/react-collection": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz", - "integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==", + "node_modules/@langchain/langgraph-sdk/node_modules/p-queue/node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "license": "MIT" + }, + "node_modules/@langchain/langgraph-sdk/node_modules/p-queue/node_modules/p-timeout": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-7.0.1.tgz", + "integrity": "sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg==", "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-slot": "1.2.3" - }, - "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" + "engines": { + "node": ">=20" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-slot": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", - "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "node_modules/@langchain/langgraph-sdk/node_modules/p-retry": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-7.1.1.tgz", + "integrity": "sha512-J5ApzjyRkkf601HpEeykoiCvzHQjWxPAHhyjFcEUP2SWq0+35NKh8TLhpLw+Dkq5TZBFvUM6UigdE9hIVYTl5w==", "license": "MIT", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" + "is-network-error": "^1.1.0" }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "engines": { + "node": ">=20" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@radix-ui/react-compose-refs": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", - "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", + "node_modules/@langchain/langgraph-sdk/node_modules/uuid": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.2.tgz", + "integrity": "sha512-vzi9uRZ926x4XV73S/4qQaTwPXM2JBj6/6lI/byHH1jOpCzb0zDbfytgA9LcN/hzb2l7WQSQnxITOVx5un/wGw==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "bin": { + "uuid": "dist-node/bin/uuid" } }, - "node_modules/@radix-ui/react-context": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", - "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "node_modules/@langchain/protocol": { + "version": "0.0.15", + "resolved": "https://registry.npmjs.org/@langchain/protocol/-/protocol-0.0.15.tgz", + "integrity": "sha512-MllvbpMjqHevUm+v94M422mH7XKN+wGCvJRBVROTWBotEDOATYB4Ktk2UheYP859y9o2LlhtPek5t1T9eyfAbQ==", + "license": "MIT" + }, + "node_modules/@lukeed/csprng": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@lukeed/csprng/-/csprng-1.1.0.tgz", + "integrity": "sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==", "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "engines": { + "node": ">=8" } }, - "node_modules/@radix-ui/react-direction": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz", - "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==", + "node_modules/@lukeed/ms": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@lukeed/ms/-/ms-2.0.2.tgz", + "integrity": "sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA==", "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "engines": { + "node": ">=8" } }, - "node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz", - "integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==", + "node_modules/@lukeed/uuid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@lukeed/uuid/-/uuid-2.0.1.tgz", + "integrity": "sha512-qC72D4+CDdjGqJvkFMMEAtancHUQ7/d/tAiHf64z8MopFDmcrtbcJuerDtFceuAfQJ2pDSfCKCtbqoGBNnwg0w==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-escape-keydown": "1.1.1" - }, - "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" + "@lukeed/csprng": "^1.1.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "engines": { + "node": ">=8" } }, - "node_modules/@radix-ui/react-focus-guards": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz", - "integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==", + "node_modules/@monaco-editor/loader": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@monaco-editor/loader/-/loader-1.7.0.tgz", + "integrity": "sha512-gIwR1HrJrrx+vfyOhYmCZ0/JcWqG5kbfG7+d3f/C1LXk2EvzAbHSg3MQ5lO2sMlo9izoAZ04shohfKLVT6crVA==", "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "dependencies": { + "state-local": "^1.0.6" } }, - "node_modules/@radix-ui/react-focus-scope": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz", - "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==", + "node_modules/@monaco-editor/react": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@monaco-editor/react/-/react-4.7.0.tgz", + "integrity": "sha512-cyzXQCtO47ydzxpQtCGSQGOC8Gk3ZUeBXFAxD+CWXYFo5OqZyZUonFl0DwUlTyAfRHntBfw2p3w4s9R6oe1eCA==", "license": "MIT", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1" + "@monaco-editor/loader": "^1.5.0" }, "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" + "monaco-editor": ">= 0.25.0 < 1", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@open-policy-agent/opa-wasm": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@open-policy-agent/opa-wasm/-/opa-wasm-1.10.0.tgz", + "integrity": "sha512-ymR/nFS3nO9o24j9xowGGQaf+Gmb813QcxUpVZkfRlJkawKWqSIllnEH15agyWjijmOIyhA+OBErenx6N3jphw==", + "license": "Apache-2.0", + "dependencies": { + "sprintf-js": "^1.1.2", + "yaml": "^1.10.2" + } + }, + "node_modules/@open-policy-agent/opa-wasm/node_modules/yaml": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz", + "integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==", + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "node_modules/@opentelemetry/api": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.1.tgz", + "integrity": "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==", + "license": "Apache-2.0", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/api-logs": { + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.218.0.tgz", + "integrity": "sha512-fmEWp5kXlGEc3i/lR698Hz41DfGyN4Tbe4g7L1AxSc7fF8Xeh/FQ9Quqpa9dVA413Q1Ad43QOLzU4JoXgbFPWw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "engines": { + "node": ">=8.0.0" } }, - "node_modules/@radix-ui/react-id": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", - "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", - "license": "MIT", + "node_modules/@opentelemetry/auto-instrumentations-node": { + "version": "0.76.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/auto-instrumentations-node/-/auto-instrumentations-node-0.76.0.tgz", + "integrity": "sha512-44KWgqsMuqfV4UhOcwwnDeK8CpB5LT1MmpZj6sKXFXu2q6rjKo622pWgOgn5Ntp5Qal9q1uBX2VS8mvTpsMeyw==", + "license": "Apache-2.0", "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.1" + "@opentelemetry/instrumentation": "^0.218.0", + "@opentelemetry/instrumentation-amqplib": "^0.65.0", + "@opentelemetry/instrumentation-aws-lambda": "^0.70.0", + "@opentelemetry/instrumentation-aws-sdk": "^0.73.0", + "@opentelemetry/instrumentation-bunyan": "^0.63.0", + "@opentelemetry/instrumentation-cassandra-driver": "^0.63.0", + "@opentelemetry/instrumentation-connect": "^0.61.0", + "@opentelemetry/instrumentation-cucumber": "^0.34.0", + "@opentelemetry/instrumentation-dataloader": "^0.35.0", + "@opentelemetry/instrumentation-dns": "^0.61.0", + "@opentelemetry/instrumentation-express": "^0.66.0", + "@opentelemetry/instrumentation-fs": "^0.37.0", + "@opentelemetry/instrumentation-generic-pool": "^0.61.0", + "@opentelemetry/instrumentation-graphql": "^0.66.0", + "@opentelemetry/instrumentation-grpc": "^0.218.0", + "@opentelemetry/instrumentation-hapi": "^0.64.0", + "@opentelemetry/instrumentation-http": "^0.218.0", + "@opentelemetry/instrumentation-ioredis": "^0.66.0", + "@opentelemetry/instrumentation-kafkajs": "^0.27.0", + "@opentelemetry/instrumentation-knex": "^0.62.0", + "@opentelemetry/instrumentation-koa": "^0.66.0", + "@opentelemetry/instrumentation-lru-memoizer": "^0.62.0", + "@opentelemetry/instrumentation-memcached": "^0.61.0", + "@opentelemetry/instrumentation-mongodb": "^0.71.0", + "@opentelemetry/instrumentation-mongoose": "^0.64.0", + "@opentelemetry/instrumentation-mysql": "^0.64.0", + "@opentelemetry/instrumentation-mysql2": "^0.64.0", + "@opentelemetry/instrumentation-nestjs-core": "^0.64.0", + "@opentelemetry/instrumentation-net": "^0.62.0", + "@opentelemetry/instrumentation-openai": "^0.16.0", + "@opentelemetry/instrumentation-oracledb": "^0.43.0", + "@opentelemetry/instrumentation-pg": "^0.70.0", + "@opentelemetry/instrumentation-pino": "^0.64.0", + "@opentelemetry/instrumentation-redis": "^0.66.0", + "@opentelemetry/instrumentation-restify": "^0.63.0", + "@opentelemetry/instrumentation-router": "^0.62.0", + "@opentelemetry/instrumentation-runtime-node": "^0.31.0", + "@opentelemetry/instrumentation-socket.io": "^0.65.0", + "@opentelemetry/instrumentation-tedious": "^0.37.0", + "@opentelemetry/instrumentation-undici": "^0.28.0", + "@opentelemetry/instrumentation-winston": "^0.62.0", + "@opentelemetry/resource-detector-alibaba-cloud": "^0.33.8", + "@opentelemetry/resource-detector-aws": "^2.18.0", + "@opentelemetry/resource-detector-azure": "^0.26.0", + "@opentelemetry/resource-detector-container": "^0.8.9", + "@opentelemetry/resource-detector-gcp": "^0.53.0", + "@opentelemetry/resources": "^2.0.0", + "@opentelemetry/sdk-node": "^0.218.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" }, "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "@opentelemetry/api": "^1.4.1", + "@opentelemetry/core": "^2.0.0" } }, - "node_modules/@radix-ui/react-popper": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz", - "integrity": "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==", - "license": "MIT", + "node_modules/@opentelemetry/configuration": { + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/configuration/-/configuration-0.218.0.tgz", + "integrity": "sha512-W8wIz7H2R1pufR5jfjb3gU2XkMpm2x/7b1RJcsuzvd70Il/rWWE+g5/Od7hQKrxRTSrTrOWlru101PWXz5I1EQ==", + "license": "Apache-2.0", "dependencies": { - "@floating-ui/react-dom": "^2.0.0", - "@radix-ui/react-arrow": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-layout-effect": "1.1.1", - "@radix-ui/react-use-rect": "1.1.1", - "@radix-ui/react-use-size": "1.1.1", - "@radix-ui/rect": "1.1.1" + "@opentelemetry/core": "2.7.1", + "yaml": "^2.0.0" }, - "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" + "engines": { + "node": "^18.19.0 || >=20.6.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "peerDependencies": { + "@opentelemetry/api": "^1.9.0" + } + }, + "node_modules/@opentelemetry/configuration/node_modules/@opentelemetry/core": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.1.tgz", + "integrity": "sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/context-async-hooks": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-2.7.1.tgz", + "integrity": "sha512-OPFBYuXEn1E4ja3Y6eeA7O+ZnLBNcXTV5Cgsn1VaqBZ6hC5FnpZPLBNme1LJY8ZtF4aOujPKFoeWN4ik487KuQ==", + "license": "Apache-2.0", + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/core": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.10.0.tgz", + "integrity": "sha512-/wNZ8twnEQQA4HoHu22+vcsdru6pWPWxW+7w+FlxT6Id7PE/WIbZmVKkte+PF72e0F2dnImFeHD2syyE1Mw6MQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-grpc": { + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-grpc/-/exporter-logs-otlp-grpc-0.218.0.tgz", + "integrity": "sha512-hoxrNH1l/Xy6F9WTJ5IK+6j1r9nQFlPOmrnTlhYHTySdunfXLmUCPv3bQtKYntxag9h3wLYBZQ2HI6FOx+BT2g==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/grpc-js": "^1.14.3", + "@opentelemetry/core": "2.7.1", + "@opentelemetry/otlp-exporter-base": "0.218.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.218.0", + "@opentelemetry/otlp-transformer": "0.218.0", + "@opentelemetry/sdk-logs": "0.218.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-grpc/node_modules/@opentelemetry/core": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.1.tgz", + "integrity": "sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-proto": { + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-proto/-/exporter-logs-otlp-proto-0.218.0.tgz", + "integrity": "sha512-1/noQNsp9gXD75HPzgjBrcF1+XTtry7pFAUfxVEJgg7mPv2AawKQuYkhMmJ8qjxz4Ubc3Y8bwvfxevXsKTq4cg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.218.0", + "@opentelemetry/core": "2.7.1", + "@opentelemetry/otlp-exporter-base": "0.218.0", + "@opentelemetry/otlp-transformer": "0.218.0", + "@opentelemetry/resources": "2.7.1", + "@opentelemetry/sdk-logs": "0.218.0", + "@opentelemetry/sdk-trace-base": "2.7.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-proto/node_modules/@opentelemetry/core": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.1.tgz", + "integrity": "sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-proto/node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.7.1.tgz", + "integrity": "sha512-NAYIlsF8MPUsKqJMiDQJTMPOmlbawC1Iz/omMLygZ1C9am8fTKYjTaI+OZM+WTY3t3Glo0wnOg/6/pac6RGPPw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/resources": "2.7.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-grpc": { + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-grpc/-/exporter-metrics-otlp-grpc-0.218.0.tgz", + "integrity": "sha512-YapQ9vNMX0NSZF6LK5pWAFfjpJleV2O9uYWfYGeb/5F1Kb9rPGK8tZDMJFa/sOksgdFuflDvYuA0B4qjDB4fjQ==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/grpc-js": "^1.14.3", + "@opentelemetry/core": "2.7.1", + "@opentelemetry/exporter-metrics-otlp-http": "0.218.0", + "@opentelemetry/otlp-exporter-base": "0.218.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.218.0", + "@opentelemetry/otlp-transformer": "0.218.0", + "@opentelemetry/resources": "2.7.1", + "@opentelemetry/sdk-metrics": "2.7.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-grpc/node_modules/@opentelemetry/core": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.1.tgz", + "integrity": "sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-http": { + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-http/-/exporter-metrics-otlp-http-0.218.0.tgz", + "integrity": "sha512-bV7d2OuMpZu2+gAaxUAhzfZ0h3WVZk8ETQUEE3DNSntbTaMpuITjtm8I0rNyHFdm7Ax57K6ty7SgFXlBmOLIvQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/otlp-exporter-base": "0.218.0", + "@opentelemetry/otlp-transformer": "0.218.0", + "@opentelemetry/resources": "2.7.1", + "@opentelemetry/sdk-metrics": "2.7.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-http/node_modules/@opentelemetry/core": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.1.tgz", + "integrity": "sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-proto": { + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-proto/-/exporter-metrics-otlp-proto-0.218.0.tgz", + "integrity": "sha512-ubLddKjWULhla9YZRCj/rTBeppjJYE4e9w0icx5mTu3eFhWjQzbV75NYjXuIlEG+NJsBl6d+sTFw5Qu+oej4oQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/exporter-metrics-otlp-http": "0.218.0", + "@opentelemetry/otlp-exporter-base": "0.218.0", + "@opentelemetry/otlp-transformer": "0.218.0", + "@opentelemetry/resources": "2.7.1", + "@opentelemetry/sdk-metrics": "2.7.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-metrics-otlp-proto/node_modules/@opentelemetry/core": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.1.tgz", + "integrity": "sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-prometheus": { + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-prometheus/-/exporter-prometheus-0.218.0.tgz", + "integrity": "sha512-RT5oEyu1kddZJ1vt7/BUo5wV+P7hpNAESsR3dUd3+8deHuX7gWNoCOZn+SfDT+hJHlIJ5h/AxiCLXIrutswDJg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/resources": "2.7.1", + "@opentelemetry/sdk-metrics": "2.7.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-prometheus/node_modules/@opentelemetry/core": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.1.tgz", + "integrity": "sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc": { + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.218.0.tgz", + "integrity": "sha512-3fXxVQEj9TNAFaCi79JeFKfeLd0sDtInaR3gaZDVlzNSPHtz8PZuCV34JKWjD4XXzT20IdMe8IpX6mRVNDA4Tw==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/grpc-js": "^1.14.3", + "@opentelemetry/core": "2.7.1", + "@opentelemetry/otlp-exporter-base": "0.218.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.218.0", + "@opentelemetry/otlp-transformer": "0.218.0", + "@opentelemetry/resources": "2.7.1", + "@opentelemetry/sdk-trace-base": "2.7.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/core": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.1.tgz", + "integrity": "sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.7.1.tgz", + "integrity": "sha512-NAYIlsF8MPUsKqJMiDQJTMPOmlbawC1Iz/omMLygZ1C9am8fTKYjTaI+OZM+WTY3t3Glo0wnOg/6/pac6RGPPw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/resources": "2.7.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http": { + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.218.0.tgz", + "integrity": "sha512-8dqezsmPhtKitIK/eTipZhYl9EX2/gNQ5zUMhaz3uxEURwfkNf8IPvo6yNfrzbxdtpAOybS/+h7wmIWYqFSpiw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/otlp-exporter-base": "0.218.0", + "@opentelemetry/otlp-transformer": "0.218.0", + "@opentelemetry/resources": "2.7.1", + "@opentelemetry/sdk-trace-base": "2.7.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/core": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.1.tgz", + "integrity": "sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.7.1.tgz", + "integrity": "sha512-NAYIlsF8MPUsKqJMiDQJTMPOmlbawC1Iz/omMLygZ1C9am8fTKYjTaI+OZM+WTY3t3Glo0wnOg/6/pac6RGPPw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/resources": "2.7.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto": { + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.218.0.tgz", + "integrity": "sha512-r1Msf8SNLRmwh9J6XQ5uh82D7CdDWMNHnPB7LAVHjzut0TkSeKc5KcIvr4SvHvfk/xwN5gxC+VLKQ1k0o8PSPw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/otlp-exporter-base": "0.218.0", + "@opentelemetry/otlp-transformer": "0.218.0", + "@opentelemetry/resources": "2.7.1", + "@opentelemetry/sdk-trace-base": "2.7.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/core": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.1.tgz", + "integrity": "sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.7.1.tgz", + "integrity": "sha512-NAYIlsF8MPUsKqJMiDQJTMPOmlbawC1Iz/omMLygZ1C9am8fTKYjTaI+OZM+WTY3t3Glo0wnOg/6/pac6RGPPw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/resources": "2.7.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-2.7.1.tgz", + "integrity": "sha512-mfsD9bKAxcKrh5+y08TPodvClBO0CznBE3p79YAGnO81WI4LrdsGA65T53e4iTSbCalW4WaUpkbeJcbpyIUHfg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/resources": "2.7.1", + "@opentelemetry/sdk-trace-base": "2.7.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/core": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.1.tgz", + "integrity": "sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.7.1.tgz", + "integrity": "sha512-NAYIlsF8MPUsKqJMiDQJTMPOmlbawC1Iz/omMLygZ1C9am8fTKYjTaI+OZM+WTY3t3Glo0wnOg/6/pac6RGPPw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/resources": "2.7.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/instrumentation": { + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.218.0.tgz", + "integrity": "sha512-mIZil8Es+sYDK5m+DQiwAwF57F14TF2YlEqvIjZ/RQWcxDBwRGsKfdK2Tv65OU9meQKCMzSIFS9mxAcnAb6Bkg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.218.0", + "import-in-the-middle": "^3.0.0", + "require-in-the-middle": "^8.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-amqplib": { + "version": "0.65.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-amqplib/-/instrumentation-amqplib-0.65.0.tgz", + "integrity": "sha512-fF7fNHA59n3y23ROfst2EbSxmP+L3E+snZO6aMU4w4xD84mfejAivspIAsqa9arX5HZlBK6dslHz5dWGNp5D0A==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.218.0", + "@opentelemetry/semantic-conventions": "^1.33.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-aws-lambda": { + "version": "0.70.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-aws-lambda/-/instrumentation-aws-lambda-0.70.0.tgz", + "integrity": "sha512-HT74cQxi/iiVEz5dRdNdfGCFzPFbkxSiwHfFPHDwkRcr1JKQqI6hm8qeXEvEiJ+36xIU1KkQMDfeThJ1ifnUiA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.218.0", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@types/aws-lambda": "^8.10.155" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-aws-sdk": { + "version": "0.73.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-aws-sdk/-/instrumentation-aws-sdk-0.73.0.tgz", + "integrity": "sha512-0INPkHbR6o4J3psE+ncwWaE7qtDpb2p+i+qfV82cfwYLCXavYCGosBZ/S4pOErDVJYIyQVIsNAHhaUgaL313SQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.218.0", + "@opentelemetry/semantic-conventions": "^1.34.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-bunyan": { + "version": "0.63.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-bunyan/-/instrumentation-bunyan-0.63.0.tgz", + "integrity": "sha512-z0xPSZ62d3I7sG2sUTyQ5/ES1RdESP2eOETiMLY9gPSp+HZwbsAyj7T/2sdZKYD+O2ajRHZEil+DBoUolf1ocQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "^0.218.0", + "@opentelemetry/instrumentation": "^0.218.0", + "@types/bunyan": "1.8.11" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-cassandra-driver": { + "version": "0.63.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-cassandra-driver/-/instrumentation-cassandra-driver-0.63.0.tgz", + "integrity": "sha512-jnVTOr3h/46UDalEwJ4ITux8UWwHmnsOik5WFs3JB/UrUj8Wad5eI+KpOEBuOUeOfPB9sce11qgVw3WXU2r+hg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.218.0", + "@opentelemetry/semantic-conventions": "^1.37.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-connect": { + "version": "0.61.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-connect/-/instrumentation-connect-0.61.0.tgz", + "integrity": "sha512-ZTQ0W3Lb7GJsOd+72cG8FJQKA5DqYfELJGLmChrJIezRSLfJIfofwKEGLX5rMtFJmwckpichQkBZWjid5dvnVQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.218.0", + "@opentelemetry/semantic-conventions": "^1.27.0", + "@types/connect": "3.4.38" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-cucumber": { + "version": "0.34.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-cucumber/-/instrumentation-cucumber-0.34.0.tgz", + "integrity": "sha512-VK63Cm8osAdsSZpULPk+qnNktQUJzmnIOv2wuh79fV41WuTM38uOFC3s978/24pDkSljhN4EYCbPRLrAhXfKSA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.218.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/instrumentation-dataloader": { + "version": "0.35.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dataloader/-/instrumentation-dataloader-0.35.0.tgz", + "integrity": "sha512-6x6UPP0tLzrdj15PIEN3qgp/WCcESCavHJfkIKoyLmy4UjGLF1KgEUMyD74xhbKGo426uvMbhvCgZC0ye8nO/A==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.218.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-dns": { + "version": "0.61.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dns/-/instrumentation-dns-0.61.0.tgz", + "integrity": "sha512-5D8xFaw9GXq9ZIOAvG7NPDivFfZWFAekLGFn1B7ppyhuAYBVHGybFpx4Q9BV1Uup3yzCdiD78KhyH7c3dKOYSw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.218.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-express": { + "version": "0.66.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-express/-/instrumentation-express-0.66.0.tgz", + "integrity": "sha512-G1xTh5M5shklMgIyUXWDjU2BakulKtcISaM4U5TyanvO7R4xbB3iC7YQ8QKegLXaOs81Ku8RlcIcbYRrz/82wQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.218.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-fs": { + "version": "0.37.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fs/-/instrumentation-fs-0.37.0.tgz", + "integrity": "sha512-5mxhFuwAK0FFvisUdvuywaZ9ySMZ15HfbN6IpLn0gwRh9s1/QBcpLznQ/A15cZs1QFtBJ+JXIHdwY7WOD0c4Eg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.218.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-generic-pool": { + "version": "0.61.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-generic-pool/-/instrumentation-generic-pool-0.61.0.tgz", + "integrity": "sha512-tvp5PWnGRPHY/kz9Kg1IRLBL0qUAxMSNG623f+ZGEsvnCVEjr3tFyw1JGQzM+B3eZKkO+Dp/LYrtOSfb69D5lA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.218.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-graphql": { + "version": "0.66.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-graphql/-/instrumentation-graphql-0.66.0.tgz", + "integrity": "sha512-D4PN1tStj6rnOdofnt2xINJjtT1k2ockzaODrn76VEBZeqJ3QsEvKFfunB0EFAohO4xswVp14VAVmKNnGzA1Dw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.218.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-grpc": { + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-grpc/-/instrumentation-grpc-0.218.0.tgz", + "integrity": "sha512-kcDCNrC7IWNXEKQriGrwuh5jjbMFU5exOQzU9ufEY9UkACNcgYIdOd7XpX3IqZ3UPSnZyZtlwgfsbC5SNlEDbA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "0.218.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-hapi": { + "version": "0.64.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-hapi/-/instrumentation-hapi-0.64.0.tgz", + "integrity": "sha512-PCHgCICCDz7p9BgCU9gQz2smbqu4V4P8QtWJ7DLjL3bmzSdrgy6EGvecDg1YuhjBsoN08SR+y36hgdHkqCgrzQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.218.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-http": { + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-http/-/instrumentation-http-0.218.0.tgz", + "integrity": "sha512-x9djaqdzpT8WAboep1H9nCAQ1E+MMsm08TNfA02TqM3bNNddZeiim+E3KMWVQFaX6JpUy7V0nm/wfN/K2Em+Zw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/instrumentation": "0.218.0", + "@opentelemetry/semantic-conventions": "^1.29.0", + "forwarded-parse": "2.1.2" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-http/node_modules/@opentelemetry/core": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.1.tgz", + "integrity": "sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/instrumentation-ioredis": { + "version": "0.66.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-ioredis/-/instrumentation-ioredis-0.66.0.tgz", + "integrity": "sha512-UfTAcaBKCzLUZ9opvfOLV4bH46XiNFqUsKykfPCIefDIxJ1iUYtMOucNaiZ+/kjQdPy5i6Ef5tk2IAjxol4X1w==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.218.0", + "@opentelemetry/redis-common": "^0.38.3", + "@opentelemetry/semantic-conventions": "^1.33.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-kafkajs": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-kafkajs/-/instrumentation-kafkajs-0.27.0.tgz", + "integrity": "sha512-kl/C2AU4KZGHlMZD12nMFXcMjxSHvu5Q0UPSQ6IJeBfCadYuWgW+sWIa2JZVK/A0qRYm2cncekJyeBHQDyfUUg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.218.0", + "@opentelemetry/semantic-conventions": "^1.30.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-knex": { + "version": "0.62.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-knex/-/instrumentation-knex-0.62.0.tgz", + "integrity": "sha512-XgfhCAWwSqA0YnwaEKdpvQMavc90D3R65frhLCO9JNl867EulNps9tm6pjGIg+GiYuewn00gEzW4HQ5btgYxGQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.218.0", + "@opentelemetry/semantic-conventions": "^1.33.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-koa": { + "version": "0.66.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-koa/-/instrumentation-koa-0.66.0.tgz", + "integrity": "sha512-04x/z21WTMEfy3lUSr4aTj8WsTN3OZF901hJ+ciOwdwf7AK8UJTpZCXw6KQ3G4Vag56q1HoMihCONeWZLeld1g==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.218.0", + "@opentelemetry/semantic-conventions": "^1.36.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.9.0" + } + }, + "node_modules/@opentelemetry/instrumentation-lru-memoizer": { + "version": "0.62.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-lru-memoizer/-/instrumentation-lru-memoizer-0.62.0.tgz", + "integrity": "sha512-AlGKIdk6ZT7WmIozfUb2LjOcI3AhQrvAXKX0zi1cVcnw2QlRbVYyV5GTa2Th9ebuczVfWPaoPrmZw61zCp/czw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.218.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-memcached": { + "version": "0.61.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-memcached/-/instrumentation-memcached-0.61.0.tgz", + "integrity": "sha512-qiCR9Wovf5AHzn6g+LXhvwMmv2I6zhHz2I2tEHZMmBuD8c18bkJzGFxHoSBlxdApRT+SW13r9472dDMm4BRjgQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.218.0", + "@opentelemetry/semantic-conventions": "^1.33.0", + "@types/memcached": "^2.2.6" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-mongodb": { + "version": "0.71.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.71.0.tgz", + "integrity": "sha512-6rwfVjAUY69CKkyGqzL+F5X7Nzw0+Ke9pOxk9xUPJpy8vracZxuQYF7rWu02sV1xOgi4u52449SuVhD+zaSiIA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.218.0", + "@opentelemetry/semantic-conventions": "^1.33.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-mongoose": { + "version": "0.64.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongoose/-/instrumentation-mongoose-0.64.0.tgz", + "integrity": "sha512-iCIqeUaERN8Uc5Rrtg4zvQ6d7z5JQ5iUmbnr/JHYPxAidDowmRc8/wDMJeMKRfLPTj336Zu0ec7rH/ak/4N9vw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.218.0", + "@opentelemetry/semantic-conventions": "^1.33.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-mysql": { + "version": "0.64.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql/-/instrumentation-mysql-0.64.0.tgz", + "integrity": "sha512-W1w76AJkP7i0uzzAe7nsCMWq4+EMSA550f1lAmxDPdQC5FnreNbRIm/tod2OS9gVrYvRrQXNkFmZJKGo4kzCnw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.218.0", + "@opentelemetry/semantic-conventions": "^1.33.0", + "@types/mysql": "2.15.27" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-mysql2": { + "version": "0.64.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql2/-/instrumentation-mysql2-0.64.0.tgz", + "integrity": "sha512-yTu0mYh/qJPSE86VmNLQww5uugDyvCS2KJIPfPtIk2ufoEUoHPsV6Iynnvmz588Moq04aBLxfTa/EtE4A2ykWA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.218.0", + "@opentelemetry/semantic-conventions": "^1.33.0", + "@opentelemetry/sql-common": "^0.41.2" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-nestjs-core": { + "version": "0.64.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-nestjs-core/-/instrumentation-nestjs-core-0.64.0.tgz", + "integrity": "sha512-PW1ArxryMwF8/IXq1nzlQs7tmr/fWd1tf71AHevZT3Fm0hW7jRX9JEfYgIAcKDvmbqcJEr5K1224NEimrRPbuQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.218.0", + "@opentelemetry/semantic-conventions": "^1.30.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-net": { + "version": "0.62.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-net/-/instrumentation-net-0.62.0.tgz", + "integrity": "sha512-Gt2kzpACpmIad+q3LQqe8UNHuoVvdLuFpB6SN/A6xLPKNllb+ksPUYQhj1kXdZOpcFZNGKDXHyN+TUCVCk1TRw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.218.0", + "@opentelemetry/semantic-conventions": "^1.33.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-openai": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-openai/-/instrumentation-openai-0.16.0.tgz", + "integrity": "sha512-I0KKybyqqFOxSBgYKQNdR/EF3LvzSaAUT7Y75xkjbgscY+V8UWDpUbY68POLhUC3SKMlGvZmrTSxcQ+Y0vRhNw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "^0.218.0", + "@opentelemetry/instrumentation": "^0.218.0", + "@opentelemetry/semantic-conventions": "^1.36.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-oracledb": { + "version": "0.43.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-oracledb/-/instrumentation-oracledb-0.43.0.tgz", + "integrity": "sha512-7Z4kOOdnrHX4S5gCeWhnnpWQwEd7weRjDhJA1nSrwTYtAcVWNjk5wsMKHBCTDCN0uJtA9T6PouZ+AKRYiS1Rrg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.218.0", + "@opentelemetry/semantic-conventions": "^1.34.0", + "@types/oracledb": "6.5.2" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-pg": { + "version": "0.70.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.70.0.tgz", + "integrity": "sha512-g8WXwwOUXfjiEmATwjB/33QKE2AkIpNe4KIuJJh4djtXgCL0Wne+AzAfjuDIAspGvO1txQp8ibKsLd3SBmcvJA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.218.0", + "@opentelemetry/semantic-conventions": "^1.34.0", + "@opentelemetry/sql-common": "^0.41.2", + "@types/pg": "8.15.6", + "@types/pg-pool": "2.0.7" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-pino": { + "version": "0.64.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pino/-/instrumentation-pino-0.64.0.tgz", + "integrity": "sha512-+vDL7tZMZjkp8BpYMx/cL2/HWGsNUqKcRmAIIEaQu/6F44oM6xGDMCSqMKHdKCsH1+WW52EYdHbWkVGTF0KVsQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "^0.218.0", + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.218.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-redis": { + "version": "0.66.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis/-/instrumentation-redis-0.66.0.tgz", + "integrity": "sha512-bVShkag6vP2VQO0cpA8CHjOohWbKNYLyjiwGkOnSAwou1TPc6pf9DssFUxwqN2XF1J4oqP0LVSvN9kZUzMecfA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.218.0", + "@opentelemetry/redis-common": "^0.38.3", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-restify": { + "version": "0.63.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-restify/-/instrumentation-restify-0.63.0.tgz", + "integrity": "sha512-Z73YxZpt0Y56uRu2pRWOjO5wXHvZqF46K4czoKRTGlUifzzFmUZxyOeAAECACuMRSLZmZ394WJin0MDgU9iW9w==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.218.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-router": { + "version": "0.62.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-router/-/instrumentation-router-0.62.0.tgz", + "integrity": "sha512-0w8ok7GbXtYvX7TtLp72qQJKNyI7lD72Fy2NsNKIcQAv6TqGox5javFyXrIrCAtZHCONePxeAwAYj1Qd9si9OQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.218.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-runtime-node": { + "version": "0.31.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-runtime-node/-/instrumentation-runtime-node-0.31.0.tgz", + "integrity": "sha512-HkLsuEfUDahFiL/xFtEqJDMp7sp8ynOtA045bJi9nAH8CrPvljPW5SgJQb2mQqEYJQopbWYZ2lPqQEfj7bYgJg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "^0.218.0", + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.218.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@radix-ui/react-portal": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz", - "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==", - "license": "MIT", + "node_modules/@opentelemetry/instrumentation-socket.io": { + "version": "0.65.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-socket.io/-/instrumentation-socket.io-0.65.0.tgz", + "integrity": "sha512-dNvIbD40h0z69stQ9cIeAWRyy5WyQM1a1XnFthekc/oi/ipX4E6oYJBM4X2xKBxjZMTjdV5VshLoNeYMSBsnjw==", + "license": "Apache-2.0", "dependencies": { - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-layout-effect": "1.1.1" + "@opentelemetry/instrumentation": "^0.218.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" }, "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" + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-tedious": { + "version": "0.37.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-tedious/-/instrumentation-tedious-0.37.0.tgz", + "integrity": "sha512-cGLF46UsgeI1334atJxLO36yQlV7WXKg35Mp+e2NXo2vOTfIZTVqoKOzExVOTOwT4AQjfGVEDxyq5wXybUYXIA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.218.0", + "@opentelemetry/semantic-conventions": "^1.33.0", + "@types/tedious": "^4.0.14" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@radix-ui/react-presence": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz", - "integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==", - "license": "MIT", + "node_modules/@opentelemetry/instrumentation-undici": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-undici/-/instrumentation-undici-0.28.0.tgz", + "integrity": "sha512-7nh4Gw7PhYtQm82FIJtWUhx6iZQJj0bdkKe2RQb3XNIyxu0o9rM1J5Xt083SsG2tCbQZpX9/mlDxhTrK1Z/lVQ==", + "license": "Apache-2.0", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-use-layout-effect": "1.1.1" + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.218.0", + "@opentelemetry/semantic-conventions": "^1.24.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" }, "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" + "@opentelemetry/api": "^1.7.0" + } + }, + "node_modules/@opentelemetry/instrumentation-winston": { + "version": "0.62.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-winston/-/instrumentation-winston-0.62.0.tgz", + "integrity": "sha512-pr1U9ZV4RRy23qMVrRzebfxwDWjp44xA7sC0PAdeW9v4HDcfOr0ejdTJmIsBGvhkNHPBajfieaIF9b6/9wjErA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "^0.218.0", + "@opentelemetry/instrumentation": "^0.218.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@radix-ui/react-primitive": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", - "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", - "license": "MIT", + "node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.218.0.tgz", + "integrity": "sha512-ZwqpkNL5W7RyGJPDZ9g06DvKp8KFTWPJPN12anpMQYSKpTSU0z3EIZuPq9vPGpS8siFyOqDYDAuCwlNO9FqgbA==", + "license": "Apache-2.0", "dependencies": { - "@radix-ui/react-slot": "1.2.3" + "@opentelemetry/core": "2.7.1", + "@opentelemetry/otlp-transformer": "0.218.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" }, "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" + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base/node_modules/@opentelemetry/core": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.1.tgz", + "integrity": "sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, - "node_modules/@radix-ui/react-primitive/node_modules/@radix-ui/react-slot": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", - "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", - "license": "MIT", + "node_modules/@opentelemetry/otlp-grpc-exporter-base": { + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.218.0.tgz", + "integrity": "sha512-H/lCGJ536N98VpYJOaWTQOkv4Dx6TnmStK6Rqfu1W7KkFbPAx04hjdYEMZF/YbnHzPUSIK4kM6OE2GKGBTpV9A==", + "license": "Apache-2.0", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" + "@grpc/grpc-js": "^1.14.3", + "@opentelemetry/core": "2.7.1", + "@opentelemetry/otlp-exporter-base": "0.218.0", + "@opentelemetry/otlp-transformer": "0.218.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" }, "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base/node_modules/@opentelemetry/core": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.1.tgz", + "integrity": "sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, - "node_modules/@radix-ui/react-roving-focus": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz", - "integrity": "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==", - "license": "MIT", + "node_modules/@opentelemetry/otlp-transformer": { + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.218.0.tgz", + "integrity": "sha512-CFaKH87WAzjuJ4awowTTLzUvMfaRfiOFG5+qm5S5ncyalRtN4ecQ+YmuANJSCrVPuvZFEkUgKhBPBndxi3rHsQ==", + "license": "Apache-2.0", "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-collection": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-controllable-state": "1.2.2" + "@opentelemetry/api-logs": "0.218.0", + "@opentelemetry/core": "2.7.1", + "@opentelemetry/resources": "2.7.1", + "@opentelemetry/sdk-logs": "0.218.0", + "@opentelemetry/sdk-metrics": "2.7.1", + "@opentelemetry/sdk-trace-base": "2.7.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" }, "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" + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/core": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.1.tgz", + "integrity": "sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, - "node_modules/@radix-ui/react-scroll-area": { - "version": "1.2.10", - "resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.10.tgz", - "integrity": "sha512-tAXIa1g3sM5CGpVT0uIbUx/U3Gs5N8T52IICuCtObaos1S8fzsrPXG5WObkQN3S6NVl6wKgPhAIiBGbWnvc97A==", - "license": "MIT", + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.7.1.tgz", + "integrity": "sha512-NAYIlsF8MPUsKqJMiDQJTMPOmlbawC1Iz/omMLygZ1C9am8fTKYjTaI+OZM+WTY3t3Glo0wnOg/6/pac6RGPPw==", + "license": "Apache-2.0", "dependencies": { - "@radix-ui/number": "1.1.1", - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-layout-effect": "1.1.1" + "@opentelemetry/core": "2.7.1", + "@opentelemetry/resources": "2.7.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" }, "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" + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-b3": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-2.7.1.tgz", + "integrity": "sha512-RJid6E2CKyeGfKBzXKF21ejabGMHypFkPAh3qZ+NvI+SGjuIye79t3PmiqcDgtRzdKH6ynXzbfslQ8DfpRUg2A==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, - "node_modules/@radix-ui/react-select": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.6.tgz", - "integrity": "sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/number": "1.1.1", - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-collection": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.11", - "@radix-ui/react-focus-guards": "1.1.3", - "@radix-ui/react-focus-scope": "1.1.7", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-popper": "1.2.8", - "@radix-ui/react-portal": "1.1.9", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-slot": "1.2.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-controllable-state": "1.2.2", - "@radix-ui/react-use-layout-effect": "1.1.1", - "@radix-ui/react-use-previous": "1.1.1", - "@radix-ui/react-visually-hidden": "1.2.3", - "aria-hidden": "^1.2.4", - "react-remove-scroll": "^2.6.3" + "node_modules/@opentelemetry/propagator-b3/node_modules/@opentelemetry/core": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.1.tgz", + "integrity": "sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" }, "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" + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-jaeger": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-2.10.0.tgz", + "integrity": "sha512-yw/IX8DL470dSMZJoE82ScfYGp7JWZ/G8kFJo35ZILUVTB2jFPTOaioN+8s09pH0RHsWNhweVZb+ZnjJJpCChg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.10.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/redis-common": { + "version": "0.38.3", + "resolved": "https://registry.npmjs.org/@opentelemetry/redis-common/-/redis-common-0.38.3.tgz", + "integrity": "sha512-VCghU1JYs/4gP6Gqf/xro9MEsZ7LrMv2uONVsaESKL38ZOB9BqnI98FfS23wjMnHlpuE+TTaWSoAVNpTwYXzjw==", + "license": "Apache-2.0", + "engines": { + "node": "^18.19.0 || >=20.6.0" + } + }, + "node_modules/@opentelemetry/resource-detector-alibaba-cloud": { + "version": "0.33.8", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-alibaba-cloud/-/resource-detector-alibaba-cloud-0.33.8.tgz", + "integrity": "sha512-RnSB/uxkElny0/WBFEtIG2HRG0cpSNTRdE+YSB7Poa+uljK+ddCacEZYz/PMgZh+cs586XstJQxdyjz0jtcAug==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/resources": "^2.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/resource-detector-aws": { + "version": "2.18.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-aws/-/resource-detector-aws-2.18.0.tgz", + "integrity": "sha512-wyMM4UoRuHvI2KjqnTzvyW8Yv7MKRGA+I78Xti6gTEw7hBhqXU1SRo+f9KrsQfeeiOn+TkDuvxavuaAQbD3i6g==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/resources": "^2.0.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/resource-detector-azure": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-azure/-/resource-detector-azure-0.26.0.tgz", + "integrity": "sha512-7KxF7mlwI2nKja/iEdwPqOaS0QAJbhT9ye4DeYZnXdOS/4phfonk5nSmyGDBYhBL7J30MPL91oZNuGYRKXZAXA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/resources": "^2.0.0", + "@opentelemetry/semantic-conventions": "^1.37.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/resource-detector-container": { + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-container/-/resource-detector-container-0.8.9.tgz", + "integrity": "sha512-Xd2C4HjW9hl75iqZT7tQNy2yRBUqNucq2O9+e0FJRNkbiItInYVMzc0S0KDXcx/vZBwNmlrKS3R0uLCU9ULsGA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/resources": "^2.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/resource-detector-gcp": { + "version": "0.53.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-gcp/-/resource-detector-gcp-0.53.0.tgz", + "integrity": "sha512-RCV31v23ZwZfYR3LPkuORHTHIOvfm3hZBT7hAzSO0+oAIrG/Dm0ld5tV4lYNO05GjI7sHQdRcbSqzEYAvQcQuw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/resources": "^2.0.0", + "gcp-metadata": "^8.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/resources": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.7.1.tgz", + "integrity": "sha512-DeT6KKolmC4e/dRQvMQ/RwlnzhaqeiFOXY5ngoOPJ07GgVVKxZOg9EcrNZb5aTzUn+iCrJldAgOfQm1O/QfPAQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/resources/node_modules/@opentelemetry/core": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.1.tgz", + "integrity": "sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs": { + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.218.0.tgz", + "integrity": "sha512-QvnNdugatFTVCJXH0Mcu7GOOJSylA9j127kIezOE4YwTI4YbowRons2K4WZTv5FMS8T4q9P0NdaRHdkSmeAIag==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.218.0", + "@opentelemetry/core": "2.7.1", + "@opentelemetry/resources": "2.7.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/core": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.1.tgz", + "integrity": "sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-metrics": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.7.1.tgz", + "integrity": "sha512-MpDJdkiFDs3Pm1RHO3KByuZbuBdJEXEAkiC0+yJdsZGVCdf1RpHR6n+LHDcS7ffmfrt5kVCzJSCfm4z2C7v0uQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/resources": "2.7.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.9.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-metrics/node_modules/@opentelemetry/core": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.1.tgz", + "integrity": "sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node": { + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-node/-/sdk-node-0.218.0.tgz", + "integrity": "sha512-tPMjHrLV5gsfNdYqoRHjeGbCAZBXXD9c1Qo/2ut7VwnUABDNh76xNxrT0SEhkIIJuCN45bbN1vZnYL1gY0IkOg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.218.0", + "@opentelemetry/configuration": "0.218.0", + "@opentelemetry/context-async-hooks": "2.7.1", + "@opentelemetry/core": "2.7.1", + "@opentelemetry/exporter-logs-otlp-grpc": "0.218.0", + "@opentelemetry/exporter-logs-otlp-http": "0.218.0", + "@opentelemetry/exporter-logs-otlp-proto": "0.218.0", + "@opentelemetry/exporter-metrics-otlp-grpc": "0.218.0", + "@opentelemetry/exporter-metrics-otlp-http": "0.218.0", + "@opentelemetry/exporter-metrics-otlp-proto": "0.218.0", + "@opentelemetry/exporter-prometheus": "0.218.0", + "@opentelemetry/exporter-trace-otlp-grpc": "0.218.0", + "@opentelemetry/exporter-trace-otlp-http": "0.218.0", + "@opentelemetry/exporter-trace-otlp-proto": "0.218.0", + "@opentelemetry/exporter-zipkin": "2.7.1", + "@opentelemetry/instrumentation": "0.218.0", + "@opentelemetry/otlp-exporter-base": "0.218.0", + "@opentelemetry/propagator-b3": "2.7.1", + "@opentelemetry/propagator-jaeger": "2.7.1", + "@opentelemetry/resources": "2.7.1", + "@opentelemetry/sdk-logs": "0.218.0", + "@opentelemetry/sdk-metrics": "2.7.1", + "@opentelemetry/sdk-trace-base": "2.7.1", + "@opentelemetry/sdk-trace-node": "2.7.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/core": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.1.tgz", + "integrity": "sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/exporter-logs-otlp-http": { + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-http/-/exporter-logs-otlp-http-0.218.0.tgz", + "integrity": "sha512-Qx+4rpVHzgg89dawcWRHyt+XRXeLnhFz/qBtvggmjkcgPUdr+NAB0/u/eIPA8yAeJV0J80Vz43JZCh/XFvZFGw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.218.0", + "@opentelemetry/core": "2.7.1", + "@opentelemetry/otlp-exporter-base": "0.218.0", + "@opentelemetry/otlp-transformer": "0.218.0", + "@opentelemetry/sdk-logs": "0.218.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.7.1.tgz", + "integrity": "sha512-NAYIlsF8MPUsKqJMiDQJTMPOmlbawC1Iz/omMLygZ1C9am8fTKYjTaI+OZM+WTY3t3Glo0wnOg/6/pac6RGPPw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/resources": "2.7.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, - "node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-slot": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", - "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", - "license": "MIT", + "node_modules/@opentelemetry/sdk-trace-node": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-2.7.1.tgz", + "integrity": "sha512-pCpQxU68lV+I9s9svqMyVu5iHdDDUnqUpSxqwyCU8A9ejEsSnMPCbearwsUO4yk08ZJzAIUCFuReMdVQvHrdvg==", + "license": "Apache-2.0", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" + "@opentelemetry/context-async-hooks": "2.7.1", + "@opentelemetry/core": "2.7.1", + "@opentelemetry/sdk-trace-base": "2.7.1" }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "engines": { + "node": "^18.19.0 || >=20.6.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, - "node_modules/@radix-ui/react-slot": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.4.tgz", - "integrity": "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==", - "license": "MIT", + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/core": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.1.tgz", + "integrity": "sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw==", + "license": "Apache-2.0", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" + "@opentelemetry/semantic-conventions": "^1.29.0" }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "engines": { + "node": "^18.19.0 || >=20.6.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, - "node_modules/@radix-ui/react-tabs": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.13.tgz", - "integrity": "sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==", - "license": "MIT", + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.7.1.tgz", + "integrity": "sha512-NAYIlsF8MPUsKqJMiDQJTMPOmlbawC1Iz/omMLygZ1C9am8fTKYjTaI+OZM+WTY3t3Glo0wnOg/6/pac6RGPPw==", + "license": "Apache-2.0", "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-roving-focus": "1.1.11", - "@radix-ui/react-use-controllable-state": "1.2.2" + "@opentelemetry/core": "2.7.1", + "@opentelemetry/resources": "2.7.1", + "@opentelemetry/semantic-conventions": "^1.29.0" }, - "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" + "engines": { + "node": "^18.19.0 || >=20.6.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, - "node_modules/@radix-ui/react-tooltip": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.8.tgz", - "integrity": "sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg==", - "license": "MIT", + "node_modules/@opentelemetry/semantic-conventions": { + "version": "1.41.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.41.1.tgz", + "integrity": "sha512-/UhIkaZgPutTFmQ7RnIJGgDXZmtEJ7Dvi86xNTFWcnRxVRNk/aotsqDJYeEvDP+FSMB2SdW+pQzNMcWP0rwuNA==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/sql-common": { + "version": "0.41.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/sql-common/-/sql-common-0.41.2.tgz", + "integrity": "sha512-4mhWm3Z8z+i508zQJ7r6Xi7y4mmoJpdvH0fZPFRkWrdp5fq7hhZ2HhYokEOLkfqSMgPR4Z9EyB3DBkbKGOqZiQ==", + "license": "Apache-2.0", "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.11", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-popper": "1.2.8", - "@radix-ui/react-portal": "1.1.9", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-slot": "1.2.3", - "@radix-ui/react-use-controllable-state": "1.2.2", - "@radix-ui/react-visually-hidden": "1.2.3" + "@opentelemetry/core": "^2.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" }, "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" + "@opentelemetry/api": "^1.1.0" + } + }, + "node_modules/@patternfly/chatbot": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@patternfly/chatbot/-/chatbot-6.5.0.tgz", + "integrity": "sha512-+n6v7/dbAlp5wpdlcQrx4u3sQT6FlMgiy8KRtNgzfYXc9ZZUP6RqeGvQ7qTSlp4xbfiS4U+uRS99Mv9wMahr/g==", + "license": "MIT", + "dependencies": { + "@patternfly/react-code-editor": "^6.1.0", + "@patternfly/react-core": "^6.1.0", + "@patternfly/react-icons": "^6.1.0", + "@patternfly/react-styles": "^6.1.0", + "@patternfly/react-table": "^6.1.0", + "@segment/analytics-next": "^1.76.0", + "clsx": "^2.1.0", + "path-browserify": "^1.0.1", + "posthog-js": "^1.194.4", + "react-markdown": "^9.0.1", + "rehype-external-links": "^3.0.0", + "rehype-highlight": "^7.0.0", + "rehype-sanitize": "^6.0.0", + "rehype-unwrap-images": "^1.0.0", + "remark-gfm": "^4.0.0", + "unist-util-visit": "^5.0.0" + }, + "peerDependencies": { + "@monaco-editor/react": "^4.7.0", + "monaco-editor": "^0.54.0", + "react": "^18 || ^19", + "react-dom": "^18 || ^19" }, "peerDependenciesMeta": { - "@types/react": { - "optional": true + "@monaco-editor/react": { + "optional": false }, - "@types/react-dom": { - "optional": true + "monaco-editor": { + "optional": false } } }, - "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-slot": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", - "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "node_modules/@patternfly/patternfly": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@patternfly/patternfly/-/patternfly-6.4.0.tgz", + "integrity": "sha512-4drFhg74sEc/fftark5wZevODIog17qR4pwLCdB3j5iK3Uu5oMA2SdLhsEeEQggalfnFzve/Km87MdVR0ghhvQ==", + "license": "MIT" + }, + "node_modules/@patternfly/react-code-editor": { + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/@patternfly/react-code-editor/-/react-code-editor-6.4.3.tgz", + "integrity": "sha512-1HKlm73jNu8rP8DJfJZxTEjJAM2YP2Q4EsOH3oXAF4OQia6a4wo6IzzpGlrrvmlDTXPsFa+zz08xu8ESa+rSOA==", "license": "MIT", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" + "@monaco-editor/react": "^4.6.0", + "@patternfly/react-core": "^6.4.3", + "@patternfly/react-icons": "^6.4.0", + "@patternfly/react-styles": "^6.4.0", + "react-dropzone": "14.3.5", + "tslib": "^2.8.1" }, "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "react": "^17 || ^18 || ^19", + "react-dom": "^17 || ^18 || ^19" } }, - "node_modules/@radix-ui/react-use-callback-ref": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", - "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", + "node_modules/@patternfly/react-code-editor/node_modules/react-dropzone": { + "version": "14.3.5", + "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-14.3.5.tgz", + "integrity": "sha512-9nDUaEEpqZLOz5v5SUcFA0CjM4vq8YbqO0WRls+EYT7+DvxUdzDPKNCPLqGfj3YL9MsniCLCD4RFA6M95V6KMQ==", "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "dependencies": { + "attr-accept": "^2.2.4", + "file-selector": "^2.1.0", + "prop-types": "^15.8.1" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "engines": { + "node": ">= 10.13" + }, + "peerDependencies": { + "react": ">= 16.8 || 18.0.0" } }, - "node_modules/@radix-ui/react-use-controllable-state": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", - "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", + "node_modules/@patternfly/react-core": { + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/@patternfly/react-core/-/react-core-6.4.3.tgz", + "integrity": "sha512-CYf+DFmcV5LCTQh7ZNBhHgqaEbDKwKOxBB/3Si6xFn8fIH8h/Wj3ZHSm6rYslQS7dJUusqyv5gJ2dR9LixT3TA==", "license": "MIT", "dependencies": { - "@radix-ui/react-use-effect-event": "0.0.2", - "@radix-ui/react-use-layout-effect": "1.1.1" + "@patternfly/react-icons": "^6.4.0", + "@patternfly/react-styles": "^6.4.0", + "@patternfly/react-tokens": "^6.4.0", + "focus-trap": "7.6.4", + "react-dropzone": "^14.3.5", + "tslib": "^2.8.1" }, "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "react": "^17 || ^18 || ^19", + "react-dom": "^17 || ^18 || ^19" } }, - "node_modules/@radix-ui/react-use-effect-event": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz", - "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==", + "node_modules/@patternfly/react-icons": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@patternfly/react-icons/-/react-icons-6.4.0.tgz", + "integrity": "sha512-SPjzatm73NUYv/BL6A/cjRA5sFQ15NkiyPAcT8gmklI7HY+ptd6/eg49uBDFmxTQcSwbb5ISW/R6wwCQBY2M+Q==", "license": "MIT", - "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.1" - }, "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "react": "^17 || ^18 || ^19", + "react-dom": "^17 || ^18 || ^19" } }, - "node_modules/@radix-ui/react-use-escape-keydown": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz", - "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==", + "node_modules/@patternfly/react-styles": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@patternfly/react-styles/-/react-styles-6.4.0.tgz", + "integrity": "sha512-EXmHA67s5sy+Wy/0uxWoUQ52jr9lsH2wV3QcgtvVc5zxpyBX89gShpqv4jfVqaowznHGDoL6fVBBrSe9BYOliQ==", + "license": "MIT" + }, + "node_modules/@patternfly/react-table": { + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/@patternfly/react-table/-/react-table-6.4.3.tgz", + "integrity": "sha512-lPHjVOEkJpCtXMtE48pLGwMmLsCgYJvoiZpuW2/Vlg0y0pmg0nfYUVDBBTXfQ3dgskn/zb5SAgZr158VQQoBtw==", "license": "MIT", "dependencies": { - "@radix-ui/react-use-callback-ref": "1.1.1" + "@patternfly/react-core": "^6.4.3", + "@patternfly/react-icons": "^6.4.0", + "@patternfly/react-styles": "^6.4.0", + "@patternfly/react-tokens": "^6.4.0", + "lodash": "^4.17.23", + "tslib": "^2.8.1" }, "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "react": "^17 || ^18 || ^19", + "react-dom": "^17 || ^18 || ^19" } }, - "node_modules/@radix-ui/react-use-layout-effect": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", - "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "node_modules/@patternfly/react-tokens": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@patternfly/react-tokens/-/react-tokens-6.4.0.tgz", + "integrity": "sha512-iZthBoXSGQ/+PfGTdPFJVulaJZI3rwE+7A/whOXPGp3Jyq3k6X52pr1+5nlO6WHasbZ9FyeZGqXf4fazUZNjbw==", + "license": "MIT" + }, + "node_modules/@pinojs/redact": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@pinojs/redact/-/redact-0.4.0.tgz", + "integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==", + "license": "MIT" + }, + "node_modules/@playwright/test": { + "version": "1.61.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.61.1.tgz", + "integrity": "sha512-8nKv6+0RJSL9FE4jYOEGXnPeM/Hg12qZpmqzZjRh3qM0Y7c3z1mrOTfFLids72RDQYVh9WpLEfR5WdpNX4fkig==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.61.1" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" } }, - "node_modules/@radix-ui/react-use-previous": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz", - "integrity": "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==", + "node_modules/@posthog/browser-common": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@posthog/browser-common/-/browser-common-0.5.0.tgz", + "integrity": "sha512-8DaxVZS1bQPbA514RePurLNbYjei3P4jhnC206DwVv5XThmZM3QdlsXenI2ujE3pLbgQ79hYn9o1Kda8I3WK/Q==", "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "dependencies": { + "@posthog/core": "^1.47.0", + "@posthog/types": "^1.402.2" } }, - "node_modules/@radix-ui/react-use-rect": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz", - "integrity": "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==", + "node_modules/@posthog/core": { + "version": "1.47.0", + "resolved": "https://registry.npmjs.org/@posthog/core/-/core-1.47.0.tgz", + "integrity": "sha512-LW62V+9yx7G7mLd+EYpwW0PiaPZI8XRJ1tLuhw+9fXHpugLp8XQD5JuCQ2kIXvoUfyx1DpKfGQY3dUnzMOIn7Q==", "license": "MIT", "dependencies": { - "@radix-ui/rect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "@posthog/types": "^1.402.2" } }, - "node_modules/@radix-ui/react-use-size": { + "node_modules/@posthog/types": { + "version": "1.403.0", + "resolved": "https://registry.npmjs.org/@posthog/types/-/types-1.403.0.tgz", + "integrity": "sha512-QbkO0epmdq38xhxwP214YRi0vgpZiXqhamaTjKT6kVGJYTtsE5qZ1GTgLp12IYehg/rd+whYYErH3/DeX8pj3Q==", + "license": "MIT" + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.5.tgz", + "integrity": "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz", - "integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.1.tgz", + "integrity": "sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.1.tgz", + "integrity": "sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==", + "license": "BSD-3-Clause", "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "@protobufjs/aspromise": "^1.1.1" } }, - "node_modules/@radix-ui/react-visually-hidden": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.3.tgz", - "integrity": "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==", + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.1.tgz", + "integrity": "sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg==", + "license": "BSD-3-Clause" + }, + "node_modules/@reduxjs/toolkit": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.11.2.tgz", + "integrity": "sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ==", "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.1.3" + "@standard-schema/spec": "^1.0.0", + "@standard-schema/utils": "^0.3.0", + "immer": "^11.0.0", + "redux": "^5.0.1", + "redux-thunk": "^3.1.0", + "reselect": "^5.1.0" }, "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" + "react": "^16.9.0 || ^17.0.0 || ^18 || ^19", + "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" }, "peerDependenciesMeta": { - "@types/react": { + "react": { "optional": true }, - "@types/react-dom": { + "react-redux": { "optional": true } } }, - "node_modules/@radix-ui/rect": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz", - "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==", - "license": "MIT" - }, "node_modules/@rolldown/pluginutils": { "version": "1.0.0-beta.27", "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", @@ -2261,6 +4250,97 @@ "win32" ] }, + "node_modules/@segment/analytics-core": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@segment/analytics-core/-/analytics-core-1.8.3.tgz", + "integrity": "sha512-63X8e2DWb2ZnJ9S3H8iSOFnbeDXMkTGhA8kZG4eFAO07QBwBVyWo5BCpv6vmuOMEkv2le6t2FMg1p6ZeJQA/4A==", + "license": "MIT", + "dependencies": { + "@lukeed/uuid": "^2.0.0", + "@segment/analytics-generic-utils": "1.2.0", + "dset": "^3.1.4", + "tslib": "^2.4.1" + } + }, + "node_modules/@segment/analytics-generic-utils": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@segment/analytics-generic-utils/-/analytics-generic-utils-1.2.0.tgz", + "integrity": "sha512-DfnW6mW3YQOLlDQQdR89k4EqfHb0g/3XvBXkovH1FstUN93eL1kfW9CsDcVQyH3bAC5ZsFyjA/o/1Q2j0QeoWw==", + "license": "MIT", + "dependencies": { + "tslib": "^2.4.1" + } + }, + "node_modules/@segment/analytics-next": { + "version": "1.84.0", + "resolved": "https://registry.npmjs.org/@segment/analytics-next/-/analytics-next-1.84.0.tgz", + "integrity": "sha512-V5scoCrhiTpRPOuBwNVpvE4KvHwrjgQ5fs282R72KiAZ0HoxmjLgReZYjZogBf7qq/c6VmQG3nidwh8PQGcY1A==", + "license": "MIT", + "dependencies": { + "@lukeed/uuid": "^2.0.0", + "@segment/analytics-core": "1.8.3", + "@segment/analytics-generic-utils": "1.2.0", + "@segment/analytics-page-tools": "1.0.0", + "@segment/analytics.js-video-plugins": "^0.2.1", + "@segment/facade": "^3.4.9", + "dset": "^3.1.4", + "js-cookie": "3.0.1", + "node-fetch": "^2.6.7", + "tslib": "^2.4.1", + "unfetch": "^4.1.0" + } + }, + "node_modules/@segment/analytics-page-tools": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@segment/analytics-page-tools/-/analytics-page-tools-1.0.0.tgz", + "integrity": "sha512-o9OVB91qLB9qb0Bw1HfjmWm5AnrMNULRjx++4lBqLt8InKRX1urrRBparVlpj+yJA0sckN5ZcsfazRLuPgBYDQ==", + "license": "MIT", + "dependencies": { + "tslib": "^2.4.1" + } + }, + "node_modules/@segment/analytics.js-video-plugins": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@segment/analytics.js-video-plugins/-/analytics.js-video-plugins-0.2.1.tgz", + "integrity": "sha512-lZwCyEXT4aaHBLNK433okEKdxGAuyrVmop4BpQqQSJuRz0DglPZgd9B/XjiiWs1UyOankg2aNYMN3VcS8t4eSQ==", + "license": "ISC", + "dependencies": { + "unfetch": "^3.1.1" + } + }, + "node_modules/@segment/analytics.js-video-plugins/node_modules/unfetch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/unfetch/-/unfetch-3.1.2.tgz", + "integrity": "sha512-L0qrK7ZeAudGiKYw6nzFjnJ2D5WHblUBwmHIqtPS6oKUd+Hcpk7/hKsSmcHsTlpd1TbTNsiRBUKRq3bHLNIqIw==", + "license": "MIT" + }, + "node_modules/@segment/facade": { + "version": "3.4.10", + "resolved": "https://registry.npmjs.org/@segment/facade/-/facade-3.4.10.tgz", + "integrity": "sha512-xVQBbB/lNvk/u8+ey0kC/+g8pT3l0gCT8O2y9Z+StMMn3KAFAQ9w8xfgef67tJybktOKKU7pQGRPolRM1i1pdA==", + "license": "SEE LICENSE IN LICENSE", + "dependencies": { + "@segment/isodate-traverse": "^1.1.1", + "inherits": "^2.0.4", + "new-date": "^1.0.3", + "obj-case": "0.2.1" + } + }, + "node_modules/@segment/isodate": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@segment/isodate/-/isodate-1.0.3.tgz", + "integrity": "sha512-BtanDuvJqnACFkeeYje7pWULVv8RgZaqKHWwGFnL/g/TH/CcZjkIVTfGDp/MAxmilYHUkrX70SqwnYSTNEaN7A==", + "license": "SEE LICENSE IN LICENSE" + }, + "node_modules/@segment/isodate-traverse": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@segment/isodate-traverse/-/isodate-traverse-1.1.1.tgz", + "integrity": "sha512-+G6e1SgAUkcq0EDMi+SRLfT48TNlLPF3QnSgFGVs0V9F3o3fq/woQ2rHFlW20W0yy5NnCUH0QGU3Am2rZy/E3w==", + "license": "SEE LICENSE IN LICENSE", + "dependencies": { + "@segment/isodate": "^1.0.3" + } + }, "node_modules/@sideway/address": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", @@ -2288,6 +4368,25 @@ "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", "license": "BSD-3-Clause" }, + "node_modules/@sinclair/typebox": { + "version": "0.34.50", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.50.tgz", + "integrity": "sha512-ydBWw0G6WFwWHzh9RK4B5c690UkreOG0llq0r+DaI7LgKgxigf8mhHzIPI3S0850g1BPkq/zpuCfrq4QFgUlTQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "license": "MIT" + }, + "node_modules/@standard-schema/utils": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz", + "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", + "license": "MIT" + }, "node_modules/@swc/core": { "version": "1.15.18", "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.15.18.tgz", @@ -2725,6 +4824,64 @@ "node": ">=14.0.0" } }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.8.1", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.1.0", + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.8.1", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": { + "version": "1.1.0", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.1", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1", + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": { + "version": "2.8.1", + "inBundle": true, + "license": "0BSD", + "optional": true + }, "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.1.tgz", @@ -2771,6 +4928,139 @@ "vite": "^5.2.0 || ^6 || ^7" } }, + "node_modules/@testing-library/dom": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", + "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "picocolors": "1.1.1", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@testing-library/jest-dom": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.9.1.tgz", + "integrity": "sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@adobe/css-tools": "^4.4.0", + "aria-query": "^5.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.6.3", + "picocolors": "^1.1.1", + "redent": "^3.0.0" + }, + "engines": { + "node": ">=14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", + "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@testing-library/react": { + "version": "16.3.2", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.2.tgz", + "integrity": "sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@testing-library/dom": "^10.0.0", + "@types/react": "^18.0.0 || ^19.0.0", + "@types/react-dom": "^18.0.0 || ^19.0.0", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@testing-library/user-event": { + "version": "14.6.1", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.1.tgz", + "integrity": "sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "peerDependencies": { + "@testing-library/dom": ">=7.21.4" + } + }, + "node_modules/@types/aria-query": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@types/aws-lambda": { + "version": "8.10.161", + "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.161.tgz", + "integrity": "sha512-rUYdp+MQwSFocxIOcSsYSF3YYYC/uUpMbCY/mbO21vGqfrEYvNSoPyKYDj6RhXXpPfS0KstW9RwG3qXh9sL7FQ==", + "license": "MIT" + }, + "node_modules/@types/bunyan": { + "version": "1.8.11", + "resolved": "https://registry.npmjs.org/@types/bunyan/-/bunyan-1.8.11.tgz", + "integrity": "sha512-758fRH7umIMk5qt5ELmRMff4mLDlN+xyYzC+dkPTdKwbSkJFvz6xwyScrytPU0QIBbRRwbiE8/BIg8bpajerNQ==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/debug": { "version": "4.1.12", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", @@ -2780,6 +5070,13 @@ "@types/ms": "*" } }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -2804,6 +5101,91 @@ "@types/unist": "*" } }, + "node_modules/@types/hoist-non-react-statics": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.7.tgz", + "integrity": "sha512-PQTyIulDkIDro8P+IHbKCsw7U2xxBYflVzW/FgWdCAePD9xGSidgA76/GeJ6lBKoblyhf9pBY763gbrN+1dI8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "hoist-non-react-statics": "^3.3.0" + }, + "peerDependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "30.0.0", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-30.0.0.tgz", + "integrity": "sha512-XTYugzhuwqWjws0CVz8QpM36+T+Dz5mTEBKhNs/esGLnCIlGdRy+Dq78NRjd7ls7r8BC8ZRMOrKlkO1hU0JOwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^30.0.0", + "pretty-format": "^30.0.0" + } + }, + "node_modules/@types/jest-axe": { + "version": "3.5.9", + "resolved": "https://registry.npmjs.org/@types/jest-axe/-/jest-axe-3.5.9.tgz", + "integrity": "sha512-z98CzR0yVDalCEuhGXXO4/zN4HHuSebAukXDjTLJyjEAgoUf1H1i+sr7SUB/mz8CRS/03/XChsx0dcLjHkndoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/jest": "*", + "axe-core": "^3.5.5" + } + }, + "node_modules/@types/jest/node_modules/pretty-format": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.4.1.tgz", + "integrity": "sha512-K6KiKMHTL4jjX4u3Kir2EW07nRfcqVTXIImx50wbjHQTcZPgg+gjVeNTIT3l3L1Rd4UefxfogquC9J37SoFyyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "30.4.1", + "ansi-styles": "^5.2.0", + "react-is-18": "npm:react-is@^18.3.1", + "react-is-19": "npm:react-is@^19.2.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@types/js-yaml": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", + "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -2819,22 +5201,68 @@ "@types/unist": "*" } }, + "node_modules/@types/memcached": { + "version": "2.2.10", + "resolved": "https://registry.npmjs.org/@types/memcached/-/memcached-2.2.10.tgz", + "integrity": "sha512-AM9smvZN55Gzs2wRrqeMHVP7KE8KWgCJO/XL5yCly2xF6EKa4YlbpK+cLSAH4NG/Ah64HrlegmGqW8kYws7Vxg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/ms": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", "license": "MIT" }, + "node_modules/@types/mysql": { + "version": "2.15.27", + "resolved": "https://registry.npmjs.org/@types/mysql/-/mysql-2.15.27.tgz", + "integrity": "sha512-YfWiV16IY0OeBfBCk8+hXKmdTKrKlwKN1MNKAPBu5JYxLwBEZl7QzeEpGnlZb3VMGJrrGmB84gXiH+ofs/TezA==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/node": { "version": "22.19.15", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.15.tgz", "integrity": "sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg==", - "devOptional": true, "license": "MIT", "dependencies": { "undici-types": "~6.21.0" } }, + "node_modules/@types/oracledb": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/@types/oracledb/-/oracledb-6.5.2.tgz", + "integrity": "sha512-kK1eBS/Adeyis+3OlBDMeQQuasIDLUYXsi2T15ccNJ0iyUpQ4xDF7svFu3+bGVrI0CMBUclPciz+lsQR3JX3TQ==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/pg": { + "version": "8.15.6", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.15.6.tgz", + "integrity": "sha512-NoaMtzhxOrubeL/7UZuNTrejB4MPAJ0RpxZqXQf2qXuVlTPuG6Y8p4u9dKRaue4yjmC7ZhzVO2/Yyyn25znrPQ==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "pg-protocol": "*", + "pg-types": "^2.2.0" + } + }, + "node_modules/@types/pg-pool": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/pg-pool/-/pg-pool-2.0.7.tgz", + "integrity": "sha512-U4CwmGVQcbEuqpyju8/ptOKg6gEC+Tqsvj2xS9o1g71bUh8twxnC6ZL5rZKCsGN0iyH0CwgUyc9VR5owNQF9Ng==", + "license": "MIT", + "dependencies": { + "@types/pg": "*" + } + }, "node_modules/@types/react": { "version": "19.2.14", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", @@ -2848,30 +5276,99 @@ "version": "19.2.3", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", - "devOptional": true, + "dev": true, "license": "MIT", "peerDependencies": { "@types/react": "^19.2.0" } }, + "node_modules/@types/react-redux": { + "version": "7.1.34", + "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.34.tgz", + "integrity": "sha512-GdFaVjEbYv4Fthm2ZLvj1VSCedV7TqE5y1kNwnjSdBOTXuRSgowux6J8TAct15T3CKBr63UMk+2CO7ilRhyrAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hoist-non-react-statics": "^3.3.0", + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0", + "redux": "^4.0.0" + } + }, + "node_modules/@types/react-redux/node_modules/redux": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", + "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.9.2" + } + }, "node_modules/@types/retry": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", "license": "MIT" }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/tedious": { + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/@types/tedious/-/tedious-4.0.14.tgz", + "integrity": "sha512-KHPsfX/FoVbUGbyYvk1q9MMQHLPeRZhRJZdO45Q4YjvFkv4hMNghCWTvy7rdKessBsmtz4euWCWAB6/tVpI1Iw==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT", + "optional": true + }, "node_modules/@types/unist": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", "license": "MIT" }, + "node_modules/@types/use-sync-external-store": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", + "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==", + "license": "MIT" + }, "node_modules/@types/uuid": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==", "license": "MIT" }, + "node_modules/@types/yargs": { + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.57.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.57.1.tgz", @@ -3071,16 +5568,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", - "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { - "node": "18 || 20 || >=22" + "node": "20 || >=22" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { @@ -3151,27 +5648,171 @@ "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "license": "ISC" + }, + "node_modules/@vitejs/plugin-react-swc": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-3.11.0.tgz", + "integrity": "sha512-YTJCGFdNMHCMfjODYtxRNVAYmTWQ1Lb8PulP/2/f/oEEtglw8oKxKIZmmRkyXrVrHfsKOaVkAc3NT9/dMutO5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "1.0.0-beta.27", + "@swc/core": "^1.12.11" + }, + "peerDependencies": { + "vite": "^4 || ^5 || ^6 || ^7" + } + }, + "node_modules/@vitest/coverage-v8": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.10.tgz", + "integrity": "sha512-IM49HmthevbgAO4anp1hwtoT9wYe59w0LR00gr+eagHE+ZJ5lK4sLPeO0ubgoJcwLk6dehU3R24N+FbEEKDc8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^1.0.2", + "@vitest/utils": "4.1.10", + "ast-v8-to-istanbul": "^1.0.0", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.2.0", + "magicast": "^0.5.2", + "obug": "^2.1.1", + "std-env": "^4.0.0-rc.1", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@vitest/browser": "4.1.10", + "vitest": "4.1.10" + }, + "peerDependenciesMeta": { + "@vitest/browser": { + "optional": true + } + } + }, + "node_modules/@vitest/expect": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.10.tgz", + "integrity": "sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.1.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.1.10", + "@vitest/utils": "4.1.10", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.10.tgz", + "integrity": "sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.1.10", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.10.tgz", + "integrity": "sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.10.tgz", + "integrity": "sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.1.10", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.10.tgz", + "integrity": "sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.10", + "@vitest/utils": "4.1.10", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.10.tgz", + "integrity": "sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/@ungap/structured-clone": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", - "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", - "license": "ISC" - }, - "node_modules/@vitejs/plugin-react-swc": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-3.11.0.tgz", - "integrity": "sha512-YTJCGFdNMHCMfjODYtxRNVAYmTWQ1Lb8PulP/2/f/oEEtglw8oKxKIZmmRkyXrVrHfsKOaVkAc3NT9/dMutO5w==", + "node_modules/@vitest/utils": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.10.tgz", + "integrity": "sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==", "dev": true, "license": "MIT", "dependencies": { - "@rolldown/pluginutils": "1.0.0-beta.27", - "@swc/core": "^1.12.11" + "@vitest/pretty-format": "4.1.10", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" }, - "peerDependencies": { - "vite": "^4 || ^5 || ^6 || ^7" + "funding": { + "url": "https://opencollective.com/vitest" } }, "node_modules/abstract-logging": { @@ -3184,7 +5825,6 @@ "version": "8.16.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", - "dev": true, "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -3193,6 +5833,15 @@ "node": ">=0.4.0" } }, + "node_modules/acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", + "license": "MIT", + "peerDependencies": { + "acorn": "^8" + } + }, "node_modules/acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", @@ -3203,6 +5852,15 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, "node_modules/ajv": { "version": "6.14.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", @@ -3263,7 +5921,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -3299,21 +5956,47 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, "license": "Python-2.0" }, - "node_modules/aria-hidden": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", - "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", - "license": "MIT", + "node_modules/aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "dev": true, + "license": "Apache-2.0", "dependencies": { - "tslib": "^2.0.0" - }, + "dequal": "^2.0.3" + } + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=12" + } + }, + "node_modules/ast-v8-to-istanbul": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-1.0.4.tgz", + "integrity": "sha512-0bC0/4bTSrnwdhU3IsZDwEdojvuPrSg59OYZfKsLRtJZ0u8VBx9DebfqqG8bRdCC0I7vjgxmPi41P0lpkhJHtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.31", + "estree-walker": "^3.0.3", + "js-tokens": "^10.0.0" } }, + "node_modules/ast-v8-to-istanbul/node_modules/js-tokens": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-10.0.0.tgz", + "integrity": "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==", + "dev": true, + "license": "MIT" + }, "node_modules/atomic-sleep": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", @@ -3323,6 +6006,15 @@ "node": ">=8.0.0" } }, + "node_modules/attr-accept": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.5.tgz", + "integrity": "sha512-0bDNnY/u6pPwHDMoF0FieU354oBi0a8rD9FcsLwzcGWbc8KS8KPIi7y+s13OlVY+gMWc/9xEMUgNE6Qm8ZllYQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/avvio": { "version": "9.2.0", "resolved": "https://registry.npmjs.org/avvio/-/avvio-9.2.0.tgz", @@ -3343,6 +6035,16 @@ "fastq": "^1.17.1" } }, + "node_modules/axe-core": { + "version": "3.5.6", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-3.5.6.tgz", + "integrity": "sha512-LEUDjgmdJoA3LqklSTwKYqkjcZ4HKc4ddIYGSAiSkr46NTjzg2L9RNB+lekO9P7Dlpa87+hBtzc2Fzn/+GUWMQ==", + "dev": true, + "license": "MPL-2.0", + "engines": { + "node": ">=4" + } + }, "node_modules/bail": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", @@ -3380,6 +6082,25 @@ ], "license": "MIT" }, + "node_modules/bidi-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", + "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "require-from-string": "^2.0.2" + } + }, + "node_modules/bignumber.js": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", + "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -3394,9 +6115,9 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "dev": true, "license": "MIT", "dependencies": { @@ -3449,10 +6170,21 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", @@ -3469,6 +6201,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -3484,6 +6217,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "license": "MIT", "dependencies": { "has-flag": "^4.0.0" @@ -3570,23 +6304,32 @@ "node": ">= 6" } }, - "node_modules/class-variance-authority": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", - "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", - "license": "Apache-2.0", - "dependencies": { - "clsx": "^2.1.1" - }, - "funding": { - "url": "https://polar.sh/cva" + "node_modules/ci-info": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" } }, + "node_modules/cjs-module-lexer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", + "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", + "license": "MIT" + }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, "license": "ISC", "dependencies": { "string-width": "^4.2.0", @@ -3606,6 +6349,15 @@ "node": ">=6" } }, + "node_modules/cluster-key-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -3649,15 +6401,15 @@ "license": "MIT" }, "node_modules/concurrently": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.1.tgz", - "integrity": "sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==", + "version": "9.2.4", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.4.tgz", + "integrity": "sha512-TZ0CEhyzvFjgtAvHTusDMgj7wNdihCh7LLLrzdUOXIhdlnL2JBBGA9eJxR24rtqgmdjh3OA3hrN1rCHj6HM8qA==", "dev": true, "license": "MIT", "dependencies": { "chalk": "4.1.2", "rxjs": "7.8.2", - "shell-quote": "1.8.3", + "shell-quote": "1.9.0", "supports-color": "8.1.1", "tree-kill": "1.2.2", "yargs": "17.7.2" @@ -3673,27 +6425,26 @@ "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" } }, - "node_modules/console-table-printer": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/console-table-printer/-/console-table-printer-2.15.0.tgz", - "integrity": "sha512-SrhBq4hYVjLCkBVOWaTzceJalvn5K1Zq5aQA6wXC/cYjI3frKWNPEMK3sZsJfNNQApvCQmgBcc13ZKmFj8qExw==", - "license": "MIT", - "dependencies": { - "simple-wcswidth": "^1.1.2" - } - }, "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-2.0.1.tgz", + "integrity": "sha512-e+H0ZXHSWYrENhQzw1LPuP4oF5MzVKmDU6d3hxlvaPEYLLg62MxtQNPRx4SYSuYJSBUgnQIG4HIN2tEtNv7Dog==", "license": "MIT", - "dependencies": { - "safe-buffer": "5.2.1" - }, "engines": { - "node": ">= 0.6" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, "node_modules/cookie": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", @@ -3707,10 +6458,22 @@ "url": "https://opencollective.com/express" } }, + "node_modules/core-js": { + "version": "3.49.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.49.0.tgz", + "integrity": "sha512-es1U2+YTtzpwkxVLwAFdSpaIMyQaq0PBgm3YD1W3Qpsn1NAmO3KSgZfu+oGSWVu6NvLHoHCV/aYcsE5wiB7ALg==", + "hasInstallScript": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -3721,12 +6484,56 @@ "node": ">= 8" } }, + "node_modules/css-tree": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", + "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.27.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "dev": true, + "license": "MIT" + }, "node_modules/csstype": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", "license": "MIT" }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/data-urls": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-7.0.0.tgz", + "integrity": "sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^16.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, "node_modules/dateformat": { "version": "4.6.3", "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz", @@ -3763,6 +6570,13 @@ "node": ">=0.10.0" } }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "dev": true, + "license": "MIT" + }, "node_modules/decode-named-character-reference": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz", @@ -3783,6 +6597,15 @@ "dev": true, "license": "MIT" }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10" + } + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -3810,12 +6633,6 @@ "node": ">=8" } }, - "node_modules/detect-node-es": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", - "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", - "license": "MIT" - }, "node_modules/devlop": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", @@ -3829,11 +6646,44 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/dompurify": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.7.tgz", + "integrity": "sha512-VaTstWtsneJY8xzy7DekmYWEOZcmzIe3Qb3zPd4STve1OBTa+e+WmS1ITQec1fZYXI3HCsOZZiSMpG6oxoWMWQ==", + "license": "(MPL-2.0 OR Apache-2.0)", + "peer": true + }, + "node_modules/dset": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/dset/-/dset-3.1.4.tgz", + "integrity": "sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, "license": "MIT" }, "node_modules/end-of-stream": { @@ -3859,6 +6709,26 @@ "node": ">=10.13.0" } }, + "node_modules/entities": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-8.0.0.tgz", + "integrity": "sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-module-lexer": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", + "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", + "dev": true, + "license": "MIT" + }, "node_modules/esbuild": { "version": "0.27.4", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.4.tgz", @@ -3905,7 +6775,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -4107,6 +6976,16 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -4123,6 +7002,34 @@ "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", "license": "MIT" }, + "node_modules/expect": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/expect/-/expect-30.4.1.tgz", + "integrity": "sha512-PMARsyh/JtqC20HoGqlFcIlQAyqUtW4PlI1rup1uhYJtKuwAjbvWi3GQMAn+STdHum/dk8xrKfUM1+5SAwpolA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/expect-utils": "30.4.1", + "@jest/get-type": "30.1.0", + "jest-matcher-utils": "30.4.1", + "jest-message-util": "30.4.1", + "jest-mock": "30.4.1", + "jest-util": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -4225,9 +7132,58 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", - "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.5.tgz", + "integrity": "sha512-gHwA1O9LDIcKunMKhObS/HimwtehO1nPUECKAu5TpKgaO19fcWEl4bliWe1jWxVFvIXztJjjQ4L8XQ1EU9f7Jw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fastify": { + "version": "5.11.3", + "resolved": "https://registry.npmjs.org/fastify/-/fastify-5.11.3.tgz", + "integrity": "sha512-W6hzDP8s0iSeL7LGwY6Oc/ZxuXWOvFEMs6p2L0Si415YRo27W5pBKdOTXxhemBDeSTAcpYf5evRA9onF2OYhPA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "@fastify/ajv-compiler": "^4.0.5", + "@fastify/error": "^4.0.0", + "@fastify/fast-json-stringify-compiler": "^5.0.0", + "@fastify/proxy-addr": "^5.0.0", + "abstract-logging": "^2.0.1", + "avvio": "^9.0.0", + "fast-json-stringify": "^7.0.0", + "find-my-way": "^9.6.0", + "light-my-request": "^6.0.0", + "pino": "^9.14.0 || ^10.1.0", + "process-warning": "^5.0.0", + "rfdc": "^1.3.1", + "secure-json-parse": "^4.0.0", + "semver": "^7.6.0", + "toad-cache": "^3.7.0" + } + }, + "node_modules/fastify-plugin": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/fastify-plugin/-/fastify-plugin-5.1.0.tgz", + "integrity": "sha512-FAIDA8eovSt5qcDgcBvDuX/v0Cjz0ohGhENZ/wpc3y+oZCY2afZ9Baqql3g/lC+OHRnciQol4ww7tuthOb9idw==", "funding": [ { "type": "github", @@ -4238,12 +7194,28 @@ "url": "https://opencollective.com/fastify" } ], - "license": "BSD-3-Clause" + "license": "MIT" }, - "node_modules/fastify": { - "version": "5.8.2", - "resolved": "https://registry.npmjs.org/fastify/-/fastify-5.8.2.tgz", - "integrity": "sha512-lZmt3navvZG915IE+f7/TIVamxIwmBd+OMB+O9WBzcpIwOo6F0LTh0sluoMFk5VkrKTvvrwIaoJPkir4Z+jtAg==", + "node_modules/fastify/node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/fastify/node_modules/fast-json-stringify": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-7.0.1.tgz", + "integrity": "sha512-eRSayARSbbwlBjpP4vnTTIRD5QPcIrmihPxDeN1DtKnHPg66UuJLx+8hlK1kaFdjvzyQ/dzALoi4vwAQ+T+iZA==", "funding": [ { "type": "github", @@ -4256,27 +7228,18 @@ ], "license": "MIT", "dependencies": { - "@fastify/ajv-compiler": "^4.0.5", - "@fastify/error": "^4.0.0", - "@fastify/fast-json-stringify-compiler": "^5.0.0", - "@fastify/proxy-addr": "^5.0.0", - "abstract-logging": "^2.0.1", - "avvio": "^9.0.0", - "fast-json-stringify": "^6.0.0", - "find-my-way": "^9.0.0", - "light-my-request": "^6.0.0", - "pino": "^9.14.0 || ^10.1.0", - "process-warning": "^5.0.0", - "rfdc": "^1.3.1", - "secure-json-parse": "^4.0.0", - "semver": "^7.6.0", - "toad-cache": "^3.7.0" + "@fastify/merge-json-schemas": "^0.2.0", + "ajv": "^8.12.0", + "ajv-formats": "^3.0.1", + "fast-uri": "^4.0.0", + "json-schema-ref-resolver": "^3.0.0", + "rfdc": "^1.2.0" } }, - "node_modules/fastify-plugin": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/fastify-plugin/-/fastify-plugin-5.1.0.tgz", - "integrity": "sha512-FAIDA8eovSt5qcDgcBvDuX/v0Cjz0ohGhENZ/wpc3y+oZCY2afZ9Baqql3g/lC+OHRnciQol4ww7tuthOb9idw==", + "node_modules/fastify/node_modules/fast-json-stringify/node_modules/fast-uri": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-4.1.2.tgz", + "integrity": "sha512-TyGmBcbDTZXcb2cj5MV89DrF42DKvb3y5DDUNh95iO+IMeAzMkVSxK1PZRrRIpc9yg8U2GhGdbofNa0LS/a4Bw==", "funding": [ { "type": "github", @@ -4287,6 +7250,12 @@ "url": "https://opencollective.com/fastify" } ], + "license": "BSD-3-Clause" + }, + "node_modules/fastify/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "license": "MIT" }, "node_modules/fastq": { @@ -4298,6 +7267,35 @@ "reusify": "^1.0.4" } }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/fflate": { + "version": "0.4.8", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.4.8.tgz", + "integrity": "sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==", + "license": "MIT" + }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -4311,6 +7309,18 @@ "node": ">=16.0.0" } }, + "node_modules/file-selector": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-2.1.2.tgz", + "integrity": "sha512-QgXo+mXTe8ljeqUFaX3QVHc5osSItJ/Km+xpocx0aSqWGMSCf6qYs/VnzZgS864Pjn5iceMRFigeAV7AfTlaig==", + "license": "MIT", + "dependencies": { + "tslib": "^2.7.0" + }, + "engines": { + "node": ">= 12" + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -4325,9 +7335,9 @@ } }, "node_modules/find-my-way": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-9.5.0.tgz", - "integrity": "sha512-VW2RfnmscZO5KgBY5XVyKREMW5nMZcxDy+buTOsL+zIPnBlbKm+00sgzoQzq1EVh4aALZLfKdwv6atBGcjvjrQ==", + "version": "9.7.0", + "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-9.7.0.tgz", + "integrity": "sha512-f2JHn75x2JlwUwLenZypgczR7YWMb/uO9BvUXtus+JMgkbIkLADd38cI4EiV+OQqrGo1Zlq6V8wnqMJ8e62wUQ==", "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", @@ -4370,28 +7380,39 @@ } }, "node_modules/flatted": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.1.tgz", - "integrity": "sha512-IxfVbRFVlV8V/yRaGzk0UVIcsKKHMSfYw66T/u4nTwlWteQePsxe//LjudR1AMX4tZW3WFCh3Zqa/sjlqpbURQ==", + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.4.tgz", + "integrity": "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==", "dev": true, "license": "ISC" }, - "node_modules/foreground-child": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "license": "ISC", + "node_modules/focus-trap": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.6.4.tgz", + "integrity": "sha512-xx560wGBk7seZ6y933idtjJQc1l+ck+pI3sKvhKozdBV1dRZoKhkW5xoCaFv9tQiX5RH1xfSxjuNu6g+lmN/gw==", + "license": "MIT", "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" + "tabbable": "^6.2.0" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" }, "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=12.20.0" } }, + "node_modules/forwarded-parse": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/forwarded-parse/-/forwarded-parse-2.1.2.tgz", + "integrity": "sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==", + "license": "MIT" + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -4406,25 +7427,61 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/gaxios": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.4.tgz", + "integrity": "sha512-bTIgTsM2bWn3XklZISBTQX7ZSddGW+IO3bMdGaemHZ3tbqExMENHLx6kKZ/KlejgrMtj8q7wBItt51yegqalrA==", + "license": "Apache-2.0", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "node-fetch": "^3.3.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/gaxios/node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/gcp-metadata": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-8.1.2.tgz", + "integrity": "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==", + "license": "Apache-2.0", + "dependencies": { + "gaxios": "^7.0.0", + "google-logging-utils": "^1.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/get-nonce": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", - "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/get-tsconfig": { "version": "4.13.6", "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.6.tgz", @@ -4439,24 +7496,17 @@ } }, "node_modules/glob": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", - "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", "license": "BlueOak-1.0.0", "dependencies": { - "foreground-child": "^3.3.1", - "jackspeak": "^4.1.1", - "minimatch": "^10.1.1", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^2.0.0" - }, - "bin": { - "glob": "dist/esm/bin.mjs" + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" }, "engines": { - "node": "20 || >=22" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -4485,24 +7535,24 @@ } }, "node_modules/glob/node_modules/brace-expansion": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", - "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { - "node": "18 || 20 || >=22" + "node": "20 || >=22" } }, "node_modules/glob/node_modules/minimatch": { - "version": "10.2.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", - "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^5.0.2" + "brace-expansion": "^5.0.8" }, "engines": { "node": "18 || 20 || >=22" @@ -4524,6 +7574,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/google-logging-utils": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-1.1.3.tgz", + "integrity": "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -4534,11 +7593,67 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" } }, + "node_modules/hast-util-has-property": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-has-property/-/hast-util-has-property-3.0.0.tgz", + "integrity": "sha512-MNilsvEKLFpV604hwfhVStK0usFY/QmM5zX16bo7EjnAEGofr5YyI37kzopBlZJkHD4t887i+q/C8/tr5Q94cA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-interactive": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-interactive/-/hast-util-interactive-3.0.0.tgz", + "integrity": "sha512-9VFa3kP6AT40BNYcPmn3jpsG+1KPDF0rUFCrFVQDUsuUXZ3YLODm8UGV0tmYzFpcOIQXTAOi2ccS3ywlj2dQTA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-has-property": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-is-element": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz", + "integrity": "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-sanitize": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/hast-util-sanitize/-/hast-util-sanitize-5.0.2.tgz", + "integrity": "sha512-3yTWghByc50aGS7JlGhk61SPenfE/p1oaFeNwkOOyrscaOkMGrcW9+Cy/QAIOBpZxP1yqDIzFMR0+Np0i0+usg==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@ungap/structured-clone": "^1.0.0", + "unist-util-position": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/hast-util-to-jsx-runtime": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", @@ -4566,6 +7681,22 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/hast-util-to-text": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-4.0.2.tgz", + "integrity": "sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "hast-util-is-element": "^3.0.0", + "unist-util-find-after": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/hast-util-whitespace": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", @@ -4579,6 +7710,15 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/helmet": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-8.1.0.tgz", + "integrity": "sha512-jOiHyAZsmnr8LqoPGmCjYAaiuWwjAPLgY8ZX2XrmHawt99/u1y6RgrZMTeoPfpUbV96HOalYgz1qzkRbw54Pmg==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/help-me": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz", @@ -4586,6 +7726,45 @@ "dev": true, "license": "MIT" }, + "node_modules/highlight.js": { + "version": "11.11.1", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz", + "integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz", + "integrity": "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@exodus/bytes": "^1.6.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, "node_modules/html-url-attributes": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz", @@ -4616,6 +7795,19 @@ "url": "https://opencollective.com/express" } }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -4633,6 +7825,16 @@ "dev": true, "license": "ISC" }, + "node_modules/immer": { + "version": "11.1.8", + "resolved": "https://registry.npmjs.org/immer/-/immer-11.1.8.tgz", + "integrity": "sha512-/tbkHMW7y10Lx6i1crLjD4/OhNkRG+Fo7byZHtah0547nIeXYcpIXaUh0IAQY6gO5459qpGGYapcEOHtFXkIuA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, "node_modules/import-fresh": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", @@ -4650,6 +7852,21 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/import-in-the-middle": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-3.0.1.tgz", + "integrity": "sha512-pYkiyXVL2Mf3pozdlDGV6NAObxQx13Ae8knZk1UJRJ6uRW/ZRmTGHlQYtrsSl7ubuE5F8CD1z+s1n4RHNuTtuA==", + "license": "Apache-2.0", + "dependencies": { + "acorn": "^8.15.0", + "acorn-import-attributes": "^1.9.5", + "cjs-module-lexer": "^2.2.0", + "module-details-from-path": "^1.0.4" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -4660,6 +7877,16 @@ "node": ">=0.8.19" } }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -4672,6 +7899,30 @@ "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==", "license": "MIT" }, + "node_modules/ioredis": { + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.10.1.tgz", + "integrity": "sha512-HuEDBTI70aYdx1v6U97SbNx9F1+svQKBDo30o0b9fw055LMepzpOOd0Ccg9Q6tbqmBSJaMuY0fB7yw9/vjBYCA==", + "license": "MIT", + "dependencies": { + "@ioredis/commands": "1.5.1", + "cluster-key-slot": "^1.1.0", + "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" + }, + "engines": { + "node": ">=12.22.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ioredis" + } + }, "node_modules/ipaddr.js": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.3.0.tgz", @@ -4681,6 +7932,18 @@ "node": ">= 10" } }, + "node_modules/is-absolute-url": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-4.0.1.tgz", + "integrity": "sha512-/51/TKE88Lmm7Gc4/8btclNXWS+g50wXhYJq8HWIBAGUBnoAdRu1aXeh364t/O7wXDAcTJDP8PNuNKWUDWie+A==", + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-alphabetical": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", @@ -4742,7 +8005,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -4771,6 +8033,18 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/is-network-error": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.3.2.tgz", + "integrity": "sha512-PhBY86zaxNZUuWP6h13Vu5oFe0XY6/UlKzQnYFELzGVHygP3MxmvTfYSG7GN3aIab/iWudSMgjSnG9Dq+nHrgA==", + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -4793,25 +8067,351 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, + "license": "MIT" + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, "license": "ISC" }, - "node_modules/jackspeak": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.2.3.tgz", - "integrity": "sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==", - "license": "BlueOak-1.0.0", + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@isaacs/cliui": "^9.0.0" + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" }, "engines": { - "node": "20 || >=22" + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-axe": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/jest-axe/-/jest-axe-10.0.0.tgz", + "integrity": "sha512-9QR0M7//o5UVRnEUUm68IsGapHrcKGakYy9dKWWMX79LmeUKguDI6DREyljC5I13j78OUmtKLF5My6ccffLFBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "axe-core": "4.10.2", + "chalk": "4.1.2", + "jest-matcher-utils": "29.2.2", + "lodash.merge": "4.6.2" + }, + "engines": { + "node": ">= 16.0.0" + } + }, + "node_modules/jest-axe/node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-axe/node_modules/@sinclair/typebox": { + "version": "0.27.10", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz", + "integrity": "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/jest-axe/node_modules/axe-core": { + "version": "4.10.2", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.2.tgz", + "integrity": "sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w==", + "dev": true, + "license": "MPL-2.0", + "engines": { + "node": ">=4" + } + }, + "node_modules/jest-axe/node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-axe/node_modules/jest-matcher-utils": { + "version": "29.2.2", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.2.2.tgz", + "integrity": "sha512-4DkJ1sDPT+UX2MR7Y3od6KtvRi9Im1ZGLGgdLFLm4lPexbTaCgJW5NN3IOXlQHF7NSHY/VHhflQ+WoKtD/vyCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.2.1", + "jest-get-type": "^29.2.0", + "pretty-format": "^29.2.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-axe/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-axe/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/jest-diff": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.4.1.tgz", + "integrity": "sha512-CRpFK0RtLriVDGcPPAnR6HMVI8bSR2jnUIgralhauzYQZIb4RH9AtEInTuQr65LmmGggGcRT6HIASxwqsVsmlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/diff-sequences": "30.4.0", + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "pretty-format": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-diff/node_modules/pretty-format": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.4.1.tgz", + "integrity": "sha512-K6KiKMHTL4jjX4u3Kir2EW07nRfcqVTXIImx50wbjHQTcZPgg+gjVeNTIT3l3L1Rd4UefxfogquC9J37SoFyyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "30.4.1", + "ansi-styles": "^5.2.0", + "react-is-18": "npm:react-is@^18.3.1", + "react-is-19": "npm:react-is@^19.2.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.4.1.tgz", + "integrity": "sha512-zvYfX5CaeEkFrrLS9suWe9rvJrm9J1Iv3ua8kIBv9GEPzcnsfBf0bob37la7s67fs0nlBC3EuvkOLnXQKxtx4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "jest-diff": "30.4.1", + "pretty-format": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-matcher-utils/node_modules/pretty-format": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.4.1.tgz", + "integrity": "sha512-K6KiKMHTL4jjX4u3Kir2EW07nRfcqVTXIImx50wbjHQTcZPgg+gjVeNTIT3l3L1Rd4UefxfogquC9J37SoFyyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "30.4.1", + "ansi-styles": "^5.2.0", + "react-is-18": "npm:react-is@^18.3.1", + "react-is-19": "npm:react-is@^19.2.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.4.1.tgz", + "integrity": "sha512-kwCKIvq0MCW1HzLoGola9Te6JUdzgV0loyKJ3Qghrkz9i5/RRIHsL95BMQc2HBBhlBKC4j22K9p11TGHH8RBpQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@jest/types": "30.4.1", + "@types/stack-utils": "^2.0.3", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "jest-util": "30.4.1", + "picomatch": "^4.0.3", + "pretty-format": "30.4.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.6" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-message-util/node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/jest-message-util/node_modules/pretty-format": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.4.1.tgz", + "integrity": "sha512-K6KiKMHTL4jjX4u3Kir2EW07nRfcqVTXIImx50wbjHQTcZPgg+gjVeNTIT3l3L1Rd4UefxfogquC9J37SoFyyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "30.4.1", + "ansi-styles": "^5.2.0", + "react-is-18": "npm:react-is@^18.3.1", + "react-is-19": "npm:react-is@^19.2.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-mock": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.4.1.tgz", + "integrity": "sha512-/i8SVb8/NSB7RfNi8gfqu8gxLV23KaL5EpAttyb9iz8qWRIqXRLflycz/32wXsYkOnaUlx8NAKnJYtpsmXUmfw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.4.1", + "@types/node": "*", + "jest-util": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-regex-util": { + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.4.0.tgz", + "integrity": "sha512-mWlvLviKIgIQ8VCuM1xRdD0TWp3zlzionlmDBjuXVBs+VkmXq6FgW9T4Emr7oGz/Rk6feDCGyiugolcQEyp3mg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-util": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.4.1.tgz", + "integrity": "sha512-vjQb1sACEiv13DKJMDToJpzVW0joCsIQrmbg0fi7CyOOt+g9jTuQl2A216pWRBYhOVt53XbL/2LbMKg1BECWOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.4.1", + "@types/node": "*", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "graceful-fs": "^4.2.11", + "picomatch": "^4.0.3" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-util/node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, "node_modules/jiti": { @@ -4824,9 +8424,9 @@ } }, "node_modules/joi": { - "version": "17.13.3", - "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", - "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", + "version": "17.13.4", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.4.tgz", + "integrity": "sha512-1RuuER6kmt8K8I3nIWvPZKi5RQCb568ZPyY4Pwjlua+yo+63ZTmIwxLZH0heBmiKN4uxjvCiarDrjaeH84xicQ==", "license": "BSD-3-Clause", "dependencies": { "@hapi/hoek": "^9.3.0", @@ -4852,6 +8452,12 @@ "node": ">=10" } }, + "node_modules/js-cookie": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.8.tgz", + "integrity": "sha512-yeJd4aNAdYZQjaon2bpD/Gb0B/omw7HQOsynXXcOiWVCacbBcPlgn8S/d1X6blFSaHao7ozqtW7NZW19xpCtIw==", + "license": "MIT" + }, "node_modules/js-tiktoken": { "version": "1.0.21", "resolved": "https://registry.npmjs.org/js-tiktoken/-/js-tiktoken-1.0.21.tgz", @@ -4861,11 +8467,26 @@ "base64-js": "^1.5.1" } }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", - "dev": true, + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz", + "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -4874,6 +8495,56 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsdom": { + "version": "29.1.1", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-29.1.1.tgz", + "integrity": "sha512-ECi4Fi2f7BdJtUKTflYRTiaMxIB0O6zfR1fX0GXpUrf6flp8QIYn1UT20YQqdSOfk2dfkCwS8LAFoJDEppNK5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/css-color": "^5.1.11", + "@asamuzakjp/dom-selector": "^7.1.1", + "@bramus/specificity": "^2.4.2", + "@csstools/css-syntax-patches-for-csstree": "^1.1.3", + "@exodus/bytes": "^1.15.0", + "css-tree": "^3.2.1", + "data-urls": "^7.0.0", + "decimal.js": "^10.6.0", + "html-encoding-sniffer": "^6.0.0", + "is-potential-custom-element-name": "^1.0.1", + "lru-cache": "^11.3.5", + "parse5": "^8.0.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^6.0.1", + "undici": "^7.25.0", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^8.0.1", + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^16.0.1", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24.0.0" + }, + "peerDependencies": { + "canvas": "^3.0.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "license": "MIT", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", @@ -4925,23 +8596,19 @@ } }, "node_modules/langsmith": { - "version": "0.3.87", - "resolved": "https://registry.npmjs.org/langsmith/-/langsmith-0.3.87.tgz", - "integrity": "sha512-XXR1+9INH8YX96FKWc5tie0QixWz6tOqAsAKfcJyPkE0xPep+NDz0IQLR32q4bn10QK3LqD2HN6T3n6z1YLW7Q==", + "version": "0.8.10", + "resolved": "https://registry.npmjs.org/langsmith/-/langsmith-0.8.10.tgz", + "integrity": "sha512-OG07MX2vyWcLyx/RhXPcydZVpuJSmCrcI1PxVmOR7TOoGWnGV68yUIaA85kc9v0gBkyD6cWa2VnBNBZcTN/qcQ==", "license": "MIT", "dependencies": { - "@types/uuid": "^10.0.0", - "chalk": "^4.1.2", - "console-table-printer": "^2.12.1", - "p-queue": "^6.6.2", - "semver": "^7.6.3", - "uuid": "^10.0.0" + "p-queue": "6.6.2" }, "peerDependencies": { "@opentelemetry/api": "*", "@opentelemetry/exporter-trace-otlp-proto": "*", "@opentelemetry/sdk-trace-base": "*", - "openai": "*" + "openai": "*", + "ws": ">=7" }, "peerDependenciesMeta": { "@opentelemetry/api": { @@ -4955,22 +8622,12 @@ }, "openai": { "optional": true + }, + "ws": { + "optional": true } } }, - "node_modules/langsmith/node_modules/uuid": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", - "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -5287,6 +8944,30 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "license": "MIT" + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "license": "MIT" + }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", + "license": "MIT" + }, + "node_modules/lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==", + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -5294,6 +8975,12 @@ "dev": true, "license": "MIT" }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "license": "Apache-2.0" + }, "node_modules/longest-streak": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", @@ -5304,10 +8991,37 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lowlight": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-3.3.0.tgz", + "integrity": "sha512-0JNhgFoPvP6U6lE/UdVsSq99tn6DhjjpAj5MxG49ewd2mOBVtwWYIT8ClyABhq198aXXODMU6Ox8DrGy/CpTZQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "devlop": "^1.0.0", + "highlight.js": "~11.11.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/lru-cache": { - "version": "11.2.7", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz", - "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==", + "version": "11.3.6", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.6.tgz", + "integrity": "sha512-Gf/KoL3C/MlI7Bt0PGI9I+TeTC/I6r/csU58N4BSNc4lppLBeKsOdFYkK+dX0ABDUMJNfCHTyPpzwwO21Awd3A==", "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" @@ -5322,6 +9036,17 @@ "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "lz-string": "bin/bin.js" + } + }, "node_modules/magic-string": { "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", @@ -5331,6 +9056,85 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, + "node_modules/magicast": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.3.tgz", + "integrity": "sha512-pVKE4UdSQ7DvHzivsCIFx2BJn1mHG6KsyrFcaxFx6tONdneEuThrDx0Cj3AMg58KyN4pzYT+LHOotxDQDjNvkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.3", + "@babel/types": "^7.29.0", + "source-map-js": "^1.2.1" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/markdown-table": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", + "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/marked": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-14.0.0.tgz", + "integrity": "sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ==", + "license": "MIT", + "peer": true, + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mdast-util-find-and-replace": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", + "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "escape-string-regexp": "^5.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/mdast-util-from-markdown": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.3.tgz", @@ -5355,6 +9159,107 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/mdast-util-gfm": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", + "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", + "license": "MIT", + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-gfm-autolink-literal": "^2.0.0", + "mdast-util-gfm-footnote": "^2.0.0", + "mdast-util-gfm-strikethrough": "^2.0.0", + "mdast-util-gfm-table": "^2.0.0", + "mdast-util-gfm-task-list-item": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-autolink-literal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", + "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "ccount": "^2.0.0", + "devlop": "^1.0.0", + "mdast-util-find-and-replace": "^3.0.0", + "micromark-util-character": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-strikethrough": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", + "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", + "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-task-list-item": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", + "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/mdast-util-mdx-expression": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", @@ -5484,6 +9389,13 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/mdn-data": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", + "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", + "dev": true, + "license": "CC0-1.0" + }, "node_modules/micromark": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", @@ -5500,57 +9412,178 @@ ], "license": "MIT", "dependencies": { - "@types/debug": "^4.0.0", - "debug": "^4.0.0", - "decode-named-character-reference": "^1.0.0", + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", + "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", + "license": "MIT", + "dependencies": { + "micromark-extension-gfm-autolink-literal": "^2.0.0", + "micromark-extension-gfm-footnote": "^2.0.0", + "micromark-extension-gfm-strikethrough": "^2.0.0", + "micromark-extension-gfm-table": "^2.0.0", + "micromark-extension-gfm-tagfilter": "^2.0.0", + "micromark-extension-gfm-task-list-item": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", + "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-strikethrough": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", + "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-table": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", + "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", + "license": "MIT", + "dependencies": { "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/micromark-core-commonmark": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", - "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], + "node_modules/micromark-extension-gfm-tagfilter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", + "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-task-list-item": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", + "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", "license": "MIT", "dependencies": { - "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", - "micromark-factory-destination": "^2.0.0", - "micromark-factory-label": "^2.0.0", "micromark-factory-space": "^2.0.0", - "micromark-factory-title": "^2.0.0", - "micromark-factory-whitespace": "^2.0.0", "micromark-util-character": "^2.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-html-tag-name": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, "node_modules/micromark-factory-destination": { @@ -5938,6 +9971,16 @@ "node": ">=10.0.0" } }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/minimatch": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", @@ -5970,6 +10013,23 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/module-details-from-path": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.4.tgz", + "integrity": "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==", + "license": "MIT" + }, + "node_modules/monaco-editor": { + "version": "0.54.0", + "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.54.0.tgz", + "integrity": "sha512-hx45SEUoLatgWxHKCmlLJH81xBo0uXP4sRkESUpmDQevfi+e7K1VuiSprK6UpQ8u4zOcKNiH0pMvHvlMWA/4cw==", + "license": "MIT", + "peer": true, + "dependencies": { + "dompurify": "3.1.7", + "marked": "14.0.0" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -5986,9 +10046,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "version": "3.3.18", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", + "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", "funding": [ { "type": "github", @@ -6010,6 +10070,77 @@ "dev": true, "license": "MIT" }, + "node_modules/new-date": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/new-date/-/new-date-1.0.3.tgz", + "integrity": "sha512-0fsVvQPbo2I18DT2zVHpezmeeNYV2JaJSrseiHLc17GNOxJzUdx5mvSigPu8LtIfZSij5i1wXnXFspEs2CD6hA==", + "license": "SEE LICENSE IN LICENSE", + "dependencies": { + "@segment/isodate": "1.0.3" + } + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/nodemon": { "version": "3.1.14", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.14.tgz", @@ -6050,16 +10181,16 @@ } }, "node_modules/nodemon/node_modules/brace-expansion": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", - "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { - "node": "18 || 20 || >=22" + "node": "20 || >=22" } }, "node_modules/nodemon/node_modules/has-flag": { @@ -6111,6 +10242,32 @@ "node": ">=0.10.0" } }, + "node_modules/obj-case": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/obj-case/-/obj-case-0.2.1.tgz", + "integrity": "sha512-PquYBBTy+Y6Ob/O2574XHhDtHJlV1cJHMCgW+rDRc9J5hhmRelJB3k5dTK/3cVmFVtzvAKuENeuLpoyTzMzkOg==", + "license": "MIT" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/obug": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", + "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT" + }, "node_modules/on-exit-leak-free": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", @@ -6230,12 +10387,6 @@ "node": ">=8" } }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "license": "BlueOak-1.0.0" - }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -6274,6 +10425,25 @@ "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", "license": "MIT" }, + "node_modules/parse5": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz", + "integrity": "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^8.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "license": "MIT" + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -6288,6 +10458,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -6309,6 +10480,44 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "license": "ISC", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.13.0.tgz", + "integrity": "sha512-zzdvXfS6v89r6v7OcFCHfHlyG/wvry1ALxZo4LqgUoy7W9xhBDMaqOuMiF3qEV45VqsN6rdlcehHrfDtlCPc8w==", + "license": "MIT" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "license": "MIT", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -6316,9 +10525,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true, "license": "MIT", "engines": { @@ -6403,10 +10612,57 @@ "integrity": "sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==", "license": "MIT" }, + "node_modules/playwright": { + "version": "1.61.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.61.1.tgz", + "integrity": "sha512-DWnY5o3YbLWK4GovuAVwpqL+1VwGNdUGrRr++8j8PtQQzvAVZUIMjKQ90fY689sEJZJBbZVw1rXaOKSTitkzPQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.61.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.61.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.61.1.tgz", + "integrity": "sha512-h7Qlt6m4REp25qvIdvbDtVmD4LqVXfpRxhORv9L0jzETM05p4fuPJ3dKyuSXQxDSbXnmS79HAgi9589lGSpLkg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/postcss": { - "version": "8.5.8", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", - "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "version": "8.5.26", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz", + "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", "funding": [ { "type": "opencollective", @@ -6423,23 +10679,131 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.11", + "nanoid": "^3.3.17", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, "engines": { - "node": "^10 || ^12 || >=14" + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.1.tgz", + "integrity": "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "license": "MIT", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/posthog-js": { + "version": "1.416.0", + "resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.416.0.tgz", + "integrity": "sha512-9Rd0tq2WTA1U07VucWuCMwlzr+YvODglyN4mOf61TVrxPeMA7KVBiOEAThqaIvAv2kHbZvwTjbd8Ww2oTs221w==", + "license": "(Apache-2.0 AND MIT)", + "dependencies": { + "@posthog/browser-common": "^0.5.0", + "@posthog/core": "^1.47.0", + "@posthog/types": "^1.403.0", + "core-js": "^3.49.0", + "dompurify": "^3.4.13", + "fflate": "^0.4.8", + "preact": "^10.29.3", + "query-selector-shadow-dom": "^1.0.1", + "web-vitals": "^5.3.0", + "web-vitals-soft-navs": "npm:web-vitals@6.0.0" + } + }, + "node_modules/posthog-js/node_modules/dompurify": { + "version": "3.4.13", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.13.tgz", + "integrity": "sha512-2vmYIoqjze2d+kakP8S/nS5shfsl587kzwEjcGlTdiksUVgFHnFCsLYDVj/JNqJVOQZGSYBTmuycv0PodwmnMQ==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, + "node_modules/preact": { + "version": "10.29.8", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.29.8.tgz", + "integrity": "sha512-ej2aVZ+vZ8WO7tvlQWRM9N63A0KzF9q4mWJfDUHgYaIofWY9hu74QdnQrjoPMmZi2/nZ5gN0bJCQF49xQqx09Q==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + }, + "peerDependencies": { + "preact-render-to-string": ">=5" + }, + "peerDependenciesMeta": { + "preact-render-to-string": { + "optional": true + } + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "node_modules/pretty-format/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true, "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } + "peer": true }, "node_modules/process-warning": { "version": "5.0.0", @@ -6457,6 +10821,17 @@ ], "license": "MIT" }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, "node_modules/property-information": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", @@ -6467,6 +10842,29 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/protobufjs": { + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.5.tgz", + "integrity": "sha512-/FPD0nUc9jH6rfFjji9IBqOz4pcSE3CsT1m7Ep6Mdb0LxSUMj8hgl6GomOvZzpNpAqqGaXA0P3VSrZLFzIhQrw==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.5", + "@protobufjs/eventemitter": "^1.1.1", + "@protobufjs/fetch": "^1.1.1", + "@protobufjs/float": "^1.0.2", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.1", + "@types/node": ">=13.7.0", + "long": "^5.3.2" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/pstree.remy": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", @@ -6495,6 +10893,12 @@ "node": ">=6" } }, + "node_modules/query-selector-shadow-dom": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/query-selector-shadow-dom/-/query-selector-shadow-dom-1.0.1.tgz", + "integrity": "sha512-lT5yCqEBgfoMYpf3F2xQRK7zEr1rhIIZuceDK6+xRkJQ4NMbHTwXqk4NkwDwQMNqXgG9r9fyHnzwNVs6zV5KRw==", + "license": "MIT" + }, "node_modules/quick-format-unescaped": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", @@ -6522,6 +10926,45 @@ "react": "^19.2.4" } }, + "node_modules/react-dropzone": { + "version": "14.4.1", + "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-14.4.1.tgz", + "integrity": "sha512-QDuV76v3uKbHiH34SpwifZ+gOLi1+RdsCO1kl5vxMT4wW8R82+sthjvBw4th3NHF/XX6FBsqDYZVNN+pnhaw0g==", + "license": "MIT", + "dependencies": { + "attr-accept": "^2.2.4", + "file-selector": "^2.1.0", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">= 10.13" + }, + "peerDependencies": { + "react": ">= 16.8 || 18.0.0" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/react-is-18": { + "name": "react-is", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/react-is-19": { + "name": "react-is", + "version": "19.2.7", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.7.tgz", + "integrity": "sha512-kZFnouyVv7eP/Phmrlo9FK+zcAdriZJvzxXHF1Sl1P377WSGe2G/JxVolhTrB/jeV47lKImhNUsijjHAAbcl/A==", + "dev": true, + "license": "MIT" + }, "node_modules/react-markdown": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-9.1.0.tgz", @@ -6549,57 +10992,33 @@ "react": ">=18" } }, - "node_modules/react-remove-scroll": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz", - "integrity": "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==", + "node_modules/react-redux": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz", + "integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==", "license": "MIT", "dependencies": { - "react-remove-scroll-bar": "^2.3.7", - "react-style-singleton": "^2.2.3", - "tslib": "^2.1.0", - "use-callback-ref": "^1.3.3", - "use-sidecar": "^1.1.3" - }, - "engines": { - "node": ">=10" + "@types/use-sync-external-store": "^0.0.6", + "use-sync-external-store": "^1.4.0" }, "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + "@types/react": "^18.2.25 || ^19", + "react": "^18.0 || ^19", + "redux": "^5.0.0" }, "peerDependenciesMeta": { "@types/react": { "optional": true - } - } - }, - "node_modules/react-remove-scroll-bar": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", - "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", - "license": "MIT", - "dependencies": { - "react-style-singleton": "^2.2.2", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { + }, + "redux": { "optional": true } } }, "node_modules/react-router": { - "version": "7.13.1", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.13.1.tgz", - "integrity": "sha512-td+xP4X2/6BJvZoX6xw++A2DdEi++YypA69bJUV5oVvqf6/9/9nNlD70YO1e9d3MyamJEBQFEzk6mbfDYbqrSA==", + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.18.2.tgz", + "integrity": "sha512-aUVMjFm3GAPTTZL7oYr5E7ETiqfQCHRLH+B+5afnICvf0r7kkK4eR6SMuwbSTJw/7t+12khT/Kahij49fqOCIg==", "license": "MIT", "dependencies": { "cookie": "^1.0.1", @@ -6619,12 +11038,12 @@ } }, "node_modules/react-router-dom": { - "version": "7.13.1", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.13.1.tgz", - "integrity": "sha512-UJnV3Rxc5TgUPJt2KJpo1Jpy0OKQr0AjgbZzBFjaPJcFOb2Y8jA5H3LT8HUJAiRLlWrEXWHbF1Z4SCZaQjWDHw==", + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.18.2.tgz", + "integrity": "sha512-AIKJ/jgGlFb3EbfCXk5Gzshiwt+l3mqbCrNjmEWMMjqQxNJ3svBa6bgzFyCC2Sw3RA0VWF1kg3uQf2OFhxb8hw==", "license": "MIT", "dependencies": { - "react-router": "7.13.1" + "react-router": "7.18.2" }, "engines": { "node": ">=20.0.0" @@ -6634,28 +11053,6 @@ "react-dom": ">=18" } }, - "node_modules/react-style-singleton": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", - "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", - "license": "MIT", - "dependencies": { - "get-nonce": "^1.0.0", - "tslib": "^2.0.0" - }, - "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 - } - } - }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -6678,6 +11075,139 @@ "node": ">= 12.13.0" } }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/redis-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", + "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", + "license": "MIT", + "dependencies": { + "redis-errors": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/redux": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", + "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", + "license": "MIT" + }, + "node_modules/redux-thunk": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz", + "integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==", + "license": "MIT", + "peerDependencies": { + "redux": "^5.0.0" + } + }, + "node_modules/rehype-external-links": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/rehype-external-links/-/rehype-external-links-3.0.0.tgz", + "integrity": "sha512-yp+e5N9V3C6bwBeAC4n796kc86M4gJCdlVhiMTxIrJG5UHDMh+PJANf9heqORJbt1nrCbDwIlAZKjANIaVBbvw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@ungap/structured-clone": "^1.0.0", + "hast-util-is-element": "^3.0.0", + "is-absolute-url": "^4.0.0", + "space-separated-tokens": "^2.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-highlight": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/rehype-highlight/-/rehype-highlight-7.0.2.tgz", + "integrity": "sha512-k158pK7wdC2qL3M5NcZROZ2tR/l7zOzjxXd5VGdcfIyoijjQqpHd3JKtYSBDpDZ38UI2WJWuFAtkMDxmx5kstA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-to-text": "^4.0.0", + "lowlight": "^3.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-sanitize": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/rehype-sanitize/-/rehype-sanitize-6.0.0.tgz", + "integrity": "sha512-CsnhKNsyI8Tub6L4sm5ZFsme4puGfc6pYylvXo1AeqaGbjOYyzNv3qZPwvs0oMJ39eryyeOdmxwUIo94IpEhqg==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-sanitize": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-unwrap-images": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rehype-unwrap-images/-/rehype-unwrap-images-1.0.0.tgz", + "integrity": "sha512-wzW5Mk9IlVF2UwXC5NtIZsx1aHYbV8+bLWjJnlZaaamz5QU52RppWtq1uEZJqGo8d9Y4RuDqidB6r9RFpKugIg==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-interactive": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-gfm": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", + "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-gfm": "^3.0.0", + "micromark-extension-gfm": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-stringify": "^11.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/remark-parse": { "version": "11.0.0", "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", @@ -6711,11 +11241,25 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/remark-stringify": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", + "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-to-markdown": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -6730,6 +11274,25 @@ "node": ">=0.10.0" } }, + "node_modules/require-in-the-middle": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-8.0.1.tgz", + "integrity": "sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.5", + "module-details-from-path": "^1.0.3" + }, + "engines": { + "node": ">=9.3.0 || >=8.10.0 <9.0.0" + } + }, + "node_modules/reselect": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", + "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==", + "license": "MIT" + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -6838,30 +11401,10 @@ "tslib": "^2.1.0" } }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, "node_modules/safe-regex2": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-5.1.0.tgz", - "integrity": "sha512-pNHAuBW7TrcleFHsxBr5QMi/Iyp0ENjUKz7GCcX1UO7cMh+NmVK6HxQckNL1tJp1XAJVjG6B8OKIPqodqj9rtw==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-5.1.1.tgz", + "integrity": "sha512-mOSBvHGDZMuIEZMdOz/aCEYDCv0E7nfcNsIhUF+/P+xC7Hyf3FkvymqgPbg9D1EdSGu+uKbJgy09K/RKKc7kJA==", "funding": [ { "type": "github", @@ -6889,6 +11432,19 @@ "node": ">=10" } }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, "node_modules/scheduler": { "version": "0.27.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", @@ -6939,6 +11495,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" @@ -6951,15 +11508,16 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/shell-quote": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", - "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.9.0.tgz", + "integrity": "sha512-Iov+JwFv/2HcTpcwNMKd8+IWNb8tboQJNQTkAY/LLVK7gGH9jy+LGkVqPxfekHl+yMmiqXszdGWXgkfml7hjqA==", "dev": true, "license": "MIT", "engines": { @@ -6969,17 +11527,12 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" }, "node_modules/simple-oauth2": { "version": "5.1.0", @@ -7006,11 +11559,15 @@ "node": ">=10" } }, - "node_modules/simple-wcswidth": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/simple-wcswidth/-/simple-wcswidth-1.1.2.tgz", - "integrity": "sha512-j7piyCjAeTDSjzTSQ7DokZtMNwNlEAyxqSZeCS+CXH7fJ4jx3FuJ/mTW3mE+6JLs4VJBbcll0Kjn+KXI5t21Iw==", - "license": "MIT" + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } }, "node_modules/sonic-boom": { "version": "4.2.1", @@ -7049,6 +11606,54 @@ "node": ">= 10.x" } }, + "node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "license": "BSD-3-Clause" + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/standard-as-callback": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", + "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==", + "license": "MIT" + }, + "node_modules/state-local": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/state-local/-/state-local-1.0.7.tgz", + "integrity": "sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==", + "license": "MIT" + }, "node_modules/statuses": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", @@ -7058,11 +11663,17 @@ "node": ">= 0.8" } }, + "node_modules/std-env": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz", + "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==", + "dev": true, + "license": "MIT" + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -7091,7 +11702,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -7100,6 +11710,19 @@ "node": ">=8" } }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -7147,6 +11770,19 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tabbable": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.4.0.tgz", + "integrity": "sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==", + "license": "MIT" + }, "node_modules/tailwind-merge": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.5.0.tgz", @@ -7188,6 +11824,23 @@ "node": ">=20" } }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.2.tgz", + "integrity": "sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/tinyglobby": { "version": "0.2.15", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", @@ -7222,9 +11875,9 @@ } }, "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", "license": "MIT", "engines": { "node": ">=12" @@ -7233,6 +11886,36 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/tinyrainbow": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", + "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tldts": { + "version": "7.0.30", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.30.tgz", + "integrity": "sha512-ELrFxuqsDdHUwoh0XxDbxuLD3Wnz49Z57IFvTtvWy1hJdcMZjXLIuonjilCiWHlT2GbE4Wlv1wKVTzDFnXH1aw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^7.0.30" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "7.0.30", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.30.tgz", + "integrity": "sha512-uiHN8PIB1VmWyS98eZYja4xzlYqeFZVjb4OuYlJQnZAuJhMw4PbKQOKgHKhBdJR3FE/t5mUQ1Kd80++B+qhD1Q==", + "dev": true, + "license": "MIT" + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -7274,6 +11957,32 @@ "nodetouch": "bin/nodetouch.js" } }, + "node_modules/tough-cookie": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.1.tgz", + "integrity": "sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^7.0.5" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tr46": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz", + "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=20" + } + }, "node_modules/tree-kill": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", @@ -7411,11 +12120,26 @@ "dev": true, "license": "MIT" }, + "node_modules/undici": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.29.0.tgz", + "integrity": "sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, "node_modules/undici-types": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "devOptional": true, + "license": "MIT" + }, + "node_modules/unfetch": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/unfetch/-/unfetch-4.2.0.tgz", + "integrity": "sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==", "license": "MIT" }, "node_modules/unified": { @@ -7437,6 +12161,20 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/unist-util-find-after": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-5.0.0.tgz", + "integrity": "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/unist-util-is": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", @@ -7515,53 +12253,19 @@ "punycode": "^2.1.0" } }, - "node_modules/use-callback-ref": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", - "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", - "license": "MIT", - "dependencies": { - "tslib": "^2.0.0" - }, - "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 - } - } - }, - "node_modules/use-sidecar": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", - "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", "license": "MIT", - "dependencies": { - "detect-node-es": "^1.1.0", - "tslib": "^2.0.0" - }, - "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": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "node_modules/uuid": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", - "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.1.tgz", + "integrity": "sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ==", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" @@ -7600,9 +12304,9 @@ } }, "node_modules/vite": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", - "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.3.tgz", + "integrity": "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==", "license": "MIT", "dependencies": { "esbuild": "^0.25.0", @@ -8148,9 +12852,112 @@ } }, "node_modules/vite/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/vitest": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.10.tgz", + "integrity": "sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.1.10", + "@vitest/mocker": "4.1.10", + "@vitest/pretty-format": "4.1.10", + "@vitest/runner": "4.1.10", + "@vitest/snapshot": "4.1.10", + "@vitest/spy": "4.1.10", + "@vitest/utils": "4.1.10", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^4.0.0-rc.1", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.1.10", + "@vitest/browser-preview": "4.1.10", + "@vitest/browser-webdriverio": "4.1.10", + "@vitest/coverage-istanbul": "4.1.10", + "@vitest/coverage-v8": "4.1.10", + "@vitest/ui": "4.1.10", + "happy-dom": "*", + "jsdom": "*", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "vite": { + "optional": false + } + } + }, + "node_modules/vitest/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -8159,10 +12966,81 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/web-vitals": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-5.3.0.tgz", + "integrity": "sha512-q6LWsLatGYZp5VGBIOvbTj6JBV2nOmC8KvWztXBmwJcfFAzhwKwbOxhUH306XY3CcaZDUlSmSuNPBsCn0bFu+g==", + "license": "Apache-2.0" + }, + "node_modules/web-vitals-soft-navs": { + "name": "web-vitals", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-6.0.0.tgz", + "integrity": "sha512-Guaibvy/+uNtL6Bsu4jmMJGzuSl91oeRH5iO9pPRbYftnFUr3yqT1TUNX/OE4o9HexuEMU3Kb/Wg7iKhlffZUA==", + "license": "Apache-2.0" + }, + "node_modules/webidl-conversions": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz", + "integrity": "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20" + } + }, + "node_modules/whatwg-mimetype": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz", + "integrity": "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/whatwg-url": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-16.0.1.tgz", + "integrity": "sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@exodus/bytes": "^1.11.0", + "tr46": "^6.0.0", + "webidl-conversions": "^8.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -8174,6 +13052,23 @@ "node": ">= 8" } }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", @@ -8188,7 +13083,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", @@ -8206,7 +13100,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -8225,21 +13118,60 @@ "dev": true, "license": "ISC" }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT" + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, "license": "ISC", "engines": { "node": ">=10" } }, + "node_modules/yaml": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, "node_modules/yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, "license": "MIT", "dependencies": { "cliui": "^8.0.1", @@ -8258,7 +13190,6 @@ "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, "license": "ISC", "engines": { "node": ">=12" diff --git a/package.json b/package.json index 14840bff..97906d10 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "template-ui", "private": true, - "version": "0.0.0", + "version": "0.1.0", "type": "module", "scripts": { "dev": "concurrently \"npm run dev:client\" \"npm run dev:server\"", @@ -12,54 +12,95 @@ "build:server": "tsc --project src/server/tsconfig.json", "start": "node --env-file=.env --inspect dist/server/index.js", "lint": "eslint .", - "preview": "vite preview" + "preview": "vite preview", + "test": "vitest", + "test:run": "vitest run", + "test:coverage": "vitest run --coverage", + "test:e2e": "playwright test" }, "dependencies": { "@fastify/cookie": "^11.0.2", "@fastify/cors": "^11.1.0", + "@fastify/helmet": "^13.0.2", "@fastify/oauth2": "^8.1.2", + "@fastify/rate-limit": "^10.3.0", "@fastify/session": "^11.1.0", - "@fastify/static": "^8.2.0", + "@fastify/static": "^10.1.3", "@fastify/url-data": "^6.0.3", "@langchain/core": "^0.3.55", - "@langchain/langgraph-sdk": "^0.0.74", - "@radix-ui/react-scroll-area": "^1.2.8", - "@radix-ui/react-select": "^2.2.4", - "@radix-ui/react-slot": "^1.2.2", - "@radix-ui/react-tabs": "^1.1.11", - "@radix-ui/react-tooltip": "^1.2.6", + "@langchain/langgraph-sdk": "^1.9.2", + "@open-policy-agent/opa-wasm": "^1.10.0", + "@opentelemetry/auto-instrumentations-node": "^0.76.0", + "@opentelemetry/exporter-metrics-otlp-http": "^0.218.0", + "@opentelemetry/exporter-trace-otlp-http": "^0.218.0", + "@opentelemetry/resources": "^2.7.1", + "@opentelemetry/sdk-node": "^0.218.0", + "@opentelemetry/semantic-conventions": "^1.41.1", + "@patternfly/chatbot": "^6.5.0", + "@patternfly/patternfly": "^6.4.0", + "@patternfly/react-core": "^6.4.3", + "@patternfly/react-icons": "^6.4.0", + "@patternfly/react-styles": "^6.4.0", + "@patternfly/react-table": "^6.4.3", + "@reduxjs/toolkit": "^2.11.2", "@tailwindcss/vite": "^4.1.5", "@types/uuid": "^10.0.0", - "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "fastify": "^5.5.0", "fastify-plugin": "^5.0.1", + "ioredis": "^5.10.1", + "js-yaml": "^4.3.1", "lucide-react": "^0.508.0", "react": "^19.0.0", "react-dom": "^19.0.0", "react-markdown": "^9.0.3", + "react-redux": "^9.2.0", + "remark-gfm": "^4.0.1", "react-router-dom": "^7.5.3", + "rehype-highlight": "^7.0.2", + "rehype-sanitize": "^6.0.0", "tailwind-merge": "^3.2.0", "tailwindcss": "^4.1.5", "uuid": "^11.1.0" }, + "overrides": { + "@grpc/grpc-js": "^1.14.4", + "@opentelemetry/propagator-jaeger": ">=2.9.0", + "js-cookie": ">=3.0.7", + "langsmith": ">=0.6.0" + }, + "engines": { + "node": ">=22.13.0" + }, "devDependencies": { + "@axe-core/playwright": "^4.12.1", "@eslint/js": "^9.22.0", + "@playwright/test": "^1.61.1", + "@testing-library/jest-dom": "^6.9.1", + "@testing-library/react": "^16.3.2", + "@testing-library/user-event": "^14.6.1", + "@types/jest-axe": "^3.5.9", + "@types/js-yaml": "^4.0.9", "@types/node": "^22.17.2", "@types/react": "^19.1.2", "@types/react-dom": "^19.1.3", + "@types/react-redux": "^7.1.34", "@vitejs/plugin-react-swc": "^3.9.0", + "@vitest/coverage-v8": "^4.1.10", "concurrently": "^9.2.0", "eslint": "^9.22.0", "eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-refresh": "^0.4.19", "globals": "^16.0.0", + "jest-axe": "^10.0.0", + "jsdom": "^29.1.1", "nodemon": "^3.1.10", "pino-pretty": "^13.1.1", "tsx": "^4.20.4", "tw-animate-css": "^1.2.9", "typescript": "~5.7.2", "typescript-eslint": "^8.26.1", - "vite": "^6.3.4" + "vite": "^6.3.4", + "vitest": "^4.1.6" } } diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 00000000..c441ceca --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,35 @@ +import { defineConfig, devices } from '@playwright/test'; + +export default defineConfig({ + testDir: './e2e', + timeout: process.env.CI ? 60_000 : 30_000, + expect: { timeout: process.env.CI ? 15_000 : 10_000 }, + fullyParallel: false, + forbidOnly: !!process.env.CI, + retries: process.env.CI ? 2 : 0, + reporter: [['list'], ['html', { open: 'never' }]], + use: { + baseURL: 'http://localhost:18080', + trace: 'on-first-retry', + }, + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + ], + webServer: { + // Bypasses --env-file=.env (not needed in CI) by invoking the compiled server directly. + command: 'node dist/server/index.js', + url: 'http://localhost:18080', + reuseExistingServer: !process.env.CI, + timeout: 30_000, + env: { + PORT: '18080', + AUTH_ENABLED: 'false', + ENVIRONMENT: 'test', + AGENT_HOST: 'http://localhost:19999', + COOKIE_SIGN: 'playwright-test-secret-32-chars-min-length', + }, + }, +}); diff --git a/public/redhat-logo.svg b/public/redhat-logo.svg new file mode 100644 index 00000000..f8b0f994 --- /dev/null +++ b/public/redhat-logo.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/AnnouncementBanner.tsx b/src/components/AnnouncementBanner.tsx new file mode 100644 index 00000000..57a43b31 --- /dev/null +++ b/src/components/AnnouncementBanner.tsx @@ -0,0 +1,86 @@ +import { useState, useEffect, useCallback } from 'react'; +import { Alert, AlertActionCloseButton } from '@patternfly/react-core'; + +type AnnouncementType = 'info' | 'warning' | 'danger' | 'success'; + +interface AnnouncementResponse { + enabled: boolean; + message?: string; + type?: string; +} + +function messageHash(message: string): string { + let h = 0; + for (let i = 0; i < message.length; ) { + const cp = message.codePointAt(i); + if (cp === undefined) break; + h = Math.trunc(Math.imul(31, h) + cp); + i += cp > 0xffff ? 2 : 1; + } + return String(h); +} + +function toVariant(type: string | undefined): AnnouncementType { + if (type === 'warning' || type === 'danger' || type === 'success' || type === 'info') { + return type; + } + return 'info'; +} + +const STORAGE_PREFIX = 'announcement_dismissed_'; + +export function AnnouncementBanner() { + const [payload, setPayload] = useState(null); + const [dismissed, setDismissed] = useState(false); + + useEffect(() => { + let cancelled = false; + (async () => { + try { + const res = await fetch('/api/announcement'); + if (!res.ok || cancelled) return; + const data = (await res.json()) as AnnouncementResponse; + if (cancelled) return; + setPayload(data); + if (data.enabled && data.message) { + const key = STORAGE_PREFIX + messageHash(data.message); + if (sessionStorage.getItem(key) === '1') { + setDismissed(true); + } + } + } catch { + // ignore fetch errors + } + })(); + return () => { + cancelled = true; + }; + }, []); + + const handleClose = useCallback(() => { + if (payload?.message) { + try { + sessionStorage.setItem(STORAGE_PREFIX + messageHash(payload.message), '1'); + } catch { /* storage full or unavailable */ } + } + setDismissed(true); + }, [payload?.message]); + + if (!payload?.enabled || !payload.message || dismissed) { + return null; + } + + const variant = toVariant(payload.type); + + return ( +
+ } + /> +
+ ); +} diff --git a/src/frontend/App.tsx b/src/frontend/App.tsx index d2bf7f0d..47be18bd 100644 --- a/src/frontend/App.tsx +++ b/src/frontend/App.tsx @@ -1,28 +1,111 @@ - -import { Routes, Route } from 'react-router-dom'; -import { ChatProvider } from './contexts/ChatContext'; +import { useEffect } from 'react'; +import { Routes, Route, Navigate } from 'react-router-dom'; +import { useDispatch, useSelector } from 'react-redux'; +import { AnnouncementBanner } from '../components/AnnouncementBanner'; import { AppLayout } from './components/layout/AppLayout'; import { HomePage } from './pages/HomePage'; import { ChatRoutePage } from './pages/ChatPage'; +import { SettingsPage } from './pages/SettingsPage'; import { ErrorBoundary } from './components/ErrorBoundary'; +import { ToastNotifications } from './components/ToastNotifications'; +import { useThemeSync } from './hooks/useThemeSync'; +import { loadConfig, setBranding, setFeatures } from './redux/slices/config'; +import { setConfigDefaults } from './redux/slices/userSettings'; +import type { RootState, AppDispatch } from './redux/store'; -// Main App component - clean and simple! export default function App() { + const dispatch = useDispatch(); + const { branding, loading, error } = useSelector((state: RootState) => state.config); + const theme = useSelector((state: RootState) => state.userSettings.theme); + + useThemeSync(); + + // Load config at app init + useEffect(() => { + // Seed branding/features from server-injected HTML data + const serverBranding = window.APP_DATA?.branding; + const serverFeatures = window.APP_DATA?.features; + if (serverBranding) { + dispatch(setBranding(serverBranding)); + } + if (serverFeatures) { + dispatch(setFeatures(serverFeatures)); + dispatch(setConfigDefaults({ debug_mode_default: serverFeatures.debug_mode_default })); + } + + dispatch(loadConfig()).then((result: any) => { + if (result.meta.requestStatus === 'fulfilled' && result.payload?.features) { + dispatch(setConfigDefaults({ + debug_mode_default: result.payload.features.debug_mode_default, + })); + } + }); + }, [dispatch]); + + // Apply branding to DOM when config loads + useEffect(() => { + if (!branding) return; + + // Apply colors based on current theme + const colors = theme === 'dark' ? branding.colors?.dark : branding.colors?.light; + if (!colors) return; + + document.documentElement.style.setProperty('--primary', colors.primary); + document.documentElement.style.setProperty('--accent', colors.accent); + document.documentElement.style.setProperty('--background', colors.background); + document.documentElement.style.setProperty('--foreground', colors.foreground); + + // Update document title + if (branding.title) { + document.title = branding.title; + } + + // Update favicon - use favicon_url if provided, otherwise fall back to logo_url + const faviconUrl = branding.favicon_url || branding.logo_url; + if (faviconUrl) { + const link = document.querySelector("link[rel='icon']") as HTMLLinkElement; + if (link) { + link.href = faviconUrl; + } + } + }, [branding, theme]); + + // Don't block render on config failure - fall back to defaults + // Only show loading on initial mount, not on errors + if (loading && !error && !branding) { + return ( +
+ Loading configuration... +
+ ); + } + + // Log error but don't block render - config will fall back to defaults + if (error) { + console.warn('Config load failed, using defaults:', error); + } + return ( { console.error('Top-level application error:', error, errorInfo); - // Here you could report to error tracking service }} > - - - - } /> - } /> - - - + + + + } /> + } /> + } /> + } /> + + + ); -} \ No newline at end of file +} diff --git a/src/frontend/components/AIMessageRenderer.test.tsx b/src/frontend/components/AIMessageRenderer.test.tsx new file mode 100644 index 00000000..5fd05c63 --- /dev/null +++ b/src/frontend/components/AIMessageRenderer.test.tsx @@ -0,0 +1,152 @@ +import React from 'react'; +import { describe, it, expect, vi } from 'vitest'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import type { Message } from '@langchain/langgraph-sdk'; +import { AIMessageRenderer } from './ChatMessagesView'; +import type { InterruptInfo } from '../types/deep-agent'; + +// ── Fixtures ────────────────────────────────────────────────────────────────── + +/** Cast a plain object to the LangGraph Message type — only the fields used by + * AIMessageRenderer are needed at runtime. */ +function makeMsg(overrides: Record): Message { + return { type: 'ai', content: '', id: 'msg-1', tool_calls: [], ...overrides } as unknown as Message; +} + +const CREATE_PR_TOOL_CALL = { + id: 'tc-1', + name: 'github_create_pr', + args: { title: 'My PR', base: 'main' }, +}; + +const hitlInterrupt: InterruptInfo = { + value: { + action_requests: [{ name: 'github_create_pr', args: { title: 'My PR' } }], + review_configs: [{ action_name: 'github_create_pr', allowed_decisions: ['approve', 'reject'] }], + }, + resumable: true, +}; + +// ── Tests ───────────────────────────────────────────────────────────────────── + +describe('AIMessageRenderer — HITL approval (production path for HITLInterruptValue)', () => { + it('renders Approve and Reject buttons for a pending tool-call interrupt', () => { + const msg = makeMsg({ tool_calls: [CREATE_PR_TOOL_CALL] }); + + render( + , + ); + + expect(screen.getByRole('button', { name: /approve tool call: github_create_pr/i })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: /reject tool call: github_create_pr/i })).toBeInTheDocument(); + }); + + it('calls onInterruptResume with approve decisions when Approve is clicked', async () => { + const onInterruptResume = vi.fn(); + const msg = makeMsg({ tool_calls: [CREATE_PR_TOOL_CALL] }); + + render( + , + ); + + await userEvent.click(screen.getByRole('button', { name: /approve tool call: github_create_pr/i })); + expect(onInterruptResume).toHaveBeenCalledOnce(); + const [decisions] = onInterruptResume.mock.calls[0]; + expect(decisions).toEqual(expect.arrayContaining([{ type: 'approve' }])); + expect(decisions.every((d: { type: string }) => d.type === 'approve')).toBe(true); + }); + + it('calls onInterruptResume with reject decisions when Reject is clicked', async () => { + const onInterruptResume = vi.fn(); + const msg = makeMsg({ tool_calls: [CREATE_PR_TOOL_CALL] }); + + render( + , + ); + + await userEvent.click(screen.getByRole('button', { name: /reject tool call: github_create_pr/i })); + expect(onInterruptResume).toHaveBeenCalledOnce(); + const [decisions] = onInterruptResume.mock.calls[0]; + expect(decisions.every((d: { type: string }) => d.type === 'reject')).toBe(true); + }); + + it('calls onAlwaysAllow and approves when Always allow is clicked', async () => { + const onInterruptResume = vi.fn(); + const onAlwaysAllow = vi.fn(); + const msg = makeMsg({ tool_calls: [CREATE_PR_TOOL_CALL] }); + + render( + , + ); + + await userEvent.click(screen.getByRole('button', { name: /always allow tool: github_create_pr/i })); + expect(onAlwaysAllow).toHaveBeenCalledWith(['github_create_pr']); + expect(onInterruptResume).toHaveBeenCalledOnce(); + const [decisions] = onInterruptResume.mock.calls[0]; + expect(decisions.every((d: { type: string }) => d.type === 'approve')).toBe(true); + }); + + it('hides approval buttons when pendingInterrupt is cleared (post-approval)', () => { + const msg = makeMsg({ tool_calls: [CREATE_PR_TOOL_CALL] }); + + const { rerender } = render( + , + ); + + expect(screen.queryByRole('button', { name: /approve tool call: github_create_pr/i })).toBeInTheDocument(); + + rerender( + , + ); + + expect(screen.queryByRole('button', { name: /approve tool call: github_create_pr/i })).not.toBeInTheDocument(); + expect(screen.queryByRole('button', { name: /reject tool call: github_create_pr/i })).not.toBeInTheDocument(); + }); + + it('does NOT render approval buttons when pendingInterrupt is null', () => { + const msg = makeMsg({ tool_calls: [CREATE_PR_TOOL_CALL] }); + + render( + , + ); + + expect(screen.queryByRole('button', { name: /approve tool call/i })).not.toBeInTheDocument(); + expect(screen.queryByRole('button', { name: /reject tool call/i })).not.toBeInTheDocument(); + }); + + it('does NOT render approval buttons when onInterruptResume is not provided', () => { + const msg = makeMsg({ tool_calls: [CREATE_PR_TOOL_CALL] }); + + render( + , + ); + + expect(screen.queryByRole('button', { name: /approve tool call/i })).not.toBeInTheDocument(); + }); +}); diff --git a/src/frontend/components/ActivityTimeline.tsx b/src/frontend/components/ActivityTimeline.tsx index d59df59b..6030d1fe 100644 --- a/src/frontend/components/ActivityTimeline.tsx +++ b/src/frontend/components/ActivityTimeline.tsx @@ -1,10 +1,9 @@ import { Card, - CardContent, - CardDescription, + CardBody, CardHeader, -} from "@/components/ui/card"; -import { ScrollArea } from "@/components/ui/scroll-area"; + CardTitle, +} from "@patternfly/react-core"; import { Loader2, Activity, @@ -59,25 +58,22 @@ export function ActivityTimeline({ }, [isLoading, processedEvents]); return ( - - - -
setIsTimelineCollapsed(!isTimelineCollapsed)} - > - Research - {isTimelineCollapsed ? ( - - ) : ( - - )} -
-
+ + setIsTimelineCollapsed(!isTimelineCollapsed)} + > + + Research + {isTimelineCollapsed ? ( + + ) : ( + + )} + {!isTimelineCollapsed && ( - - + {isLoading && processedEvents.length === 0 && (
@@ -138,8 +134,7 @@ export function ActivityTimeline({

) : null} - - + )} ); diff --git a/src/frontend/components/ArtifactViewer.tsx b/src/frontend/components/ArtifactViewer.tsx new file mode 100644 index 00000000..9f3f8bb8 --- /dev/null +++ b/src/frontend/components/ArtifactViewer.tsx @@ -0,0 +1,70 @@ +import { useState } from 'react'; +import { Button, Label } from '@patternfly/react-core'; +import { Copy, Check, FileText, Code, FileJson } from 'lucide-react'; +import ReactMarkdown from 'react-markdown'; +import remarkGfm from 'remark-gfm'; +import { detectArtifactKind, type ArtifactKind } from '../types/deep-agent'; + +interface ArtifactViewerProps { + readonly content: string; + readonly title?: string; +} + +const KIND_META: Record = { + code: { label: 'Code', color: 'purple', icon: Code }, + json: { label: 'JSON', color: 'orange', icon: FileJson }, + markdown: { label: 'Markdown', color: 'blue', icon: FileText }, + text: { label: 'Text', color: 'grey', icon: FileText }, +}; + +export function ArtifactViewer({ content, title }: ArtifactViewerProps) { + const [copied, setCopied] = useState(false); + const kind = detectArtifactKind(content); + const meta = KIND_META[kind]; + const KindIcon = meta.icon; + + const handleCopy = async () => { + try { + await navigator.clipboard.writeText(content); + setCopied(true); + setTimeout(() => setCopied(false), 2000); + } catch { + /* clipboard unavailable (e.g. non-secure context) */ + } + }; + + return ( +
+
+
+ + {title && {title}} + +
+ +
+ +
+ {kind === 'markdown' ? ( +
+ {content} +
+ ) : kind === 'json' ? ( +
+            {(() => { try { return JSON.stringify(JSON.parse(content), null, 2); } catch { return content; } })()}
+          
+ ) : ( +
{content}
+ )} +
+
+ ); +} diff --git a/src/frontend/components/ChatErrorBoundary.tsx b/src/frontend/components/ChatErrorBoundary.tsx index 3ee50ecf..5ce99687 100644 --- a/src/frontend/components/ChatErrorBoundary.tsx +++ b/src/frontend/components/ChatErrorBoundary.tsx @@ -1,5 +1,6 @@ import { Component, ErrorInfo, ReactNode } from 'react'; -import { Button } from './ui/button'; +import { ErrorRecovery } from './ErrorRecovery'; +import { buildAppPath } from '../lib/app-paths'; interface Props { children: ReactNode; @@ -24,60 +25,36 @@ export class ChatErrorBoundary extends Component { componentDidCatch(error: Error, errorInfo: ErrorInfo) { console.error('ChatErrorBoundary caught an error:', error, errorInfo); - - // Report to error tracking service (Sentry, LogRocket, etc.) - // Example: reportError(error, { context: 'chat', chatId: this.props.chatId }); } private handleRetry = () => { this.setState({ hasError: false, error: undefined }); - if (this.props.onRetry) { - this.props.onRetry(); - } + this.props.onRetry?.(); }; private handleGoHome = () => { - window.location.href = '/'; + globalThis.location.href = buildAppPath('/'); + }; + + private handleRefreshChat = () => { + window.location.reload(); }; render() { if (this.state.hasError) { return (
-
-
🔧
-

Chat Error

-

- There was a problem with this chat session. This might be due to a network issue - or a problem with the message processing. -

- - {this.state.error && ( -
-

Error:

-

- {this.state.error.message} -

-
- )} - -
- - -
+
+
); diff --git a/src/frontend/components/ChatMessagesView.tsx b/src/frontend/components/ChatMessagesView.tsx index 24994437..760a8d73 100644 --- a/src/frontend/components/ChatMessagesView.tsx +++ b/src/frontend/components/ChatMessagesView.tsx @@ -1,78 +1,251 @@ import type React from "react"; import type { Message } from "@langchain/langgraph-sdk"; -import { ScrollArea } from "./ui/scroll-area"; -import { CheckCircle, ChevronDown, ChevronRight, Copy, CopyCheck, Loader2, Settings } from "lucide-react"; +import { AlertCircle, Check, CheckCircle, ChevronDown, ChevronRight, Copy, Download, Loader2, Pencil, RotateCcw, Settings, Bot, User, ShieldCheck } from "lucide-react"; +import type { InterruptInfo } from "../types/deep-agent"; import { InputForm } from "./InputForm"; -import { useState, ReactNode, useMemo } from "react"; +import { McpStatusPanel } from "./McpStatusPanel"; +import { useState, ReactNode, useMemo, useEffect, useRef, useCallback, Fragment } from "react"; import { cn } from "../lib/utils"; -import { Badge } from "./ui/badge"; +import { + Dropdown, + DropdownItem, + DropdownList, + ExpandableSection, + Label, + MenuToggle, + type MenuToggleElement, +} from "@patternfly/react-core"; import { ProcessedEvent, } from "./ActivityTimeline"; import { StreamEvent } from "../hooks/useDataStream"; import ReactMarkdown from "react-markdown"; +import remarkGfm from "remark-gfm"; +import { isSubAgentToolCall, detectArtifactKind } from "../types/deep-agent"; +import { SubAgentIndicator } from "./SubAgentIndicator"; +import { ArtifactViewer } from "./ArtifactViewer"; +import { TodoStrip } from "./TodoStrip"; +import { FeedbackButtons } from "./FeedbackButtons"; +import { CustomDataRenderer } from "./CustomDataRenderer"; + +function extractMessageText(content: unknown): string { + if (typeof content === 'string') return content; + if (Array.isArray(content)) { + return content + .map((b: unknown) => { + if (typeof b === 'string') return b; + if (b && typeof b === 'object' && 'type' in b && 'text' in b) { + const block = b as { type: string; text: string }; + if (block.type === 'text' && typeof block.text === 'string') return block.text; + } + return ''; + }) + .join(''); + } + return ''; +} + +function stripThinkingFromPlainText(text: string): { thinking: string; display: string } { + const thinkingParts: string[] = []; + let display = text; + const patterns: RegExp[] = [ + /([\s\S]*?)<\/think>/gi, + /([\s\S]*?)<\/redacted_thinking>/gi, + /([\s\S]*?)<\/thinking>/gi, + ]; + for (const re of patterns) { + display = display.replace(re, (_full, inner: string) => { + thinkingParts.push(String(inner).trim()); + return ''; + }); + } + return { + thinking: thinkingParts.filter(Boolean).join('\n\n'), + display: display.replace(/\n{3,}/g, '\n\n').trim(), + }; +} + +function partitionMessageContent(content: unknown): { thinkingText: string; markdownForDisplay: string } { + if (typeof content === 'string') { + const { thinking, display } = stripThinkingFromPlainText(content); + return { thinkingText: thinking, markdownForDisplay: display }; + } + if (Array.isArray(content)) { + const thinkingFromBlocks: string[] = []; + const restBlocks: unknown[] = []; + for (const b of content) { + if ( + b && + typeof b === 'object' && + 'type' in b && + (b as { type: string }).type === 'thinking' + ) { + const block = b as { type: string; text?: string; reasoning?: string }; + const piece = + typeof block.text === 'string' + ? block.text + : typeof block.reasoning === 'string' + ? block.reasoning + : JSON.stringify(block); + thinkingFromBlocks.push(piece); + continue; + } + restBlocks.push(b); + } + const restJoined = restBlocks + .map((b: unknown) => { + if (typeof b === 'string') return b; + if (b && typeof b === 'object' && 'type' in b && 'text' in b) { + const block = b as { type: string; text: string }; + if (block.type === 'text' && typeof block.text === 'string') return block.text; + } + return ''; + }) + .join(''); + const { thinking: thinkingInText, display: afterTags } = stripThinkingFromPlainText(restJoined); + const thinkingText = [thinkingFromBlocks.join('\n\n'), thinkingInText].filter(Boolean).join('\n\n'); + return { thinkingText, markdownForDisplay: afterTags }; + } + return { thinkingText: '', markdownForDisplay: '' }; +} + +function getCopyableAiMessageText(content: unknown): string { + const { thinkingText, markdownForDisplay } = partitionMessageContent(content); + const main = + markdownForDisplay.length > 0 ? markdownForDisplay : extractMessageText(content); + if (!thinkingText) return main; + return [thinkingText, main].filter((s) => s.length > 0).join('\n\n'); +} -// Markdown component props type from former ReportView type MdComponentProps = { className?: string; children?: ReactNode; [key: string]: unknown; }; -// Markdown components (from former ReportView.tsx) +function getPlainTextFromReactNode(node: React.ReactNode): string { + if (node == null || typeof node === 'boolean') return ''; + if (typeof node === 'string' || typeof node === 'number') return String(node); + if (Array.isArray(node)) return node.map(getPlainTextFromReactNode).join(''); + if (typeof node === 'object' && 'props' in node) { + const el = node as { props?: { children?: React.ReactNode } }; + return getPlainTextFromReactNode(el.props?.children); + } + return ''; +} + +function MarkdownPre({ className, children, ...props }: MdComponentProps) { + const [copied, setCopied] = useState(false); + const codeText = useMemo(() => getPlainTextFromReactNode(children), [children]); + + const handleCopy = () => { + void navigator.clipboard.writeText(codeText).then(() => { + setCopied(true); + window.setTimeout(() => setCopied(false), 2000); + }); + }; + + return ( +
+ +
+        {children}
+      
+
+ ); +} + +function MessageCopyButton({ text }: { text: string }) { + const [copied, setCopied] = useState(false); + const disabled = text.length === 0; + + return ( + + ); +} + const mdComponents = { h1: ({ className, children, ...props }: MdComponentProps) => ( -

+

{children}

), h2: ({ className, children, ...props }: MdComponentProps) => ( -

+

{children}

), h3: ({ className, children, ...props }: MdComponentProps) => ( -

+

{children}

), - p: ({ className, children, ...props }: MdComponentProps) => ( -

+ p: ({ className, children, node: _node, ...props }: MdComponentProps) => ( +

{children}

), a: ({ className, children, href, ...props }: MdComponentProps) => ( - + + ), ul: ({ className, children, ...props }: MdComponentProps) => ( -
    +
      {children}
    ), ol: ({ className, children, ...props }: MdComponentProps) => ( -
      +
        {children}
      ), - li: ({ className, children, ...props }: MdComponentProps) => ( -
    1. + li: ({ className, children, node: _node, ...props }: MdComponentProps) => ( +
    2. {children}
    3. ), blockquote: ({ className, children, ...props }: MdComponentProps) => (
      ( ), - pre: ({ className, children, ...props }: MdComponentProps) => ( -
      -      {children}
      -    
      - ), + pre: MarkdownPre, hr: ({ className, ...props }: MdComponentProps) => ( -
      +
      ), table: ({ className, children, ...props }: MdComponentProps) => ( -
      +
      {children}
      ), thead: ({ className, children, ...props }: MdComponentProps) => ( - + {children} ), @@ -123,20 +286,14 @@ const mdComponents = { ), tr: ({ className, children, ...props }: MdComponentProps) => ( - + {children} ), th: ({ className, children, ...props }: MdComponentProps) => ( ), td: ({ className, children, ...props }: MdComponentProps) => ( - + {children} ), - img: ({ className, ...props }: MdComponentProps) => ( - + img: ({ className, alt, ...props }: MdComponentProps) => ( + {(alt ), }; -// Props for HumanMessageBubble interface HumanMessageBubbleProps { message: Message; - mdComponents: typeof mdComponents; + messageIndex: number; + isLastHuman: boolean; + isLoading?: boolean; + onEditMessage?: (messageIndex: number, newContent: string) => void; } -// HumanMessageBubble Component const HumanMessageBubble: React.FC = ({ message, + messageIndex, + isLastHuman, + isLoading = false, + onEditMessage, }) => { + const [isEditing, setIsEditing] = useState(false); + const [draft, setDraft] = useState(''); + const plain = extractMessageText(message.content); + const canEdit = Boolean(onEditMessage) && isLastHuman && !isLoading; + + const startEdit = () => { + setDraft(plain); + setIsEditing(true); + }; + + const cancelEdit = () => { + setIsEditing(false); + setDraft(''); + }; + + const saveEdit = () => { + const trimmed = draft.trim(); + if (trimmed === '' || !onEditMessage) return; + onEditMessage(messageIndex, trimmed); + setIsEditing(false); + }; + return ( -
      - - {typeof message.content === "string" - ? message.content - : JSON.stringify(message.content)} - +
      +
      + {isEditing ? ( +
      +