From 42d04fb8263eaafbbef37b33a3837f612388b3a0 Mon Sep 17 00:00:00 2001 From: Abhinav Gautam Date: Wed, 11 Mar 2026 16:03:56 +0530 Subject: [PATCH] additions --- .idea/copilot.data.migration.ask2agent.xml | 6 + .idea/material_theme_project_new.xml | 10 + AGENTS.md | 49 + README.md | 58 +- WORKFLOW.md | 70 + bin/www | 39 +- docker-compose.yml | 24 +- docs/ARCHITECTURE.md | 30 + docs/CODEX_APP_SETUP.md | 46 + docs/QUALITY.md | 25 + docs/README.md | 26 + docs/RELIABILITY.md | 18 + docs/SECURITY.md | 23 + docs/SYMPHONY_SETUP.md | 82 + docs/WORKFLOW.md | 34 + docs/design-docs/README.md | 3 + docs/exec-plans/README.md | 3 + .../active/sample-template-hardening.md | 27 + docs/product-specs/README.md | 3 + docs/references/README.md | 3 + docs/references/quality-score.json | 44 + env.development.example | 13 + eslint.config.js | 45 +- helper/cron-timer.js | 15 - helper/cron.js | 10 + init.js | 5 +- models/assigner_rule.js | 67 + models/assignment.js | 62 + models/init-models.js | 15 + models/workflow_action.js | 71 + models/workflow_application_registry.js | 53 + models/workflow_escalation_matrix.js | 92 + package-lock.json | 3812 ++++++++++++++--- package.json | 19 +- scripts/new-plan.js | 48 + scripts/score-quality.js | 31 + scripts/symphony-bootstrap.sh | 18 + scripts/validate-docs.js | 34 + scripts/worktree-bootstrap.sh | 22 + service/v1/index.js | 2 +- service/v1/workflow-engine/README.md | 1006 +++++ .../v1/workflow-engine/assignee-resolver.js | 146 + service/v1/workflow-engine/assignment.js | 266 ++ service/v1/workflow-engine/auto-escalation.js | 140 + .../v1/workflow-engine/escalation-matrix.js | 55 + service/v1/workflow-engine/index.js | 8 + .../v1/workflow-engine/workflow-actions.js | 93 + .../v1/workflow-engine/workflow-support.js | 180 + service/v1/workflow-engine/workflow.js | 195 + utils/index.js | 8 +- utils/mail.js | 33 +- utils/message.js | 69 +- utils/upload.js | 226 +- 53 files changed, 6728 insertions(+), 754 deletions(-) create mode 100644 .idea/copilot.data.migration.ask2agent.xml create mode 100644 .idea/material_theme_project_new.xml create mode 100644 AGENTS.md create mode 100644 WORKFLOW.md create mode 100644 docs/ARCHITECTURE.md create mode 100644 docs/CODEX_APP_SETUP.md create mode 100644 docs/QUALITY.md create mode 100644 docs/RELIABILITY.md create mode 100644 docs/SECURITY.md create mode 100644 docs/SYMPHONY_SETUP.md create mode 100644 docs/WORKFLOW.md create mode 100644 docs/design-docs/README.md create mode 100644 docs/exec-plans/README.md create mode 100644 docs/exec-plans/active/sample-template-hardening.md create mode 100644 docs/product-specs/README.md create mode 100644 docs/references/README.md create mode 100644 docs/references/quality-score.json create mode 100644 helper/cron.js create mode 100644 models/assigner_rule.js create mode 100644 models/assignment.js create mode 100644 models/workflow_action.js create mode 100644 models/workflow_application_registry.js create mode 100644 models/workflow_escalation_matrix.js create mode 100644 scripts/new-plan.js create mode 100644 scripts/score-quality.js create mode 100755 scripts/symphony-bootstrap.sh create mode 100644 scripts/validate-docs.js create mode 100755 scripts/worktree-bootstrap.sh create mode 100644 service/v1/workflow-engine/README.md create mode 100644 service/v1/workflow-engine/assignee-resolver.js create mode 100644 service/v1/workflow-engine/assignment.js create mode 100644 service/v1/workflow-engine/auto-escalation.js create mode 100644 service/v1/workflow-engine/escalation-matrix.js create mode 100644 service/v1/workflow-engine/index.js create mode 100644 service/v1/workflow-engine/workflow-actions.js create mode 100644 service/v1/workflow-engine/workflow-support.js create mode 100644 service/v1/workflow-engine/workflow.js diff --git a/.idea/copilot.data.migration.ask2agent.xml b/.idea/copilot.data.migration.ask2agent.xml new file mode 100644 index 0000000..1f2ea11 --- /dev/null +++ b/.idea/copilot.data.migration.ask2agent.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/material_theme_project_new.xml b/.idea/material_theme_project_new.xml new file mode 100644 index 0000000..2780963 --- /dev/null +++ b/.idea/material_theme_project_new.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..545042b --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,49 @@ +# AGENTS.md + +This repository is a Node.js backend template maintained for human developers and Codex. + +## Read this first + +1. `README.md` +2. `docs/README.md` +3. `docs/ARCHITECTURE.md` +4. `docs/WORKFLOW.md` +5. `docs/QUALITY.md` +6. Active plan files under `docs/exec-plans/active/` + +## Operating rules + +- Keep changes small and verifiable. +- Do not delete existing template features unless the task explicitly requires it. +- Prefer extending existing patterns over introducing parallel patterns. +- Read a file before changing it. +- Update docs when behavior, architecture, or workflow changes. +- Run verification before declaring work done. + +## Definition of done + +A task is complete only when: + +- implementation is present, +- docs are updated when needed, +- `npm run verify` passes, or failures are explained precisely, +- plan file status is updated if the task used an execution plan. + +## Where things live + +- Product and engineering guidance: `docs/` +- Active execution plans: `docs/exec-plans/active/` +- Completed execution plans: `docs/exec-plans/completed/` +- Codex project config: `.codex/` +- Worktree/bootstrap helpers: `scripts/` + +## Command preferences + +- Use `npm run verify` for the standard local verification flow. +- Use `npm run worktree:bootstrap` in new worktrees. +- Use `npm run plan:new -- --slug= --title=""` to create a new execution plan. + +## Constraints + +- Preserve the existing non-agentic template structure and behavior. +- Additive improvements are preferred to replacements. diff --git a/README.md b/README.md index 7924e06..6c21522 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,30 @@ Production-ready Node.js/Express.js backend template for building scalable, secu **Maintained by:** Codebucket Solutions Pvt. Ltd. 📚 **Documentation**: See [`docs/`](./docs) for detailed usage guides. +Workflow engine guide: [`service/v1/workflow-engine/README.md`](./service/v1/workflow-engine/README.md) + +## 🤖 Codex / Agent-Ready Additions + +This template now includes an additive repository operating layer for Codex and other coding agents while preserving the existing backend template structure. + +### Added capabilities + +- `AGENTS.md` for repo-level operating guidance +- `.codex/config.toml` for trusted project-scoped Codex defaults +- `docs/ARCHITECTURE.md`, `docs/WORKFLOW.md`, `docs/QUALITY.md`, `docs/SECURITY.md`, and `docs/RELIABILITY.md` +- execution plans under `docs/exec-plans/` +- deterministic worktree bootstrap script +- repository verification and quality scoring scripts +- CI workflow for baseline enforcement +- Codex app setup guide in `docs/CODEX_APP_SETUP.md` + +### Recommended commands + +```bash +npm run worktree:bootstrap +npm run verify +npm run plan:new -- --slug=my-task --title="My Task" +``` --- @@ -60,7 +84,7 @@ node-template/ ├── routes/ # API routes (versioned) │ └── v1/ ├── service/ # Business logic layer -│ └── v1/ +│ └── v1/ # Includes workflow/assignment orchestration services ├── utils/ # Utility functions ├── plop-templates/ # Code generation templates ├── app.js # Express app configuration @@ -195,6 +219,15 @@ npm run plop - **Virus scanning** support (ClamAV) - **Upload rate limiting** +### 10. **Workflow & Assignment Engine** 🔄 + +- **Sequelize-backed assignment rules** for auto-assignment and reassignment +- **Workflow action timeline** for application-level state transitions +- **Escalation matrix** for stage/action-driven routing and auto escalation +- **Resolver-based assignee lookup** for context-sensitive assignment flows + +📖 **[View workflow engine guide →](./service/v1/workflow-engine/README.md)** + ### 10. **Email Notifications** 📧 - **Nodemailer** with Zoho SMTP @@ -246,12 +279,29 @@ NODE_ENV=development # JWT JWT_PRIVATE_KEY=your_secret_key -# Email (Zoho) +# Email +EMAIL_TRANSPORT=SMTP EMAIL_HOST=smtp.zoho.in EMAIL_PORT=465 EMAIL_USER=your_email@example.com EMAIL_PASSWORD=your_email_password +# Mail server transport (used when EMAIL_TRANSPORT is not SMTP) +MAILSERVER_URL= +MAILSERVER_SENDERID= +MAILSERVER_ACCESSTOKEN= + +# SMS +SMS_TRANSPORT=GOVT +SMS_SERVER_URL= +SMS_SENDER_ID= +SMS_ACCESS_TOKEN= +PASSWORDSMS= +ENDPOINT= +KEY= +SENDERID= +USERNAMESMS= + # Redis (Production Rate Limiting) REDIS_HOST=127.0.0.1 REDIS_PORT=6379 @@ -506,3 +556,7 @@ PORT=9001 For internal support, contact: **team@thecodebucket.com** Repository: https://github.com/Codebucket-Solutions/node-template + +## Symphony support + +This template now includes a root `WORKFLOW.md`, `scripts/symphony-bootstrap.sh`, and `docs/SYMPHONY_SETUP.md` so it can be used with OpenAI Symphony in addition to Codex worktree workflows. diff --git a/WORKFLOW.md b/WORKFLOW.md new file mode 100644 index 0000000..7bc92f9 --- /dev/null +++ b/WORKFLOW.md @@ -0,0 +1,70 @@ +--- +tracker: + kind: linear + api_key: $LINEAR_API_KEY + project_slug: "replace-with-your-linear-project-slug" +workspace: + root: $SYMPHONY_WORKSPACE_ROOT +hooks: + after_create: | + git clone --depth 1 "$SOURCE_REPO_URL" . + bash scripts/symphony-bootstrap.sh +agent: + max_concurrent_agents: 5 + max_turns: 20 +codex: + command: "${CODEX_BIN:-codex} app-server" + # approval_policy values depend on your Codex app-server version. + # Common values documented by OpenAI include: untrusted, on-failure, on-request, never. + approval_policy: on-request + thread_sandbox: workspace-write +server: + port: 4100 +--- + +# Symphony workflow contract + +You are working on a tracked issue in this repository. + +## Read before making changes + +1. `AGENTS.md` +2. `README.md` +3. `docs/README.md` +4. `docs/ARCHITECTURE.md` +5. `docs/WORKFLOW.md` +6. `docs/QUALITY.md` +7. The most relevant files under `docs/exec-plans/active/` + +## Repository expectations + +- Preserve all existing template behavior unless the issue explicitly requires otherwise. +- Prefer additive changes over replacements. +- Keep changes small, verifiable, and easy to review. +- Read files before editing them. +- Update docs when architecture, behavior, workflow, or developer setup changes. +- Run repository verification before finishing. + +## Issue handling + +- Treat the Linear issue identifier, title, and body as the task contract. +- If the issue is broad, create or update an execution plan under `docs/exec-plans/active/` before making large changes. +- If the issue is underspecified, choose the safest minimal implementation that still resolves the request. + +## Verification + +Before declaring the issue done, run: + +```sh +npm run verify +``` + +If verification fails, either fix the issue or report the failure precisely with the command output and a concise explanation. + +## Completion checklist + +- Implementation completed. +- Docs updated when needed. +- Plan updated when used. +- Verification run. +- Final summary includes files changed, verification status, and any follow-up work. diff --git a/bin/www b/bin/www index a6b7cc7..9b4e2ad 100644 --- a/bin/www +++ b/bin/www @@ -10,38 +10,39 @@ const cpu = require("os").cpus().length; const app = require("../app"); const debug = require("debug")("sidebrain:server"); const http = require("http"); -const port = normalizePort(process.env.PORT || "3000"); +const port = normalizePort(process.env.PORT || "8080"); let server; +const initMaster = require("../init"); /** * Get port from environment and store in Express. */ -if (cluster.isMaster) { - // Master initialization (DB, Cron, etc.) - const initMaster = require("../init"); - initMaster() - .then(() => { - console.log("✔ Master initialization completed"); - }) - .catch(err => { - console.error("❌ Master initialization failed:", err); - process.exit(1); - }); +async function runMaster() { + console.log("Starting master initialization"); + await initMaster(); + console.log("Master-only services initialized"); - // Worker const workers = - process.env.NODE_ENV === "stage" || process.env.NODE_ENV === "development" + process.env.WEB_CONCURRENCY ?? + (process.env.NODE_ENV === "stage" || process.env.NODE_ENV === "development" ? 1 - : Math.max(1, cpu); - for (let i = 0; i < workers; i++) { - console.log("Forking worker", i + 1, "of", workers); + : Math.max(1, cpu)); + + for (let i = 0; i < Number(workers); i++) { + console.log("Forking worker", i + 1, "of", Number(workers)); cluster.fork(); } +} + +if (cluster.isPrimary) { + runMaster().catch(err => { + console.error("Master initialization failed:", err); + process.exit(1); + }); - // Auto-restart worker on crash cluster.on("exit", worker => { - console.log(`Worker ${worker.process.pid} died → restarting…`); + console.log(`Worker ${worker.process.pid} died, restarting`); cluster.fork(); }); } else { diff --git a/docker-compose.yml b/docker-compose.yml index eb018e8..1d834e5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,15 +1,15 @@ services: - node-template: - image: harbor.internal.codebuckets.in/node-template/stage - container_name: node-template - restart: unless-stopped - ports: - - "127.0.0.1:25000:3000" - networks: - - database_network - env_file: - - /home/empty.env + node-template: + image: harbor.internal.codebuckets.in/node-template/stage + container_name: node-template + restart: unless-stopped + ports: + - "127.0.0.1:25000:3000" + networks: + - database_network + env_file: + - /home/empty.env networks: - database_network: - external: true + database_network: + external: true diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md new file mode 100644 index 0000000..8ead789 --- /dev/null +++ b/docs/ARCHITECTURE.md @@ -0,0 +1,30 @@ +# Architecture + +This repository is an additive enhancement of the existing Node.js backend template. The original application structure remains the primary runtime architecture. + +## Runtime architecture + +```text +Request -> Routes -> Middleware -> Controllers -> Services -> Models -> Database +``` + +## Harness-engineering overlay + +The repository also includes a repo-native operating layer designed for Codex and other coding agents: + +- `AGENTS.md`: short operational contract for agents. +- `docs/`: versioned system of record for architecture, workflow, quality, reliability, and security. +- `docs/exec-plans/`: checked-in execution plans for task tracking. +- `.codex/`: project-scoped Codex configuration. +- `scripts/`: deterministic setup, verification, and plan-generation helpers. +- `.github/workflows/`: mechanical enforcement in CI. + +## Architectural principle + +Application code and repo operations are separate concerns: + +- App code continues to live in `app.js`, `routes/`, `middleware/`, `models/`, `helper/`, and `utils/`. +- Stateful business workflows can be implemented in `service/v1/` with Sequelize models backing assignment, workflow action, and escalation rules. +- Agent operating guidance lives in repo-root docs and scripts. + +This keeps the backend template usable for normal development while making the repository legible to Codex. diff --git a/docs/CODEX_APP_SETUP.md b/docs/CODEX_APP_SETUP.md new file mode 100644 index 0000000..a193076 --- /dev/null +++ b/docs/CODEX_APP_SETUP.md @@ -0,0 +1,46 @@ +# Codex App Setup + +This repository is prepared for Codex app, Codex IDE extension, and Codex CLI workflows. + +## 1. Trust the repository + +Open the repository in Codex and trust the project so `.codex/config.toml` is loaded. + +## 2. Create a local environment + +In the Codex app settings for this project, create a local environment and set the setup script to: + +```sh +npm ci || npm install +npm run worktree:bootstrap +``` + +OpenAI documents that local environments can define setup steps for worktrees and common project actions, and that this configuration is stored inside the `.codex` folder at the repo root. citeturn337372search0turn993475view0turn993475view1 + +## 3. Add project actions + +Recommended actions: + +- Verify -> `npm run verify` +- Start dev server -> `npm run start:dev` +- Validate docs -> `npm run docs:validate` +- Score quality -> `npm run quality:score` +- New plan -> `npm run plan:new -- --slug=<slug> --title="<title>"` + +The Codex app exposes actions in the top bar and runs them in the integrated terminal. citeturn993475view0 + +## 4. Use worktrees for isolated tasks + +Codex app supports independent worktrees so multiple tasks do not interfere with each other. citeturn337372search2 + +Suggested pattern: + +```sh +git worktree add ../wt-task-name -b task/name +cd ../wt-task-name +npm run worktree:bootstrap +``` + +## 5. Use AGENTS.md as the entry point + +Codex reads `AGENTS.md` files before doing work, so keep high-level guidance there and detailed guidance in `docs/`. citeturn337372search1 diff --git a/docs/QUALITY.md b/docs/QUALITY.md new file mode 100644 index 0000000..c02cffd --- /dev/null +++ b/docs/QUALITY.md @@ -0,0 +1,25 @@ +# Quality + +## Baseline checks + +- ESLint +- Prettier formatting check +- Documentation validation +- Lightweight repository quality score generation + +## Standard command + +```sh +npm run verify +``` + +## Quality goals + +- Small diffs over broad rewrites +- Update docs when behavior or architecture changes +- Keep instructions discoverable from the repo root +- Ensure worktree setup is deterministic + +## Output artifacts + +- `docs/references/quality-score.json` diff --git a/docs/README.md b/docs/README.md index ad3c8a6..56b6c37 100644 --- a/docs/README.md +++ b/docs/README.md @@ -26,6 +26,30 @@ Complete guide for using the environment-aware rate limiting system with example - Redis configuration - Testing and monitoring +### [Architecture](./ARCHITECTURE.md) + +Repository architecture plus the additive harness-engineering layer for Codex. + +### [Workflow](./WORKFLOW.md) + +Human and Codex app workflow, including worktree usage. + +### [Quality](./QUALITY.md) + +Baseline verification rules and repository quality scoring. + +### [Security](./SECURITY.md) + +Repository and Codex operating constraints for safer changes. + +### [Reliability](./RELIABILITY.md) + +Deterministic setup and operational guidance for worktrees and CI. + +### [Codex App Setup](./CODEX_APP_SETUP.md) + +Instructions for configuring Codex app local environments and project actions. + ## Coming Soon Additional documentation will be added for: @@ -42,3 +66,5 @@ Additional documentation will be added for: --- For general overview and quick start, see the main [README](../README.md). + +- `SYMPHONY_SETUP.md`: how to use this template with OpenAI Symphony. diff --git a/docs/RELIABILITY.md b/docs/RELIABILITY.md new file mode 100644 index 0000000..1eedece --- /dev/null +++ b/docs/RELIABILITY.md @@ -0,0 +1,18 @@ +# Reliability + +## Goals + +- New worktrees should be bootstrappable with one command. +- Verification should be deterministic and easy to repeat. +- Documentation should stay fresh enough for Codex to use as a system of record. + +## Reliability controls + +- `scripts/worktree-bootstrap.sh` +- `scripts/validate-docs.js` +- `scripts/score-quality.js` +- CI workflow under `.github/workflows/ci.yml` + +## Operational note + +If a verify step fails due to missing local services such as MySQL or Redis, record the exact failure in the active plan and keep the remaining checks passing. diff --git a/docs/SECURITY.md b/docs/SECURITY.md new file mode 100644 index 0000000..c05cab1 --- /dev/null +++ b/docs/SECURITY.md @@ -0,0 +1,23 @@ +# Security + +## Repository operating guidance + +- Keep secrets out of the repository. +- Prefer environment variables or secret managers for runtime configuration. +- Review changes to auth, rate limiting, uploads, mail, and database access carefully. +- Do not weaken validation, authorization, or security middleware without explicit requirements. + +## Codex safety guidance + +- Default Codex project configuration uses `approval_policy = "on-request"` and `sandbox_mode = "workspace-write"`. +- Review shell commands that install packages, change Git state, or alter deployment files. + +## Existing security-related template areas + +- `middleware/auth.js` +- `middleware/rate-limiter.js` +- `middleware/validator.js` +- `utils/token.js` +- `utils/hash.js` +- `utils/upload.js` +- `helper/casbin-enforcer.js` diff --git a/docs/SYMPHONY_SETUP.md b/docs/SYMPHONY_SETUP.md new file mode 100644 index 0000000..b908d0a --- /dev/null +++ b/docs/SYMPHONY_SETUP.md @@ -0,0 +1,82 @@ +# Symphony setup + +This repository includes a root `WORKFLOW.md` and a `scripts/symphony-bootstrap.sh` helper so it can be used with OpenAI Symphony. + +## What Symphony expects + +OpenAI's Symphony reference flow polls Linear for work, creates one workspace per issue, launches Codex in app-server mode inside that workspace, sends a workflow prompt, and keeps Codex working until the issue is done. It also expects the repository to provide a `WORKFLOW.md` file, and the Elixir reference implementation defaults to `./WORKFLOW.md` if no custom path is supplied. + +## 1. Required environment variables + +Set these before starting Symphony: + +```sh +export LINEAR_API_KEY="<your-linear-personal-api-key>" +export SOURCE_REPO_URL="git@github.com:your-org/your-repo.git" +export SYMPHONY_WORKSPACE_ROOT="$HOME/code/symphony-workspaces" +export CODEX_BIN="codex" +``` + +`LINEAR_API_KEY` is the Linear personal token Symphony reads by default. `WORKFLOW.md` can also reference `$LINEAR_API_KEY`, `$SYMPHONY_WORKSPACE_ROOT`, and other environment-backed values. + +## 2. Update `WORKFLOW.md` + +Edit these values in the YAML front matter: + +- `tracker.project_slug`: the Linear project slug +- `workspace.root`: the parent directory where Symphony should create per-issue workspaces +- `hooks.after_create`: normally keep the repo clone plus `bash scripts/symphony-bootstrap.sh` +- `codex.command`: the Codex executable and optional model flags you want Symphony to use +- `server.port`: optional dashboard port for the Symphony service + +### Finding the Linear project slug + +In Linear, open the project and copy the URL. The slug is the project segment in that URL. + +## 3. Install and run Symphony + +The public OpenAI Symphony repo currently ships an experimental Elixir reference implementation. Its documented startup flow is: + +```sh +git clone https://github.com/openai/symphony +cd symphony/elixir +mise trust +mise install +mise exec -- mix setup +mise exec -- mix build +mise exec -- ./bin/symphony /absolute/path/to/your/repo/WORKFLOW.md --port 4100 +``` + +If you omit the path, Symphony defaults to its local `./WORKFLOW.md`, so passing your repo's workflow path is the safest option. + +## 4. Linear workflow statuses + +OpenAI's reference README notes that its default workflow expects non-standard Linear statuses: + +- `Rework` +- `Human Review` +- `Merging` + +Add those in Linear Team Settings if you want to follow the reference flow closely. Otherwise, customize the workflow body and your Symphony setup to match your existing status model. + +## 5. Recommended repo usage + +- Keep `AGENTS.md` and `docs/` up to date. +- Use `docs/exec-plans/active/` for medium and large changes. +- Keep `scripts/worktree-bootstrap.sh` and `scripts/symphony-bootstrap.sh` deterministic. +- Let Symphony create one fresh workspace per issue. +- Avoid relying on shell aliases, personal dotfiles, or untracked local config inside workspaces. + +## 6. Verification expectation + +Your Symphony workflow already tells Codex to run: + +```sh +npm run verify +``` + +Keep that command healthy and fast. It is your main repository-level guardrail during autonomous runs. + +## 7. Optional skills + +OpenAI's reference README says you can optionally copy `commit`, `push`, `pull`, `land`, and `linear` skills into your repo. This template does not ship those skills because they are optional and may depend on your exact workflow. Add them later under `.codex/skills/` if you decide to mirror the reference setup more closely. diff --git a/docs/WORKFLOW.md b/docs/WORKFLOW.md new file mode 100644 index 0000000..c1c896a --- /dev/null +++ b/docs/WORKFLOW.md @@ -0,0 +1,34 @@ +# Workflow + +## Standard human workflow + +1. Create a branch. +2. Make the smallest useful change. +3. Run `npm run verify`. +4. Update docs if architecture or usage changed. +5. Commit and open a PR. + +## Codex app workflow + +1. Open the repo in the Codex app or IDE extension. +2. Trust the project so project-scoped `.codex/config.toml` can load. +3. Configure a local environment in the Codex app using the commands in `.codex/README.md`. +4. Start work in Local mode for exploration. +5. Switch to Worktree mode for isolated implementation tasks. +6. Use `npm run worktree:bootstrap` in each new worktree. +7. Use active execution plans in `docs/exec-plans/active/` for medium or large tasks. +8. Run `npm run verify` before review or merge. + +## Worktree workflow + +```sh +git worktree add ../wt-feature-name -b feature/name +cd ../wt-feature-name +npm run worktree:bootstrap +``` + +## Plan lifecycle + +- Create a new plan with `npm run plan:new -- --slug=<slug> --title="<title>"`. +- Keep status, acceptance criteria, and verification notes current. +- Move finished plans from `docs/exec-plans/active/` to `docs/exec-plans/completed/`. diff --git a/docs/design-docs/README.md b/docs/design-docs/README.md new file mode 100644 index 0000000..5ec408a --- /dev/null +++ b/docs/design-docs/README.md @@ -0,0 +1,3 @@ +# Design Docs + +Store implementation design docs and ADR-style notes here for substantial backend changes. diff --git a/docs/exec-plans/README.md b/docs/exec-plans/README.md new file mode 100644 index 0000000..f429f7a --- /dev/null +++ b/docs/exec-plans/README.md @@ -0,0 +1,3 @@ +# Execution Plans + +Use `active/` for ongoing work and `completed/` for archived work. diff --git a/docs/exec-plans/active/sample-template-hardening.md b/docs/exec-plans/active/sample-template-hardening.md new file mode 100644 index 0000000..93428fb --- /dev/null +++ b/docs/exec-plans/active/sample-template-hardening.md @@ -0,0 +1,27 @@ +# Execution Plan: template-hardening + +- Status: active +- Owner: unassigned +- Scope: additive repository hardening for Codex workflows + +## Goal + +Make the repository easier for Codex and human contributors to operate without changing existing application behavior. + +## Acceptance criteria + +- Repo-scoped guidance exists. +- Worktree bootstrap is deterministic. +- Verification is available through one command. +- CI runs the baseline checks. + +## Checklist + +- [x] Add `AGENTS.md` +- [x] Add repo operating docs +- [x] Add scripts for bootstrap, doc validation, quality scoring, and plan generation +- [x] Add CI workflow + +## Verification notes + +- Run `npm run verify` diff --git a/docs/product-specs/README.md b/docs/product-specs/README.md new file mode 100644 index 0000000..5b09fda --- /dev/null +++ b/docs/product-specs/README.md @@ -0,0 +1,3 @@ +# Product Specs + +Store feature-level product requirements here when this template is used to start a real backend project. diff --git a/docs/references/README.md b/docs/references/README.md new file mode 100644 index 0000000..d3be98f --- /dev/null +++ b/docs/references/README.md @@ -0,0 +1,3 @@ +# References + +Generated and supporting artifacts live here, such as quality scores or architecture inventories. diff --git a/docs/references/quality-score.json b/docs/references/quality-score.json new file mode 100644 index 0000000..5d50621 --- /dev/null +++ b/docs/references/quality-score.json @@ -0,0 +1,44 @@ +{ + "generatedAt": "2026-03-11T10:26:06.224Z", + "score": 100, + "passed": 9, + "total": 9, + "checks": [ + { + "name": "AGENTS.md", + "ok": true + }, + { + "name": "docs/ARCHITECTURE.md", + "ok": true + }, + { + "name": "docs/WORKFLOW.md", + "ok": true + }, + { + "name": "docs/QUALITY.md", + "ok": true + }, + { + "name": "docs/SECURITY.md", + "ok": true + }, + { + "name": "docs/RELIABILITY.md", + "ok": true + }, + { + "name": ".codex/config.toml", + "ok": true + }, + { + "name": "scripts/worktree-bootstrap.sh", + "ok": true + }, + { + "name": "CI workflow", + "ok": true + } + ] +} diff --git a/env.development.example b/env.development.example index cac03db..a7c94a4 100644 --- a/env.development.example +++ b/env.development.example @@ -7,6 +7,19 @@ EMAIL_PORT=465 EMAIL_HOST="smtp.zoho.in" EMAIL_USER="team@thecodebucket.com" EMAIL_PASSWORD="Team$123" +EMAIL_TRANSPORT=SMTP +MAILSERVER_URL= +MAILSERVER_SENDERID= +MAILSERVER_ACCESSTOKEN= +SMS_TRANSPORT=GOVT +SMS_SERVER_URL= +SMS_SENDER_ID= +SMS_ACCESS_TOKEN= +PASSWORDSMS= +ENDPOINT= +KEY= +SENDERID= +USERNAMESMS= BASEURL=http://localhost:5000/ VIRUS_CHECKER=OFF JWT_PRIVATE_KEY=swc_random_string diff --git a/eslint.config.js b/eslint.config.js index 3c647b3..5c083be 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,6 +1,45 @@ +const path = require("node:path"); const eslintPluginPrettier = require("eslint-plugin-prettier"); const eslintConfigPrettier = require("eslint-config-prettier"); -const filenamesSimple = require("eslint-plugin-filenames-simple"); + +const kebabCaseFilenamesPlugin = { + rules: { + "naming-convention": { + meta: { + type: "suggestion", + docs: { + description: "enforce kebab-case JavaScript filenames", + }, + schema: [], + }, + create(context) { + return { + Program(node) { + const filename = context.filename; + + if (!filename || filename === "<input>") { + return; + } + + const extension = path.extname(filename); + const basename = path.basename(filename, extension); + const isKebabCase = /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(basename); + + if (!isKebabCase) { + context.report({ + node, + message: "Filename '{{name}}' should be kebab-case.", + data: { + name: basename, + }, + }); + } + }, + }; + }, + }, + }, +}; module.exports = [ { @@ -40,7 +79,7 @@ module.exports = [ }, plugins: { "prettier": eslintPluginPrettier, - "filenames-simple": filenamesSimple, + "filenames-simple": kebabCaseFilenamesPlugin, }, rules: { ...eslintConfigPrettier.rules, @@ -73,7 +112,7 @@ module.exports = [ "brace-style": ["error", "1tbs"], // Naming Conventions - "filenames-simple/naming-convention": ["error", { rule: "kebab-case" }], + "filenames-simple/naming-convention": "error", "semi": ["error", "always"], }, }, diff --git a/helper/cron-timer.js b/helper/cron-timer.js index 0930d85..889067f 100644 --- a/helper/cron-timer.js +++ b/helper/cron-timer.js @@ -1,5 +1,3 @@ -const cron = require("node-cron"); - const sampleScheduledTask = async () => { console.log(`[CRON Job] Execution started: ${new Date().toLocaleString()}`); @@ -11,19 +9,6 @@ const sampleScheduledTask = async () => { } }; -// --- Setting the CRON Schedule --- - -// Format: second minute hour day-of-month month day-of-week -// * Second: 0-59 -// * Minute: 0-59 -// * Hour: 0-23 -// * Day-of-month: 1-31 -// * Month: 1-12 (or jan-dec) -// * Day-of-week: 0-7 (0 or 7 = Sunday, 1 = Monday) - -// Current Schedule: Run Every day, at exactly 12:00:00 AM (midnight). -cron.schedule(`0 0 0 * * *`, sampleScheduledTask); - module.exports = { sampleScheduledTask, }; diff --git a/helper/cron.js b/helper/cron.js new file mode 100644 index 0000000..9de8990 --- /dev/null +++ b/helper/cron.js @@ -0,0 +1,10 @@ +const cron = require("node-cron"); +const { sampleScheduledTask } = require("./cron-timer"); + +const registerCronJobs = () => { + cron.schedule("0 0 0 * * *", sampleScheduledTask); +}; + +module.exports = { + registerCronJobs, +}; diff --git a/init.js b/init.js index bbc4ccb..7c389cf 100644 --- a/init.js +++ b/init.js @@ -1,5 +1,5 @@ const sequelize = require("./config/db"); -const { sampleScheduledTask } = require("./helper/cron-timer"); +const { registerCronJobs } = require("./helper/cron"); function initMaster() { console.log("Master Initialization..."); @@ -9,8 +9,7 @@ function initMaster() { .then(() => { console.log("Database connected"); - // CRON Jobs - sampleScheduledTask(); + registerCronJobs(); console.log("Cron jobs started"); }) .catch(err => { diff --git a/models/assigner_rule.js b/models/assigner_rule.js new file mode 100644 index 0000000..e4441f9 --- /dev/null +++ b/models/assigner_rule.js @@ -0,0 +1,67 @@ +module.exports = function (sequelize, DataTypes) { + return sequelize.define( + "assigner_rule", + { + id: { + autoIncrement: true, + type: DataTypes.BIGINT.UNSIGNED, + allowNull: false, + primaryKey: true, + }, + application_type: { + type: DataTypes.STRING(50), + allowNull: false, + }, + conditions_json: { + type: DataTypes.JSON, + allowNull: true, + }, + assignee_type: { + type: DataTypes.ENUM("USER", "ROLE"), + allowNull: false, + }, + assignee_id: { + type: DataTypes.STRING(100), + allowNull: false, + }, + assignee_mode: { + type: DataTypes.ENUM("STATIC", "CONTEXT"), + allowNull: false, + defaultValue: "STATIC", + }, + assignee_resolver_key: { + type: DataTypes.STRING(50), + allowNull: true, + }, + assignee_resolver_params: { + type: DataTypes.JSON, + allowNull: true, + }, + priority: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: 100, + }, + is_active: { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: true, + }, + created_at: { + type: DataTypes.DATE, + allowNull: false, + defaultValue: DataTypes.NOW, + }, + updated_at: { + type: DataTypes.DATE, + allowNull: false, + defaultValue: DataTypes.NOW, + }, + }, + { + sequelize, + tableName: "assigner_rule", + timestamps: false, + }, + ); +}; diff --git a/models/assignment.js b/models/assignment.js new file mode 100644 index 0000000..8182bda --- /dev/null +++ b/models/assignment.js @@ -0,0 +1,62 @@ +module.exports = function (sequelize, DataTypes) { + return sequelize.define( + "assignment", + { + id: { + autoIncrement: true, + type: DataTypes.BIGINT.UNSIGNED, + allowNull: false, + primaryKey: true, + }, + application_type: { + type: DataTypes.STRING(50), + allowNull: false, + }, + application_id: { + type: DataTypes.STRING(50), + allowNull: false, + }, + assignee_type: { + type: DataTypes.ENUM("USER", "ROLE"), + allowNull: false, + }, + assignee_id: { + type: DataTypes.STRING(100), + allowNull: false, + }, + assigned_by: { + type: DataTypes.STRING(50), + allowNull: true, + }, + assigned_at: { + type: DataTypes.DATE, + allowNull: false, + defaultValue: DataTypes.NOW, + }, + status: { + type: DataTypes.ENUM("ACTIVE", "CLOSED"), + allowNull: false, + defaultValue: "ACTIVE", + }, + closed_at: { + type: DataTypes.DATE, + allowNull: true, + }, + created_at: { + type: DataTypes.DATE, + allowNull: false, + defaultValue: DataTypes.NOW, + }, + updated_at: { + type: DataTypes.DATE, + allowNull: false, + defaultValue: DataTypes.NOW, + }, + }, + { + sequelize, + tableName: "assignment", + timestamps: false, + }, + ); +}; diff --git a/models/init-models.js b/models/init-models.js index 6eb3648..661e4d1 100644 --- a/models/init-models.js +++ b/models/init-models.js @@ -1,20 +1,35 @@ +const _assigner_rule = require("./assigner_rule"); +const _assignment = require("./assignment"); const DataTypes = require("sequelize").DataTypes; const _casbin_rule = require("./casbin_rule"); const _sys_dropdown_list = require("./sys_dropdown_list"); const _sys_otp_template = require("./sys_otp_template"); const _sys_tables = require("./sys_tables"); +const _workflow_action = require("./workflow_action"); +const _workflow_application_registry = require("./workflow_application_registry"); +const _workflow_escalation_matrix = require("./workflow_escalation_matrix"); function initModels(sequelize) { + const assigner_rule = _assigner_rule(sequelize, DataTypes); + const assignment = _assignment(sequelize, DataTypes); const casbin_rule = _casbin_rule(sequelize, DataTypes); const sys_dropdown_list = _sys_dropdown_list(sequelize, DataTypes); const sys_otp_template = _sys_otp_template(sequelize, DataTypes); const sys_tables = _sys_tables(sequelize, DataTypes); + const workflow_action = _workflow_action(sequelize, DataTypes); + const workflow_application_registry = _workflow_application_registry(sequelize, DataTypes); + const workflow_escalation_matrix = _workflow_escalation_matrix(sequelize, DataTypes); return { + assigner_rule, + assignment, casbin_rule, sys_dropdown_list, sys_otp_template, sys_tables, + workflow_action, + workflow_application_registry, + workflow_escalation_matrix, }; } module.exports = initModels; diff --git a/models/workflow_action.js b/models/workflow_action.js new file mode 100644 index 0000000..cb34f2a --- /dev/null +++ b/models/workflow_action.js @@ -0,0 +1,71 @@ +module.exports = function (sequelize, DataTypes) { + return sequelize.define( + "workflow_action", + { + id: { + autoIncrement: true, + type: DataTypes.BIGINT.UNSIGNED, + allowNull: false, + primaryKey: true, + }, + application_type: { + type: DataTypes.STRING(50), + allowNull: false, + }, + application_id: { + type: DataTypes.STRING(50), + allowNull: false, + }, + action: { + type: DataTypes.STRING(50), + allowNull: false, + }, + from_assignee_type: { + type: DataTypes.ENUM("USER", "ROLE"), + allowNull: true, + }, + from_assignee_id: { + type: DataTypes.STRING(100), + allowNull: true, + }, + to_assignee_type: { + type: DataTypes.ENUM("USER", "ROLE"), + allowNull: true, + }, + to_assignee_id: { + type: DataTypes.STRING(100), + allowNull: true, + }, + from_stage: { + type: DataTypes.STRING(100), + allowNull: true, + }, + to_stage: { + type: DataTypes.STRING(100), + allowNull: true, + }, + remarks: { + type: DataTypes.TEXT, + allowNull: true, + }, + metadata_json: { + type: DataTypes.JSON, + allowNull: true, + }, + actor_user_id: { + type: DataTypes.STRING(50), + allowNull: true, + }, + created_at: { + type: DataTypes.DATE, + allowNull: false, + defaultValue: DataTypes.NOW, + }, + }, + { + sequelize, + tableName: "workflow_action", + timestamps: false, + }, + ); +}; diff --git a/models/workflow_application_registry.js b/models/workflow_application_registry.js new file mode 100644 index 0000000..a2e4300 --- /dev/null +++ b/models/workflow_application_registry.js @@ -0,0 +1,53 @@ +module.exports = function (sequelize, DataTypes) { + return sequelize.define( + "workflow_application_registry", + { + id: { + autoIncrement: true, + type: DataTypes.BIGINT.UNSIGNED, + allowNull: false, + primaryKey: true, + }, + application_type: { + type: DataTypes.STRING(50), + allowNull: false, + }, + model_name: { + type: DataTypes.STRING(100), + allowNull: false, + }, + id_field: { + type: DataTypes.STRING(100), + allowNull: false, + }, + stage_field: { + type: DataTypes.STRING(100), + allowNull: false, + }, + current_assignment_field: { + type: DataTypes.STRING(100), + allowNull: false, + }, + is_active: { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: true, + }, + created_at: { + type: DataTypes.DATE, + allowNull: false, + defaultValue: DataTypes.NOW, + }, + updated_at: { + type: DataTypes.DATE, + allowNull: false, + defaultValue: DataTypes.NOW, + }, + }, + { + sequelize, + tableName: "workflow_application_registry", + timestamps: false, + }, + ); +}; diff --git a/models/workflow_escalation_matrix.js b/models/workflow_escalation_matrix.js new file mode 100644 index 0000000..7184f43 --- /dev/null +++ b/models/workflow_escalation_matrix.js @@ -0,0 +1,92 @@ +module.exports = function (sequelize, DataTypes) { + return sequelize.define( + "workflow_escalation_matrix", + { + id: { + autoIncrement: true, + type: DataTypes.BIGINT.UNSIGNED, + allowNull: false, + primaryKey: true, + }, + application_type: { + type: DataTypes.STRING(50), + allowNull: false, + }, + current_stage: { + type: DataTypes.STRING(100), + allowNull: false, + }, + current_assignee_type: { + type: DataTypes.ENUM("USER", "ROLE"), + allowNull: true, + }, + current_assignee_id: { + type: DataTypes.STRING(100), + allowNull: true, + }, + action: { + type: DataTypes.STRING(50), + allowNull: false, + }, + next_stage: { + type: DataTypes.STRING(100), + allowNull: true, + }, + next_assignee_type: { + type: DataTypes.ENUM("USER", "ROLE"), + allowNull: true, + }, + next_assignee_id: { + type: DataTypes.STRING(100), + allowNull: true, + }, + next_assignee_mode: { + type: DataTypes.ENUM("STATIC", "CONTEXT"), + allowNull: false, + defaultValue: "STATIC", + }, + next_assignee_resolver_key: { + type: DataTypes.STRING(50), + allowNull: true, + }, + next_assignee_resolver_params: { + type: DataTypes.JSON, + allowNull: true, + }, + is_active: { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: true, + }, + priority: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: 100, + }, + auto_trigger: { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: false, + }, + auto_after_minutes: { + type: DataTypes.INTEGER, + allowNull: true, + }, + created_at: { + type: DataTypes.DATE, + allowNull: false, + defaultValue: DataTypes.NOW, + }, + updated_at: { + type: DataTypes.DATE, + allowNull: false, + defaultValue: DataTypes.NOW, + }, + }, + { + sequelize, + tableName: "workflow_escalation_matrix", + timestamps: false, + }, + ); +}; diff --git a/package-lock.json b/package-lock.json index fc2e839..b767730 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,10 @@ "license": "ISC", "dependencies": { "@codebucket/azure-env": "^1.0.2", + "@codebucket/files": "^1.0.0", "@codebucket/logserver-transport": "^1.0.2", + "@codebucket/mail-transport": "^1.0.0", + "@codebucket/sms": "^1.0.0", "aws-sdk": "^2.1309.0", "axios": "^1.3.2", "bcryptjs": "^2.4.3", @@ -33,6 +36,7 @@ "jsonwebtoken": "^9.0.0", "moment-timezone": "^0.5.40", "mysql2": "^3.1.0", + "node-cron": "^4.2.1", "nodemailer": "^7.0.10", "pino": "^8.20.0", "pino-http": "^9.0.0", @@ -45,825 +49,2501 @@ "devDependencies": { "eslint": "^9.39.1", "eslint-config-prettier": "^10.1.8", - "eslint-plugin-filenames-simple": "^0.9.0", "eslint-plugin-node": "^11.1.0", "eslint-plugin-prettier": "^5.5.4", "husky": "^9.1.7", "lint-staged": "^16.2.7", + "nodemon": "^3.1.11", "plop": "^3.1.1", "prettier": "^3.5.3" } }, - "node_modules/@azure/abort-controller": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", - "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", - "license": "MIT", + "node_modules/@aws-crypto/crc32": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", + "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", + "license": "Apache-2.0", "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=16.0.0" } }, - "node_modules/@azure/core-auth": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.9.0.tgz", - "integrity": "sha512-FPwHpZywuyasDSLMqJ6fhbOK3TqUdviZNF8OqRGA4W5Ewib2lEEZ+pBsYcBa88B2NGO/SEnYPGhyBqNlE8ilSw==", - "license": "MIT", + "node_modules/@aws-crypto/crc32c": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32c/-/crc32c-5.2.0.tgz", + "integrity": "sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==", + "license": "Apache-2.0", "dependencies": { - "@azure/abort-controller": "^2.0.0", - "@azure/core-util": "^1.11.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" } }, - "node_modules/@azure/core-client": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.9.2.tgz", - "integrity": "sha512-kRdry/rav3fUKHl/aDLd/pDLcB+4pOFwPPTVEExuMyaI5r+JBbMWqRbCY1pn5BniDaU3lRxO9eaQ1AmSMehl/w==", - "license": "MIT", + "node_modules/@aws-crypto/sha1-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha1-browser/-/sha1-browser-5.2.0.tgz", + "integrity": "sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==", + "license": "Apache-2.0", "dependencies": { - "@azure/abort-controller": "^2.0.0", - "@azure/core-auth": "^1.4.0", - "@azure/core-rest-pipeline": "^1.9.1", - "@azure/core-tracing": "^1.0.0", - "@azure/core-util": "^1.6.1", - "@azure/logger": "^1.0.0", + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" } }, - "node_modules/@azure/core-http-compat": { + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/is-array-buffer": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@azure/core-http-compat/-/core-http-compat-2.2.0.tgz", - "integrity": "sha512-1kW8ZhN0CfbNOG6C688z5uh2yrzALE7dDXHiR9dY4vt+EbhGZQSbjDa5bQd2rf3X2pdWMsXbqbArxUyeNdvtmg==", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", "dependencies": { - "@azure/abort-controller": "^2.0.0", - "@azure/core-client": "^1.3.0", - "@azure/core-rest-pipeline": "^1.19.0" + "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=14.0.0" } }, - "node_modules/@azure/core-lro": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.7.2.tgz", - "integrity": "sha512-0YIpccoX8m/k00O7mDDMdJpbr6mf1yWo2dfmxt5A8XVZVVMz2SSKaEbMCeJRvgQ0IaSlqhjT47p4hVIRRy90xw==", - "license": "MIT", + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", "dependencies": { - "@azure/abort-controller": "^2.0.0", - "@azure/core-util": "^1.2.0", - "@azure/logger": "^1.0.0", + "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=14.0.0" } }, - "node_modules/@azure/core-paging": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.6.2.tgz", - "integrity": "sha512-YKWi9YuCU04B55h25cnOYZHxXYtEvQEbKST5vqRga7hWY9ydd3FZHdeQF8pyh+acWZvppw13M/LMGx0LABUVMA==", - "license": "MIT", + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=14.0.0" } }, - "node_modules/@azure/core-rest-pipeline": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.19.0.tgz", - "integrity": "sha512-bM3308LRyg5g7r3Twprtqww0R/r7+GyVxj4BafcmVPo4WQoGt5JXuaqxHEFjw2o3rvFZcUPiqJMg6WuvEEeVUA==", - "license": "MIT", + "node_modules/@aws-crypto/sha256-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", + "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-js": "^5.2.0", + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", "dependencies": { - "@azure/abort-controller": "^2.0.0", - "@azure/core-auth": "^1.8.0", - "@azure/core-tracing": "^1.0.1", - "@azure/core-util": "^1.11.0", - "@azure/logger": "^1.0.0", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=14.0.0" } }, - "node_modules/@azure/core-tracing": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.2.0.tgz", - "integrity": "sha512-UKTiEJPkWcESPYJz3X5uKRYyOcJD+4nYph+KpfdPRnQJVrZfk0KJgdnaAWKfhsBBtAf/D58Az4AvCJEmWgIBAg==", - "license": "MIT", + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=14.0.0" } }, - "node_modules/@azure/core-util": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.11.0.tgz", - "integrity": "sha512-DxOSLua+NdpWoSqULhjDyAZTXFdP/LKkqtYuxxz1SCN289zk3OG8UOpnCQAz/tygyACBtWp/BoO72ptK7msY8g==", - "license": "MIT", + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", "dependencies": { - "@azure/abort-controller": "^2.0.0", + "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=14.0.0" } }, - "node_modules/@azure/identity": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.6.0.tgz", - "integrity": "sha512-ANpO1iAvcZmpD4QY7/kaE/P2n66pRXsDp3nMUC6Ow3c9KfXOZF7qMU9VgqPw8m7adP7TVIbVyrCEmD9cth3KQQ==", - "license": "MIT", + "node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", + "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "license": "Apache-2.0", "dependencies": { - "@azure/abort-controller": "^2.0.0", - "@azure/core-auth": "^1.9.0", - "@azure/core-client": "^1.9.2", - "@azure/core-rest-pipeline": "^1.17.0", - "@azure/core-tracing": "^1.0.0", - "@azure/core-util": "^1.11.0", - "@azure/logger": "^1.0.0", - "@azure/msal-browser": "^4.0.1", - "@azure/msal-node": "^2.15.0", - "events": "^3.0.0", - "jws": "^4.0.0", - "open": "^8.0.0", - "stoppable": "^1.1.0", - "tslib": "^2.2.0" + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=16.0.0" } }, - "node_modules/@azure/keyvault-common": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@azure/keyvault-common/-/keyvault-common-2.0.0.tgz", - "integrity": "sha512-wRLVaroQtOqfg60cxkzUkGKrKMsCP6uYXAOomOIysSMyt1/YM0eUn9LqieAWM8DLcU4+07Fio2YGpPeqUbpP9w==", - "license": "MIT", + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", + "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", + "license": "Apache-2.0", "dependencies": { - "@azure/abort-controller": "^2.0.0", - "@azure/core-auth": "^1.3.0", - "@azure/core-client": "^1.5.0", - "@azure/core-rest-pipeline": "^1.8.0", - "@azure/core-tracing": "^1.0.0", - "@azure/core-util": "^1.10.0", - "@azure/logger": "^1.1.4", - "tslib": "^2.2.0" + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=14.0.0" } }, - "node_modules/@azure/keyvault-secrets": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@azure/keyvault-secrets/-/keyvault-secrets-4.9.0.tgz", - "integrity": "sha512-XkLsuzxFdhVIOVcELhso8OhQgY2lSWZcZn6fqjE848FwP9lJemJhu7nxYy8Q1nns6XqPB0WsTPmEkR8eu852vA==", - "license": "MIT", + "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", "dependencies": { - "@azure/abort-controller": "^2.0.0", - "@azure/core-auth": "^1.3.0", - "@azure/core-client": "^1.5.0", - "@azure/core-http-compat": "^2.0.1", - "@azure/core-lro": "^2.2.0", - "@azure/core-paging": "^1.1.1", - "@azure/core-rest-pipeline": "^1.8.0", - "@azure/core-tracing": "^1.0.0", - "@azure/core-util": "^1.0.0", - "@azure/keyvault-common": "^2.0.0", - "@azure/logger": "^1.0.0", - "tslib": "^2.2.0" + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=14.0.0" } }, - "node_modules/@azure/logger": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.1.4.tgz", - "integrity": "sha512-4IXXzcCdLdlXuCG+8UKEwLA1T1NHqUfanhXYHiQTn+6sfWCZXduqbtXDGceg3Ce5QxTGo7EqmbV6Bi+aqKuClQ==", - "license": "MIT", + "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=14.0.0" } }, - "node_modules/@azure/msal-browser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-4.2.0.tgz", - "integrity": "sha512-MXQjgAgjg/2VRKV+UPWHESoZPcue2ZvWKfpBLCyTUyixP+mhCl0q5D1+xDiwBGV3lru2poKZVZDQAOE40wKmWg==", - "license": "MIT", + "node_modules/@aws-sdk/client-s3": { + "version": "3.1006.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.1006.0.tgz", + "integrity": "sha512-tm8R/LgWDC3zWPMCdD990owvBrmuIM2A39+OWKW/HyAomWi6ancPz/H1K/hmxf0bqdXAaRUHBQMAmzwb1aR33Q==", + "license": "Apache-2.0", "dependencies": { - "@azure/msal-common": "15.1.1" + "@aws-crypto/sha1-browser": "5.2.0", + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.973.19", + "@aws-sdk/credential-provider-node": "^3.972.19", + "@aws-sdk/middleware-bucket-endpoint": "^3.972.7", + "@aws-sdk/middleware-expect-continue": "^3.972.7", + "@aws-sdk/middleware-flexible-checksums": "^3.973.5", + "@aws-sdk/middleware-host-header": "^3.972.7", + "@aws-sdk/middleware-location-constraint": "^3.972.7", + "@aws-sdk/middleware-logger": "^3.972.7", + "@aws-sdk/middleware-recursion-detection": "^3.972.7", + "@aws-sdk/middleware-sdk-s3": "^3.972.19", + "@aws-sdk/middleware-ssec": "^3.972.7", + "@aws-sdk/middleware-user-agent": "^3.972.20", + "@aws-sdk/region-config-resolver": "^3.972.7", + "@aws-sdk/signature-v4-multi-region": "^3.996.7", + "@aws-sdk/types": "^3.973.5", + "@aws-sdk/util-endpoints": "^3.996.4", + "@aws-sdk/util-user-agent-browser": "^3.972.7", + "@aws-sdk/util-user-agent-node": "^3.973.5", + "@smithy/config-resolver": "^4.4.10", + "@smithy/core": "^3.23.9", + "@smithy/eventstream-serde-browser": "^4.2.11", + "@smithy/eventstream-serde-config-resolver": "^4.3.11", + "@smithy/eventstream-serde-node": "^4.2.11", + "@smithy/fetch-http-handler": "^5.3.13", + "@smithy/hash-blob-browser": "^4.2.12", + "@smithy/hash-node": "^4.2.11", + "@smithy/hash-stream-node": "^4.2.11", + "@smithy/invalid-dependency": "^4.2.11", + "@smithy/md5-js": "^4.2.11", + "@smithy/middleware-content-length": "^4.2.11", + "@smithy/middleware-endpoint": "^4.4.23", + "@smithy/middleware-retry": "^4.4.40", + "@smithy/middleware-serde": "^4.2.12", + "@smithy/middleware-stack": "^4.2.11", + "@smithy/node-config-provider": "^4.3.11", + "@smithy/node-http-handler": "^4.4.14", + "@smithy/protocol-http": "^5.3.11", + "@smithy/smithy-client": "^4.12.3", + "@smithy/types": "^4.13.0", + "@smithy/url-parser": "^4.2.11", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-body-length-browser": "^4.2.2", + "@smithy/util-body-length-node": "^4.2.3", + "@smithy/util-defaults-mode-browser": "^4.3.39", + "@smithy/util-defaults-mode-node": "^4.2.42", + "@smithy/util-endpoints": "^3.3.2", + "@smithy/util-middleware": "^4.2.11", + "@smithy/util-retry": "^4.2.11", + "@smithy/util-stream": "^4.5.17", + "@smithy/util-utf8": "^4.2.2", + "@smithy/util-waiter": "^4.2.12", + "tslib": "^2.6.2" }, "engines": { - "node": ">=0.8.0" + "node": ">=20.0.0" } }, - "node_modules/@azure/msal-common": { - "version": "15.1.1", - "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-15.1.1.tgz", - "integrity": "sha512-bvLWYq9fleAcTJ6H+hfkG91On6vI/UhGyOB7Z6r0Bsa+KTL3zPtigmGCOJgdxrEklOYD88X9SehexLDH/5NRKQ==", - "license": "MIT", + "node_modules/@aws-sdk/core": { + "version": "3.973.19", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.973.19.tgz", + "integrity": "sha512-56KePyOcZnKTWCd89oJS1G6j3HZ9Kc+bh/8+EbvtaCCXdP6T7O7NzCiPuHRhFLWnzXIaXX3CxAz0nI5My9spHQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.5", + "@aws-sdk/xml-builder": "^3.972.10", + "@smithy/core": "^3.23.9", + "@smithy/node-config-provider": "^4.3.11", + "@smithy/property-provider": "^4.2.11", + "@smithy/protocol-http": "^5.3.11", + "@smithy/signature-v4": "^5.3.11", + "@smithy/smithy-client": "^4.12.3", + "@smithy/types": "^4.13.0", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-middleware": "^4.2.11", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=0.8.0" + "node": ">=20.0.0" } }, - "node_modules/@azure/msal-node": { - "version": "2.16.2", - "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-2.16.2.tgz", - "integrity": "sha512-An7l1hEr0w1HMMh1LU+rtDtqL7/jw74ORlc9Wnh06v7TU/xpG39/Zdr1ZJu3QpjUfKJ+E0/OXMW8DRSWTlh7qQ==", - "license": "MIT", + "node_modules/@aws-sdk/crc64-nvme": { + "version": "3.972.4", + "resolved": "https://registry.npmjs.org/@aws-sdk/crc64-nvme/-/crc64-nvme-3.972.4.tgz", + "integrity": "sha512-HKZIZLbRyvzo/bXZU7Zmk6XqU+1C9DjI56xd02vwuDIxedxBEqP17t9ExhbP9QFeNq/a3l9GOcyirFXxmbDhmw==", + "license": "Apache-2.0", "dependencies": { - "@azure/msal-common": "14.16.0", - "jsonwebtoken": "^9.0.0", - "uuid": "^8.3.0" + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=16" + "node": ">=20.0.0" } }, - "node_modules/@azure/msal-node/node_modules/@azure/msal-common": { - "version": "14.16.0", - "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-14.16.0.tgz", - "integrity": "sha512-1KOZj9IpcDSwpNiQNjt0jDYZpQvNZay7QAEi/5DLubay40iGYtLzya/jbjRPLyOTZhEKyL1MzPuw2HqBCjceYA==", - "license": "MIT", + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.972.17", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.17.tgz", + "integrity": "sha512-MBAMW6YELzE1SdkOniqr51mrjapQUv8JXSGxtwRjQV0mwVDutVsn22OPAUt4RcLRvdiHQmNBDEFP9iTeSVCOlA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.19", + "@aws-sdk/types": "^3.973.5", + "@smithy/property-provider": "^4.2.11", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=0.8.0" + "node": ">=20.0.0" } }, - "node_modules/@azure/msal-node/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.972.19", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.19.tgz", + "integrity": "sha512-9EJROO8LXll5a7eUFqu48k6BChrtokbmgeMWmsH7lBb6lVbtjslUYz/ShLi+SHkYzTomiGBhmzTW7y+H4BxsnA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.19", + "@aws-sdk/types": "^3.973.5", + "@smithy/fetch-http-handler": "^5.3.13", + "@smithy/node-http-handler": "^4.4.14", + "@smithy/property-provider": "^4.2.11", + "@smithy/protocol-http": "^5.3.11", + "@smithy/smithy-client": "^4.12.3", + "@smithy/types": "^4.13.0", + "@smithy/util-stream": "^4.5.17", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" } }, - "node_modules/@casbin/expression-eval": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/@casbin/expression-eval/-/expression-eval-5.3.0.tgz", - "integrity": "sha512-mMTHMYXcnBBv/zMvxMpcdVyt2bfw8Y0GnmRLbkFQ1CVJZb4XZp7xWjRh7ymOLuJdsu58rci9gmOOv/99DtJvPA==", - "license": "MIT", + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.972.18", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.18.tgz", + "integrity": "sha512-vthIAXJISZnj2576HeyLBj4WTeX+I7PwWeRkbOa0mVX39K13SCGxCgOFuKj2ytm9qTlLOmXe4cdEnroteFtJfw==", + "license": "Apache-2.0", "dependencies": { - "jsep": "^0.3.0" + "@aws-sdk/core": "^3.973.19", + "@aws-sdk/credential-provider-env": "^3.972.17", + "@aws-sdk/credential-provider-http": "^3.972.19", + "@aws-sdk/credential-provider-login": "^3.972.18", + "@aws-sdk/credential-provider-process": "^3.972.17", + "@aws-sdk/credential-provider-sso": "^3.972.18", + "@aws-sdk/credential-provider-web-identity": "^3.972.18", + "@aws-sdk/nested-clients": "^3.996.8", + "@aws-sdk/types": "^3.973.5", + "@smithy/credential-provider-imds": "^4.2.11", + "@smithy/property-provider": "^4.2.11", + "@smithy/shared-ini-file-loader": "^4.4.6", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" } }, - "node_modules/@codebucket/azure-env": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@codebucket/azure-env/-/azure-env-1.0.2.tgz", - "integrity": "sha512-Wx4nZK1UiFEoezBMcq9dP7gTWmCvDgt9TZyvSBIvbI61rVNDpgWn9ITg/U/p+Q9w6fW4THs2/jH0oHNxg5B3sw==", + "node_modules/@aws-sdk/credential-provider-login": { + "version": "3.972.18", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.18.tgz", + "integrity": "sha512-kINzc5BBxdYBkPZ0/i1AMPMOk5b5QaFNbYMElVw5QTX13AKj6jcxnv/YNl9oW9mg+Y08ti19hh01HhyEAxsSJQ==", + "license": "Apache-2.0", "dependencies": { - "@azure/identity": "^4.0.1", - "@azure/keyvault-secrets": "^4.8.0" + "@aws-sdk/core": "^3.973.19", + "@aws-sdk/nested-clients": "^3.996.8", + "@aws-sdk/types": "^3.973.5", + "@smithy/property-provider": "^4.2.11", + "@smithy/protocol-http": "^5.3.11", + "@smithy/shared-ini-file-loader": "^4.4.6", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" } }, - "node_modules/@codebucket/logserver-transport": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@codebucket/logserver-transport/-/logserver-transport-1.0.2.tgz", - "integrity": "sha512-M1JykHON1lNkfTWKn0c46AxIl2DB+mJKWgceGQItOL1VZmdB77pZJMqNQWHt84FDDEQf+hwi0TeC3gQe+QcAeg==", + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.972.19", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.19.tgz", + "integrity": "sha512-yDWQ9dFTr+IMxwanFe7+tbN5++q8psZBjlUwOiCXn1EzANoBgtqBwcpYcHaMGtn0Wlfj4NuXdf2JaEx1lz5RaQ==", + "license": "Apache-2.0", "dependencies": { - "axios": "^1.6.8", - "pino-abstract-transport": "^1.1.0", - "winston": "^3.13.0", - "winston-transport": "^4.7.0" - } - }, - "node_modules/@colors/colors": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", - "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", - "license": "MIT", + "@aws-sdk/credential-provider-env": "^3.972.17", + "@aws-sdk/credential-provider-http": "^3.972.19", + "@aws-sdk/credential-provider-ini": "^3.972.18", + "@aws-sdk/credential-provider-process": "^3.972.17", + "@aws-sdk/credential-provider-sso": "^3.972.18", + "@aws-sdk/credential-provider-web-identity": "^3.972.18", + "@aws-sdk/types": "^3.973.5", + "@smithy/credential-provider-imds": "^4.2.11", + "@smithy/property-provider": "^4.2.11", + "@smithy/shared-ini-file-loader": "^4.4.6", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=0.1.90" + "node": ">=20.0.0" } }, - "node_modules/@dabh/diagnostics": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", - "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", - "license": "MIT", + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.972.17", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.17.tgz", + "integrity": "sha512-c8G8wT1axpJDgaP3xzcy+q8Y1fTi9A2eIQJvyhQ9xuXrUZhlCfXbC0vM9bM1CUXiZppFQ1p7g0tuUMvil/gCPg==", + "license": "Apache-2.0", "dependencies": { - "colorspace": "1.1.x", - "enabled": "2.0.x", - "kuler": "^2.0.0" + "@aws-sdk/core": "^3.973.19", + "@aws-sdk/types": "^3.973.5", + "@smithy/property-provider": "^4.2.11", + "@smithy/shared-ini-file-loader": "^4.4.6", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" } }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", - "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.972.18", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.18.tgz", + "integrity": "sha512-YHYEfj5S2aqInRt5ub8nDOX8vAxgMvd84wm2Y3WVNfFa/53vOv9T7WOAqXI25qjj3uEcV46xxfqdDQk04h5XQA==", + "license": "Apache-2.0", "dependencies": { - "eslint-visitor-keys": "^3.4.3" + "@aws-sdk/core": "^3.973.19", + "@aws-sdk/nested-clients": "^3.996.8", + "@aws-sdk/token-providers": "3.1005.0", + "@aws-sdk/types": "^3.973.5", + "@smithy/property-provider": "^4.2.11", + "@smithy/shared-ini-file-loader": "^4.4.6", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + "node": ">=20.0.0" } }, - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.972.18", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.18.tgz", + "integrity": "sha512-OqlEQpJ+J3T5B96qtC1zLLwkBloechP+fezKbCH0sbd2cCc0Ra55XpxWpk/hRj69xAOYtHvoC4orx6eTa4zU7g==", "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "dependencies": { + "@aws-sdk/core": "^3.973.19", + "@aws-sdk/nested-clients": "^3.996.8", + "@aws-sdk/types": "^3.973.5", + "@smithy/property-provider": "^4.2.11", + "@smithy/shared-ini-file-loader": "^4.4.6", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", - "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", - "dev": true, - "license": "MIT", "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + "node": ">=20.0.0" } }, - "node_modules/@eslint/config-array": { - "version": "0.21.1", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", - "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", - "dev": true, + "node_modules/@aws-sdk/lib-storage": { + "version": "3.1006.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/lib-storage/-/lib-storage-3.1006.0.tgz", + "integrity": "sha512-oc7GkB4hN6DBJgbTr4l8Hvv4MXJYUU1lhnUEQnXNiOpr5VhLBtZLxZVb4HotPKGVNvCPDKoJ8gxpy15EjUAS8A==", "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^2.1.7", - "debug": "^4.3.1", - "minimatch": "^3.1.2" + "@smithy/abort-controller": "^4.2.11", + "@smithy/middleware-endpoint": "^4.4.23", + "@smithy/smithy-client": "^4.12.3", + "buffer": "5.6.0", + "events": "3.3.0", + "stream-browserify": "3.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=20.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-s3": "^3.1006.0" } }, - "node_modules/@eslint/config-array/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==", - "dev": true, + "node_modules/@aws-sdk/lib-storage/node_modules/buffer": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz", + "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==", "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "base64-js": "^1.0.2", + "ieee754": "^1.1.4" } }, - "node_modules/@eslint/config-array/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/middleware-bucket-endpoint": { + "version": "3.972.7", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.972.7.tgz", + "integrity": "sha512-goX+axlJ6PQlRnzE2bQisZ8wVrlm6dXJfBzMJhd8LhAIBan/w1Kl73fJnalM/S+18VnpzIHumyV6DtgmvqG5IA==", + "license": "Apache-2.0", "dependencies": { - "ms": "^2.1.3" + "@aws-sdk/types": "^3.973.5", + "@aws-sdk/util-arn-parser": "^3.972.3", + "@smithy/node-config-provider": "^4.3.11", + "@smithy/protocol-http": "^5.3.11", + "@smithy/types": "^4.13.0", + "@smithy/util-config-provider": "^4.2.2", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">=20.0.0" } }, - "node_modules/@eslint/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", + "node_modules/@aws-sdk/middleware-expect-continue": { + "version": "3.972.7", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.972.7.tgz", + "integrity": "sha512-mvWqvm61bmZUKmmrtl2uWbokqpenY3Mc3Jf4nXB/Hse6gWxLPaCQThmhPBDzsPSV8/Odn8V6ovWt3pZ7vy4BFQ==", + "license": "Apache-2.0", "dependencies": { - "brace-expansion": "^1.1.7" + "@aws-sdk/types": "^3.973.5", + "@smithy/protocol-http": "^5.3.11", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" }, "engines": { - "node": "*" + "node": ">=20.0.0" } }, - "node_modules/@eslint/config-array/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@eslint/config-helpers": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", - "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", - "dev": true, + "node_modules/@aws-sdk/middleware-flexible-checksums": { + "version": "3.973.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.973.5.tgz", + "integrity": "sha512-Dp3hqE5W6hG8HQ3Uh+AINx9wjjqYmFHbxede54sGj3akx/haIQrkp85lNdTdC+ouNUcSYNiuGkzmyDREfHX1Gg==", "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.17.0" + "@aws-crypto/crc32": "5.2.0", + "@aws-crypto/crc32c": "5.2.0", + "@aws-crypto/util": "5.2.0", + "@aws-sdk/core": "^3.973.19", + "@aws-sdk/crc64-nvme": "^3.972.4", + "@aws-sdk/types": "^3.973.5", + "@smithy/is-array-buffer": "^4.2.2", + "@smithy/node-config-provider": "^4.3.11", + "@smithy/protocol-http": "^5.3.11", + "@smithy/types": "^4.13.0", + "@smithy/util-middleware": "^4.2.11", + "@smithy/util-stream": "^4.5.17", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=20.0.0" } }, - "node_modules/@eslint/core": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", - "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", - "dev": true, + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.972.7", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.972.7.tgz", + "integrity": "sha512-aHQZgztBFEpDU1BB00VWCIIm85JjGjQW1OG9+98BdmaOpguJvzmXBGbnAiYcciCd+IS4e9BEq664lhzGnWJHgQ==", "license": "Apache-2.0", "dependencies": { - "@types/json-schema": "^7.0.15" + "@aws-sdk/types": "^3.973.5", + "@smithy/protocol-http": "^5.3.11", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=20.0.0" } }, - "node_modules/@eslint/eslintrc": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", - "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/middleware-location-constraint": { + "version": "3.972.7", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.972.7.tgz", + "integrity": "sha512-vdK1LJfffBp87Lj0Bw3WdK1rJk9OLDYdQpqoKgmpIZPe+4+HawZ6THTbvjhJt4C4MNnRrHTKHQjkwBiIpDBoig==", + "license": "Apache-2.0", "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" + "@aws-sdk/types": "^3.973.5", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">=20.0.0" } }, - "node_modules/@eslint/eslintrc/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==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.972.7", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.972.7.tgz", + "integrity": "sha512-LXhiWlWb26txCU1vcI9PneESSeRp/RYY/McuM4SpdrimQR5NgwaPb4VJCadVeuGWgh6QmqZ6rAKSoL1ob16W6w==", + "license": "Apache-2.0", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "@aws-sdk/types": "^3.973.5", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" } }, - "node_modules/@eslint/eslintrc/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.972.7", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.972.7.tgz", + "integrity": "sha512-l2VQdcBcYLzIzykCHtXlbpiVCZ94/xniLIkAj0jpnpjY4xlgZx7f56Ypn+uV1y3gG0tNVytJqo3K9bfMFee7SQ==", + "license": "Apache-2.0", "dependencies": { - "ms": "^2.1.3" + "@aws-sdk/types": "^3.973.5", + "@aws/lambda-invoke-store": "^0.2.2", + "@smithy/protocol-http": "^5.3.11", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">=20.0.0" } }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", + "node_modules/@aws-sdk/middleware-sdk-s3": { + "version": "3.972.19", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.972.19.tgz", + "integrity": "sha512-/CtOHHVFg4ZuN6CnLnYkrqWgVEnbOBC4kNiKa+4fldJ9cioDt3dD/f5vpq0cWLOXwmGL2zgVrVxNhjxWpxNMkg==", + "license": "Apache-2.0", "dependencies": { - "brace-expansion": "^1.1.7" + "@aws-sdk/core": "^3.973.19", + "@aws-sdk/types": "^3.973.5", + "@aws-sdk/util-arn-parser": "^3.972.3", + "@smithy/core": "^3.23.9", + "@smithy/node-config-provider": "^4.3.11", + "@smithy/protocol-http": "^5.3.11", + "@smithy/signature-v4": "^5.3.11", + "@smithy/smithy-client": "^4.12.3", + "@smithy/types": "^4.13.0", + "@smithy/util-config-provider": "^4.2.2", + "@smithy/util-middleware": "^4.2.11", + "@smithy/util-stream": "^4.5.17", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" }, "engines": { - "node": "*" + "node": ">=20.0.0" } }, - "node_modules/@eslint/eslintrc/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@eslint/js": { - "version": "9.39.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.1.tgz", - "integrity": "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node_modules/@aws-sdk/middleware-ssec": { + "version": "3.972.7", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.972.7.tgz", + "integrity": "sha512-G9clGVuAml7d8DYzY6DnRi7TIIDRvZ3YpqJPz/8wnWS5fYx/FNWNmkO6iJVlVkQg9BfeMzd+bVPtPJOvC4B+nQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.5", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" }, - "funding": { - "url": "https://eslint.org/donate" + "engines": { + "node": ">=20.0.0" } }, - "node_modules/@eslint/object-schema": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", - "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", - "dev": true, + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.972.20", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.972.20.tgz", + "integrity": "sha512-3kNTLtpUdeahxtnJRnj/oIdLAUdzTfr9N40KtxNhtdrq+Q1RPMdCJINRXq37m4t5+r3H70wgC3opW46OzFcZYA==", "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.19", + "@aws-sdk/types": "^3.973.5", + "@aws-sdk/util-endpoints": "^3.996.4", + "@smithy/core": "^3.23.9", + "@smithy/protocol-http": "^5.3.11", + "@smithy/types": "^4.13.0", + "@smithy/util-retry": "^4.2.11", + "tslib": "^2.6.2" + }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=20.0.0" } }, - "node_modules/@eslint/plugin-kit": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", - "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", - "dev": true, + "node_modules/@aws-sdk/nested-clients": { + "version": "3.996.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.996.8.tgz", + "integrity": "sha512-6HlLm8ciMW8VzfB80kfIx16PBA9lOa9Dl+dmCBi78JDhvGlx3I7Rorwi5PpVRkL31RprXnYna3yBf6UKkD/PqA==", "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.17.0", - "levn": "^0.4.1" + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.973.19", + "@aws-sdk/middleware-host-header": "^3.972.7", + "@aws-sdk/middleware-logger": "^3.972.7", + "@aws-sdk/middleware-recursion-detection": "^3.972.7", + "@aws-sdk/middleware-user-agent": "^3.972.20", + "@aws-sdk/region-config-resolver": "^3.972.7", + "@aws-sdk/types": "^3.973.5", + "@aws-sdk/util-endpoints": "^3.996.4", + "@aws-sdk/util-user-agent-browser": "^3.972.7", + "@aws-sdk/util-user-agent-node": "^3.973.5", + "@smithy/config-resolver": "^4.4.10", + "@smithy/core": "^3.23.9", + "@smithy/fetch-http-handler": "^5.3.13", + "@smithy/hash-node": "^4.2.11", + "@smithy/invalid-dependency": "^4.2.11", + "@smithy/middleware-content-length": "^4.2.11", + "@smithy/middleware-endpoint": "^4.4.23", + "@smithy/middleware-retry": "^4.4.40", + "@smithy/middleware-serde": "^4.2.12", + "@smithy/middleware-stack": "^4.2.11", + "@smithy/node-config-provider": "^4.3.11", + "@smithy/node-http-handler": "^4.4.14", + "@smithy/protocol-http": "^5.3.11", + "@smithy/smithy-client": "^4.12.3", + "@smithy/types": "^4.13.0", + "@smithy/url-parser": "^4.2.11", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-body-length-browser": "^4.2.2", + "@smithy/util-body-length-node": "^4.2.3", + "@smithy/util-defaults-mode-browser": "^4.3.39", + "@smithy/util-defaults-mode-node": "^4.2.42", + "@smithy/util-endpoints": "^3.3.2", + "@smithy/util-middleware": "^4.2.11", + "@smithy/util-retry": "^4.2.11", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=20.0.0" } }, - "node_modules/@hapi/hoek": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", - "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==", - "license": "BSD-3-Clause" - }, - "node_modules/@hapi/topo": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", - "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", - "license": "BSD-3-Clause", + "node_modules/@aws-sdk/region-config-resolver": { + "version": "3.972.7", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.972.7.tgz", + "integrity": "sha512-/Ev/6AI8bvt4HAAptzSjThGUMjcWaX3GX8oERkB0F0F9x2dLSBdgFDiyrRz3i0u0ZFZFQ1b28is4QhyqXTUsVA==", + "license": "Apache-2.0", "dependencies": { - "@hapi/hoek": "^9.0.0" + "@aws-sdk/types": "^3.973.5", + "@smithy/config-resolver": "^4.4.10", + "@smithy/node-config-provider": "^4.3.11", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" } }, - "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", - "dev": true, + "node_modules/@aws-sdk/signature-v4-multi-region": { + "version": "3.996.7", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.7.tgz", + "integrity": "sha512-mYhh7FY+7OOqjkYkd6+6GgJOsXK1xBWmuR+c5mxJPj2kr5TBNeZq+nUvE9kANWAux5UxDVrNOSiEM/wlHzC3Lg==", "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-sdk-s3": "^3.972.19", + "@aws-sdk/types": "^3.973.5", + "@smithy/protocol-http": "^5.3.11", + "@smithy/signature-v4": "^5.3.11", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=18.18.0" + "node": ">=20.0.0" } }, - "node_modules/@humanfs/node": { - "version": "0.16.7", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", - "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", - "dev": true, + "node_modules/@aws-sdk/token-providers": { + "version": "3.1005.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1005.0.tgz", + "integrity": "sha512-vMxd+ivKqSxU9bHx5vmAlFKDAkjGotFU56IOkDa5DaTu1WWwbcse0yFHEm9I537oVvodaiwMl3VBwgHfzQ2rvw==", "license": "Apache-2.0", "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.4.0" + "@aws-sdk/core": "^3.973.19", + "@aws-sdk/nested-clients": "^3.996.8", + "@aws-sdk/types": "^3.973.5", + "@smithy/property-provider": "^4.2.11", + "@smithy/shared-ini-file-loader": "^4.4.6", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=18.18.0" + "node": ">=20.0.0" } }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, + "node_modules/@aws-sdk/types": { + "version": "3.973.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.973.5.tgz", + "integrity": "sha512-hl7BGwDCWsjH8NkZfx+HgS7H2LyM2lTMAI7ba9c8O0KqdBLTdNJivsHpqjg9rNlAlPyREb6DeDRXUl0s8uFdmQ==", "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=12.22" + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/util-arn-parser": { + "version": "3.972.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.972.3.tgz", + "integrity": "sha512-HzSD8PMFrvgi2Kserxuff5VitNq2sgf3w9qxmskKDiDTThWfVteJxuCS9JXiPIPtmCrp+7N9asfIaVhBFORllA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "engines": { + "node": ">=20.0.0" } }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", - "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", - "dev": true, + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.996.4", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.996.4.tgz", + "integrity": "sha512-Hek90FBmd4joCFj+Vc98KLJh73Zqj3s2W56gjAcTkrNLMDI5nIFkG9YpfcJiVI1YlE2Ne1uOQNe+IgQ/Vz2XRA==", "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.5", + "@smithy/types": "^4.13.0", + "@smithy/url-parser": "^4.2.11", + "@smithy/util-endpoints": "^3.3.2", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=18.18" + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.965.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.965.5.tgz", + "integrity": "sha512-WhlJNNINQB+9qtLtZJcpQdgZw3SCDCpXdUJP7cToGwHbCWCnRckGlc6Bx/OhWwIYFNAn+FIydY8SZ0QmVu3xTQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "engines": { + "node": ">=20.0.0" } }, - "node_modules/@inquirer/external-editor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz", - "integrity": "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.972.7", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.972.7.tgz", + "integrity": "sha512-7SJVuvhKhMF/BkNS1n0QAJYgvEwYbK2QLKBrzDiwQGiTRU6Yf1f3nehTzm/l21xdAOtWSfp2uWSddPnP2ZtsVw==", + "license": "Apache-2.0", "dependencies": { - "chardet": "^2.1.1", - "iconv-lite": "^0.7.0" + "@aws-sdk/types": "^3.973.5", + "@smithy/types": "^4.13.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.973.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.973.5.tgz", + "integrity": "sha512-Dyy38O4GeMk7UQ48RupfHif//gqnOPbq/zlvRssc11E2mClT+aUfc3VS2yD8oLtzqO3RsqQ9I3gOBB4/+HjPOw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "^3.972.20", + "@aws-sdk/types": "^3.973.5", + "@smithy/node-config-provider": "^4.3.11", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=18" + "node": ">=20.0.0" }, "peerDependencies": { - "@types/node": ">=18" + "aws-crt": ">=1.0.0" }, "peerDependenciesMeta": { - "@types/node": { + "aws-crt": { "optional": true } } }, - "node_modules/@inquirer/external-editor/node_modules/iconv-lite": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", - "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/xml-builder": { + "version": "3.972.10", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.10.tgz", + "integrity": "sha512-OnejAIVD+CxzyAUrVic7lG+3QRltyja9LoNqCE/1YVs8ichoTbJlVSaZ9iSMcnHLyzrSNtvaOGjSDRP+d/ouFA==", + "license": "Apache-2.0", "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" + "@smithy/types": "^4.13.0", + "fast-xml-parser": "5.4.1", + "tslib": "^2.6.2" }, "engines": { - "node": ">=0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" + "node": ">=20.0.0" } }, - "node_modules/@noble/hashes": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", - "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", - "license": "MIT", + "node_modules/@aws/lambda-invoke-store": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.3.tgz", + "integrity": "sha512-oLvsaPMTBejkkmHhjf09xTgk71mOqyr/409NKhRIL08If7AhVfUsJhVsx386uJaqNd42v9kWamQ9lFbkoC2dYw==", + "license": "Apache-2.0", "engines": { - "node": "^14.21.3 || >=16" + "node": ">=18.0.0" + } + }, + "node_modules/@azure/abort-controller": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", + "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" }, - "funding": { - "url": "https://paulmillr.com/funding/" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, + "node_modules/@azure/core-auth": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.9.0.tgz", + "integrity": "sha512-FPwHpZywuyasDSLMqJ6fhbOK3TqUdviZNF8OqRGA4W5Ewib2lEEZ+pBsYcBa88B2NGO/SEnYPGhyBqNlE8ilSw==", "license": "MIT", "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" + "@azure/abort-controller": "^2.0.0", + "@azure/core-util": "^1.11.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 8" + "node": ">=18.0.0" } }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, + "node_modules/@azure/core-client": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.9.2.tgz", + "integrity": "sha512-kRdry/rav3fUKHl/aDLd/pDLcB+4pOFwPPTVEExuMyaI5r+JBbMWqRbCY1pn5BniDaU3lRxO9eaQ1AmSMehl/w==", "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.4.0", + "@azure/core-rest-pipeline": "^1.9.1", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.6.1", + "@azure/logger": "^1.0.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 8" + "node": ">=18.0.0" } }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, + "node_modules/@azure/core-http-compat": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@azure/core-http-compat/-/core-http-compat-2.2.0.tgz", + "integrity": "sha512-1kW8ZhN0CfbNOG6C688z5uh2yrzALE7dDXHiR9dY4vt+EbhGZQSbjDa5bQd2rf3X2pdWMsXbqbArxUyeNdvtmg==", "license": "MIT", "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" + "@azure/abort-controller": "^2.0.0", + "@azure/core-client": "^1.3.0", + "@azure/core-rest-pipeline": "^1.19.0" }, "engines": { - "node": ">= 8" + "node": ">=18.0.0" } }, - "node_modules/@paralleldrive/cuid2": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.3.1.tgz", - "integrity": "sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==", + "node_modules/@azure/core-lro": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.7.2.tgz", + "integrity": "sha512-0YIpccoX8m/k00O7mDDMdJpbr6mf1yWo2dfmxt5A8XVZVVMz2SSKaEbMCeJRvgQ0IaSlqhjT47p4hVIRRy90xw==", "license": "MIT", "dependencies": { - "@noble/hashes": "^1.1.5" + "@azure/abort-controller": "^2.0.0", + "@azure/core-util": "^1.2.0", + "@azure/logger": "^1.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@pkgr/core": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", - "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", - "dev": true, + "node_modules/@azure/core-paging": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.6.2.tgz", + "integrity": "sha512-YKWi9YuCU04B55h25cnOYZHxXYtEvQEbKST5vqRga7hWY9ydd3FZHdeQF8pyh+acWZvppw13M/LMGx0LABUVMA==", "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-rest-pipeline": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.19.0.tgz", + "integrity": "sha512-bM3308LRyg5g7r3Twprtqww0R/r7+GyVxj4BafcmVPo4WQoGt5JXuaqxHEFjw2o3rvFZcUPiqJMg6WuvEEeVUA==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.8.0", + "@azure/core-tracing": "^1.0.1", + "@azure/core-util": "^1.11.0", + "@azure/logger": "^1.0.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "tslib": "^2.6.2" }, - "funding": { - "url": "https://opencollective.com/pkgr" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@redis/bloom": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-5.10.0.tgz", - "integrity": "sha512-doIF37ob+l47n0rkpRNgU8n4iacBlKM9xLiP1LtTZTvz8TloJB8qx/MgvhMhKdYG+CvCY2aPBnN2706izFn/4A==", + "node_modules/@azure/core-tracing": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.2.0.tgz", + "integrity": "sha512-UKTiEJPkWcESPYJz3X5uKRYyOcJD+4nYph+KpfdPRnQJVrZfk0KJgdnaAWKfhsBBtAf/D58Az4AvCJEmWgIBAg==", "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 18" + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-util": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.11.0.tgz", + "integrity": "sha512-DxOSLua+NdpWoSqULhjDyAZTXFdP/LKkqtYuxxz1SCN289zk3OG8UOpnCQAz/tygyACBtWp/BoO72ptK7msY8g==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/identity": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.6.0.tgz", + "integrity": "sha512-ANpO1iAvcZmpD4QY7/kaE/P2n66pRXsDp3nMUC6Ow3c9KfXOZF7qMU9VgqPw8m7adP7TVIbVyrCEmD9cth3KQQ==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.9.0", + "@azure/core-client": "^1.9.2", + "@azure/core-rest-pipeline": "^1.17.0", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.11.0", + "@azure/logger": "^1.0.0", + "@azure/msal-browser": "^4.0.1", + "@azure/msal-node": "^2.15.0", + "events": "^3.0.0", + "jws": "^4.0.0", + "open": "^8.0.0", + "stoppable": "^1.1.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/keyvault-common": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@azure/keyvault-common/-/keyvault-common-2.0.0.tgz", + "integrity": "sha512-wRLVaroQtOqfg60cxkzUkGKrKMsCP6uYXAOomOIysSMyt1/YM0eUn9LqieAWM8DLcU4+07Fio2YGpPeqUbpP9w==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.3.0", + "@azure/core-client": "^1.5.0", + "@azure/core-rest-pipeline": "^1.8.0", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.10.0", + "@azure/logger": "^1.1.4", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/keyvault-secrets": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@azure/keyvault-secrets/-/keyvault-secrets-4.9.0.tgz", + "integrity": "sha512-XkLsuzxFdhVIOVcELhso8OhQgY2lSWZcZn6fqjE848FwP9lJemJhu7nxYy8Q1nns6XqPB0WsTPmEkR8eu852vA==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.3.0", + "@azure/core-client": "^1.5.0", + "@azure/core-http-compat": "^2.0.1", + "@azure/core-lro": "^2.2.0", + "@azure/core-paging": "^1.1.1", + "@azure/core-rest-pipeline": "^1.8.0", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.0.0", + "@azure/keyvault-common": "^2.0.0", + "@azure/logger": "^1.0.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/logger": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.1.4.tgz", + "integrity": "sha512-4IXXzcCdLdlXuCG+8UKEwLA1T1NHqUfanhXYHiQTn+6sfWCZXduqbtXDGceg3Ce5QxTGo7EqmbV6Bi+aqKuClQ==", + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/msal-browser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-4.2.0.tgz", + "integrity": "sha512-MXQjgAgjg/2VRKV+UPWHESoZPcue2ZvWKfpBLCyTUyixP+mhCl0q5D1+xDiwBGV3lru2poKZVZDQAOE40wKmWg==", + "license": "MIT", + "dependencies": { + "@azure/msal-common": "15.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-common": { + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-15.1.1.tgz", + "integrity": "sha512-bvLWYq9fleAcTJ6H+hfkG91On6vI/UhGyOB7Z6r0Bsa+KTL3zPtigmGCOJgdxrEklOYD88X9SehexLDH/5NRKQ==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-node": { + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-2.16.2.tgz", + "integrity": "sha512-An7l1hEr0w1HMMh1LU+rtDtqL7/jw74ORlc9Wnh06v7TU/xpG39/Zdr1ZJu3QpjUfKJ+E0/OXMW8DRSWTlh7qQ==", + "license": "MIT", + "dependencies": { + "@azure/msal-common": "14.16.0", + "jsonwebtoken": "^9.0.0", + "uuid": "^8.3.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@azure/msal-node/node_modules/@azure/msal-common": { + "version": "14.16.0", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-14.16.0.tgz", + "integrity": "sha512-1KOZj9IpcDSwpNiQNjt0jDYZpQvNZay7QAEi/5DLubay40iGYtLzya/jbjRPLyOTZhEKyL1MzPuw2HqBCjceYA==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-node/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@casbin/expression-eval": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@casbin/expression-eval/-/expression-eval-5.3.0.tgz", + "integrity": "sha512-mMTHMYXcnBBv/zMvxMpcdVyt2bfw8Y0GnmRLbkFQ1CVJZb4XZp7xWjRh7ymOLuJdsu58rci9gmOOv/99DtJvPA==", + "license": "MIT", + "dependencies": { + "jsep": "^0.3.0" + } + }, + "node_modules/@codebucket/azure-env": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@codebucket/azure-env/-/azure-env-1.0.2.tgz", + "integrity": "sha512-Wx4nZK1UiFEoezBMcq9dP7gTWmCvDgt9TZyvSBIvbI61rVNDpgWn9ITg/U/p+Q9w6fW4THs2/jH0oHNxg5B3sw==", + "dependencies": { + "@azure/identity": "^4.0.1", + "@azure/keyvault-secrets": "^4.8.0" + } + }, + "node_modules/@codebucket/files": { + "version": "1.0.33", + "resolved": "https://registry.npmjs.org/@codebucket/files/-/files-1.0.33.tgz", + "integrity": "sha512-55klxFgjMHsqTV+ZhTxeVX0/W4w7tDq2gQGGHQz1oH6093GXJpfJW6YKjupra84f4eZUAhBVvEfg8hXRqKLUtA==", + "dependencies": { + "@aws-sdk/client-s3": "^3.758.0", + "@aws-sdk/lib-storage": "^3.758.0", + "archiver": "^6.0.1", + "fs-extra": "^11.2.0", + "mime": "^4.0.7", + "multer": "^2.0.0" + }, + "peerDependencies": { + "@azure/storage-blob": "^12.28.0" + }, + "peerDependenciesMeta": { + "@azure/storage-blob": { + "optional": true + } + } + }, + "node_modules/@codebucket/files/node_modules/mime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@codebucket/logserver-transport": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@codebucket/logserver-transport/-/logserver-transport-1.0.2.tgz", + "integrity": "sha512-M1JykHON1lNkfTWKn0c46AxIl2DB+mJKWgceGQItOL1VZmdB77pZJMqNQWHt84FDDEQf+hwi0TeC3gQe+QcAeg==", + "dependencies": { + "axios": "^1.6.8", + "pino-abstract-transport": "^1.1.0", + "winston": "^3.13.0", + "winston-transport": "^4.7.0" + } + }, + "node_modules/@codebucket/mail-transport": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@codebucket/mail-transport/-/mail-transport-1.0.4.tgz", + "integrity": "sha512-mN3w9R3SXGGoMRIuQPshLevoZnIQKFlIFGtEyGicxEnCdRZWgIRvy61lQjQV6+mu+1LUobirkq9DECAfgq0cEQ==", + "license": "MIT", + "dependencies": { + "@types/nodemailer": "^6.4.17", + "axios": "^1.6.8", + "form-data": "^4.0.2", + "nodemailer": "^6.9.4" + } + }, + "node_modules/@codebucket/mail-transport/node_modules/nodemailer": { + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.10.1.tgz", + "integrity": "sha512-Z+iLaBGVaSjbIzQ4pX6XV41HrooLsQ10ZWPUehGmuantvzWoDVBnmsdUcOIDM1t+yPor5pDhVlDESgOMEGxhHA==", + "license": "MIT-0", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@codebucket/sms": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@codebucket/sms/-/sms-1.0.11.tgz", + "integrity": "sha512-/nqkGn6iNMitg3tIp4hqZb+yPNaxiEDz70Vzzu8mxV14/tNDqg4Sq5UXRKHBLGV/vK3+IieKNOJ5DhQAkGrQEQ==", + "license": "MIT", + "dependencies": { + "axios": "^1.5.0" + } + }, + "node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", + "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "license": "MIT", + "dependencies": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", + "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", + "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-array/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/config-array/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@eslint/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/config-array/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@eslint/js": { + "version": "9.39.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.1.tgz", + "integrity": "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@hapi/topo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", + "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@inquirer/external-editor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz", + "integrity": "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chardet": "^2.1.1", + "iconv-lite": "^0.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/external-editor/node_modules/iconv-lite": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", + "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@paralleldrive/cuid2": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.3.1.tgz", + "integrity": "sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "^1.1.5" + } + }, + "node_modules/@pkgr/core": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", + "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/pkgr" + } + }, + "node_modules/@redis/bloom": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-5.10.0.tgz", + "integrity": "sha512-doIF37ob+l47n0rkpRNgU8n4iacBlKM9xLiP1LtTZTvz8TloJB8qx/MgvhMhKdYG+CvCY2aPBnN2706izFn/4A==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.10.0" + } + }, + "node_modules/@redis/client": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.10.0.tgz", + "integrity": "sha512-JXmM4XCoso6C75Mr3lhKA3eNxSzkYi3nCzxDIKY+YOszYsJjuKbFgVtguVPbLMOttN4iu2fXoc2BGhdnYhIOxA==", + "license": "MIT", + "dependencies": { + "cluster-key-slot": "1.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@redis/json": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/@redis/json/-/json-5.10.0.tgz", + "integrity": "sha512-B2G8XlOmTPUuZtD44EMGbtoepQG34RCDXLZbjrtON1Djet0t5Ri7/YPXvL9aomXqP8lLTreaprtyLKF4tmXEEA==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.10.0" + } + }, + "node_modules/@redis/search": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-5.10.0.tgz", + "integrity": "sha512-3SVcPswoSfp2HnmWbAGUzlbUPn7fOohVu2weUQ0S+EMiQi8jwjL+aN2p6V3TI65eNfVsJ8vyPvqWklm6H6esmg==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.10.0" + } + }, + "node_modules/@redis/time-series": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-5.10.0.tgz", + "integrity": "sha512-cPkpddXH5kc/SdRhF0YG0qtjL+noqFT0AcHbQ6axhsPsO7iqPi1cjxgdkE9TNeKiBUUdCaU1DbqkR/LzbzPBhg==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.10.0" + } + }, + "node_modules/@sideway/address": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", + "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@sideway/formula": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", + "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==", + "license": "BSD-3-Clause" + }, + "node_modules/@sideway/pinpoint": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@smithy/abort-controller": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.11.tgz", + "integrity": "sha512-Hj4WoYWMJnSpM6/kchsm4bUNTL9XiSyhvoMb2KIq4VJzyDt7JpGHUZHkVNPZVC7YE1tf8tPeVauxpFBKGW4/KQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/chunked-blob-reader": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-5.2.2.tgz", + "integrity": "sha512-St+kVicSyayWQca+I1rGitaOEH6uKgE8IUWoYnnEX26SWdWQcL6LvMSD19Lg+vYHKdT9B2Zuu7rd3i6Wnyb/iw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/chunked-blob-reader-native": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-4.2.3.tgz", + "integrity": "sha512-jA5k5Udn7Y5717L86h4EIv06wIr3xn8GM1qHRi/Nf31annXcXHJjBKvgztnbn2TxH3xWrPBfgwHsOwZf0UmQWw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-base64": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/config-resolver": { + "version": "4.4.10", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.10.tgz", + "integrity": "sha512-IRTkd6ps0ru+lTWnfnsbXzW80A8Od8p3pYiZnW98K2Hb20rqfsX7VTlfUwhrcOeSSy68Gn9WBofwPuw3e5CCsg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.11", + "@smithy/types": "^4.13.0", + "@smithy/util-config-provider": "^4.2.2", + "@smithy/util-endpoints": "^3.3.2", + "@smithy/util-middleware": "^4.2.11", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/core": { + "version": "3.23.9", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.23.9.tgz", + "integrity": "sha512-1Vcut4LEL9HZsdpI0vFiRYIsaoPwZLjAxnVQDUMQK8beMS+EYPLDQCXtbzfxmM5GzSgjfe2Q9M7WaXwIMQllyQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/middleware-serde": "^4.2.12", + "@smithy/protocol-http": "^5.3.11", + "@smithy/types": "^4.13.0", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-body-length-browser": "^4.2.2", + "@smithy/util-middleware": "^4.2.11", + "@smithy/util-stream": "^4.5.17", + "@smithy/util-utf8": "^4.2.2", + "@smithy/uuid": "^1.1.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/credential-provider-imds": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.11.tgz", + "integrity": "sha512-lBXrS6ku0kTj3xLmsJW0WwqWbGQ6ueooYyp/1L9lkyT0M02C+DWwYwc5aTyXFbRaK38ojALxNixg+LxKSHZc0g==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.11", + "@smithy/property-provider": "^4.2.11", + "@smithy/types": "^4.13.0", + "@smithy/url-parser": "^4.2.11", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-codec": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.2.11.tgz", + "integrity": "sha512-Sf39Ml0iVX+ba/bgMPxaXWAAFmHqYLTmbjAPfLPLY8CrYkRDEqZdUsKC1OwVMCdJXfAt0v4j49GIJ8DoSYAe6w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@smithy/types": "^4.13.0", + "@smithy/util-hex-encoding": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-browser": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.2.11.tgz", + "integrity": "sha512-3rEpo3G6f/nRS7fQDsZmxw/ius6rnlIpz4UX6FlALEzz8JoSxFmdBt0SZnthis+km7sQo6q5/3e+UJcuQivoXA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^4.2.11", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-config-resolver": { + "version": "4.3.11", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.3.11.tgz", + "integrity": "sha512-XeNIA8tcP/GDWnnKkO7qEm/bg0B/bP9lvIXZBXcGZwZ+VYM8h8k9wuDvUODtdQ2Wcp2RcBkPTCSMmaniVHrMlA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-node": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.2.11.tgz", + "integrity": "sha512-fzbCh18rscBDTQSCrsp1fGcclLNF//nJyhjldsEl/5wCYmgpHblv5JSppQAyQI24lClsFT0wV06N1Porn0IsEw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^4.2.11", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-universal": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.2.11.tgz", + "integrity": "sha512-MJ7HcI+jEkqoWT5vp+uoVaAjBrmxBtKhZTeynDRG/seEjJfqyg3SiqMMqyPnAMzmIfLaeJ/uiuSDP/l9AnMy/Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-codec": "^4.2.11", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/fetch-http-handler": { + "version": "5.3.13", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.13.tgz", + "integrity": "sha512-U2Hcfl2s3XaYjikN9cT4mPu8ybDbImV3baXR0PkVlC0TTx808bRP3FaPGAzPtB8OByI+JqJ1kyS+7GEgae7+qQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.3.11", + "@smithy/querystring-builder": "^4.2.11", + "@smithy/types": "^4.13.0", + "@smithy/util-base64": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-blob-browser": { + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-4.2.12.tgz", + "integrity": "sha512-1wQE33DsxkM/waftAhCH9VtJbUGyt1PJ9YRDpOu+q9FUi73LLFUZ2fD8A61g2mT1UY9k7b99+V1xZ41Rz4SHRQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/chunked-blob-reader": "^5.2.2", + "@smithy/chunked-blob-reader-native": "^4.2.3", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-node": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.11.tgz", + "integrity": "sha512-T+p1pNynRkydpdL015ruIoyPSRw9e/SQOWmSAMmmprfswMrd5Ow5igOWNVlvyVFZlxXqGmyH3NQwfwy8r5Jx0A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.0", + "@smithy/util-buffer-from": "^4.2.2", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-stream-node": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-4.2.11.tgz", + "integrity": "sha512-hQsTjwPCRY8w9GK07w1RqJi3e+myh0UaOWBBhZ1UMSDgofH/Q1fEYzU1teaX6HkpX/eWDdm7tAGR0jBPlz9QEQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.0", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/invalid-dependency": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.11.tgz", + "integrity": "sha512-cGNMrgykRmddrNhYy1yBdrp5GwIgEkniS7k9O1VLB38yxQtlvrxpZtUVvo6T4cKpeZsriukBuuxfJcdZQc/f/g==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/is-array-buffer": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.2.2.tgz", + "integrity": "sha512-n6rQ4N8Jj4YTQO3YFrlgZuwKodf4zUFs7EJIWH86pSCWBaAtAGBFfCM7Wx6D2bBJ2xqFNxGBSrUWswT3M0VJow==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/md5-js": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-4.2.11.tgz", + "integrity": "sha512-350X4kGIrty0Snx2OWv7rPM6p6vM7RzryvFs6B/56Cux3w3sChOb3bymo5oidXJlPcP9fIRxGUCk7GqpiSOtng==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.0", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-content-length": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.11.tgz", + "integrity": "sha512-UvIfKYAKhCzr4p6jFevPlKhQwyQwlJ6IeKLDhmV1PlYfcW3RL4ROjNEDtSik4NYMi9kDkH7eSwyTP3vNJ/u/Dw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.3.11", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-endpoint": { + "version": "4.4.23", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.23.tgz", + "integrity": "sha512-UEFIejZy54T1EJn2aWJ45voB7RP2T+IRzUqocIdM6GFFa5ClZncakYJfcYnoXt3UsQrZZ9ZRauGm77l9UCbBLw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.23.9", + "@smithy/middleware-serde": "^4.2.12", + "@smithy/node-config-provider": "^4.3.11", + "@smithy/shared-ini-file-loader": "^4.4.6", + "@smithy/types": "^4.13.0", + "@smithy/url-parser": "^4.2.11", + "@smithy/util-middleware": "^4.2.11", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-retry": { + "version": "4.4.40", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.40.tgz", + "integrity": "sha512-YhEMakG1Ae57FajERdHNZ4ShOPIY7DsgV+ZoAxo/5BT0KIe+f6DDU2rtIymNNFIj22NJfeeI6LWIifrwM0f+rA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.11", + "@smithy/protocol-http": "^5.3.11", + "@smithy/service-error-classification": "^4.2.11", + "@smithy/smithy-client": "^4.12.3", + "@smithy/types": "^4.13.0", + "@smithy/util-middleware": "^4.2.11", + "@smithy/util-retry": "^4.2.11", + "@smithy/uuid": "^1.1.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-serde": { + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.12.tgz", + "integrity": "sha512-W9g1bOLui7Xn5FABRVS0o3rXL0gfN37d/8I/W7i0N7oxjx9QecUmXEMSUMADTODwdtka9cN43t5BI2CodLJpng==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.3.11", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-stack": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.11.tgz", + "integrity": "sha512-s+eenEPW6RgliDk2IhjD2hWOxIx1NKrOHxEwNUaUXxYBxIyCcDfNULZ2Mu15E3kwcJWBedTET/kEASPV1A1Akg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-config-provider": { + "version": "4.3.11", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.11.tgz", + "integrity": "sha512-xD17eE7kaLgBBGf5CZQ58hh2YmwK1Z0O8YhffwB/De2jsL0U3JklmhVYJ9Uf37OtUDLF2gsW40Xwwag9U869Gg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^4.2.11", + "@smithy/shared-ini-file-loader": "^4.4.6", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-http-handler": { + "version": "4.4.14", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.4.14.tgz", + "integrity": "sha512-DamSqaU8nuk0xTJDrYnRzZndHwwRnyj/n/+RqGGCcBKB4qrQem0mSDiWdupaNWdwxzyMU91qxDmHOCazfhtO3A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^4.2.11", + "@smithy/protocol-http": "^5.3.11", + "@smithy/querystring-builder": "^4.2.11", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/property-provider": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.11.tgz", + "integrity": "sha512-14T1V64o6/ndyrnl1ze1ZhyLzIeYNN47oF/QU6P5m82AEtyOkMJTb0gO1dPubYjyyKuPD6OSVMPDKe+zioOnCg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/protocol-http": { + "version": "5.3.11", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.11.tgz", + "integrity": "sha512-hI+barOVDJBkNt4y0L2mu3Ugc0w7+BpJ2CZuLwXtSltGAAwCb3IvnalGlbDV/UCS6a9ZuT3+exd1WxNdLb5IlQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/querystring-builder": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.11.tgz", + "integrity": "sha512-7spdikrYiljpket6u0up2Ck2mxhy7dZ0+TDd+S53Dg2DHd6wg+YNJrTCHiLdgZmEXZKI7LJZcwL3721ZRDFiqA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.0", + "@smithy/util-uri-escape": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/querystring-parser": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.11.tgz", + "integrity": "sha512-nE3IRNjDltvGcoThD2abTozI1dkSy8aX+a2N1Rs55en5UsdyyIXgGEmevUL3okZFoJC77JgRGe99xYohhsjivQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/service-error-classification": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.11.tgz", + "integrity": "sha512-HkMFJZJUhzU3HvND1+Yw/kYWXp4RPDLBWLcK1n+Vqw8xn4y2YiBhdww8IxhkQjP/QlZun5bwm3vcHc8AqIU3zw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/shared-ini-file-loader": { + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.4.6.tgz", + "integrity": "sha512-IB/M5I8G0EeXZTHsAxpx51tMQ5R719F3aq+fjEB6VtNcCHDc0ajFDIGDZw+FW9GxtEkgTduiPpjveJdA/CX7sw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/signature-v4": { + "version": "5.3.11", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.11.tgz", + "integrity": "sha512-V1L6N9aKOBAN4wEHLyqjLBnAz13mtILU0SeDrjOaIZEeN6IFa6DxwRt1NNpOdmSpQUfkBj0qeD3m6P77uzMhgQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.2.2", + "@smithy/protocol-http": "^5.3.11", + "@smithy/types": "^4.13.0", + "@smithy/util-hex-encoding": "^4.2.2", + "@smithy/util-middleware": "^4.2.11", + "@smithy/util-uri-escape": "^4.2.2", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/smithy-client": { + "version": "4.12.3", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.12.3.tgz", + "integrity": "sha512-7k4UxjSpHmPN2AxVhvIazRSzFQjWnud3sOsXcFStzagww17j1cFQYqTSiQ8xuYK3vKLR1Ni8FzuT3VlKr3xCNw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.23.9", + "@smithy/middleware-endpoint": "^4.4.23", + "@smithy/middleware-stack": "^4.2.11", + "@smithy/protocol-http": "^5.3.11", + "@smithy/types": "^4.13.0", + "@smithy/util-stream": "^4.5.17", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.13.0.tgz", + "integrity": "sha512-COuLsZILbbQsdrwKQpkkpyep7lCsByxwj7m0Mg5v66/ZTyenlfBc40/QFQ5chO0YN/PNEH1Bi3fGtfXPnYNeDw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/url-parser": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.11.tgz", + "integrity": "sha512-oTAGGHo8ZYc5VZsBREzuf5lf2pAurJQsccMusVZ85wDkX66ojEc/XauiGjzCj50A61ObFTPe6d7Pyt6UBYaing==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/querystring-parser": "^4.2.11", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-base64": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.3.2.tgz", + "integrity": "sha512-XRH6b0H/5A3SgblmMa5ErXQ2XKhfbQB+Fm/oyLZ2O2kCUrwgg55bU0RekmzAhuwOjA9qdN5VU2BprOvGGUkOOQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.2.2", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-body-length-browser": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.2.2.tgz", + "integrity": "sha512-JKCrLNOup3OOgmzeaKQwi4ZCTWlYR5H4Gm1r2uTMVBXoemo1UEghk5vtMi1xSu2ymgKVGW631e2fp9/R610ZjQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-body-length-node": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.2.3.tgz", + "integrity": "sha512-ZkJGvqBzMHVHE7r/hcuCxlTY8pQr1kMtdsVPs7ex4mMU+EAbcXppfo5NmyxMYi2XU49eqaz56j2gsk4dHHPG/g==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-buffer-from": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.2.2.tgz", + "integrity": "sha512-FDXD7cvUoFWwN6vtQfEta540Y/YBe5JneK3SoZg9bThSoOAC/eGeYEua6RkBgKjGa/sz6Y+DuBZj3+YEY21y4Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-config-provider": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.2.2.tgz", + "integrity": "sha512-dWU03V3XUprJwaUIFVv4iOnS1FC9HnMHDfUrlNDSh4315v0cWyaIErP8KiqGVbf5z+JupoVpNM7ZB3jFiTejvQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-browser": { + "version": "4.3.39", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.39.tgz", + "integrity": "sha512-ui7/Ho/+VHqS7Km2wBw4/Ab4RktoiSshgcgpJzC4keFPs6tLJS4IQwbeahxQS3E/w98uq6E1mirCH/id9xIXeQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^4.2.11", + "@smithy/smithy-client": "^4.12.3", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-node": { + "version": "4.2.42", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.42.tgz", + "integrity": "sha512-QDA84CWNe8Akpj15ofLO+1N3Rfg8qa2K5uX0y6HnOp4AnRYRgWrKx/xzbYNbVF9ZsyJUYOfcoaN3y93wA/QJ2A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/config-resolver": "^4.4.10", + "@smithy/credential-provider-imds": "^4.2.11", + "@smithy/node-config-provider": "^4.3.11", + "@smithy/property-provider": "^4.2.11", + "@smithy/smithy-client": "^4.12.3", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-endpoints": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.3.2.tgz", + "integrity": "sha512-+4HFLpE5u29AbFlTdlKIT7jfOzZ8PDYZKTb3e+AgLz986OYwqTourQ5H+jg79/66DB69Un1+qKecLnkZdAsYcA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.11", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "@redis/client": "^5.10.0" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@redis/client": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.10.0.tgz", - "integrity": "sha512-JXmM4XCoso6C75Mr3lhKA3eNxSzkYi3nCzxDIKY+YOszYsJjuKbFgVtguVPbLMOttN4iu2fXoc2BGhdnYhIOxA==", - "license": "MIT", + "node_modules/@smithy/util-hex-encoding": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.2.2.tgz", + "integrity": "sha512-Qcz3W5vuHK4sLQdyT93k/rfrUwdJ8/HZ+nMUOyGdpeGA1Wxt65zYwi3oEl9kOM+RswvYq90fzkNDahPS8K0OIg==", + "license": "Apache-2.0", "dependencies": { - "cluster-key-slot": "1.1.2" + "tslib": "^2.6.2" }, "engines": { - "node": ">= 18" + "node": ">=18.0.0" } }, - "node_modules/@redis/json": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/@redis/json/-/json-5.10.0.tgz", - "integrity": "sha512-B2G8XlOmTPUuZtD44EMGbtoepQG34RCDXLZbjrtON1Djet0t5Ri7/YPXvL9aomXqP8lLTreaprtyLKF4tmXEEA==", - "license": "MIT", - "engines": { - "node": ">= 18" + "node_modules/@smithy/util-middleware": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.11.tgz", + "integrity": "sha512-r3dtF9F+TpSZUxpOVVtPfk09Rlo4lT6ORBqEvX3IBT6SkQAdDSVKR5GcfmZbtl7WKhKnmb3wbDTQ6ibR2XHClw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "@redis/client": "^5.10.0" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@redis/search": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/@redis/search/-/search-5.10.0.tgz", - "integrity": "sha512-3SVcPswoSfp2HnmWbAGUzlbUPn7fOohVu2weUQ0S+EMiQi8jwjL+aN2p6V3TI65eNfVsJ8vyPvqWklm6H6esmg==", - "license": "MIT", - "engines": { - "node": ">= 18" + "node_modules/@smithy/util-retry": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.11.tgz", + "integrity": "sha512-XSZULmL5x6aCTTii59wJqKsY1l3eMIAomRAccW7Tzh9r8s7T/7rdo03oektuH5jeYRlJMPcNP92EuRDvk9aXbw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^4.2.11", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "@redis/client": "^5.10.0" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@redis/time-series": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-5.10.0.tgz", - "integrity": "sha512-cPkpddXH5kc/SdRhF0YG0qtjL+noqFT0AcHbQ6axhsPsO7iqPi1cjxgdkE9TNeKiBUUdCaU1DbqkR/LzbzPBhg==", - "license": "MIT", + "node_modules/@smithy/util-stream": { + "version": "4.5.17", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.17.tgz", + "integrity": "sha512-793BYZ4h2JAQkNHcEnyFxDTcZbm9bVybD0UV/LEWmZ5bkTms7JqjfrLMi2Qy0E5WFcCzLwCAPgcvcvxoeALbAQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/fetch-http-handler": "^5.3.13", + "@smithy/node-http-handler": "^4.4.14", + "@smithy/types": "^4.13.0", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-buffer-from": "^4.2.2", + "@smithy/util-hex-encoding": "^4.2.2", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 18" + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-uri-escape": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.2.2.tgz", + "integrity": "sha512-2kAStBlvq+lTXHyAZYfJRb/DfS3rsinLiwb+69SstC9Vb0s9vNWkRwpnj918Pfi85mzi42sOqdV72OLxWAISnw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" }, - "peerDependencies": { - "@redis/client": "^5.10.0" + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@sideway/address": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", - "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", - "license": "BSD-3-Clause", + "node_modules/@smithy/util-utf8": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.2.2.tgz", + "integrity": "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw==", + "license": "Apache-2.0", "dependencies": { - "@hapi/hoek": "^9.0.0" + "@smithy/util-buffer-from": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/@sideway/formula": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", - "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==", - "license": "BSD-3-Clause" + "node_modules/@smithy/util-waiter": { + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.2.12.tgz", + "integrity": "sha512-ek5hyDrzS6mBFsNCEX8LpM+EWSLq6b9FdmPRlkpXXhiJE6aIZehKT9clC6+nFpZAA+i/Yg0xlaPeWGNbf5rzQA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^4.2.11", + "@smithy/types": "^4.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, - "node_modules/@sideway/pinpoint": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", - "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", - "license": "BSD-3-Clause" + "node_modules/@smithy/uuid": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@smithy/uuid/-/uuid-1.1.2.tgz", + "integrity": "sha512-O/IEdcCUKkubz60tFbGA7ceITTAJsty+lBjNoorP4Z6XRqaFb/OjQjZODophEcuq68nKm6/0r+6/lLQ+XVpk8g==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, "node_modules/@types/debug": { "version": "4.1.12", @@ -932,6 +2612,15 @@ "undici-types": "~6.20.0" } }, + "node_modules/@types/nodemailer": { + "version": "6.4.23", + "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.23.tgz", + "integrity": "sha512-aFV3/NsYFLSx9mbb5gtirBSXJnAlrusoKNuPbxsASWc7vrKLmIrTQRpdcxNcSFL3VW2A2XpeLEavwb2qMi6nlQ==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/through": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.33.tgz", @@ -1117,6 +2806,121 @@ "dev": true, "license": "MIT" }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", + "license": "MIT" + }, + "node_modules/archiver": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-6.0.2.tgz", + "integrity": "sha512-UQ/2nW7NMl1G+1UnrLypQw1VdT9XZg/ECcKPq7l+STzStrSivFIXIp34D8M5zeNGW5NoOupdYCHv6VySCPNNlw==", + "license": "MIT", + "dependencies": { + "archiver-utils": "^4.0.1", + "async": "^3.2.4", + "buffer-crc32": "^0.2.1", + "readable-stream": "^3.6.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^3.0.0", + "zip-stream": "^5.0.1" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/archiver-utils": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-4.0.1.tgz", + "integrity": "sha512-Q4Q99idbvzmgCTEAAhi32BkOyq8iVI5EwdO0PmBDSGIzzjYNdcFn7Q7k3OzbLy4kLUPXfJtG6fO2RjftXbobBg==", + "license": "MIT", + "dependencies": { + "glob": "^8.0.0", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash": "^4.17.15", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/archiver-utils/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "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", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/archiver-utils/node_modules/minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/archiver-utils/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/archiver/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -1242,50 +3046,151 @@ "node": ">= 10.0.0" } }, - "node_modules/aws-sdk/node_modules/events": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==", - "license": "MIT", + "node_modules/aws-sdk/node_modules/events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==", + "license": "MIT", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/aws-sdk/node_modules/uuid": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", + "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/aws-ssl-profiles": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz", + "integrity": "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/axios": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", + "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/b4a": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.0.tgz", + "integrity": "sha512-qRuSmNSkGQaHwNbM7J78Wwy+ghLEYF1zNrSeMxj4Kgw6y33O3mXcQ6Ie9fRvfU/YnxWkOchPXbaLb73TkIsfdg==", + "license": "Apache-2.0", + "peerDependencies": { + "react-native-b4a": "*" + }, + "peerDependenciesMeta": { + "react-native-b4a": { + "optional": true + } + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/bare-events": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.2.tgz", + "integrity": "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==", + "license": "Apache-2.0", + "peerDependencies": { + "bare-abort-controller": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + } + } + }, + "node_modules/bare-fs": { + "version": "4.5.5", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.5.5.tgz", + "integrity": "sha512-XvwYM6VZqKoqDll8BmSww5luA5eflDzY0uEFfBJtFKe4PAAtxBjU3YIxzIBzhyaEQBy1VXEQBto4cpN5RZJw+w==", + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.5.4", + "bare-path": "^3.0.0", + "bare-stream": "^2.6.4", + "bare-url": "^2.2.2", + "fast-fifo": "^1.3.2" + }, + "engines": { + "bare": ">=1.16.0" + }, + "peerDependencies": { + "bare-buffer": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + } + } + }, + "node_modules/bare-os": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.7.1.tgz", + "integrity": "sha512-ebvMaS5BgZKmJlvuWh14dg9rbUI84QeV3WlWn6Ph6lFI8jJoh7ADtVTyD2c93euwbe+zgi0DVrl4YmqXeM9aIA==", + "license": "Apache-2.0", "engines": { - "node": ">=0.4.x" + "bare": ">=1.14.0" } }, - "node_modules/aws-sdk/node_modules/uuid": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", - "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==", - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" + "node_modules/bare-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.0.tgz", + "integrity": "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==", + "license": "Apache-2.0", + "dependencies": { + "bare-os": "^3.0.1" } }, - "node_modules/aws-ssl-profiles": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz", - "integrity": "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==", - "license": "MIT", - "engines": { - "node": ">= 6.0.0" + "node_modules/bare-stream": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.8.1.tgz", + "integrity": "sha512-bSeR8RfvbRwDpD7HWZvn8M3uYNDrk7m9DQjYOFkENZlXW8Ju/MPaqUPQq5LqJ3kyjEm07siTaAQ7wBKCU59oHg==", + "license": "Apache-2.0", + "dependencies": { + "streamx": "^2.21.0", + "teex": "^1.0.1" + }, + "peerDependencies": { + "bare-buffer": "*", + "bare-events": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + }, + "bare-events": { + "optional": true + } } }, - "node_modules/axios": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", - "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", - "license": "MIT", + "node_modules/bare-url": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.3.2.tgz", + "integrity": "sha512-ZMq4gd9ngV5aTMa5p9+UfY0b3skwhHELaDkhEHetMdX0LRkW9kzaym4oo/Eh+Ghm0CCDuMTsRIGM/ytUc1ZYmw==", + "license": "Apache-2.0", "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.4", - "proxy-from-env": "^1.1.0" + "bare-path": "^3.0.0" } }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT" - }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -1312,6 +3217,19 @@ "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==", "license": "MIT" }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/bl": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz", @@ -1409,6 +3327,12 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/bowser": { + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.14.1.tgz", + "integrity": "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==", + "license": "MIT" + }, "node_modules/brace-expansion": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", @@ -1442,12 +3366,38 @@ "isarray": "^1.0.0" } }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", "license": "BSD-3-Clause" }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -1784,6 +3734,31 @@ "dev": true, "license": "MIT" }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, "node_modules/clamscan": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/clamscan/-/clamscan-2.4.0.tgz", @@ -2008,12 +3983,70 @@ "node": ">=20" } }, + "node_modules/compress-commons": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-5.0.3.tgz", + "integrity": "sha512-/UIcLWvwAQyVibgpQDPtfNM3SvqN7G9elAPAV7GM0L53EbNWwWiCsWtK8Fwed/APEbptPHXs5PuW+y8Bq8lFTA==", + "license": "MIT", + "dependencies": { + "crc-32": "^1.2.0", + "crc32-stream": "^5.0.0", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/compress-commons/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "license": "MIT" }, + "node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "engines": [ + "node >= 6.0" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/constant-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/constant-case/-/constant-case-3.0.4.tgz", @@ -2062,6 +4095,12 @@ "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", "license": "MIT" }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, "node_modules/cors": { "version": "2.8.5", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", @@ -2075,6 +4114,45 @@ "node": ">= 0.10" } }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/crc32-stream": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-5.0.1.tgz", + "integrity": "sha512-lO1dFui+CEUh/ztYIpgpKItKW9Bb4NWakCRJrnqAbFIYD+OZAwb2VfD5T5eXMw2FNcsDHkQcNl/Wh3iVXYwU6g==", + "license": "MIT", + "dependencies": { + "crc-32": "^1.2.0", + "readable-stream": "^3.4.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/crc32-stream/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/cross-env": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", @@ -2581,22 +4659,6 @@ "eslint": ">=4.19.1" } }, - "node_modules/eslint-plugin-filenames-simple": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-filenames-simple/-/eslint-plugin-filenames-simple-0.9.0.tgz", - "integrity": "sha512-eIs+fIO/YXjjlNRdMK3H2HiF0BJ6zj3Of1+REBMnHMbqqgxX1VReIV/gf+ZfP3/bbCAHk5PE3SXi7Fjt3uzUGw==", - "dev": true, - "license": "MIT", - "dependencies": { - "pluralize": "^8.0.0" - }, - "engines": { - "node": ">=14.17.0" - }, - "peerDependencies": { - "eslint": ">=7.0.0 <9.0.0" - } - }, "node_modules/eslint-plugin-node": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", @@ -2929,6 +4991,15 @@ "node": ">=0.8.x" } }, + "node_modules/events-universal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", + "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.7.0" + } + }, "node_modules/expand-tilde": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", @@ -3033,6 +5104,12 @@ "dev": true, "license": "Apache-2.0" }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "license": "MIT" + }, "node_modules/fast-glob": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", @@ -3079,6 +5156,40 @@ "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", "license": "MIT" }, + "node_modules/fast-xml-builder": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.1.0.tgz", + "integrity": "sha512-7mtITW/we2/wTUZqMyBOR2F8xP4CRxMiSEcQxPIqdRWdO2L/HZSOlzoNyghmyDwNB8BDxePooV1ZTJpkOUhdRg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "path-expression-matcher": "^1.1.2" + } + }, + "node_modules/fast-xml-parser": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.4.1.tgz", + "integrity": "sha512-BQ30U1mKkvXQXXkAGcuyUA/GA26oEB7NzOtsxCDtyu62sjGw5QraKFhx2Em3WQNjPw9PG6MQ9yuIIgkSDfGu5A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "fast-xml-builder": "^1.0.0", + "strnum": "^2.1.2" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, "node_modules/fastq": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.0.tgz", @@ -3349,12 +5460,41 @@ "node": ">= 0.6" } }, + "node_modules/fs-extra": { + "version": "11.3.4", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.4.tgz", + "integrity": "sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "license": "ISC" }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -3585,7 +5725,6 @@ "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, "license": "ISC" }, "node_modules/handlebars": { @@ -3848,6 +5987,13 @@ "node": ">= 4" } }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true, + "license": "ISC" + }, "node_modules/import-fresh": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", @@ -4142,6 +6288,19 @@ "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", "license": "MIT" }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", @@ -4510,6 +6669,18 @@ "dev": true, "license": "MIT" }, + "node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, "node_modules/jsonwebtoken": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", @@ -4618,6 +6789,48 @@ "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", "license": "MIT" }, + "node_modules/lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "license": "MIT", + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, + "node_modules/lazystream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/lazystream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/lazystream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -5337,6 +7550,25 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, + "node_modules/multer": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/multer/-/multer-2.1.1.tgz", + "integrity": "sha512-mo+QTzKlx8R7E5ylSXxWzGoXoZbOsRMpyitcht8By2KHvMbf3tjwosZ/Mu/XYU6UuJ3VZnODIrak5ZrPiPyB6A==", + "license": "MIT", + "dependencies": { + "append-field": "^1.0.0", + "busboy": "^1.6.0", + "concat-stream": "^2.0.0", + "type-is": "^1.6.18" + }, + "engines": { + "node": ">= 10.16.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/mute-stream": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", @@ -5435,6 +7667,15 @@ "tslib": "^2.0.3" } }, + "node_modules/node-cron": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/node-cron/-/node-cron-4.2.1.tgz", + "integrity": "sha512-lgimEHPE/QDgFlywTd8yTR61ptugX3Qer29efeyWw2rv259HtGBNn1vZVmp8lB9uo9wC0t/AT4iGqXxia+CJFg==", + "license": "ISC", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/node-plop": { "version": "0.31.1", "resolved": "https://registry.npmjs.org/node-plop/-/node-plop-0.31.1.tgz", @@ -5469,6 +7710,144 @@ "node": ">=6.0.0" } }, + "node_modules/nodemon": { + "version": "3.1.14", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.14.tgz", + "integrity": "sha512-jakjZi93UtB3jHMWsXL68FXSAosbLfY0In5gtKq3niLSkrWznrVBzXFNOEMJUfc9+Ke7SHWoAZsiMkNP3vq6Jw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^10.2.1", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nodemon/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/nodemon/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/nodemon/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/nodemon/node_modules/minimatch": { + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/nodemon/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nodemon/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/nodemon/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -5847,6 +8226,21 @@ "node": ">=8" } }, + "node_modules/path-expression-matcher": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.1.2.tgz", + "integrity": "sha512-LXWqJmcpp2BKOEmgt4CyuESFmBfPuhJlAHKJsFzuJU6CxErWk75BrO+Ni77M9OxHN6dCYKM4vj+21Z6cOL96YQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -6059,16 +8453,6 @@ "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, - "node_modules/pluralize": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", - "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/possible-typed-array-names": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", @@ -6126,6 +8510,12 @@ "node": ">= 0.6.0" } }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, "node_modules/process-warning": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-3.0.0.tgz", @@ -6151,6 +8541,13 @@ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "license": "MIT" }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true, + "license": "MIT" + }, "node_modules/pump": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", @@ -6326,6 +8723,40 @@ ], "license": "BSD-3-Clause" }, + "node_modules/readdir-glob": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", + "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.1.0" + } + }, + "node_modules/readdir-glob/node_modules/minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, "node_modules/real-require": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", @@ -6985,6 +9416,32 @@ "is-arrayish": "^0.3.1" } }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/simple-update-notifier/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -7139,6 +9596,49 @@ "npm": ">=6" } }, + "node_modules/stream-browserify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", + "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==", + "license": "MIT", + "dependencies": { + "inherits": "~2.0.4", + "readable-stream": "^3.5.0" + } + }, + "node_modules/stream-browserify/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/streamx": { + "version": "2.23.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.23.0.tgz", + "integrity": "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==", + "license": "MIT", + "dependencies": { + "events-universal": "^1.0.0", + "fast-fifo": "^1.3.2", + "text-decoder": "^1.1.0" + } + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -7198,6 +9698,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strnum": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.0.tgz", + "integrity": "sha512-Y7Bj8XyJxnPAORMZj/xltsfo55uOiyHcU2tnAVzHUnSJR/KsEX+9RoDeXEnsXtl/CX4fAcrt64gZ13aGaWPeBg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -7240,6 +9752,36 @@ "url": "https://opencollective.com/synckit" } }, + "node_modules/tar-stream": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.8.tgz", + "integrity": "sha512-U6QpVRyCGHva435KoNWy9PRoi2IFYCgtEhq9nmrPPpbRacPs9IH4aJ3gbrFC8dPcXvdSZ4XXfXT5Fshbp2MtlQ==", + "license": "MIT", + "dependencies": { + "b4a": "^1.6.4", + "bare-fs": "^4.5.5", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "node_modules/teex": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz", + "integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==", + "license": "MIT", + "dependencies": { + "streamx": "^2.12.5" + } + }, + "node_modules/text-decoder": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz", + "integrity": "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==", + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.4" + } + }, "node_modules/text-hex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", @@ -7300,6 +9842,16 @@ "integrity": "sha512-OsLcGGbYF3rMjPUf8oKktyvCiUxSbqMMS39m33MAjLTC1DVIH6x3WSt63/M77ihI09+Sdfk1AXvfhCEeUmC7mg==", "license": "MIT" }, + "node_modules/touch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", + "dev": true, + "license": "ISC", + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, "node_modules/triple-beam": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", @@ -7353,6 +9905,12 @@ "node": ">= 0.6" } }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "license": "MIT" + }, "node_modules/uglify-js": { "version": "3.19.3", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", @@ -7377,12 +9935,28 @@ "node": ">=0.10.0" } }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true, + "license": "MIT" + }, "node_modules/undici-types": { "version": "6.20.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", "license": "MIT" }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -7712,6 +10286,34 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zip-stream": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-5.0.2.tgz", + "integrity": "sha512-LfOdrUvPB8ZoXtvOBz6DlNClfvi//b5d56mSWyJi7XbH/HfhOHfUhOqxhT/rUiR7yiktlunqRo+jY6y/cWC/5g==", + "license": "MIT", + "dependencies": { + "archiver-utils": "^4.0.1", + "compress-commons": "^5.0.1", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/zip-stream/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } } } } diff --git a/package.json b/package.json index f21c66d..6529db2 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,13 @@ "lint:fix": "eslint . --fix", "format": "prettier --write \"**/*.{js,json,md,yml,yaml}\"", "format:check": "prettier --check \"**/*.{js,json,md,yml,yaml}\"", - "prepare": "husky install" + "prepare": "husky install", + "docs:validate": "node scripts/validate-docs.js", + "quality:score": "node scripts/score-quality.js", + "plan:new": "node scripts/new-plan.js", + "worktree:bootstrap": "bash scripts/worktree-bootstrap.sh", + "verify": "npm run lint && npm run format:check && npm run docs:validate && npm run quality:score", + "symphony:bootstrap": "bash scripts/symphony-bootstrap.sh" }, "repository": { "type": "git", @@ -26,6 +32,9 @@ }, "homepage": "https://github.com/Codebucket-Solutions/node-template#readme", "dependencies": { + "@codebucket/files": "^1.0.0", + "@codebucket/mail-transport": "^1.0.0", + "@codebucket/sms": "^1.0.0", "@codebucket/azure-env": "^1.0.2", "@codebucket/logserver-transport": "^1.0.2", "aws-sdk": "^2.1309.0", @@ -53,6 +62,7 @@ "nodemailer": "^7.0.10", "pino": "^8.20.0", "pino-http": "^9.0.0", + "node-cron": "^4.2.1", "pino-pretty": "^11.0.0", "rate-limit-redis": "^4.3.0", "redis": "^5.10.0", @@ -62,16 +72,19 @@ "devDependencies": { "eslint": "^9.39.1", "eslint-config-prettier": "^10.1.8", - "eslint-plugin-filenames-simple": "^0.9.0", "eslint-plugin-node": "^11.1.0", "eslint-plugin-prettier": "^5.5.4", "husky": "^9.1.7", "lint-staged": "^16.2.7", - "node-cron": "^4.2.1", "nodemon": "^3.1.11", "plop": "^3.1.1", "prettier": "^3.5.3" }, + "overrides": { + "@codebucket/files": { + "mime": "3.0.0" + } + }, "lint-staged": { "*.js": [ "eslint", diff --git a/scripts/new-plan.js b/scripts/new-plan.js new file mode 100644 index 0000000..6dda0dc --- /dev/null +++ b/scripts/new-plan.js @@ -0,0 +1,48 @@ +const fs = require("fs"); +const path = require("path"); + +function getArg(name) { + const prefix = `--${name}=`; + const arg = process.argv.find(item => item.startsWith(prefix)); + return arg ? arg.slice(prefix.length) : ""; +} + +const slug = getArg("slug").trim(); +const title = getArg("title").trim(); + +if (!slug || !title) { + console.error('Usage: npm run plan:new -- --slug=my-task --title="My Task"'); + process.exit(1); +} + +const target = path.join(process.cwd(), "docs", "exec-plans", "active", `${slug}.md`); +if (fs.existsSync(target)) { + console.error(`Plan already exists: ${target}`); + process.exit(1); +} + +const content = `# Execution Plan: ${slug} + +- Status: active +- Owner: unassigned +- Scope: ${title} + +## Goal +Describe the intended result. + +## Acceptance criteria +- [ ] Criterion 1 +- [ ] Criterion 2 + +## Checklist +- [ ] Inspect existing implementation +- [ ] Implement smallest useful change +- [ ] Update docs if needed +- [ ] Run verification + +## Verification notes +- Pending +`; + +fs.writeFileSync(target, content); +console.log(`Created ${path.relative(process.cwd(), target)}`); diff --git a/scripts/score-quality.js b/scripts/score-quality.js new file mode 100644 index 0000000..b6154f4 --- /dev/null +++ b/scripts/score-quality.js @@ -0,0 +1,31 @@ +const fs = require("fs"); +const path = require("path"); + +const checks = [ + ["AGENTS.md", fs.existsSync("AGENTS.md")], + ["docs/ARCHITECTURE.md", fs.existsSync("docs/ARCHITECTURE.md")], + ["docs/WORKFLOW.md", fs.existsSync("docs/WORKFLOW.md")], + ["docs/QUALITY.md", fs.existsSync("docs/QUALITY.md")], + ["docs/SECURITY.md", fs.existsSync("docs/SECURITY.md")], + ["docs/RELIABILITY.md", fs.existsSync("docs/RELIABILITY.md")], + [".codex/config.toml", fs.existsSync(".codex/config.toml")], + ["scripts/worktree-bootstrap.sh", fs.existsSync("scripts/worktree-bootstrap.sh")], + ["CI workflow", fs.existsSync(".github/workflows/ci.yml")], +]; + +const passed = checks.filter(([, ok]) => ok).length; +const total = checks.length; +const score = Math.round((passed / total) * 100); + +const payload = { + generatedAt: new Date().toISOString(), + score, + passed, + total, + checks: checks.map(([name, ok]) => ({ name, ok })), +}; + +const outDir = path.join(process.cwd(), "docs", "references"); +fs.mkdirSync(outDir, { recursive: true }); +fs.writeFileSync(path.join(outDir, "quality-score.json"), JSON.stringify(payload, null, 2)); +console.log(`Quality score generated: ${score}`); diff --git a/scripts/symphony-bootstrap.sh b/scripts/symphony-bootstrap.sh new file mode 100755 index 0000000..c6f0925 --- /dev/null +++ b/scripts/symphony-bootstrap.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Symphony creates a fresh workspace per issue. This script makes that workspace +# ready for Codex without depending on machine-specific shell state. + +if command -v npm >/dev/null 2>&1; then + npm ci || npm install +else + echo "npm is required" >&2 + exit 1 +fi + +# Reuse the existing harness/bootstrap checks. +bash scripts/worktree-bootstrap.sh + +# Additional lightweight signal for Symphony-specific runs. +echo "Symphony bootstrap complete." diff --git a/scripts/validate-docs.js b/scripts/validate-docs.js new file mode 100644 index 0000000..91cac91 --- /dev/null +++ b/scripts/validate-docs.js @@ -0,0 +1,34 @@ +const fs = require("fs"); +const path = require("path"); + +const root = process.cwd(); +const requiredFiles = [ + "AGENTS.md", + "docs/ARCHITECTURE.md", + "docs/WORKFLOW.md", + "docs/QUALITY.md", + "docs/SECURITY.md", + "docs/RELIABILITY.md", + ".codex/config.toml", +]; + +let failed = false; +for (const rel of requiredFiles) { + const full = path.join(root, rel); + if (!fs.existsSync(full)) { + console.error(`Missing required repo guidance file: ${rel}`); + failed = true; + } +} + +const activePlansDir = path.join(root, "docs", "exec-plans", "active"); +if (!fs.existsSync(activePlansDir)) { + console.error("Missing active plans directory: docs/exec-plans/active"); + failed = true; +} + +if (failed) { + process.exit(1); +} + +console.log("Documentation validation passed."); diff --git a/scripts/worktree-bootstrap.sh b/scripts/worktree-bootstrap.sh new file mode 100755 index 0000000..6e5e9e0 --- /dev/null +++ b/scripts/worktree-bootstrap.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +set -euo pipefail + +if command -v npm >/dev/null 2>&1; then + npm ci || npm install +else + echo "npm is required" >&2 + exit 1 +fi + +if npm run | grep -q " lint"; then + npm run lint || true +fi + +if npm run | grep -q " format:check"; then + npm run format:check || true +fi + +node scripts/validate-docs.js +node scripts/score-quality.js + +echo "Worktree bootstrap complete." diff --git a/service/v1/index.js b/service/v1/index.js index ab6bfc4..a57366c 100644 --- a/service/v1/index.js +++ b/service/v1/index.js @@ -1,3 +1,3 @@ module.exports = { - // ReplaceWithService + workflowEngine: require("./workflow-engine"), }; diff --git a/service/v1/workflow-engine/README.md b/service/v1/workflow-engine/README.md new file mode 100644 index 0000000..af0b50e --- /dev/null +++ b/service/v1/workflow-engine/README.md @@ -0,0 +1,1006 @@ +# Workflow Engine + +This folder contains a generic workflow and assignment engine built on Sequelize models. + +The goal is simple: + +- keep track of who currently owns a piece of work, +- record every workflow action in a timeline, +- decide what actions are allowed at each stage, +- move work to the next assignee or stage, +- and optionally auto-escalate overdue work. + +This README explains the engine in plain language and in implementation detail. + +## What Problem This Solves + +Many business applications have records that move between people, roles, and stages. + +Examples: + +- a grievance moves from Head Office to Division to Depot, +- a ticket moves from support to engineering, +- an approval moves from maker to checker to approver. + +Every such system usually needs the same things: + +1. A way to know who currently owns the record. +2. A way to know what stage the record is currently in. +3. A way to record every transition for audit/history. +4. A rule book that says what action is allowed from which stage. +5. A way to assign the next owner. +6. A way to automatically escalate if nothing happens for some time. + +This folder implements those generic building blocks. + +## High-Level Idea + +The engine does not own your business entity itself. + +It only manages workflow behavior around that entity. + +For example, if your business record is a ticket, complaint, grievance, approval, or task, that business table remains your application table. The workflow engine only needs to know: + +- which Sequelize model represents that application type, +- which field is the record id, +- which field stores the current stage, +- which field stores the current assignment pointer. + +Then the engine stores workflow metadata in separate generic tables: + +- `workflow_application_registry` +- `assigner_rule` +- `assignment` +- `workflow_action` +- `workflow_escalation_matrix` + +## Folder Contents + +- `assignment.js` + Handles creating assignments, closing current assignments, and auto-assignment. +- `assignee-resolver.js` + Resolves dynamic assignees from rules and runtime context. +- `escalation-matrix.js` + Resolves the correct workflow transition row for a stage/action pair. +- `workflow.js` + Performs workflow transitions and returns the timeline. +- `workflow-actions.js` + Computes what actions are allowed for the current assignee. +- `auto-escalation.js` + Finds overdue assignments and triggers configured automatic transitions. +- `workflow-support.js` + Shared helpers for model lookup, application adapters, JSON parsing, and rule matching. +- `index.js` + Re-exports the engine services. + +## Data Model + +### 1. `assigner_rule` + +Purpose: + +- decides who should get a record when it is auto-assigned. + +Important fields: + +- `application_type` + The kind of record, for example `TICKET`, `COMPLAINT`, `APPROVAL`, or `GRIEVANCE`. +- `conditions_json` + Optional condition object. If present, the incoming context must match it. +- `assignee_type` + Usually `USER` or `ROLE`. +- `assignee_id` + The static id if the rule is direct. +- `assignee_mode` + `STATIC` or `CONTEXT`. +- `assignee_resolver_key` + Optional key column kept for compatibility with existing schemas. The current engine expects full resolver specs instead of hard-coded named keys. +- `assignee_resolver_params` + Optional JSON payload used by the resolver. +- `priority` + Lower number wins. +- `is_active` + Whether the rule is active. + +How it is used: + +1. Find all active rules for the application type. +2. Sort by priority. +3. Pick the first rule whose `conditions_json` matches the runtime context. +4. If the rule is `STATIC`, use the assignee directly. +5. If the rule is `CONTEXT`, resolve the assignee dynamically. + +### 2. `workflow_application_registry` + +Purpose: + +- tells the engine how to locate and update each application type in a table-driven way. + +Important fields: + +- `application_type` +- `model_name` +- `id_field` +- `stage_field` +- `current_assignment_field` +- `is_active` + +How it is used: + +1. The engine receives an `applicationType`. +2. It loads the matching active row from `workflow_application_registry`. +3. It finds the Sequelize model named in `model_name`. +4. It uses the configured field names to load and update the application record. + +This is what makes the engine table-controlled instead of hard-coded for one business entity. + +### 3. `assignment` + +Purpose: + +- stores current and past ownership of an application record. + +Important fields: + +- `application_type` +- `application_id` +- `assignee_type` +- `assignee_id` +- `assigned_by` +- `assigned_at` +- `status` + `ACTIVE` or `CLOSED` +- `closed_at` + +How it is used: + +- there should be at most one active assignment for a given application record, +- when a new assignment is created, the old active one is closed, +- the application record points to the active assignment using `current_assignment_id`. + +### 4. `workflow_action` + +Purpose: + +- stores the audit trail of workflow transitions. + +Important fields: + +- `application_type` +- `application_id` +- `action` +- `from_assignee_type` +- `from_assignee_id` +- `to_assignee_type` +- `to_assignee_id` +- `from_stage` +- `to_stage` +- `remarks` +- `metadata_json` +- `actor_user_id` + +How it is used: + +- every important transition writes one row here, +- this table powers the workflow timeline, +- this is your audit history. + +### 5. `workflow_escalation_matrix` + +Purpose: + +- acts like the workflow rule book. + +Important fields: + +- `application_type` +- `current_stage` +- `current_assignee_type` +- `current_assignee_id` +- `action` +- `next_stage` +- `next_assignee_type` +- `next_assignee_id` +- `next_assignee_mode` +- `next_assignee_resolver_key` +- `next_assignee_resolver_params` +- `priority` +- `is_active` +- `auto_trigger` +- `auto_after_minutes` + +How it is used: + +- when a user performs an action, the engine looks here to decide what should happen next, +- if `current_assignee_type` and `current_assignee_id` are null, that row is a wildcard, +- otherwise the current assignee must match, +- lower priority wins, +- if `auto_trigger` is enabled, the row can also be used by the auto-escalation worker. + +## Host Application Requirements + +The engine is generic, but it still needs host models to exist in Sequelize. + +For every application type you want to drive through the engine, you need: + +1. A Sequelize model registered in the app. +2. A row in `workflow_application_registry` that tells the engine: + - the application type name, + - the Sequelize model name, + - the id field, + - the stage field, + - the current assignment field. + +Example registry row: + +```json +{ + "application_type": "TICKET", + "model_name": "ticket", + "id_field": "ticket_id", + "stage_field": "stage", + "current_assignment_field": "current_assignment_id", + "is_active": true +} +``` + +The assignee resolver is also generic now. + +For `LOOKUP` mode, it needs: + +1. A registered Sequelize model for the target table. +2. A resolver spec that says: + - which table/model to read, + - which fields to match, + - which result field becomes the assignee id, + - what assignee type should be returned. + +If these host models are not registered in Sequelize, the engine fails fast with clear errors. + +## Seeding Guide + +The engine is generic, but it does not do anything until its control tables are seeded. + +In practice, there are four things you seed: + +1. `workflow_application_registry` +2. `assigner_rule` +3. `workflow_escalation_matrix` +4. optional application-side seed data such as users, roles, departments, queues, or lookup rows used by resolvers + +The cleanest rollout order is: + +1. Seed the host application model data your resolver depends on. +2. Seed `workflow_application_registry`. +3. Seed `assigner_rule`. +4. Seed `workflow_escalation_matrix`. +5. Create one application record and test: + auto assignment, allowed actions, manual transition, and auto escalation. + +### 1. Seed `workflow_application_registry` + +This table tells the engine how to read and update a given application type. + +Example: + +```sql +INSERT INTO workflow_application_registry + (application_type, model_name, id_field, stage_field, current_assignment_field, is_active) +VALUES + ('TICKET', 'ticket', 'ticket_id', 'stage', 'current_assignment_id', 1); +``` + +What this means: + +- when the engine receives `applicationType = TICKET`, +- it will look for a Sequelize model named `ticket` or `Ticket`, +- it will read the record id from `ticket_id`, +- it will read and update the stage in `stage`, +- it will read and update the active assignment pointer in `current_assignment_id`. + +Another example: + +```sql +INSERT INTO workflow_application_registry + (application_type, model_name, id_field, stage_field, current_assignment_field, is_active) +VALUES + ('COMPLAINT', 'complaint', 'complaint_id', 'status_stage', 'assignment_ref_id', 1); +``` + +That shows why this table exists: + +- different business tables can use different field names, +- and the engine still works without code changes. + +### 2. Seed `assigner_rule` + +This table answers: + +"When a new record should be assigned automatically, who gets it?" + +There are two main styles. + +#### Static assignment rule + +Example: + +```sql +INSERT INTO assigner_rule + (application_type, conditions_json, assignee_type, assignee_id, assignee_mode, priority, is_active) +VALUES + ('TICKET', NULL, 'ROLE', 'LEVEL1_SUPPORT', 'STATIC', 10, 1); +``` + +What this means: + +- every new `TICKET` with no more specific rule match goes to role `LEVEL1_SUPPORT`. + +#### Context-driven assignment rule + +Example: + +```sql +INSERT INTO assigner_rule + ( + application_type, + conditions_json, + assignee_type, + assignee_id, + assignee_mode, + assignee_resolver_params, + priority, + is_active + ) +VALUES + ( + 'TICKET', + JSON_OBJECT('category', 'NETWORK'), + 'USER', + '0', + 'CONTEXT', + JSON_OBJECT( + 'mode', 'LOOKUP', + 'table', 'user_directory', + 'assigneeType', 'USER', + 'assigneeIdField', 'id', + 'where', JSON_OBJECT( + 'status', 'Active', + 'department_id', JSON_OBJECT('valueFromContext', 'departmentId'), + 'location', JSON_OBJECT( + 'code', JSON_OBJECT('valueFromContext', 'locationCode') + ) + ) + ), + 1, + 1 + ); +``` + +What this means: + +- only apply this rule when `context.category === "NETWORK"`, +- look up a row from model/table `user_directory`, +- match: + - `status = Active` + - `department_id = context.departmentId` + - `location.code = context.locationCode` +- return that row’s `id` as the assignee, +- because priority is `1`, this rule wins before the fallback static rule with priority `10`. + +Important note: + +- for `CONTEXT` mode, `assignee_type` and `assignee_id` are not the final assignee; + they are effectively placeholders because the resolver computes the real target. + +### 3. Seed `workflow_escalation_matrix` + +This table answers: + +"If the record is currently at stage X and the current assignee performs action Y, what happens next?" + +#### Example: forward from triage to investigation + +```sql +INSERT INTO workflow_escalation_matrix + ( + application_type, + current_stage, + current_assignee_type, + current_assignee_id, + action, + next_stage, + next_assignee_type, + next_assignee_id, + next_assignee_mode, + priority, + is_active + ) +VALUES + ( + 'TICKET', + 'TRIAGE', + NULL, + NULL, + 'FORWARD', + 'INVESTIGATION', + 'ROLE', + 'LEVEL2_SUPPORT', + 'STATIC', + 10, + 1 + ); +``` + +What this means: + +- when a `TICKET` is in `TRIAGE`, +- and someone performs `FORWARD`, +- move the record to stage `INVESTIGATION`, +- assign it to role `LEVEL2_SUPPORT`, +- the current assignee does not matter because the current assignee columns are null. + +#### Example: stage-specific contextual escalation + +```sql +INSERT INTO workflow_escalation_matrix + ( + application_type, + current_stage, + action, + next_stage, + next_assignee_type, + next_assignee_id, + next_assignee_mode, + next_assignee_resolver_params, + priority, + is_active + ) +VALUES + ( + 'TICKET', + 'LEVEL2_REVIEW', + 'ESCALATE', + 'MANAGER_REVIEW', + 'USER', + '0', + 'CONTEXT', + JSON_OBJECT( + 'mode', 'LOOKUP', + 'table', 'user_directory', + 'assigneeType', 'USER', + 'assigneeIdField', 'id', + 'where', JSON_OBJECT( + 'status', 'Active', + 'role', 'MANAGER', + 'department_id', JSON_OBJECT('valueFromContext', 'departmentId') + ) + ), + 5, + 1 + ); +``` + +What this means: + +- from stage `LEVEL2_REVIEW`, +- action `ESCALATE` moves the record to `MANAGER_REVIEW`, +- the next assignee is not hard-coded, +- instead, the engine resolves a manager for the current `departmentId`. + +### 4. Seed auto-escalation behavior + +Auto escalation is not a separate engine. + +It is just a timed use of the same `workflow_escalation_matrix`. + +Example: + +```sql +INSERT INTO workflow_escalation_matrix + ( + application_type, + current_stage, + action, + next_stage, + next_assignee_type, + next_assignee_id, + next_assignee_mode, + priority, + is_active, + auto_trigger, + auto_after_minutes + ) +VALUES + ( + 'TICKET', + 'LEVEL1_PENDING', + 'ESCALATE', + 'LEVEL2_PENDING', + 'ROLE', + 'LEVEL2_SUPPORT', + 'STATIC', + 1, + 1, + 1, + 30 + ); +``` + +What this means: + +- if a `TICKET` stays assigned at stage `LEVEL1_PENDING`, +- and the active assignment has been open for 30 minutes, +- the auto escalation worker can perform `ESCALATE`, +- moving the record to `LEVEL2_PENDING`, +- and reassigning it to `LEVEL2_SUPPORT`. + +### 5. Recommended first test seed + +If you are wiring this engine for the first time, do not start with complex contextual rules. + +Start with this minimal path: + +1. one registry row, +2. one static assigner rule, +3. one simple matrix row for `FORWARD`, +4. one simple matrix row for `CLOSED` or terminal resolution, +5. one test application record. + +Example minimal seed set: + +```sql +INSERT INTO workflow_application_registry + (application_type, model_name, id_field, stage_field, current_assignment_field, is_active) +VALUES + ('TICKET', 'ticket', 'ticket_id', 'stage', 'current_assignment_id', 1); + +INSERT INTO assigner_rule + (application_type, assignee_type, assignee_id, assignee_mode, priority, is_active) +VALUES + ('TICKET', 'ROLE', 'LEVEL1_SUPPORT', 'STATIC', 1, 1); + +INSERT INTO workflow_escalation_matrix + ( + application_type, + current_stage, + action, + next_stage, + next_assignee_type, + next_assignee_id, + next_assignee_mode, + priority, + is_active + ) +VALUES + ('TICKET', 'ASSIGNED', 'FORWARD', 'IN_PROGRESS', 'ROLE', 'LEVEL2_SUPPORT', 'STATIC', 1, 1), + ('TICKET', 'IN_PROGRESS', 'CLOSED', 'CLOSED', NULL, NULL, 'STATIC', 1, 1); +``` + +That gives you the smallest realistic end-to-end path: + +- auto-assign to level 1, +- forward to level 2, +- close the record. + +Once that works, add context resolvers and timed escalations. + +## Service-by-Service Explanation + +### `assignment.js` + +This service handles ownership. + +Main methods: + +- `autoAssign(...)` +- `assign(...)` +- `getCurrent(...)` +- `closeCurrent(...)` + +#### `autoAssign` + +This method is for the first assignment or any rule-driven assignment. + +What it does: + +1. Validates that `applicationType` and `applicationId` exist. +2. Loads active `assigner_rule` rows for that type. +3. Matches the first rule whose `conditions_json` fits the supplied context. +4. Resolves the assignee: + either direct from the rule or dynamically through `AssigneeResolver`. +5. Calls `assign(...)` to create the actual assignment row. +6. Writes a workflow action with action `AUTO_ASSIGNED`. + +#### `assign` + +This is the core ownership change method. + +What it does: + +1. Finds the current active assignment for the record. +2. Locks it inside a transaction. +3. Closes it if it exists. +4. Creates a new `assignment` row. +5. Writes a `workflow_action` row for audit. +6. Returns the new `assignmentId`. + +This method is transaction-aware. If a parent transaction exists, it uses that transaction instead of creating a new one. + +#### `getCurrent` + +Returns the latest active assignment for a record. + +#### `closeCurrent` + +Closes the active assignment and writes a `CLOSED` workflow action entry. + +### `assignee-resolver.js` + +This service converts a rule into a real assignee. + +Supported styles: + +1. Static resolver +2. Lookup resolver + +#### Static resolver + +Example: + +```json +{ + "mode": "STATIC", + "assigneeType": "USER", + "assigneeId": 50 +} +``` + +This directly returns: + +- `assigneeType = USER` +- `assigneeId = 50` + +#### Lookup resolver + +Example: + +```json +{ + "mode": "LOOKUP", + "table": "user_directory", + "assigneeType": "USER", + "assigneeIdField": "id", + "where": { + "status": "Active", + "department_id": { "valueFromContext": "departmentId" }, + "location": { + "code": { "valueFromContext": "locationCode" } + } + } +} +``` + +This means: + +1. Look at the runtime context. +2. Read values such as `context.departmentId` and `context.locationCode`. +3. Query the configured Sequelize model using exact field filters where possible. +4. Perform structured object matching in application code for nested JSON-like values. +5. Return the configured assignee id field from the matched row. + +That means lookup resolution is no longer tied to a specific `login` table or to specific pre-defined business keys. + +### `escalation-matrix.js` + +This service finds the correct workflow rule. + +Input: + +- `applicationType` +- `currentStage` +- `action` +- current assignee info + +What it does: + +1. Loads all active matrix rows matching application type, current stage, and action. +2. Sorts them by priority. +3. Returns the first row that matches the current assignee. +4. If a row has no current assignee constraint, it behaves like a wildcard. + +This is the decision engine for transitions. + +### `workflow.js` + +This is the main orchestration service. + +Main methods: + +- `timeline(...)` +- `takeAction(...)` + +#### `timeline` + +Returns the ordered list of `workflow_action` rows for an application record. + +Optional behavior: + +- if `filterAssignedToMe` is true, it first confirms that the current active assignment belongs to the logged-in user or role. + +#### `takeAction` + +This is the heart of the workflow engine. + +What it does: + +1. Loads the application record through the adapter. +2. Loads the current active assignment. +3. Verifies ownership unless `bypassOwnershipCheck` is enabled. +4. Resolves the correct transition from `workflow_escalation_matrix`. +5. Determines the `nextStage`. +6. Determines the next assignee: + direct values or dynamic resolver depending on matrix configuration. +7. If there is a next assignee: + creates a new assignment and closes the old one. +8. If there is no next assignee: + closes the current assignment. +9. Updates the application record: + `stage` and `current_assignment_id`. +10. Returns a summary object. + +This method is transaction-safe and is used both by normal user actions and auto escalation. + +### `workflow-actions.js` + +This service answers a UI question: + +"What actions can the current assignee perform right now?" + +What it does: + +1. Loads the application record. +2. Loads the current active assignment. +3. Confirms that the record is assigned to the current user or role. +4. Loads all active matrix rows for the current stage. +5. Filters rows that match the current assignee. +6. Deduplicates by action. +7. Returns action metadata for the frontend or API consumer. + +This is useful for: + +- action buttons, +- permission-aware UI, +- workflow action APIs. + +### `auto-escalation.js` + +This service performs timed escalations. + +What it does: + +1. Reads active assignments. +2. Loads the related application record. +3. Ensures the assignment is still the current assignment. +4. Resolves the escalation matrix row for the record’s current stage. +5. Checks whether `auto_trigger = true`. +6. Checks whether `assigned_at + auto_after_minutes` is overdue. +7. If yes, runs `Workflow.takeAction(...)` with `bypassOwnershipCheck`. + +This means auto escalation uses the exact same workflow engine as normal user actions. That is important because it keeps behavior consistent. + +## End-to-End Flow + +Here is a typical lifecycle. + +### Case 1: Auto assignment + +1. An application record is created. +2. Application code calls `Assignment.autoAssign(...)`. +3. The engine loads `assigner_rule`. +4. It matches a rule using the runtime context. +5. It resolves the assignee. +6. It creates an active `assignment`. +7. It writes a `workflow_action` row with `AUTO_ASSIGNED`. +8. The application record can then set `current_assignment_id`. + +### Case 2: Manual action by current assignee + +1. Current assignee clicks `ESCALATE`. +2. Application code calls `Workflow.takeAction(...)`. +3. The engine verifies that the record is really assigned to that user or role. +4. It finds the correct transition row in `workflow_escalation_matrix`. +5. It resolves the next assignee if needed. +6. It closes the old assignment. +7. It creates the new assignment. +8. It writes a `workflow_action` row. +9. It updates the business record’s stage and `current_assignment_id`. + +### Case 3: Automatic escalation + +1. A scheduler calls `AutoEscalationService.run()`. +2. It scans active assignments. +3. It filters to overdue records only. +4. It re-checks current ownership in a transaction. +5. It calls `Workflow.takeAction(...)` with system context. +6. The record moves exactly as if a user had performed the action. + +## Why This Design Uses Sequelize Models Instead of Raw SQL + +The referenced BSRTC implementation used direct queries. + +This implementation intentionally does not. + +Reasons: + +- keeps the engine aligned with this template’s Sequelize-based structure, +- makes transactions and row updates easier to follow, +- allows the host application to register models once and reuse them everywhere, +- reduces SQL duplication in service code, +- makes dynamic app integration cleaner through `sequelize.models`. + +There is still one important tradeoff: + +- structured JSON-like matching for generic lookup resolvers is done in JavaScript after Sequelize fetches rows using ordinary filters. + +That keeps the engine database-agnostic at the service layer, but it may be less efficient than database-native JSON operators for very large datasets. + +## Transaction Behavior + +Most write methods support either: + +- using an existing transaction passed by the caller, +- or creating their own transaction if none is provided. + +This matters because a full workflow action often needs to update multiple things together: + +- assignment rows, +- workflow audit row, +- application record stage, +- current assignment pointer. + +If any one of those steps fails, the whole change should roll back. + +## Error Handling Style + +The engine uses `ErrorHandler` and existing `statusCodes` from the repo. + +It fails early for common problems such as: + +- missing application type, +- missing application id, +- unsupported application type, +- missing host models, +- no active assignment, +- no matching assigner rule, +- no matching escalation matrix row, +- unauthorized action attempts. + +## Current Limitations + +At the moment the engine is intentionally generic but not unlimited. + +Known constraints: + +- host applications must populate `workflow_application_registry` correctly, +- assignment matching logic for `conditions_json` is exact-match only, +- structured lookup matching is resolved in application code, not with DB JSON operators, +- auto escalation expects the application model to expose the current assignment pointer. + +These are acceptable constraints for a first generic engine and are easy to extend in one place. + +## How To Extend It + +### Add another application type + +1. Register the Sequelize model in the host app. +2. Insert a row into `workflow_application_registry`. +3. Start using that `application_type` in rules and matrix rows. + +No engine code change is required for that step. + +### Add a new resolver type + +Edit `assignee-resolver.js`. + +Options: + +- add another `mode`, +- add richer matching operators, +- or add database-native matching for a specific provider if needed. + +### Add richer condition matching + +Edit `matchesConditions(...)` in `workflow-support.js`. + +Right now it only checks exact equality for keys in `conditions_json`. + +You could extend it for: + +- array inclusion, +- range matching, +- nested keys, +- custom predicates. + +## Minimal Usage Example + +### Export access + +```js +const { workflowEngine } = require("../../service/v1"); +``` + +### Auto assign a record + +```js +await new workflowEngine.Assignment().autoAssign({ + applicationType: "TICKET", + applicationId: ticketId, + context: { + departmentId: 12, + locationCode: "PATNA", + }, +}); +``` + +### Get allowed actions for current user + +```js +const actions = await new workflowEngine.WorkflowActions().allowedActions( + { + applicationType: "TICKET", + applicationId: ticketId, + }, + req.user, +); +``` + +### Perform a workflow action + +```js +await new workflowEngine.Workflow().takeAction( + { + applicationType: "TICKET", + applicationId: ticketId, + action: "ESCALATE", + remarks: "Escalating to next level", + context: { + departmentId: 12, + locationCode: "PATNA", + }, + }, + req.user, +); +``` + +### Get the timeline + +```js +const timeline = await new workflowEngine.Workflow().timeline({ + applicationType: "TICKET", + applicationId: ticketId, +}); +``` + +### Run auto escalation + +```js +const result = await new workflowEngine.AutoEscalationService().run({ + applicationType: "TICKET", + action: "ESCALATE", + batchSize: 25, +}); +``` + +## Mental Model Summary + +If you remember only one thing, remember this: + +- `assigner_rule` decides the first or automatic owner, +- `workflow_application_registry` tells the engine how to reach the business table, +- `assignment` stores current ownership, +- `workflow_action` stores history, +- `workflow_escalation_matrix` decides the next move, +- `Workflow.takeAction()` is the main engine, +- `AutoEscalationService.run()` is just an automated caller of the same engine. + +That is the whole design. diff --git a/service/v1/workflow-engine/assignee-resolver.js b/service/v1/workflow-engine/assignee-resolver.js new file mode 100644 index 0000000..1bcd216 --- /dev/null +++ b/service/v1/workflow-engine/assignee-resolver.js @@ -0,0 +1,146 @@ +const sequelize = require("../../../config/db"); +const { ErrorHandler, statusCodes } = require("../../../helper"); +const { + matchesStructuredValue, + resolveContextValue, + toModelNameCandidates, +} = require("./workflow-support"); + +const { SERVER_ERROR, BAD_GATEWAY } = statusCodes; + +class AssigneeResolver { + #normalize(spec = {}) { + if ( + spec && + typeof spec === "object" && + spec.resolver && + typeof spec.resolver === "object" + ) { + return { resolver: spec.resolver, context: spec.context || {} }; + } + + const resolverKey = spec?.resolverKey; + const context = spec?.context || {}; + if (!resolverKey) { + return { resolver: null, context }; + } + return { resolver: null, context }; + } + + async resolve(spec = {}, existingTx = null) { + const { resolver, context } = this.#normalize(spec); + if (!resolver) { + throw new ErrorHandler(BAD_GATEWAY, "resolver spec is required"); + } + + const exec = async transaction => { + const mode = String(resolver.mode || "").toUpperCase(); + if (mode === "STATIC") { + if ( + !resolver.assigneeType || + resolver.assigneeId === null || + resolver.assigneeId === undefined + ) { + throw new ErrorHandler( + BAD_GATEWAY, + "STATIC resolver requires assigneeType and assigneeId", + ); + } + + return { + assigneeType: String(resolver.assigneeType).toUpperCase(), + assigneeId: String(resolver.assigneeId), + }; + } + + if (mode !== "LOOKUP") { + throw new ErrorHandler(BAD_GATEWAY, `Unsupported resolver mode: ${resolver.mode}`); + } + + const model = + sequelize.models[ + toModelNameCandidates(resolver.table || resolver.model).find( + name => sequelize.models[name], + ) + ]; + if (!model) { + throw new ErrorHandler( + BAD_GATEWAY, + `Model not registered for resolver table: ${resolver.table}`, + ); + } + + const whereSpec = resolver?.where; + if (!whereSpec || typeof whereSpec !== "object") { + throw new ErrorHandler(BAD_GATEWAY, "LOOKUP resolver requires a where object"); + } + + const exactWhere = {}; + const structuredWhere = []; + for (const [field, specValue] of Object.entries(whereSpec)) { + const resolvedValue = resolveContextValue(specValue, context); + if (resolvedValue === null || resolvedValue === undefined) { + throw new ErrorHandler( + BAD_GATEWAY, + `Missing resolver value for field '${field}'`, + ); + } + + if ( + resolvedValue && + typeof resolvedValue === "object" && + !Array.isArray(resolvedValue) + ) { + structuredWhere.push({ field, value: resolvedValue }); + } else { + exactWhere[field] = resolvedValue; + } + } + + const rows = await model.findAll({ + where: exactWhere, + order: [["id", "ASC"]], + transaction, + }); + + const match = rows.find(row => + structuredWhere.every(({ field, value }) => + matchesStructuredValue(row[field], value), + ), + ); + + if (!match) { + throw new ErrorHandler( + BAD_GATEWAY, + `No assignee match found for resolver table: ${resolver.table}`, + ); + } + + const assigneeType = String(resolver.assigneeType || "USER").toUpperCase(); + const assigneeIdField = resolver.assigneeIdField || "id"; + if (match[assigneeIdField] === null || match[assigneeIdField] === undefined) { + throw new ErrorHandler( + BAD_GATEWAY, + `Resolver result field '${assigneeIdField}' is missing on table ${resolver.table}`, + ); + } + + return { assigneeType, assigneeId: String(match[assigneeIdField]) }; + }; + + try { + if (existingTx) { + return await exec(existingTx); + } + return await sequelize.transaction(exec); + } catch (error) { + console.error(error); + if (error.statusCode) { + throw error; + } + throw new ErrorHandler(SERVER_ERROR, error.message || error); + } + } +} + +module.exports = AssigneeResolver; diff --git a/service/v1/workflow-engine/assignment.js b/service/v1/workflow-engine/assignment.js new file mode 100644 index 0000000..5565bc4 --- /dev/null +++ b/service/v1/workflow-engine/assignment.js @@ -0,0 +1,266 @@ +const sequelize = require("../../../config/db"); +const { ErrorHandler, statusCodes } = require("../../../helper"); +const AssigneeResolver = require("./assignee-resolver"); +const { getWorkflowModels, matchesConditions, parseJson } = require("./workflow-support"); + +const { SERVER_ERROR, BAD_GATEWAY } = statusCodes; + +class Assignment { + async autoAssign( + { applicationType, applicationId, context = {} }, + user = null, + existingTx = null, + ) { + if (!applicationType || !applicationId) { + throw new ErrorHandler(BAD_GATEWAY, "applicationType and applicationId are required"); + } + + const exec = async transaction => { + const { AssignerRule } = getWorkflowModels(); + const rules = await AssignerRule.findAll({ + where: { + application_type: String(applicationType).toUpperCase(), + is_active: true, + }, + order: [ + ["priority", "ASC"], + ["id", "ASC"], + ], + transaction, + }); + + const matched = rules.find(rule => matchesConditions(rule.conditions_json, context)); + if (!matched) { + throw new ErrorHandler( + BAD_GATEWAY, + `No assigner rule configured for ${applicationType}`, + ); + } + + let assigneeType = matched.assignee_type; + let assigneeId = matched.assignee_id; + if (String(matched.assignee_mode || "STATIC").toUpperCase() === "CONTEXT") { + const resolved = await new AssigneeResolver().resolve( + matched.assignee_resolver_key + ? { + resolverKey: matched.assignee_resolver_key, + params: parseJson(matched.assignee_resolver_params), + context, + } + : { + resolver: parseJson(matched.assignee_resolver_params), + context, + }, + transaction, + ); + assigneeType = resolved.assigneeType; + assigneeId = resolved.assigneeId; + } + + return this.assign( + { + applicationType, + applicationId, + assigneeType, + assigneeId, + action: "AUTO_ASSIGNED", + toStage: "ASSIGNED", + }, + user, + transaction, + ); + }; + + try { + if (existingTx) { + return await exec(existingTx); + } + return await sequelize.transaction(exec); + } catch (error) { + console.error(error); + if (error.statusCode) { + throw error; + } + throw new ErrorHandler(SERVER_ERROR, error.message || error); + } + } + + async assign(payload, user = null, existingTx = null) { + const { + applicationType, + applicationId, + assigneeType, + assigneeId, + action = "ASSIGNED", + fromStage = null, + toStage = null, + remarks = null, + metadata = null, + logWorkflowAction = true, + } = payload; + + if (!applicationType || !applicationId || !assigneeType || !assigneeId) { + throw new ErrorHandler( + BAD_GATEWAY, + "applicationType, applicationId, assigneeType, assigneeId are required", + ); + } + + const exec = async transaction => { + const { Assignment: AssignmentModel, WorkflowAction } = getWorkflowModels(); + const currentRow = await AssignmentModel.findOne({ + where: { + application_type: String(applicationType).toUpperCase(), + application_id: String(applicationId), + status: "ACTIVE", + }, + order: [["id", "DESC"]], + transaction, + lock: true, + }); + + if (currentRow) { + currentRow.status = "CLOSED"; + currentRow.closed_at = new Date(); + await currentRow.save({ transaction }); + } + + const created = await AssignmentModel.create( + { + application_type: String(applicationType).toUpperCase(), + application_id: String(applicationId), + assignee_type: String(assigneeType).toUpperCase(), + assignee_id: String(assigneeId), + assigned_by: user?.userId || null, + }, + { transaction }, + ); + + if (logWorkflowAction) { + await WorkflowAction.create( + { + application_type: String(applicationType).toUpperCase(), + application_id: String(applicationId), + action: String(action).toUpperCase(), + from_assignee_type: currentRow?.assignee_type || null, + from_assignee_id: currentRow?.assignee_id || null, + to_assignee_type: String(assigneeType).toUpperCase(), + to_assignee_id: String(assigneeId), + from_stage: fromStage, + to_stage: toStage, + remarks, + metadata_json: metadata, + actor_user_id: user?.userId || null, + }, + { transaction }, + ); + } + + return { assignmentId: created.id }; + }; + + try { + if (existingTx) { + return await exec(existingTx); + } + return await sequelize.transaction(exec); + } catch (error) { + console.error(error); + if (error.statusCode) { + throw error; + } + throw new ErrorHandler(SERVER_ERROR, error.message || error); + } + } + + async getCurrent({ applicationType, applicationId }, existingTx = null) { + if (!applicationType || !applicationId) { + throw new ErrorHandler(BAD_GATEWAY, "applicationType and applicationId are required"); + } + + try { + const { Assignment: AssignmentModel } = getWorkflowModels(); + return await AssignmentModel.findOne({ + where: { + application_type: String(applicationType).toUpperCase(), + application_id: String(applicationId), + status: "ACTIVE", + }, + order: [["id", "DESC"]], + transaction: existingTx || undefined, + }); + } catch (error) { + console.error(error); + if (error.statusCode) { + throw error; + } + throw new ErrorHandler(SERVER_ERROR, error.message || error); + } + } + + async closeCurrent( + { applicationType, applicationId, currentStage = null, toStage = null, remarks = null }, + user = null, + existingTx = null, + ) { + if (!applicationType || !applicationId) { + throw new ErrorHandler(BAD_GATEWAY, "applicationType and applicationId are required"); + } + + const exec = async transaction => { + const { Assignment: AssignmentModel, WorkflowAction } = getWorkflowModels(); + const row = await AssignmentModel.findOne({ + where: { + application_type: String(applicationType).toUpperCase(), + application_id: String(applicationId), + status: "ACTIVE", + }, + order: [["id", "DESC"]], + transaction, + lock: true, + }); + + if (!row) { + return { closed: false }; + } + + row.status = "CLOSED"; + row.closed_at = new Date(); + await row.save({ transaction }); + + await WorkflowAction.create( + { + application_type: String(applicationType).toUpperCase(), + application_id: String(applicationId), + action: "CLOSED", + from_assignee_type: row.assignee_type, + from_assignee_id: row.assignee_id, + to_assignee_type: null, + to_assignee_id: null, + from_stage: currentStage, + to_stage: toStage, + remarks, + actor_user_id: user?.userId || null, + }, + { transaction }, + ); + + return { closed: true }; + }; + + try { + if (existingTx) { + return await exec(existingTx); + } + return await sequelize.transaction(exec); + } catch (error) { + console.error(error); + if (error.statusCode) { + throw error; + } + throw new ErrorHandler(SERVER_ERROR, error.message || error); + } + } +} + +module.exports = Assignment; diff --git a/service/v1/workflow-engine/auto-escalation.js b/service/v1/workflow-engine/auto-escalation.js new file mode 100644 index 0000000..5842a3f --- /dev/null +++ b/service/v1/workflow-engine/auto-escalation.js @@ -0,0 +1,140 @@ +const sequelize = require("../../../config/db"); +const { ErrorHandler, statusCodes } = require("../../../helper"); +const AssignmentService = require("./assignment"); +const Workflow = require("./workflow"); +const EscalationMatrix = require("./escalation-matrix"); +const { getApplicationAdapter, getWorkflowModels } = require("./workflow-support"); + +const { SERVER_ERROR, BAD_GATEWAY } = statusCodes; + +class AutoEscalationService { + async run(opts = {}) { + if (!opts.applicationType) { + throw new ErrorHandler(BAD_GATEWAY, "applicationType is required"); + } + + const applicationType = String(opts.applicationType).toUpperCase(); + const action = String(opts.action || "ESCALATE").toUpperCase(); + const batchSize = Number.isFinite(opts.batchSize) ? Number(opts.batchSize) : 25; + + try { + const candidates = await this.#findCandidates({ applicationType, action, batchSize }); + let escalated = 0; + let skipped = 0; + const errors = []; + + for (const candidate of candidates) { + try { + const ok = await this.#processOne({ + applicationType, + applicationId: candidate.applicationId, + assignmentId: candidate.assignmentId, + action, + }); + if (ok) { + escalated++; + } else { + skipped++; + } + } catch (error) { + errors.push({ + applicationId: candidate.applicationId, + message: error?.message || String(error), + }); + console.error("auto-escalation error", error); + } + } + + return { scanned: candidates.length, escalated, skipped, errors }; + } catch (error) { + console.error(error); + if (error.statusCode) { + throw error; + } + throw new ErrorHandler(SERVER_ERROR, error.message || error); + } + } + + async #findCandidates({ applicationType, action, batchSize }) { + const adapter = await getApplicationAdapter(applicationType); + + const assignments = await getWorkflowModels().Assignment.findAll({ + where: { + application_type: applicationType, + status: "ACTIVE", + }, + order: [["assigned_at", "ASC"]], + limit: batchSize * 4, + }); + + const candidates = []; + for (const assignment of assignments) { + if (candidates.length >= batchSize) { + break; + } + + const record = await adapter.model.findOne({ + where: { [adapter.idField]: String(assignment.application_id) }, + }); + if (!record) { + continue; + } + if (String(record[adapter.currentAssignmentField] || "") !== String(assignment.id)) { + continue; + } + + const matrix = await new EscalationMatrix().resolve({ + applicationType, + currentStage: record[adapter.stageField], + action, + currentAssigneeType: assignment.assignee_type, + currentAssigneeId: assignment.assignee_id, + }); + if (!matrix?.auto_trigger || !matrix.auto_after_minutes) { + continue; + } + + const dueAt = + new Date(assignment.assigned_at).getTime() + + Number(matrix.auto_after_minutes) * 60 * 1000; + if (Date.now() < dueAt) { + continue; + } + + candidates.push({ + applicationId: assignment.application_id, + assignmentId: assignment.id, + matrixId: matrix.id, + }); + } + + return candidates; + } + + async #processOne({ applicationType, applicationId, assignmentId, action }) { + return sequelize.transaction(async transaction => { + const current = await new AssignmentService().getCurrent( + { applicationType, applicationId }, + transaction, + ); + if (!current || String(current.id) !== String(assignmentId)) { + return false; + } + + await new Workflow().takeAction( + { + applicationType, + applicationId, + action, + remarks: "auto escalated", + }, + { isAuth: true, userId: null, role: null }, + { bypassOwnershipCheck: true, transaction }, + ); + + return true; + }); + } +} + +module.exports = AutoEscalationService; diff --git a/service/v1/workflow-engine/escalation-matrix.js b/service/v1/workflow-engine/escalation-matrix.js new file mode 100644 index 0000000..6dfed9b --- /dev/null +++ b/service/v1/workflow-engine/escalation-matrix.js @@ -0,0 +1,55 @@ +const { ErrorHandler, statusCodes } = require("../../../helper"); +const { getWorkflowModels } = require("./workflow-support"); + +const { SERVER_ERROR, BAD_GATEWAY } = statusCodes; + +class EscalationMatrix { + async resolve( + { applicationType, currentStage, action, currentAssigneeType, currentAssigneeId }, + existingTx = null, + ) { + if (!applicationType || !currentStage || !action) { + throw new ErrorHandler( + BAD_GATEWAY, + "applicationType, currentStage and action are required", + ); + } + + try { + const { WorkflowEscalationMatrix } = getWorkflowModels(); + const rows = await WorkflowEscalationMatrix.findAll({ + where: { + application_type: String(applicationType).toUpperCase(), + current_stage: currentStage, + action: String(action).toUpperCase(), + is_active: true, + }, + order: [ + ["priority", "ASC"], + ["id", "ASC"], + ], + transaction: existingTx || undefined, + }); + + return ( + rows.find(row => { + if (!row.current_assignee_type && !row.current_assignee_id) { + return true; + } + return ( + String(row.current_assignee_type) === String(currentAssigneeType) && + String(row.current_assignee_id) === String(currentAssigneeId) + ); + }) || null + ); + } catch (error) { + console.error(error); + if (error.statusCode) { + throw error; + } + throw new ErrorHandler(SERVER_ERROR, error.message || error); + } + } +} + +module.exports = EscalationMatrix; diff --git a/service/v1/workflow-engine/index.js b/service/v1/workflow-engine/index.js new file mode 100644 index 0000000..65a0911 --- /dev/null +++ b/service/v1/workflow-engine/index.js @@ -0,0 +1,8 @@ +module.exports = { + Assignment: require("./assignment"), + AssigneeResolver: require("./assignee-resolver"), + AutoEscalationService: require("./auto-escalation"), + EscalationMatrix: require("./escalation-matrix"), + Workflow: require("./workflow"), + WorkflowActions: require("./workflow-actions"), +}; diff --git a/service/v1/workflow-engine/workflow-actions.js b/service/v1/workflow-engine/workflow-actions.js new file mode 100644 index 0000000..58481ec --- /dev/null +++ b/service/v1/workflow-engine/workflow-actions.js @@ -0,0 +1,93 @@ +const { ErrorHandler, statusCodes } = require("../../../helper"); +const AssignmentService = require("./assignment"); +const { getApplicationAdapter, getWorkflowModels, parseJson } = require("./workflow-support"); + +const { SERVER_ERROR, BAD_GATEWAY, UNAUTHORIZED } = statusCodes; + +class WorkflowActions { + async allowedActions({ applicationType, applicationId }, user) { + if (!applicationType || !applicationId) { + throw new ErrorHandler(BAD_GATEWAY, "applicationType and applicationId are required"); + } + if (!user || user.isAuth !== true) { + throw new ErrorHandler(UNAUTHORIZED, "Unauthorized"); + } + + try { + const adapter = await getApplicationAdapter(applicationType); + const record = await adapter.model.findOne({ + where: { [adapter.idField]: String(applicationId) }, + }); + if (!record?.[adapter.stageField]) { + return []; + } + + const current = await new AssignmentService().getCurrent({ + applicationType, + applicationId, + }); + if (!current) { + return []; + } + + const isMine = + (current.assignee_type === "USER" && + String(current.assignee_id) === String(user.userId || "")) || + (current.assignee_type === "ROLE" && + String(current.assignee_id) === String(user.role || "")); + if (!isMine) { + throw new ErrorHandler(UNAUTHORIZED, "Unauthorized"); + } + + const { WorkflowEscalationMatrix } = getWorkflowModels(); + const rows = await WorkflowEscalationMatrix.findAll({ + where: { + application_type: String(applicationType).toUpperCase(), + current_stage: record[adapter.stageField], + is_active: true, + }, + order: [ + ["priority", "ASC"], + ["id", "ASC"], + ], + }); + + const seen = new Set(); + return rows + .filter(row => { + const assigneeMatch = + (!row.current_assignee_type && !row.current_assignee_id) || + (String(row.current_assignee_type) === String(current.assignee_type) && + String(row.current_assignee_id) === String(current.assignee_id)); + if (!assigneeMatch || seen.has(row.action)) { + return false; + } + seen.add(row.action); + return true; + }) + .map(row => ({ + action: row.action, + next_stage: row.next_stage, + next_assignee_type: row.next_assignee_type, + next_assignee_id: row.next_assignee_id, + next_assignee_mode: row.next_assignee_mode, + next_assignee_resolver_key: row.next_assignee_resolver_key, + next_assignee_resolver_params: row.next_assignee_resolver_params, + nextAssigneeResolver: row.next_assignee_resolver_key + ? { + resolverKey: row.next_assignee_resolver_key, + params: parseJson(row.next_assignee_resolver_params), + } + : parseJson(row.next_assignee_resolver_params), + })); + } catch (error) { + console.error(error); + if (error.statusCode) { + throw error; + } + throw new ErrorHandler(SERVER_ERROR, error.message || error); + } + } +} + +module.exports = WorkflowActions; diff --git a/service/v1/workflow-engine/workflow-support.js b/service/v1/workflow-engine/workflow-support.js new file mode 100644 index 0000000..2a2f8bc --- /dev/null +++ b/service/v1/workflow-engine/workflow-support.js @@ -0,0 +1,180 @@ +const { ErrorHandler, statusCodes } = require("../../../helper"); +const sequelize = require("../../../config/db"); + +const { BAD_GATEWAY } = statusCodes; + +const parseJson = value => { + if (value === null || value === undefined) { + return null; + } + if (typeof value === "string") { + try { + return JSON.parse(value); + } catch { + return null; + } + } + return value; +}; + +const getModel = names => { + for (const name of names) { + if (sequelize.models[name]) { + return sequelize.models[name]; + } + } + return null; +}; + +const getWorkflowModels = () => { + const models = { + AssignerRule: getModel(["assigner_rule"]), + Assignment: getModel(["assignment"]), + WorkflowAction: getModel(["workflow_action"]), + WorkflowApplicationRegistry: getModel(["workflow_application_registry"]), + WorkflowEscalationMatrix: getModel(["workflow_escalation_matrix"]), + }; + + for (const [key, model] of Object.entries(models).filter(([name]) => + [ + "AssignerRule", + "Assignment", + "WorkflowAction", + "WorkflowApplicationRegistry", + "WorkflowEscalationMatrix", + ].includes(name), + )) { + if (!model) { + throw new ErrorHandler(BAD_GATEWAY, `${key} model is not registered`); + } + } + + return models; +}; + +const toModelNameCandidates = tableOrModelName => { + if (!tableOrModelName) { + return []; + } + + const base = String(tableOrModelName); + const pascal = base + .split(/[_-\s]+/) + .filter(Boolean) + .map(part => part.charAt(0).toUpperCase() + part.slice(1)) + .join(""); + + return Array.from(new Set([base, base.toLowerCase(), pascal])); +}; + +const getApplicationAdapter = async (applicationType, transaction = null) => { + const type = String(applicationType || "").toUpperCase(); + const { WorkflowApplicationRegistry } = getWorkflowModels(); + const config = await WorkflowApplicationRegistry.findOne({ + where: { + application_type: type, + is_active: true, + }, + order: [["id", "DESC"]], + transaction: transaction || undefined, + }); + + if (!config) { + throw new ErrorHandler(BAD_GATEWAY, `Unsupported applicationType: ${applicationType}`); + } + + const model = getModel(toModelNameCandidates(config.model_name)); + if (!model) { + throw new ErrorHandler( + BAD_GATEWAY, + `Model not registered for applicationType: ${applicationType}`, + ); + } + + return { + type, + model, + idField: config.id_field, + stageField: config.stage_field, + currentAssignmentField: config.current_assignment_field, + }; +}; + +const matchesConditions = (conditions, context) => { + const parsed = parseJson(conditions); + if (!parsed || typeof parsed !== "object") { + return true; + } + + return Object.entries(parsed).every(([key, value]) => context?.[key] === value); +}; + +const matchesMapping = (mappingValue, expected) => { + if (Array.isArray(mappingValue)) { + return mappingValue.some(entry => matchesMapping(entry, expected)); + } + + if (!mappingValue || typeof mappingValue !== "object") { + return false; + } + + return ( + String(mappingValue.key) === String(expected.key) && + String(mappingValue.table) === String(expected.table) && + String(mappingValue.value) === String(expected.value) + ); +}; + +const resolveContextValue = (valueSpec, context) => { + if (valueSpec && typeof valueSpec === "object" && !Array.isArray(valueSpec)) { + if (Object.prototype.hasOwnProperty.call(valueSpec, "valueFromContext")) { + const key = String(valueSpec.valueFromContext); + return ( + context?.[key] ?? + context?.[key.replace(/[A-Z]/g, match => `_${match.toLowerCase()}`)] ?? + null + ); + } + + if (Object.prototype.hasOwnProperty.call(valueSpec, "value")) { + return valueSpec.value; + } + + const out = {}; + for (const [key, value] of Object.entries(valueSpec)) { + out[key] = resolveContextValue(value, context); + } + return out; + } + + return valueSpec; +}; + +const matchesStructuredValue = (actual, expected) => { + if (Array.isArray(actual)) { + return actual.some(entry => matchesStructuredValue(entry, expected)); + } + + if (expected && typeof expected === "object" && !Array.isArray(expected)) { + if (!actual || typeof actual !== "object") { + return false; + } + + return Object.entries(expected).every(([key, value]) => + matchesStructuredValue(actual[key], value), + ); + } + + return String(actual) === String(expected); +}; + +module.exports = { + parseJson, + getWorkflowModels, + getApplicationAdapter, + toModelNameCandidates, + matchesConditions, + matchesMapping, + resolveContextValue, + matchesStructuredValue, +}; diff --git a/service/v1/workflow-engine/workflow.js b/service/v1/workflow-engine/workflow.js new file mode 100644 index 0000000..9291f88 --- /dev/null +++ b/service/v1/workflow-engine/workflow.js @@ -0,0 +1,195 @@ +const sequelize = require("../../../config/db"); +const { ErrorHandler, statusCodes } = require("../../../helper"); +const AssignmentService = require("./assignment"); +const AssigneeResolver = require("./assignee-resolver"); +const EscalationMatrix = require("./escalation-matrix"); +const { getApplicationAdapter, getWorkflowModels, parseJson } = require("./workflow-support"); + +const { SERVER_ERROR, BAD_GATEWAY, UNAUTHORIZED } = statusCodes; + +class Workflow { + async timeline({ applicationType, applicationId, filterAssignedToMe = false }, user = null) { + if (!applicationType || !applicationId) { + throw new ErrorHandler(BAD_GATEWAY, "applicationType and applicationId are required"); + } + + try { + const { WorkflowAction } = getWorkflowModels(); + if (filterAssignedToMe) { + if (!user || user.isAuth !== true) { + throw new ErrorHandler(UNAUTHORIZED, "Unauthorized"); + } + + const current = await new AssignmentService().getCurrent({ + applicationType, + applicationId, + }); + const isMine = + current && + ((current.assignee_type === "USER" && + String(current.assignee_id) === String(user.userId || "")) || + (current.assignee_type === "ROLE" && + String(current.assignee_id) === String(user.role || ""))); + if (!isMine) { + throw new ErrorHandler(UNAUTHORIZED, "Unauthorized"); + } + } + + return WorkflowAction.findAll({ + where: { + application_type: String(applicationType).toUpperCase(), + application_id: String(applicationId), + }, + order: [["id", "ASC"]], + }); + } catch (error) { + console.error(error); + if (error.statusCode) { + throw error; + } + throw new ErrorHandler(SERVER_ERROR, error.message || error); + } + } + + async takeAction( + { applicationType, applicationId, action, remarks = null, context = {} }, + user = null, + opts = {}, + ) { + if (!applicationType || !applicationId || !action) { + throw new ErrorHandler( + BAD_GATEWAY, + "applicationType, applicationId and action are required", + ); + } + + const exec = async transaction => { + const adapter = await getApplicationAdapter(applicationType, transaction); + const record = await adapter.model.findOne({ + where: { [adapter.idField]: String(applicationId) }, + transaction, + lock: true, + }); + if (!record) { + throw new ErrorHandler(BAD_GATEWAY, `${applicationType} not found`); + } + + const currentAssignment = await new AssignmentService().getCurrent( + { applicationType, applicationId }, + transaction, + ); + if (!currentAssignment) { + throw new ErrorHandler(BAD_GATEWAY, "No active assignment found"); + } + + if (!opts.bypassOwnershipCheck) { + if (!user || user.isAuth !== true) { + throw new ErrorHandler(UNAUTHORIZED, "Unauthorized"); + } + + const isMine = + (currentAssignment.assignee_type === "USER" && + String(currentAssignment.assignee_id) === String(user.userId || "")) || + (currentAssignment.assignee_type === "ROLE" && + String(currentAssignment.assignee_id) === String(user.role || "")); + if (!isMine) { + throw new ErrorHandler(UNAUTHORIZED, "Unauthorized"); + } + } + + const matrix = await new EscalationMatrix().resolve( + { + applicationType, + currentStage: record[adapter.stageField], + action, + currentAssigneeType: currentAssignment.assignee_type, + currentAssigneeId: currentAssignment.assignee_id, + }, + transaction, + ); + if (!matrix) { + throw new ErrorHandler( + BAD_GATEWAY, + `No workflow transition configured for ${applicationType}/${action}`, + ); + } + + const nextStage = matrix.next_stage ?? record[adapter.stageField]; + let assignmentId = record[adapter.currentAssignmentField]; + if (String(matrix.next_assignee_mode || "STATIC").toUpperCase() === "CONTEXT") { + const resolved = await new AssigneeResolver().resolve( + matrix.next_assignee_resolver_key + ? { + resolverKey: matrix.next_assignee_resolver_key, + params: parseJson(matrix.next_assignee_resolver_params), + context, + } + : { + resolver: parseJson(matrix.next_assignee_resolver_params), + context, + }, + transaction, + ); + matrix.next_assignee_type = resolved.assigneeType; + matrix.next_assignee_id = resolved.assigneeId; + } + + if (matrix.next_assignee_type && matrix.next_assignee_id) { + const assigned = await new AssignmentService().assign( + { + applicationType, + applicationId, + assigneeType: matrix.next_assignee_type, + assigneeId: matrix.next_assignee_id, + action, + fromStage: record[adapter.stageField], + toStage: nextStage, + remarks, + }, + user, + transaction, + ); + assignmentId = assigned.assignmentId; + } else { + await new AssignmentService().closeCurrent( + { + applicationType, + applicationId, + currentStage: record[adapter.stageField], + toStage: nextStage, + remarks, + }, + user, + transaction, + ); + assignmentId = null; + } + + record[adapter.stageField] = nextStage; + record[adapter.currentAssignmentField] = assignmentId; + await record.save({ transaction }); + + return { + applicationId: String(applicationId), + action: String(action).toUpperCase(), + nextStage, + currentAssignmentId: assignmentId, + }; + }; + + try { + if (opts.transaction) { + return await exec(opts.transaction); + } + return await sequelize.transaction(exec); + } catch (error) { + console.error(error); + if (error.statusCode) { + throw error; + } + throw new ErrorHandler(SERVER_ERROR, error.message || error); + } + } +} + +module.exports = Workflow; diff --git a/utils/index.js b/utils/index.js index 0c9c276..c62a145 100644 --- a/utils/index.js +++ b/utils/index.js @@ -5,7 +5,7 @@ const { camelize } = require("./helper"); const { getDate, addDate, updateFormat } = require("./time"); const { compare, hashPassword } = require("./hash"); const { generateOtp, messenger } = require("./message"); -const { copyFiles, formidableUpload, uploadDocument, scanFile } = require("./upload"); +const { fileUpload, fileDownload } = require("./upload"); const { sendMail } = require("./mail"); module.exports = { @@ -21,10 +21,8 @@ module.exports = { hashPassword, addDate, generateOtp, - copyFiles, sendMail, messenger, - formidableUpload, - uploadDocument, - scanFile, + fileUpload, + fileDownload, }; diff --git a/utils/mail.js b/utils/mail.js index 9641ed9..edf96d4 100644 --- a/utils/mail.js +++ b/utils/mail.js @@ -1,14 +1,27 @@ const nodemailer = require("nodemailer"); +const { createTransport } = require("@codebucket/mail-transport"); -const transporter = nodemailer.createTransport({ - host: process.env.EMAIL_HOST, - port: process.env.EMAIL_PORT, - secure: false, - auth: { - user: process.env.EMAIL_USER, - pass: process.env.EMAIL_PASSWORD, - }, -}); +let transporter = null; + +if (process.env.EMAIL_TRANSPORT === "SMTP") { + transporter = nodemailer.createTransport({ + host: process.env.EMAIL_HOST, + port: process.env.EMAIL_PORT, + secure: false, + auth: { + user: process.env.EMAIL_USER, + pass: process.env.EMAIL_PASSWORD, + }, + }); +} else { + transporter = nodemailer.createTransport( + createTransport({ + url: process.env.MAILSERVER_URL, + senderId: process.env.MAILSERVER_SENDERID, + accessToken: process.env.MAILSERVER_ACCESSTOKEN, + }), + ); +} transporter.verify(error => { if (error) { @@ -22,7 +35,7 @@ module.exports = { sendMail: async (recipient, subject, body, attachments = []) => { try { const options = { - from: `"Govt. Of Bihar" ${process.env.EMAIL_USER}`, // sender address + from: `${process.env.EMAIL_USER}`, // sender address to: recipient, // list of receivers priority: "high", subject, diff --git a/utils/message.js b/utils/message.js index dff3fa8..12fe785 100644 --- a/utils/message.js +++ b/utils/message.js @@ -1,39 +1,58 @@ const axios = require("axios"); +const { SmsSender, SmsServerProvider } = require("@codebucket/sms"); const { PASSWORDSMS, ENDPOINT, KEY, SENDERID, USERNAMESMS } = process.env; const { encryptedPassword, hashGenerator } = require("./message-helper"); module.exports = { - messenger: async (message, phone, DLT) => { + messenger: async (message, phone, DLT, otp) => { try { - const encPassword = encryptedPassword(PASSWORDSMS), - templateId = DLT; + if (process.env.SMS_TRANSPORT === "SERVER") { + const smsSender = new SmsSender( + new SmsServerProvider({ + smsServerUrl: process.env.SMS_SERVER_URL, + senderId: process.env.SMS_SENDER_ID, + accessToken: process.env.SMS_ACCESS_TOKEN, + }), + ); - const finalMessage = message.trim(); + const variables = [String(otp)]; - const hash = await hashGenerator(USERNAMESMS, SENDERID, finalMessage, KEY); + await smsSender.send({ + type: "template", + to: [`91${phone}`], + templateId: DLT, + variables, + }); - const query = `username=${USERNAMESMS}&password=${encPassword}&smsservicetype=otpmsg&content=${finalMessage}&mobileno=${phone}&senderid=${SENDERID}&key=${hash}&templateid=${templateId}`; + console.log(message); + } else { + const encPassword = encryptedPassword(PASSWORDSMS); + const templateId = DLT; + const finalMessage = message.trim(); + const hash = await hashGenerator(USERNAMESMS, SENDERID, finalMessage, KEY); + const query = `username=${USERNAMESMS}&password=${encPassword}&smsservicetype=otpmsg&content=${finalMessage}&mobileno=${phone}&senderid=${SENDERID}&key=${hash}&templateid=${templateId}`; - axios - .post(ENDPOINT, query) - .then(async resp => { - console.log(resp.data); - }) - .catch(error => { - console.error(error); - throw error; - }); + axios + .post(ENDPOINT, query) + .then(async resp => { + console.log(resp.data); + }) + .catch(error => { + console.error(error); + throw error; + }); + } } catch (error) { console.log(error); return 0; } }, - generateOtp: (lenght = 4) => { + generateOtp: (length = 4) => { try { const string = "123456789"; let OTP = ""; const len = string.length; - for (let i = 0; i < lenght; i++) { + for (let i = 0; i < length; i++) { OTP += string[Math.floor(Math.random() * len)]; } return OTP; @@ -42,4 +61,20 @@ module.exports = { throw error; } }, + govtMessage: async (message, phone, DLT) => { + const encPassword = encryptedPassword(PASSWORDSMS); + const templateId = DLT; + const hash = await hashGenerator(USERNAMESMS, SENDERID, message.trim(), KEY); + const query = `username=${USERNAMESMS}&password=${encPassword}&smsservicetype=otpmsg&content=${message.trim()}&mobileno=${phone}&senderid=${SENDERID}&key=${hash}&templateid=${templateId}`; + + axios + .post(ENDPOINT, query) + .then(async resp => { + console.log(resp.data); + }) + .catch(error => { + console.error(error); + throw error; + }); + }, }; diff --git a/utils/upload.js b/utils/upload.js index 1eb9d25..d3550b6 100644 --- a/utils/upload.js +++ b/utils/upload.js @@ -1,71 +1,179 @@ -const util = require("util"); const fs = require("fs"); -const formidable = require("formidable"); -const copyFilePromise = util.promisify(fs.copyFile); -const NodeClam = require("clamscan"); - -const scanFile = async filePath => { - if (process.env.VIRUS_CHECKER === "OFF") { - return true; - } - const clamscan = await new NodeClam().init({ - remove_infected: true, - debug_mode: false, - scan_recursively: false, - clamdscan: { - socket: process.env.CLAMDSCAN_SOCKET || "/var/run/clamav/clamd.ctl", - timeout: 120000, - local_fallback: true, - path: process.env.CLAMDSCAN_PATH || "/var/lib/clamav", - config_file: process.env.CLAMDSCAN_CONFIG_FILE || "/etc/clamav/clamd.conf", +const path = require("path"); +const { Files } = require("@codebucket/files"); +const { SUCCESS } = require("./constant"); + +let filesConfig; +if (process.env.UPLOAD_SERVER === "S3") { + filesConfig = { + type: "s3", + publicBaseUrl: `${process.env.BASEURL}/files`, + bucketName: process.env.S3_BUCKET_NAME, + endpoint: process.env.S3_ENDPOINT, + s3Config: { + region: process.env.S3_REGION, + credentials: { + accessKeyId: process.env.S3_ACCESS_KEY_ID, + secretAccessKey: process.env.S3_SECRET_ACCESS_KEY, + }, }, - }); + }; +} else { + filesConfig = { + type: "filesystem", + publicBaseUrl: `${process.env.BASEURL}/files`, + baseDir: "./public/uploads", + }; +} - const { is_infected, viruses } = await clamscan.scan_file(filePath); +const fileStorage = new Files(filesConfig); - if (is_infected) { - console.error(`Virus scan failed, file INFECTED`, { filePath, viruses }); - } else { - console.log(`Virus scan OK`, { filePath }); - } +module.exports = { + fileUpload: async (files, body, _key) => { + let filePath; + try { + filePath = files?.path; + const fileName = + new Date() + .toISOString() + .replace(/:/g, "-") + .replace(/[^a-z0-9]/gi, "_") + .toLowerCase() + path.extname(files.originalname); - return is_infected; -}; + await fileStorage.upload( + `/${process.env.prefix}/${body.userId}/${fileName}`, + fs.readFileSync(files.path), + ); -const copyFiles = (srcDir, destDir, dir) => { - if (!fs.existsSync(dir)) { - fs.mkdirSync(dir, { recursive: true }); - } - return copyFilePromise(srcDir, destDir); -}; + const url = fileStorage.getPublicUrl( + `/${process.env.prefix}/${body.userId}/${fileName}`, + ); + + return { + message: SUCCESS, + url, + name: fileName, + }; + } catch (error) { + console.error(error); + } finally { + if (filePath) { + fs.promises.unlink(filePath).catch(() => { + // ignore cleanup errors + }); + } + } + }, + fileDownload: async (req, res) => { + let fullPath = req.path; + fullPath = fullPath.replace("/files", ""); + + const normalized = path.posix.normalize(fullPath).replace(/\\/g, "/"); + + if ( + normalized.includes("..") || + !normalized.startsWith("/") || + (normalized.length && normalized.includes("\0")) + ) { + return res.status(400).json({ message: "Invalid file path" }); + } + + const safeName = path.posix.basename(normalized) || "download"; + const forcedDisposition = `attachment; filename="${safeName.replace(/[^a-zA-Z0-9._-]/g, "_")}"; filename*=UTF-8''${encodeURIComponent(safeName)}`; + + const originalSetHeader = res.setHeader.bind(res); + res.setHeader = (name, value) => { + if (String(name).toLowerCase() === "content-disposition") { + return originalSetHeader("Content-Disposition", forcedDisposition); + } + return originalSetHeader(name, value); + }; -const formidableUpload = async req => { - try { - const form = new formidable.IncomingForm(); - console.log(form.parse); - form.keepExtensions = true; - form.on("error", e => console.log(e)); - form.on("aborted", () => console.log("aborted")); - form.multiples = true; - const formfields = await new Promise((resolve, reject) => { - form.parse(req, (err, fields, files) => { - if (err) { - reject(new Error("Form Not parsed ")); - return; + const originalWriteHead = res.writeHead; + res.writeHead = function patchedWriteHead(statusCode, statusMessage, headers) { + try { + let actualStatusMessage = statusMessage; + let actualHeaders = headers; + if (typeof actualStatusMessage === "object" && actualHeaders === undefined) { + actualHeaders = actualStatusMessage; + actualStatusMessage = undefined; } - resolve({ fields, files }); - }); // form.parse - }); + res.setHeader("X-Content-Type-Options", "nosniff"); + res.setHeader("Content-Disposition", forcedDisposition); + res.setHeader("Referrer-Policy", "no-referrer"); + res.setHeader("Cross-Origin-Resource-Policy", "same-site"); + res.setHeader( + "Content-Security-Policy", + "default-src 'none'; sandbox; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; object-src 'none'", + ); + res.setHeader("Cache-Control", "no-store, max-age=0"); + res.setHeader("Pragma", "no-cache"); - return formfields; - } catch (error) { - console.error("Error in formidable upload:", error.message); - } -}; + if (actualHeaders && typeof actualHeaders === "object") { + actualHeaders["Content-Disposition"] = forcedDisposition; + actualHeaders["X-Content-Type-Options"] = "nosniff"; + } -module.exports = { - formidableUpload, - scanFile, - copyFiles, + return actualStatusMessage !== undefined + ? originalWriteHead.call(this, statusCode, actualStatusMessage, actualHeaders) + : originalWriteHead.call(this, statusCode, actualHeaders); + } catch { + return originalWriteHead.apply(this, arguments); + } + }; + + if (!res.headersSent) { + res.setHeader("X-Content-Type-Options", "nosniff"); + res.setHeader("Content-Disposition", forcedDisposition); + res.setHeader("Referrer-Policy", "no-referrer"); + res.setHeader("Cross-Origin-Resource-Policy", "same-site"); + res.setHeader( + "Content-Security-Policy", + "default-src 'none'; sandbox; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; object-src 'none'", + ); + res.setHeader("Cache-Control", "no-store, max-age=0"); + res.setHeader("Pragma", "no-cache"); + } + + try { + console.log("[fileDownload]", { normalized }); + await fileStorage.download(normalized, res); + + if (!res.headersSent) { + process.nextTick(() => { + try { + if (!res.headersSent) { + res.setHeader("Content-Disposition", forcedDisposition); + } + } catch { + // ignore + } + }); + } + } catch (err) { + console.error("[fileDownload] download failed", { + normalized, + err: err?.message || err, + }); + + if (res.headersSent) { + try { + res.end(); + } catch { + // ignore + } + return; + } + + const msg = String(err?.message || ""); + const isNotFound = /not\s*found|no\s*such|404|key/i.test(msg); + return res.status(isNotFound ? 404 : 502).json({ + message: isNotFound ? "File not found" : "Unable to download file", + }); + } finally { + res.setHeader = originalSetHeader; + res.writeHead = originalWriteHead; + } + }, };