diff --git a/.cursorrules b/.cursorrules new file mode 100644 index 0000000..507c062 --- /dev/null +++ b/.cursorrules @@ -0,0 +1,14 @@ +# Collection Module Template Project Rules + +## Architecture Context + +For every conversation about code architecture, building features, or refactoring, load the Collection Module architecture patterns: + +stripe_collection_module/.cursor/mcps/collection-module.mcp + +This file contains the architectural patterns for: +- Services, Controllers, Clients, and Adapters +- Dependency Injection patterns +- Logging patterns +- Testing patterns +- Error handling patterns diff --git a/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md similarity index 100% rename from PULL_REQUEST_TEMPLATE.md rename to .github/PULL_REQUEST_TEMPLATE.md diff --git a/.github/workflows/README.md b/.github/workflows/README.md new file mode 100644 index 0000000..42694dc --- /dev/null +++ b/.github/workflows/README.md @@ -0,0 +1,179 @@ +# GitHub Actions Workflows + +This directory contains automated CI/CD workflows for the Stripe Collection Module. + +## Available Workflows + +### CI Workflow (`ci.yml`) + +**Triggers:** +- On push to `main`, `master`, or `develop` branches +- On pull requests to `main`, `master`, or `develop` branches + +**What it does:** + +#### Test & Lint Job +1. **Checkout** - Gets the code +2. **Setup Node.js** - Installs Node.js 18.x with npm caching +3. **Install** - Runs `npm ci` for clean dependency install +4. **Lint** - Runs ESLint to check code quality +5. **Test** - Runs Jest tests with coverage +6. **Upload Coverage** - Sends coverage to Codecov (optional) +7. **Check Thresholds** - Verifies 70% coverage threshold +8. **Comment PR** - Posts coverage report as PR comment + +#### Build Job +1. **Checkout** - Gets the code +2. **Setup Node.js** - Installs Node.js 18.x +3. **Install** - Runs `npm ci` +4. **Build** - Compiles TypeScript to JavaScript +5. **Verify** - Checks that build artifacts were created + +## Viewing Results + +### On Pull Requests + +When you create a PR, the CI workflow will: +1. โœ… Run all tests and show results +2. ๐Ÿ“Š Post coverage report as a comment +3. ๐Ÿ” Run linter and flag issues +4. ๐Ÿ—๏ธ Verify the build succeeds + +### Status Badges + +Add to your README: + +```markdown +[![CI](https://github.com/YOUR_USERNAME/YOUR_REPO/actions/workflows/ci.yml/badge.svg)](https://github.com/YOUR_USERNAME/YOUR_REPO/actions/workflows/ci.yml) +``` + +## Local Testing + +Before pushing, verify your changes locally: + +```bash +cd stripe_collection_module + +# Run linter +npm run lint + +# Run tests with coverage +npm test -- --coverage + +# Build +npm run build +``` + +## Configuration + +### Node Version + +The workflow uses Node.js 18.x to match the `.nvmrc` file. + +### Coverage Thresholds + +Tests must maintain 70% coverage across: +- Statements +- Branches +- Functions +- Lines + +### Caching + +The workflow caches npm dependencies for faster runs. + +## Troubleshooting + +### Failing Tests + +If tests fail in CI but pass locally: +1. Check Node.js version matches (18.x) +2. Clear local `node_modules` and reinstall +3. Run `npm ci` instead of `npm install` +4. Check for environment-specific code + +### Build Failures + +If build fails: +1. Run `npm run build` locally +2. Check TypeScript errors +3. Verify all imports are correct +4. Check `tsconfig.json` configuration + +### Coverage Failures + +If coverage is below threshold: +1. Run `npm test -- --coverage` locally +2. Check which files need tests +3. Add tests to increase coverage +4. See `docs/TESTING.md` for testing guide + +## Customization + +### Adding More Jobs + +To add a new job, edit `.github/workflows/ci.yml`: + +```yaml +new_job_name: + name: My New Job + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Do something + run: echo "Hello" +``` + +### Changing Node Version + +Update the matrix in `ci.yml`: + +```yaml +strategy: + matrix: + node-version: [18.x, 20.x] # Test multiple versions +``` + +### Optional: Codecov Integration + +To enable Codecov coverage reports: +1. Sign up at https://codecov.io +2. Add your repository +3. No additional config needed - workflow already includes Codecov step + +## Security + +### Secrets + +The workflow doesn't require secrets by default. If you add steps that need secrets: + +1. Add secrets in GitHub repo settings +2. Reference in workflow: + +```yaml +- name: Step using secret + env: + SECRET_NAME: ${{ secrets.SECRET_NAME }} + run: some-command +``` + +### Permissions + +The workflow has minimal permissions by default. It only needs: +- Read access to code +- Write access to comments (for coverage reporting) + +## Best Practices + +1. **Always run tests locally first** +2. **Keep CI fast** - Don't add unnecessary steps +3. **Fix failing tests immediately** - Don't merge with failing CI +4. **Review coverage reports** - Maintain high coverage +5. **Update workflows** - Keep actions up to date + +## Resources + +- [GitHub Actions Documentation](https://docs.github.com/en/actions) +- [Jest Documentation](https://jestjs.io/) +- [Codecov Documentation](https://docs.codecov.com/) + diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..0798d62 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,132 @@ +name: CI + +on: + push: + branches: [main, master, develop] + pull_request: + branches: [main, master, develop] + +jobs: + lint: + name: Lint + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 18.x + cache: 'npm' + cache-dependency-path: stripe_collection_module/package-lock.json + + - name: Install dependencies + run: | + cd stripe_collection_module + npm ci + + - name: Create env.ts for build + run: | + cd stripe_collection_module + cp code/env.sample.ts code/env.ts + + - name: Run ESLint + run: | + cd stripe_collection_module + npm run lint + + test: + name: Test + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 18.x + cache: 'npm' + cache-dependency-path: stripe_collection_module/package-lock.json + + - name: Install dependencies + run: | + cd stripe_collection_module + npm ci + + - name: Create env.ts for build + run: | + cd stripe_collection_module + cp code/env.sample.ts code/env.ts + + - name: Run tests with coverage + run: | + cd stripe_collection_module + npm test -- --coverage --ci + + - name: Comment PR with coverage + if: github.event_name == 'pull_request' + uses: actions/github-script@v7 + continue-on-error: true + with: + script: | + const fs = require('fs'); + const coveragePath = './stripe_collection_module/coverage/coverage-summary.json'; + + if (fs.existsSync(coveragePath)) { + const coverage = JSON.parse(fs.readFileSync(coveragePath, 'utf8')); + const total = coverage.total; + + const comment = `## ๐Ÿ“Š Test Coverage Report + + | Category | Coverage | + |----------|----------| + | **Statements** | ${total.statements.pct.toFixed(2)}% | + | **Branches** | ${total.branches.pct.toFixed(2)}% | + | **Functions** | ${total.functions.pct.toFixed(2)}% | + | **Lines** | ${total.lines.pct.toFixed(2)}% | + + ${total.statements.pct >= 70 ? 'โœ…' : 'โŒ'} Coverage threshold: 70% + `; + + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: comment + }); + } + + build: + name: Build Check + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 18.x + cache: 'npm' + cache-dependency-path: stripe_collection_module/package-lock.json + + - name: Install dependencies + run: | + cd stripe_collection_module + npm ci + + - name: Create env.ts for build + run: | + cd stripe_collection_module + cp code/env.sample.ts code/env.ts + + - name: Build TypeScript + run: | + cd stripe_collection_module + npm run build + echo "โœ… TypeScript compilation successful" diff --git a/README.md b/README.md index ac9340b..07db5b4 100644 --- a/README.md +++ b/README.md @@ -1,73 +1,465 @@ -# Stripe Collection Module -This repository contains the Collection Module example template for integrating -Stripe with the Root platform. +# Stripe Collection Module Template -For a template to be available to be used by developers, it has to be assigned -to the Template Organization in that stack. Every stack has a Template -Organization, and those are the details you need to update in the targets.yaml -file. +[![CI](https://github.com/YOUR_USERNAME/YOUR_REPO/actions/workflows/ci.yml/badge.svg)](https://github.com/YOUR_USERNAME/YOUR_REPO/actions/workflows/ci.yml) +[![Coverage](https://img.shields.io/badge/coverage-92%25-brightgreen.svg)](https://github.com/YOUR_USERNAME/YOUR_REPO) +[![Node](https://img.shields.io/badge/node-18.x-brightgreen.svg)](https://nodejs.org) +[![TypeScript](https://img.shields.io/badge/typescript-5.x-blue.svg)](https://www.typescriptlang.org/) -## Getting started with a new template +A production-ready template for building collection modules that integrate Stripe with the Root Platform. Includes comprehensive testing, structured logging, and clear architecture patterns. -If the template is brand new and doesn't exist on the target Template -Organisation, start by creating the Collection Module on the Template -Organisation. Remember to update the key in the payload below. +--- -Endpoint +## ๐Ÿš€ Quick Start + +### Automated Setup (Recommended) + +```bash +./setup.sh +``` + +This will automatically: +- โœ“ Install all dependencies +- โœ“ Configure Root Platform settings (`.root-config.json` and `.root-auth`) +- โœ“ Create your environment file from template +- โœ“ Run validation checks +- โœ“ Show you next steps + +### Manual Setup + +```bash +cd stripe_collection_module +npm install +cp code/env.sample.ts code/env.ts +# Edit code/env.ts with your Stripe API keys +npm run validate +npm test +``` + +### Start Implementing + +See [docs/CUSTOMIZING.md](./stripe_collection_module/docs/CUSTOMIZING.md) to implement Stripe integration. + +--- + +## ๐Ÿ“‹ What's Included + +This template provides: + +- โœ… **Root Platform Ready** - Optimized for Root Platform deployment +- โœ… **Comprehensive Testing** - Jest with 60+ tests, factories, and utilities +- โœ… **Structured Logging** - JSON logging integrated with Root Platform +- โœ… **Dependency Injection** - Clean, testable service architecture +- โœ… **Type-Safe Configuration** - Injectable, validated configuration service +- โœ… **Stripe Integration** - Ready-to-implement Stripe service stubs +- โœ… **Production Best Practices** - Error handling, retries, monitoring +- โœ… **Complete Documentation** - Setup, deployment, customization guides + +--- + +## ๐Ÿ“š Documentation + +### Getting Started + +- **[Getting Started](./SETUP.md)** - Complete walkthrough for new users +- **[Setup Guide](./stripe_collection_module/docs/SETUP.md)** - Detailed setup reference +- **[Root Configuration](./stripe_collection_module/docs/ROOT_CONFIGURATION.md)** - Root Platform config files +- **[Implementation Guide](./stripe_collection_module/docs/CUSTOMIZING.md)** - Stripe implementation guide + +### Development + +- **[Architecture](./stripe_collection_module/docs/ARCHITECTURE.md)** - System design and patterns +- **[Testing Guide](./stripe_collection_module/docs/TESTING.md)** - Writing and running tests +- **[Best Practices](./stripe_collection_module/docs/BEST_PRACTICES.md)** - Production patterns + +### Deployment + +- **[Deployment Guide](./stripe_collection_module/docs/DEPLOYMENT.md)** - Root Platform deployment +- **[Webhooks Setup](./stripe_collection_module/docs/WEBHOOKS.md)** - Webhook configuration +- **[Log Viewing](./stripe_collection_module/docs/LOG_VIEWING.md)** - Root Platform log access + +--- + +## ๐Ÿ—๏ธ Architecture + +``` +Stripe โ†’ Root Platform โ†’ Stripe Collection Module โ†’ Root API + โ†“ + Root Platform Logs +``` + +### Key Components + +**Core Services:** +- `ConfigurationService` - Type-safe, validated configuration +- `LogService` - Dual-output structured logging +- `RenderService` - HTML generation for dashboards + +**Stripe Integration:** +- Stripe SDK client wrapper +- Stripe service layer for business logic +- Stripe webhook event processors +- Stripe to Root data adapters + +**Root Integration:** +- Root API client wrapper +- Policy and payment management +- Lifecycle hook handlers + +### Project Structure + +``` +stripe_collection_module/ +โ”œโ”€โ”€ code/ +โ”‚ โ”œโ”€โ”€ core/ # DI container & domain models +โ”‚ โ”œโ”€โ”€ services/ # Business logic +โ”‚ โ”œโ”€โ”€ clients/ # API wrappers +โ”‚ โ”œโ”€โ”€ controllers/ # Event processors +โ”‚ โ”œโ”€โ”€ lifecycle-hooks/ # Root platform hooks +โ”‚ โ”œโ”€โ”€ utils/ # Utilities +โ”‚ โ””โ”€โ”€ env.sample.ts # Configuration template +โ”œโ”€โ”€ __tests__/ # Comprehensive test suite +โ”œโ”€โ”€ docs/ # Detailed documentation +โ”œโ”€โ”€ infrastructure/ # AWS templates (SAM/CloudFormation) +โ””โ”€โ”€ scripts/ # Build and deployment scripts +``` + +--- + +## ๐Ÿ”ง Configuration + +### Required Environment Variables + +| Variable | Description | Example | +|----------|-------------|---------| +| `ENVIRONMENT` | Environment name | `production` or `development` | +| `STRIPE_SECRET_KEY_LIVE` | Stripe API secret key | `sk_live_...` | +| `STRIPE_PUBLISHABLE_KEY_LIVE` | Stripe publishable key | `pk_live_...` | +| `STRIPE_WEBHOOK_SIGNING_SECRET_LIVE` | Stripe webhook secret | `whsec_...` | +| `ROOT_API_KEY_LIVE` | Root API key | `production_...` | +| `ROOT_BASE_URL_LIVE` | Root API URL | `https://api.rootplatform.com/v1/insurance` | +| `ROOT_COLLECTION_MODULE_KEY` | Module identifier | `cm_stripe` | + +See `stripe_collection_module/code/env.sample.ts` for complete configuration template. + +### Configuration Files + +- `stripe_collection_module/code/env.ts` - Environment variables (gitignored) +- `stripe_collection_module/.root-config.json` - Root module config +- `stripe_collection_module/package.json` - Dependencies and scripts + +--- + +## ๐Ÿงช Testing + +```bash +cd stripe_collection_module + +# Run all tests +npm test + +# Watch mode +npm run test:watch + +# With coverage +npm run test:coverage + +# Validate configuration +npm run validate +``` + +See [docs/TESTING.md](./stripe_collection_module/docs/TESTING.md) for testing guide. + +--- + +## ๐Ÿšข Deployment + +### Root Platform Deployment + +Deployment is handled through the Root Platform API: ```bash -> POST /v1/insurance/collection-modules HTTP/1.1 +# Prepare for deployment +cd stripe_collection_module +npm run validate +npm test +npm run build + +# Tag your release +git tag -a v1.0.0 -m "Release version 1.0.0" +git push origin v1.0.0 + +# Publish to Root Platform (Sandbox) +curl -X POST \ + -H "Authorization: Basic {{api_key}}" \ + "{{host}}/v1/apps/{{org_id}}/insurance/collection-modules/{{cm_key}}/publish?bumpSandbox=true" + +# Publish to Production +curl -X POST \ + -H "Authorization: Basic {{api_key}}" \ + "{{host}}/v1/apps/{{org_id}}/insurance/collection-modules/{{cm_key}}/publish?bumpSandbox=false" ``` -Payload +See [docs/DEPLOYMENT.md](./stripe_collection_module/docs/DEPLOYMENT.md) for detailed deployment guide. + +### Environment-Specific Deployment + +**Sandbox:** +- Use sandbox/test credentials +- Deploy with `bumpSandbox=true` +- Test with provider sandbox + +**Production:** +- Use production credentials +- Deploy with `bumpSandbox=false` +- Configure via Root Platform dashboard +- Monitor via Root Platform logs + +--- -```json -{ - "key": "payment_provider_cm_template", - "name": "Payment Provider Collection Module Template", - "key_of_collection_module_to_clone": "blank_starter_template" -} +## ๐Ÿ” Monitoring + +### Root Platform Logs + +Structured JSON logs accessible via Root Platform: + +- View logs in Root Platform dashboard +- Filter by severity, time, or search terms +- Monitor real-time activity +- Track webhook processing +- Debug lifecycle hook execution + +### Metrics + +Track via Root Platform dashboard: +- Request volume +- Error rates +- Processing latency +- Webhook delivery status +- API call success rates + +### Monitoring + +Set up monitoring for: +- High error rates +- Slow response times +- Failed webhook deliveries +- API connectivity issues + +--- + +## ๐Ÿ” Security + +### Best Practices + +- โœ… Store secrets securely (never commit env.ts) +- โœ… Verify all webhook signatures +- โœ… Validate all inputs +- โœ… Never log sensitive data (API keys, PII, card numbers) +- โœ… Use environment-specific credentials +- โœ… Rotate API keys quarterly +- โœ… Use Stripe test mode for development +- โœ… Monitor security events in Root Platform dashboard + +### Webhook Security + +All webhooks must: +1. Verify signature from provider +2. Validate event structure +3. Log processing attempts +4. Handle duplicate events (idempotency) + +See [docs/WEBHOOKS.md](./stripe_collection_module/docs/WEBHOOKS.md) for implementation details. + +--- + +## ๐ŸŽฏ Implementing Stripe Integration + +This template provides a complete structure for Stripe integration with stub implementations. + +### Implementation Steps + +1. **Implement Services** - Complete the stub methods in `code/services/stripe.service.ts` +2. **Add Stripe Clients** - Configure Stripe SDK client in `code/clients/stripe-client.ts` +3. **Create Event Processors** - Implement Stripe webhook handlers in `code/controllers/stripe-event-processors/` +4. **Wire Lifecycle Hooks** - Connect Root Platform lifecycle hooks to Stripe operations +5. **Add Validation** - Implement input validation with Joi +6. **Write Tests** - Add comprehensive test coverage + +See [docs/CUSTOMIZING.md](./stripe_collection_module/docs/CUSTOMIZING.md) for detailed implementation guide. + +### Included Patterns + +The template includes: +- Retry logic with exponential backoff +- Error handling and classification +- Webhook signature verification +- Idempotency handling +- Structured logging +- Dependency injection + +--- + +## ๐Ÿ“ฆ Features + +### Logging System + +**Structured logging:** +- JSON logs to Root Platform +- Multiple log levels (DEBUG, INFO, WARN, ERROR) +- Rich metadata support +- Request tracking +- Easy debugging via dashboard + +### Dependency Injection + +**Clean, testable architecture:** +- Custom DI container (no heavy frameworks) +- Service lifetime management (singleton/transient) +- Easy mocking for tests +- Clear dependency graphs + +### Configuration Management + +**Type-safe configuration:** +- Injectable `ConfigurationService` +- Environment-specific configs +- Validation on startup +- Helpful error messages + +### Testing Infrastructure + +**Comprehensive test support:** +- Jest with TypeScript +- Test utilities and helpers +- Mock factories for common objects +- High test coverage (70%+) + +--- + +## ๐Ÿ› ๏ธ Available Scripts + +```bash +# Validation +npm run validate # Validate configuration + +# Development +npm run lint # Check code quality +npm run lint:fix # Auto-fix linting issues +npm run prettier # Format code +npm test # Run tests +npm run test:watch # Watch mode + +# Build +npm run build # Compile TypeScript +npm run package:lambda # Create deployment package + +# Cleanup +npm run clean # Remove build artifacts ``` -For updating existing collection modules, you can go ahead and deploy any -changes using the instructions below. +--- + +## ๐Ÿ› Troubleshooting + +### Common Issues + +| Issue | Solution | +|-------|----------| +| "ENVIRONMENT is not set" | Set `NODE_ENV` in `code/env.ts` | +| "Module not found" errors | Run `npm install` | +| Tests failing | Run `nvm use` then `npm install` | +| TypeScript errors | Check `tsconfig.json` includes all files | +| Placeholder warnings | Update `code/env.ts` with real values | + +See documentation for detailed troubleshooting guides. + +--- + +## ๐Ÿ“ Maintenance + +### Regular Tasks + +- **Weekly**: Review logs and metrics +- **Monthly**: Update dependencies +- **Quarterly**: Rotate API keys +- **As Needed**: Update documentation + +### Monitoring + +- Check Root Platform dashboard daily +- Review error rates and patterns +- Monitor webhook processing +- Track API success rates + +--- + +## ๐Ÿค Contributing to Template + +When improving this template: + +1. Keep it Stripe-focused +2. Update documentation +3. Add tests for new features +4. Follow existing patterns +5. Update this README + +--- + +## ๐Ÿ“„ License + +Review the LICENSE file for usage terms. + +--- + +## ๐Ÿ”— Resources + +### Stripe Documentation + +- **Stripe API**: https://stripe.com/docs/api +- **Stripe Webhooks**: https://stripe.com/docs/webhooks +- **Stripe Testing**: https://stripe.com/docs/testing +- **Stripe CLI**: https://stripe.com/docs/stripe-cli +- **Stripe Node.js**: https://github.com/stripe/stripe-node + +### Root Platform + +- **Root Platform Documentation**: Contact your Root representative +- **API Reference**: Available in Root Platform dashboard +- **Collection Modules**: Platform-specific documentation + +--- + +## ๐Ÿ“ž Support -## Configuration +### Documentation -### Root Config -Copy .root-config.sample.json and rename to .root-config.json. Add your root configuration here. +All documentation is in `stripe_collection_module/docs/`: +- Setup and configuration +- Customization guide +- Deployment instructions +- Testing guide +- Best practices +- Architecture overview -| Variable | Description | -| ------------------------ | -------------------------------------------------------------------------------------------- | -| `collectionModuleKey` | The unique key of the collection module. | -| `collectionModuleName` | The name of the collection module. | -| `organizationId` | The Root organization ID for the collection module. | -| `host` | The host URL | +### Getting Help +1. Check the [documentation](./stripe_collection_module/docs/) +2. Review [troubleshooting](#troubleshooting) section +3. Check Root Platform logs for errors +4. Review Stripe documentation -### Env.ts +--- -Copy env.sample and rename to env.ts. Add your environment variables here. +## โœจ What's Next? -| Variable | Description | -| ------------------------------------- | -------------------------------------------------------------------------------------------- | -| `STRIPE_WEBHOOK_SIGNING_SECRET_LIVE` | The Stripe webhook signing secret for the live environment. | -| `STRIPE_WEBHOOK_SIGNING_SECRET_TEST` | The Stripe webhook signing secret for the test environment. | -| `STRIPE_PRODUCT_ID_LIVE` | The Stripe product id for the live environment. | -| `STRIPE_PRODUCT_ID_TEST` | The Stripe product id for the test environment. | -| `STRIPE_PUBLISHABLE_KEY_LIVE` | The Stripe publishable key for the live environment. | -| `STRIPE_PUBLISHABLE_KEY_TEST` | The Stripe publishable key for the test environment. | -| `STRIPE_SECRET_KEY_LIVE` | The Stripe API secret key for the live environment. | -| `STRIPE_SECRET_KEY_TEST` | The Stripe API secret key for the test environment. | -| `ROOT_COLLECTION_MODULE_KEY` | The collection module unique key. | -| `ROOT_API_KEY_LIVE` | The Root API key for the production environment. | -| `ROOT_API_KEY_TEST` | The Root API key for the sandbox environment. | -| `ROOT_BASE_URL_LIVE` | The Root API base URL the production environment. | -| `ROOT_BASE_URL_TEST` | The Root API base URL the sandbox environment. | +After setup: +1. **Implement** - Complete Stripe service implementations ([docs/CUSTOMIZING.md](./stripe_collection_module/docs/CUSTOMIZING.md)) +2. **Test** - Write comprehensive tests ([docs/TESTING.md](./stripe_collection_module/docs/TESTING.md)) +3. **Deploy** - Publish to Root Platform ([docs/DEPLOYMENT.md](./stripe_collection_module/docs/DEPLOYMENT.md)) +4. **Monitor** - Review Root Platform logs and metrics +5. **Iterate** - Refine based on production usage -## How to deploy the template +--- -Once your code has been merged into main, go to the [github repository](https://github.com/RootBank/collection-module-template_stripe) -and select [releases](TODO). Create a new release add the new version number e.g. v2.0.0. -The collection module will be pushed and published. \ No newline at end of file +**Built with โค๏ธ for the Root Platform** diff --git a/SETUP.md b/SETUP.md new file mode 100644 index 0000000..59da576 --- /dev/null +++ b/SETUP.md @@ -0,0 +1,358 @@ +# Getting Started with the Stripe Collection Module Template + +This guide will help you set up and customize this template for your specific use case. + +## Prerequisites + +- Node.js 18+ (check with `node --version`) +- npm or yarn +- Root Platform account and API credentials +- Stripe account and API keys + +## Initial Setup + +### 1. Clone and Run Setup + +```bash +# Clone the repository (or use this template) +git clone +cd collection-module-template_stripe + +# Run the automated setup script +./setup.sh +``` + +The setup script will: +- โœ“ Install all dependencies +- โœ“ Configure Root Platform settings (`.root-config.json` and `.root-auth`) +- โœ“ Create `code/env.ts` from the template +- โœ“ Run validation checks +- โœ“ Show you next steps + +### 2. Configure Root Platform + +The setup script will prompt you for: + +**`.root-config.json`** - Collection module metadata: +- Collection Module Key (e.g., `cm_stripe_yourcompany`) +- Collection Module Name (e.g., "Your Company Stripe Integration") +- Organization ID +- Root Platform Host URL + +**`.root-auth`** - Authentication: +- Root Platform API Key (stored securely, gitignored) + +For detailed information about these files, see [Root Configuration Guide](stripe_collection_module/docs/ROOT_CONFIGURATION.md). + +### 3. Configure Environment + +Edit `stripe_collection_module/code/env.ts` with your actual credentials: + +```typescript +export default { + // Root Platform Configuration + rootOrganisationId: 'your-org-id', + rootApiKey: 'your-api-key', + rootEnvironment: 'sandbox', // or 'production' + rootBaseUrl: 'https://api.root.co.za', + + // Stripe Configuration + stripeSecretKey: 'sk_test_...', + stripeWebhookSigningSecret: 'whsec_...', + stripePublishableKey: 'pk_test_...', + stripeProductId: 'prod_...', + + // Module Configuration + environment: 'development', +}; +``` + +**โš ๏ธ Important:** Never commit sensitive files to version control: +- `code/env.ts` - Contains API keys and secrets +- `.root-auth` - Contains Root Platform API key + +Both files are already in `.gitignore`. + +## Project Structure + +``` +collection-module-template_stripe/ +โ”œโ”€โ”€ setup.sh # Automated setup script +โ”œโ”€โ”€ README.md # Main documentation +โ””โ”€โ”€ stripe_collection_module/ + โ”œโ”€โ”€ code/ + โ”‚ โ”œโ”€โ”€ controllers/ # Event handlers + โ”‚ โ”‚ โ”œโ”€โ”€ root-event-processors/ + โ”‚ โ”‚ โ””โ”€โ”€ stripe-event-processors/ + โ”‚ โ”œโ”€โ”€ services/ # Business logic + โ”‚ โ”‚ โ”œโ”€โ”€ config.service.ts + โ”‚ โ”‚ โ”œโ”€โ”€ log.service.ts + โ”‚ โ”‚ โ”œโ”€โ”€ root.service.ts + โ”‚ โ”‚ โ””โ”€โ”€ stripe.service.ts + โ”‚ โ”œโ”€โ”€ core/ # DI container setup + โ”‚ โ”œโ”€โ”€ clients/ # API clients + โ”‚ โ”œโ”€โ”€ utils/ # Utilities + โ”‚ โ”œโ”€โ”€ lifecycle-hooks/ # Root Platform hooks + โ”‚ โ”œโ”€โ”€ webhook-hooks.ts # Stripe webhook handler + โ”‚ โ””โ”€โ”€ main.ts # Entry point + โ”œโ”€โ”€ __tests__/ # Tests + โ”œโ”€โ”€ docs/ # Detailed documentation + โ”‚ โ”œโ”€โ”€ SETUP.md + โ”‚ โ”œโ”€โ”€ DEPLOYMENT.md + โ”‚ โ”œโ”€โ”€ CUSTOMIZING.md + โ”‚ โ””โ”€โ”€ BEST_PRACTICES.md + โ””โ”€โ”€ scripts/ # Deployment scripts +``` + +## Customization Steps + +### 1. Update Module Metadata + +Edit `stripe_collection_module/package.json`: + +```json +{ + "name": "your-collection-module-name", + "version": "1.0.0", + "description": "Your module description" +} +``` + +### 2. Implement Your Controllers + +The template includes example controllers. Customize them for your use case: + +**Stripe Event Processor Example:** + +```typescript +// code/controllers/stripe-event-processors/invoice-paid.controller.ts +export class InvoicePaidController { + async handle(invoice: Stripe.Invoice): Promise { + // Your logic here + } +} +``` + +**Root Event Processor Example:** + +```typescript +// code/controllers/root-event-processors/payment-creation.controller.ts +export class PaymentCreationController { + async handle(params: any): Promise { + // Your logic here + } +} +``` + +### 3. Add Your Services + +Extend or create new services in `code/services/`: + +```typescript +// code/services/your-service.ts +export class YourService { + constructor( + private readonly logService: LogService, + // ... other dependencies + ) {} + + async yourMethod(): Promise { + // Your logic + } +} +``` + +### 4. Register in DI Container + +Add your services and controllers to `code/core/container.setup.ts`: + +```typescript +container.register( + ServiceToken.YOUR_SERVICE, + (c) => { + const logService = c.resolve(ServiceToken.LOG_SERVICE); + return new YourService(logService); + }, + ServiceLifetime.SINGLETON +); +``` + +## Development Workflow + +### Running Locally + +```bash +cd stripe_collection_module + +# Lint your code +npm run lint + +# Run tests +npm run test + +# Build the module +npm run build +``` + +### Testing + +```bash +# Run all tests +npm run test + +# Run with coverage +npm run test:coverage + +# Run in watch mode +npm run test:watch +``` + +### Validation + +```bash +# Check configuration +npm run validate + +# Format code +npm run format + +# Pre-deployment checks +npm run predeploy +``` + +## Deployment + +### First Deployment + +1. Ensure all tests pass: `npm run test` +2. Build the module: `npm run build` +3. Deploy to sandbox first: `npm run deploy:sandbox` +4. Test thoroughly in sandbox environment +5. Deploy to production: `npm run deploy:production` + +### Automated Deployment + +The deployment script handles: +- Pre-deployment validation +- Building the module +- Git tagging (with semver) +- Publishing to Root Platform API +- Dry-run mode for testing + +```bash +# Deploy to sandbox +npm run deploy:sandbox + +# Deploy to production +npm run deploy:production + +# Test deployment without publishing +npm run deploy:dry-run +``` + +See [DEPLOYMENT.md](stripe_collection_module/docs/DEPLOYMENT.md) for details. + +## Configuration Management + +### Environment-Specific Config + +The template uses a single `env.ts` file. For multiple environments: + +**Option 1: Environment Variables** + +```typescript +// code/env.ts +export default { + rootApiKey: process.env.ROOT_API_KEY || 'fallback', + // ... +}; +``` + +**Option 2: Multiple Config Files** (not recommended for templates) + +```bash +code/env.development.ts +code/env.sandbox.ts +code/env.production.ts +``` + +### Secrets Management + +**Never commit secrets!** The template's `.gitignore` already excludes: +- `code/env.ts` - Stripe keys and module configuration +- `.root-auth` - Root Platform API key +- `*.env` - Any environment files +- `.env*` - Environment file variants + +## Next Steps + +1. **Review Documentation** + - [Customization Guide](stripe_collection_module/docs/CUSTOMIZING.md) + - [Best Practices](stripe_collection_module/docs/BEST_PRACTICES.md) + - [Deployment Guide](stripe_collection_module/docs/DEPLOYMENT.md) + +2. **Implement Your Logic** + - Start with one controller + - Add services as needed + - Write tests as you go + +3. **Test Thoroughly** + - Unit tests for services + - Integration tests for controllers + - Test in sandbox environment + +4. **Deploy** + - Follow the deployment guide + - Start with sandbox + - Monitor logs and metrics + +## Getting Help + +- Check the [README](stripe_collection_module/README.md) for overview +- Review [example controllers](stripe_collection_module/code/controllers/) +- See [Stripe Integration Guide](stripe_collection_module/docs/CUSTOMIZING.md) +- Root Platform docs: https://docs.root.co.za +- Stripe API docs: https://stripe.com/docs/api + +## Common Issues + +### "Module not found" errors + +```bash +rm -rf node_modules package-lock.json +npm install +``` + +### Build failures + +```bash +npm run clean +npm install +npm run build +``` + +### Type errors from SDKs + +Make sure you're using the correct SDK types: +- Stripe types: `Stripe.Customer`, `Stripe.Invoice`, etc. +- Root types: `root.Policy`, `root.PaymentMethod`, etc. + +### Deployment fails + +1. Check your API credentials in `env.ts` +2. Ensure tests pass: `npm run test` +3. Try dry-run first: `npm run deploy:dry-run` + +## Contributing to the Template + +If you find improvements that would benefit all users of this template: + +1. Keep changes generic and configurable +2. Update documentation +3. Add tests +4. Submit a pull request + +--- + +**Ready to build?** Start with `./setup.sh` and follow the wizard! ๐Ÿš€ + diff --git a/setup.sh b/setup.sh new file mode 100755 index 0000000..7e3b2d2 --- /dev/null +++ b/setup.sh @@ -0,0 +1,564 @@ +#!/bin/bash + +# Collection Module Template Setup Script +# This script helps you set up the template after cloning + +set -e + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +CYAN='\033[0;36m' +MAGENTA='\033[0;35m' +NC='\033[0m' # No Color + +# Read required Node version from .nvmrc +NVMRC_PATH="stripe_collection_module/.nvmrc" +if [ -f "$NVMRC_PATH" ]; then + REQUIRED_NODE_VERSION=$(cat "$NVMRC_PATH" | tr -d '\n\r') +else + # Fallback to default if .nvmrc doesn't exist + REQUIRED_NODE_VERSION="18" + echo -e "${YELLOW}โš ๏ธ .nvmrc not found, using default Node version: ${REQUIRED_NODE_VERSION}${NC}" +fi + +echo -e "${BLUE}โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—${NC}" +echo -e "${BLUE}โ•‘ Stripe Collection Module Template - Setup Wizard โ•‘${NC}" +echo -e "${BLUE}โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•${NC}" +echo "" + +# Check if we're in the right directory +if [ ! -f "stripe_collection_module/package.json" ]; then + echo -e "${RED}โŒ Error: This script must be run from the template root directory${NC}" + exit 1 +fi + +# Function to prompt with default value +prompt_with_default() { + local prompt="$1" + local default="$2" + local value + + if [ -n "$default" ]; then + read -p "$prompt [$default]: " value + echo "${value:-$default}" + else + read -p "$prompt: " value + echo "$value" + fi +} + +# Function to prompt for required value +prompt_required() { + local prompt="$1" + local value + + while [ -z "$value" ]; do + read -p "$prompt: " value + if [ -z "$value" ]; then + echo -e "${RED}โŒ This value is required${NC}" + fi + done + + echo "$value" +} + +# Function to prompt for password/secret (no echo) +prompt_secret() { + local prompt="$1" + local value + + while [ -z "$value" ]; do + read -s -p "$prompt: " value + echo "" + if [ -z "$value" ]; then + echo -e "${RED}โŒ This value is required${NC}" + fi + done + + echo "$value" +} + +# ============================================================================ +# Step 1: Check and setup Node.js version +# ============================================================================ +echo -e "${YELLOW}๐Ÿ”ง Step 1: Checking Node.js version...${NC}" + +CURRENT_NODE_VERSION=$(node -v 2>/dev/null | sed 's/v//' | cut -d. -f1) + +if [ -z "$CURRENT_NODE_VERSION" ]; then + echo -e "${RED}โŒ Node.js is not installed${NC}" + echo -e "${BLUE}โ„น๏ธ Please install Node.js ${REQUIRED_NODE_VERSION}.x first${NC}" + echo -e "${BLUE}โ„น๏ธ Recommended: Install nvm (Node Version Manager)${NC}" + echo -e "${CYAN} curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash${NC}" + exit 1 +fi + +if [ "$CURRENT_NODE_VERSION" -lt "$REQUIRED_NODE_VERSION" ]; then + echo -e "${RED}โŒ Node.js version ${CURRENT_NODE_VERSION}.x detected${NC}" + echo -e "${RED}โŒ Node.js ${REQUIRED_NODE_VERSION}.x or higher is required${NC}" + + # Check if nvm is available + if command -v nvm &> /dev/null || [ -f "$HOME/.nvm/nvm.sh" ]; then + echo -e "${BLUE}โ„น๏ธ NVM detected. Attempting to install and use Node.js ${REQUIRED_NODE_VERSION}...${NC}" + + # Load nvm if not already loaded + export NVM_DIR="$HOME/.nvm" + [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" + + # Install and use the required version + nvm install "$REQUIRED_NODE_VERSION" + nvm use "$REQUIRED_NODE_VERSION" + + echo -e "${GREEN}โœ“ Now using Node.js $(node -v)${NC}" + else + echo -e "${BLUE}โ„น๏ธ Please install Node.js ${REQUIRED_NODE_VERSION}.x or use nvm${NC}" + exit 1 + fi +elif [ "$CURRENT_NODE_VERSION" -ne "$REQUIRED_NODE_VERSION" ]; then + echo -e "${YELLOW}โš ๏ธ Node.js version ${CURRENT_NODE_VERSION}.x detected${NC}" + echo -e "${YELLOW}โš ๏ธ Node.js ${REQUIRED_NODE_VERSION}.x is recommended${NC}" + + # Check if nvm is available + if command -v nvm &> /dev/null || [ -f "$HOME/.nvm/nvm.sh" ]; then + read -p "Switch to Node.js ${REQUIRED_NODE_VERSION}.x using nvm? (y/n) " -n 1 -r + echo "" + if [[ $REPLY =~ ^[Yy]$ ]]; then + export NVM_DIR="$HOME/.nvm" + [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" + + nvm install "$REQUIRED_NODE_VERSION" 2>/dev/null || true + nvm use "$REQUIRED_NODE_VERSION" + + echo -e "${GREEN}โœ“ Now using Node.js $(node -v)${NC}" + fi + fi +else + echo -e "${GREEN}โœ“ Node.js ${CURRENT_NODE_VERSION}.x detected (required: ${REQUIRED_NODE_VERSION}.x)${NC}" +fi + +# Ensure nvm use is called in the stripe_collection_module directory +cd stripe_collection_module +if [ -f ".nvmrc" ]; then + if command -v nvm &> /dev/null || [ -f "$HOME/.nvm/nvm.sh" ]; then + export NVM_DIR="$HOME/.nvm" + [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" + nvm use 2>/dev/null || echo -e "${YELLOW}โš ๏ธ Could not automatically switch Node version${NC}" + fi +fi +echo "" + +# ============================================================================ +# Step 2: Check for Root Platform CLI (rp) +# ============================================================================ +echo -e "${YELLOW}๐Ÿ”ง Step 2: Checking Root Platform CLI...${NC}" + +if command -v rp &> /dev/null; then + echo -e "${GREEN}โœ“ Root Platform CLI (rp) is installed${NC}" + rp --version 2>/dev/null || echo -e "${BLUE}โ„น๏ธ rp version info not available${NC}" +else + echo -e "${YELLOW}โš ๏ธ Root Platform CLI (rp) not found${NC}" + echo -e "${BLUE}โ„น๏ธ The rp CLI is required for deploying Collection Modules${NC}" + + read -p "Install Root Platform CLI globally? (y/n) " -n 1 -r + echo "" + + if [[ $REPLY =~ ^[Yy]$ ]]; then + echo -e "${BLUE}Installing root-platform-cli globally...${NC}" + npm install -g root-platform-cli + + if command -v rp &> /dev/null; then + echo -e "${GREEN}โœ“ Root Platform CLI installed successfully${NC}" + rp --version 2>/dev/null || true + else + echo -e "${RED}โŒ Installation failed. Please install manually:${NC}" + echo -e "${CYAN} npm install -g root-platform-cli${NC}" + echo -e "${YELLOW}โš ๏ธ Continuing setup, but deployment will require rp CLI${NC}" + fi + else + echo -e "${YELLOW}โš ๏ธ Skipping rp CLI installation${NC}" + echo -e "${BLUE}โ„น๏ธ You can install it later with: npm install -g root-platform-cli${NC}" + fi +fi +echo "" + +# ============================================================================ +# Step 3: Install dependencies +# ============================================================================ +echo -e "${YELLOW}๐Ÿ“ฆ Step 3: Installing dependencies...${NC}" +if command -v npm &> /dev/null; then + npm install + echo -e "${GREEN}โœ“ Dependencies installed${NC}" +else + echo -e "${RED}โŒ npm not found. Please install Node.js first.${NC}" + exit 1 +fi +echo "" + +# ============================================================================ +# Step 4: Configure Root Platform settings (.root-config.json) +# ============================================================================ +echo -e "${YELLOW}๐Ÿ”‘ Step 4: Configuring Root Platform...${NC}" +echo "" + +# Check if .root-config.json needs setup +if [ ! -f ".root-config.json" ]; then + echo -e "${RED}โŒ .root-config.json not found${NC}" + exit 1 +fi + +# Check if this is the default template config +if grep -q "my_collection_module_cm_stripe\|test_cm" .root-config.json 2>/dev/null; then + echo -e "${MAGENTA}โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—${NC}" + echo -e "${MAGENTA}โ•‘ Root Platform Configuration โ•‘${NC}" + echo -e "${MAGENTA}โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•${NC}" + echo "" + echo -e "${CYAN}Please provide the following Root Platform settings:${NC}" + echo "" + + # Prompt for configuration values + cm_key=$(prompt_required "Collection Module Key (e.g., cm_stripe_yourcompany)") + cm_name=$(prompt_required "Collection Module Name (e.g., Your Company Stripe Integration)") + org_id=$(prompt_required "Organization ID") + host=$(prompt_with_default "Root Platform Host" "https://api.rootplatform.com") + + # Update .root-config.json + cat > .root-config.json << EOF +{ + "collectionModuleKey": "${cm_key}", + "collectionModuleName": "${cm_name}", + "organizationId": "${org_id}", + "host": "${host}", + "settings": { + "legacyCodeExecution": false + }, + "manualTransactions": [] +} +EOF + echo "" + echo -e "${GREEN}โœ“ Created .root-config.json${NC}" +else + echo -e "${GREEN}โœ“ .root-config.json already configured${NC}" + + # Load values from existing .root-config.json + if command -v jq &> /dev/null; then + cm_key=$(jq -r '.collectionModuleKey' .root-config.json) + cm_name=$(jq -r '.collectionModuleName' .root-config.json) + org_id=$(jq -r '.organizationId' .root-config.json) + host=$(jq -r '.host' .root-config.json) + else + # Fallback to grep/sed if jq not available + cm_key=$(grep -o '"collectionModuleKey"[[:space:]]*:[[:space:]]*"[^"]*"' .root-config.json | sed 's/.*"\([^"]*\)"$/\1/') + cm_name=$(grep -o '"collectionModuleName"[[:space:]]*:[[:space:]]*"[^"]*"' .root-config.json | sed 's/.*"\([^"]*\)"$/\1/') + org_id=$(grep -o '"organizationId"[[:space:]]*:[[:space:]]*"[^"]*"' .root-config.json | sed 's/.*"\([^"]*\)"$/\1/') + host=$(grep -o '"host"[[:space:]]*:[[:space:]]*"[^"]*"' .root-config.json | sed 's/.*"\([^"]*\)"$/\1/') + fi + + echo -e "${BLUE}โ„น๏ธ Loaded: CM Key=${cm_key}, Org ID=${org_id}${NC}" +fi + +# Set up .root-auth +echo "" +if [ ! -f ".root-auth" ]; then + echo -e "${CYAN}Root Platform API Key Configuration:${NC}" + echo -e "${BLUE}โ„น๏ธ Get this from: Root Platform โ†’ Settings โ†’ API Keys${NC}" + api_key=$(prompt_secret "Root Platform API Key") + echo "ROOT_API_KEY=${api_key}" > .root-auth + echo -e "${GREEN}โœ“ Created .root-auth${NC}" + echo -e "${BLUE}โ„น๏ธ This file is gitignored for security${NC}" +else + echo -e "${GREEN}โœ“ .root-auth already exists${NC}" + # Load API key if we need to create the collection module + source .root-auth + api_key="$ROOT_API_KEY" +fi +echo "" + +# ============================================================================ +# Step 4b: Create Collection Module on Root Platform +# ============================================================================ +echo -e "${YELLOW}๐Ÿš€ Step 4b: Creating Collection Module on Root Platform...${NC}" +echo "" + +if [ -n "$cm_key" ] && [ -n "$api_key" ]; then + echo -e "${BLUE}โ„น๏ธ Attempting to create collection module: ${cm_key}${NC}" + echo -e "${BLUE}โ„น๏ธ This is required before you can deploy${NC}" + echo "" + + # Create the collection module on Root Platform + # Note: Not specifying key_of_collection_module_to_clone creates a blank module + # This is correct for template setup - code will be deployed via rp push + CM_CREATE_RESPONSE=$(curl -X POST \ + -u "${api_key}:" \ + -H "Content-Type: application/json" \ + -d "{ + \"key\": \"${cm_key}\", + \"name\": \"${cm_name}\" + }" \ + -w "\nHTTP_STATUS:%{http_code}" \ + -s \ + "${host}/v1/apps/${org_id}/insurance/collection-modules") + + # Extract HTTP status + CM_HTTP_STATUS=$(echo "$CM_CREATE_RESPONSE" | grep "HTTP_STATUS:" | cut -d: -f2) + CM_RESPONSE_BODY=$(echo "$CM_CREATE_RESPONSE" | sed '/HTTP_STATUS:/d') + + if [ "$CM_HTTP_STATUS" -ge 200 ] && [ "$CM_HTTP_STATUS" -lt 300 ]; then + echo -e "${GREEN}โœ“ Collection module created successfully on Root Platform${NC}" + elif [ "$CM_HTTP_STATUS" -eq 409 ] || echo "$CM_RESPONSE_BODY" | grep -q "already exists"; then + echo -e "${YELLOW}โš ๏ธ Collection module already exists (this is OK)${NC}" + else + echo -e "${YELLOW}โš ๏ธ Could not create collection module (HTTP $CM_HTTP_STATUS)${NC}" + echo -e "${BLUE}โ„น๏ธ Response: $CM_RESPONSE_BODY${NC}" + echo "" + echo -e "${BLUE}โ„น๏ธ You may need to create it manually:${NC}" + echo -e "${CYAN} curl -X POST -u '\$API_KEY:' \\${NC}" + echo -e "${CYAN} -H 'Content-Type: application/json' \\${NC}" + echo -e "${CYAN} -d '{\"key\":\"${cm_key}\",\"name\":\"${cm_name}\"}' \\${NC}" + echo -e "${CYAN} '${host}/v1/apps/${org_id}/insurance/collection-modules'${NC}" + fi +else + echo -e "${YELLOW}โš ๏ธ Skipping collection module creation (missing configuration)${NC}" +fi +echo "" + +# ============================================================================ +# Step 5: Set up environment configuration (code/env.ts) +# ============================================================================ +echo -e "${YELLOW}โš™๏ธ Step 5: Setting up environment configuration...${NC}" +echo "" + +# Check if env.ts already exists +if [ -f "code/env.ts" ]; then + echo -e "${BLUE}โ„น๏ธ code/env.ts already exists${NC}" + read -p "Do you want to reconfigure it? (y/n) " -n 1 -r + echo "" + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + echo -e "${GREEN}โœ“ Keeping existing code/env.ts${NC}" + echo "" + else + rm code/env.ts + fi +fi + +if [ ! -f "code/env.ts" ]; then + echo -e "${MAGENTA}โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—${NC}" + echo -e "${MAGENTA}โ•‘ Environment Configuration โ•‘${NC}" + echo -e "${MAGENTA}โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•${NC}" + echo "" + echo -e "${CYAN}Please provide the following configuration values:${NC}" + echo "" + + # Environment + echo -e "${YELLOW}Environment Settings:${NC}" + node_env=$(prompt_with_default "NODE_ENV (development/production)" "development") + echo "" + + # Stripe Webhook Secrets + echo -e "${YELLOW}Stripe Webhook Signing Secrets:${NC}" + echo -e "${BLUE}โ„น๏ธ Get from: Stripe Dashboard โ†’ Developers โ†’ Webhooks โ†’ Signing secret${NC}" + stripe_webhook_secret_sandbox=$(prompt_with_default "Stripe Webhook Signing Secret (SANDBOX)" "whsec_xxxxx") + stripe_webhook_secret_production=$(prompt_with_default "Stripe Webhook Signing Secret (PRODUCTION)" "whsec_xxxxx") + echo "" + + # Stripe Product IDs + echo -e "${YELLOW}Stripe Product IDs:${NC}" + echo -e "${BLUE}โ„น๏ธ Get from: Stripe Dashboard โ†’ Products${NC}" + stripe_product_id_sandbox=$(prompt_with_default "Stripe Product ID (SANDBOX)" "prod_xxxxx") + stripe_product_id_production=$(prompt_with_default "Stripe Product ID (PRODUCTION)" "prod_xxxxx") + echo "" + + # Stripe Publishable Keys + echo -e "${YELLOW}Stripe API Keys - Publishable (Public):${NC}" + echo -e "${BLUE}โ„น๏ธ Get from: Stripe Dashboard โ†’ Developers โ†’ API keys${NC}" + stripe_publishable_key_sandbox=$(prompt_with_default "Stripe Publishable Key (SANDBOX)" "pk_test_xxxxx") + stripe_publishable_key_production=$(prompt_with_default "Stripe Publishable Key (PRODUCTION)" "pk_live_xxxxx") + echo "" + + # Stripe Secret Keys + echo -e "${YELLOW}Stripe API Keys - Secret (Private):${NC}" + echo -e "${BLUE}โ„น๏ธ Get from: Stripe Dashboard โ†’ Developers โ†’ API keys${NC}" + echo -e "${RED}โš ๏ธ NEVER expose these publicly!${NC}" + stripe_secret_key_sandbox=$(prompt_with_default "Stripe Secret Key (SANDBOX)" "sk_test_xxxxx") + stripe_secret_key_production=$(prompt_with_default "Stripe Secret Key (PRODUCTION)" "sk_live_xxxxx") + echo "" + + # Root Configuration + echo -e "${YELLOW}Root Platform Configuration:${NC}" + # Use the cm_key from .root-config.json if it exists + if [ -n "$cm_key" ]; then + root_collection_module_key="$cm_key" + echo -e "${BLUE}โ„น๏ธ Using Collection Module Key from .root-config.json: ${root_collection_module_key}${NC}" + else + root_collection_module_key=$(prompt_with_default "Root Collection Module Key (must match .root-config.json)" "my_collection_module_cm_stripe") + fi + echo "" + + echo -e "${YELLOW}Root API Keys:${NC}" + echo -e "${BLUE}โ„น๏ธ Get from: Root Platform โ†’ Settings โ†’ API Keys${NC}" + root_api_key_sandbox=$(prompt_with_default "Root API Key (SANDBOX)" "sandbox_xxxxx") + root_api_key_production=$(prompt_with_default "Root API Key (PRODUCTION)" "production_xxxxx") + echo "" + + echo -e "${YELLOW}Root API Base URLs:${NC}" + root_base_url_sandbox=$(prompt_with_default "Root Base URL (SANDBOX)" "https://sandbox.rootplatform.com/v1/insurance") + root_base_url_production=$(prompt_with_default "Root Base URL (PRODUCTION)" "https://api.rootplatform.com/v1/insurance") + echo "" + + # Optional Configuration + echo -e "${YELLOW}Optional Configuration:${NC}" + time_delay=$(prompt_with_default "Time delay for processing in milliseconds" "10000") + echo "" + + # Generate env.ts file + cat > code/env.ts << EOF +/** + * Environment Configuration + * + * IMPORTANT: Never commit this file to version control! + * The .gitignore file should exclude it. + * + * Generated by setup.sh on $(date) + */ + +// ============================================================================ +// ENVIRONMENT +// ============================================================================ +export const NODE_ENV = '${node_env}'; + +// ============================================================================ +// PAYMENT PROVIDER CONFIGURATION (Stripe) +// ============================================================================ + +// Webhook Signing Secrets +export const STRIPE_WEBHOOK_SIGNING_SECRET_LIVE = '${stripe_webhook_secret_production}'; +export const STRIPE_WEBHOOK_SIGNING_SECRET_TEST = '${stripe_webhook_secret_sandbox}'; + +// Product IDs +export const STRIPE_PRODUCT_ID_LIVE = '${stripe_product_id_production}'; +export const STRIPE_PRODUCT_ID_TEST = '${stripe_product_id_sandbox}'; + +// API Keys - Publishable (Public) +export const STRIPE_PUBLISHABLE_KEY_LIVE = '${stripe_publishable_key_production}'; +export const STRIPE_PUBLISHABLE_KEY_TEST = '${stripe_publishable_key_sandbox}'; + +// API Keys - Secret (Private) +// NEVER expose these publicly! +export const STRIPE_SECRET_KEY_LIVE = '${stripe_secret_key_production}'; +export const STRIPE_SECRET_KEY_TEST = '${stripe_secret_key_sandbox}'; + +// ============================================================================ +// ROOT PLATFORM CONFIGURATION +// ============================================================================ + +// Collection Module Key +export const ROOT_COLLECTION_MODULE_KEY = '${root_collection_module_key}'; + +// Root API Keys +export const ROOT_API_KEY_LIVE = '${root_api_key_production}'; +export const ROOT_API_KEY_SANDBOX = '${root_api_key_sandbox}'; + +// Root API Base URLs +export const ROOT_BASE_URL_LIVE = '${root_base_url_production}'; +export const ROOT_BASE_URL_SANDBOX = '${root_base_url_sandbox}'; + +// ============================================================================ +// OPTIONAL CONFIGURATION +// ============================================================================ + +export const TIME_DELAY_IN_MILLISECONDS = '${time_delay}'; +EOF + + echo -e "${GREEN}โœ“ Created code/env.ts with your configuration${NC}" + echo -e "${RED}โš ๏ธ Remember: Never commit code/env.ts to version control!${NC}" +else + echo -e "${GREEN}โœ“ code/env.ts already exists and configured${NC}" +fi +echo "" + +# ============================================================================ +# Step 6: Run validation +# ============================================================================ +echo -e "${YELLOW}โœ“ Step 6: Running validation checks...${NC}" +if npm run lint > /dev/null 2>&1; then + echo -e "${GREEN}โœ“ Linting passed${NC}" +else + echo -e "${YELLOW}โš ๏ธ Linting has warnings (this is normal for a template)${NC}" +fi + +if npm run build > /dev/null 2>&1; then + echo -e "${GREEN}โœ“ Build successful${NC}" +else + echo -e "${RED}โŒ Build failed - please check your configuration${NC}" + echo -e "${BLUE}โ„น๏ธ You may need to review code/env.ts and .root-config.json${NC}" +fi +echo "" + +# ============================================================================ +# Step 7: Summary and Next Steps +# ============================================================================ +echo -e "${BLUE}โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—${NC}" +echo -e "${BLUE}โ•‘ Setup Complete! ๐ŸŽ‰ โ•‘${NC}" +echo -e "${BLUE}โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•${NC}" +echo "" +echo -e "${GREEN}Configuration Summary:${NC}" +echo -e " ${CYAN}โœ“${NC} Node.js version: $(node -v)" +if command -v rp &> /dev/null; then + echo -e " ${CYAN}โœ“${NC} Root Platform CLI (rp) installed" +else + echo -e " ${YELLOW}โš ${NC} Root Platform CLI (rp) not installed" +fi +echo -e " ${CYAN}โœ“${NC} Dependencies installed" +echo -e " ${CYAN}โœ“${NC} .root-config.json configured" +echo -e " ${CYAN}โœ“${NC} .root-auth configured" +echo -e " ${CYAN}โœ“${NC} code/env.ts configured" +echo "" +echo -e "${YELLOW}Files Created/Updated:${NC}" +echo -e " ${BLUE}โ†’${NC} ${CYAN}stripe_collection_module/.root-config.json${NC}" +echo -e " ${BLUE}โ†’${NC} ${CYAN}stripe_collection_module/.root-auth${NC}" +echo -e " ${BLUE}โ†’${NC} ${CYAN}stripe_collection_module/code/env.ts${NC}" +echo "" +echo -e "${GREEN}Next Steps:${NC}" +echo "" +echo -e "${CYAN}1. Before running commands, ensure correct Node version:${NC}" +echo -e " ${YELLOW}cd stripe_collection_module && nvm use${NC}" +echo "" + +# Show rp CLI installation reminder if not installed +if ! command -v rp &> /dev/null; then + echo -e "${CYAN}2. Install Root Platform CLI (required for deployment):${NC}" + echo -e " ${YELLOW}npm install -g root-platform-cli${NC}" + echo "" + STEP_OFFSET=1 +else + STEP_OFFSET=0 +fi + +echo -e "${CYAN}$((2 + STEP_OFFSET)). Review and customize your code:${NC}" +echo -e " ${BLUE}โ†’${NC} Implement controllers in ${YELLOW}code/controllers/${NC}" +echo -e " ${BLUE}โ†’${NC} Add your business logic to services" +echo -e " ${BLUE}โ†’${NC} Update ${YELLOW}README.md${NC} with your module details" +echo "" +echo -e "${CYAN}$((3 + STEP_OFFSET)). Development commands:${NC}" +echo -e " ${YELLOW}npm run lint${NC} - Check code quality" +echo -e " ${YELLOW}npm run test${NC} - Run tests" +echo -e " ${YELLOW}npm run test:integration${NC} - Run integration tests" +echo -e " ${YELLOW}npm run build${NC} - Build the module" +echo "" +echo -e "${CYAN}$((4 + STEP_OFFSET)). Deploy when ready:${NC}" +echo -e " ${BLUE}โ†’${NC} Deployment uses ${YELLOW}rp push${NC} followed by publish" +echo -e " ${YELLOW}npm run deploy:sandbox${NC} - Deploy to sandbox" +echo -e " ${YELLOW}npm run deploy:production${NC} - Deploy to production" +echo -e " ${YELLOW}npm run deploy:dry-run:sandbox${NC} - Test deployment (sandbox)" +echo -e " ${YELLOW}npm run deploy:dry-run:production${NC} - Test deployment (production)" +echo "" +echo -e "${CYAN}$((5 + STEP_OFFSET)). Review documentation:${NC}" +echo -e " ${BLUE}โ†’${NC} ${YELLOW}stripe_collection_module/docs/SETUP.md${NC}" +echo -e " ${BLUE}โ†’${NC} ${YELLOW}stripe_collection_module/docs/DEPLOYMENT.md${NC}" +echo -e " ${BLUE}โ†’${NC} ${YELLOW}stripe_collection_module/docs/CUSTOMIZING.md${NC}" +echo "" +echo -e "${GREEN}โœจ Happy coding!${NC}" +echo "" + diff --git a/stripe_collection_module/.cursor/mcps/collection-module.mcp b/stripe_collection_module/.cursor/mcps/collection-module.mcp new file mode 100644 index 0000000..4303b1a --- /dev/null +++ b/stripe_collection_module/.cursor/mcps/collection-module.mcp @@ -0,0 +1,586 @@ +# Collection Module Architecture Patterns + +This MCP provides architectural patterns and guidelines for the Stripe Collection Module. + +## Architecture Overview + +### Layer Structure +``` +Entry Points (main.ts, webhook-hooks) + โ†“ +Lifecycle Hooks & Controllers (orchestration) + โ†“ +Services (business logic) + โ†“ +Clients & Adapters (infrastructure) + โ†“ +External APIs (Stripe, Root) +``` + +### Key Principles +- **Separation of Concerns**: Each layer has single responsibility +- **Dependency Injection**: All dependencies via constructor +- **Type Safety**: TypeScript throughout +- **Testability**: Unit tests with mocked dependencies + +## Clients Pattern + +### Purpose +Thin wrappers around external API SDKs. + +### Structure +```typescript +import Stripe from 'stripe'; +import { getConfigService } from '../services/config-instance'; + +export default class StripeClient { + public stripeSDK: Stripe; + + constructor() { + const config = getConfigService(); + this.stripeSDK = new Stripe(config.get('stripeSecretKey')); + } +} +``` + +### Usage +```typescript +import StripeClient from '../clients/stripe-client'; + +const stripeClient = new StripeClient(); + +// Direct SDK access +const customer = await stripeClient.stripeSDK.customers.create({ + email: 'customer@example.com', +}); +``` + +### Guidelines +- Keep clients thin - expose the SDK +- Use ConfigurationService for credentials +- Don't wrap every SDK method +- Error handling done in services + +## Services Pattern + +### Purpose +Business logic layer that orchestrates operations. + +### Structure +```typescript +import { LogService } from './log.service'; +import StripeClient from '../clients/stripe-client'; + +export class MyService { + constructor( + private readonly logService: LogService, + private readonly stripeClient: StripeClient + ) {} + + async performOperation(params: OperationParams): Promise { + this.logService.info('Starting operation', 'MyService', params); + + try { + // 1. Fetch data + const data = await this.stripeClient.stripeSDK.someMethod(); + + // 2. Transform data + const transformed = this.transformData(data); + + // 3. Update systems + await this.updateSomething(transformed); + + this.logService.info('Operation completed', 'MyService'); + return result; + } catch (error: any) { + this.logService.error('Operation failed', 'MyService', { params }, error); + throw error; + } + } + + private transformData(data: any) { + // Business logic here + return transformed; + } +} +``` + +### Available Services +- **LogService**: Structured JSON logging +- **ConfigurationService**: Type-safe configuration +- **RenderService**: HTML generation for dashboard +- **RootService**: Root Platform operations + +### Guidelines +- Inject all dependencies via constructor +- Use LogService for all logging +- Handle errors with context +- Keep focused (single responsibility) +- Write unit tests with mocked dependencies + +## Adapters Pattern + +### Purpose +Data transformation between Stripe and Root formats. + +### Structure +```typescript +import Stripe from 'stripe'; +import * as root from '@rootplatform/node-sdk'; + +export default class StripeToRootAdapter { + convertInvoiceToRootPayment( + invoice: Stripe.Invoice, + params: ConvertInvoiceParams + ) { + return { + status: params.status, + failure_reason: params.failureReason || invoice.last_finalization_error?.message, + failure_action: params.failureAction || root.FailureAction.BlockRetry, + }; + } + + convertCustomerToAppData(customer: Stripe.Customer) { + return { + stripe_customer_id: customer.id, + stripe_email: customer.email, + stripe_default_payment_method: customer.invoice_settings.default_payment_method, + stripe_created_at: new Date(customer.created * 1000).toISOString(), + }; + } +} +``` + +### Usage +```typescript +const adapter = new StripeToRootAdapter(); + +// Convert webhook data +const paymentUpdate = adapter.convertInvoiceToRootPayment(invoice, { + status: PaymentStatus.Successful, +}); + +// Store customer info +const appData = adapter.convertCustomerToAppData(customer); +await rootService.updatePolicyAppData(policyId, appData); +``` + +### Guidelines +- Keep adapters pure (no side effects) +- Handle null/undefined values gracefully +- Add comprehensive tests +- Don't make API calls in adapters +- Don't add business logic + +## Controllers Pattern + +### Purpose +Event processors that orchestrate service calls. + +### Structure +```typescript +import { LogService } from '../../services/log.service'; +import { RootService } from '../../services/root.service'; +import StripeClient from '../../clients/stripe-client'; + +export class InvoicePaidController { + constructor( + private readonly logService: LogService, + private readonly rootService: RootService, + private readonly stripeClient: StripeClient + ) {} + + async handle(invoice: Stripe.Invoice): Promise { + this.logService.info('Processing invoice.paid', 'InvoicePaidController', { + invoiceId: invoice.id, + }); + + // 1. Validate input + if (!invoice.metadata?.rootPaymentId) { + this.logService.warn('No payment ID in invoice', 'InvoicePaidController'); + return; + } + + // 2. Coordinate services + await this.rootService.updatePaymentStatus({ + paymentId: invoice.metadata.rootPaymentId, + status: PaymentStatus.Successful, + }); + + this.logService.info('Invoice processed successfully', 'InvoicePaidController'); + } +} +``` + +### Guidelines +- Keep controllers thin (< 100 lines) +- Inject all dependencies via constructor +- Use services for business logic +- Log important steps +- Handle errors gracefully +- Return early for validation failures + +## Lifecycle Hooks Pattern + +### Purpose +Root Platform callback functions. + +### Structure +```typescript +import { getLogService } from '../services/log-instance'; +import { RootService } from '../services/root.service'; +import StripeClient from '../clients/stripe-client'; + +export async function afterPolicyPaymentMethodAssigned({ policy }) { + const logService = getLogService(); + const rootService = new RootService(logService); + const stripeClient = new StripeClient(); + + logService.info('Payment method assigned', 'afterPolicyPaymentMethodAssigned', { + policyId: policy.policy_id, + }); + + try { + // 1. Get payment method + const paymentMethod = await rootService.getPolicyPaymentMethod(policy.policy_id); + + // 2. Process with Stripe + const customerId = policy.app_data?.stripe_customer_id; + await stripeClient.stripeSDK.paymentMethods.attach( + paymentMethod.module.payment_method, + { customer: customerId } + ); + + logService.info('Payment method attached', 'afterPolicyPaymentMethodAssigned'); + } catch (error: any) { + logService.error('Failed to attach payment method', 'afterPolicyPaymentMethodAssigned', {}, error); + throw error; + } +} +``` + +### Available Hooks +- **Payment Method**: renderCreatePaymentMethod, createPaymentMethod, renderViewPaymentMethod +- **Policy**: afterPolicyIssued, afterPolicyPaymentMethodAssigned, afterPolicyUpdated, afterPolicyCancelled +- **Payment**: afterPaymentCreated, afterPaymentUpdated +- **Alteration**: afterAlterationPackageApplied + +### Guidelines +- Log hook entry and exit +- Validate input parameters +- Handle errors gracefully +- Use services for business logic +- Keep hooks thin (orchestration only) + +## Dependency Injection Pattern + +### Container Registration +```typescript +// core/container.setup.ts +container.register( + ServiceToken.MY_SERVICE, + (c) => { + const logService = c.resolve(ServiceToken.LOG_SERVICE); + const config = c.resolve(ServiceToken.CONFIG_SERVICE); + + const { MyService } = require('../services/my.service'); + return new MyService(logService, config); + }, + ServiceLifetime.SINGLETON +); +``` + +### Service Usage +```typescript +import { getContainer } from './core/container.setup'; +import { ServiceToken } from './core/container'; + +const container = getContainer(); +const myService = container.resolve(ServiceToken.MY_SERVICE); +await myService.doSomething(); +``` + +### Service Lifetimes +- **SINGLETON**: LogService, ConfigService (shared state/expensive to create) +- **TRANSIENT**: Controllers (per-request instances) + +### Guidelines +- Use ServiceTokens (Symbols) for type safety +- Register services at startup +- Inject dependencies via constructor +- Test with mocked services + +## Logging Pattern + +### Usage +```typescript +import { getLogService } from '../services/log-instance'; + +const logService = getLogService(); + +// Basic logging +logService.info('Operation completed', 'MyService'); +logService.error('Operation failed', 'MyService', {}, error); + +// With metadata +logService.info('Payment created', 'PaymentService', { + paymentId: 'payment_123', + amount: 10000, +}); + +// With correlation ID +const correlationId = logService.generateCorrelationId(); +logService.info('Processing request', 'WebhookHandler'); +// ... all subsequent logs include this correlation ID +logService.clearCorrelationId(); +``` + +### Guidelines +- Always provide context (service/controller name) +- Include relevant metadata +- Use appropriate log levels (DEBUG, INFO, WARN, ERROR) +- Don't log sensitive data + +## Testing Patterns + +### Service Testing +```typescript +describe('MyService', () => { + let service: MyService; + let mockLogService: jest.Mocked; + let mockStripeClient: jest.Mocked; + + beforeEach(() => { + mockLogService = { + info: jest.fn(), + error: jest.fn(), + } as any; + + mockStripeClient = { + stripeSDK: { + customers: { + create: jest.fn().mockResolvedValue({ id: 'cus_123' }), + }, + }, + } as any; + + service = new MyService(mockLogService, mockStripeClient); + }); + + it('should perform operation successfully', async () => { + const result = await service.performOperation({ id: '123' }); + + expect(result).toBeDefined(); + expect(mockLogService.info).toHaveBeenCalledWith( + 'Starting operation', + 'MyService', + { id: '123' } + ); + }); +}); +``` + +### Controller Testing +```typescript +describe('InvoicePaidController', () => { + let controller: InvoicePaidController; + let mockRootService: jest.Mocked; + + beforeEach(() => { + mockRootService = { + updatePaymentStatus: jest.fn(), + } as any; + + controller = new InvoicePaidController( + mockLogService, + mockRootService, + mockStripeClient + ); + }); + + it('should update payment status', async () => { + const invoice = { id: 'in_123', metadata: { rootPaymentId: 'pay_123' } }; + + await controller.handle(invoice); + + expect(mockRootService.updatePaymentStatus).toHaveBeenCalledWith({ + paymentId: 'pay_123', + status: PaymentStatus.Successful, + }); + }); +}); +``` + +## Error Handling Pattern + +### In Services +```typescript +try { + const result = await externalApiCall(); + return result; +} catch (error: any) { + this.logService.error( + 'API call failed', + 'MyService', + { params }, + error + ); + throw error; // Re-throw after logging +} +``` + +### In Controllers +```typescript +async handle(event: Event): Promise { + try { + await this.processEvent(event); + } catch (error: any) { + this.logService.error('Event processing failed', 'MyController', { event }, error); + throw error; + } +} +``` + +### In Lifecycle Hooks +```typescript +export async function afterPolicyIssued({ policy }) { + const logService = getLogService(); + + try { + await processPolicyIssued(policy); + } catch (error: any) { + logService.error('Failed to process policy issued', 'afterPolicyIssued', {}, error); + throw error; // Root Platform will handle retry + } +} +``` + +## Common Patterns + +### Orchestration Pattern (Service) +```typescript +async createCustomerAndSubscription(policy: Policy) { + // 1. Create customer + const customer = await this.stripeClient.stripeSDK.customers.create({ + email: policy.email, + }); + + // 2. Attach payment method + await this.stripeClient.stripeSDK.paymentMethods.attach( + paymentMethodId, + { customer: customer.id } + ); + + // 3. Create subscription + const subscription = await this.stripeClient.stripeSDK.subscriptions.create({ + customer: customer.id, + items: [{ price: priceId }], + }); + + // 4. Update Root + await this.rootService.updatePolicyAppData(policy.policy_id, { + stripe_customer_id: customer.id, + stripe_subscription_id: subscription.id, + }); + + return { customer, subscription }; +} +``` + +### Transformation Pattern (Adapter) +```typescript +mapStripeStatusToRoot(stripeStatus: string): PaymentStatus { + const mapping = { + paid: PaymentStatus.Successful, + open: PaymentStatus.Pending, + void: PaymentStatus.Cancelled, + uncollectible: PaymentStatus.Failed, + }; + return mapping[stripeStatus] || PaymentStatus.Failed; +} +``` + +### Idempotency Pattern (Hook) +```typescript +export async function afterPaymentCreated({ policy, payment }) { + // Check if already processed + const existingPaymentIntent = payment.metadata?.stripe_payment_intent_id; + if (existingPaymentIntent) { + logService.info('Payment already processed', 'afterPaymentCreated'); + return; + } + + // Process payment + const paymentIntent = await createPaymentIntent(payment); + + // Store reference to prevent re-processing + await updatePaymentMetadata(payment.payment_id, { + stripe_payment_intent_id: paymentIntent.id, + }); +} +``` + +## Best Practices Summary + +### Do โœ… +- Use dependency injection +- Log with context and metadata +- Handle errors gracefully +- Write tests for all business logic +- Keep layers separated +- Use TypeScript types +- Return early for validation failures + +### Don't โŒ +- Put business logic in controllers or hooks +- Create dependencies with `new` (use DI) +- Skip error handling +- Make clients stateful +- Skip input validation +- Log sensitive data +- Mix layer responsibilities + +## Quick Reference + +### Creating New Features +1. Create Service (if needed) - `code/services/` +2. Create Controller (if handling events) - `code/controllers/` +3. Register in DI Container - `code/core/container.setup.ts` +4. Wire in Entry Point - `code/webhook-hooks.ts` or `code/lifecycle-hooks/` +5. Add Tests - `__tests__/` + +### File Locations +- **Services**: `code/services/` - Business logic +- **Controllers**: `code/controllers/` - Event processors +- **Clients**: `code/clients/` - API wrappers +- **Adapters**: `code/adapters/` - Data transformation +- **Lifecycle Hooks**: `code/lifecycle-hooks/` - Root callbacks +- **Core/DI**: `code/core/` - Dependency injection +- **Utils**: `code/utils/` - Reusable helpers + +### Common Imports +```typescript +// Logging +import { getLogService } from '../services/log-instance'; + +// Configuration +import { getConfigService } from '../services/config-instance'; + +// Clients +import StripeClient from '../clients/stripe-client'; +import rootClient from '../clients/root-client'; + +// Services +import { RootService } from '../services/root.service'; +import { StripeService } from '../services/stripe.service'; +import { RenderService } from '../services/render.service'; + +// DI Container +import { getContainer } from '../core/container.setup'; +import { ServiceToken } from '../core/container'; + +// Types +import * as root from '@rootplatform/node-sdk'; +import Stripe from 'stripe'; +``` + diff --git a/stripe_collection_module/.eslintrc.js b/stripe_collection_module/.eslintrc.js index a276107..cc75e2c 100644 --- a/stripe_collection_module/.eslintrc.js +++ b/stripe_collection_module/.eslintrc.js @@ -188,6 +188,7 @@ module.exports = { es2020: true, node: true, mocha: true, + jest: true, }, extends: [ 'eslint:recommended', @@ -224,6 +225,12 @@ module.exports = { }, ignorePatterns: ['node_modules*/'], overrides: [ + { + files: ['__tests__/**/*', '**/*.test.ts', '**/*.spec.ts'], + env: { + jest: true, + }, + }, { files: ['./**/*.js'], rules: { diff --git a/stripe_collection_module/.gitignore b/stripe_collection_module/.gitignore new file mode 100644 index 0000000..df42d51 --- /dev/null +++ b/stripe_collection_module/.gitignore @@ -0,0 +1,3 @@ +.root-auth +node_modules +code/**/env.* diff --git a/stripe_collection_module/.nvmrc b/stripe_collection_module/.nvmrc new file mode 100644 index 0000000..35f4978 --- /dev/null +++ b/stripe_collection_module/.nvmrc @@ -0,0 +1,2 @@ +20 + diff --git a/stripe_collection_module/.prettierrc b/stripe_collection_module/.prettierrc new file mode 100644 index 0000000..63d982b --- /dev/null +++ b/stripe_collection_module/.prettierrc @@ -0,0 +1,11 @@ +{ + "semi": true, + "trailingComma": "es5", + "singleQuote": true, + "printWidth": 80, + "tabWidth": 2, + "useTabs": false, + "arrowParens": "always", + "endOfLine": "lf" +} + diff --git a/stripe_collection_module/.root-config.json b/stripe_collection_module/.root-config.json index d79e97f..5e688b3 100644 --- a/stripe_collection_module/.root-config.json +++ b/stripe_collection_module/.root-config.json @@ -1,10 +1,8 @@ { - "collectionModuleKey": "my_collection_module_cm_stripe", - "collectionModuleName": "Stripe Collection Module", + "collectionModuleKey": "demo_stripe_v3", + "collectionModuleName": "Demo Stripe V2", "organizationId": "00000000-0000-0000-0000-000000000001", "host": "http://localhost:4000", - "settings": { - "legacyCodeExecution": false - }, + "settings": {}, "manualTransactions": [] -} \ No newline at end of file +} diff --git a/stripe_collection_module/.root-config.json.sample b/stripe_collection_module/.root-config.json.sample new file mode 100644 index 0000000..29ab2c5 --- /dev/null +++ b/stripe_collection_module/.root-config.json.sample @@ -0,0 +1,13 @@ +{ + "collectionModuleKey": "my_collection_module_cm_stripe", + "collectionModuleName": "Stripe Collection Module", + "organizationId": "00000000-0000-0000-0000-000000000001", + "host": "http://localhost:4000", + "settings": { + "legacyCodeExecution": false + }, + "manualTransactions": [] +} + + + diff --git a/stripe_collection_module/.vscode/launch.json b/stripe_collection_module/.vscode/launch.json new file mode 100644 index 0000000..b734406 --- /dev/null +++ b/stripe_collection_module/.vscode/launch.json @@ -0,0 +1,96 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Jest: Current File", + "program": "${workspaceFolder}/node_modules/.bin/jest", + "args": [ + "${fileBasenameNoExtension}", + "--config", + "jest.config.js", + "--runInBand", + "--no-cache" + ], + "console": "integratedTerminal", + "internalConsoleOptions": "neverOpen", + "windows": { + "program": "${workspaceFolder}/node_modules/jest/bin/jest" + } + }, + { + "type": "node", + "request": "launch", + "name": "Jest: All Tests", + "program": "${workspaceFolder}/node_modules/.bin/jest", + "args": [ + "--config", + "jest.config.js", + "--runInBand", + "--no-cache" + ], + "console": "integratedTerminal", + "internalConsoleOptions": "neverOpen", + "windows": { + "program": "${workspaceFolder}/node_modules/jest/bin/jest" + } + }, + { + "type": "node", + "request": "launch", + "name": "Jest: Watch Mode", + "program": "${workspaceFolder}/node_modules/.bin/jest", + "args": [ + "--config", + "jest.config.js", + "--watch", + "--runInBand" + ], + "console": "integratedTerminal", + "internalConsoleOptions": "neverOpen", + "windows": { + "program": "${workspaceFolder}/node_modules/jest/bin/jest" + } + }, + { + "type": "node", + "request": "launch", + "name": "Debug TypeScript File", + "program": "${workspaceFolder}/node_modules/.bin/ts-node", + "args": [ + "${relativeFile}" + ], + "cwd": "${workspaceFolder}", + "console": "integratedTerminal", + "internalConsoleOptions": "neverOpen" + }, + { + "type": "node", + "request": "attach", + "name": "Attach to Process", + "port": 9229, + "restart": true, + "skipFiles": [ + "/**" + ] + }, + { + "type": "node", + "request": "launch", + "name": "Debug Lambda Locally", + "program": "${workspaceFolder}/code/main.ts", + "preLaunchTask": "npm: build", + "outFiles": [ + "${workspaceFolder}/dist/**/*.js" + ], + "env": { + "NODE_ENV": "development", + "AWS_REGION": "us-east-1" + }, + "console": "integratedTerminal", + "internalConsoleOptions": "neverOpen" + } + ] +} + diff --git a/stripe_collection_module/.vscode/settings.json b/stripe_collection_module/.vscode/settings.json new file mode 100644 index 0000000..d4e0653 --- /dev/null +++ b/stripe_collection_module/.vscode/settings.json @@ -0,0 +1,87 @@ +{ + // Editor + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.fixAll.eslint": "explicit", + "source.organizeImports": "explicit" + }, + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.tabSize": 2, + "editor.rulers": [80, 120], + + // TypeScript + "typescript.tsdk": "node_modules/typescript/lib", + "typescript.enablePromptUseWorkspaceTsdk": true, + "typescript.preferences.importModuleSpecifier": "relative", + "typescript.updateImportsOnFileMove.enabled": "always", + + // JavaScript/TypeScript specific + "[javascript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[typescript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[json]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[jsonc]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + + // ESLint + "eslint.validate": [ + "javascript", + "javascriptreact", + "typescript", + "typescriptreact" + ], + "eslint.format.enable": false, + + // Prettier + "prettier.requireConfig": true, + + // Files + "files.exclude": { + "**/.git": true, + "**/.DS_Store": true, + "**/node_modules": true, + "**/dist": true, + "**/coverage": true + }, + "files.watcherExclude": { + "**/node_modules/**": true, + "**/dist/**": true, + "**/coverage/**": true + }, + "files.eol": "\n", + "files.trimTrailingWhitespace": true, + "files.insertFinalNewline": true, + + // Search + "search.exclude": { + "**/node_modules": true, + "**/dist": true, + "**/coverage": true, + "**/*.log": true + }, + + // Git + "git.ignoreLimitWarning": true, + + // Jest + "jest.autoRun": "off", + "jest.showCoverageOnLoad": false, + + // Terminal + "terminal.integrated.env.osx": { + "NODE_ENV": "development" + }, + "terminal.integrated.env.linux": { + "NODE_ENV": "development" + }, + + // Problems + "problems.showCurrentInStatus": true +} + diff --git a/stripe_collection_module/.vscode/tasks.json b/stripe_collection_module/.vscode/tasks.json new file mode 100644 index 0000000..5787e38 --- /dev/null +++ b/stripe_collection_module/.vscode/tasks.json @@ -0,0 +1,57 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "npm: build", + "type": "npm", + "script": "build", + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": [ + "$tsc" + ] + }, + { + "label": "npm: test", + "type": "npm", + "script": "test", + "group": { + "kind": "test", + "isDefault": true + }, + "problemMatcher": [] + }, + { + "label": "npm: lint", + "type": "npm", + "script": "lint", + "problemMatcher": [ + "$eslint-stylish" + ] + }, + { + "label": "npm: lint:fix", + "type": "npm", + "script": "lint:fix", + "problemMatcher": [ + "$eslint-stylish" + ] + }, + { + "label": "npm: validate", + "type": "npm", + "script": "validate", + "problemMatcher": [] + }, + { + "label": "npm: test:watch", + "type": "npm", + "script": "test:watch", + "isBackground": true, + "problemMatcher": [] + } + ] +} + diff --git a/stripe_collection_module/__tests__/adapters/stripe-to-root-adapter.test.ts b/stripe_collection_module/__tests__/adapters/stripe-to-root-adapter.test.ts new file mode 100644 index 0000000..cb99577 --- /dev/null +++ b/stripe_collection_module/__tests__/adapters/stripe-to-root-adapter.test.ts @@ -0,0 +1,174 @@ +/** + * StripeToRootAdapter Tests + */ + +import Stripe from 'stripe'; +import * as root from '@rootplatform/node-sdk'; +import StripeToRootAdapter from '../../code/adapters/stripe-to-root-adapter'; + +describe('StripeToRootAdapter', () => { + let adapter: StripeToRootAdapter; + + beforeEach(() => { + adapter = new StripeToRootAdapter(); + }); + + describe('convertInvoiceToRootPayment', () => { + it('should convert invoice with successful status', () => { + const mockInvoice = { + id: 'inv_123', + amount_paid: 10000, + } as any; + + const result = adapter.convertInvoiceToRootPayment(mockInvoice, { + status: root.PaymentStatus.Successful, + }); + + expect(result).toEqual({ + status: root.PaymentStatus.Successful, + failure_reason: undefined, + failure_action: root.FailureAction.BlockRetry, + }); + }); + + it('should convert invoice with failed status and custom failure reason', () => { + const mockInvoice = { + id: 'inv_123', + } as any; + + const result = adapter.convertInvoiceToRootPayment(mockInvoice, { + status: root.PaymentStatus.Failed, + failureReason: 'Card declined', + failureAction: root.FailureAction.AllowRetry, + }); + + expect(result).toEqual({ + status: root.PaymentStatus.Failed, + failure_reason: 'Card declined', + failure_action: root.FailureAction.AllowRetry, + }); + }); + + it('should use invoice last_finalization_error when no failure reason provided', () => { + const mockInvoice = { + id: 'inv_123', + last_finalization_error: { + message: 'Payment failed due to insufficient funds', + }, + } as any; + + const result = adapter.convertInvoiceToRootPayment(mockInvoice, { + status: root.PaymentStatus.Failed, + }); + + expect(result).toEqual({ + status: root.PaymentStatus.Failed, + failure_reason: 'Payment failed due to insufficient funds', + failure_action: root.FailureAction.BlockRetry, + }); + }); + + it('should handle invoice with no error information', () => { + const mockInvoice = { + id: 'inv_123', + } as any; + + const result = adapter.convertInvoiceToRootPayment(mockInvoice, { + status: root.PaymentStatus.Pending, + }); + + expect(result).toEqual({ + status: root.PaymentStatus.Pending, + failure_reason: undefined, + failure_action: root.FailureAction.BlockRetry, + }); + }); + + it('should prioritize provided failure reason over invoice error', () => { + const mockInvoice = { + id: 'inv_123', + last_finalization_error: { + message: 'Invoice error message', + }, + } as any; + + const result = adapter.convertInvoiceToRootPayment(mockInvoice, { + status: root.PaymentStatus.Failed, + failureReason: 'Custom error message', + }); + + expect(result.failure_reason).toBe('Custom error message'); + }); + }); + + describe('convertCustomerToAppData', () => { + it('should convert customer with all fields', () => { + const mockCustomer = { + id: 'cus_123', + email: 'test@example.com', + invoice_settings: { + default_payment_method: 'pm_123', + }, + created: 1640995200, // 2022-01-01 00:00:00 UTC + } as any; + + const result = adapter.convertCustomerToAppData(mockCustomer); + + expect(result).toEqual({ + stripe_customer_id: 'cus_123', + stripe_email: 'test@example.com', + stripe_default_payment_method: 'pm_123', + stripe_created_at: '2022-01-01T00:00:00.000Z', + }); + }); + + it('should handle customer with null email', () => { + const mockCustomer = { + id: 'cus_123', + email: null, + invoice_settings: { + default_payment_method: null, + }, + created: 1640995200, + } as any; + + const result = adapter.convertCustomerToAppData(mockCustomer); + + expect(result).toEqual({ + stripe_customer_id: 'cus_123', + stripe_email: null, + stripe_default_payment_method: null, + stripe_created_at: '2022-01-01T00:00:00.000Z', + }); + }); + + it('should handle customer with no default payment method', () => { + const mockCustomer = { + id: 'cus_456', + email: 'user@example.com', + invoice_settings: { + default_payment_method: null, + }, + created: 1672531200, // 2023-01-01 00:00:00 UTC + } as any; + + const result = adapter.convertCustomerToAppData(mockCustomer); + + expect(result.stripe_default_payment_method).toBeNull(); + expect(result.stripe_created_at).toBe('2023-01-01T00:00:00.000Z'); + }); + + it('should correctly convert Unix timestamp to ISO string', () => { + const mockCustomer = { + id: 'cus_789', + email: 'test@test.com', + invoice_settings: {}, + created: 0, // Epoch + } as any; + + const result = adapter.convertCustomerToAppData(mockCustomer); + + expect(result.stripe_created_at).toBe('1970-01-01T00:00:00.000Z'); + }); + }); +}); diff --git a/stripe_collection_module/__tests__/clients/root-client.test.ts b/stripe_collection_module/__tests__/clients/root-client.test.ts new file mode 100644 index 0000000..6ba61d8 --- /dev/null +++ b/stripe_collection_module/__tests__/clients/root-client.test.ts @@ -0,0 +1,31 @@ +/** + * RootClient Tests + */ + +jest.mock('../../code/services/config-instance', () => ({ + getConfigService: jest.fn(() => ({ + get: jest.fn((key: string) => { + if (key === 'rootApiKey') return 'test_root_key'; + if (key === 'rootBaseUrl') return 'https://test.root.co.za'; + return null; + }), + })), +})); + +import rootClient from '../../code/clients/root-client'; + +describe('RootClient', () => { + it('should export the Root SDK namespace', () => { + expect(rootClient).toBeDefined(); + }); + + it('should have getPolicyById function', () => { + expect(rootClient.getPolicyById).toBeDefined(); + expect(typeof rootClient.getPolicyById).toBe('function'); + }); + + it('should have updatePaymentsAsync function', () => { + expect(rootClient.updatePaymentsAsync).toBeDefined(); + expect(typeof rootClient.updatePaymentsAsync).toBe('function'); + }); +}); diff --git a/stripe_collection_module/__tests__/clients/stripe-client.test.ts b/stripe_collection_module/__tests__/clients/stripe-client.test.ts new file mode 100644 index 0000000..57da9de --- /dev/null +++ b/stripe_collection_module/__tests__/clients/stripe-client.test.ts @@ -0,0 +1,35 @@ +/** + * StripeClient Tests + */ + +jest.mock('../../code/services/config-instance'); + +import StripeClient from '../../code/clients/stripe-client'; +import { setupConfigMock } from '../test-helpers'; + +describe('StripeClient', () => { + beforeEach(() => { + setupConfigMock(); + }); + + it('should instantiate with Stripe SDK', () => { + const client = new StripeClient(); + + expect(client).toBeDefined(); + expect(client.stripeSDK).toBeDefined(); + }); + + it('should have stripeSDK property', () => { + const client = new StripeClient(); + + expect(client.stripeSDK).toBeTruthy(); + expect(typeof client.stripeSDK).toBe('object'); + }); + + it('should create new instance each time', () => { + const client1 = new StripeClient(); + const client2 = new StripeClient(); + + expect(client1).not.toBe(client2); + }); +}); diff --git a/stripe_collection_module/__tests__/core/container.setup.test.ts b/stripe_collection_module/__tests__/core/container.setup.test.ts new file mode 100644 index 0000000..d728416 --- /dev/null +++ b/stripe_collection_module/__tests__/core/container.setup.test.ts @@ -0,0 +1,117 @@ +/** + * Container Setup Tests + */ + +import { + createContainer, + getContainer, + setContainer, + resetContainer, +} from '../../code/core/container.setup'; +import { ServiceToken } from '../../code/core/container'; + +describe('Container Setup', () => { + describe('createContainer', () => { + it('should create a container with all services registered', () => { + const container = createContainer(); + + expect(container).toBeDefined(); + expect(container.has(ServiceToken.CONFIG_SERVICE)).toBe(true); + expect(container.has(ServiceToken.LOG_SERVICE)).toBe(true); + expect(container.has(ServiceToken.STRIPE_CLIENT)).toBe(true); + expect(container.has(ServiceToken.ROOT_CLIENT)).toBe(true); + expect(container.has(ServiceToken.ROOT_SERVICE)).toBe(true); + expect(container.has(ServiceToken.STRIPE_SERVICE)).toBe(true); + }); + + it('should allow resolving ConfigService', () => { + const container = createContainer(); + const configService = container.resolve( + ServiceToken.CONFIG_SERVICE + ) as any; + + expect(configService).toBeDefined(); + expect(configService.get).toBeDefined(); + }); + + it('should allow resolving LogService', () => { + const container = createContainer(); + const logService = container.resolve(ServiceToken.LOG_SERVICE) as any; + + expect(logService).toBeDefined(); + expect(logService.info).toBeDefined(); + }); + + it('should have RootService registered', () => { + const container = createContainer(); + + expect(container.has(ServiceToken.ROOT_SERVICE)).toBe(true); + }); + + it('should have StripeService registered', () => { + const container = createContainer(); + + expect(container.has(ServiceToken.STRIPE_SERVICE)).toBe(true); + }); + + it('should have RenderService registered', () => { + const container = createContainer(); + + expect(container.has(ServiceToken.RENDER_SERVICE)).toBe(true); + }); + + // TODO: Add controller registration tests when controllers are implemented + // it('should have controllers registered', () => { + // const container = createContainer(); + // + // expect(container.has(ServiceToken.YOUR_CONTROLLER)).toBe(true); + // }); + }); + + describe('getContainer', () => { + beforeEach(() => { + resetContainer(); + }); + + it('should return a container instance', () => { + const container = getContainer(); + + expect(container).toBeDefined(); + }); + + it('should return the same instance on multiple calls', () => { + const container1 = getContainer(); + const container2 = getContainer(); + + expect(container1).toBe(container2); + }); + + it('should create container if not exists', () => { + resetContainer(); + const container = getContainer(); + + expect(container).toBeDefined(); + expect(container.has(ServiceToken.LOG_SERVICE)).toBe(true); + }); + }); + + describe('setContainer', () => { + it('should allow setting a custom container', () => { + const customContainer = createContainer(); + setContainer(customContainer); + + const retrieved = getContainer(); + expect(retrieved).toBe(customContainer); + }); + }); + + describe('resetContainer', () => { + it('should reset the global container', () => { + const container1 = getContainer(); + resetContainer(); + const container2 = getContainer(); + + expect(container1).not.toBe(container2); + }); + }); +}); diff --git a/stripe_collection_module/__tests__/core/container.test.ts b/stripe_collection_module/__tests__/core/container.test.ts new file mode 100644 index 0000000..9b172b5 --- /dev/null +++ b/stripe_collection_module/__tests__/core/container.test.ts @@ -0,0 +1,318 @@ +/** + * Container Tests + */ + +import { + Container, + ServiceLifetime, + ServiceToken, +} from '../../code/core/container'; + +// Mock services for testing +class MockServiceA { + constructor(public name: string = 'ServiceA') {} +} + +class MockServiceB { + constructor( + public serviceA: MockServiceA, + public name: string = 'ServiceB', + ) {} +} + +describe('Container', () => { + let container: Container; + + beforeEach(() => { + container = new Container(); + }); + + describe('Service Registration', () => { + it('should register a service', () => { + container.register( + 'MockServiceA', + () => new MockServiceA(), + ServiceLifetime.SINGLETON, + ); + + expect(container.has('MockServiceA')).toBe(true); + }); + + it('should register multiple services', () => { + container.register( + 'ServiceA', + () => new MockServiceA(), + ServiceLifetime.SINGLETON, + ); + container.register( + 'ServiceB', + () => new MockServiceB(new MockServiceA()), + ServiceLifetime.SINGLETON, + ); + + expect(container.has('ServiceA')).toBe(true); + expect(container.has('ServiceB')).toBe(true); + }); + + it('should support symbol tokens', () => { + const token = Symbol('MockService'); + container.register( + token, + () => new MockServiceA(), + ServiceLifetime.SINGLETON, + ); + + expect(container.has(token)).toBe(true); + }); + }); + + describe('Service Resolution', () => { + it('should resolve a registered service', () => { + container.register( + 'MockServiceA', + () => new MockServiceA(), + ServiceLifetime.SINGLETON, + ); + + const service = container.resolve('MockServiceA'); + + expect(service).toBeInstanceOf(MockServiceA); + expect(service.name).toBe('ServiceA'); + }); + + it('should throw error when resolving unregistered service', () => { + expect(() => { + container.resolve('NonExistent'); + }).toThrow('Service not registered: NonExistent'); + }); + + it('should support dependency injection', () => { + container.register( + 'ServiceA', + () => new MockServiceA(), + ServiceLifetime.SINGLETON, + ); + + container.register( + 'ServiceB', + (c) => new MockServiceB(c.resolve('ServiceA')), + ServiceLifetime.SINGLETON, + ); + + const serviceB = container.resolve('ServiceB'); + + expect(serviceB).toBeInstanceOf(MockServiceB); + expect(serviceB.serviceA).toBeInstanceOf(MockServiceA); + expect(serviceB.name).toBe('ServiceB'); + }); + }); + + describe('Service Lifetimes', () => { + it('should return same instance for singleton services', () => { + container.register( + 'MockServiceA', + () => new MockServiceA(), + ServiceLifetime.SINGLETON, + ); + + const instance1 = container.resolve('MockServiceA'); + const instance2 = container.resolve('MockServiceA'); + + expect(instance1).toBe(instance2); + }); + + it('should return new instance for transient services', () => { + container.register( + 'MockServiceA', + () => new MockServiceA(), + ServiceLifetime.TRANSIENT, + ); + + const instance1 = container.resolve('MockServiceA'); + const instance2 = container.resolve('MockServiceA'); + + expect(instance1).not.toBe(instance2); + expect(instance1).toBeInstanceOf(MockServiceA); + expect(instance2).toBeInstanceOf(MockServiceA); + }); + }); + + describe('Service Replacement', () => { + it('should replace a registered service', () => { + container.register( + 'MockServiceA', + () => new MockServiceA('Original'), + ServiceLifetime.SINGLETON, + ); + + const original = container.resolve('MockServiceA'); + expect(original.name).toBe('Original'); + + container.replace( + 'MockServiceA', + () => new MockServiceA('Replaced'), + ServiceLifetime.SINGLETON, + ); + + const replaced = container.resolve('MockServiceA'); + expect(replaced.name).toBe('Replaced'); + expect(replaced).not.toBe(original); + }); + }); + + describe('Service Unregistration', () => { + it('should unregister a service', () => { + container.register( + 'MockServiceA', + () => new MockServiceA(), + ServiceLifetime.SINGLETON, + ); + + expect(container.has('MockServiceA')).toBe(true); + + container.unregister('MockServiceA'); + + expect(container.has('MockServiceA')).toBe(false); + }); + }); + + describe('Container Management', () => { + it('should clear all services', () => { + container.register( + 'ServiceA', + () => new MockServiceA(), + ServiceLifetime.SINGLETON, + ); + container.register( + 'ServiceB', + () => new MockServiceB(new MockServiceA()), + ServiceLifetime.SINGLETON, + ); + + expect(container.has('ServiceA')).toBe(true); + expect(container.has('ServiceB')).toBe(true); + + container.clear(); + + expect(container.has('ServiceA')).toBe(false); + expect(container.has('ServiceB')).toBe(false); + }); + + it('should return all registered tokens', () => { + container.register( + 'ServiceA', + () => new MockServiceA(), + ServiceLifetime.SINGLETON, + ); + container.register( + 'ServiceB', + () => new MockServiceB(new MockServiceA()), + ServiceLifetime.SINGLETON, + ); + + const tokens = container.getRegisteredTokens(); + + expect(tokens).toHaveLength(2); + expect(tokens).toContain('ServiceA'); + expect(tokens).toContain('ServiceB'); + }); + }); + + describe('Service Tokens', () => { + it('should have predefined service tokens', () => { + expect(ServiceToken.LOG_SERVICE).toBeDefined(); + expect(ServiceToken.CONFIG_SERVICE).toBeDefined(); + expect(ServiceToken.STRIPE_CLIENT).toBeDefined(); + expect(ServiceToken.ROOT_CLIENT).toBeDefined(); + expect(ServiceToken.ROOT_CLIENT).toBeDefined(); + expect(ServiceToken.ROOT_SERVICE).toBeDefined(); + expect(ServiceToken.STRIPE_SERVICE).toBeDefined(); + expect(ServiceToken.RENDER_SERVICE).toBeDefined(); + }); + + it('should use symbols for type-safe tokens', () => { + expect(typeof ServiceToken.LOG_SERVICE).toBe('symbol'); + expect(typeof ServiceToken.CONFIG_SERVICE).toBe('symbol'); + }); + }); + + describe('Testing Support', () => { + it('should support mocking services for tests', () => { + // Register real service + container.register( + 'ServiceA', + () => new MockServiceA('Real'), + ServiceLifetime.SINGLETON, + ); + + // Use service + const realService = container.resolve('ServiceA'); + expect(realService.name).toBe('Real'); + + // Replace with mock for testing + container.replace( + 'ServiceA', + () => new MockServiceA('Mock'), + ServiceLifetime.SINGLETON, + ); + + const mockService = container.resolve('ServiceA'); + expect(mockService.name).toBe('Mock'); + }); + + it('should allow creating isolated test containers', () => { + const testContainer = new Container(); + + testContainer.register( + 'MockServiceA', + () => new MockServiceA('Test'), + ServiceLifetime.SINGLETON, + ); + + const service = testContainer.resolve('MockServiceA'); + expect(service.name).toBe('Test'); + + // Original container is unaffected + expect(container.has('MockServiceA')).toBe(false); + }); + }); + + describe('Complex Dependency Graph', () => { + it('should resolve complex dependency chains', () => { + class ServiceC { + constructor( + public serviceA: MockServiceA, + public serviceB: MockServiceB, + ) {} + } + + container.register( + 'ServiceA', + () => new MockServiceA(), + ServiceLifetime.SINGLETON, + ); + + container.register( + 'ServiceB', + (c) => new MockServiceB(c.resolve('ServiceA')), + ServiceLifetime.SINGLETON, + ); + + container.register( + 'ServiceC', + (c) => + new ServiceC( + c.resolve('ServiceA'), + c.resolve('ServiceB'), + ), + ServiceLifetime.SINGLETON, + ); + + const serviceC = container.resolve('ServiceC'); + + expect(serviceC.serviceA).toBeInstanceOf(MockServiceA); + expect(serviceC.serviceB).toBeInstanceOf(MockServiceB); + expect(serviceC.serviceB.serviceA).toBe(serviceC.serviceA); // Same singleton instance + }); + }); +}); diff --git a/stripe_collection_module/__tests__/helpers/factories.ts b/stripe_collection_module/__tests__/helpers/factories.ts new file mode 100644 index 0000000..5035e29 --- /dev/null +++ b/stripe_collection_module/__tests__/helpers/factories.ts @@ -0,0 +1,171 @@ +/** + * Test data factories + * Create mock objects for testing + */ + +import Stripe from 'stripe'; + +/** + * Create a mock Stripe customer + */ +export const createMockStripeCustomer = ( + overrides?: Partial +): Stripe.Customer => { + return { + id: 'cus_test_123', + object: 'customer', + created: Date.now() / 1000, + email: 'test@example.com', + name: 'Test Customer', + description: 'Test customer for unit tests', + livemode: false, + metadata: {}, + ...overrides, + } as Stripe.Customer; +}; + +/** + * Create a mock Stripe payment method + */ +export const createMockStripePaymentMethod = ( + overrides?: Partial +): Stripe.PaymentMethod => { + return { + id: 'pm_test_123', + object: 'payment_method', + created: Date.now() / 1000, + type: 'card', + livemode: false, + card: { + brand: 'visa', + last4: '4242', + exp_month: 12, + exp_year: 2025, + funding: 'credit', + checks: null, + country: 'US', + fingerprint: 'test_fingerprint', + generated_from: null, + networks: null, + three_d_secure_usage: null, + wallet: null, + }, + billing_details: { + address: null, + email: null, + name: null, + phone: null, + }, + metadata: {}, + ...overrides, + } as Stripe.PaymentMethod; +}; + +/** + * Create a mock Stripe subscription + */ +export const createMockStripeSubscription = ( + overrides?: Partial +): Stripe.Subscription => { + return { + id: 'sub_test_123', + object: 'subscription', + created: Date.now() / 1000, + current_period_start: Date.now() / 1000, + current_period_end: Date.now() / 1000 + 30 * 24 * 60 * 60, + customer: 'cus_test_123', + status: 'active', + items: { + object: 'list', + data: [], + has_more: false, + url: '/v1/subscription_items', + }, + metadata: {}, + ...overrides, + } as Stripe.Subscription; +}; + +/** + * Create a mock Stripe invoice + */ +export const createMockStripeInvoice = ( + overrides?: Partial +): Stripe.Invoice => { + return { + id: 'in_test_123', + object: 'invoice', + created: Date.now() / 1000, + customer: 'cus_test_123', + status: 'paid', + amount_due: 10000, + amount_paid: 10000, + amount_remaining: 0, + currency: 'zar', + lines: { + object: 'list', + data: [], + has_more: false, + url: '/v1/invoices/in_test_123/lines', + }, + metadata: {}, + ...overrides, + } as Stripe.Invoice; +}; + +/** + * Create a mock Root policy + */ +export const createMockRootPolicy = (overrides?: any): any => { + return { + policy_id: 'policy_test_123', + policy_number: 'TEST-12345', + policyholder_id: 'policyholder_test_123', + start_date: '2024-01-01T00:00:00Z', + end_date: '2025-01-01T00:00:00Z', + monthly_premium: 50000, // in cents + currency: 'ZAR', + billing_frequency: 'monthly', + billing_day: 1, + app_data: {}, + ...overrides, + }; +}; + +/** + * Create a mock Root payment method + */ +export const createMockRootPaymentMethod = (overrides?: any): any => { + return { + payment_method_id: 'pm_root_test_123', + collection_module_key: 'test_collection_module', + collection_module_definition_id: 'cmd_test_123', + module: { + id: 'si_test_123', + usage: 'off_session', + object: 'setup_intent', + status: 'succeeded', + livemode: false, + payment_method: 'pm_test_123', + }, + ...overrides, + }; +}; + +/** + * Create a mock Root payment + */ +export const createMockRootPayment = (overrides?: any): any => { + return { + payment_id: 'payment_test_123', + policy_id: 'policy_test_123', + amount: 50000, // in cents + currency: 'ZAR', + status: 'pending', + payment_type: 'premium', + payment_date: '2024-01-01T00:00:00Z', + description: 'Test payment', + external_reference: 'ext_ref_123', + ...overrides, + }; +}; diff --git a/stripe_collection_module/__tests__/helpers/test-utils.ts b/stripe_collection_module/__tests__/helpers/test-utils.ts new file mode 100644 index 0000000..1f2c42f --- /dev/null +++ b/stripe_collection_module/__tests__/helpers/test-utils.ts @@ -0,0 +1,63 @@ +/** + * Test utilities and helper functions + */ + +/** + * Wait for a specified amount of time + */ +export const wait = (ms: number): Promise => { + return new Promise((resolve) => setTimeout(resolve, ms)); +}; + +/** + * Mock timer utilities + */ +export const mockTimers = { + enable: () => { + jest.useFakeTimers(); + }, + disable: () => { + jest.useRealTimers(); + }, + advance: (ms: number) => { + jest.advanceTimersByTime(ms); + }, + runAll: () => { + jest.runAllTimers(); + }, +}; + +/** + * Create a mock function with type safety + */ +export const createMockFn = < + T extends (...args: any[]) => any, +>(): jest.MockedFunction => { + return jest.fn() as unknown as jest.MockedFunction; +}; + +/** + * Assert that a function throws a specific error + */ +export const expectToThrow = async ( + fn: () => any, + errorMessage?: string | RegExp +): Promise => { + await expect(fn()).rejects.toThrow(errorMessage); +}; + +/** + * Create a spy on console methods + */ +export const spyOnConsole = ( + method: 'log' | 'info' | 'warn' | 'error' | 'debug' +) => { + return jest.spyOn(console, method).mockImplementation(() => {}); +}; + +/** + * Restore all console spies + */ +export const restoreConsole = () => { + jest.restoreAllMocks(); +}; diff --git a/stripe_collection_module/__tests__/lifecycle-hooks/payment-method.hooks.test.ts b/stripe_collection_module/__tests__/lifecycle-hooks/payment-method.hooks.test.ts new file mode 100644 index 0000000..096d07a --- /dev/null +++ b/stripe_collection_module/__tests__/lifecycle-hooks/payment-method.hooks.test.ts @@ -0,0 +1,254 @@ +/** + * Payment Method Hooks Tests + */ + +import * as paymentMethodHooks from '../../code/lifecycle-hooks/payment-method.hooks'; +import { getContainer } from '../../code/core/container.setup'; +import { ServiceToken } from '../../code/core/container'; +import { + createMockLogService, + createMockStripeClient, + createMockConfigService, +} from '../test-helpers'; + +jest.mock('../../code/core/container.setup'); + +describe('Payment Method Hooks', () => { + let mockContainer: any; + let mockLogService: ReturnType; + let mockStripeClient: ReturnType; + let mockConfigService: ReturnType; + let mockRenderService: any; + + beforeEach(() => { + jest.clearAllMocks(); + + mockLogService = createMockLogService(); + mockStripeClient = createMockStripeClient(); + mockConfigService = createMockConfigService(); + + mockRenderService = { + renderCreatePaymentMethod: jest.fn(), + renderViewPaymentMethodSummary: jest.fn(), + }; + + mockContainer = { + resolve: jest.fn((token: symbol) => { + if (token === ServiceToken.LOG_SERVICE) return mockLogService; + if (token === ServiceToken.STRIPE_CLIENT) return mockStripeClient; + if (token === ServiceToken.CONFIG_SERVICE) return mockConfigService; + if (token === ServiceToken.RENDER_SERVICE) return mockRenderService; + return null; + }), + }; + + (getContainer as jest.Mock).mockReturnValue(mockContainer); + }); + + describe('createPaymentMethod', () => { + it('should return module data with setup intent', () => { + const setupIntent = { + id: 'seti_123', + usage: 'off_session', + object: 'setup_intent', + status: 'succeeded', + livemode: false, + payment_method: 'pm_123', + }; + + const result = paymentMethodHooks.createPaymentMethod({ + data: { setupIntent }, + }); + + expect(result).toEqual({ + module: { + id: 'seti_123', + usage: 'off_session', + object: 'setup_intent', + status: 'succeeded', + livemode: false, + payment_method: 'pm_123', + }, + }); + expect(mockLogService.info).toHaveBeenCalledWith( + 'Creating payment method', + 'createPaymentMethod', + { setupIntent } + ); + }); + + it('should return module data without setup intent', () => { + const data = { customField: 'value' }; + + const result = paymentMethodHooks.createPaymentMethod({ + data: data as any, + }); + + expect(result).toEqual({ + module: data, + }); + }); + + it('should handle empty data', () => { + const result = paymentMethodHooks.createPaymentMethod({}); + + expect(result).toEqual({ + module: undefined, + }); + }); + }); + + describe('renderCreatePaymentMethod', () => { + it('should render payment method creation form', async () => { + ( + mockStripeClient.stripeSDK.setupIntents.create as jest.Mock + ).mockResolvedValue({ + client_secret: 'seti_123_secret_456', + }); + mockRenderService.renderCreatePaymentMethod.mockReturnValue( + '
Payment Form
' + ); + + const result = await paymentMethodHooks.renderCreatePaymentMethod(); + + expect(mockStripeClient.stripeSDK.setupIntents.create).toHaveBeenCalledWith( + {} + ); + expect(mockRenderService.renderCreatePaymentMethod).toHaveBeenCalledWith({ + stripePublishableKey: 'pk_test_123', + setupIntentClientSecret: 'seti_123_secret_456', + }); + expect(result).toBe('
Payment Form
'); + }); + + it('should throw error if client secret is missing', async () => { + ( + mockStripeClient.stripeSDK.setupIntents.create as jest.Mock + ).mockResolvedValue({ + client_secret: null, + }); + + await expect( + paymentMethodHooks.renderCreatePaymentMethod() + ).rejects.toThrow('Setup intent client secret is missing'); + + expect(mockLogService.error).toHaveBeenCalled(); + }); + + it('should handle setup intent creation error', async () => { + ( + mockStripeClient.stripeSDK.setupIntents.create as jest.Mock + ).mockRejectedValue(new Error('Stripe API error')); + + await expect( + paymentMethodHooks.renderCreatePaymentMethod() + ).rejects.toThrow('Error creating setup intent: Stripe API error'); + + expect(mockLogService.error).toHaveBeenCalledWith( + 'Error rendering payment method form', + 'renderCreatePaymentMethod', + {}, + expect.any(Error) + ); + }); + }); + + describe('renderViewPaymentMethodSummary', () => { + it('should render payment method summary', async () => { + const mockPaymentMethod = { + id: 'pm_123', + type: 'card', + card: { + brand: 'visa', + last4: '4242', + }, + }; + + ( + mockStripeClient.stripeSDK.paymentMethods.retrieve as jest.Mock + ).mockResolvedValue(mockPaymentMethod); + mockRenderService.renderViewPaymentMethodSummary.mockReturnValue( + 'Visa ****4242' + ); + + const result = await paymentMethodHooks.renderViewPaymentMethodSummary({ + payment_method: 'pm_123', + }); + + expect( + mockStripeClient.stripeSDK.paymentMethods.retrieve + ).toHaveBeenCalledWith('pm_123'); + expect( + mockRenderService.renderViewPaymentMethodSummary + ).toHaveBeenCalledWith({ + payment_method: { + module: { + payment_method: 'pm_123', + }, + }, + }); + expect(result).toBe('Visa ****4242'); + }); + + it('should return error message if payment method ID is missing', async () => { + const result = await paymentMethodHooks.renderViewPaymentMethodSummary({ + payment_method: null, + }); + + expect(result).toContain('No payment method found'); + expect( + mockStripeClient.stripeSDK.paymentMethods.retrieve + ).not.toHaveBeenCalled(); + }); + + it('should handle payment method retrieval error', async () => { + ( + mockStripeClient.stripeSDK.paymentMethods.retrieve as jest.Mock + ).mockRejectedValue(new Error('Not found')); + + await expect( + paymentMethodHooks.renderViewPaymentMethodSummary({ + payment_method: 'pm_invalid', + }) + ).rejects.toThrow('Error retrieving payment method: Not found'); + + expect(mockLogService.error).toHaveBeenCalled(); + }); + }); + + describe('renderViewPaymentMethod', () => { + it('should return empty string (stub implementation)', () => { + const result = paymentMethodHooks.renderViewPaymentMethod(); + + expect(result).toBe(''); + }); + }); + + describe('afterPolicyPaymentMethodAssigned', () => { + it('should log payment method assignment', () => { + const policy = { policy_id: 'policy_123' }; + + paymentMethodHooks.afterPolicyPaymentMethodAssigned({ policy }); + + expect(mockLogService.info).toHaveBeenCalledWith( + 'Payment method assigned to policy', + 'afterPolicyPaymentMethodAssigned', + { policyId: 'policy_123' } + ); + }); + }); + + describe('afterPaymentMethodRemoved', () => { + it('should log payment method removal', () => { + const policy = { policy_id: 'policy_456' }; + + paymentMethodHooks.afterPaymentMethodRemoved({ policy }); + + expect(mockLogService.info).toHaveBeenCalledWith( + 'Payment method removed from policy', + 'afterPaymentMethodRemoved', + { policyId: 'policy_456' } + ); + }); + }); +}); diff --git a/stripe_collection_module/__tests__/lifecycle-hooks/payment.hooks.test.ts b/stripe_collection_module/__tests__/lifecycle-hooks/payment.hooks.test.ts new file mode 100644 index 0000000..0662455 --- /dev/null +++ b/stripe_collection_module/__tests__/lifecycle-hooks/payment.hooks.test.ts @@ -0,0 +1,93 @@ +/** + * Payment Hooks Tests + */ + +import * as paymentHooks from '../../code/lifecycle-hooks/payment.hooks'; +import { getContainer } from '../../code/core/container.setup'; +import { ServiceToken } from '../../code/core/container'; +import { createMockLogService } from '../test-helpers'; + +jest.mock('../../code/core/container.setup'); + +describe('Payment Hooks', () => { + let mockContainer: any; + let mockLogService: ReturnType; + + beforeEach(() => { + jest.clearAllMocks(); + + mockLogService = createMockLogService(); + + mockContainer = { + resolve: jest.fn((token: symbol) => { + if (token === ServiceToken.LOG_SERVICE) return mockLogService; + return null; + }), + }; + + (getContainer as jest.Mock).mockReturnValue(mockContainer); + }); + + describe('afterPaymentCreated', () => { + it('should log payment creation with payment and policy IDs', () => { + const policy = { policy_id: 'pol_123' }; + const payment = { + payment_id: 'pay_123', + amount: 10000, + currency: 'USD', + }; + + paymentHooks.afterPaymentCreated({ policy, payment }); + + expect(mockLogService.info).toHaveBeenCalledWith( + 'Payment created', + 'afterPaymentCreated', + { policyId: 'pol_123', paymentId: 'pay_123' } + ); + }); + + it('should log payment creation without IDs', () => { + const policy = {}; + const payment = {}; + + paymentHooks.afterPaymentCreated({ policy, payment }); + + expect(mockLogService.info).toHaveBeenCalledWith( + 'Payment created', + 'afterPaymentCreated', + { policyId: undefined, paymentId: undefined } + ); + }); + }); + + describe('afterPaymentUpdated', () => { + it('should log payment update with payment and policy IDs', () => { + const policy = { policy_id: 'pol_456' }; + const payment = { + payment_id: 'pay_456', + status: 'successful', + }; + + paymentHooks.afterPaymentUpdated({ policy, payment }); + + expect(mockLogService.info).toHaveBeenCalledWith( + 'Payment updated', + 'afterPaymentUpdated', + { policyId: 'pol_456', paymentId: 'pay_456' } + ); + }); + + it('should log payment update without IDs', () => { + const policy = {}; + const payment = {}; + + paymentHooks.afterPaymentUpdated({ policy, payment }); + + expect(mockLogService.info).toHaveBeenCalledWith( + 'Payment updated', + 'afterPaymentUpdated', + { policyId: undefined, paymentId: undefined } + ); + }); + }); +}); diff --git a/stripe_collection_module/__tests__/lifecycle-hooks/policy.hooks.test.ts b/stripe_collection_module/__tests__/lifecycle-hooks/policy.hooks.test.ts new file mode 100644 index 0000000..fe1c2d9 --- /dev/null +++ b/stripe_collection_module/__tests__/lifecycle-hooks/policy.hooks.test.ts @@ -0,0 +1,216 @@ +/** + * Policy Hooks Tests + */ + +import * as policyHooks from '../../code/lifecycle-hooks/policy.hooks'; +import { getContainer } from '../../code/core/container.setup'; +import { ServiceToken } from '../../code/core/container'; +import { createMockLogService } from '../test-helpers'; + +jest.mock('../../code/core/container.setup'); + +describe('Policy Hooks', () => { + let mockContainer: any; + let mockLogService: ReturnType; + + beforeEach(() => { + jest.clearAllMocks(); + + mockLogService = createMockLogService(); + + mockContainer = { + resolve: jest.fn((token: symbol) => { + if (token === ServiceToken.LOG_SERVICE) return mockLogService; + return null; + }), + }; + + (getContainer as jest.Mock).mockReturnValue(mockContainer); + }); + + describe('afterPolicyIssued', () => { + it('should log policy issuance', () => { + policyHooks.afterPolicyIssued(); + + expect(mockLogService.info).toHaveBeenCalledWith( + 'Policy issued', + 'afterPolicyIssued' + ); + }); + }); + + describe('afterPolicyUpdated', () => { + it('should log policy update with policy ID and updates', () => { + const policy = { + policy_id: 'policy_456', + }; + const updates = { + status: 'active', + sum_assured: 100000, + }; + + policyHooks.afterPolicyUpdated({ policy, updates }); + + expect(mockLogService.info).toHaveBeenCalledWith( + 'Policy updated', + 'afterPolicyUpdated', + { + policyId: 'policy_456', + updates: { status: 'active', sum_assured: 100000 }, + } + ); + }); + + it('should log policy update without IDs', () => { + const policy = {}; + const updates = {}; + + policyHooks.afterPolicyUpdated({ policy, updates }); + + expect(mockLogService.info).toHaveBeenCalledWith( + 'Policy updated', + 'afterPolicyUpdated', + { + policyId: undefined, + updates: {}, + } + ); + }); + }); + + describe('afterPolicyCancelled', () => { + it('should log policy cancellation with policy ID', () => { + const policy = { + policy_id: 'policy_111', + status: 'cancelled', + }; + + policyHooks.afterPolicyCancelled({ policy }); + + expect(mockLogService.info).toHaveBeenCalledWith( + 'Policy cancelled', + 'afterPolicyCancelled', + { policyId: 'policy_111' } + ); + }); + + it('should log policy cancellation without policy ID', () => { + const policy = {}; + + policyHooks.afterPolicyCancelled({ policy }); + + expect(mockLogService.info).toHaveBeenCalledWith( + 'Policy cancelled', + 'afterPolicyCancelled', + { policyId: undefined } + ); + }); + }); + + describe('afterPolicyExpired', () => { + it('should log policy expiration with policy ID', () => { + const policy = { + policy_id: 'policy_222', + status: 'expired', + }; + + policyHooks.afterPolicyExpired({ policy }); + + expect(mockLogService.info).toHaveBeenCalledWith( + 'Policy expired', + 'afterPolicyExpired', + { policyId: 'policy_222' } + ); + }); + + it('should log policy expiration without policy ID', () => { + const policy = {}; + + policyHooks.afterPolicyExpired({ policy }); + + expect(mockLogService.info).toHaveBeenCalledWith( + 'Policy expired', + 'afterPolicyExpired', + { policyId: undefined } + ); + }); + }); + + describe('afterPolicyLapsed', () => { + it('should log policy lapse with policy ID', () => { + const policy = { + policy_id: 'policy_333', + status: 'lapsed', + }; + + policyHooks.afterPolicyLapsed({ policy }); + + expect(mockLogService.info).toHaveBeenCalledWith( + 'Policy lapsed', + 'afterPolicyLapsed', + { policyId: 'policy_333' } + ); + }); + + it('should log policy lapse without policy ID', () => { + const policy = {}; + + policyHooks.afterPolicyLapsed({ policy }); + + expect(mockLogService.info).toHaveBeenCalledWith( + 'Policy lapsed', + 'afterPolicyLapsed', + { policyId: undefined } + ); + }); + }); + + describe('afterAlterationPackageApplied', () => { + it('should log alteration package application with policy ID and hook key', () => { + const policy = { + policy_id: 'policy_444', + }; + const alteration_package = { + package_id: 'pkg_123', + type: 'sum_assured_increase', + }; + const alteration_hook_key = 'increase_sum_assured'; + + policyHooks.afterAlterationPackageApplied({ + policy, + alteration_package, + alteration_hook_key, + }); + + expect(mockLogService.info).toHaveBeenCalledWith( + 'Alteration package applied', + 'afterAlterationPackageApplied', + { + policyId: 'policy_444', + alterationHookKey: 'increase_sum_assured', + } + ); + }); + + it('should log without policy ID', () => { + const policy = {}; + const alteration_package = {}; + const alteration_hook_key = 'test_key'; + + policyHooks.afterAlterationPackageApplied({ + policy, + alteration_package, + alteration_hook_key, + }); + + expect(mockLogService.info).toHaveBeenCalledWith( + 'Alteration package applied', + 'afterAlterationPackageApplied', + { + policyId: undefined, + alterationHookKey: 'test_key', + } + ); + }); + }); +}); diff --git a/stripe_collection_module/__tests__/services/config-instance.test.ts b/stripe_collection_module/__tests__/services/config-instance.test.ts new file mode 100644 index 0000000..bc4afba --- /dev/null +++ b/stripe_collection_module/__tests__/services/config-instance.test.ts @@ -0,0 +1,35 @@ +/** + * Config Instance Tests + */ + +import { + getConfigService, + isConfigServiceInitialized, +} from '../../code/services/config-instance'; + +describe('Config Instance', () => { + describe('getConfigService', () => { + it('should return a ConfigurationService instance', () => { + const service = getConfigService(); + + expect(service).toBeDefined(); + expect(service.get).toBeDefined(); + expect(typeof service.get).toBe('function'); + }); + + it('should return the same instance on multiple calls', () => { + const service1 = getConfigService(); + const service2 = getConfigService(); + + expect(service1).toBe(service2); + }); + }); + + describe('isConfigServiceInitialized', () => { + it('should return true after getConfigService is called', () => { + getConfigService(); + + expect(isConfigServiceInitialized()).toBe(true); + }); + }); +}); diff --git a/stripe_collection_module/__tests__/services/config.service.test.ts b/stripe_collection_module/__tests__/services/config.service.test.ts new file mode 100644 index 0000000..2a5cf10 --- /dev/null +++ b/stripe_collection_module/__tests__/services/config.service.test.ts @@ -0,0 +1,294 @@ +/** + * ConfigurationService Tests + */ + +import { + ConfigurationService, + EnvironmentConfig, +} from '../../code/services/config.service'; + +describe('ConfigurationService', () => { + let originalEnv: string | undefined; + + beforeEach(() => { + // Save original environment + originalEnv = process.env.ENVIRONMENT; + }); + + afterEach(() => { + // Restore original environment + if (originalEnv !== undefined) { + process.env.ENVIRONMENT = originalEnv; + } else { + delete process.env.ENVIRONMENT; + } + }); + + describe('Initialization', () => { + it('should initialize with default environment (sandbox)', () => { + delete process.env.ENVIRONMENT; + + const config = new ConfigurationService({ skipValidation: true }); + + expect(config.getEnvironment()).toBe('sandbox'); + expect(config.isSandbox()).toBe(true); + expect(config.isProduction()).toBe(false); + }); + + it('should initialize with production environment', () => { + process.env.ENVIRONMENT = 'production'; + + const config = new ConfigurationService({ skipValidation: true }); + + expect(config.getEnvironment()).toBe('production'); + expect(config.isProduction()).toBe(true); + expect(config.isSandbox()).toBe(false); + }); + + it('should initialize with environment from options', () => { + const config = new ConfigurationService({ + environment: 'production', + skipValidation: true, + }); + + expect(config.getEnvironment()).toBe('production'); + expect(config.isProduction()).toBe(true); + }); + + it('should prioritize options.environment over process.env.ENVIRONMENT', () => { + process.env.ENVIRONMENT = 'sandbox'; + + const config = new ConfigurationService({ + environment: 'production', + skipValidation: true, + }); + + expect(config.getEnvironment()).toBe('production'); + }); + }); + + describe('Validation', () => { + it('should throw error for invalid environment', () => { + expect(() => { + new ConfigurationService({ environment: 'invalid' }); + }).toThrow('Invalid ENVIRONMENT: invalid'); + }); + + it('should validate environment on initialization by default', () => { + process.env.ENVIRONMENT = 'production'; + + // Should not throw with valid config + expect(() => { + new ConfigurationService(); + }).not.toThrow(); + }); + + it('should skip validation when skipValidation is true', () => { + // This would normally fail validation due to missing env vars + // but with skipValidation it should work + expect(() => { + new ConfigurationService({ + environment: 'production', + skipValidation: true, + }); + }).not.toThrow(); + }); + }); + + describe('Configuration Access', () => { + let config: ConfigurationService; + + beforeEach(() => { + config = new ConfigurationService({ skipValidation: true }); + }); + + it('should get configuration values by key', () => { + const environment = config.get('environment'); + expect(typeof environment).toBe('string'); + expect(environment).toBeDefined(); + }); + + it('should get stripe secret key', () => { + const secretKey = config.get('stripeSecretKey'); + expect(secretKey).toBeDefined(); + }); + + it('should get root API key', () => { + const apiKey = config.get('rootApiKey'); + expect(apiKey).toBeDefined(); + }); + + it('should get all configuration', () => { + const allConfig = config.getAll(); + + expect(allConfig).toHaveProperty('environment'); + expect(allConfig).toHaveProperty('stripeSecretKey'); + expect(allConfig).toHaveProperty('rootApiKey'); + }); + + it('should return a copy when getting all configuration', () => { + const config1 = config.getAll(); + const config2 = config.getAll(); + + expect(config1).not.toBe(config2); // Different objects + expect(config1).toEqual(config2); // Same values + }); + }); + + describe('Environment-Specific Configuration', () => { + it('should load production configuration', () => { + const config = new ConfigurationService({ + environment: 'production', + skipValidation: true, + }); + + expect(config.get('environment')).toBe('production'); + // Production should use LIVE keys + expect(config.get('stripePublishableKey')).toContain('pk_live'); + expect(config.get('stripeSecretKey')).toContain('sk_live'); + }); + + it('should load sandbox configuration', () => { + const config = new ConfigurationService({ + environment: 'sandbox', + skipValidation: true, + }); + + expect(config.get('environment')).toBe('sandbox'); + // Sandbox should use TEST keys + expect(config.get('stripePublishableKey')).toContain('pk_test'); + expect(config.get('stripeSecretKey')).toContain('sk_test'); + }); + + it('should have different API keys for production vs sandbox', () => { + const prodConfig = new ConfigurationService({ + environment: 'production', + skipValidation: true, + }); + const sandboxConfig = new ConfigurationService({ + environment: 'sandbox', + skipValidation: true, + }); + + expect(prodConfig.get('stripeSecretKey')).not.toBe( + sandboxConfig.get('stripeSecretKey') + ); + expect(prodConfig.get('rootApiKey')).not.toBe( + sandboxConfig.get('rootApiKey') + ); + }); + }); + + describe('Helper Methods', () => { + let config: ConfigurationService; + + beforeEach(() => { + config = new ConfigurationService({ skipValidation: true }); + }); + + it('should parse time delay as number', () => { + const delayMs = config.getTimeDelayMs(); + + expect(typeof delayMs).toBe('number'); + expect(delayMs).toBeGreaterThanOrEqual(0); + }); + + it('should return correct environment name', () => { + const sandboxConfig = new ConfigurationService({ + environment: 'sandbox', + skipValidation: true, + }); + const prodConfig = new ConfigurationService({ + environment: 'production', + skipValidation: true, + }); + + expect(sandboxConfig.getEnvironment()).toBe('sandbox'); + expect(prodConfig.getEnvironment()).toBe('production'); + }); + + it('should correctly identify production environment', () => { + const prodConfig = new ConfigurationService({ + environment: 'production', + skipValidation: true, + }); + + expect(prodConfig.isProduction()).toBe(true); + expect(prodConfig.isSandbox()).toBe(false); + }); + + it('should correctly identify sandbox environment', () => { + const sandboxConfig = new ConfigurationService({ + environment: 'sandbox', + skipValidation: true, + }); + + expect(sandboxConfig.isSandbox()).toBe(true); + expect(sandboxConfig.isProduction()).toBe(false); + }); + }); + + describe('Type Safety', () => { + it('should provide type-safe configuration access', () => { + const config = new ConfigurationService({ skipValidation: true }); + + // TypeScript should ensure these are the correct types + const environment: string = config.get('environment'); + const stripeKey: string = config.get('stripeSecretKey'); + + expect(typeof environment).toBe('string'); + expect(typeof stripeKey).toBe('string'); + }); + + it('should return complete EnvironmentConfig object', () => { + const config = new ConfigurationService({ skipValidation: true }); + const allConfig: EnvironmentConfig = config.getAll(); + + // Should have all required properties + expect(allConfig).toHaveProperty('environment'); + expect(allConfig).toHaveProperty('stripeSecretKey'); + expect(allConfig).toHaveProperty('stripePublishableKey'); + expect(allConfig).toHaveProperty('stripeProductId'); + expect(allConfig).toHaveProperty('stripeWebhookSigningSecret'); + expect(allConfig).toHaveProperty('rootApiKey'); + expect(allConfig).toHaveProperty('rootBaseUrl'); + expect(allConfig).toHaveProperty('rootCollectionModuleKey'); + expect(allConfig).toHaveProperty('timeDelayInMilliseconds'); + }); + }); + + describe('Integration with Container', () => { + it('should be instantiable without dependencies', () => { + // ConfigurationService should not depend on other services + expect(() => { + new ConfigurationService({ skipValidation: true }); + }).not.toThrow(); + }); + + it('should create new instance each time (not singleton by itself)', () => { + const config1 = new ConfigurationService({ skipValidation: true }); + const config2 = new ConfigurationService({ skipValidation: true }); + + expect(config1).not.toBe(config2); + expect(config1.getAll()).toEqual(config2.getAll()); + }); + }); + + describe('Error Handling', () => { + it('should provide clear error for invalid environment', () => { + expect(() => { + new ConfigurationService({ environment: 'staging' }); + }).toThrow(/Invalid ENVIRONMENT: staging/); + }); + + it('should list valid environments in error message', () => { + try { + new ConfigurationService({ environment: 'invalid' }); + fail('Should have thrown an error'); + } catch (error: any) { + expect(error.message).toContain('production'); + expect(error.message).toContain('sandbox'); + } + }); + }); +}); diff --git a/stripe_collection_module/__tests__/services/log-instance.test.ts b/stripe_collection_module/__tests__/services/log-instance.test.ts new file mode 100644 index 0000000..5a3ad5a --- /dev/null +++ b/stripe_collection_module/__tests__/services/log-instance.test.ts @@ -0,0 +1,37 @@ +/** + * Log Instance Tests + */ + +import { + getLogService, + isLogServiceInitialized, +} from '../../code/services/log-instance'; + +describe('Log Instance', () => { + describe('getLogService', () => { + it('should return a LogService instance', () => { + const service = getLogService(); + + expect(service).toBeDefined(); + expect(service.info).toBeDefined(); + expect(service.debug).toBeDefined(); + expect(service.warn).toBeDefined(); + expect(service.error).toBeDefined(); + }); + + it('should return the same instance on multiple calls', () => { + const service1 = getLogService(); + const service2 = getLogService(); + + expect(service1).toBe(service2); + }); + }); + + describe('isLogServiceInitialized', () => { + it('should return true after getLogService is called', () => { + getLogService(); + + expect(isLogServiceInitialized()).toBe(true); + }); + }); +}); diff --git a/stripe_collection_module/__tests__/services/log.service.test.ts b/stripe_collection_module/__tests__/services/log.service.test.ts new file mode 100644 index 0000000..64c8d67 --- /dev/null +++ b/stripe_collection_module/__tests__/services/log.service.test.ts @@ -0,0 +1,169 @@ +/** + * LogService Tests + */ + +import { LogService, LogLevel } from '../../code/services/log.service'; + +describe('LogService', () => { + let logService: LogService; + let consoleDebugSpy: jest.SpyInstance; + let consoleInfoSpy: jest.SpyInstance; + let consoleWarnSpy: jest.SpyInstance; + let consoleErrorSpy: jest.SpyInstance; + + beforeEach(() => { + logService = new LogService({ + environment: 'test', + }); + + // Spy on console methods + consoleDebugSpy = jest.spyOn(console, 'debug').mockImplementation(); + consoleInfoSpy = jest.spyOn(console, 'info').mockImplementation(); + consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(); + consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + describe('Basic Logging', () => { + it('should log debug messages', () => { + logService.debug('Test debug message', 'TestContext'); + + expect(consoleDebugSpy).toHaveBeenCalledTimes(1); + const logOutput = JSON.parse(consoleDebugSpy.mock.calls[0][0]); + expect(logOutput.level).toBe('DEBUG'); + expect(logOutput.message).toBe('Test debug message'); + expect(logOutput.context).toBe('TestContext'); + }); + + it('should log info messages', () => { + logService.info('Test info message'); + + expect(consoleInfoSpy).toHaveBeenCalledTimes(1); + const logOutput = JSON.parse(consoleInfoSpy.mock.calls[0][0]); + expect(logOutput.level).toBe('INFO'); + expect(logOutput.message).toBe('Test info message'); + }); + + it('should log warning messages', () => { + logService.warn('Test warning message'); + + expect(consoleWarnSpy).toHaveBeenCalledTimes(1); + const logOutput = JSON.parse(consoleWarnSpy.mock.calls[0][0]); + expect(logOutput.level).toBe('WARN'); + expect(logOutput.message).toBe('Test warning message'); + }); + + it('should log error messages', () => { + const testError = new Error('Test error'); + logService.error('Test error message', 'ErrorContext', {}, testError); + + expect(consoleErrorSpy).toHaveBeenCalledTimes(1); + const logOutput = JSON.parse(consoleErrorSpy.mock.calls[0][0]); + expect(logOutput.level).toBe('ERROR'); + expect(logOutput.message).toBe('Test error message'); + expect(logOutput.error.message).toBe('Test error'); + expect(logOutput.error.stack).toBeDefined(); + }); + + it('should include metadata in logs', () => { + const metadata = { userId: '123', action: 'test' }; + logService.info('Test with metadata', 'TestContext', metadata); + + const logOutput = JSON.parse(consoleInfoSpy.mock.calls[0][0]); + expect(logOutput.metadata).toEqual(metadata); + }); + + it('should include environment in logs', () => { + logService.info('Test environment'); + + const logOutput = JSON.parse(consoleInfoSpy.mock.calls[0][0]); + expect(logOutput.environment).toBe('test'); + }); + }); + + describe('Correlation IDs', () => { + it('should generate and set correlation ID', () => { + const correlationId = logService.generateCorrelationId(); + + expect(correlationId).toBeDefined(); + expect(typeof correlationId).toBe('string'); + expect(logService.getCorrelationId()).toBe(correlationId); + }); + + it('should include correlation ID in logs', () => { + const correlationId = logService.generateCorrelationId(); + logService.info('Test with correlation ID'); + + const logOutput = JSON.parse(consoleInfoSpy.mock.calls[0][0]); + expect(logOutput.correlationId).toBe(correlationId); + }); + + it('should allow setting custom correlation ID', () => { + logService.setCorrelationId('custom-correlation-id'); + + expect(logService.getCorrelationId()).toBe('custom-correlation-id'); + + logService.info('Test message'); + const logOutput = JSON.parse(consoleInfoSpy.mock.calls[0][0]); + expect(logOutput.correlationId).toBe('custom-correlation-id'); + }); + + it('should clear correlation ID', () => { + logService.setCorrelationId('test-id'); + logService.clearCorrelationId(); + + expect(logService.getCorrelationId()).toBeNull(); + }); + }); + + // Buffer management tests removed - LogService now only outputs to stdout + + describe('Log Level Filtering', () => { + it('should respect minimum log level', () => { + const infoOnlyService = new LogService({ + environment: 'test', + minLogLevel: LogLevel.INFO, + }); + + // Spy on console again for the new service + const debugSpy = jest.spyOn(console, 'debug').mockImplementation(); + const infoSpy = jest.spyOn(console, 'info').mockImplementation(); + + infoOnlyService.debug('Debug message'); + infoOnlyService.info('Info message'); + + expect(debugSpy).not.toHaveBeenCalled(); + expect(infoSpy).toHaveBeenCalledTimes(1); + + debugSpy.mockRestore(); + infoSpy.mockRestore(); + }); + }); + + describe('Structured Output', () => { + it('should output valid JSON to stdout', () => { + logService.info('Test message', 'TestContext', { key: 'value' }); + + const logOutput = JSON.parse(consoleInfoSpy.mock.calls[0][0]); + + expect(logOutput).toHaveProperty('timestamp'); + expect(logOutput).toHaveProperty('level'); + expect(logOutput).toHaveProperty('environment'); + expect(logOutput).toHaveProperty('message'); + expect(logOutput).toHaveProperty('context'); + expect(logOutput).toHaveProperty('metadata'); + }); + + it('should include timestamp in ISO format', () => { + logService.info('Test message'); + + const logOutput = JSON.parse(consoleInfoSpy.mock.calls[0][0]); + const timestamp = new Date(logOutput.timestamp); + + expect(timestamp.toISOString()).toBe(logOutput.timestamp); + }); + }); +}); diff --git a/stripe_collection_module/__tests__/services/render.service.test.ts b/stripe_collection_module/__tests__/services/render.service.test.ts new file mode 100644 index 0000000..975ec8d --- /dev/null +++ b/stripe_collection_module/__tests__/services/render.service.test.ts @@ -0,0 +1,255 @@ +/** + * RenderService Tests + */ + +import { RenderService } from '../../code/services/render.service'; + +describe('RenderService', () => { + let renderService: RenderService; + + beforeEach(() => { + renderService = new RenderService(); + }); + + describe('renderCreatePaymentMethod', () => { + it('should render payment method creation form', () => { + const html = renderService.renderCreatePaymentMethod({ + stripePublishableKey: 'pk_test_123', + setupIntentClientSecret: 'seti_123_secret_456', + }); + + expect(html).toContain(''); + expect(html).toContain('pk_test_123'); + expect(html).toContain('seti_123_secret_456'); + expect(html).toContain('stripe.com/v3/'); + expect(html).toContain('payment-element'); + }); + + it('should escape special characters in keys', () => { + const html = renderService.renderCreatePaymentMethod({ + stripePublishableKey: 'pk_test_', + last4: '4242', + exp_month: 12, + exp_year: 2025, + }, + }, + }); + + expect(html).toContain('<script>'); + expect(html).not.toContain('', + module: { + id: 'si_123', + payment_method: 'pm_123', + livemode: false, + status: 'succeeded', + usage: 'off_session', + }, + }, + policy: { + billing_day: 15, + }, + }); + + expect(html).toContain('<script>'); + expect(html).not.toContain(' - - - -
-
-
-
-
- - - - `; -}; - -export const renderViewPaymentMethodSummary = async (params: { - payment_method: any; -}) => { - const { payment_method } = params; - - const paymentMethodDetails = await stripeClient.stripeSDK.paymentMethods - .retrieve(payment_method?.module?.payment_method as string) - .catch((error) => { - throw new ModuleError( - `Error retrieving payment method: ${error.message}`, - ); - }); - - return ` - - - - - - - - -
-

Stripe payment method

-
- Card: ${ - paymentMethodDetails.card?.brand || 'Unknown' - } **** **** **** ${ - paymentMethodDetails.card?.last4 || 'Unknown' - }, Expires: ${paymentMethodDetails.card?.exp_month || 'Unknown'}/${ - paymentMethodDetails.card?.exp_year || 'Unknown' - } -
-
- - `; -}; - -export const renderViewPaymentMethod = async (params: any) => { - const { payment_method, policy } = params; - - return ` - - - - - Simple Table - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TypeCollection module
Key${payment_method.collection_module_key}
Id${payment_method.module.id}
Payment method${payment_method.module.payment_method}
Billing day${policy.billing_day}
Livemode${payment_method.module.livemode}
Status${payment_method.module.status}
Usage${payment_method.module.usage}
- - - `; -}; - -export const afterPolicyPaymentMethodAssigned = async ({ - policy, -}: { - policy: root.Policy; -}) => { - Logger.info(`start`, { - policy, - }); - - await new ProcessPolicyPaymentMethodAssignedEventController().process({ - policy, - }); - - Logger.info(`complete`, { - policy, - }); -}; - -export const afterPaymentCreated = async ({ - policy, - payment, -}: { - policy: root.Policy; - payment: any; -}) => { - Logger.info(`start`, { - policy, - }); - - await new ProcessCreatePaymentEventController().process({ - rootPaymentId: payment.payment_id, - rootPolicyId: policy.policy_id, - amount: payment.amount, - description: payment.description, - status: payment.status, - stripePaymentMethodId: payment.stripe_payment_method_id, - }); - - Logger.info(`complete`, { - policy, - }); -}; - -export function afterPaymentUpdated({ - policy, - payment, -}: { - policy: root.Policy; - payment: root.PaymentMethod; -}) { - Logger.info(`start`, { - policy, - payment, - }); - - // Not implemented - - Logger.info(`complete`, { - policy, - payment, - }); -} - -export const afterPaymentMethodRemoved = async ({ - policy, -}: { - policy: root.Policy; -}) => { - Logger.info(`start`, { - policy, - }); - - await new ProcessPolicyCancellationEventsController().process( - policy.policy_id, - RootSupportedEvent.PaymentMethodRemoved, - ); - - Logger.info(`complete`, { - policy, - }); -}; - -export const afterPolicyCancelled = async ({ - policy, -}: { - policy: root.Policy; -}) => { - Logger.info(`start`, { - policy, - }); - - await new ProcessPolicyCancellationEventsController().process( - policy.policy_id, - RootSupportedEvent.PolicyCancelled, - ); - - Logger.info(`complete`, { - policy, - }); -}; - -export const afterPolicyExpired = async ({ - policy, -}: { - policy: root.Policy; -}) => { - Logger.info(`start`, { - policy, - }); - - await new ProcessPolicyCancellationEventsController().process( - policy.policy_id, - RootSupportedEvent.PolicyExpired, - ); - - Logger.info(`complete`, { - policy, - }); -}; - -export const afterPolicyLapsed = async ({ - policy, -}: { - policy: root.Policy; -}) => { - Logger.info(`start`, { - policy, - }); - - await new ProcessPolicyCancellationEventsController().process( - policy.policy_id, - RootSupportedEvent.PolicyLapsed, - ); - - Logger.info(`complete`, { - policy, - }); -}; - -export const afterPolicyUpdated = async ({ - policy, - updates, -}: { - policy: root.Policy; - updates: any; -}) => { - Logger.info('start', { - policy, - updates, - }); - - await new ProcessPolicyUpdatedEventController().process({ - rootPolicyId: policy.policy_id, - updates, - }); - - Logger.info('complete', { - policy, - updates, - }); -}; - -export const afterAlterationPackageApplied = async ({ - policy, - alteration_package, - alteration_hook_key, -}: { - policy: root.Policy; - alteration_package: any; - alteration_hook_key: string; -}) => { - Logger.info(`start`, { - policy, - alteration_package, - alteration_hook_key, - }); - - await new ProcessPolicyAlterationEventsController().process({ - rootPolicyId: policy.policy_id, - updatedMonthlyPremiumAmount: policy.monthly_premium, - currency: policy.currency, - billingFrequency: policy.billing_frequency as - | 'monthly' - | 'yearly' - | 'once_off', - rootPolicyStartDate: policy.start_date, - rootPolicyEndDate: policy.end_date, - billingDay: policy.billing_day, - policyAppData: policy.app_data, - alterationHookKey: alteration_hook_key, - alterationPackage: alteration_package, - }); - - Logger.info(`complete`, { - policy, - alteration_package, - alteration_hook_key, - }); -}; diff --git a/stripe_collection_module/code/lifecycle-hooks/index.ts b/stripe_collection_module/code/lifecycle-hooks/index.ts new file mode 100644 index 0000000..37d9fc2 --- /dev/null +++ b/stripe_collection_module/code/lifecycle-hooks/index.ts @@ -0,0 +1,33 @@ +/** + * Lifecycle Hooks + * + * These are called by the Root platform at various points in the policy lifecycle. + * + * Architecture: + * - Uses dependency injection via getContainer() + * - Organized by domain (payment methods, payments, policy) + * - Each hook resolves services as needed + */ + +// Payment Method Hooks +export { + createPaymentMethod, + renderCreatePaymentMethod, + renderViewPaymentMethodSummary, + renderViewPaymentMethod, + afterPolicyPaymentMethodAssigned, + afterPaymentMethodRemoved, +} from './payment-method.hooks'; + +// Payment Hooks +export { afterPaymentCreated, afterPaymentUpdated } from './payment.hooks'; + +// Policy Hooks +export { + afterPolicyIssued, + afterPolicyUpdated, + afterPolicyCancelled, + afterPolicyExpired, + afterPolicyLapsed, + afterAlterationPackageApplied, +} from './policy.hooks'; diff --git a/stripe_collection_module/code/lifecycle-hooks/payment-method.hooks.ts b/stripe_collection_module/code/lifecycle-hooks/payment-method.hooks.ts new file mode 100644 index 0000000..03994d9 --- /dev/null +++ b/stripe_collection_module/code/lifecycle-hooks/payment-method.hooks.ts @@ -0,0 +1,199 @@ +/** + * Payment Method Lifecycle Hooks + * + * Handles payment method creation, viewing, and assignment + */ + +import { getContainer } from '../core/container.setup'; +import { ServiceToken } from '../core/container'; +import { LogService } from '../services/log.service'; +import { RenderService } from '../services/render.service'; +import { ConfigurationService } from '../services/config.service'; +import StripeClient from '../clients/stripe-client'; + +/** + * Create payment method - returns module data structure + * + * This is called when a setup intent is completed. + */ +export function createPaymentMethod({ + data, +}: { + data?: { setupIntent?: any }; +}): { module: any } { + const container = getContainer(); + const logService = container.resolve(ServiceToken.LOG_SERVICE); + + logService.info('Creating payment method', 'createPaymentMethod', data); + + if (data?.setupIntent) { + return { + module: { + id: data.setupIntent.id, + usage: data.setupIntent.usage, + object: data.setupIntent.object, + status: data.setupIntent.status, + livemode: data.setupIntent.livemode, + payment_method: data.setupIntent.payment_method, + }, + }; + } + + return { + module: data, + }; +} + +/** + * Render payment method creation form + * + * Returns HTML form with Stripe Elements for capturing payment details. + */ +export async function renderCreatePaymentMethod(): Promise { + const container = getContainer(); + const logService = container.resolve(ServiceToken.LOG_SERVICE); + const renderService = container.resolve( + ServiceToken.RENDER_SERVICE + ); + const configService = container.resolve( + ServiceToken.CONFIG_SERVICE + ); + const stripeClient = container.resolve( + ServiceToken.STRIPE_CLIENT + ); + + logService.info( + 'Rendering payment method creation form', + 'renderCreatePaymentMethod' + ); + + try { + // Create Stripe setup intent + const setupIntent = await stripeClient.stripeSDK.setupIntents.create({}); + + if (!setupIntent.client_secret) { + throw new Error('Setup intent client secret is missing'); + } + + // Render form using RenderService + return renderService.renderCreatePaymentMethod({ + stripePublishableKey: configService.get('stripePublishableKey'), + setupIntentClientSecret: setupIntent.client_secret, + }); + } catch (error) { + const errorMessage = + error instanceof Error ? error.message : 'Unknown error'; + logService.error( + 'Error rendering payment method form', + 'renderCreatePaymentMethod', + {}, + error as Error + ); + throw new Error(`Error creating setup intent: ${errorMessage}`); + } +} + +/** + * Render payment method summary view (compact card) + * + * Returns HTML showing a brief summary of the payment method. + */ +export async function renderViewPaymentMethodSummary(params: { + payment_method: any; +}): Promise { + const container = getContainer(); + const logService = container.resolve(ServiceToken.LOG_SERVICE); + const renderService = container.resolve( + ServiceToken.RENDER_SERVICE + ); + const stripeClient = container.resolve( + ServiceToken.STRIPE_CLIENT + ); + + logService.info( + 'Rendering payment method summary', + 'renderViewPaymentMethodSummary' + ); + + try { + const { payment_method: paymentMethod } = params; + + if (!paymentMethod) { + return '
No payment method found
'; + } + + // Retrieve payment method from Stripe + const stripePaymentMethod = + await stripeClient.stripeSDK.paymentMethods.retrieve( + paymentMethod as string + ); + + return renderService.renderViewPaymentMethodSummary({ + payment_method: { + module: { + payment_method: stripePaymentMethod.id, + }, + }, + }); + } catch (error) { + const errorMessage = + error instanceof Error ? error.message : 'Unknown error'; + logService.error( + 'Error rendering payment method summary', + 'renderViewPaymentMethodSummary', + {}, + error as Error + ); + throw new Error(`Error retrieving payment method: ${errorMessage}`); + } +} + +/** + * Render detailed payment method view + * + * TODO: Implement full payment method details view + */ +export function renderViewPaymentMethod(): string { + // Stub - will be implemented when needed + return ''; +} + +/** + * Called after a payment method is assigned to a policy + * + * TODO: Implement payment method assignment logic + */ +export function afterPolicyPaymentMethodAssigned({ + policy, +}: { + policy: any; +}): void { + const container = getContainer(); + const logService = container.resolve(ServiceToken.LOG_SERVICE); + + logService.info( + 'Payment method assigned to policy', + 'afterPolicyPaymentMethodAssigned', + { policyId: policy.policy_id } + ); + + // Stub - implement your logic here +} + +/** + * Called after a payment method is removed from a policy + * + * TODO: Implement payment method removal logic + */ +export function afterPaymentMethodRemoved({ policy }: { policy: any }): void { + const container = getContainer(); + const logService = container.resolve(ServiceToken.LOG_SERVICE); + + logService.info( + 'Payment method removed from policy', + 'afterPaymentMethodRemoved', + { policyId: policy.policy_id } + ); + + // Stub - implement your logic here +} diff --git a/stripe_collection_module/code/lifecycle-hooks/payment.hooks.ts b/stripe_collection_module/code/lifecycle-hooks/payment.hooks.ts new file mode 100644 index 0000000..21433eb --- /dev/null +++ b/stripe_collection_module/code/lifecycle-hooks/payment.hooks.ts @@ -0,0 +1,55 @@ +/** + * Payment Lifecycle Hooks + * + * Handles payment creation and updates + */ + +import { getContainer } from '../core/container.setup'; +import { ServiceToken } from '../core/container'; +import { LogService } from '../services/log.service'; + +/** + * Called after a payment is created + * + * TODO: Implement payment creation logic + */ +export function afterPaymentCreated({ + policy, + payment, +}: { + policy: any; + payment: any; +}): void { + const container = getContainer(); + const logService = container.resolve(ServiceToken.LOG_SERVICE); + + logService.info('Payment created', 'afterPaymentCreated', { + policyId: policy.policy_id, + paymentId: payment.payment_id, + }); + + // Stub - implement your logic here +} + +/** + * Called after a payment is updated + * + * TODO: Implement payment update logic + */ +export function afterPaymentUpdated({ + policy, + payment, +}: { + policy: any; + payment: any; +}): void { + const container = getContainer(); + const logService = container.resolve(ServiceToken.LOG_SERVICE); + + logService.info('Payment updated', 'afterPaymentUpdated', { + policyId: policy.policy_id, + paymentId: payment.payment_id, + }); + + // Stub - implement your logic here +} diff --git a/stripe_collection_module/code/lifecycle-hooks/policy.hooks.ts b/stripe_collection_module/code/lifecycle-hooks/policy.hooks.ts new file mode 100644 index 0000000..cfc8f55 --- /dev/null +++ b/stripe_collection_module/code/lifecycle-hooks/policy.hooks.ts @@ -0,0 +1,124 @@ +/** + * Policy Lifecycle Hooks + * + * Handles various policy lifecycle events + */ + +import { getContainer } from '../core/container.setup'; +import { ServiceToken } from '../core/container'; +import { LogService } from '../services/log.service'; + +/** + * Called after a policy is issued + * + * TODO: Implement policy issued logic + */ +export function afterPolicyIssued(): void { + const container = getContainer(); + const logService = container.resolve(ServiceToken.LOG_SERVICE); + + logService.info('Policy issued', 'afterPolicyIssued'); + + // Stub - implement your logic here +} + +/** + * Called after a policy is updated + * + * TODO: Implement policy update logic + */ +export function afterPolicyUpdated({ + policy, + updates, +}: { + policy: any; + updates: any; +}): void { + const container = getContainer(); + const logService = container.resolve(ServiceToken.LOG_SERVICE); + + logService.info('Policy updated', 'afterPolicyUpdated', { + policyId: policy.policy_id, + updates, + }); + + // Stub - implement your logic here +} + +/** + * Called after a policy is cancelled + * + * TODO: Implement policy cancellation logic + */ +export function afterPolicyCancelled({ policy }: { policy: any }): void { + const container = getContainer(); + const logService = container.resolve(ServiceToken.LOG_SERVICE); + + logService.info('Policy cancelled', 'afterPolicyCancelled', { + policyId: policy.policy_id, + }); + + // Stub - implement your logic here +} + +/** + * Called after a policy expires + * + * TODO: Implement policy expiration logic + */ +export function afterPolicyExpired({ policy }: { policy: any }): void { + const container = getContainer(); + const logService = container.resolve(ServiceToken.LOG_SERVICE); + + logService.info('Policy expired', 'afterPolicyExpired', { + policyId: policy.policy_id, + }); + + // Stub - implement your logic here +} + +/** + * Called after a policy lapses + * + * TODO: Implement policy lapse logic + */ +export function afterPolicyLapsed({ policy }: { policy: any }): void { + const container = getContainer(); + const logService = container.resolve(ServiceToken.LOG_SERVICE); + + logService.info('Policy lapsed', 'afterPolicyLapsed', { + policyId: policy.policy_id, + }); + + // Stub - implement your logic here +} + +/** + * Called after an alteration package is applied to a policy + * + * TODO: Implement alteration package logic + */ +export function afterAlterationPackageApplied({ + policy, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + alteration_package, + alteration_hook_key, +}: { + policy: any; + alteration_package: any; + alteration_hook_key: string; +}): void { + const container = getContainer(); + const logService = container.resolve(ServiceToken.LOG_SERVICE); + + logService.info( + 'Alteration package applied', + 'afterAlterationPackageApplied', + { + policyId: policy.policy_id, + alterationHookKey: alteration_hook_key, + } + ); + + // Stub - implement your logic here +} diff --git a/stripe_collection_module/code/main.ts b/stripe_collection_module/code/main.ts index bd323f9..0ad666e 100644 --- a/stripe_collection_module/code/main.ts +++ b/stripe_collection_module/code/main.ts @@ -1,2 +1,16 @@ -export * from './lifecycle-hooks'; +/** + * Collection Module Main Entry Point + * + * This is the main entry point for the collection module. + * It initializes the DI container and exports lifecycle hooks. + */ + +import { getContainer } from './core/container.setup'; + +// Initialize the DI container on module load +// This sets up all services including ConfigurationService and LogService +getContainer(); + +// Export lifecycle hooks and webhook handlers +export * from './lifecycle-hooks/'; export * from './webhook-hooks'; diff --git a/stripe_collection_module/code/sample.env.ts b/stripe_collection_module/code/sample.env.ts deleted file mode 100644 index 73f532e..0000000 --- a/stripe_collection_module/code/sample.env.ts +++ /dev/null @@ -1,24 +0,0 @@ -export const NODE_ENV = 'development'; - -export const STRIPE_WEBHOOK_SIGNING_SECRET_LIVE = 'whsec_.....'; -export const STRIPE_WEBHOOK_SIGNING_SECRET_TEST = 'whsec_.....'; - -export const STRIPE_PRODUCT_ID_LIVE = 'prod_.....'; -export const STRIPE_PRODUCT_ID_TEST = 'prod_.....'; - -export const TIME_DELAY_IN_MILLISECONDS = '10000'; - -export const STRIPE_PUBLISHABLE_KEY_LIVE = 'pk_live_.....'; -export const STRIPE_PUBLISHABLE_KEY_TEST = 'pk_test_.....'; - -export const STRIPE_API_KEY_LIVE = 'sk_live_.....'; -export const STRIPE_API_KEY_TEST = 'sk_test_.....'; - -export const ROOT_COLLECTION_MODULE_KEY = 'cm_stripe_admiral_business'; - -export const ROOT_API_KEY_LIVE = 'production_.....'; -export const ROOT_API_KEY_SANDBOX = 'sandbox_.....'; -export const ROOT_BASE_URL_LIVE = - 'https://api.uk.rootplatform.com/v1/insurance'; -export const ROOT_BASE_URL_SANDBOX = - 'https://sandbox.uk.rootplatform.com/v1/insurance'; diff --git a/stripe_collection_module/code/services/config-instance.ts b/stripe_collection_module/code/services/config-instance.ts new file mode 100644 index 0000000..72b30e5 --- /dev/null +++ b/stripe_collection_module/code/services/config-instance.ts @@ -0,0 +1,43 @@ +/** + * Configuration Instance Helper + * + * Provides easy access to the ConfigurationService from the DI container. + * This is a convenience wrapper similar to log-instance.ts + */ + +import { getContainer } from '../core/container.setup'; +import { ServiceToken } from '../core/container'; +import { ConfigurationService } from './config.service'; + +/** + * Get the ConfigurationService instance from the container + * + * @returns ConfigurationService instance + * @throws Error if container is not initialized + * + * @example + * ```typescript + * import { getConfigService } from './services/config-instance'; + * + * const config = getConfigService(); + * const apiKey = config.get('stripeSecretKey'); + * ``` + */ +export function getConfigService(): ConfigurationService { + const container = getContainer(); + return container.resolve(ServiceToken.CONFIG_SERVICE); +} + +/** + * Check if ConfigurationService is initialized in the container + * + * @returns true if ConfigurationService is available + */ +export function isConfigServiceInitialized(): boolean { + try { + const container = getContainer(); + return container.has(ServiceToken.CONFIG_SERVICE); + } catch { + return false; + } +} diff --git a/stripe_collection_module/code/services/config.service.ts b/stripe_collection_module/code/services/config.service.ts new file mode 100644 index 0000000..8416bb5 --- /dev/null +++ b/stripe_collection_module/code/services/config.service.ts @@ -0,0 +1,220 @@ +/** + * ConfigurationService - Injectable configuration management + * + * This service provides type-safe access to environment-specific configuration. + * It validates configuration on initialization and throws errors for missing values. + */ + +import * as env from '../env'; + +export interface EnvironmentConfig { + timeDelayInMilliseconds: string; + environment: Environment; + stripeWebhookSigningSecret: string; + stripeProductId: string; + rootCollectionModuleKey: string; + stripePublishableKey: string; + stripeSecretKey: string; + rootApiKey: string; + rootBaseUrl: string; +} + +export interface ConfigMap { + production: EnvironmentConfig; + sandbox: EnvironmentConfig; +} + +export interface ConfigurationServiceOptions { + environment?: string; + skipValidation?: boolean; // For testing +} + +export enum Environment { + PRODUCTION = 'production', + SANDBOX = 'sandbox', +} + +/** + * ConfigurationService - Manages environment-specific configuration + * + * @example + * ```typescript + * const config = new ConfigurationService(); + * const apiKey = config.get('stripeSecretKey'); + * const isProduction = config.isProduction(); + * ``` + */ +export class ConfigurationService { + private readonly configs: ConfigMap; + private readonly environment: string; + private readonly currentConfig: EnvironmentConfig; + + constructor(options: ConfigurationServiceOptions = {}) { + this.environment = + options.environment || process.env.ENVIRONMENT || Environment.SANDBOX; + this.configs = this.buildConfigMap(); + + if (!options.skipValidation) { + this.validateEnvironment(); + } + + this.currentConfig = this.configs[this.environment as keyof ConfigMap]; + } + + /** + * Build configuration map from environment variables + */ + private buildConfigMap(): ConfigMap { + const baseConfig = { + timeDelayInMilliseconds: env.TIME_DELAY_IN_MILLISECONDS, + rootCollectionModuleKey: env.ROOT_COLLECTION_MODULE_KEY, + environment: this.environment, + }; + + const production: EnvironmentConfig = { + ...baseConfig, + environment: Environment.PRODUCTION, + stripeWebhookSigningSecret: env.STRIPE_WEBHOOK_SIGNING_SECRET_LIVE, + stripePublishableKey: env.STRIPE_PUBLISHABLE_KEY_LIVE, + stripeSecretKey: env.STRIPE_SECRET_KEY_LIVE, + stripeProductId: env.STRIPE_PRODUCT_ID_LIVE, + rootApiKey: env.ROOT_API_KEY_LIVE, + rootBaseUrl: env.ROOT_BASE_URL_LIVE, + }; + + const sandbox: EnvironmentConfig = { + ...baseConfig, + environment: Environment.SANDBOX, + stripeWebhookSigningSecret: env.STRIPE_WEBHOOK_SIGNING_SECRET_TEST, + stripePublishableKey: env.STRIPE_PUBLISHABLE_KEY_TEST, + stripeSecretKey: env.STRIPE_SECRET_KEY_TEST, + stripeProductId: env.STRIPE_PRODUCT_ID_TEST, + rootApiKey: env.ROOT_API_KEY_SANDBOX, + rootBaseUrl: env.ROOT_BASE_URL_SANDBOX, + }; + + return { + production, + sandbox, + }; + } + + /** + * Validate that environment is set correctly and all config values are present + */ + private validateEnvironment(): void { + if (!this.environment) { + throw new Error( + 'ENVIRONMENT is not set. Set ENVIRONMENT=production or ENVIRONMENT=sandbox' + ); + } + + const validEnvironments = Object.keys(this.configs); + + if (!validEnvironments.includes(this.environment)) { + throw new Error( + `Invalid ENVIRONMENT: ${ + this.environment + }. Valid values: ${validEnvironments.join(', ')}` + ); + } + + const config = this.configs[this.environment as keyof ConfigMap]; + const missingKeys: string[] = []; + const invalidUrls: string[] = []; + + // Check for missing values + for (const [key, value] of Object.entries(config)) { + if (value === '' || value === null || value === undefined) { + missingKeys.push(key); + } + } + + // Validate URL formats + if (config.rootBaseUrl && !this.isValidUrl(config.rootBaseUrl)) { + invalidUrls.push(`rootBaseUrl: "${config.rootBaseUrl}"`); + } + + // Combine validation errors + const errors: string[] = []; + + if (missingKeys.length > 0) { + errors.push( + `Missing required configuration values for ${ + this.environment + }: ${missingKeys.join(', ')}`, + 'Hint: Check that code/env.ts is properly configured with all required values.', + 'For AWS Lambda: Ensure all environment variables are set in Lambda configuration.' + ); + } + + if (invalidUrls.length > 0) { + errors.push(`Invalid URL format(s): ${invalidUrls.join(', ')}`); + } + + if (errors.length > 0) { + throw new Error(`Configuration validation failed:\n${errors.join('\n')}`); + } + } + + /** + * Validate URL format + */ + private isValidUrl(url: string): boolean { + try { + const parsed = new URL(url); + return ['http:', 'https:'].includes(parsed.protocol); + } catch { + return false; + } + } + + /** + * Get a configuration value by key + */ + public get(key: K): EnvironmentConfig[K] { + return this.currentConfig[key]; + } + + /** + * Get the entire current configuration object + */ + public getAll(): EnvironmentConfig { + return { ...this.currentConfig }; + } + + /** + * Get the current environment name + */ + public getEnvironment(): string { + return this.environment; + } + + /** + * Check if running in production + */ + public isProduction(): boolean { + return this.environment === Environment.PRODUCTION.toString(); + } + + /** + * Check if running in sandbox + */ + public isSandbox(): boolean { + return this.environment === Environment.SANDBOX.toString(); + } + + /** + * @deprecated Use isSandbox() instead. This method is kept for backwards compatibility. + */ + public isDevelopment(): boolean { + return this.isSandbox(); + } + + /** + * Get time delay in milliseconds as a number + */ + public getTimeDelayMs(): number { + return parseInt(this.currentConfig.timeDelayInMilliseconds, 10); + } +} diff --git a/stripe_collection_module/code/services/log-instance.ts b/stripe_collection_module/code/services/log-instance.ts new file mode 100644 index 0000000..ca3cec4 --- /dev/null +++ b/stripe_collection_module/code/services/log-instance.ts @@ -0,0 +1,43 @@ +/** + * LogService Instance Helper + * + * Provides easy access to the LogService from the DI container. + * The LogService is automatically initialized when the container is created. + */ + +import { getContainer } from '../core/container.setup'; +import { ServiceToken } from '../core/container'; +import { LogService } from './log.service'; + +/** + * Get the LogService instance from the container + * + * @returns LogService instance + * @throws Error if container is not initialized + * + * @example + * ```typescript + * import { getLogService } from './services/log-instance'; + * + * const logService = getLogService(); + * logService.info('Hello, world!'); + * ``` + */ +export function getLogService(): LogService { + const container = getContainer(); + return container.resolve(ServiceToken.LOG_SERVICE); +} + +/** + * Check if LogService is initialized in the container + * + * @returns true if LogService is available + */ +export function isLogServiceInitialized(): boolean { + try { + const container = getContainer(); + return container.has(ServiceToken.LOG_SERVICE); + } catch { + return false; + } +} diff --git a/stripe_collection_module/code/services/log.service.ts b/stripe_collection_module/code/services/log.service.ts new file mode 100644 index 0000000..09310a8 --- /dev/null +++ b/stripe_collection_module/code/services/log.service.ts @@ -0,0 +1,212 @@ +/** + * LogService - Structured logging system + * + * Features: + * - Structured JSON output to stdout (for DataDog/CloudWatch) + * - Correlation IDs for request tracking + * - Multiple log levels + */ + +import { randomUUID } from 'crypto'; + +export enum LogLevel { + DEBUG = 'DEBUG', + INFO = 'INFO', + WARN = 'WARN', + ERROR = 'ERROR', +} + +export interface LogEntry { + timestamp: string; + level: LogLevel; + message: string; + context?: string; + correlationId?: string; + metadata?: Record; + error?: { + message: string; + stack?: string; + name?: string; + }; +} + +export interface LogServiceConfig { + environment: string; + minLogLevel?: LogLevel; // Minimum log level to output +} + +export class LogService { + private readonly minLogLevel: LogLevel; + private readonly environment: string; + private currentCorrelationId: string | null = null; + + // Map log levels to numeric priorities for comparison + private static readonly LOG_LEVEL_PRIORITY: Record = { + [LogLevel.DEBUG]: 0, + [LogLevel.INFO]: 1, + [LogLevel.WARN]: 2, + [LogLevel.ERROR]: 3, + }; + + constructor(config: LogServiceConfig) { + this.environment = config.environment; + this.minLogLevel = config.minLogLevel || LogLevel.DEBUG; + } + + /** + * Set correlation ID for the current request/operation + */ + public setCorrelationId(correlationId: string): void { + this.currentCorrelationId = correlationId; + } + + /** + * Generate and set a new correlation ID + */ + public generateCorrelationId(): string { + const correlationId = randomUUID(); + this.currentCorrelationId = correlationId; + return correlationId; + } + + /** + * Clear the current correlation ID + */ + public clearCorrelationId(): void { + this.currentCorrelationId = null; + } + + /** + * Get the current correlation ID + */ + public getCorrelationId(): string | null { + return this.currentCorrelationId; + } + + /** + * Log a debug message + */ + public debug( + message: string, + context?: string, + metadata?: Record + ): void { + this.log(LogLevel.DEBUG, message, context, metadata); + } + + /** + * Log an info message + */ + public info( + message: string, + context?: string, + metadata?: Record + ): void { + this.log(LogLevel.INFO, message, context, metadata); + } + + /** + * Log a warning message + */ + public warn( + message: string, + context?: string, + metadata?: Record + ): void { + this.log(LogLevel.WARN, message, context, metadata); + } + + /** + * Log an error message + */ + public error( + message: string, + context?: string, + metadata?: Record, + error?: Error + ): void { + const errorData = error + ? { + message: error.message, + stack: error.stack, + name: error.name, + } + : undefined; + + this.log(LogLevel.ERROR, message, context, metadata, errorData); + } + + /** + * Core logging method + */ + private log( + level: LogLevel, + message: string, + context?: string, + metadata?: Record, + error?: { message: string; stack?: string; name?: string } + ): void { + // Check if this log level should be output + if (!this.shouldLog(level)) { + return; + } + + const entry: LogEntry = { + timestamp: new Date().toISOString(), + level, + message, + context, + correlationId: this.currentCorrelationId || undefined, + metadata, + error, + }; + + // Output to stdout as JSON (for DataDog/CloudWatch) + this.outputToStdout(entry); + } + + /** + * Check if a log level should be output + */ + private shouldLog(level: LogLevel): boolean { + return ( + LogService.LOG_LEVEL_PRIORITY[level] >= + LogService.LOG_LEVEL_PRIORITY[this.minLogLevel] + ); + } + + /** + * Output log entry to stdout as structured JSON + */ + private outputToStdout(entry: LogEntry): void { + const output = { + timestamp: entry.timestamp, + level: entry.level, + environment: this.environment, + message: entry.message, + ...(entry.context && { context: entry.context }), + ...(entry.correlationId && { correlationId: entry.correlationId }), + ...(entry.metadata && { metadata: entry.metadata }), + ...(entry.error && { error: entry.error }), + }; + + // Use console methods based on log level + switch (entry.level) { + case LogLevel.DEBUG: + console.debug(JSON.stringify(output)); + break; + case LogLevel.INFO: + console.info(JSON.stringify(output)); + break; + case LogLevel.WARN: + console.warn(JSON.stringify(output)); + break; + case LogLevel.ERROR: + console.error(JSON.stringify(output)); + break; + default: + console.log(JSON.stringify(output)); + break; + } + } +} diff --git a/stripe_collection_module/code/services/render.service.ts b/stripe_collection_module/code/services/render.service.ts new file mode 100644 index 0000000..acf016e --- /dev/null +++ b/stripe_collection_module/code/services/render.service.ts @@ -0,0 +1,353 @@ +/** + * RenderService - Handles HTML rendering for dashboard views + * + * This service consolidates all HTML rendering logic used by lifecycle hooks + * to display UI elements on the Root dashboard. + */ + +export interface RenderPaymentMethodParams { + stripePublishableKey: string; + setupIntentClientSecret: string; +} + +export interface ViewPaymentMethodParams { + payment_method: { + collection_module_key: string; + module: { + id: string; + payment_method: string; + livemode: boolean; + status: string; + usage: string; + }; + }; + policy: { + billing_day: number; + }; +} + +export interface ViewPaymentMethodSummaryParams { + payment_method: { + module: { + payment_method: string; + }; + }; + paymentMethodDetails?: { + card?: { + brand: string; + last4: string; + exp_month: number; + exp_year: number; + }; + }; +} + +export class RenderService { + /** + * Common CSS styles used across all renders + */ + private readonly commonStyles = ` + .api-attributes { + font-size: 9px; + } + .api-attributes-wrapper { + display: none; + } + `; + + /** + * Render the payment method creation form with Stripe Elements + * + * This creates a form that allows users to enter their payment details. + * Uses Stripe Elements for PCI compliance. + */ + renderCreatePaymentMethod(params: RenderPaymentMethodParams): string { + const { stripePublishableKey, setupIntentClientSecret } = params; + + return ` + + + + + + + + +
+
+
+
+
+ + +`; + } + + /** + * Render payment method summary view (compact card view) + * + * Displays a brief summary of the payment method, typically used in lists. + */ + renderViewPaymentMethodSummary( + params: ViewPaymentMethodSummaryParams + ): string { + const { paymentMethodDetails } = params; + + const brand = paymentMethodDetails?.card?.brand || 'Unknown'; + const last4 = paymentMethodDetails?.card?.last4 || 'Unknown'; + const expMonth = paymentMethodDetails?.card?.exp_month || 'Unknown'; + const expYear = paymentMethodDetails?.card?.exp_year || 'Unknown'; + + return ` + + + + + + + + +
+

Stripe payment method

+
+ Card: ${this.escapeHtml(brand)} **** **** **** ${this.escapeHtml( + last4 + )}, + Expires: ${this.escapeHtml(String(expMonth))}/${this.escapeHtml( + String(expYear) + )} +
+
+ +`; + } + + /** + * Render full payment method details view + * + * Displays comprehensive information about the payment method. + */ + renderViewPaymentMethod(params: ViewPaymentMethodParams): string { + const { payment_method: paymentMethod, policy } = params; + + return ` + + + + + Payment Method Details + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeCollection module
Key${this.escapeHtml( + paymentMethod.collection_module_key + )}
Id${this.escapeHtml(paymentMethod.module.id)}
Payment method${this.escapeHtml(paymentMethod.module.payment_method)}
Billing day${this.escapeHtml(String(policy.billing_day))}
Livemode${this.escapeHtml(String(paymentMethod.module.livemode))}
Status${this.escapeHtml(paymentMethod.module.status)}
Usage${this.escapeHtml(paymentMethod.module.usage)}
+ + +`; + } + + /** + * Escape HTML to prevent XSS + */ + private escapeHtml(text: string): string { + const map: Record = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''', + }; + return text.replaceAll(/[&<>"']/g, (m) => map[m]); + } + + /** + * Escape JavaScript strings for safe embedding in HTML + */ + private escapeJs(text: string): string { + return text + .replaceAll('\\', '\\\\') + .replaceAll("'", String.raw`\'`) + .replaceAll('"', String.raw`\"`) + .replaceAll('\n', String.raw`\n`) + .replaceAll('\r', String.raw`\r`) + .replaceAll('\t', String.raw`\t`); + } +} diff --git a/stripe_collection_module/code/services/root.service.ts b/stripe_collection_module/code/services/root.service.ts new file mode 100644 index 0000000..7a9b011 --- /dev/null +++ b/stripe_collection_module/code/services/root.service.ts @@ -0,0 +1,79 @@ +/** + * RootService - Business logic for Root Platform operations + * + * This service provides a high-level interface for Root Platform operations. + * It wraps the Root client and provides domain-specific methods. + */ + +import * as root from '@rootplatform/node-sdk'; +import { LogService } from './log.service'; +import RootClient from '../clients/root-client'; + +export interface UpdatePaymentStatusParams { + paymentId: string; + status: root.PaymentStatus; + failureReason?: string; + failureAction?: root.FailureAction; +} + +export class RootService { + constructor( + private readonly logService: LogService, + private readonly rootClient: typeof RootClient + ) {} + + /** + * Get a policy by ID + */ + async getPolicy(policyId: string): Promise { + this.logService.debug(`Getting policy: ${policyId}`, 'RootService'); + + try { + const result = await this.rootClient.getPolicyById({ policyId }); + return result; + } catch (error: any) { + this.logService.error( + `Failed to get policy: ${error.message}`, + 'RootService', + { policyId, error } + ); + throw error; + } + } + + /** + * Update payment status on Root Platform + */ + async updatePaymentStatus(params: UpdatePaymentStatusParams): Promise { + this.logService.info('Updating payment status', 'RootService', params); + + try { + await this.rootClient.updatePaymentsAsync({ + paymentUpdates: [ + { + payment_id: params.paymentId, + status: params.status, + failure_reason: params.failureReason, + failure_action: params.failureAction, + }, + ], + }); + + this.logService.info( + 'Payment status updated successfully', + 'RootService', + { + paymentId: params.paymentId, + status: params.status, + } + ); + } catch (error: any) { + this.logService.error( + `Failed to update payment status: ${error.message}`, + 'RootService', + { params, error } + ); + throw error; + } + } +} diff --git a/stripe_collection_module/code/services/stripe.service.ts b/stripe_collection_module/code/services/stripe.service.ts new file mode 100644 index 0000000..335aeb8 --- /dev/null +++ b/stripe_collection_module/code/services/stripe.service.ts @@ -0,0 +1,278 @@ +/** + * StripeService - Business logic for Stripe operations + * + * This service provides a high-level interface for Stripe operations. + * It wraps the Stripe client and provides domain-specific methods. + */ + +import Stripe from 'stripe'; +import { LogService } from './log.service'; +import StripeClient from '../clients/stripe-client'; + +export interface CreateCustomerParams { + email: string; + name?: string; + metadata?: Record; +} + +export interface CreatePaymentIntentParams { + amount: number; + currency: string; + customerId: string; + description?: string; + metadata?: Record; + confirm?: boolean; + offSession?: boolean; +} + +export interface AttachPaymentMethodParams { + paymentMethodId: string; + customerId: string; +} + +export class StripeService { + constructor( + private readonly logService: LogService, + private readonly stripeClient: StripeClient + ) {} + + /** + * Create a Stripe customer + */ + async createCustomer(params: CreateCustomerParams): Promise { + this.logService.info('Creating Stripe customer', 'StripeService', { + email: params.email, + }); + + try { + const customer = await this.stripeClient.stripeSDK.customers.create({ + email: params.email, + name: params.name, + metadata: params.metadata, + }); + + this.logService.info( + 'Stripe customer created successfully', + 'StripeService', + { + customerId: customer.id, + email: customer.email, + } + ); + + return customer; + } catch (error: any) { + this.logService.error( + `Failed to create Stripe customer: ${error.message}`, + 'StripeService', + { params, error } + ); + throw error; + } + } + + /** + * Get a Stripe customer by ID + */ + async getCustomer(customerId: string): Promise { + this.logService.debug( + `Getting Stripe customer: ${customerId}`, + 'StripeService' + ); + + try { + const customer = + await this.stripeClient.stripeSDK.customers.retrieve(customerId); + + if (customer.deleted) { + throw new Error(`Customer ${customerId} has been deleted`); + } + + return customer as Stripe.Customer; + } catch (error: any) { + this.logService.error( + `Failed to get Stripe customer: ${error.message}`, + 'StripeService', + { customerId, error } + ); + throw error; + } + } + + /** + * Update a Stripe customer + */ + async updateCustomer( + customerId: string, + params: Stripe.CustomerUpdateParams + ): Promise { + this.logService.info('Updating Stripe customer', 'StripeService', { + customerId, + }); + + try { + const customer = await this.stripeClient.stripeSDK.customers.update( + customerId, + params + ); + + this.logService.info( + 'Stripe customer updated successfully', + 'StripeService', + { + customerId: customer.id, + } + ); + + return customer; + } catch (error: any) { + this.logService.error( + `Failed to update Stripe customer: ${error.message}`, + 'StripeService', + { customerId, params, error } + ); + throw error; + } + } + + /** + * Create a payment intent + */ + async createPaymentIntent( + params: CreatePaymentIntentParams + ): Promise { + this.logService.info('Creating Stripe payment intent', 'StripeService', { + amount: params.amount, + currency: params.currency, + customerId: params.customerId, + }); + + try { + const paymentIntent = + await this.stripeClient.stripeSDK.paymentIntents.create({ + amount: params.amount, + currency: params.currency, + customer: params.customerId, + description: params.description, + metadata: params.metadata, + payment_method_types: ['card'], + confirm: params.confirm, + off_session: params.offSession, + }); + + this.logService.info( + 'Payment intent created successfully', + 'StripeService', + { + paymentIntentId: paymentIntent.id, + status: paymentIntent.status, + } + ); + + return paymentIntent; + } catch (error: any) { + this.logService.error( + `Failed to create payment intent: ${error.message}`, + 'StripeService', + { params, error } + ); + throw error; + } + } + + /** + * Get a payment method by ID + */ + async getPaymentMethod( + paymentMethodId: string + ): Promise { + this.logService.debug( + `Getting payment method: ${paymentMethodId}`, + 'StripeService' + ); + + try { + return await this.stripeClient.stripeSDK.paymentMethods.retrieve( + paymentMethodId + ); + } catch (error: any) { + this.logService.error( + `Failed to get payment method: ${error.message}`, + 'StripeService', + { paymentMethodId, error } + ); + throw error; + } + } + + /** + * Attach a payment method to a customer + */ + async attachPaymentMethod( + params: AttachPaymentMethodParams + ): Promise { + this.logService.info('Attaching payment method', 'StripeService', params); + + try { + const paymentMethod = + await this.stripeClient.stripeSDK.paymentMethods.attach( + params.paymentMethodId, + { + customer: params.customerId, + } + ); + + this.logService.info( + 'Payment method attached successfully', + 'StripeService', + { + paymentMethodId: params.paymentMethodId, + customerId: params.customerId, + } + ); + + return paymentMethod; + } catch (error: any) { + this.logService.error( + `Failed to attach payment method: ${error.message}`, + 'StripeService', + { params, error } + ); + throw error; + } + } + + /** + * Cancel a subscription + */ + async cancelSubscription( + subscriptionId: string + ): Promise { + this.logService.info('Canceling Stripe subscription', 'StripeService', { + subscriptionId, + }); + + try { + const subscription = + await this.stripeClient.stripeSDK.subscriptions.cancel(subscriptionId); + + this.logService.info( + 'Subscription canceled successfully', + 'StripeService', + { + subscriptionId, + status: subscription.status, + } + ); + + return subscription; + } catch (error: any) { + this.logService.error( + `Failed to cancel subscription: ${error.message}`, + 'StripeService', + { subscriptionId, error } + ); + throw error; + } + } +} diff --git a/stripe_collection_module/code/utils/error-types.ts b/stripe_collection_module/code/utils/error-types.ts new file mode 100644 index 0000000..d36f0f6 --- /dev/null +++ b/stripe_collection_module/code/utils/error-types.ts @@ -0,0 +1,205 @@ +/** + * Enhanced Error Types with Request Tracking + * + * Provides structured error types with categorization, retry logic, and request tracking. + */ + +/** + * Error Categories + */ +export enum ErrorCategory { + VALIDATION = 'validation', + NOT_FOUND = 'not_found', + AUTHENTICATION = 'authentication', + AUTHORIZATION = 'authorization', + NETWORK = 'network', + SERVER_ERROR = 'server_error', + TIMEOUT = 'timeout', + RATE_LIMIT = 'rate_limit', + UNKNOWN = 'unknown', +} + +/** + * Enhanced module error with request tracking + */ +export class EnhancedModuleError extends Error { + public readonly timestamp: Date; + public requestId?: string; + public correlationId?: string; + + constructor( + message: string, + public readonly category: ErrorCategory = ErrorCategory.UNKNOWN, + public readonly retryable: boolean = false, + public readonly statusCode?: number, + public readonly context?: Record + ) { + super(message); + this.name = 'EnhancedModuleError'; + this.timestamp = new Date(); + Error.captureStackTrace(this, EnhancedModuleError); + } + + /** + * Add request tracking information + */ + withRequestId(requestId: string, correlationId?: string): this { + this.requestId = requestId; + this.correlationId = correlationId || requestId; + return this; + } + + /** + * Convert error to JSON for logging + */ + toJSON(): Record { + return { + name: this.name, + message: this.message, + category: this.category, + retryable: this.retryable, + statusCode: this.statusCode, + timestamp: this.timestamp.toISOString(), + requestId: this.requestId, + correlationId: this.correlationId, + context: this.context, + stack: this.stack, + }; + } +} + +/** + * Validation error + */ +export class ValidationError extends EnhancedModuleError { + constructor(message: string, context?: Record) { + super(message, ErrorCategory.VALIDATION, false, 400, context); + this.name = 'ValidationError'; + } +} + +/** + * Not found error + */ +export class NotFoundError extends EnhancedModuleError { + constructor(message: string, context?: Record) { + super(message, ErrorCategory.NOT_FOUND, false, 404, context); + this.name = 'NotFoundError'; + } +} + +/** + * Network error + */ +export class NetworkError extends EnhancedModuleError { + constructor(message: string, context?: Record) { + super(message, ErrorCategory.NETWORK, true, 503, context); + this.name = 'NetworkError'; + } +} + +/** + * Timeout error + */ +export class TimeoutError extends EnhancedModuleError { + constructor(message: string, context?: Record) { + super(message, ErrorCategory.TIMEOUT, true, 504, context); + this.name = 'TimeoutError'; + } +} + +/** + * Rate limit error + */ +export class RateLimitError extends EnhancedModuleError { + constructor(message: string, context?: Record) { + super(message, ErrorCategory.RATE_LIMIT, true, 429, context); + this.name = 'RateLimitError'; + } +} + +/** + * Server error + */ +export class ServerError extends EnhancedModuleError { + constructor(message: string, context?: Record) { + super(message, ErrorCategory.SERVER_ERROR, true, 500, context); + this.name = 'ServerError'; + } +} + +/** + * Categorize an unknown error + */ +export function categorizeError(error: any): ErrorCategory { + // Already categorized + if (error instanceof EnhancedModuleError) { + return error.category; + } + + // HTTP status codes + if (error.statusCode) { + if (error.statusCode === 404) return ErrorCategory.NOT_FOUND; + if (error.statusCode === 401 || error.statusCode === 403) + return ErrorCategory.AUTHENTICATION; + if (error.statusCode === 429) return ErrorCategory.RATE_LIMIT; + if (error.statusCode >= 500) return ErrorCategory.SERVER_ERROR; + if (error.statusCode >= 400) return ErrorCategory.VALIDATION; + } + + // Network errors + if ( + error.code === 'ECONNREFUSED' || + error.code === 'ETIMEDOUT' || + error.code === 'ENOTFOUND' || + error.code === 'NETWORK_ERROR' + ) { + return ErrorCategory.NETWORK; + } + + // Timeout errors + if (error.name === 'TimeoutError' || error.message?.includes('timeout')) { + return ErrorCategory.TIMEOUT; + } + + return ErrorCategory.UNKNOWN; +} + +/** + * Check if error should be retried + */ +export function isRetryableError(error: any): boolean { + // Module errors have explicit retryable flag + if (error instanceof EnhancedModuleError) { + return error.retryable; + } + + // Categorize and check + const category = categorizeError(error); + + return [ + ErrorCategory.NETWORK, + ErrorCategory.TIMEOUT, + ErrorCategory.RATE_LIMIT, + ErrorCategory.SERVER_ERROR, + ].includes(category); +} + +/** + * Format error for logging + */ +export function formatErrorForLogging(error: any): Record { + if (error instanceof EnhancedModuleError) { + return error.toJSON(); + } + + return { + name: error.name || 'Error', + message: error.message || 'Unknown error', + category: categorizeError(error), + retryable: isRetryableError(error), + statusCode: error.statusCode, + code: error.code, + stack: error.stack, + }; +} diff --git a/stripe_collection_module/code/utils/error.ts b/stripe_collection_module/code/utils/error.ts index c3ba140..48d95ad 100644 --- a/stripe_collection_module/code/utils/error.ts +++ b/stripe_collection_module/code/utils/error.ts @@ -1,12 +1,13 @@ -import Config from '../config'; +import { getConfigService } from '../services/config-instance'; export default class ModuleError extends Error { constructor(message: string, metadata?: Record) { const metadataString = JSON.stringify(metadata); const stackTrace = new Error('Error').stack; const caller = stackTrace?.split('\n')[2].trim().split(' ')[1]; + const config = getConfigService(); super( - `[${Config.env.environment} | ${caller}] ${message} ${metadataString}`, + `[${config.get('environment')} | ${caller}] ${message} ${metadataString}` ); } } diff --git a/stripe_collection_module/code/utils/index.ts b/stripe_collection_module/code/utils/index.ts index e6473e2..95223ac 100644 --- a/stripe_collection_module/code/utils/index.ts +++ b/stripe_collection_module/code/utils/index.ts @@ -1,123 +1,26 @@ -import { StripeEvents } from '../interfaces'; -import StripeClient from '../clients/stripe-client'; -import moment from 'moment-timezone'; -import Config from '../config'; -import rootClient from '../clients/root-client'; -import Logger from './logger'; -import ModuleError from './error'; - -export function getStripeProductId() { - return Config.env.stripeProductId; -} - -export function convertStripeTimestampToSAST(timestamp: number) { - // Stripe sends the timestamp in seconds - // We have to convert to milliseconds to get the right conversion - // The true parameter in .toISOString keeps the conversion at UTC+2 - const utcTime = moment(timestamp * 1000); - return utcTime.clone().tz('Africa/Johannesburg').toISOString(true); -} - -/** - * Checks if a policy payment method has a collection module definition associated with it. - */ -export async function isPolicyPaymentMethodLinkedToCollectionModule( - policyId: string, -) { - const paymentMethod = await rootClient.SDK.getPolicyPaymentMethod({ - policyId, - }); - return !!paymentMethod.collection_module_definition_id; -} - -export async function getPolicyIdFromStripeEvent(event: any) { - const { type } = event; - - Logger.info(`Check for policyId in event type ${type}`, { - event, - }); - - const dataObject = event.data.object; - - switch (type) { - case StripeEvents.InvoiceCreated: { - const rootPolicyId = - dataObject?.metadata?.rootPolicyId || - dataObject?.subscription_details?.metadata?.rootPolicyId; - return rootPolicyId; - } - case StripeEvents.InvoicePaid: - case StripeEvents.InvoicePaymentFailed: - case StripeEvents.InvoiceVoided: - case StripeEvents.InvoiceMarkedUncollectible: { - return getPolicyIdFromInvoice(dataObject.id as string); - } - case StripeEvents.ChargeRefunded: { - return getPolicyIdFromInvoice(dataObject.invoice as string); - } - case StripeEvents.ChargeDisputeFundsWithdrawn: { - const charge = await getChargeDetails(dataObject.charge as string); - return getPolicyIdFromInvoice(charge.invoice as string); - } - case StripeEvents.SubscriptionScheduleUpdated: { - return dataObject?.metadata?.rootPolicyId; - } - case StripeEvents.PaymentIntentSucceeded: - case StripeEvents.PaymentIntentFailed: { - return dataObject?.metadata?.rootPolicyId; - } - default: - return undefined; - } -} - /** - * Get's the policyId from a Stripe invoice + * Utility Functions + * + * This file re-exports commonly used utilities for convenience. + * Actual implementations are in their respective modules. */ -async function getPolicyIdFromInvoice(invoiceId: string) { - const stripeAPIClient = new StripeClient(); - const invoice = await stripeAPIClient.stripeSDK.invoices.retrieve(invoiceId); - return invoice.subscription_details?.metadata?.rootPolicyId; -} - -/** - * Get's the charge details using a chargeId - */ -async function getChargeDetails(chargeId: string) { - const stripeAPIClient = new StripeClient(); - const charge = await stripeAPIClient.stripeSDK.charges.retrieve(chargeId); - return charge; -} - -/** - * Gets the next occurrence of a target day on or after a reference date - * @param referenceDate - * @param targetDay - * @returns - */ -export const getNextOccurrence = ( - referenceDate: moment.Moment, - targetDay: number, -) => { - if (targetDay < 1) { - throw new ModuleError( - `Target Day needs to be >= 1 to be valid. TargetDay=${targetDay}`, - ); - } - - // Find the next occurrence of the target day on or after the reference date - let nextOccurrence = moment(referenceDate).date(targetDay); - - if (nextOccurrence.isBefore(referenceDate)) { - nextOccurrence = nextOccurrence.add(1, 'months'); - } - - if (nextOccurrence < referenceDate) { - throw new ModuleError(`NextOccurrence date needs to be >= ReferenceDate.`, { - nextOccurrence: nextOccurrence.toISOString(), - referenceDate: referenceDate.toISOString(), - }); - } - return nextOccurrence; -}; +// Error utilities +export { default as ModuleError } from './error'; +export { + EnhancedModuleError, + ValidationError, + NotFoundError, + NetworkError, + TimeoutError, + RateLimitError, + ServerError, + ErrorCategory, + categorizeError, + isRetryableError, + formatErrorForLogging, +} from './error-types'; + +// Retry and timeout utilities +export { retryWithBackoff } from './retry'; +export { withTimeout } from './timeout'; diff --git a/stripe_collection_module/code/utils/logger.ts b/stripe_collection_module/code/utils/logger.ts index 3e13b3e..95af131 100644 --- a/stripe_collection_module/code/utils/logger.ts +++ b/stripe_collection_module/code/utils/logger.ts @@ -1,4 +1,4 @@ -import Config from '../config'; +import { getConfigService } from '../services/config-instance'; enum LogLevel { DEBUG = 'debug', @@ -11,7 +11,7 @@ export default class Logger { private static logMessage( logLevel: LogLevel, message: string, - metadata?: Record, + metadata?: Record ): void { const metadataString = JSON.stringify(metadata); // We're just using this to get the caller function name. No other way to do it in TS due to strict mode. @@ -19,8 +19,11 @@ export default class Logger { ?.split('\n')[2] .trim() .split(' ')[1]; + const config = getConfigService(); console[logLevel]( - `[${Config.env.environment.toUpperCase()} | ${caller}] ${message} ${metadataString}`, + `[${config + .get('environment') + .toUpperCase()} | ${caller}] ${message} ${metadataString}` ); } diff --git a/stripe_collection_module/code/utils/retry.ts b/stripe_collection_module/code/utils/retry.ts new file mode 100644 index 0000000..5da57e1 --- /dev/null +++ b/stripe_collection_module/code/utils/retry.ts @@ -0,0 +1,176 @@ +/** + * Retry Utilities + * + * Provides retry logic with exponential backoff and configurable strategies. + */ + +export interface RetryOptions { + /** Maximum number of retry attempts */ + maxRetries?: number; + /** Initial delay in milliseconds */ + initialDelay?: number; + /** Maximum delay in milliseconds */ + maxDelay?: number; + /** Backoff multiplier (2 = exponential) */ + backoffMultiplier?: number; + /** Function to determine if error should trigger retry */ + shouldRetry?: (error: any) => boolean; + /** Callback before each retry */ + onRetry?: (attempt: number, error: any) => void | Promise; +} + +/** + * Retry an async operation with exponential backoff + * + * @example + * ```typescript + * const result = await retryWithBackoff( + * () => apiClient.call(), + * { + * maxRetries: 3, + * initialDelay: 1000, + * shouldRetry: (error) => error.statusCode >= 500 + * } + * ); + * ``` + */ +export async function retryWithBackoff( + operation: () => Promise, + options: RetryOptions = {} +): Promise { + const { + maxRetries = 3, + initialDelay = 1000, + maxDelay = 30000, + backoffMultiplier = 2, + shouldRetry = () => true, + onRetry, + } = options; + + let lastError: Error; + let delay = initialDelay; + + for (let attempt = 0; attempt <= maxRetries; attempt++) { + try { + return await operation(); + } catch (error) { + lastError = error as Error; + + // Check if we should retry + if (attempt === maxRetries || !shouldRetry(error)) { + throw error; + } + + // Call onRetry callback if provided + if (onRetry) { + await onRetry(attempt + 1, error); + } + + // Wait before retrying + await sleep(delay); + + // Calculate next delay with exponential backoff + delay = Math.min(delay * backoffMultiplier, maxDelay); + } + } + + throw lastError!; +} + +/** + * Sleep for specified milliseconds + */ +export function sleep(ms: number): Promise { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + +/** + * Retry with jitter to prevent thundering herd + * + * @example + * ```typescript + * const result = await retryWithJitter( + * () => apiClient.call(), + * { maxRetries: 3 } + * ); + * ``` + */ +export async function retryWithJitter( + operation: () => Promise, + options: RetryOptions = {} +): Promise { + return retryWithBackoff(operation, { + ...options, + onRetry: async (attempt, error) => { + // Add random jitter (0-50% of delay) + const jitter = Math.random() * 0.5; + await sleep((options.initialDelay || 1000) * jitter); + + if (options.onRetry) { + await options.onRetry(attempt, error); + } + }, + }); +} + +/** + * Retry only for specific error types + * + * @example + * ```typescript + * const result = await retryForErrors( + * () => apiClient.call(), + * ['NETWORK_ERROR', 'TIMEOUT'], + * { maxRetries: 3 } + * ); + * ``` + */ +export async function retryForErrors( + operation: () => Promise, + retryableErrorCodes: string[], + options: RetryOptions = {} +): Promise { + return retryWithBackoff(operation, { + ...options, + shouldRetry: (error) => { + const errorCode: string = + error.code || error.statusCode?.toString() || ''; + return retryableErrorCodes.includes(errorCode); + }, + }); +} + +/** + * Retry for network and server errors only + * + * @example + * ```typescript + * const result = await retryForNetworkErrors( + * () => apiClient.call() + * ); + * ``` + */ +export async function retryForNetworkErrors( + operation: () => Promise, + options: RetryOptions = {} +): Promise { + return retryWithBackoff(operation, { + ...options, + shouldRetry: (error) => { + // Retry on network errors + if ( + error.code === 'ECONNREFUSED' || + error.code === 'ETIMEDOUT' || + error.code === 'ENOTFOUND' + ) { + return true; + } + + // Retry on 5xx errors or 429 (rate limit) + return ( + (error.statusCode && error.statusCode >= 500) || + error.statusCode === 429 + ); + }, + }); +} diff --git a/stripe_collection_module/code/utils/stripe-utils.ts b/stripe_collection_module/code/utils/stripe-utils.ts deleted file mode 100644 index 039cc93..0000000 --- a/stripe_collection_module/code/utils/stripe-utils.ts +++ /dev/null @@ -1,198 +0,0 @@ -import 'moment-timezone'; -import Stripe from 'stripe'; - -import { StripeEvents } from '../interfaces/stripe-events'; -import moment from 'moment-timezone'; -import Config from '../config'; -import StripeClient from '../clients/stripe-client'; -import rootClient from '../clients/root-client'; -import Logger from './logger'; -import ModuleError from './error'; - -class StripeUtils { - private stripeClient: StripeClient; - - constructor() { - this.stripeClient = new StripeClient(); - } - - async getSuccessfulInvoiceCharges(params: { stripeCustomerId: string }) { - const { stripeCustomerId } = params; - const charges = await this.stripeClient.stripeSDK.charges.list({ - customer: stripeCustomerId, - limit: 3, - }); - const chargesToRefund = charges.data.filter( - (charge) => - ['succeeded'].includes(charge.status) && - !charge.refunded && - !!charge.invoice, - ); - return chargesToRefund; - } - - async refundCharges(params: { charges: any[] }) { - const { charges } = params; - for (const charge of charges) { - await this.stripeClient.stripeSDK.refunds.create({ - charge: charge.id.toString(), - reason: 'requested_by_customer', - }); - - Logger.debug(`Charge refunded`, { - charge, - }); - } - } - - convertStripeTimestampToSAST = (timestamp: number) => { - const utcTime = moment(timestamp * 1000); - return utcTime.clone().tz('Africa/Johannesburg').toISOString(true); - }; - - async cancelStripeScheduleAndSubscription(params: { - rootPolicyId: string; - policyAppData: any; - prorate: boolean; - invoice_now: boolean; - }) { - const { rootPolicyId, policyAppData, prorate, invoice_now } = params; - - Logger.info( - `Start cancelling Stripe schedule & subscription for policy ${rootPolicyId}`, - ); - - try { - const stripeSubscriptionScheduleId = - policyAppData.stripe_subscription_schedule_id; - const stripeSubscriptionId = policyAppData.stripe_subscription_id; - - if (stripeSubscriptionScheduleId) { - const subscriptionSchedule = - await this.stripeClient.stripeSDK.subscriptionSchedules.retrieve( - stripeSubscriptionScheduleId as string, - ); - - const subscriptionId = - stripeSubscriptionId || subscriptionSchedule.subscription; - - if (['not_started', 'active'].includes(subscriptionSchedule.status)) { - await this.stripeClient.stripeSDK.subscriptionSchedules.cancel( - stripeSubscriptionScheduleId as string, - { - invoice_now, - prorate, - }, - ); - } - - if (subscriptionId) { - const subscription = - await this.stripeClient.stripeSDK.subscriptions.retrieve( - subscriptionId as string, - ); - - if (subscription.status !== 'canceled') { - await this.stripeClient.stripeSDK.subscriptions.cancel( - subscriptionId as string, - { - prorate, - invoice_now, - }, - ); - } - } - } - } catch (error: any) { - throw new ModuleError( - `There was an error cancelling the Stripe schedule or subscription for policy ${rootPolicyId}: ${error.message}`, - { - rootPolicyId, - error, - }, - ); - } - } - - getStripeProductId() { - return Config.env.stripeProductId; - } - - async getPolicyIdFromStripeEvent(event: any) { - const { type } = event; - - Logger.info(`Check for policyId in event type ${type}`, { - event, - }); - - const dataObject = event.data.object; - - switch (type) { - case StripeEvents.InvoiceCreated: { - const rootPolicyId = - dataObject?.metadata?.rootPolicyId || - dataObject?.subscription_details?.metadata?.rootPolicyId; - return rootPolicyId; - } - case StripeEvents.InvoicePaid: - case StripeEvents.InvoicePaymentFailed: - case StripeEvents.InvoiceVoided: - case StripeEvents.InvoiceMarkedUncollectible: { - return this.getPolicyIdFromInvoice(dataObject.id as string); - } - case StripeEvents.ChargeRefunded: { - return this.getPolicyIdFromInvoice(dataObject.invoice as string); - } - case StripeEvents.ChargeDisputeFundsWithdrawn: { - const charge: Stripe.Charge = await this.getChargeDetails( - dataObject.charge as string, - ); - if (typeof charge.invoice === 'string') { - return this.getPolicyIdFromInvoice(charge.invoice); - } - } - case StripeEvents.SubscriptionScheduleUpdated: { - return dataObject?.metadata?.rootPolicyId; - } - case StripeEvents.PaymentIntentSucceeded: - case StripeEvents.PaymentIntentFailed: { - return dataObject?.metadata?.rootPolicyId; - } - default: - return undefined; - } - } - - async isPolicyPaymentMethodLinkedToCollectionModule(policyId: string) { - const paymentMethod = await rootClient.SDK.getPolicyPaymentMethod({ - policyId, - }); - return !!paymentMethod.collection_module_definition_id; - } - - async getPolicyIdFromInvoice(invoiceId: string) { - const invoice = await this.stripeClient.stripeSDK.invoices.retrieve( - invoiceId, - ); - return invoice.subscription_details?.metadata?.rootPolicyId; - } - - async getPolicyIdFromPaymentIntent({ - paymentIntentId, - }: { - paymentIntentId: string; - }) { - const paymentIntent = - await this.stripeClient.stripeSDK.paymentIntents.retrieve( - paymentIntentId, - ); - return paymentIntent.metadata.rootPolicyId; - } - - async getChargeDetails(chargeId: string) { - const charge = await this.stripeClient.stripeSDK.charges.retrieve(chargeId); - return charge; - } -} - -export { StripeUtils }; diff --git a/stripe_collection_module/code/utils/timeout.ts b/stripe_collection_module/code/utils/timeout.ts new file mode 100644 index 0000000..8b7c029 --- /dev/null +++ b/stripe_collection_module/code/utils/timeout.ts @@ -0,0 +1,119 @@ +/** + * Timeout Utilities + * + * Provides timeout handling for async operations. + */ + +const TIMEOUT_MESSAGE = 'Operation timeout'; + +/** + * Execute an operation with a timeout + * + * @param operation - The async operation to execute + * @param timeoutMs - Timeout in milliseconds + * @returns Promise that resolves with operation result or rejects on timeout + * + * @example + * ```typescript + * try { + * const result = await withTimeout(apiCall(), 5000); + * } catch (error) { + * if (error.message === 'Operation timeout') { + * console.error('Operation timed out after 5 seconds'); + * } + * } + * ``` + */ +export async function withTimeout( + operation: Promise, + timeoutMs: number +): Promise { + return Promise.race([ + operation, + new Promise((_, reject) => + setTimeout(() => reject(new Error(TIMEOUT_MESSAGE)), timeoutMs) + ), + ]); +} + +/** + * Execute an operation with a timeout and fallback value + * + * @param operation - The async operation to execute + * @param timeoutMs - Timeout in milliseconds + * @param fallbackValue - Value to return on timeout + * @returns Promise that resolves with operation result or fallback value + * + * @example + * ```typescript + * const result = await withTimeoutFallback( + * apiCall(), + * 5000, + * { default: true } + * ); + * ``` + */ +export async function withTimeoutFallback( + operation: Promise, + timeoutMs: number, + fallbackValue: T +): Promise { + try { + return await withTimeout(operation, timeoutMs); + } catch (error) { + if (error instanceof Error && error.message === TIMEOUT_MESSAGE) { + return fallbackValue; + } + throw error; + } +} + +/** + * Create a timeout error with context + */ +export class TimeoutError extends Error { + constructor( + message: string, + public readonly timeoutMs: number, + public readonly operation?: string + ) { + super(message); + this.name = 'TimeoutError'; + } +} + +/** + * Execute operation with timeout and custom error + * + * @example + * ```typescript + * try { + * await withTimeoutError( + * apiCall(), + * 5000, + * 'API call timed out', + * 'fetchUserData' + * ); + * } catch (error) { + * if (error instanceof TimeoutError) { + * console.error(`${error.operation} timed out after ${error.timeoutMs}ms`); + * } + * } + * ``` + */ +export async function withTimeoutError( + operation: Promise, + timeoutMs: number, + errorMessage: string = TIMEOUT_MESSAGE, + operationName?: string +): Promise { + return Promise.race([ + operation, + new Promise((_, reject) => + setTimeout( + () => reject(new TimeoutError(errorMessage, timeoutMs, operationName)), + timeoutMs + ) + ), + ]); +} diff --git a/stripe_collection_module/code/webhook-hooks.ts b/stripe_collection_module/code/webhook-hooks.ts index 1e95a3d..9c4a48c 100644 --- a/stripe_collection_module/code/webhook-hooks.ts +++ b/stripe_collection_module/code/webhook-hooks.ts @@ -1,214 +1,158 @@ -import ProcessInvoiceCreatedEventController from './controllers/stripe-event-processors/processInvoiceCreatedEventController'; -import ProcessInvoicePaidEventController from './controllers/stripe-event-processors/processInvoicePaidEventController'; +/** + * Webhook Handler for Stripe Events + * + * This module handles incoming webhook requests from Stripe. + * It verifies signatures and routes events to appropriate controllers. + * + * Architecture: + * - Signature verification + * - Event routing via dependency injection + * - Minimal logic - delegates to controllers + */ + import * as crypto from 'crypto'; -import { StripeEvents } from './interfaces'; - -import { - getPolicyIdFromStripeEvent, - isPolicyPaymentMethodLinkedToCollectionModule, -} from './utils'; -import ProcessInvoicePaymentFailedEventController from './controllers/stripe-event-processors/processInvoicePaymentFailedEventController'; -import ProcessSubscriptionScheduleUpdatedEventController from './controllers/stripe-event-processors/processSubscriptionScheduleUpdatedEventController'; -import ProcessInvoiceMarkedUncollectableEventController from './controllers/stripe-event-processors/processInvoiceMarkedUncollectableEventController'; -import ProcessPaymentIntentSucceededEventController from './controllers/stripe-event-processors/processPaymentIntentSucceededEventController'; -import ProcessInvoiceChargeRefundedEventController from './controllers/stripe-event-processors/processInvoiceChargeRefundedEventController'; -import Stripe from 'stripe'; -import Config from './config'; -import Logger from './utils/logger'; -import ModuleError from './utils/error'; - -const authWebhookRequest = async (request: any) => { - // https://stripe.com/docs/webhooks/signatures#verify-manually - const { headers } = request.request; - const signature: any = { t: undefined, v1: undefined }; - headers['stripe-signature'].split(',').map((rawElement: any) => { +import { getContainer } from './core/container.setup'; +import { ServiceToken } from './core/container'; +import { LogService } from './services/log.service'; +import { getConfigService } from './services/config-instance'; +import { StripeEvents } from './interfaces/stripe-events'; + +/** + * Verify Stripe webhook signature + */ +const verifyWebhookSignature = (request: any): boolean => { + const { headers, body } = request.request; + const stripeSignature: string = headers['stripe-signature']; + + if (!stripeSignature) { + return false; + } + + // Parse signature header: t=timestamp,v1=signature + const signature: { t?: string; v1?: string } = { + t: undefined, + v1: undefined, + }; + const elements = stripeSignature.split(','); + + for (const rawElement of elements) { const [prefix, value] = rawElement.split('='); - if (['t', 'v1'].includes(prefix)) { + if (prefix === 't' || prefix === 'v1') { signature[prefix] = value; } - }); + } - const { body } = request.request; + if (!signature.t || !signature.v1) { + return false; + } + // Verify signature + const config = getConfigService(); + const webhookSecret = config.get('stripeWebhookSigningSecret'); const signedPayload = `${signature.t}.${body.toString('utf8')}`; - const expectedSignature = crypto - .createHmac('sha256', Config.env.stripeWebhookSigningSecret) + .createHmac('sha256', webhookSecret) .update(signedPayload) .digest('hex'); - // Compare the expected signature with the received signature - const signatureVerified = crypto.timingSafeEqual( - Buffer.from(signature.v1, 'hex'), - Buffer.from(expectedSignature, 'hex'), - ); - - if (!signatureVerified) { - return { - response: { - status: 403, - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({}), - }, - }; + try { + return crypto.timingSafeEqual( + Buffer.from(signature.v1, 'hex') as unknown as Uint8Array, + Buffer.from(expectedSignature, 'hex') as unknown as Uint8Array + ); + } catch (error) { + return false; } }; /** - * @typedef {Object} Request - * @property {string | null} body - The incoming request's body - * @property {Record} headers - An object containing the incoming request's body - * @property {string} method - The HTTP method used to make to incoming request (e.g. "POST") - */ - -/** - * @typedef {Object} Response - * @property {number} status - The response status code (valid range is 200 to 599) - * @property {string} body - The response body - */ - -/** - * @typedef {Object} ProcessWebhookRequestResult - * @property {Response} response - The response object - */ - -/** - * Process incoming webhook request. + * Process incoming Stripe webhook request * - * @param {Request} request - * @returns {ProcessWebhookRequestResult} + * @param request - Incoming webhook request from Stripe + * @returns Response object */ export const processWebhookRequest = async (request: any) => { - const authResult = await authWebhookRequest(request); - if (authResult) { - return authResult; - } + const container = getContainer(); + const logService = container.resolve(ServiceToken.LOG_SERVICE); - const parsedBody = JSON.parse(request.request.body); - // When handling a new Stripe event, please check getPolicyIdFromStripeEvent - // So the event data can be handled in that function too - const policyId = await getPolicyIdFromStripeEvent(parsedBody); - Logger.info(`policyId from stripe event: ${policyId}`); + try { + // Verify webhook signature + if (!verifyWebhookSignature(request)) { + logService.warn( + 'Webhook signature verification failed', + 'WebhookHandler' + ); + return { + response: { + status: 403, + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ error: 'Invalid signature' }), + }, + }; + } - if (!policyId) { - Logger.info('No policyId found in the event', { - event: parsedBody, + // Parse webhook event + const event = JSON.parse(request.request.body as string); + logService.info('Received Stripe webhook', 'WebhookHandler', { + eventType: event.type, + eventId: event.id, }); - return { - response: { - status: 200, - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({}), - }, - }; - } - - Logger.info(`Processing stripe event of type: ${parsedBody.type}`, { - policyId, - event: parsedBody, - }); + // Route to appropriate controller + // TODO: Implement your event handlers here + switch (event.type) { + // Handle setup intent succeeded - notify frontend + case StripeEvents.SetupIntentSucceeded: { + logService.info('Setup intent succeeded', 'WebhookHandler', { + setupIntentId: event.data.object.id, + paymentMethod: event.data.object.payment_method, + }); + break; + } - const assignedToCollectionModule = - await isPolicyPaymentMethodLinkedToCollectionModule(policyId); + // Example: Handle invoice.paid events + // case StripeEvents.InvoicePaid: { + // const controller = container.resolve( + // ServiceToken.YOUR_CONTROLLER + // ); + // await controller.handle(event.data.object); + // break; + // } + + // Example: Handle payment_intent.succeeded events + // case StripeEvents.PaymentIntentSucceeded: { + // const controller = container.resolve( + // ServiceToken.PAYMENT_INTENT_SUCCEEDED_CONTROLLER + // ); + // await controller.handle(event.data.object); + // break; + // } - if (!assignedToCollectionModule) { - Logger.debug( - `Ignoring this request as this policy payment method has not been assigned a collection module - policyId: ${policyId}`, - ); + default: + logService.info('Received unhandled Stripe event', 'WebhookHandler', { + eventType: event.type, + eventId: event.id, + }); + } return { response: { status: 200, headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({}), + body: JSON.stringify({ received: true }), }, }; - } - - Logger.debug(`Processing stripe event of type: ${parsedBody.type}`, { - policyId, - event: parsedBody, - }); - - try { - const payload = parsedBody.data.object; - - /** - * Add / remove the Stripe event types that you want to handle in the switch statement below. - */ - switch (parsedBody.type) { - case StripeEvents.InvoiceCreated: { - await new ProcessInvoiceCreatedEventController().process( - payload as Stripe.Invoice, - ); - break; - } - - case StripeEvents.InvoicePaid: { - await new ProcessInvoicePaidEventController().process( - payload as Stripe.Invoice, - ); - break; - } - - case StripeEvents.InvoicePaymentFailed: { - await new ProcessInvoicePaymentFailedEventController().process( - payload as Stripe.Invoice, - ); - break; - } - - case StripeEvents.SubscriptionScheduleUpdated: { - await new ProcessSubscriptionScheduleUpdatedEventController().process( - payload as Stripe.SubscriptionSchedule, - ); - break; - } - - case StripeEvents.InvoiceVoided: - case StripeEvents.InvoiceMarkedUncollectible: { - await new ProcessInvoiceMarkedUncollectableEventController().process( - payload as Stripe.Invoice, - ); - break; - } - - case StripeEvents.ChargeRefunded: { - await new ProcessInvoiceChargeRefundedEventController().process( - payload as Stripe.Charge, - ); - break; - } - - case StripeEvents.PaymentIntentSucceeded: { - await new ProcessPaymentIntentSucceededEventController().process( - payload as Stripe.PaymentIntent, - ); - break; - } - - default: - // Unexpected event type - throw new ModuleError( - `Collection module does not handle event type '${parsedBody.type}'.`, - ); - } + } catch (error: any) { + logService.error('Error processing webhook', 'WebhookHandler', { + error: error.message, + }); return { response: { - status: 200, + status: 500, headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({}), + body: JSON.stringify({ error: 'Internal server error' }), }, }; - } catch (error) { - throw new ModuleError( - `Error processing stripe event of type: ${parsedBody.type}`, - { - error, - event: parsedBody, - }, - ); } }; diff --git a/stripe_collection_module/docs/ADAPTERS.md b/stripe_collection_module/docs/ADAPTERS.md new file mode 100644 index 0000000..719178c --- /dev/null +++ b/stripe_collection_module/docs/ADAPTERS.md @@ -0,0 +1,356 @@ +# Adapters + +This directory contains data transformation adapters that convert between different system formats. + +Adapters handle: +- Data transformation between systems +- Field name and type mapping +- Business rule application during transformation +- Type safety enforcement + +## Architecture + +``` +Stripe API โ†’ StripeToRootAdapter โ†’ Root Platform API +``` + +## Available Adapters + +### StripeToRootAdapter + +Converts Stripe data structures to Root Platform formats. + +**Location**: `stripe-to-root-adapter.ts` + +**Methods**: + +#### convertInvoiceToRootPayment + +Convert Stripe invoice to Root payment update parameters. + +```typescript +import StripeToRootAdapter from '../adapters/stripe-to-root-adapter'; +import { PaymentStatus } from '@rootplatform/node-sdk'; + +const adapter = new StripeToRootAdapter(); + +const paymentUpdate = adapter.convertInvoiceToRootPayment(stripeInvoice, { + status: PaymentStatus.Successful, + failureReason: 'Card declined', + failureAction: FailureAction.BlockRetry, +}); + +// Returns: { status, failure_reason, failure_action } +``` + +Used when processing `invoice.paid` or `invoice.payment_failed` webhooks. + +#### convertCustomerToAppData + +Convert Stripe customer to Root policy app_data. + +```typescript +const adapter = new StripeToRootAdapter(); + +const appData = adapter.convertCustomerToAppData(stripeCustomer); + +await rootClient.SDK.updatePolicy({ + policyId, + body: { app_data: appData }, +}); + +// Returns: { +// stripe_customer_id, +// stripe_email, +// stripe_default_payment_method, +// stripe_created_at +// } +``` + +Used for storing Stripe customer information in Root policy. + +## When to Use Adapters + +### โœ… Use Adapters When: + +1. **Converting Between Systems** + ```typescript + // Converting webhook data to update Root + const rootPayment = adapter.convertInvoiceToRootPayment(invoice, { ... }); + await rootService.updatePayment(rootPayment); + ``` + +2. **Mapping Complex Objects** + ```typescript + // Extracting relevant data from Stripe objects + const metadata = adapter.convertPaymentIntentToMetadata(paymentIntent); + ``` + +3. **Applying Business Rules** + ```typescript + // Applying transformation rules (e.g., refunds must be negative) + const refund = adapter.convertChargeToRefund(charge); + // Returns amount as negative value + ``` + +4. **Standardizing Data** + ```typescript + // Converting timestamps, currencies, status codes + const standardized = adapter.convertToStandard(externalData); + ``` + +### โŒ Don't Use Adapters For: + +1. Simple value assignment (just assign directly) +2. API client methods (put in clients) +3. Business logic (put in services) +4. Data validation (use validation libraries) + +## Creating a New Adapter + +### Example: Root to Stripe Adapter + +```typescript +// adapters/root-to-stripe-adapter.ts +import Stripe from 'stripe'; +import * as root from '@rootplatform/node-sdk'; + +export interface ConvertPolicyParams { + includeMetadata?: boolean; +} + +export default class RootToStripeAdapter { + /** + * Convert Root policy to Stripe customer creation parameters + * + * @param policy - Root policy object + * @param params - Conversion options + * @returns Stripe customer creation parameters + */ + convertPolicyToCustomerParams( + policy: root.Policy, + params: ConvertPolicyParams = {} + ): Stripe.CustomerCreateParams { + return { + email: policy.policyholder.email, + name: `${policy.policyholder.first_name} ${policy.policyholder.last_name}`, + phone: policy.policyholder.cellphone, + metadata: params.includeMetadata ? { + root_policy_id: policy.policy_id, + root_policy_number: policy.policy_number, + } : undefined, + }; + } + + /** + * Convert Root billing amount to Stripe amount (cents) + */ + convertRootAmountToStripe(amount: number, currency: string): number { + // Root uses cents, Stripe uses cents - but good to have explicit conversion + return Math.round(amount); + } + + /** + * Convert Root billing frequency to Stripe interval + */ + convertBillingFrequency(frequency: root.BillingFrequency): Stripe.Price.Recurring.Interval { + const mapping: Record = { + monthly: 'month', + annually: 'year', + // Add other mappings as needed + }; + + return mapping[frequency] || 'month'; + } +} +``` + +### Using Multiple Adapters + +```typescript +// In a service +import StripeToRootAdapter from '../adapters/stripe-to-root-adapter'; +import RootToStripeAdapter from '../adapters/root-to-stripe-adapter'; + +export class SyncService { + constructor( + private readonly stripeToRoot: StripeToRootAdapter, + private readonly rootToStripe: RootToStripeAdapter + ) {} + + async syncPolicyToStripe(policy: root.Policy) { + // Use Root โ†’ Stripe adapter + const customerParams = this.rootToStripe.convertPolicyToCustomerParams(policy); + const customer = await stripeClient.customers.create(customerParams); + + // Use Stripe โ†’ Root adapter + const appData = this.stripeToRoot.convertCustomerToAppData(customer); + await rootClient.SDK.updatePolicy({ + policyId: policy.policy_id, + body: { app_data: appData }, + }); + } +} +``` + +## Testing Adapters + +Adapters are pure functions and easy to test: + +```typescript +import StripeToRootAdapter from '../adapters/stripe-to-root-adapter'; +import { PaymentStatus, FailureAction } from '@rootplatform/node-sdk'; + +describe('StripeToRootAdapter', () => { + let adapter: StripeToRootAdapter; + + beforeEach(() => { + adapter = new StripeToRootAdapter(); + }); + + describe('convertInvoiceToRootPayment', () => { + it('should convert paid invoice to successful payment', () => { + const invoice = { + id: 'in_123', + status: 'paid', + last_finalization_error: null, + } as any; + + const result = adapter.convertInvoiceToRootPayment(invoice, { + status: PaymentStatus.Successful, + }); + + expect(result).toEqual({ + status: PaymentStatus.Successful, + failure_reason: undefined, + failure_action: FailureAction.BlockRetry, + }); + }); + + it('should include failure reason from invoice error', () => { + const invoice = { + id: 'in_123', + status: 'open', + last_finalization_error: { + message: 'Card was declined', + }, + } as any; + + const result = adapter.convertInvoiceToRootPayment(invoice, { + status: PaymentStatus.Failed, + }); + + expect(result.failure_reason).toBe('Card was declined'); + }); + + it('should use custom failure reason when provided', () => { + const invoice = { id: 'in_123' } as any; + + const result = adapter.convertInvoiceToRootPayment(invoice, { + status: PaymentStatus.Failed, + failureReason: 'Custom reason', + }); + + expect(result.failure_reason).toBe('Custom reason'); + }); + }); + + describe('convertCustomerToAppData', () => { + it('should extract relevant customer data', () => { + const customer = { + id: 'cus_123', + email: 'test@example.com', + created: 1609459200, // 2021-01-01 00:00:00 UTC + invoice_settings: { + default_payment_method: 'pm_123', + }, + } as any; + + const result = adapter.convertCustomerToAppData(customer); + + expect(result).toEqual({ + stripe_customer_id: 'cus_123', + stripe_email: 'test@example.com', + stripe_default_payment_method: 'pm_123', + stripe_created_at: '2021-01-01T00:00:00.000Z', + }); + }); + }); +}); +``` + +## Best Practices + +### Do โœ… + +- Keep adapters pure (no side effects) +- Make methods static or instance-based (both work) +- Document transformation rules +- Add comprehensive tests +- Handle null/undefined values gracefully +- Use TypeScript for type safety +- Create interfaces for parameters + +### Don't โŒ + +- Make API calls in adapters +- Add business logic (keep it pure transformation) +- Mutate input parameters +- Throw errors (return defaults or undefined) +- Skip null checks +- Create stateful adapters + +## Common Transformation Patterns + +### Status Mapping + +```typescript +mapStatus(externalStatus: string): InternalStatus { + const mapping = { + 'external_pending': InternalStatus.Pending, + 'external_complete': InternalStatus.Success, + 'external_failed': InternalStatus.Failed, + }; + + return mapping[externalStatus] || InternalStatus.Unknown; +} +``` + +### Date Conversion + +```typescript +convertUnixToISO(unixTimestamp: number): string { + return new Date(unixTimestamp * 1000).toISOString(); +} +``` + +### Amount Transformation + +```typescript +convertToMinorUnits(amount: number, currency: string): number { + // Most currencies use 2 decimal places + const multiplier = 100; + return Math.round(amount * multiplier); +} +``` + +### Field Renaming + +```typescript +renameFields(source: SourceObject): DestinationObject { + return { + destination_field: source.source_field, + another_field: source.different_name, + // Apply transformations while renaming + created_at: new Date(source.timestamp).toISOString(), + }; +} +``` + +## Related Documentation + +- [Services Documentation](../services/README.md) - Using adapters in services +- [Controllers Documentation](../controllers/README.md) - Data flow +- [Testing Guide](../../docs/TESTING.md) - Testing adapters +- [Best Practices](../../docs/BEST_PRACTICES.md) - Design patterns + diff --git a/stripe_collection_module/docs/ARCHITECTURE.md b/stripe_collection_module/docs/ARCHITECTURE.md new file mode 100644 index 0000000..011b04e --- /dev/null +++ b/stripe_collection_module/docs/ARCHITECTURE.md @@ -0,0 +1,211 @@ +# Collection Module Architecture + +This document describes the architecture of the Stripe Collection Module template. This template has been rebuilt from the ground up with a focus on testability, maintainability, and clear separation of concerns. + +## Overview + +The collection module follows a simple service-oriented architecture with dependency injection. It's designed to be easy to understand and extend while providing solid patterns for building integrations. + +## Directory Structure + +``` +stripe_collection_module/ +โ”œโ”€โ”€ code/ +โ”‚ โ”œโ”€โ”€ core/ # Core domain models and DI container +โ”‚ โ”‚ โ”œโ”€โ”€ models/ # Domain model interfaces +โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ customer.model.ts +โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ payment.model.ts +โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ subscription.model.ts +โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ policy.model.ts +โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ payment-method.model.ts +โ”‚ โ”‚ โ”œโ”€โ”€ container.ts # Dependency injection container +โ”‚ โ”‚ โ””โ”€โ”€ container.setup.ts # Container configuration +โ”‚ โ”‚ +โ”‚ โ”œโ”€โ”€ services/ # Business logic services +โ”‚ โ”‚ โ”œโ”€โ”€ log.service.ts # Structured JSON logging +โ”‚ โ”‚ โ”œโ”€โ”€ config.service.ts # Configuration management +โ”‚ โ”‚ โ”œโ”€โ”€ render.service.ts # Payment method rendering +โ”‚ โ”‚ โ””โ”€โ”€ root.service.ts # Root platform operations +โ”‚ โ”‚ +โ”‚ โ”œโ”€โ”€ clients/ # API client wrappers +โ”‚ โ”‚ โ”œโ”€โ”€ stripe-client.ts +โ”‚ โ”‚ โ””โ”€โ”€ root-client.ts +โ”‚ โ”‚ +โ”‚ โ”œโ”€โ”€ adapters/ # Data transformation +โ”‚ โ”‚ โ””โ”€โ”€ stripe-to-root-adapter.ts +โ”‚ โ”‚ +โ”‚ โ”œโ”€โ”€ lifecycle-hooks/ # Root platform hooks +โ”‚ โ”‚ โ””โ”€โ”€ index.ts # Main lifecycle hooks +โ”‚ โ”‚ +โ”‚ โ”œโ”€โ”€ utils/ # Utilities +โ”‚ โ”‚ โ”œโ”€โ”€ error.ts +โ”‚ โ”‚ โ””โ”€โ”€ logger.ts (legacy) +โ”‚ โ”‚ +โ”‚ โ”œโ”€โ”€ lifecycle-hooks.ts # Exports lifecycle hooks +โ”‚ โ”œโ”€โ”€ webhook-hooks.ts # Webhook processing +โ”‚ โ”œโ”€โ”€ config.ts # Configuration +โ”‚ โ””โ”€โ”€ main.ts # Entry point +โ”‚ +โ”œโ”€โ”€ __tests__/ # Test files +โ”‚ โ”œโ”€โ”€ core/ +โ”‚ โ”œโ”€โ”€ services/ +โ”‚ โ”œโ”€โ”€ lifecycle-hooks/ +โ”‚ โ””โ”€โ”€ helpers/ +โ”‚ +โ””โ”€โ”€ docs/ # Documentation + โ”œโ”€โ”€ ARCHITECTURE.md + โ”œโ”€โ”€ TESTING.md + โ””โ”€โ”€ LOG_VIEWING.md +``` + +## Key Architectural Decisions + +### 1. Service-Oriented Architecture + +All business logic is encapsulated in services. Services are: +- **Injectable** - Accept dependencies via constructor +- **Testable** - Can be easily mocked and tested in isolation +- **Single Responsibility** - Each service has a clear, focused purpose + +### 2. Dependency Injection + +We use a simple, custom DI container that: +- Manages service lifecycles (singleton vs transient) +- Resolves dependencies automatically +- Makes testing easy through service replacement +- Avoids the complexity of heavy DI frameworks + +Example: +```typescript +// Register a service +container.register( + ServiceToken.LOG_SERVICE, + () => new LogService({ environment: 'production' }), + ServiceLifetime.SINGLETON +); + +// Resolve a service +const logService = container.resolve(ServiceToken.LOG_SERVICE); +``` + +### 3. Domain Models + +Domain models define clear contracts: +- Provider-agnostic where possible +- Specific implementations for Stripe/Root +- Located in `core/models/` +- No business logic, just data structures + +### 4. Structured Logging + +The `LogService` provides structured JSON logging to stdout for log aggregation systems (DataDog, CloudWatch, etc.) + +Features: +- Correlation IDs for request tracking +- Multiple log levels (DEBUG, INFO, WARN, ERROR) +- Metadata support +- JSON-formatted output for easy parsing + +### 5. HTML Rendering + +The `RenderService` centralizes all HTML generation: +- Payment method creation forms (Stripe Elements) +- Payment method summary views +- Payment method detail views +- XSS protection via HTML escaping + +## Data Flow + +### Webhook Processing + +``` +Stripe Webhook + โ†“ +webhook-hooks.ts (signature verification) + โ†“ +Event Router (switch on event type) + โ†“ +Event-Specific Controller + โ”œโ†’ Stripe Client (get details) + โ”œโ†’ RootService (update payments) + โ””โ†’ LogService (record activities) +``` + +## Service Descriptions + +### LogService +- **Purpose**: Structured JSON logging to stdout +- **Features**: Correlation IDs, JSON formatting, multiple log levels + +### ConfigurationService +- **Purpose**: Type-safe, validated configuration management +- **Features**: Environment-specific configs, validation on startup + +### RenderService +- **Purpose**: Generate HTML for dashboard views +- **Features**: XSS protection, consistent styling + +### RootService +- **Purpose**: Root platform operations +- **Features**: Business logic for Root API interactions + +### StripeService +- **Purpose**: Stripe operations +- **Features**: Business logic for Root API interactions + +## Extension Points + +When implementing the full Stripe/Root integration: + +1. **Add Event Controllers**: Create controllers for additional Stripe webhook events +2. **Implement Lifecycle Hooks**: Add logic for payment method assignment, policy updates, etc. +3. **Add Business Services**: Create service layers for complex orchestration logic +4. **Add Validation**: Create input validation for webhook events +5. **Write Tests**: Add comprehensive test coverage for new features + +## Testing Strategy + +See [TESTING.md](./TESTING.md) for detailed testing guidelines. + +Key principles: +- Unit test all services with mocked dependencies +- Test orchestration logic in controllers +- Mock external APIs (Stripe, Root) +- Use the DI container for easy mocking + +## Configuration + +Configuration is managed via environment variables: +- Loaded in `config.ts` +- Validated on startup +- Injectable for testing + +## Error Handling + +- Custom `ModuleError` class with structured logging +- Errors include context and metadata +- Stack traces captured and logged +- User-friendly error messages for dashboard + +## Security + +- Webhook signature verification +- HTML escaping for XSS prevention +- Sensitive data not logged +- Secure environment variable handling + +## Next Steps + +1. Implement additional Stripe event controllers as needed +2. Add lifecycle hook implementations for your specific use case +3. Create integration tests for critical workflows +4. Set up monitoring and alerting via DataDog/CloudWatch +5. Performance optimization and error handling improvements + +## Questions? + +For questions or clarifications, consult: +- [TESTING.md](./TESTING.md) - Testing guide +- [CUSTOMIZING.md](./CUSTOMIZING.md) - Implementation guide +- Source code comments and JSDoc + diff --git a/stripe_collection_module/docs/BEST_PRACTICES.md b/stripe_collection_module/docs/BEST_PRACTICES.md new file mode 100644 index 0000000..9f53941 --- /dev/null +++ b/stripe_collection_module/docs/BEST_PRACTICES.md @@ -0,0 +1,637 @@ +# Best Practices for Collection Modules + +This guide covers production best practices for building and maintaining collection modules. + +## Table of Contents + +- [Architecture](#architecture) +- [Error Handling](#error-handling) +- [Performance](#performance) +- [Security](#security) +- [Testing](#testing) +- [Logging & Monitoring](#logging--monitoring) +- [Cost Optimization](#cost-optimization) +- [Maintenance](#maintenance) + +--- + +## Architecture + +### Use Dependency Injection + +โœ… **Do:** +```typescript +class PaymentService { + constructor( + private readonly client: PaymentClient, + private readonly logger: LogService + ) {} +} +``` + +โŒ **Don't:** +```typescript +class PaymentService { + private client = new PaymentClient(); // Hard dependency + private logger = console; // Global dependency +} +``` + +### Keep Services Stateless + +โœ… **Do:** +```typescript +class PaymentService { + async processPayment(request: PaymentRequest) { + // No instance state, idempotent operation + return await this.client.create(request); + } +} +``` + +โŒ **Don't:** +```typescript +class PaymentService { + private processedIds = new Set(); // State won't persist across Lambda invocations +} +``` + +### Separate Concerns + +โœ… **Do:** +- **Clients**: API communication only +- **Services**: Business logic only +- **Controllers**: Event routing only +- **Models**: Data structures only + +โŒ **Don't:** +- Mix API calls with business logic +- Put business logic in controllers +- Use models for data transformation + +--- + +## Error Handling + +### Use Structured Errors + +โœ… **Do:** +```typescript +class PaymentError extends Error { + constructor( + message: string, + public readonly code: string, + public readonly retryable: boolean, + public readonly context?: Record + ) { + super(message); + this.name = 'PaymentError'; + } +} + +throw new PaymentError( + 'Payment failed', + 'PAYMENT_DECLINED', + false, // Don't retry + { orderId: '123', reason: 'insufficient_funds' } +); +``` + +โŒ **Don't:** +```typescript +throw new Error('Payment failed'); // No context, unclear if retryable +``` + +### Implement Retry Logic + +โœ… **Do:** +```typescript +async function withRetry( + operation: () => Promise, + maxRetries: number = 3 +): Promise { + for (let attempt = 0; attempt < maxRetries; attempt++) { + try { + return await operation(); + } catch (error) { + if (!error.retryable || attempt === maxRetries - 1) { + throw error; + } + await sleep(Math.pow(2, attempt) * 1000); // Exponential backoff + } + } +} +``` + +### Handle Timeouts + +โœ… **Do:** +```typescript +async function withTimeout( + operation: Promise, + timeoutMs: number +): Promise { + return Promise.race([ + operation, + new Promise((_, reject) => + setTimeout(() => reject(new Error('Operation timeout')), timeoutMs) + ) + ]); +} +``` + +--- + +## Performance + +### Optimize Cold Starts + +โœ… **Do:** +- Minimize dependencies +- Lazy-load heavy modules +- Use ARM architecture (Graviton2) +- Increase memory allocation (faster CPU) + +```typescript +// Lazy load heavy dependencies +let stripeSDK: any; +async function getStripeSDK() { + if (!stripeSDK) { + stripeSDK = await import('stripe'); + } + return stripeSDK; +} +``` + +### Use Connection Pooling + +โœ… **Do:** +```typescript +// Reuse client across invocations +let cachedClient: StripeClient; + +export function getStripeClient(): StripeClient { + if (!cachedClient) { + cachedClient = new StripeClient(); + } + return cachedClient; +} +``` + +โŒ **Don't:** +```typescript +// Creates new client every time +export function processPayment() { + const client = new StripeClient(); // Slow! + // ... +} +``` + +### Batch Operations + +โœ… **Do:** +```typescript +// Process multiple items in one call +const payments = await Promise.all( + policyIds.map(id => rootClient.getPayment(id)) +); +``` + +โŒ **Don't:** +```typescript +// Sequential calls - slow! +for (const id of policyIds) { + const payment = await rootClient.getPayment(id); +} +``` + +### Rate Limiting + +โœ… **Do:** +```typescript +class RateLimiter { + private queue: Array<() => void> = []; + private processing = 0; + + constructor(private maxConcurrent: number = 10) {} + + async execute(operation: () => Promise): Promise { + while (this.processing >= this.maxConcurrent) { + await new Promise(resolve => this.queue.push(resolve)); + } + + this.processing++; + try { + return await operation(); + } finally { + this.processing--; + this.queue.shift()?.(); + } + } +} +``` + +--- + +## Security + +### Never Log Sensitive Data + +โœ… **Do:** +```typescript +logger.info('Payment processed', 'PaymentService', { + paymentId: payment.id, + amount: payment.amount, + // Don't log: card numbers, API keys, PII +}); +``` + +โŒ **Don't:** +```typescript +logger.info('Payment processed', payment); // May contain sensitive data! +``` + +### Validate All Inputs + +โœ… **Do:** +```typescript +import Joi from 'joi'; + +const paymentSchema = Joi.object({ + amount: Joi.number().positive().required(), + currency: Joi.string().length(3).required(), + policyId: Joi.string().uuid().required() +}); + +export async function createPayment(data: unknown) { + const validated = await paymentSchema.validateAsync(data); + // Now safely use validated data +} +``` + +### Verify Webhook Signatures + +โœ… **Do:** +```typescript +export async function handleWebhook(event: any) { + // ALWAYS verify signature first + const isValid = stripe.webhooks.constructEvent( + event.body, + event.headers['stripe-signature'], + webhookSecret + ); + + if (!isValid) { + return { statusCode: 400, body: 'Invalid signature' }; + } + + // Process webhook +} +``` + +โŒ **Don't:** +```typescript +export async function handleWebhook(event: any) { + // Process webhook WITHOUT verification - dangerous! + await processPayment(event.data); +} +``` + +### Use Environment-Specific Keys + +โœ… **Do:** +```typescript +const config = { + development: { + stripeKey: 'sk_test_xxxxx', // Test key + rootApiKey: 'sandbox_xxxxx' // Sandbox key + }, + production: { + stripeKey: process.env.STRIPE_KEY_LIVE, // From Secrets Manager + rootApiKey: process.env.ROOT_KEY_LIVE + } +}; +``` + +### Implement API Key Rotation + +โœ… **Do:** +- Store keys in AWS Secrets Manager +- Support multiple valid keys during rotation +- Audit key usage in CloudWatch +- Rotate keys every 90 days + +--- + +## Testing + +### Test Coverage Goals + +- **Critical paths**: 90%+ coverage +- **Services**: 80%+ coverage +- **Overall**: 70%+ coverage + +### Test Pyramid + +``` + E2E Tests (10%) + Integration Tests (30%) + Unit Tests (60%) +``` + +### Write Meaningful Tests + +โœ… **Do:** +```typescript +it('should create payment and update Root when invoice is paid', async () => { + // Arrange + const invoice = createMockInvoice({ amount: 1000 }); + mockStripeClient.getInvoice.mockResolvedValue(invoice); + mockRootClient.createPayment.mockResolvedValue({ id: 'pay_123' }); + + // Act + await processInvoicePaid({ invoiceId: invoice.id }); + + // Assert + expect(mockRootClient.createPayment).toHaveBeenCalledWith({ + policyId: invoice.metadata.policyId, + amount: 10.00, + status: 'succeeded' + }); +}); +``` + +โŒ **Don't:** +```typescript +it('works', async () => { + const result = await service.doSomething(); + expect(result).toBeDefined(); // Too vague +}); +``` + +### Use Test Factories + +โœ… **Do:** +```typescript +// __tests__/helpers/factories.ts +export function createMockPayment(overrides?: Partial): Payment { + return { + id: 'pay_123', + amount: 100, + status: 'succeeded', + ...overrides + }; +} + +// In tests +const payment = createMockPayment({ amount: 500 }); +``` + +--- + +## Logging & Monitoring + +### Use Structured Logging + +โœ… **Do:** +```typescript +logger.info('Payment processed', 'PaymentService', { + paymentId: 'pay_123', + amount: 100, + currency: 'USD', + policyId: 'pol_456', + duration: 234 // ms +}); +``` + +โŒ **Don't:** +```typescript +console.log('Payment pay_123 processed for $100 USD'); // Unstructured +``` + +### Log Appropriate Levels + +- **ERROR**: Something failed, needs attention +- **WARN**: Something unexpected, but handled +- **INFO**: Important business events +- **DEBUG**: Detailed information (dev only) + +โœ… **Do:** +```typescript +logger.info('Payment created', 'PaymentService', { paymentId }); +logger.warn('Retrying failed API call', 'StripeClient', { attempt: 2 }); +logger.error('Payment failed', 'PaymentService', { error, paymentId }); +``` + +### Include Correlation IDs + +โœ… **Do:** +```typescript +const correlationId = event.requestContext?.requestId || generateId(); + +logger.info('Processing request', 'Handler', { correlationId }); +// Pass correlationId through all operations +``` + +### Set Up Alarms + +Create CloudWatch Alarms for: +- **Error rate** > 5% over 5 minutes +- **Timeout rate** > 1% over 5 minutes +- **API latency** > 3 seconds (p95) +- **Memory usage** > 80% + +### Create Dashboards + +Monitor: +- Request volume +- Error rates by type +- Latency percentiles (p50, p95, p99) +- Cold start frequency +- Memory utilization +- Cost per invocation + +--- + +## Cost Optimization + +### Right-Size Memory + +โœ… **Do:** +- Start with 512 MB +- Monitor actual usage in CloudWatch +- Adjust based on metrics +- Test different configurations + +### Use ARM Architecture + +โœ… **Do:** +- Use ARM64 (Graviton2) for 20% cost savings +- Same or better performance +- Ensure dependencies support ARM + +### Optimize Timeouts + +โœ… **Do:** +- Set realistic timeouts +- Don't over-provision (default 3s often sufficient) +- Monitor actual duration +- Fail fast on errors + +### Manage Log Retention + +โœ… **Do:** +```bash +# Set appropriate retention +aws logs put-retention-policy \ + --log-group-name /aws/lambda/your-function \ + --retention-in-days 30 # or 7 for dev +``` + +### Use Reserved Concurrency Wisely + +โŒ **Don't:** +- Reserve concurrency unless you have a specific need +- It costs money even when not used + +โœ… **Do:** +- Use on-demand scaling +- Set account-level concurrency limits if needed + +--- + +## Maintenance + +### Version Control + +โœ… **Do:** +- Use semantic versioning +- Tag releases +- Maintain changelog +- Document breaking changes + +### Code Reviews + +โœ… **Do:** +- Review all code changes +- Check test coverage +- Verify error handling +- Review security implications + +### Documentation + +โœ… **Do:** +- Keep README up to date +- Document configuration changes +- Maintain runbooks +- Document incident responses + +### Monitoring + +โœ… **Do:** +- Review logs weekly +- Check dashboards daily +- Investigate anomalies +- Track key metrics + +### Regular Updates + +โœ… **Do:** +- Update dependencies monthly +- Review security advisories +- Test after updates +- Rotate API keys quarterly + +--- + +## Anti-Patterns + +### Don't Use Global State + +โŒ **Don't:** +```typescript +let processedEvents = []; // Won't work across Lambda invocations + +export async function processEvent(event: any) { + if (processedEvents.includes(event.id)) return; + processedEvents.push(event.id); +} +``` + +### Don't Ignore Errors + +โŒ **Don't:** +```typescript +try { + await updatePayment(); +} catch (error) { + // Silent failure - dangerous! +} +``` + +### Don't Block Event Loop + +โŒ **Don't:** +```typescript +// Synchronous crypto operations block event loop +const hash = crypto.pbkdf2Sync(password, salt, 10000, 64, 'sha512'); +``` + +โœ… **Do:** +```typescript +// Use async version +const hash = await crypto.pbkdf2(password, salt, 10000, 64, 'sha512'); +``` + +### Don't Trust User Input + +โŒ **Don't:** +```typescript +const sql = `SELECT * FROM payments WHERE id = ${req.body.id}`; +``` + +โœ… **Do:** +```typescript +// Validate and sanitize +const paymentId = paymentIdSchema.validateSync(req.body.id); +const payment = await db.getPayment(paymentId); +``` + +--- + +## Quick Reference Checklist + +### Before Deployment +- [ ] All tests passing (>70% coverage) +- [ ] No sensitive data in logs +- [ ] Error handling implemented +- [ ] Webhook signature verification +- [ ] Configuration validated +- [ ] Dependencies updated +- [ ] Security review completed +- [ ] Documentation updated + +### Production Monitoring +- [ ] CloudWatch Alarms configured +- [ ] Dashboard created +- [ ] Log retention set +- [ ] Metrics tracked +- [ ] On-call rotation defined +- [ ] Runbook documented + +### Regular Maintenance +- [ ] Review logs weekly +- [ ] Update dependencies monthly +- [ ] Rotate keys quarterly +- [ ] Review metrics monthly +- [ ] Update documentation as needed + +--- + +## Resources + +- [AWS Lambda Best Practices](https://docs.aws.amazon.com/lambda/latest/dg/best-practices.html) +- [Node.js Best Practices](https://github.com/goldbergyoni/nodebestpractices) +- [TypeScript Best Practices](https://www.typescriptlang.org/docs/handbook/declaration-files/do-s-and-don-ts.html) +- [Logging Best Practices](https://aws.amazon.com/blogs/mt/best-practices-cloudwatch-logs/) + diff --git a/stripe_collection_module/docs/CI_CD.md b/stripe_collection_module/docs/CI_CD.md new file mode 100644 index 0000000..72b003d --- /dev/null +++ b/stripe_collection_module/docs/CI_CD.md @@ -0,0 +1,267 @@ +# CI/CD Setup + +This document explains the Continuous Integration and Continuous Deployment (CI/CD) setup for the Stripe Collection Module. + +## Overview + +The project uses **GitHub Actions** for automated testing and quality checks on every pull request and push to main branches. + +## What Gets Tested + +### On Every Pull Request + +When you create or update a pull request, GitHub Actions automatically: + +1. โœ… **Runs all tests** +2. ๐Ÿ“Š **Checks code coverage** (must be โ‰ฅ70%) +3. ๐Ÿ” **Runs linter** (ESLint) +4. ๐Ÿ—๏ธ **Verifies build** (TypeScript compilation) +5. ๐Ÿ’ฌ **Posts coverage report** as PR comment + +### Status Checks + +Pull requests show three independent status checks: +- โœ… **Lint** - ESLint passes with no errors +- โœ… **Test** - All tests pass with 70%+ coverage +- โœ… **Build Check** - TypeScript compiles successfully + +**You cannot merge** until all checks pass. + +## CI Workflow Details + +### Workflow File + +`.github/workflows/ci.yml` + +### Triggers + +- **Push** to `main`, `master`, or `develop` +- **Pull requests** to `main`, `master`, or `develop` + +### Jobs + +#### 1. Lint Job + +```yaml +- Checkout code +- Setup Node.js 18.x +- Install dependencies (npm ci) +- Create env.ts from env.sample.ts +- Run ESLint +``` + +#### 2. Test Job + +```yaml +- Checkout code +- Setup Node.js 18.x +- Install dependencies (npm ci) +- Create env.ts from env.sample.ts +- Run Jest tests with coverage (275+ tests) +- Comment coverage report on PR +``` + +#### 3. Build Job + +```yaml +- Checkout code +- Setup Node.js 18.x +- Install dependencies (npm ci) +- Create env.ts from env.sample.ts +- Build TypeScript (type checking only - noEmit: true) +``` + +## Coverage Requirements + +All code must maintain **70% coverage** across: + +| Metric | Threshold | +|--------|-----------| +| Statements | 70% | +| Branches | 70% | +| Functions | 70% | +| Lines | 70% | + +## Viewing CI Results + +### 1. Pull Request Page + +Check the **Checks** tab on your PR to see: +- Test results +- Linting results +- Build status +- Coverage report (posted as comment) + +### 2. Actions Tab + +Visit the **Actions** tab in GitHub to: +- View detailed logs +- Re-run failed workflows +- See all workflow runs + +## Local Pre-Push Checklist + +Before pushing code, run locally: + +```bash +cd stripe_collection_module + +# 1. Run linter +npm run lint + +# 2. Fix linting issues +npm run lint:fix + +# 3. Run all tests +npm test + +# 4. Check coverage +npm test -- --coverage + +# 5. Build +npm run build +``` + +**If all pass locally, CI should pass too!** + +## Debugging Failed CI + +### Tests Fail in CI but Pass Locally + +**Causes:** +- Different Node.js version +- Cached dependencies +- Environment-specific code + +**Solutions:** +```bash +# Use correct Node version +nvm use + +# Clean install +rm -rf node_modules package-lock.json +npm install + +# Run with CI flag +npm test -- --ci +``` + +### Coverage Below Threshold + +**Check what needs testing:** +```bash +npm test -- --coverage --verbose +``` + +**Add tests for uncovered files** - see [TESTING.md](./TESTING.md) + +### Linter Failures + +**Run locally:** +```bash +npm run lint +``` + +**Auto-fix:** +```bash +npm run lint:fix +``` + +### Build Failures + +**Check TypeScript errors:** +```bash +npm run build +``` + +## Coverage Reports + +### On Pull Requests + +The CI automatically posts a coverage report comment: + +``` +๐Ÿ“Š Test Coverage Report + +| Category | Coverage | +|----------|----------| +| Statements | 92.91% | +| Branches | 84.77% | +| Functions | 95.38% | +| Lines | 93.49% | + +โœ… Coverage threshold: 70% +``` + +## Pull Request Template + +The project includes a PR template that prompts you to: + +- Describe changes +- Verify testing +- Update documentation +- Complete checklist + +**Use the template** to ensure quality submissions. + +## Best Practices + +### โœ… DO + +- Run tests locally before pushing +- Fix failing CI immediately +- Maintain high test coverage +- Write meaningful commit messages +- Keep PRs focused and small + +### โŒ DON'T + +- Commit with failing tests +- Skip linting errors +- Merge with failing CI +- Commit without testing +- Disable coverage checks + +## Troubleshooting + +### "Cannot find module '../env'" Error + +**Cause:** The `code/env.ts` file is gitignored and not in the repository. + +**Solution:** The CI workflow automatically creates `code/env.ts` from `code/env.sample.ts` before building. If this step fails, check that `env.sample.ts` exists and is valid. + +**Why this happens:** Environment files contain secrets and should never be committed. The CI uses the sample file as a placeholder for compilation. + +### "npm ci" Fails + +**Cause:** Outdated package-lock.json + +**Fix:** +```bash +npm install +git add package-lock.json +git commit -m "Update package-lock.json" +``` + +### Cache Issues + +**Clear GitHub Actions cache:** +1. Go to **Actions** tab +2. Click **Caches** +3. Delete relevant caches +4. Re-run workflow + +### Permissions Error + +**Ensure GitHub Actions is enabled:** +1. Go to **Settings** โ†’ **Actions** โ†’ **General** +2. Enable "Allow all actions" +3. Enable "Read and write permissions" + +## Resources + +- [GitHub Actions Docs](https://docs.github.com/en/actions) +- [Workflow Syntax](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions) +- [Jest CI Configuration](https://jestjs.io/docs/configuration#ci-boolean) +- [Node.js Action](https://github.com/actions/setup-node) + diff --git a/stripe_collection_module/docs/CLIENTS.md b/stripe_collection_module/docs/CLIENTS.md new file mode 100644 index 0000000..5d7f24f --- /dev/null +++ b/stripe_collection_module/docs/CLIENTS.md @@ -0,0 +1,263 @@ +# API Clients + +This directory contains client wrappers for external APIs (Stripe and Root Platform). Clients provide a clean interface for making API calls with proper error handling, configuration, and type safety. + +## Architecture + +``` +Controllers โ†’ Services โ†’ Clients โ†’ External APIs +``` + +Clients are thin wrappers around SDK/API libraries that handle: +- Configuration injection +- Error transformation +- Type safety +- Common API patterns + +## Available Clients + +### StripeClient + +Wrapper around the Stripe Node.js SDK with helper methods for common operations. + +**Location**: `stripe-client.ts` + +**Usage**: + +```typescript +import StripeClient from '../clients/stripe-client'; + +const stripeClient = new StripeClient(); + +// Access Stripe SDK directly +const customer = await stripeClient.stripeSDK.customers.create({ + email: 'customer@example.com', + name: 'John Doe', +}); + +// Create setup intent +const setupIntent = await stripeClient.stripeSDK.setupIntents.create({}); + +// Create payment intent +const paymentIntent = await stripeClient.stripeSDK.paymentIntents.create({ + amount: 10000, + currency: 'zar', + customer: 'cus_123', +}); +``` + +**Configuration**: +- Automatically retrieves `stripeSecretKey` from ConfigurationService +- Uses environment-specific keys (test/live) + +### RootClient + +Singleton wrapper around the Root Platform SDK. + +**Location**: `root-client.ts` + +**Usage**: + +```typescript +import rootClient from '../clients/root-client'; + +// Get policy +const policy = await rootClient.SDK.getPolicyById({ + policyId: 'policy_123' +}); + +// Update payment +await rootClient.SDK.updatePaymentsAsync({ + paymentUpdates: [{ + payment_id: 'payment_456', + status: 'successful', + }], +}); + +// Create payment +const payment = await rootClient.SDK.createPolicyPayment({ + policyId: 'policy_123', + paymentCreate: { + amount: 10000, + description: 'Premium payment', + payment_date: '2024-01-15', + payment_type: 'premium', + status: 'pending', + }, +}); +``` + +**Configuration**: +- Automatically retrieves `rootApiKey` and `rootBaseUrl` from ConfigurationService +- Uses environment-specific endpoints (sandbox/production) + +**Note**: RootClient is a singleton - import the default export, don't instantiate with `new`. + +## Error Handling + +Handle Stripe SDK errors in your services: + +```typescript +try { + const customer = await stripeClient.stripeSDK.customers.create(params); +} catch (error: any) { + logService.error('Failed to create customer', 'MyService', { params }, error); + throw error; +} +``` + +## Testing + +### Mocking StripeClient + +```typescript +import StripeClient from '../clients/stripe-client'; + +// Mock the entire client +jest.mock('../clients/stripe-client'); + +const mockStripeClient = { + stripeSDK: { + customers: { + create: jest.fn().mockResolvedValue({ id: 'cus_123' }), + }, + paymentIntents: { + create: jest.fn(), + }, + setupIntents: { + create: jest.fn(), + }, + }, +} as unknown as jest.Mocked; +``` + +### Mocking RootClient + +```typescript +import rootClient from '../clients/root-client'; + +// Mock the singleton +jest.mock('../clients/root-client', () => ({ + default: { + SDK: { + getPolicyById: jest.fn(), + updatePaymentsAsync: jest.fn(), + createPolicyPayment: jest.fn(), + }, + }, +})); + +// Use in tests +(rootClient.SDK.getPolicyById as jest.Mock).mockResolvedValue({ + policy_id: 'policy_123', + // ... other policy data +}); +``` + +## Creating a New Client + +When integrating with a new external API: + +### 1. Create Client File + +```typescript +// clients/my-api-client.ts +import { MyAPISDK } from 'my-api-sdk'; +import { getConfigService } from '../services/config-instance'; +import ModuleError from '../utils/error'; + +export default class MyAPIClient { + public sdk: MyAPISDK; + + constructor() { + const config = getConfigService(); + this.sdk = new MyAPISDK({ + apiKey: config.get('myApiKey'), + baseUrl: config.get('myApiBaseUrl'), + }); + } + + // Access SDK directly - error handling done in services + // No need to wrap every method +} +} +``` + +### 2. Add Configuration + +Update `config.service.ts` to include the new API credentials: + +```typescript +export interface EnvironmentConfig { + // ... existing config + myApiKey: string; + myApiBaseUrl: string; +} +``` + +### 3. Add Environment Variables + +Update `env.ts` and `env.sample.ts`: + +```typescript +export const MY_API_KEY = process.env.MY_API_KEY || ''; +export const MY_API_BASE_URL = process.env.MY_API_BASE_URL || ''; +``` + +### 4. Use in Services + +```typescript +import MyAPIClient from '../clients/my-api-client'; + +export class MyService { + constructor( + private readonly logService: LogService, + private readonly myApiClient: MyAPIClient + ) {} + + async performOperation() { + const result = await this.myApiClient.doSomething({ ... }); + return result; + } +} +``` + +## Best Practices + +### Do โœ… + +- Keep clients thin - expose the SDK +- Use ConfigurationService for credentials +- Document usage patterns +- Add TypeScript types where needed +- Initialize SDK in constructor + +### Don't โŒ + +- Put business logic in clients +- Create multiple instances unnecessarily +- Expose credentials directly +- Make clients stateful +- Wrap every SDK method + +## Types + +Use Stripe's built-in TypeScript types: + +```typescript +import Stripe from 'stripe'; + +// Stripe SDK provides comprehensive types +const params: Stripe.CustomerCreateParams = { + email: 'customer@example.com', + name: 'John Doe', +}; +``` + +## Related Documentation + +- [Services Documentation](../services/README.md) - Using clients in services +- [Configuration](../../docs/ROOT_CONFIGURATION.md) - Setting up API credentials +- [Error Handling](../../docs/BEST_PRACTICES.md#error-handling) - Error patterns +- [Testing Guide](../../docs/TESTING.md) - Testing with mocked clients + diff --git a/stripe_collection_module/docs/CODE_STRUCTURE.md b/stripe_collection_module/docs/CODE_STRUCTURE.md new file mode 100644 index 0000000..0281fbe --- /dev/null +++ b/stripe_collection_module/docs/CODE_STRUCTURE.md @@ -0,0 +1,395 @@ +# Code Structure Guide + +This document provides an overview of the codebase organization and links to detailed documentation for each area. + +## Directory Overview + +``` +stripe_collection_module/ +โ”œโ”€โ”€ code/ # Source code +โ”‚ โ”œโ”€โ”€ adapters/ # Data transformation layer +โ”‚ โ”œโ”€โ”€ clients/ # External API wrappers +โ”‚ โ”œโ”€โ”€ controllers/ # Event processors & orchestration +โ”‚ โ”œโ”€โ”€ core/ # DI container & infrastructure +โ”‚ โ”œโ”€โ”€ interfaces/ # TypeScript interfaces +โ”‚ โ”œโ”€โ”€ lifecycle-hooks/ # Root Platform callbacks +โ”‚ โ”œโ”€โ”€ services/ # Business logic layer +โ”‚ โ”œโ”€โ”€ utils/ # Utility functions +โ”‚ โ”œโ”€โ”€ main.ts # Entry point +โ”‚ โ””โ”€โ”€ webhook-hooks.ts # Stripe webhook handler +โ”‚ +โ”œโ”€โ”€ __tests__/ # Test files +โ”‚ โ”œโ”€โ”€ core/ # Core tests +โ”‚ โ”œโ”€โ”€ helpers/ # Test utilities +โ”‚ โ”œโ”€โ”€ lifecycle-hooks/ # Hook tests +โ”‚ โ””โ”€โ”€ services/ # Service tests +โ”‚ +โ”œโ”€โ”€ docs/ # Documentation +โ”‚ โ”œโ”€โ”€ ARCHITECTURE.md # System architecture +โ”‚ โ”œโ”€โ”€ BEST_PRACTICES.md # Coding guidelines +โ”‚ โ”œโ”€โ”€ CODE_STRUCTURE.md # This file +โ”‚ โ”œโ”€โ”€ CUSTOMIZING.md # Implementation guide +โ”‚ โ”œโ”€โ”€ DEPLOYMENT.md # Deployment guide +โ”‚ โ”œโ”€โ”€ LOG_VIEWING.md # Logging guide +โ”‚ โ”œโ”€โ”€ ROOT_CONFIGURATION.md # Root Platform config +โ”‚ โ”œโ”€โ”€ SETUP.md # Setup instructions +โ”‚ โ”œโ”€โ”€ TESTING.md # Testing guide +โ”‚ โ””โ”€โ”€ WEBHOOKS.md # Webhook guide +โ”‚ +โ””โ”€โ”€ scripts/ # Deployment scripts + โ”œโ”€โ”€ deploy.sh # Deployment automation + โ””โ”€โ”€ validate-config.sh # Config validation +``` + +## Code Organization + +### Layer Architecture + +The codebase follows a clean layered architecture: + +``` +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ Entry Points (main.ts, webhook-hooks) โ”‚ +โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค +โ”‚ Lifecycle Hooks & Controllers โ”‚ โ† Orchestration layer +โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค +โ”‚ Services โ”‚ โ† Business logic layer +โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค +โ”‚ Adapters & Clients โ”‚ โ† Infrastructure layer +โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค +โ”‚ External APIs (Stripe, Root) โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ +``` + +Each layer has a specific responsibility and only depends on layers below it. + +## Detailed Documentation + +### ๐Ÿ“ [Adapters](../code/adapters/README.md) + +Data transformation between Stripe and Root formats. + +**Key Topics**: +- What are adapters and when to use them +- StripeToRootAdapter usage examples +- Creating custom adapters +- Testing adapters + +**Quick Example**: +```typescript +import StripeToRootAdapter from './adapters/stripe-to-root-adapter'; + +const adapter = new StripeToRootAdapter(); +const rootPayment = adapter.convertInvoiceToRootPayment(invoice, { + status: PaymentStatus.Successful, +}); +``` + +--- + +### ๐Ÿ“ [Clients](../code/clients/README.md) + +Thin wrappers around external API SDKs. + +**Key Topics**: +- StripeClient configuration and usage +- RootClient singleton pattern +- Error handling in clients +- Testing with mocked clients +- Creating new API clients + +**Quick Example**: +```typescript +import StripeClient from './clients/stripe-client'; +import rootClient from './clients/root-client'; + +const stripeClient = new StripeClient(); +const customer = await stripeClient.stripeSDK.customers.create({...}); + +const policy = await rootClient.SDK.getPolicyById({ policyId: 'pol_123' }); +``` + +--- + +### ๐Ÿ“ [Controllers](../code/controllers/README.md) + +Event processors that orchestrate service calls. + +**Key Topics**: +- Controller architecture and patterns +- Stripe webhook event processors +- Root lifecycle event processors +- Dependency injection in controllers +- Creating new controllers +- Testing controllers + +**Quick Example**: +```typescript +export class InvoicePaidController { + constructor( + private readonly logService: LogService, + private readonly rootService: RootService + ) {} + + async handle(invoice: Stripe.Invoice): Promise { + // Orchestrate the workflow + } +} +``` + +--- + +### ๐Ÿ“ [Core](../code/core/README.md) + +Dependency injection container and infrastructure. + +**Key Topics**: +- DI container usage +- Service registration patterns +- Service tokens +- Singleton vs Transient lifetimes +- Testing with DI container + +**Quick Example**: +```typescript +import { getContainer } from './core/container.setup'; +import { ServiceToken } from './core/container'; + +const container = getContainer(); +const logService = container.resolve(ServiceToken.LOG_SERVICE); +``` + +--- + +### ๐Ÿ“ [Lifecycle Hooks](../code/lifecycle-hooks/README.md) + +Root Platform callback functions. + +**Key Topics**: +- What are lifecycle hooks +- Available hook functions +- Payment method hooks +- Policy lifecycle hooks +- Payment lifecycle hooks +- Implementation patterns +- Testing hooks + +**Quick Example**: +```typescript +export async function afterPolicyPaymentMethodAssigned({ policy }) { + // React to payment method assignment + await assignPaymentMethodToStripe(policy); +} +``` + +--- + +### ๐Ÿ“ [Services](../code/services/README.md) + +Business logic and domain operations. + +**Key Topics**: +- Service layer overview +- LogService - structured logging +- ConfigurationService - type-safe config +- RenderService - HTML generation +- RootService - Root Platform operations +- Creating custom services +- Testing services + +**Quick Example**: +```typescript +export class RootService { + constructor(private readonly logService: LogService) {} + + async updatePaymentStatus(params: UpdateParams): Promise { + // Business logic here + } +} +``` + +--- + +### ๐Ÿ“ [Utils](../code/utils/README.md) + +Reusable utility functions and helpers. + +**Key Topics**: +- Error types and handling +- Retry logic with exponential backoff +- Timeout utilities +- Error categorization +- Best practices + +**Quick Example**: +```typescript +import { retryWithBackoff, withTimeout } from './utils'; + +const result = await retryWithBackoff( + () => externalApiCall(), + { maxRetries: 3 } +); +``` + +--- + +## Cross-Cutting Concerns + +### Configuration + +Configuration is managed centrally: +- **Source**: `code/env.ts` - Environment variables +- **Service**: `code/services/config.service.ts` - Type-safe access +- **Documentation**: [ROOT_CONFIGURATION.md](./ROOT_CONFIGURATION.md) + +### Logging + +Structured logging throughout: +- **Service**: `code/services/log.service.ts` - LogService +- **Output**: JSON to stdout (CloudWatch/DataDog) +- **Documentation**: [LOG_VIEWING.md](./LOG_VIEWING.md) + +### Error Handling + +Consistent error handling: +- **Errors**: `code/utils/error.ts` and `error-types.ts` +- **Pattern**: Catch, log, re-throw with context +- **Documentation**: [BEST_PRACTICES.md](./BEST_PRACTICES.md#error-handling) + +### Testing + +Comprehensive test coverage: +- **Location**: `__tests__/` directory +- **Pattern**: Unit tests with mocked dependencies +- **Documentation**: [TESTING.md](./TESTING.md) + +## Common Workflows + +### Adding a New Feature + +1. **Create Service** (if needed) + - Add business logic in `code/services/` + - See: [Services README](../code/services/README.md) + +2. **Create Controller** (if handling events) + - Add event processor in `code/controllers/` + - See: [Controllers README](../code/controllers/README.md) + +3. **Register in DI Container** + - Update `code/core/container.setup.ts` + - See: [Core README](../code/core/README.md) + +4. **Wire in Entry Point** + - Update `code/webhook-hooks.ts` or `code/lifecycle-hooks/` + - See: [Lifecycle Hooks README](../code/lifecycle-hooks/README.md) + +5. **Add Tests** + - Create test file in `__tests__/` + - See: [TESTING.md](./TESTING.md) + +### Handling a Webhook + +``` +Stripe Webhook + โ†“ +webhook-hooks.ts (signature verification, routing) + โ†“ +Controller (orchestration) + โ†“ +Services (business logic) + โ†“ +Clients/Adapters (external APIs) +``` + +See: [WEBHOOKS.md](./WEBHOOKS.md) + +### Processing a Lifecycle Hook + +``` +Root Platform Event + โ†“ +lifecycle-hooks/index.ts (hook function) + โ†“ +Services (business logic) + โ†“ +Clients (external APIs) +``` + +See: [Lifecycle Hooks README](../code/lifecycle-hooks/README.md) + +## Key Principles + +### 1. Separation of Concerns + +- **Hooks/Controllers**: Orchestration +- **Services**: Business logic +- **Clients**: API calls +- **Adapters**: Data transformation +- **Utils**: Reusable helpers + +### 2. Dependency Injection + +All dependencies injected via constructor. + +### 3. Type Safety + +TypeScript with interfaces and type-safe configuration. + +### 4. Testability + +Unit tests with mocked dependencies, integration tests with test containers. + +### 5. Logging & Observability + +Structured JSON logs with correlation IDs and error context. + +## Getting Started + +### For New Developers + +1. Read [SETUP.md](./SETUP.md) - Set up your environment +2. Read [ARCHITECTURE.md](./ARCHITECTURE.md) - Understand the system +3. Read this document - Navigate the codebase +4. Start with a simple task - Add a log statement or test + +### For Implementation + +1. Read [CUSTOMIZING.md](./CUSTOMIZING.md) - Implementation guide +2. Read component READMEs - Detailed documentation +3. Look at existing code - Examples and patterns +4. Write tests first - TDD approach + +### For Deployment + +1. Read [ROOT_CONFIGURATION.md](./ROOT_CONFIGURATION.md) - Configuration +2. Read [DEPLOYMENT.md](./DEPLOYMENT.md) - Deployment process +3. Test in sandbox - Verify before production + +## Need Help? + +### Finding Code + +Use the directory structure above and component READMEs to navigate. + +### Understanding Patterns + +Each README includes: +- Architecture diagrams +- Usage examples +- Best practices +- Anti-patterns + +### Troubleshooting + +- Check relevant README for your component +- Review [BEST_PRACTICES.md](./BEST_PRACTICES.md) +- Look at test files for examples +- Check logs with [LOG_VIEWING.md](./LOG_VIEWING.md) + +## Related Documentation + +- **[ARCHITECTURE.md](./ARCHITECTURE.md)** - High-level system design +- **[BEST_PRACTICES.md](./BEST_PRACTICES.md)** - Coding standards +- **[TESTING.md](./TESTING.md)** - Testing strategy +- **[CUSTOMIZING.md](./CUSTOMIZING.md)** - Implementation guide + diff --git a/stripe_collection_module/docs/CONTROLLERS.md b/stripe_collection_module/docs/CONTROLLERS.md new file mode 100644 index 0000000..7d0fcd7 --- /dev/null +++ b/stripe_collection_module/docs/CONTROLLERS.md @@ -0,0 +1,201 @@ +# Controllers + +This directory contains event processors that handle webhooks and lifecycle hooks from Stripe and the Root Platform. + +## Architecture + +Controllers follow a **clean architecture pattern**: + +``` +Webhooks/Lifecycle Hooks โ†’ Controllers โ†’ Services โ†’ Clients + (entry) (orchestrate) (business) (infrastructure) +``` + +### Key Principles + +1. **Dependency Injection**: All dependencies injected via constructor +2. **Single Responsibility**: Each controller handles one event type +3. **Thin Orchestration**: Controllers coordinate services, don't contain business logic +4. **Testability**: Easy to mock dependencies for unit testing + +## Example Controllers + +### Stripe Event Processor + +```typescript +export class InvoicePaidController { + constructor( + private readonly logService: LogService, + private readonly rootService: RootService, + private readonly stripeClient: StripeClient, + ) {} + + async handle(invoice: Stripe.Invoice): Promise { + // 1. Validate input + // 2. Coordinate services + // 3. Handle errors + } +} +``` + +### Root Event Processor + +```typescript +export class PaymentCreationController { + constructor( + private readonly logService: LogService, + private readonly rootService: RootService, + private readonly stripeClient: StripeClient, + ) {} + + async handle(params: PaymentCreationParams): Promise { + // 1. Validate input + // 2. Coordinate services + // 3. Handle errors + } +} +``` + +## Directory Structure + +``` +controllers/ +โ”œโ”€โ”€ stripe-event-processors/ +โ”‚ โ”œโ”€โ”€ invoice-paid.controller.ts # Example Stripe webhook handler +โ”‚ โ”œโ”€โ”€ invoice-payment-failed.controller.ts +โ”‚ โ”œโ”€โ”€ charge-disputed.controller.ts +โ”‚ โ””โ”€โ”€ ... +โ”œโ”€โ”€ root-event-processors/ +โ”‚ โ”œโ”€โ”€ payment-creation.controller.ts # Example Root lifecycle handler +โ”‚ โ”œโ”€โ”€ policy-alteration.controller.ts +โ”‚ โ””โ”€โ”€ ... +โ””โ”€โ”€ README.md (this file) +``` + +## Creating a New Controller + +### 1. Create Controller File + +```typescript +// controllers/stripe-event-processors/my-event.controller.ts +import { LogService } from '../../services/log.service'; +import { RootService } from '../../services/root.service'; + +export class MyEventController { + constructor( + private readonly logService: LogService, + private readonly rootService: RootService, + // Add other dependencies as needed + ) {} + + async handle(payload: StripeEventPayload): Promise { + this.logService.info('Processing my event', 'MyEventController', { + id: payload.id, + }); + + // Your orchestration logic here + } +} +``` + +### 2. Register in DI Container + +```typescript +// core/container.setup.ts +container.register( + ServiceToken.MY_EVENT_CONTROLLER, + (c) => { + const logService = c.resolve(ServiceToken.LOG_SERVICE); + const rootService = c.resolve(ServiceToken.ROOT_SERVICE); + + const { MyEventController } = require( + '../controllers/stripe-event-processors/my-event.controller' + ); + return new MyEventController(logService, rootService); + }, + ServiceLifetime.TRANSIENT, +); +``` + +### 3. Wire in Webhook Handler + +```typescript +// webhook-hooks.ts +switch (parsedBody.type) { + case 'my.event.type': { + const controller = container.resolve( + ServiceToken.MY_EVENT_CONTROLLER + ); + await controller.handle(payload); + break; + } +} +``` + +## Testing Controllers + +Controllers are easy to test with dependency injection: + +```typescript +describe('InvoicePaidController', () => { + let controller: InvoicePaidController; + let mockLogService: jest.Mocked; + let mockRootService: jest.Mocked; + let mockStripeClient: jest.Mocked; + + beforeEach(() => { + mockLogService = createMockLogService(); + mockRootService = { + updatePaymentStatus: jest.fn(), + } as any; + mockStripeClient = createMockStripeClient(); + + controller = new InvoicePaidController( + mockLogService, + mockRootService, + mockStripeClient, + ); + }); + + it('should update Root payments when invoice is paid', async () => { + const invoice = createMockInvoice(); + + await controller.handle(invoice); + + expect(mockRootService.updatePaymentStatus).toHaveBeenCalledWith({ + paymentId: 'payment_123', + status: PaymentStatus.Successful, + }); + }); +}); +``` + +## Best Practices + +### Do โœ… + +- Keep controllers thin (< 100 lines) +- Inject all dependencies via constructor +- Use services for business logic +- Log important steps +- Handle errors gracefully +- Return early for validation failures +- Use TypeScript types + +### Don't โŒ + +- Put business logic in controllers +- Create dependencies with `new` inside controller +- Make direct SDK calls (use clients) +- Ignore errors +- Mix multiple responsibilities +- Use global singletons +- Skip input validation + +## Related Documentation + +- [Architecture Improvements](../../../ARCHITECTURE_IMPROVEMENTS.md) +- [Service Layer](../../services/README.md) +- [DI Container](../../core/container.ts) +- [Testing Guide](../../../docs/TESTING.md) + diff --git a/stripe_collection_module/docs/CORE.md b/stripe_collection_module/docs/CORE.md new file mode 100644 index 0000000..965f49c --- /dev/null +++ b/stripe_collection_module/docs/CORE.md @@ -0,0 +1,516 @@ +# Core + +This directory contains the core infrastructure of the application, including the Dependency Injection (DI) container system. + +## What's in Core? + +- **Dependency Injection Container** - Manages service lifecycles and dependencies +- **Service Tokens** - Type-safe service identifiers +- **Container Setup** - Service registration configuration + +## Dependency Injection Container + +The DI container provides: +- Automatic dependency resolution +- Lifecycle management (Singleton vs Transient) +- Service replacement for testing +- Loose coupling between components + +**Pattern**: + +```typescript +// Dependencies injected via constructor +class PaymentService { + constructor( + private logService: LogService, + private stripeClient: StripeClient + ) {} +} +``` + +## Container API + +### container.ts + +The core DI container implementation. + +**Key Classes**: + +#### Container + +Main container class for service management. + +```typescript +import { Container, ServiceLifetime } from './container'; + +const container = new Container(); + +// Register a service +container.register( + 'MyService', + () => new MyService(), + ServiceLifetime.SINGLETON +); + +// Resolve a service +const myService = container.resolve('MyService'); + +// Check if registered +if (container.has('MyService')) { + // Service exists +} + +// Replace for testing +container.replace('MyService', () => mockMyService); + +// Clear all +container.clear(); +``` + +**Methods**: +- `register(token, factory, lifetime)` - Register a service +- `resolve(token)` - Get service instance +- `has(token)` - Check if service is registered +- `unregister(token)` - Remove service +- `replace(token, factory, lifetime)` - Replace service (useful for testing) +- `clear()` - Remove all services +- `getRegisteredTokens()` - List all registered tokens + +#### ServiceLifetime + +Service lifecycle enum: + +```typescript +export enum ServiceLifetime { + SINGLETON = 'singleton', // One instance for entire app + TRANSIENT = 'transient', // New instance every time +} +``` + +**Usage**: +- **Singleton**: LogService, ConfigService (shared state/expensive to create) +- **Transient**: Controllers (per-request instances) + +#### ServiceToken + +Type-safe service identifiers using Symbols. + +```typescript +export const ServiceToken = { + // Core Services + LOG_SERVICE: Symbol('LogService'), + CONFIG_SERVICE: Symbol('ConfigService'), + + // Business Services + ROOT_SERVICE: Symbol('RootService'), + RENDER_SERVICE: Symbol('RenderService'), + + // Controllers + INVOICE_PAID_CONTROLLER: Symbol('InvoicePaidController'), + PAYMENT_CREATION_CONTROLLER: Symbol('PaymentCreationController'), +} as const; +``` + +Symbols provide type safety and prevent naming collisions. + +## Container Setup + +### container.setup.ts + +Configures all service registrations. + +**Key Functions**: + +#### createContainer() + +Creates and configures a new container with all services. + +```typescript +import { createContainer } from './core/container.setup'; + +const container = createContainer(); +const logService = container.resolve(ServiceToken.LOG_SERVICE); +``` + +#### getContainer() + +Gets the global singleton container instance. + +```typescript +import { getContainer } from './core/container.setup'; + +const container = getContainer(); +// Container is already configured with all services +``` + +#### setContainer() + +Sets the global container (useful for testing). + +```typescript +import { setContainer } from './core/container.setup'; + +const testContainer = createTestContainer(); +setContainer(testContainer); +``` + +#### resetContainer() + +Clears the global container. + +```typescript +import { resetContainer } from './core/container.setup'; + +afterEach(() => { + resetContainer(); // Clean state between tests +}); +``` + +## Service Registration + +### Registering a New Service + +#### 1. Add Service Token + +```typescript +// core/container.ts +export const ServiceToken = { + // ... existing tokens + MY_NEW_SERVICE: Symbol('MyNewService'), +} as const; +``` + +#### 2. Register in Container Setup + +```typescript +// core/container.setup.ts +container.register( + ServiceToken.MY_NEW_SERVICE, + (c) => { + // Resolve dependencies from container + const logService = c.resolve(ServiceToken.LOG_SERVICE); + const config = c.resolve(ServiceToken.CONFIG_SERVICE); + + // Create service with dependencies + const { MyNewService } = require('../services/my-new.service'); + return new MyNewService(logService, config); + }, + ServiceLifetime.SINGLETON // or TRANSIENT +); +``` + +#### 3. Use in Your Code + +```typescript +import { getContainer } from './core/container.setup'; +import { ServiceToken } from './core/container'; + +const container = getContainer(); +const myService = container.resolve(ServiceToken.MY_NEW_SERVICE); +await myService.doSomething(); +``` + +## Registration Patterns + +### Singleton Service + +For services with state or expensive initialization: + +```typescript +container.register( + ServiceToken.CONFIG_SERVICE, + () => new ConfigurationService(), + ServiceLifetime.SINGLETON +); + +// Same instance every time +const config1 = container.resolve(ServiceToken.CONFIG_SERVICE); +const config2 = container.resolve(ServiceToken.CONFIG_SERVICE); +// config1 === config2 โœ… +``` + +### Transient Service (Controllers) + +For stateless services or per-request instances: + +```typescript +container.register( + ServiceToken.INVOICE_PAID_CONTROLLER, + (c) => { + const logService = c.resolve(ServiceToken.LOG_SERVICE); + const rootService = c.resolve(ServiceToken.ROOT_SERVICE); + + const { InvoicePaidController } = require( + '../controllers/stripe-event-processors/invoice-paid.controller' + ); + return new InvoicePaidController(logService, rootService); + }, + ServiceLifetime.TRANSIENT +); + +// Different instance every time +const controller1 = container.resolve(ServiceToken.INVOICE_PAID_CONTROLLER); +const controller2 = container.resolve(ServiceToken.INVOICE_PAID_CONTROLLER); +// controller1 !== controller2 โœ… +``` + +### Service with Dependencies + +```typescript +container.register( + ServiceToken.ROOT_SERVICE, + (c) => { + // Resolve dependencies from container + const logService = c.resolve(ServiceToken.LOG_SERVICE); + + // Create service with injected dependencies + const { RootService } = require('../services/root.service'); + return new RootService(logService); + }, + ServiceLifetime.SINGLETON +); +``` + +### Lazy Loading Services + +```typescript +container.register( + ServiceToken.MY_SERVICE, + (c) => { + // Use require() for lazy loading + // Service file only loaded when first resolved + const { MyService } = require('../services/my.service'); + return new MyService(/* dependencies */); + }, + ServiceLifetime.SINGLETON +); +``` + +## Testing with DI Container + +### Option 1: Replace Services + +```typescript +import { getContainer, setContainer } from '../core/container.setup'; +import { ServiceToken } from '../core/container'; + +describe('MyController', () => { + let container: Container; + let mockLogService: jest.Mocked; + + beforeEach(() => { + // Get fresh container + container = createContainer(); + + // Replace service with mock + mockLogService = { + info: jest.fn(), + error: jest.fn(), + } as any; + + container.replace( + ServiceToken.LOG_SERVICE, + () => mockLogService, + ServiceLifetime.SINGLETON + ); + + // Set as global container + setContainer(container); + }); + + afterEach(() => { + resetContainer(); + }); + + it('should log operations', () => { + const controller = container.resolve(ServiceToken.MY_CONTROLLER); + controller.handle(); + + expect(mockLogService.info).toHaveBeenCalled(); + }); +}); +``` + +### Option 2: Create Test Container + +```typescript +function createTestContainer(): Container { + const container = new Container(); + + // Register mocks + container.register( + ServiceToken.LOG_SERVICE, + () => createMockLogService(), + ServiceLifetime.SINGLETON + ); + + container.register( + ServiceToken.CONFIG_SERVICE, + () => createMockConfig(), + ServiceLifetime.SINGLETON + ); + + return container; +} + +describe('Integration Tests', () => { + beforeEach(() => { + const testContainer = createTestContainer(); + setContainer(testContainer); + }); + + // Tests use mocked services +}); +``` + +### Option 3: Direct Injection (Unit Tests) + +```typescript +describe('MyService', () => { + let service: MyService; + let mockLogService: jest.Mocked; + + beforeEach(() => { + mockLogService = { + info: jest.fn(), + error: jest.fn(), + } as any; + + // Don't use container - inject directly + service = new MyService(mockLogService); + }); + + it('should do something', () => { + service.doSomething(); + expect(mockLogService.info).toHaveBeenCalled(); + }); +}); +``` + +## Best Practices + +### Do โœ… + +- Use ServiceTokens (Symbols) for type safety +- Register services at startup +- Inject dependencies via constructor +- Use SINGLETON for stateful services +- Use TRANSIENT for controllers +- Test with mocked services +- Document dependencies + +### Don't โŒ + +- Create services with `new` in application code +- Use string literals as service tokens +- Register services dynamically at runtime +- Make services depend on the container +- Skip dependency injection for testing +- Create circular dependencies + +## Common Patterns + +### Factory Pattern + +```typescript +container.register( + ServiceToken.STRIPE_CLIENT, + (c) => { + const config = c.resolve(ServiceToken.CONFIG_SERVICE); + + // Factory creates client with config + return new StripeClient(config.get('stripeSecretKey')); + }, + ServiceLifetime.SINGLETON +); +``` + +### Conditional Registration + +```typescript +container.register( + ServiceToken.CACHE_SERVICE, + (c) => { + const config = c.resolve(ServiceToken.CONFIG_SERVICE); + + // Return different implementations based on config + if (config.isProduction()) { + return new RedisCacheService(); + } else { + return new InMemoryCacheService(); + } + }, + ServiceLifetime.SINGLETON +); +``` + +### Decorator Pattern + +```typescript +container.register( + ServiceToken.PAYMENT_SERVICE, + (c) => { + const baseService = new PaymentService(); + const logService = c.resolve(ServiceToken.LOG_SERVICE); + + // Wrap service with logging decorator + return new LoggingPaymentService(baseService, logService); + }, + ServiceLifetime.SINGLETON +); +``` + +## Troubleshooting + +### "Service not registered" Error + +```typescript +// Problem: Token not registered +const service = container.resolve(ServiceToken.MY_SERVICE); +// Error: Service not registered: Symbol(MyService) + +// Solution: Register the service in container.setup.ts +container.register(ServiceToken.MY_SERVICE, ...); +``` + +### Circular Dependencies + +```typescript +// Problem: A depends on B, B depends on A +class ServiceA { + constructor(private serviceB: ServiceB) {} +} + +class ServiceB { + constructor(private serviceA: ServiceA) {} +} + +// Solution: Refactor to remove circular dependency +// - Extract shared logic to a third service +// - Use events/callbacks instead of direct dependencies +// - Rethink your design +``` + +### Singleton Not Working + +```typescript +// Problem: Getting different instances +const service1 = container.resolve(ServiceToken.MY_SERVICE); +const service2 = container.resolve(ServiceToken.MY_SERVICE); +// service1 !== service2 โŒ + +// Solution: Check lifecycle is SINGLETON +container.register( + ServiceToken.MY_SERVICE, + factory, + ServiceLifetime.SINGLETON // Make sure this is set! +); +``` + +## Related Documentation + +- [Services Documentation](../services/README.md) - Creating services for DI +- [Controllers Documentation](../controllers/README.md) - Using DI in controllers +- [Testing Guide](../../docs/TESTING.md) - Testing with DI +- [Best Practices](../../docs/BEST_PRACTICES.md) - DI patterns + diff --git a/stripe_collection_module/docs/CUSTOMIZING.md b/stripe_collection_module/docs/CUSTOMIZING.md new file mode 100644 index 0000000..e2651dd --- /dev/null +++ b/stripe_collection_module/docs/CUSTOMIZING.md @@ -0,0 +1,635 @@ +# Stripe Implementation Guide + +This guide explains how to complete the Stripe integration in this collection module template. + +## Overview + +The template provides a complete structure with stub implementations. You'll need to implement the actual Stripe integration by completing the service methods, configuring the Stripe client, and implementing webhook handlers. + +--- + +## Implementation Strategy + +### 1. Understand the Template Structure + +The template follows a clean architecture: + +``` +Stripe SDK Client โ†’ Stripe Service โ†’ Controllers โ†’ Root Platform Integration + โ†“ + Root API +``` + +### What's Already Done + +โœ… **Architecture**: +- Dependency injection container +- Configuration management +- Logging infrastructure +- Error handling patterns + +โœ… **Structure**: +- Service stubs with method signatures +- Event processor templates +- Test infrastructure +- Documentation + +### What You Need to Implement + +โŒ **Stripe Integration**: +- Complete service stub methods +- Configure Stripe SDK client +- Implement webhook handlers +- Add data transformation logic + +--- + +## Step-by-Step Implementation + +### Step 1: Configure Stripe Client + +**File**: `code/clients/stripe-client.ts` + +Complete the Stripe client initialization: + +```typescript +import Stripe from 'stripe'; +import { getConfigService } from '../services/config-instance'; + +export class StripeClient { + private stripe: Stripe; + + constructor(apiKey?: string) { + const config = getConfigService(); + const key = apiKey || config.get('stripeSecretKey'); + + // Initialize Stripe SDK + this.stripe = new Stripe(key, { + apiVersion: '2023-10-16', // Use latest stable version + typescript: true, + }); + } + + /** + * Get the Stripe SDK instance + */ + public getClient(): Stripe { + return this.stripe; + } + + /** + * Verify webhook signature + */ + public constructWebhookEvent( + payload: string | Buffer, + signature: string, + secret: string + ): Stripe.Event { + return this.stripe.webhooks.constructEvent(payload, signature, secret); + } +} +``` + +### Step 2: Implement Stripe Service Methods + +**File**: `code/services/stripe.service.ts` + +Complete the stub methods with actual Stripe API calls: + +```typescript +/** + * Create a Stripe customer + */ +async createCustomer(params: { + email?: string; + name?: string; + phone?: string; + paymentMethod?: string; + metadata?: Record; +}): Promise { + this.logService.info('Creating Stripe customer', 'CustomerService', params); + + try { + const customer = await this.stripeClient.getClient().customers.create({ + email: params.email, + name: params.name, + phone: params.phone, + payment_method: params.paymentMethod, + metadata: params.metadata, + }); + + this.logService.info('Customer created successfully', 'CustomerService', { + customerId: customer.id, + }); + + return this.mapStripeCustomer(customer); + } catch (error) { + this.logService.error( + `Failed to create customer: ${error.message}`, + 'CustomerService', + { error } + ); + throw error; + } +} + +/** + * Map Stripe customer to domain model + */ +private mapStripeCustomer(stripeCustomer: Stripe.Customer): StripeCustomer { + return { + id: stripeCustomer.id, + email: stripeCustomer.email, + name: stripeCustomer.name, + phone: stripeCustomer.phone, + defaultPaymentMethodId: stripeCustomer.invoice_settings?.default_payment_method as string, + created: new Date(stripeCustomer.created * 1000), + metadata: stripeCustomer.metadata, + }; +} +``` + +**Complete these methods**: +- `createCustomer()` - Create Stripe customer +- `getCustomer()` - Retrieve customer by ID +- `updateCustomer()` - Update customer details +- `createSubscription()` - Create recurring subscription +- `getSubscription()` - Get subscription details +- `updateSubscription()` - Update subscription +- `cancelSubscription()` - Cancel subscription +- `createPayment()` - Create one-time payment +- `refundPayment()` - Process refund +- `attachPaymentMethod()` - Attach payment method to customer + +### Step 3: Implement Webhook Event Processors + +**File**: `code/controllers/stripe-event-processors/processInvoicePaidEventController.ts` + +Implement handlers for each Stripe webhook event: + +```typescript +import { getLogService } from '../../services/log-instance'; +import { RootService } from '../../services/root.service'; +import Stripe from 'stripe'; + +export async function processInvoicePaid(event: Stripe.Event): Promise { + const logService = getLogService(); + const invoice = event.data.object as Stripe.Invoice; + + logService.info('Processing invoice.paid event', 'InvoicePaidController', { + invoiceId: invoice.id, + customerId: invoice.customer, + amount: invoice.amount_paid, + }); + + try { + // 1. Get Root policy ID from invoice metadata + const rootPolicyId = invoice.metadata?.rootPolicyId; + + if (!rootPolicyId) { + logService.warn('No Root policy ID in invoice metadata', 'InvoicePaidController'); + return; + } + + // 2. Create payment record in Root + const rootService = new RootService(/* inject dependencies */); + await rootService.createPayment({ + policyId: rootPolicyId, + amount: invoice.amount_paid / 100, // Convert cents to dollars + currency: invoice.currency, + status: 'succeeded', + paymentDate: new Date(invoice.status_transitions.paid_at! * 1000), + externalId: invoice.payment_intent as string, + metadata: { + invoiceId: invoice.id, + provider: 'stripe', + }, + }); + + logService.info('Invoice payment processed successfully', 'InvoicePaidController'); + } catch (error) { + logService.error( + `Failed to process invoice payment: ${error.message}`, + 'InvoicePaidController', + { invoiceId: invoice.id, error } + ); + throw error; + } +} +``` + +**Implement these event processors**: +- `processInvoicePaidEventController.ts` - Payment succeeded +- `processInvoicePaymentFailedEventController.ts` - Payment failed +- `processInvoiceCreatedEventController.ts` - Invoice created +- `processPaymentIntentSucceededEventController.ts` - Payment intent succeeded +- `processPaymentIntentFailedEventController.ts` - Payment intent failed +- `processChargeDisputedEventController.ts` - Dispute created +- `processInvoiceChargeRefundedEventController.ts` - Refund processed +- `processSubscriptionScheduleUpdatedEventController.ts` - Subscription changed + +### Step 4: Wire Webhook Handler + +**File**: `code/webhook-hooks.ts` + +Connect webhook events to processors: + +```typescript +import { getLogService } from './services/log-instance'; +import { getConfigService } from './services/config-instance'; +import { StripeClient } from './clients/stripe-client'; +import Stripe from 'stripe'; + +// Import event processors +import { processInvoicePaid } from './controllers/stripe-event-processors/processInvoicePaidEventController'; +import { processInvoicePaymentFailed } from './controllers/stripe-event-processors/processInvoicePaymentFailedEventController'; +// ... import other processors + +export async function processStripeWebhook(event: any): Promise { + const logService = getLogService(); + const config = getConfigService(); + + try { + // 1. Verify webhook signature + const stripeClient = new StripeClient(); + const signature = event.headers['stripe-signature']; + const webhookSecret = config.get('stripeWebhookSigningSecret'); + + const stripeEvent = stripeClient.constructWebhookEvent( + event.body, + signature, + webhookSecret + ); + + logService.info(`Processing Stripe webhook: ${stripeEvent.type}`, 'WebhookHandler', { + eventId: stripeEvent.id, + }); + + // 2. Route to appropriate processor + switch (stripeEvent.type) { + case 'invoice.paid': + await processInvoicePaid(stripeEvent); + break; + + case 'invoice.payment_failed': + await processInvoicePaymentFailed(stripeEvent); + break; + + case 'payment_intent.succeeded': + await processPaymentIntentSucceeded(stripeEvent); + break; + + case 'payment_intent.payment_failed': + await processPaymentIntentFailed(stripeEvent); + break; + + case 'charge.dispute.created': + await processChargeDisputed(stripeEvent); + break; + + default: + logService.info(`Unhandled event type: ${stripeEvent.type}`, 'WebhookHandler'); + } + + return { + statusCode: 200, + body: JSON.stringify({ received: true }), + }; + } catch (error) { + logService.error(`Webhook processing failed: ${error.message}`, 'WebhookHandler'); + return { + statusCode: 400, + body: JSON.stringify({ error: error.message }), + }; + } +} +``` + +### Step 5: Implement Lifecycle Hooks + +**File**: `code/lifecycle-hooks/index.ts` + +Connect Root Platform lifecycle hooks to Stripe operations: + +```typescript +import { getLogService } from '../services/log-instance'; +import { StripeService } from '../services/stripe.service'; +import { RootService } from '../services/root.service'; + +/** + * Called when a payment method is assigned to a policy + */ +export async function afterPolicyPaymentMethodAssigned(event: any): Promise { + const logService = getLogService(); + + logService.info('Processing payment method assignment', 'LifecycleHooks', { + policyId: event.policyId, + paymentMethodId: event.paymentMethodId, + }); + + try { + // 1. Get policy details from Root + const rootService = new RootService(/* dependencies */); + const policy = await rootService.getPolicy(event.policyId); + const policyholder = await rootService.getPolicyholder(policy.policyholderId); + + // 2. Create or get Stripe customer + const stripeService = new StripeService(/* dependencies */); + let customerId = policy.metadata?.stripeCustomerId; + + if (!customerId) { + const customer = await stripeService.createCustomer({ + email: policyholder.email, + name: `${policyholder.firstName} ${policyholder.lastName}`, + phone: policyholder.phone, + metadata: { + rootPolicyId: policy.id, + rootPolicyholderId: policyholder.id, + }, + }); + customerId = customer.id; + + // Update policy with Stripe customer ID + await rootService.updatePolicy(policy.id, { + metadata: { + ...policy.metadata, + stripeCustomerId: customerId, + }, + }); + } + + // 3. Create Stripe subscription + const subscription = await stripeService.createSubscription({ + customerId, + priceId: policy.metadata?.stripePriceId, + amount: policy.monthlyPremium, + currency: policy.currency, + metadata: { + rootPolicyId: policy.id, + }, + }); + + // 4. Update policy with subscription ID + await rootService.updatePolicy(policy.id, { + metadata: { + ...policy.metadata, + stripeSubscriptionId: subscription.id, + }, + }); + + logService.info('Payment method assignment completed', 'LifecycleHooks'); + } catch (error) { + logService.error(`Failed to process payment method assignment: ${error.message}`, 'LifecycleHooks'); + throw error; + } +} +``` + +### Step 6: Add Data Transformation + +**File**: `code/adapters/stripe-to-root-adapter.ts` + +Implement adapters to transform Stripe data to Root format: + +```typescript +import Stripe from 'stripe'; +import { RootPayment, RootPaymentStatus } from '../interfaces/root-payment'; + +/** + * Map Stripe payment intent to Root payment + */ +export function mapStripePaymentIntentToRootPayment( + paymentIntent: Stripe.PaymentIntent +): Partial { + return { + externalId: paymentIntent.id, + amount: paymentIntent.amount / 100, // Convert cents to dollars + currency: paymentIntent.currency.toUpperCase(), + status: mapStripePaymentStatus(paymentIntent.status), + paymentDate: new Date(paymentIntent.created * 1000), + metadata: { + provider: 'stripe', + paymentMethod: paymentIntent.payment_method, + receiptUrl: paymentIntent.charges?.data[0]?.receipt_url, + }, + }; +} + +/** + * Map Stripe payment status to Root status + */ +function mapStripePaymentStatus(stripeStatus: string): RootPaymentStatus { + const statusMap: Record = { + 'succeeded': 'succeeded', + 'processing': 'pending', + 'requires_payment_method': 'pending', + 'requires_confirmation': 'pending', + 'requires_action': 'pending', + 'canceled': 'canceled', + 'failed': 'failed', + }; + + return statusMap[stripeStatus] || 'pending'; +} +``` + +### Step 7: Add Input Validation + +**File**: `code/validation/stripe-schemas.ts` + +Add Joi validation schemas for Stripe data: + +```typescript +import Joi from 'joi'; + +export const createCustomerSchema = Joi.object({ + email: Joi.string().email().required(), + name: Joi.string().min(1).max(200).required(), + phone: Joi.string().optional(), + metadata: Joi.object().optional(), +}); + +export const createSubscriptionSchema = Joi.object({ + customerId: Joi.string().required(), + priceId: Joi.string().optional(), + amount: Joi.number().positive().required(), + currency: Joi.string().length(3).required(), + interval: Joi.string().valid('day', 'week', 'month', 'year').required(), + metadata: Joi.object().optional(), +}); + +/** + * Validate and return typed data + */ +export async function validateCreateCustomer(data: unknown) { + return await createCustomerSchema.validateAsync(data); +} +``` + +--- + +## Testing Your Implementation + +### Unit Tests + +Test each service method in isolation: + +```typescript +describe('CustomerService', () => { + let customerService: CustomerService; + let mockStripeClient: jest.Mocked; + let mockLogService: jest.Mocked; + + beforeEach(() => { + mockStripeClient = createMockStripeClient(); + mockLogService = createMockLogService(); + customerService = new CustomerService(mockLogService, mockStripeClient); + }); + + describe('createCustomer', () => { + it('should create a Stripe customer', async () => { + const params = { + email: 'test@example.com', + name: 'Test User', + }; + + mockStripeClient.getClient().customers.create.mockResolvedValue({ + id: 'cus_123', + email: params.email, + name: params.name, + } as any); + + const customer = await stripeService.createCustomer(params); + + expect(customer.id).toBe('cus_123'); + expect(mockStripeClient.getClient().customers.create).toHaveBeenCalledWith({ + email: params.email, + name: params.name, + }); + }); + }); +}); +``` + +### Integration Tests + +Test complete workflows: + +```typescript +describe('Stripe Integration', () => { + it('should create customer and subscription', async () => { + // 1. Create customer + const customer = await stripeService.createCustomer({ + email: 'test@example.com', + name: 'Test User', + }); + + expect(customer.id).toBeDefined(); + + // 2. Create subscription + const subscription = await stripeService.createSubscription({ + customerId: customer.id, + amount: 1000, + currency: 'usd', + interval: 'month', + }); + + expect(subscription.id).toBeDefined(); + expect(subscription.customerId).toBe(customer.id); + }); +}); +``` + +--- + +## Common Patterns + +### Retry Logic for API Calls + +```typescript +import { retryWithBackoff } from '../utils/retry'; + +async createCustomer(params: any) { + return retryWithBackoff( + () => this.stripeClient.getClient().customers.create(params), + { + maxRetries: 3, + initialDelay: 1000, + shouldRetry: (error) => error.statusCode >= 500 || error.code === 'ETIMEDOUT', + } + ); +} +``` + +### Idempotency for Webhooks + +```typescript +const processedEvents = new Set(); + +export async function processInvoicePaid(event: Stripe.Event) { + // Check if already processed + if (processedEvents.has(event.id)) { + logService.info('Event already processed', 'InvoicePaidController'); + return; + } + + try { + await doActualProcessing(event); + processedEvents.add(event.id); + } catch (error) { + // Don't mark as processed - allow retry + throw error; + } +} +``` + +### Error Handling + +```typescript +try { + await stripeService.createCustomer(params); +} catch (error) { + if (error.type === 'StripeCardError') { + // Customer-facing error - don't retry + throw new ValidationError('Card declined', { code: error.code }); + } else if (error.statusCode >= 500) { + // Server error - retry + throw new ServerError('Stripe API error', { error: error.message }); + } else { + // Unknown error + throw error; + } +} +``` + +--- + +## Implementation Checklist + +- [ ] Configure Stripe client with API key +- [ ] Create service layers as needed for your business logic +- [ ] Create webhook event controllers for all events you need to handle +- [ ] Wire webhook handler with event routing +- [ ] Implement lifecycle hooks for your specific use case +- [ ] Add data transformation adapters +- [ ] Add input validation schemas +- [ ] Write unit tests for all services +- [ ] Write integration tests for workflows +- [ ] Test with Stripe test mode +- [ ] Configure webhook URL in Stripe Dashboard +- [ ] Test webhook delivery +- [ ] Deploy and monitor + +--- + +## Resources + +- **Stripe API Documentation**: https://stripe.com/docs/api +- **Stripe Webhooks Guide**: https://stripe.com/docs/webhooks +- **Stripe Testing**: https://stripe.com/docs/testing +- **Stripe CLI**: https://stripe.com/docs/stripe-cli +- **Stripe Node.js Library**: https://github.com/stripe/stripe-node + +--- + +**Ready to build! ๐Ÿš€** diff --git a/stripe_collection_module/docs/DEPLOYMENT.md b/stripe_collection_module/docs/DEPLOYMENT.md new file mode 100644 index 0000000..577d055 --- /dev/null +++ b/stripe_collection_module/docs/DEPLOYMENT.md @@ -0,0 +1,573 @@ +# Collection Module Deployment Guide + +This guide covers deploying your collection module to the Root Platform. + +## Overview + +Collection modules are deployed through the Root Platform API. Root Platform handles all infrastructure, hosting, and execution automatically. You simply: + +1. Develop and test your collection module locally +2. Commit your changes to version control +3. Create a release/tag +4. Publish the version via the Root Platform API + +Root Platform takes care of: +- Infrastructure provisioning +- Code deployment +- Environment configuration +- Scaling and availability +- Monitoring and logging + +--- + +## Prerequisites + +### Required + +- Completed and tested collection module +- Root Platform account with organization access +- Root Platform API key +- Git repository (recommended) + +### You'll Need + +| Item | Description | Where to Find | +|------|-------------|---------------| +| API Key | Root Platform API key | Root Platform โ†’ Settings โ†’ API Keys | +| Organization ID | Your Root organization ID | Root Platform โ†’ Organization Settings | +| Collection Module Key | Your module's unique key | Defined in `.root-config.json` | +| Host URL | Root Platform API URL | `https://api.rootplatform.com` or regional variant | + +--- + +## Deployment Process + +### Step 1: Prepare Your Code + +Ensure your collection module is ready for deployment: + +```bash +cd stripe_collection_module + +# Run validation +npm run validate + +# Run all tests +npm test + +# Check code quality +npm run lint + +# Build (optional - Root Platform may build for you) +npm run build +``` + +**Checklist:** +- [ ] All tests passing +- [ ] No linting errors +- [ ] Configuration validated +- [ ] Code committed to git +- [ ] Sensitive data not committed (env.ts is gitignored) + +### Step 2: Create a Release + +Create a version tag in your git repository: + +```bash +# Tag your release +git tag -a v1.0.0 -m "Release version 1.0.0" +git push origin v1.0.0 + +# Or create a GitHub/GitLab release +``` + +**Version Naming:** +- Use semantic versioning: `v1.0.0`, `v1.1.0`, `v2.0.0` +- Include release notes describing changes +- Tag stable, tested versions only + +### Step 3: Publish to Root Platform + +Publish your collection module version via the Root Platform API: + +```bash +curl -X POST \ + -H "Authorization: Basic {{api_key}}" \ + "{{host}}/v1/apps/{{org_id}}/insurance/collection-modules/{{cm_key}}/publish?bumpSandbox=true" +``` + +**Parameters:** + +| Parameter | Description | Example | +|-----------|-------------|---------| +| `{{api_key}}` | Your Root Platform API key (as username in Basic Auth) | `sandbox_abc123...` | +| `{{host}}` | Root Platform API URL | `https://api.rootplatform.com` | +| `{{org_id}}` | Your organization ID | `00000000-0000-0000-0000-000000000001` | +| `{{cm_key}}` | Collection module key | `cm_stripe` | +| `bumpSandbox` | Publish to sandbox (true) or production (false) | `true` or `false` | + +**Example:** + +```bash +curl -X POST \ + -H "Authorization: Basic sandbox_sk_abc123xyz..." \ + "https://api.rootplatform.com/v1/apps/00000000-0000-0000-0000-000000000001/insurance/collection-modules/cm_stripe/publish?bumpSandbox=true" +``` + +**Response:** + +```json +{ + "success": true, + "version": "1.0.0", + "status": "published", + "environment": "sandbox" +} +``` + +### Step 4: Verify Deployment + +After publishing, verify your deployment: + +1. **Check Root Platform Dashboard** + - Navigate to Collection Modules + - Verify version is shown as deployed + - Check deployment status + +2. **Test Functionality** + - Create a test policy + - Assign a payment method + - Verify webhooks are received + - Check logs for any errors + +3. **Monitor Logs** + - Access logs via Root Platform dashboard + - Look for any deployment errors + - Verify lifecycle hooks are executing + +--- + +## Environment-Specific Deployment + +### Sandbox Deployment + +Deploy to sandbox for testing: + +```bash +npm run deploy:sandbox +``` + +```bash +curl -X POST \ + -H "Authorization: Basic {{sandbox_api_key}}" \ + "{{host}}/v1/apps/{{org_id}}/insurance/collection-modules/{{cm_key}}/publish?bumpSandbox=true" +``` + +**Use sandbox for:** +- Testing new features +- Integration testing +- QA validation +- Demo environments + +### Production Deployment + +Deploy to production after thorough testing: + +```bash +npm run deploy:production +``` + +```bash +curl -X POST \ + -H "Authorization: Basic {{production_api_key}}" \ + "{{host}}/v1/apps/{{org_id}}/insurance/collection-modules/{{cm_key}}/publish?bumpSandbox=false" +``` + +**Production checklist:** +- [ ] Tested in sandbox +- [ ] Code reviewed +- [ ] Documentation updated +- [ ] Stakeholders notified +- [ ] Rollback plan prepared +- [ ] Monitoring configured + +--- + +## Configuration Management + +### Environment Variables + +Collection modules get configuration from: + +1. **Root Platform Settings** + - Configured in the Root Platform dashboard + - Managed per environment (sandbox/production) + - Secure storage of API keys and secrets + +2. **Collection Module Metadata** + - Defined in your code + - Available via Root Platform context + +### Setting Environment Variables + +Configure in Root Platform dashboard: +1. Navigate to Collection Modules โ†’ Your Module +2. Go to Settings/Configuration +3. Add environment variables: + - Provider API keys + - Webhook secrets + - Feature flags + - Timeouts + +**Security Note:** Never commit sensitive values. Use Root Platform's secure configuration. + +--- + +## Versioning Strategy + +### Semantic Versioning + +Follow semantic versioning (semver): + +**Format:** `MAJOR.MINOR.PATCH` + +- **MAJOR** - Breaking changes (e.g., `1.0.0` โ†’ `2.0.0`) +- **MINOR** - New features, backward compatible (e.g., `1.0.0` โ†’ `1.1.0`) +- **PATCH** - Bug fixes, backward compatible (e.g., `1.0.0` โ†’ `1.0.1`) + +**Examples:** + +```bash +# Patch release (bug fix) +git tag -a v1.0.1 -m "Fix payment processing bug" + +# Minor release (new feature) +git tag -a v1.1.0 -m "Add refund support" + +# Major release (breaking change) +git tag -a v2.0.0 -m "New API structure" +``` + +### Version History + +Maintain clear version history: + +**CHANGELOG.md:** +```markdown +# Changelog + +## [1.1.0] - 2025-11-06 +### Added +- Refund processing support +- Enhanced error messages + +### Fixed +- Payment timeout handling + +## [1.0.0] - 2025-11-01 +### Added +- Initial release +- Payment processing +- Webhook handling +``` + +--- + +## Continuous Deployment + +### GitHub Actions Example + +Automate deployment with CI/CD: + +**.github/workflows/deploy.yml:** + +```yaml +name: Deploy Collection Module + +on: + push: + tags: + - 'v*' + +jobs: + deploy: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: '18' + + - name: Install dependencies + working-directory: ./stripe_collection_module + run: npm ci + + - name: Run tests + working-directory: ./stripe_collection_module + run: npm test + + - name: Validate configuration + working-directory: ./stripe_collection_module + run: npm run validate + + - name: Publish to Root Platform (Sandbox) + if: contains(github.ref, '-beta') + run: | + curl -X POST \ + -H "Authorization: Basic ${{ secrets.ROOT_SANDBOX_API_KEY }}" \ + "${{ secrets.ROOT_HOST }}/v1/apps/${{ secrets.ROOT_ORG_ID }}/insurance/collection-modules/${{ secrets.CM_KEY }}/publish?bumpSandbox=true" + + - name: Publish to Root Platform (Production) + if: "!contains(github.ref, '-beta')" + run: | + curl -X POST \ + -H "Authorization: Basic ${{ secrets.ROOT_PRODUCTION_API_KEY }}" \ + "${{ secrets.ROOT_HOST }}/v1/apps/${{ secrets.ROOT_ORG_ID }}/insurance/collection-modules/${{ secrets.CM_KEY }}/publish?bumpSandbox=false" +``` + +**GitHub Secrets Required:** +- `ROOT_SANDBOX_API_KEY` +- `ROOT_PRODUCTION_API_KEY` +- `ROOT_HOST` +- `ROOT_ORG_ID` +- `CM_KEY` + +--- + +## Rollback Strategy + +### Rolling Back a Deployment + +If issues occur, rollback to previous version: + +1. **Identify Previous Version** + ```bash + git tag -l + # v1.0.0 + # v1.0.1 + # v1.1.0 (current, broken) + ``` + +2. **Publish Previous Version** + ```bash + git checkout v1.0.1 + + curl -X POST \ + -H "Authorization: Basic {{api_key}}" \ + "{{host}}/v1/apps/{{org_id}}/insurance/collection-modules/{{cm_key}}/publish?bumpSandbox=false" + ``` + +3. **Verify Rollback** + - Check Root Platform shows correct version + - Test functionality + - Monitor logs + +### Rollback Checklist + +- [ ] Identify stable version to rollback to +- [ ] Notify stakeholders +- [ ] Execute rollback +- [ ] Verify functionality +- [ ] Document incident +- [ ] Fix issue in new version + +--- + +## Monitoring After Deployment + +### CloudWatch Logs + +Root Platform automatically logs to CloudWatch: + +**Access Logs:** +1. Root Platform Dashboard โ†’ Collection Modules โ†’ Your Module +2. Click "Logs" or "Monitoring" +3. View real-time logs +4. Filter by severity, time range, or search terms + +**What to Monitor:** +- Error rates +- Response times +- Webhook processing +- API call success rates + +### Alerting + +Set up alerts for: +- High error rates +- Slow response times +- Failed webhook deliveries +- Missing configuration + +### Health Checks + +Monitor collection module health: +- Webhook delivery status +- API connectivity +- Processing latency +- Error patterns + +--- + +## Troubleshooting + +### Deployment Failed + +**Symptoms:** +- API returns error +- Version not shown in dashboard +- Old version still running + +**Solutions:** +1. Check API response for error details +2. Verify API key has correct permissions +3. Ensure organization ID and module key are correct +4. Check collection module code for errors +5. Review Root Platform status page + +### Module Not Executing + +**Symptoms:** +- Lifecycle hooks not called +- Webhooks not received +- No logs generated + +**Solutions:** +1. Verify module is published and active +2. Check webhook URL is correct +3. Verify payment provider webhook configuration +4. Review logs for initialization errors +5. Test with sample events + +### Configuration Issues + +**Symptoms:** +- Missing environment variables +- API keys not working +- Wrong configuration values + +**Solutions:** +1. Check Root Platform dashboard configuration +2. Verify environment variables are set correctly +3. Check for typos in API keys +4. Ensure correct environment (sandbox vs production) + +--- + +## Best Practices + +### Pre-Deployment + +- โœ… Run full test suite (`npm test`) +- โœ… Validate configuration (`npm run validate`) +- โœ… Check code quality (`npm run lint`) +- โœ… Review code changes +- โœ… Update version number +- โœ… Update CHANGELOG.md +- โœ… Tag release in git + +### During Deployment + +- โœ… Deploy to sandbox first +- โœ… Test thoroughly in sandbox +- โœ… Monitor logs during deployment +- โœ… Verify webhook connectivity +- โœ… Have rollback plan ready + +### Post-Deployment + +- โœ… Verify version in dashboard +- โœ… Test critical workflows +- โœ… Monitor error rates +- โœ… Check log outputs +- โœ… Update documentation +- โœ… Notify team + +### Security + +- โœ… Never commit API keys or secrets +- โœ… Use Root Platform configuration for sensitive values +- โœ… Rotate API keys regularly +- โœ… Use different keys for sandbox/production +- โœ… Review permissions before deployment + +--- + +## Deployment Checklist + +### Pre-Deployment +- [ ] All tests passing +- [ ] Code reviewed +- [ ] Configuration validated +- [ ] Documentation updated +- [ ] Version tagged in git +- [ ] CHANGELOG updated + +### Sandbox Deployment +- [ ] Published to sandbox +- [ ] Tested lifecycle hooks +- [ ] Verified webhook processing +- [ ] Checked logs for errors +- [ ] Tested edge cases + +### Production Deployment +- [ ] Sandbox testing complete +- [ ] Stakeholders notified +- [ ] Published to production +- [ ] Verified in dashboard +- [ ] Monitored initial traffic +- [ ] Documented deployment + +### Post-Deployment +- [ ] Functionality verified +- [ ] Logs reviewed +- [ ] Metrics monitored +- [ ] Team notified +- [ ] Documentation updated + +--- + +## Support + +### Documentation + +- **Setup**: [SETUP.md](./SETUP.md) +- **Customization**: [CUSTOMIZING.md](./CUSTOMIZING.md) +- **Webhooks**: [WEBHOOKS.md](./WEBHOOKS.md) +- **Architecture**: [ARCHITECTURE.md](./ARCHITECTURE.md) +- **Testing**: [TESTING.md](./TESTING.md) + +### Getting Help + +1. Check Root Platform documentation +2. Review CloudWatch Logs +3. Check Root Platform status page +4. Contact Root Platform support + +### Common Issues + +| Issue | Solution | +|-------|----------| +| API key invalid | Verify key is correct for environment | +| Module not found | Check organization ID and module key | +| Permission denied | Verify API key has deployment permissions | +| Version conflict | Ensure version number is incremented | + +--- + +## Next Steps + +After deployment: + +1. **Monitor Performance** - Watch logs and metrics +2. **Test End-to-End** - Process real transactions +3. **Document Changes** - Update runbooks and documentation +4. **Plan Next Release** - Gather feedback and plan improvements +5. **Set Up Webhooks** - See [WEBHOOKS.md](./WEBHOOKS.md) + +--- + +**Ready to deploy! ๐Ÿš€** diff --git a/stripe_collection_module/docs/LIFECYCLE_HOOKS.md b/stripe_collection_module/docs/LIFECYCLE_HOOKS.md new file mode 100644 index 0000000..32da5a7 --- /dev/null +++ b/stripe_collection_module/docs/LIFECYCLE_HOOKS.md @@ -0,0 +1,515 @@ +# Lifecycle Hooks + +This directory contains lifecycle hook functions that are called by the Root Platform at various points in the policy and payment lifecycle. + +Lifecycle hooks are callback functions invoked by Root Platform when events occur: +- Policy issued +- Payment method assigned +- Payment created +- Policy updated +- Policy cancelled +- And more... + +## Architecture + +``` +Root Platform Event โ†’ Lifecycle Hook โ†’ Your Logic โ†’ External Systems + (trigger) (callback) (implement) (Stripe, etc.) +``` + +## Available Hooks + +### Payment Method Lifecycle + +#### renderCreatePaymentMethod() + +Renders HTML form for creating a payment method. + +**Trigger**: User clicks "Add Payment Method" in Root dashboard + +**Returns**: HTML string with Stripe Elements form + +```typescript +export async function renderCreatePaymentMethod(): Promise { + // 1. Create Stripe setup intent + const setupIntent = await stripeClient.stripeSDK.setupIntents.create({}); + + // 2. Render form with Stripe Elements + return renderService.renderCreatePaymentMethod({ + stripePublishableKey: config.get('stripePublishableKey'), + setupIntentClientSecret: setupIntent.client_secret, + }); +} +``` + +#### createPaymentMethod() + +Creates payment method data structure after form submission. + +**Trigger**: After user submits payment method form + +**Parameters**: `{ data: { setupIntent } }` + +**Returns**: `{ module: PaymentMethodData }` + +```typescript +export function createPaymentMethod({ data }): { module: any } { + return { + module: { + id: data.setupIntent.id, + usage: data.setupIntent.usage, + payment_method: data.setupIntent.payment_method, + status: data.setupIntent.status, + }, + }; +} +``` + +#### renderViewPaymentMethod() + +Renders detailed view of payment method. + +**Trigger**: Viewing payment method details in dashboard + +```typescript +export function renderViewPaymentMethod(params): string { + return renderService.renderViewPaymentMethod({ + payment_method: params.payment_method, + policy: params.policy, + }); +} +``` + +#### renderViewPaymentMethodSummary() + +Renders compact payment method summary. + +**Trigger**: Listing payment methods + +```typescript +export async function renderViewPaymentMethodSummary(params): Promise { + // Fetch payment method details from Stripe + const paymentMethodDetails = await stripeClient.stripeSDK.paymentMethods.retrieve( + params.payment_method.module.payment_method + ); + + return renderService.renderViewPaymentMethodSummary({ + payment_method: params.payment_method, + paymentMethodDetails: { card: paymentMethodDetails.card }, + }); +} +``` + +### Policy Lifecycle + +#### afterPolicyIssued() + +Called after a policy is issued. + +**Trigger**: New policy created + +**Parameters**: `{ policy }` + +**Implementation Example**: + +```typescript +export async function afterPolicyIssued({ policy }): Promise { + const logService = getLogService(); + logService.info('Policy issued', 'afterPolicyIssued', { + policyId: policy.policy_id, + }); + + // Example: Create Stripe customer for the policy + const customer = await stripeClient.stripeSDK.customers.create({ + email: policy.policyholder.email, + name: `${policy.policyholder.first_name} ${policy.policyholder.last_name}`, + metadata: { + root_policy_id: policy.policy_id, + }, + }); + + // Store customer ID in policy + await rootClient.SDK.updatePolicy({ + policyId: policy.policy_id, + body: { + app_data: { + stripe_customer_id: customer.id, + }, + }, + }); +} +``` + +#### afterPolicyPaymentMethodAssigned() + +Called when a payment method is assigned to a policy. + +**Trigger**: Payment method linked to policy + +**Parameters**: `{ policy }` + +**Implementation Example**: + +```typescript +export async function afterPolicyPaymentMethodAssigned({ policy }): Promise { + const logService = getLogService(); + logService.info('Payment method assigned', 'afterPolicyPaymentMethodAssigned', { + policyId: policy.policy_id, + }); + + // 1. Get payment method from Root + const paymentMethod = await rootClient.SDK.getPolicyPaymentMethod({ + policyId: policy.policy_id, + }); + const stripePaymentMethodId = paymentMethod.module.payment_method; + + // 2. Get or create Stripe customer + let customerId = policy.app_data?.stripe_customer_id; + if (!customerId) { + const customer = await stripeClient.stripeSDK.customers.create({ + email: policy.policyholder.email, + metadata: { root_policy_id: policy.policy_id }, + }); + customerId = customer.id; + } + + // 3. Attach payment method to customer + await stripeClient.stripeSDK.paymentMethods.attach(stripePaymentMethodId, { + customer: customerId, + }); + + // 4. Set as default payment method + await stripeClient.stripeSDK.customers.update(customerId, { + invoice_settings: { + default_payment_method: stripePaymentMethodId, + }, + }); + + // 5. Update policy with Stripe data + await rootClient.SDK.updatePolicy({ + policyId: policy.policy_id, + body: { + app_data: { + stripe_customer_id: customerId, + stripe_payment_method_id: stripePaymentMethodId, + }, + }, + }); +} +``` + +#### afterPaymentMethodRemoved() + +Called when payment method is removed from policy. + +**Trigger**: Payment method unlinked + +**Parameters**: `{ policy }` + +#### afterPolicyUpdated() + +Called when policy is updated. + +**Trigger**: Policy fields modified + +**Parameters**: `{ policy, updates }` + +**Implementation Example**: + +```typescript +export async function afterPolicyUpdated({ policy, updates }): Promise { + // Check if billing amount changed + if (updates.billing_amount) { + const customerId = policy.app_data?.stripe_customer_id; + const subscriptionId = policy.app_data?.stripe_subscription_id; + + if (subscriptionId) { + // Update subscription price + await updateStripeSubscriptionPrice( + subscriptionId, + policy.billing_amount + ); + } + } +} +``` + +#### afterPolicyCancelled() + +Called when policy is cancelled. + +**Trigger**: Policy cancellation + +**Parameters**: `{ policy }` + +**Implementation Example**: + +```typescript +export async function afterPolicyCancelled({ policy }): Promise { + const subscriptionId = policy.app_data?.stripe_subscription_id; + + if (subscriptionId) { + // Cancel Stripe subscription + await stripeClient.stripeSDK.subscriptions.cancel(subscriptionId); + } +} +``` + +#### afterPolicyExpired() + +Called when policy expires. + +**Trigger**: Policy expiration date passes + +**Parameters**: `{ policy }` + +#### afterPolicyLapsed() + +Called when policy lapses. + +**Trigger**: Policy goes into lapse + +**Parameters**: `{ policy }` + +### Payment Lifecycle + +#### afterPaymentCreated() + +Called when a payment is created. + +**Trigger**: New payment created on Root + +**Parameters**: `{ policy, payment }` + +**Implementation Example**: + +```typescript +export async function afterPaymentCreated({ policy, payment }): Promise { + const logService = getLogService(); + logService.info('Payment created', 'afterPaymentCreated', { + policyId: policy.policy_id, + paymentId: payment.payment_id, + }); + + // Get customer ID + const customerId = policy.app_data?.stripe_customer_id; + if (!customerId) { + throw new Error('Policy missing Stripe customer ID'); + } + + // Create payment intent on Stripe + const paymentIntent = await stripeClient.stripeSDK.paymentIntents.create({ + amount: payment.amount, + currency: policy.currency, + customer: customerId, + description: payment.description, + metadata: { + root_payment_id: payment.payment_id, + root_policy_id: policy.policy_id, + }, + confirm: true, + off_session: true, + }); + + logService.info('Payment intent created', 'afterPaymentCreated', { + paymentIntentId: paymentIntent.id, + }); +} +``` + +#### afterPaymentUpdated() + +Called when payment is updated. + +**Trigger**: Payment fields modified + +**Parameters**: `{ policy, payment }` + +### Alteration Lifecycle + +#### afterAlterationPackageApplied() + +Called after alteration package is applied. + +**Trigger**: Policy alteration processed + +**Parameters**: `{ policy, alteration_package, alteration_hook_key }` + +**Implementation Example**: + +```typescript +export async function afterAlterationPackageApplied({ + policy, + alteration_package, + alteration_hook_key, +}): Promise { + // Check if billing amount changed + if (alteration_package.changes.billing_amount) { + // Update Stripe subscription + await updateStripeSubscription(policy, alteration_package.changes); + } +} +``` + +## Hook Implementation Pattern + +### Basic Structure + +```typescript +export async function hookName(params: HookParams): Promise { + const logService = getLogService(); + + try { + // 1. Log entry + logService.info('Hook started', 'HookName', params); + + // 2. Validate inputs + if (!params.policy) { + throw new Error('Policy is required'); + } + + // 3. Perform operations + const result = await performOperation(params); + + // 4. Log success + logService.info('Hook completed', 'HookName', { result }); + + return result; + } catch (error: any) { + // 5. Log error + logService.error('Hook failed', 'HookName', params, error); + throw error; + } +} +``` + +### Using Services in Hooks + +```typescript +import { getLogService } from '../services/log-instance'; +import { RootService } from '../services/root.service'; +import StripeClient from '../clients/stripe-client'; + +export async function afterPolicyPaymentMethodAssigned({ policy }) { + const logService = getLogService(); + const rootService = new RootService(logService); + const stripeClient = new StripeClient(); + + // Use services for business logic + const paymentMethod = await rootClient.SDK.getPolicyPaymentMethod({ + policyId: policy.policy_id, + }); + const customer = await stripeClient.stripeSDK.customers.create({ ... }); + + await rootClient.SDK.updatePolicy({ + policyId: policy.policy_id, + body: { + app_data: { + stripe_customer_id: customer.id, + }, + }, + }); +} +``` + +## Testing Lifecycle Hooks + +Lifecycle hooks can be tested like any other function: + +```typescript +import { afterPolicyPaymentMethodAssigned } from '../lifecycle-hooks'; +import { getLogService } from '../services/log-instance'; + +jest.mock('../services/log-instance'); +jest.mock('../clients/stripe-client'); +jest.mock('../services/root.service'); + +describe('afterPolicyPaymentMethodAssigned', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should assign payment method to Stripe customer', async () => { + const policy = { + policy_id: 'policy_123', + app_data: { stripe_customer_id: 'cus_123' }, + }; + + await afterPolicyPaymentMethodAssigned({ policy }); + + expect(mockStripeClient.paymentMethods.attach).toHaveBeenCalledWith( + 'pm_123', + { customer: 'cus_123' } + ); + }); +}); +``` + +## Best Practices + +### Do โœ… + +- Log hook entry and exit +- Validate input parameters +- Handle errors gracefully +- Use services for business logic +- Keep hooks thin (orchestration only) +- Add error context +- Test with mocked dependencies + +### Don't โŒ + +- Put complex business logic in hooks +- Ignore errors +- Skip logging +- Make hooks stateful +- Hard-code configuration +- Skip input validation +- Create direct SDK calls (use clients) + +## Common Patterns + +### Idempotency + +```typescript +export async function afterPaymentCreated({ policy, payment }) { + // Check if already processed + const existingPaymentIntent = payment.metadata?.stripe_payment_intent_id; + if (existingPaymentIntent) { + logService.info('Payment already processed', 'afterPaymentCreated'); + return; + } + + // Process payment + const paymentIntent = await createPaymentIntent(payment); + + // Store reference to prevent re-processing + await updatePaymentMetadata(payment.payment_id, { + stripe_payment_intent_id: paymentIntent.id, + }); +} +``` + +### Error Recovery + +```typescript +export async function afterPolicyPaymentMethodAssigned({ policy }) { + try { + await assignPaymentMethod(policy); + } catch (error: any) { + logService.error('Failed to assign payment method', 'Hook', {}, error); + + // Don't throw - allow policy to continue + // Root Platform will retry later + return; + } +} +``` + +## Related Documentation + +- [Controllers Documentation](../controllers/README.md) - Similar patterns +- [Services Documentation](../services/README.md) - Business logic layer +- [Root Platform Docs](https://docs.root.co.za) - Official lifecycle hooks documentation +- [Webhooks](../../docs/WEBHOOKS.md) - Webhook vs lifecycle hooks + diff --git a/stripe_collection_module/docs/LOG_VIEWING.md b/stripe_collection_module/docs/LOG_VIEWING.md new file mode 100644 index 0000000..0255de3 --- /dev/null +++ b/stripe_collection_module/docs/LOG_VIEWING.md @@ -0,0 +1,321 @@ +# Log Viewing Guide + +This guide explains how to view and use the collection module logs. + +## Overview + +The collection module implements **structured JSON logging** to stdout. All logs are automatically captured by your log aggregation system (CloudWatch Logs, DataDog, etc.) where they are: + +- Persistent across Lambda invocations +- Searchable and filterable +- Available for long-term retention +- Accessible via your monitoring platform's UI and API + +The collection module outputs structured JSON to stdout - your logging infrastructure handles collection, storage, and visualization. + +## Accessing Logs + +### Via CloudWatch Logs (AWS) + +1. Navigate to AWS CloudWatch Logs +2. Find your Lambda function's log group +3. View log streams for recent invocations +4. Use CloudWatch Insights for advanced queries + +### Via DataDog (or other platforms) + +Configure your log aggregation platform to collect Lambda stdout logs. The structured JSON format makes it easy to parse and filter. + +## Log Structure + +Each log entry is a JSON object with the following fields: + +| Field | Description | +|-------|-------------| +| `timestamp` | ISO 8601 timestamp | +| `level` | DEBUG, INFO, WARN, or ERROR | +| `environment` | production or development | +| `message` | The log message | +| `context` | Which part of the code logged this (optional) | +| `correlationId` | Request tracking ID (optional) | +| `metadata` | Additional structured data (optional) | +| `error` | Error details including stack trace (optional) | + +### Log Levels + +- ๐Ÿ”ต **DEBUG** - Detailed diagnostic information +- ๐ŸŸข **INFO** - General informational messages +- ๐ŸŸก **WARN** - Warning messages (potential issues) +- ๐Ÿ”ด **ERROR** - Error messages (something went wrong) + +## Understanding Logs + +### Common Contexts + +| Context | What It Means | +|---------|---------------| +| `renderCreatePaymentMethod` | Creating payment method form | +| `afterPolicyPaymentMethodAssigned` | Processing payment method assignment | +| `afterPaymentCreated` | Processing payment creation | +| `WebhookHandler` | Processing Stripe webhook | +| `InvoicePaidController` | Processing invoice paid event | +| `PaymentCreationController` | Processing payment creation | +| `RootService` | Root API operations | + +### Correlation IDs + +Correlation IDs link related log entries together. For example, when a webhook is received, all logs for processing that webhook will share the same correlation ID. + +**Use case**: Filter by correlation ID to see the complete flow of a single request. + +### Metadata + +Metadata provides additional context in structured format: + +``` +Message: "Payment method assigned to policy" +Context: "afterPolicyPaymentMethodAssigned" +Metadata: { + "policyId": "policy_abc123", + "stripeCustomerId": "cus_xyz789" +} +``` + +## Troubleshooting with Logs + +### Example: Payment Failed + +1. Open CloudWatch Logs or DataDog +2. Filter by level: ERROR +3. Look for recent error entries +4. Check error message and stack trace +5. Note the correlation ID +6. Filter by that correlation ID to see full request flow + +### Example: Payment Method Issues + +1. Filter by context: `afterPolicyPaymentMethodAssigned` +2. Look for your policy ID in metadata +3. Follow the log sequence to see where it stopped +4. Check for warnings or errors + +### Example: Debugging Webhook Issues + +1. Filter by context: `WebhookHandler` +2. Find the webhook event by timestamp +3. Get its correlation ID +4. Filter by correlation ID to see complete processing + +## Log Retention + +### CloudWatch Logs + +- **Retention**: Configurable (default: 7-30 days based on log group settings) +- **Automatic Persistence**: All logs automatically sent to CloudWatch +- **Query Capabilities**: CloudWatch Insights for advanced querying +- **Access**: Available via CloudWatch console and API + +### DataDog (Optional) + +CloudWatch logs can optionally be forwarded to DataDog for: + +- Advanced analytics and visualization +- Alerting and monitoring +- Performance tracking +- Cross-service correlation + +## For Developers + +### Adding Logs + +Use the `LogService` throughout your code: + +```typescript +import { getLogService } from '../services/log-instance'; + +const logService = getLogService(); + +// Log with context +logService.info('Processing started', 'MyContext'); + +// Log with metadata +logService.info('Payment created', 'PaymentProcessing', { + paymentId: 'payment_123', + amount: 10000, +}); + +// Log errors +try { + await dangerousOperation(); +} catch (error) { + logService.error( + 'Operation failed', + 'MyContext', + { operation: 'dangerousOperation' }, + error + ); +} +``` + +### Using Correlation IDs + +For request flows: + +```typescript +// Generate at start of request +const correlationId = logService.generateCorrelationId(); + +logService.info('Request started', 'WebhookHandler'); + +// ... process request ... + +// All logs will include this correlation ID + +// Clear at end +logService.clearCorrelationId(); +``` + +### Best Practices + +1. **Be Descriptive** - Write clear log messages + ```typescript + // Bad + logService.info('Done'); + + // Good + logService.info('Payment method successfully attached to customer', 'RootService'); + ``` + +2. **Include Context** - Always provide context (service/controller name) + ```typescript + logService.info('Processing payment', 'PaymentCreationController'); + ``` + +3. **Add Metadata** - Include relevant IDs and data + ```typescript + logService.info('Creating payment intent', 'PaymentCreationController', { + customerId: 'cus_123', + policyId: 'pol_456', + }); + ``` + +4. **Use Appropriate Levels** + - DEBUG: Detailed diagnostic info (not shown to clients by default) + - INFO: Normal operations + - WARN: Something unusual but handled + - ERROR: Something went wrong + +5. **Don't Log Sensitive Data** + ```typescript + // Bad + logService.info('Card details', 'PaymentService', { + cardNumber: '4242424242424242', // Never do this! + }); + + // Good + logService.info('Card details received', 'PaymentService', { + cardLast4: '4242', + cardBrand: 'visa', + }); + ``` + +## Log Format + +### JSON Format + +Logs are output as structured JSON to stdout: + +```json +{ + "timestamp": "2024-01-15T10:30:45.123Z", + "level": "INFO", + "environment": "production", + "message": "Payment method assigned", + "context": "afterPolicyPaymentMethodAssigned", + "correlationId": "abc-123-def", + "metadata": { + "policyId": "policy_123", + "stripeCustomerId": "cus_456" + } +} +``` + +This format is automatically parsed by CloudWatch Logs, DataDog, and other log aggregation systems. + +## FAQ + +### Q: How long are logs kept? +**A**: In CloudWatch: Based on log group retention settings (typically 7-30 days). The retention period is configurable in CloudWatch. + +### Q: Where can I view logs? +**A**: Logs are available in CloudWatch Logs (AWS Console) or your configured log aggregation platform (DataDog, etc.). + +### Q: What if I need older logs? +**A**: All logs are retained in CloudWatch according to the retention policy. Access them via CloudWatch Logs console or the dashboard endpoint. + +### Q: Can I filter by date range? +**A**: Yes, CloudWatch Insights supports powerful filtering by time range, log level, context, correlation ID, and more. + +### Q: Why don't I see recent logs? +**A**: Logs should appear in CloudWatch within seconds. If logs are missing, check: + 1. CloudWatch log group exists for the Lambda function + 2. Lambda has IAM permissions to write to CloudWatch + 3. Logs are being output to stdout (check Lambda execution logs) + +### Q: Does Lambda cold start affect logging? +**A**: No. Logs are sent to CloudWatch regardless of cold/warm starts. Every Lambda invocation outputs logs to stdout, which CloudWatch captures automatically. + +### Q: Can I export logs? +**A**: The dashboard view is HTML. You can copy/paste or screenshot. For bulk export, contact support for DataDog access. + +### Q: What does the correlation ID mean? +**A**: It's a unique ID for a single request flow. All logs from processing one webhook or event share the same correlation ID. + +## Lambda-Specific Considerations + +### CloudWatch Logs + +The collection module automatically logs to CloudWatch: +1. **Log Group**: Auto-created by Lambda (e.g., `/aws/lambda/collection-module-function`) +2. **Log Streams**: One per Lambda execution +3. **Retention**: Configurable in CloudWatch (default: Never expire) +4. **IAM Permissions**: Lambda execution role automatically has CloudWatch Logs permissions + +### Performance + +- **Write Latency**: Negligible (~1-2ms to stdout) +- **CloudWatch Ingestion**: Near real-time (1-2 second delay) +- **Cost**: CloudWatch Logs pricing (~$0.50/GB ingested, $0.03/GB stored) + +### Monitoring + +Monitor these CloudWatch metrics: +- **Log Events**: Number of log entries +- **Ingestion**: Bytes ingested +- **Storage**: Total storage used + +### Querying Logs + +Use CloudWatch Logs Insights to query logs: + +``` +fields @timestamp, level, message, context, correlationId +| filter level = "ERROR" +| sort @timestamp desc +| limit 100 +``` + +## Support + +For issues with log viewing: +1. Check if logs are appearing in CloudWatch Logs console +2. Verify Lambda has CloudWatch Logs permissions (usually automatic) +3. Check the log group name matches your Lambda function +4. Contact support with the policy ID and timestamp +5. Include any error messages shown + +## Related Documentation + +- [ARCHITECTURE.md](./ARCHITECTURE.md) - System architecture overview +- [TESTING.md](./TESTING.md) - Testing guide including LogService tests + diff --git a/stripe_collection_module/docs/ROOT_CONFIGURATION.md b/stripe_collection_module/docs/ROOT_CONFIGURATION.md new file mode 100644 index 0000000..155afe9 --- /dev/null +++ b/stripe_collection_module/docs/ROOT_CONFIGURATION.md @@ -0,0 +1,325 @@ +# Root Platform Configuration + +This guide explains how to configure your collection module for the Root Platform. + +## Configuration Files + +### `.root-config.json` + +This file contains the collection module metadata and settings used by the Root Platform. + +**Location:** `stripe_collection_module/.root-config.json` + +**Format:** + +```json +{ + "collectionModuleKey": "cm_stripe_yourcompany", + "collectionModuleName": "Your Company Stripe Integration", + "organizationId": "00000000-0000-0000-0000-000000000001", + "host": "https://api.rootplatform.com", + "settings": { + "legacyCodeExecution": false + }, + "manualTransactions": [] +} +``` + +**Fields:** + +| Field | Required | Description | Example | +|-------|----------|-------------|---------| +| `collectionModuleKey` | โœ“ | Unique identifier for your collection module. Must start with `cm_` | `cm_stripe_acme` | +| `collectionModuleName` | โœ“ | Human-readable name displayed in Root Platform dashboard | `"ACME Stripe Integration"` | +| `organizationId` | โœ“ | Your Root Platform organization ID (UUID format) | `"12345678-1234-..."` | +| `host` | โœ“ | Root Platform API URL | `"https://api.rootplatform.com"` | +| `settings.legacyCodeExecution` | โœ“ | Whether to use legacy execution mode (should be `false` for new modules) | `false` | +| `manualTransactions` | โœ“ | Array of manual transaction configurations (usually empty) | `[]` | + +**Naming Conventions:** + +- **Collection Module Key**: Use lowercase with underscores, format: `cm_{provider}_{company}` + - Good: `cm_stripe_acme`, `cm_stripe_widgets_inc` + - Bad: `stripeModule`, `cm-stripe-acme`, `STRIPE_CM` + +- **Collection Module Name**: Use Title Case, be descriptive + - Good: `"ACME Stripe Integration"`, `"Widgets Inc Payment Collection"` + - Bad: `"stripe"`, `"collection_module"`, `"CM"` + +**Environment-Specific Configuration:** + +For different environments (sandbox/production), you can: + +1. **Option 1: Single config with environment variable** + ```json + { + "host": "https://api.rootplatform.com", + ... + } + ``` + Use different API keys in `.root-auth` for sandbox vs production. + +2. **Option 2: Multiple config files** (not recommended for this template) + ``` + .root-config.sandbox.json + .root-config.production.json + ``` + +**โš ๏ธ Important Notes:** + +- This file is **NOT** gitignored - it contains configuration metadata, not secrets +- The `.root-config.json` should be committed to version control +- Secrets go in `.root-auth` (which IS gitignored) + +--- + +### `.root-auth` + +This file contains authentication credentials for the Root Platform API. + +**Location:** `stripe_collection_module/.root-auth` + +**Format:** + +```bash +ROOT_API_KEY=sandbox_sk_abc123... +``` + +**Fields:** + +| Variable | Description | Example | +|----------|-------------|---------| +| `ROOT_API_KEY` | Your Root Platform API key | `sandbox_sk_abc123...` or `production_sk_xyz789...` | + +**Getting Your API Key:** + +1. Log in to the Root Platform dashboard +2. Navigate to **Settings โ†’ API Keys** +3. Generate a new API key with appropriate permissions: + - **Sandbox keys**: Start with `sandbox_sk_` + - **Production keys**: Start with `production_sk_` + +**Environment-Specific Keys:** + +For local development and deployment to different environments: + +**Sandbox/Development:** +```bash +ROOT_API_KEY=sandbox_sk_abc123... +``` + +**Production:** +```bash +ROOT_API_KEY=production_sk_xyz789... +``` + +**โš ๏ธ Security:** + +- **NEVER commit `.root-auth` to version control** (already in `.gitignore`) +- Store production keys securely (1Password, AWS Secrets Manager, etc.) +- Rotate API keys quarterly +- Use sandbox keys for all development and testing +- Use different API keys for CI/CD vs local development + +--- + +## Setup Process + +### Automated Setup (Recommended) + +Run the setup script from the repository root: + +```bash +./setup.sh +``` + +The script will prompt you for: +1. Collection Module Key +2. Collection Module Name +3. Organization ID +4. Root Platform Host +5. Root Platform API Key + +All values will be saved to the appropriate files. + +### Manual Setup + +1. **Configure `.root-config.json`:** + ```bash + cd stripe_collection_module + # Edit the file with your values + nano .root-config.json + ``` + +2. **Create `.root-auth`:** + ```bash + cd stripe_collection_module + cp .root-auth.sample .root-auth + # Edit with your actual API key + nano .root-auth + ``` + +--- + +## Validation + +### Verify Configuration + +Check that your configuration is valid: + +```bash +cd stripe_collection_module + +# Check .root-config.json format +cat .root-config.json | jq '.' + +# Verify .root-auth exists and has correct format +if [ -f .root-auth ]; then + echo "โœ“ .root-auth exists" + grep -q "ROOT_API_KEY=" .root-auth && echo "โœ“ ROOT_API_KEY is set" +else + echo "โŒ .root-auth not found" +fi +``` + +### Test API Connection + +You can test your Root Platform connection using the Root Platform CLI or a simple curl command: + +```bash +# Using curl +curl -H "Authorization: Basic $(cat .root-auth | grep ROOT_API_KEY | cut -d= -f2)" \ + "https://api.rootplatform.com/v1/insurance/organizations/$(jq -r .organizationId .root-config.json)" + +# Expected: JSON response with organization details +``` + +--- + +## Troubleshooting + +### "collectionModuleKey is invalid" + +**Problem:** The collection module key doesn't match Root Platform requirements. + +**Solution:** +- Must start with `cm_` +- Use only lowercase letters, numbers, and underscores +- Should be unique within your organization +- Format: `cm_{provider}_{company}` + +### "Unauthorized" or "Invalid API key" + +**Problem:** The API key in `.root-auth` is invalid or expired. + +**Solution:** +1. Check the API key is correct (no extra spaces/newlines) +2. Verify key has not been revoked in Root Platform dashboard +3. Ensure you're using the right environment key (sandbox vs production) +4. Generate a new API key if needed + +### "Organization not found" + +**Problem:** The `organizationId` in `.root-config.json` is incorrect. + +**Solution:** +1. Log in to Root Platform dashboard +2. Check your organization ID in **Settings โ†’ Organization** +3. Update `.root-config.json` with correct UUID + +### ".root-auth not found" during deployment + +**Problem:** The `.root-auth` file is missing. + +**Solution:** +```bash +cd stripe_collection_module +cp .root-auth.sample .root-auth +# Edit with your API key +nano .root-auth +``` + +### "Cannot read property 'host' of undefined" + +**Problem:** The `.root-config.json` file is malformed or missing. + +**Solution:** +```bash +# Validate JSON syntax +cat .root-config.json | jq '.' + +# If invalid, restore from sample +cp .root-config.json.sample .root-config.json +# Then edit with your values +``` + +--- + +## Environment Variables + +Some deployment scenarios may require environment variables instead of or in addition to config files: + +```bash +# For deployment scripts +export ROOT_API_KEY="sandbox_sk_abc123..." +export ROOT_ORG_ID="00000000-0000-0000-0000-000000000001" +export ROOT_HOST="https://api.rootplatform.com" +export CM_KEY="cm_stripe_yourcompany" +``` + +The deployment script (`scripts/deploy.sh`) can read from either: +1. `.root-auth` and `.root-config.json` files (preferred) +2. Environment variables (for CI/CD) + +--- + +## Best Practices + +### Local Development + +- โœ… Use sandbox API keys +- โœ… Keep `.root-auth` in your local directory only +- โœ… Never commit `.root-auth` to git +- โœ… Test with sandbox environment before production + +### CI/CD Deployment + +- โœ… Store API keys in CI/CD secrets (GitHub Secrets, GitLab CI Variables, etc.) +- โœ… Use environment variables in deployment scripts +- โœ… Rotate API keys regularly +- โœ… Use different keys for sandbox and production pipelines + +### Security + +- โœ… Treat `.root-auth` like a password file +- โœ… Never share API keys via email or chat +- โœ… Use 1Password or similar for team sharing +- โœ… Audit API key usage regularly in Root Platform dashboard +- โœ… Revoke unused or compromised keys immediately + +### Version Control + +- โœ… Commit `.root-config.json` (no secrets) +- โœ… Commit `.root-config.json.sample` (template) +- โœ… Commit `.root-auth.sample` (template) +- โŒ Never commit `.root-auth` (contains secrets) +- โŒ Never commit backup files like `.root-auth.backup` + +--- + +## Related Documentation + +- [Setup Guide](SETUP.md) - Complete setup walkthrough +- [Deployment Guide](DEPLOYMENT.md) - Deploying to Root Platform +- [Environment Configuration](../code/env.sample.ts) - Stripe and module configuration + +--- + +## Support + +If you encounter issues with Root Platform configuration: + +1. Check this documentation +2. Verify your API key in Root Platform dashboard +3. Review Root Platform logs for errors +4. Contact your Root Platform representative diff --git a/stripe_collection_module/docs/SERVICES.md b/stripe_collection_module/docs/SERVICES.md new file mode 100644 index 0000000..26193cd --- /dev/null +++ b/stripe_collection_module/docs/SERVICES.md @@ -0,0 +1,516 @@ +# Services + +This directory contains business logic services that orchestrate operations across clients, adapters, and utilities. Services form the core business layer of the application. + +## Architecture + +``` +Controllers โ†’ Services โ†’ Clients +``` + +Services encapsulate business logic and: +- Coordinate multiple operations +- Transform data between systems +- Implement business rules +- Handle complex workflows +- Testable via dependency injection + +## Available Services + +### LogService + +Structured JSON logging to stdout for log aggregation systems. + +**Location**: `log.service.ts` + +**Key Features**: +- Multiple log levels (DEBUG, INFO, WARN, ERROR) +- Correlation IDs for request tracking +- Structured JSON output +- Environment context + +**Usage**: + +```typescript +import { getLogService } from '../services/log-instance'; + +const logService = getLogService(); + +// Basic logging +logService.info('Operation completed', 'MyService'); +logService.error('Operation failed', 'MyService', {}, error); + +// With metadata +logService.info('Payment created', 'PaymentService', { + paymentId: 'payment_123', + amount: 10000, +}); + +// With correlation ID +const correlationId = logService.generateCorrelationId(); +logService.info('Processing request', 'WebhookHandler'); +// ... all subsequent logs will include this correlation ID +logService.clearCorrelationId(); +``` + +**Configuration**: + +```typescript +new LogService({ + environment: 'production', + minLogLevel: LogLevel.INFO, // Filter out DEBUG logs +}); +``` + +### ConfigurationService + +Type-safe, validated configuration management with environment-specific settings. + +**Location**: `config.service.ts` + +**Key Features**: +- Environment-specific configs (production/development) +- Validation on startup +- Type-safe access +- Injectable for testing + +**Usage**: + +```typescript +import { getConfigService } from '../services/config-instance'; + +const config = getConfigService(); + +// Get individual config values +const apiKey = config.get('stripeSecretKey'); +const baseUrl = config.get('rootBaseUrl'); + +// Check environment +if (config.isProduction()) { + // Production-specific logic +} + +// Get all config +const allConfig = config.getAll(); +``` + +**Available Configuration**: +- `stripeSecretKey` +- `stripePublishableKey` +- `stripeWebhookSigningSecret` +- `stripeProductId` +- `rootApiKey` +- `rootBaseUrl` +- `rootCollectionModuleKey` +- `environment` + +### RenderService + +HTML generation for Root Platform dashboard views. + +**Location**: `render.service.ts` + +**Key Features**: +- Payment method forms with Stripe Elements +- Payment method summary views +- Payment method detail views +- XSS protection via HTML escaping + +**Usage**: + +```typescript +import { RenderService } from '../services/render.service'; + +const renderService = new RenderService(); + +// Render payment method creation form +const html = renderService.renderCreatePaymentMethod({ + stripePublishableKey: 'pk_test_...', + setupIntentClientSecret: 'seti_...', +}); + +// Render payment method summary +const summaryHtml = renderService.renderViewPaymentMethodSummary({ + payment_method: paymentMethod, + paymentMethodDetails: { + card: { + brand: 'visa', + last4: '4242', + exp_month: 12, + exp_year: 2025, + }, + }, +}); + +// Render full payment method view +const detailHtml = renderService.renderViewPaymentMethod({ + payment_method: paymentMethod, + policy: policy, +}); +``` + +### RootService + +Business logic for Root Platform operations. + +**Location**: `root.service.ts` + +**Key Features**: +- Policy management +- Payment operations +- Type-safe wrappers around Root SDK +- Error handling and logging + +**Usage**: + +```typescript +import { RootService } from '../services/root.service'; +import { PaymentStatus } from '@rootplatform/node-sdk'; + +const rootService = new RootService(logService); + +// Get policy +const policy = await rootService.getPolicy('policy_123'); + +// Update payment status +await rootService.updatePaymentStatus({ + paymentId: 'payment_456', + status: PaymentStatus.Successful, +}); +``` + +### StripeService + +Business logic for Stripe operations. + +**Location**: `stripe.service.ts` + +**Key Features**: +- Customer management (create, get, update) +- Payment intent creation +- Payment method operations (get, attach) +- Subscription management +- Type-safe wrappers around Stripe SDK +- Error handling and logging + +**Usage**: + +```typescript +import { StripeService } from '../services/stripe.service'; + +const stripeService = new StripeService(logService); + +// Create customer +const customer = await stripeService.createCustomer({ + email: 'customer@example.com', + name: 'John Doe', + metadata: { root_policy_id: 'policy_123' }, +}); + +// Create payment intent +const paymentIntent = await stripeService.createPaymentIntent({ + amount: 10000, + currency: 'zar', + customerId: customer.id, + description: 'Premium payment', + metadata: { root_payment_id: 'payment_456' }, + confirm: true, + offSession: true, +}); + +// Attach payment method to customer +const paymentMethod = await stripeService.attachPaymentMethod({ + paymentMethodId: 'pm_123', + customerId: customer.id, +}); + +// Update customer +await stripeService.updateCustomer(customer.id, { + invoice_settings: { + default_payment_method: 'pm_123', + }, +}); + +// Cancel subscription +await stripeService.cancelSubscription('sub_123'); +``` + +## Service Helper Instances + +For convenience, some services provide instance helper functions: + +### log-instance.ts + +```typescript +import { getLogService } from '../services/log-instance'; + +// Get singleton instance +const logService = getLogService(); + +// Check if initialized +if (isLogServiceInitialized()) { + // Use log service +} +``` + +### config-instance.ts + +```typescript +import { getConfigService } from '../services/config-instance'; + +// Get singleton instance +const config = getConfigService(); + +// Check if initialized +if (isConfigServiceInitialized()) { + // Use config service +} +``` + +## Creating a New Service + +When you need to add new business logic: + +### 1. Create Service File + +```typescript +// services/my-feature.service.ts +import { LogService } from './log.service'; +import StripeClient from '../clients/stripe-client'; +import { RootService } from './root.service'; + +export class MyFeatureService { + constructor( + private readonly logService: LogService, + private readonly stripeClient: StripeClient, + private readonly rootService: RootService + ) {} + + async performOperation(params: OperationParams): Promise { + this.logService.info('Starting operation', 'MyFeatureService', params); + + try { + // 1. Fetch data from Stripe + const stripeData = await this.stripeClient.stripeSDK.someMethod(); + + // 2. Transform data + const transformed = this.transformData(stripeData); + + // 3. Update Root + await this.rootService.updateSomething(transformed); + + this.logService.info('Operation completed', 'MyFeatureService'); + return result; + } catch (error: any) { + this.logService.error( + 'Operation failed', + 'MyFeatureService', + { params }, + error + ); + throw error; + } + } + + private transformData(data: any) { + // Business logic here + return transformed; + } +} +``` + +### 2. Register in DI Container + +```typescript +// core/container.setup.ts +container.register( + ServiceToken.MY_FEATURE_SERVICE, + (c) => { + const logService = c.resolve(ServiceToken.LOG_SERVICE); + const rootService = c.resolve(ServiceToken.ROOT_SERVICE); + const stripeClient = new StripeClient(); + + const { MyFeatureService } = require('../services/my-feature.service'); + return new MyFeatureService(logService, stripeClient, rootService); + }, + ServiceLifetime.SINGLETON +); +``` + +### 3. Use in Controllers + +```typescript +import { getContainer } from '../core/container.setup'; +import { ServiceToken } from '../core/container'; + +const container = getContainer(); +const myFeatureService = container.resolve(ServiceToken.MY_FEATURE_SERVICE); + +await myFeatureService.performOperation(params); +``` + +## Testing Services + +Services are designed for easy testing with dependency injection: + +```typescript +import { MyFeatureService } from '../services/my-feature.service'; +import { LogService } from '../services/log.service'; + +describe('MyFeatureService', () => { + let service: MyFeatureService; + let mockLogService: jest.Mocked; + let mockStripeClient: jest.Mocked; + let mockRootService: jest.Mocked; + + beforeEach(() => { + // Create mocks + mockLogService = { + info: jest.fn(), + error: jest.fn(), + debug: jest.fn(), + warn: jest.fn(), + } as any; + + mockStripeClient = { + stripeSDK: { + customers: { + retrieve: jest.fn(), + }, + }, + } as any; + + mockRootService = { + updatePaymentStatus: jest.fn(), + } as any; + + // Inject mocks + service = new MyFeatureService( + mockLogService, + mockStripeClient, + mockRootService + ); + }); + + it('should perform operation successfully', async () => { + // Arrange + mockStripeClient.stripeSDK.customers.retrieve.mockResolvedValue({ + id: 'cus_123', + }); + + // Act + const result = await service.performOperation({ id: '123' }); + + // Assert + expect(result).toBeDefined(); + expect(mockLogService.info).toHaveBeenCalledWith( + 'Starting operation', + 'MyFeatureService', + { id: '123' } + ); + }); + + it('should handle errors gracefully', async () => { + // Arrange + const error = new Error('API failed'); + mockStripeClient.stripeSDK.customers.retrieve.mockRejectedValue(error); + + // Act & Assert + await expect(service.performOperation({ id: '123' })).rejects.toThrow(); + expect(mockLogService.error).toHaveBeenCalled(); + }); +}); +``` + +## Best Practices + +### Do โœ… + +- Inject all dependencies via constructor +- Use LogService for all logging +- Return domain objects, not raw API responses +- Handle errors with context +- Keep services focused (single responsibility) +- Write unit tests with mocked dependencies +- Document public methods with JSDoc + +### Don't โŒ + +- Create dependencies with `new` inside service (except clients) +- Mix infrastructure concerns with business logic +- Catch errors without logging +- Skip input validation +- Create global state/singletons (use DI container) +- Expose internal implementation details +- Skip error handling + +## Service Patterns + +### Orchestration Pattern + +```typescript +// Service coordinates multiple operations +async createCustomerAndSubscription(policy: Policy) { + // 1. Create customer + const customer = await this.stripeClient.stripeSDK.customers.create({ + email: policy.email, + }); + + // 2. Attach payment method + await this.stripeClient.stripeSDK.paymentMethods.attach( + paymentMethodId, + { customer: customer.id } + ); + + // 3. Create subscription + const subscription = await this.stripeClient.stripeSDK.subscriptions.create({ + customer: customer.id, + items: [{ price: priceId }], + }); + + // 4. Update Root + await this.rootService.updatePolicyAppData(policy.policy_id, { + stripe_customer_id: customer.id, + stripe_subscription_id: subscription.id, + }); + + return { customer, subscription }; +} +``` + +### Transformation Pattern + +```typescript +// Service transforms data between systems +async syncPaymentStatus(stripeInvoice: Stripe.Invoice, rootPaymentId: string) { + // Transform Stripe data to Root format + const status = this.mapStripeStatusToRoot(stripeInvoice.status); + const failureReason = stripeInvoice.last_finalization_error?.message; + + // Update Root + await this.rootService.updatePaymentStatus({ + paymentId: rootPaymentId, + status, + failureReason, + }); +} + +private mapStripeStatusToRoot(stripeStatus: string): PaymentStatus { + const mapping = { + paid: PaymentStatus.Successful, + open: PaymentStatus.Pending, + void: PaymentStatus.Cancelled, + uncollectible: PaymentStatus.Failed, + }; + return mapping[stripeStatus] || PaymentStatus.Failed; +} +``` + +## Related Documentation + +- [Clients Documentation](../clients/README.md) - Using API clients +- [Controllers Documentation](../controllers/README.md) - Calling services from controllers +- [Core/DI Container](../core/README.md) - Dependency injection patterns +- [Testing Guide](../../docs/TESTING.md) - Testing services +- [Best Practices](../../docs/BEST_PRACTICES.md) - Service design patterns + diff --git a/stripe_collection_module/docs/SETUP.md b/stripe_collection_module/docs/SETUP.md new file mode 100644 index 0000000..cdf038f --- /dev/null +++ b/stripe_collection_module/docs/SETUP.md @@ -0,0 +1,465 @@ +# Collection Module Setup Guide + +This guide will walk you through setting up a collection module from this template. + +## Quick Start (5 Minutes) + +### 1. Run Setup Script + +```bash +# From the repository root +chmod +x setup-template.sh +./setup-template.sh +``` + +The script will guide you through: +- Provider name and configuration +- Collection module key setup +- Environment file creation +- Dependency installation + +### 2. Update Environment Variables + +```bash +cd stripe_collection_module +# Edit code/env.ts with your actual API keys +``` + +### 3. Validate Configuration + +```bash +npm run validate +``` + +### 4. Run Tests + +```bash +nvm use # Use Node 18+ +npm test +``` + +You're ready to customize! See [CUSTOMIZING.md](./CUSTOMIZING.md) for next steps. + +--- + +## Detailed Setup Instructions + +### Prerequisites + +**Required:** +- Node.js 18+ (use `nvm` for version management) +- npm 8+ +- Git + +**Accounts Needed:** +- Root Platform account with organization access +- Payment provider account (e.g., Stripe, PayPal) +- AWS account (for Lambda deployment) + +### Step 1: Clone or Copy Template + +```bash +# Clone the repository +git clone +cd collection-module-template_stripe + +# Or copy the template directory +cp -r collection-module-template_stripe my-provider-collection-module +cd my-provider-collection-module +``` + +### Step 2: Configure Your Provider + +#### Option A: Automated Setup + +```bash +./setup-template.sh +``` + +#### Option B: Manual Setup + +1. **Update module configuration** + +Edit `stripe_collection_module/.root-config.json`: + +```json +{ + "collectionModuleKey": "cm_your_provider", + "collectionModuleName": "Your Provider Collection Module", + "organizationId": "your-root-organization-id", + "host": "http://localhost:4000", + "settings": { + "legacyCodeExecution": false + } +} +``` + +2. **Create environment file** + +```bash +cd stripe_collection_module +cp code/env.sample.ts code/env.ts +``` + +3. **Update environment variables** + +Edit `code/env.ts` with your actual credentials: + +```typescript +// Environment +export const NODE_ENV = 'development'; + +// Your Provider API Keys +export const STRIPE_SECRET_KEY_LIVE = 'sk_live_YOUR_KEY'; +export const STRIPE_SECRET_KEY_TEST = 'sk_test_YOUR_KEY'; +// ... other keys + +// Root Platform +export const ROOT_API_KEY_LIVE = 'production_YOUR_KEY'; +export const ROOT_API_KEY_SANDBOX = 'sandbox_YOUR_KEY'; +export const ROOT_BASE_URL_LIVE = 'https://api.rootplatform.com/v1/insurance'; +export const ROOT_COLLECTION_MODULE_KEY = 'cm_your_provider'; +``` + +### Step 3: Install Dependencies + +```bash +cd stripe_collection_module + +# Use correct Node version +nvm use + +# Install packages +npm install +``` + +### Step 4: Validate Setup + +Run the configuration validator: + +```bash +npm run validate +``` + +This checks: +- All required configuration files exist +- No placeholder values remain +- TypeScript compiles successfully +- Tests pass +- Correct Node version + +### Step 5: Run Tests + +```bash +# All tests +npm test + +# With coverage +npm run test:coverage + +# Watch mode for development +npm run test:watch +``` + +### Step 6: Build + +```bash +npm run build +``` + +This compiles TypeScript and creates the `dist/` folder. + +--- + +## Configuration Details + +### Required Environment Variables + +| Variable | Description | Example | +|----------|-------------|---------| +| `NODE_ENV` | Environment name | `development` or `production` | +| `STRIPE_SECRET_KEY_LIVE` | Provider API secret (production) | `sk_live_...` | +| `STRIPE_SECRET_KEY_TEST` | Provider API secret (test) | `sk_test_...` | +| `STRIPE_PUBLISHABLE_KEY_LIVE` | Provider publishable key (prod) | `pk_live_...` | +| `STRIPE_PUBLISHABLE_KEY_TEST` | Provider publishable key (test) | `pk_test_...` | +| `STRIPE_WEBHOOK_SIGNING_SECRET_LIVE` | Webhook secret (production) | `whsec_...` | +| `STRIPE_WEBHOOK_SIGNING_SECRET_TEST` | Webhook secret (test) | `whsec_...` | +| `ROOT_API_KEY_LIVE` | Root API key (production) | `production_...` | +| `ROOT_API_KEY_SANDBOX` | Root API key (sandbox) | `sandbox_...` | +| `ROOT_BASE_URL_LIVE` | Root API base URL (prod) | `https://api.rootplatform.com/v1/insurance` | +| `ROOT_BASE_URL_SANDBOX` | Root API base URL (sandbox) | `https://sandbox.rootplatform.com/v1/insurance` | +| `ROOT_COLLECTION_MODULE_KEY` | Your module key | `cm_your_provider` | + +### Optional Variables + +| Variable | Description | Default | +|----------|-------------|---------| +| `TIME_DELAY_IN_MILLISECONDS` | Processing delay | `10000` | +| `STRIPE_PRODUCT_ID_LIVE` | Provider product ID (prod) | (optional) | +| `STRIPE_PRODUCT_ID_TEST` | Provider product ID (test) | (optional) | + +### Getting API Keys + +**Stripe:** +1. Go to [Stripe Dashboard](https://dashboard.stripe.com) +2. Navigate to Developers โ†’ API keys +3. Copy your keys (use test keys for development) + +**Root Platform:** +1. Log in to Root Platform +2. Go to Settings โ†’ API Keys +3. Create or copy existing API keys +4. Use sandbox keys for development + +### Validating Configuration + +The `ConfigurationService` validates your setup automatically: +- Checks all required variables are set +- Validates URL formats +- Ensures environment is valid (`production` or `development`) + +If configuration is invalid, you'll see helpful error messages: + +``` +Configuration validation failed: +Missing required configuration values for production: stripeSecretKey, rootApiKey +Hint: Check that code/env.ts is properly configured with all required values. +For AWS Lambda: Ensure all environment variables are set in Lambda configuration. +``` + +--- + +## Development Workflow + +### Local Development Setup + +1. **Use test/sandbox credentials** + +```typescript +// code/env.ts +export const NODE_ENV = 'development'; +export const STRIPE_SECRET_KEY_LIVE = 'sk_test_...'; // Use test key +export const ROOT_API_KEY_LIVE = 'sandbox_...'; // Use sandbox key +``` + +2. **Run in watch mode** + +```bash +npm run test:watch +``` + +3. **Make code changes** + +Edit files in `code/` directory. Tests will re-run automatically. + +4. **Check linting** + +```bash +npm run lint +npm run lint:fix # Auto-fix some issues +``` + +### Project Structure + +``` +stripe_collection_module/ +โ”œโ”€โ”€ code/ # Source code +โ”‚ โ”œโ”€โ”€ core/ # Domain models & DI container +โ”‚ โ”‚ โ”œโ”€โ”€ models/ # Data models +โ”‚ โ”‚ โ”œโ”€โ”€ container.ts # DI container +โ”‚ โ”‚ โ””โ”€โ”€ container.setup.ts # Service registration +โ”‚ โ”œโ”€โ”€ services/ # Business logic +โ”‚ โ”‚ โ”œโ”€โ”€ config.service.ts # Configuration +โ”‚ โ”‚ โ”œโ”€โ”€ log.service.ts # Logging +โ”‚ โ”‚ โ”œโ”€โ”€ stripe.service.ts # Provider operations +โ”‚ โ”‚ โ””โ”€โ”€ root.service.ts # Root operations +โ”‚ โ”œโ”€โ”€ clients/ # API wrappers +โ”‚ โ”‚ โ”œโ”€โ”€ stripe-client.ts # Provider SDK wrapper +โ”‚ โ”‚ โ””โ”€โ”€ root-client.ts # Root SDK wrapper +โ”‚ โ”œโ”€โ”€ controllers/ # Event processors +โ”‚ โ”‚ โ”œโ”€โ”€ stripe-event-processors/ +โ”‚ โ”‚ โ””โ”€โ”€ root-event-processors/ +โ”‚ โ”œโ”€โ”€ lifecycle-hooks/ # Root platform hooks +โ”‚ โ”‚ โ””โ”€โ”€ index.ts +โ”‚ โ”œโ”€โ”€ utils/ # Utilities +โ”‚ โ””โ”€โ”€ env.ts # Your environment config +โ”œโ”€โ”€ __tests__/ # Test files +โ”‚ โ”œโ”€โ”€ services/ +โ”‚ โ”œโ”€โ”€ core/ +โ”‚ โ””โ”€โ”€ helpers/ +โ”œโ”€โ”€ docs/ # Documentation +โ”œโ”€โ”€ scripts/ # Build/deploy scripts +โ””โ”€โ”€ package.json +``` + +--- + +## Troubleshooting + +### Error: "ENVIRONMENT is not set" + +**Solution:** Set `NODE_ENV` in `code/env.ts`: + +```typescript +export const NODE_ENV = 'development'; +``` + +### Error: "Missing required configuration values" + +**Solution:** Check `code/env.ts` has all required variables. Look for placeholder values like `xxxxx`. + +### Error: "Module not found" + +**Solution:** Install dependencies: + +```bash +npm install +``` + +### Error: Tests failing + +**Solutions:** +1. Use correct Node version: `nvm use` +2. Clear Jest cache: `npx jest --clearCache` +3. Reinstall: `rm -rf node_modules && npm install` + +### Error: TypeScript compilation errors + +**Solution:** Check `tsconfig.json` includes all source files and dependencies are installed. + +### Validation Script Warnings + +If `npm run validate` shows warnings: +- **Placeholder values**: Update `code/env.ts` and `.root-config.json` with real values +- **Linting issues**: Run `npm run lint:fix` to auto-fix +- **Node version mismatch**: Run `nvm use` + +--- + +## Next Steps + +After completing setup: + +1. **Read customization guide**: [CUSTOMIZING.md](./CUSTOMIZING.md) + - Adapt template for your payment provider + - Implement service methods + - Add provider-specific logic + +2. **Implement webhooks**: [WEBHOOKS.md](./WEBHOOKS.md) + - Set up webhook endpoints + - Handle provider events + - Test webhook delivery + +3. **Deploy to Lambda**: [DEPLOYMENT.md](./DEPLOYMENT.md) + - Configure AWS resources + - Set up CI/CD pipeline + - Deploy and test + +4. **Review architecture**: [ARCHITECTURE.md](./ARCHITECTURE.md) + - Understand system design + - Learn service patterns + - Explore DI container + +5. **Write tests**: [TESTING.md](./TESTING.md) + - Add unit tests for your services + - Create integration tests + - Achieve good coverage + +--- + +## Getting Help + +### Documentation + +- **Setup**: This file +- **Customization**: [CUSTOMIZING.md](./CUSTOMIZING.md) +- **Deployment**: [DEPLOYMENT.md](./DEPLOYMENT.md) +- **Webhooks**: [WEBHOOKS.md](./WEBHOOKS.md) +- **Architecture**: [ARCHITECTURE.md](./ARCHITECTURE.md) +- **Testing**: [TESTING.md](./TESTING.md) +- **Template Setup**: `../TEMPLATE_SETUP.md` (root) + +### Common Tasks + +```bash +# Validate configuration +npm run validate + +# Run tests +npm test + +# Run linter +npm run lint + +# Fix linting issues +npm run lint:fix + +# Build for production +npm run build + +# Run specific test file +npm test -- services/log.service.test + +# Watch tests +npm run test:watch + +# Check coverage +npm run test:coverage +``` + +### Support + +If you're stuck: +1. Check troubleshooting section above +2. Review relevant documentation +3. Check CloudWatch Logs (after deployment) +4. Review test output for hints + +--- + +## Best Practices + +### Security + +- โœ… Never commit `code/env.ts` to git +- โœ… Use separate keys for test/production +- โœ… Rotate API keys regularly +- โœ… Use AWS Secrets Manager for production +- โœ… Enable CloudWatch Logs monitoring + +### Development + +- โœ… Use test/sandbox credentials locally +- โœ… Run tests before committing +- โœ… Keep test coverage above 70% +- โœ… Follow TypeScript best practices +- โœ… Use the DI container for dependencies + +### Configuration + +- โœ… Validate before deploying +- โœ… Document all env variables +- โœ… Use meaningful module keys +- โœ… Set appropriate time delays +- โœ… Configure proper log levels + +--- + +## Checklist + +Use this to track your setup progress: + +- [ ] Cloned/copied template +- [ ] Ran setup script or manual configuration +- [ ] Created `code/env.ts` with real values +- [ ] Updated `.root-config.json` +- [ ] Installed dependencies (`npm install`) +- [ ] Ran validation (`npm run validate`) +- [ ] All tests passing (`npm test`) +- [ ] TypeScript compiling (`npm run build`) +- [ ] No linting errors (`npm run lint`) +- [ ] Reviewed customization guide +- [ ] Ready to implement provider logic + +Once complete, move to [CUSTOMIZING.md](./CUSTOMIZING.md) to adapt the template for your provider. + diff --git a/stripe_collection_module/docs/TESTING.md b/stripe_collection_module/docs/TESTING.md new file mode 100644 index 0000000..99cebf6 --- /dev/null +++ b/stripe_collection_module/docs/TESTING.md @@ -0,0 +1,470 @@ +# Testing Guide + +This guide explains how to test the collection module effectively. + +## Overview + +We use **Jest** with **TypeScript** for unit testing. The testing infrastructure is designed to make tests easy to write, read, and maintain. + +## Running Tests + +```bash +# Run all tests +npm test + +# Run tests in watch mode (auto-rerun on changes) +npm test:watch + +# Run tests with coverage report +npm test:coverage + +# Run specific test file +npm test -- log.service.test.ts + +# Run tests matching a pattern +npm test -- --testNamePattern="LogService" +``` + +**Important**: Always run `nvm use` first to switch to Node 18+, as specified in `.nvmrc`. + +## Test Structure + +### Directory Organization + +``` +__tests__/ +โ”œโ”€โ”€ core/ # Core functionality tests +โ”‚ โ””โ”€โ”€ container.test.ts +โ”œโ”€โ”€ services/ # Service tests +โ”‚ โ”œโ”€โ”€ log.service.test.ts +โ”‚ โ””โ”€โ”€ render.service.test.ts +โ”œโ”€โ”€ lifecycle-hooks/ # Lifecycle hook tests +โ”‚ โ””โ”€โ”€ render-logs.test.ts +โ”œโ”€โ”€ helpers/ # Test utilities +โ”‚ โ”œโ”€โ”€ test-utils.ts # Helper functions +โ”‚ โ””โ”€โ”€ factories.ts # Mock data factories +โ””โ”€โ”€ setup.ts # Global test setup +``` + +### Test File Naming + +- Test files: `*.test.ts` +- Place tests adjacent to code: `__tests__/services/my-service.test.ts` +- Match source file names: `log.service.ts` โ†’ `log.service.test.ts` + +## Writing Tests + +### Basic Test Structure + +```typescript +import { MyService } from '../../code/services/my-service'; + +describe('MyService', () => { + let service: MyService; + + beforeEach(() => { + // Setup - runs before each test + service = new MyService(); + }); + + afterEach(() => { + // Cleanup - runs after each test + jest.restoreAllMocks(); + }); + + describe('methodName', () => { + it('should do something specific', () => { + // Arrange + const input = 'test'; + + // Act + const result = service.methodName(input); + + // Assert + expect(result).toBe('expected output'); + }); + }); +}); +``` + +### Testing with Dependencies + +Use the DI container for easy mocking: + +```typescript +import { Container, ServiceToken } from '../../code/core/container'; +import { MyService } from '../../code/services/my-service'; +import { LogService } from '../../code/services/log.service'; + +describe('MyService with Dependencies', () => { + let container: Container; + let mockLogService: jest.Mocked; + let myService: MyService; + + beforeEach(() => { + // Create isolated container for testing + container = new Container(); + + // Create mock log service + mockLogService = { + info: jest.fn(), + error: jest.fn(), + // ... other methods + } as any; + + // Register mock + container.register( + ServiceToken.LOG_SERVICE, + () => mockLogService, + ServiceLifetime.SINGLETON + ); + + // Get service under test + myService = new MyService( + container.resolve(ServiceToken.LOG_SERVICE) + ); + }); + + it('should log when processing', () => { + myService.process(); + + expect(mockLogService.info).toHaveBeenCalledWith( + 'Processing started', + 'MyService' + ); + }); +}); +``` + +### Using Test Factories + +We provide factory functions for creating mock data: + +```typescript +import { + createMockStripeCustomer, + createMockRootPolicy, + createMockRootPayment +} from '../helpers/factories'; + +it('should process payment', () => { + // Create mock data easily + const customer = createMockStripeCustomer({ + email: 'test@example.com' + }); + + const policy = createMockRootPolicy({ + monthly_premium: 10000 + }); + + // Test with mock data + const result = service.processPayment(customer, policy); + expect(result).toBeDefined(); +}); +``` + +### Testing Async Code + +```typescript +it('should handle async operations', async () => { + const promise = service.asyncMethod(); + + await expect(promise).resolves.toBe('success'); +}); + +it('should handle errors', async () => { + const promise = service.failingMethod(); + + await expect(promise).rejects.toThrow('Expected error'); +}); +``` + +### Mocking External APIs + +```typescript +import StripeClient from '../../code/clients/stripe-client'; + +jest.mock('../../code/clients/stripe-client'); + +describe('Service with Stripe', () => { + let mockStripeClient: jest.Mocked; + + beforeEach(() => { + mockStripeClient = { + stripeSDK: { + customers: { + create: jest.fn().mockResolvedValue({ + id: 'cus_123', + email: 'test@example.com' + }) + } + } + } as any; + }); + + it('should create customer', async () => { + const result = await service.createCustomer('test@example.com'); + + expect(mockStripeClient.stripeSDK.customers.create).toHaveBeenCalled(); + expect(result.id).toBe('cus_123'); + }); +}); +``` + +### Testing Console Output + +The LogService outputs to console, which you may want to verify: + +```typescript +it('should log to console', () => { + const consoleSpy = jest.spyOn(console, 'info').mockImplementation(); + + logService.info('Test message'); + + expect(consoleSpy).toHaveBeenCalled(); + const logOutput = JSON.parse(consoleSpy.mock.calls[0][0]); + expect(logOutput.message).toBe('Test message'); + + consoleSpy.mockRestore(); +}); +``` + +## Test Utilities + +### Available Helpers + +Located in `__tests__/helpers/test-utils.ts`: + +```typescript +import { wait, mockTimers, createMockFn } from '../helpers/test-utils'; + +// Wait for a specific time +await wait(1000); + +// Mock timers +mockTimers.enable(); +mockTimers.advance(1000); +mockTimers.runAll(); +mockTimers.disable(); + +// Create type-safe mock functions +const mockFn = createMockFn<(x: number) => string>(); +``` + +### Factory Functions + +Located in `__tests__/helpers/factories.ts`: + +- `createMockStripeCustomer()` +- `createMockStripePaymentMethod()` +- `createMockStripeSubscription()` +- `createMockStripeInvoice()` +- `createMockRootPolicy()` +- `createMockRootPaymentMethod()` +- `createMockRootPayment()` + +## Testing Best Practices + +### 1. Test Behavior, Not Implementation + +โŒ **Bad**: Testing implementation details +```typescript +it('should call private method', () => { + const spy = jest.spyOn(service as any, '_privateMethod'); + service.publicMethod(); + expect(spy).toHaveBeenCalled(); +}); +``` + +โœ… **Good**: Testing observable behavior +```typescript +it('should return processed result', () => { + const result = service.publicMethod(); + expect(result).toEqual({ processed: true }); +}); +``` + +### 2. Use Descriptive Test Names + +โŒ **Bad**: Vague test names +```typescript +it('works', () => { ... }); +it('test 1', () => { ... }); +``` + +โœ… **Good**: Descriptive test names +```typescript +it('should create customer when email is provided', () => { ... }); +it('should throw error when policy ID is invalid', () => { ... }); +``` + +### 3. Follow AAA Pattern + +Always structure tests with Arrange, Act, Assert: + +```typescript +it('should calculate total', () => { + // Arrange - Set up test data + const items = [{ price: 100 }, { price: 200 }]; + + // Act - Execute the code under test + const total = service.calculateTotal(items); + + // Assert - Verify the result + expect(total).toBe(300); +}); +``` + +### 4. Test One Thing at a Time + +โŒ **Bad**: Testing multiple things +```typescript +it('should create and update customer', () => { + const customer = service.create(); + expect(customer).toBeDefined(); + + const updated = service.update(customer.id, { name: 'New' }); + expect(updated.name).toBe('New'); +}); +``` + +โœ… **Good**: Separate tests +```typescript +it('should create customer', () => { + const customer = service.create(); + expect(customer).toBeDefined(); +}); + +it('should update customer name', () => { + const customer = service.create(); + const updated = service.update(customer.id, { name: 'New' }); + expect(updated.name).toBe('New'); +}); +``` + +### 5. Don't Test External Libraries + +Don't test Stripe SDK, Root SDK, or other external libraries. Test YOUR code: + +โŒ **Bad**: Testing Stripe SDK +```typescript +it('should call Stripe API', () => { + expect(stripe.customers.create).toBeDefined(); +}); +``` + +โœ… **Good**: Testing your service +```typescript +it('should create customer using Stripe', async () => { + mockStripe.customers.create.mockResolvedValue({ id: 'cus_123' }); + + const customer = await service.createCustomer('test@example.com'); + + expect(customer.id).toBe('cus_123'); +}); +``` + +### 6. Isolate Tests + +Each test should be independent: + +```typescript +// Good - each test creates its own data +beforeEach(() => { + service = new MyService(); +}); + +it('test 1', () => { + const data = createTestData(); + // use data +}); + +it('test 2', () => { + const data = createTestData(); + // use data +}); +``` + +## Coverage Goals + +Aim for: +- **70%+ overall coverage** +- **80%+ for services** +- **90%+ for critical paths** (payment processing, webhook handling) + +Check coverage: +```bash +npm test:coverage +``` + +View HTML report: +```bash +open coverage/lcov-report/index.html +``` + +## Debugging Tests + +### Run Single Test + +```bash +# Run specific test file +npm test -- log.service.test.ts + +# Run tests matching pattern +npm test -- --testNamePattern="should log debug" +``` + +### Debug in VS Code + +Add to `.vscode/launch.json`: +```json +{ + "type": "node", + "request": "launch", + "name": "Jest Debug", + "program": "${workspaceFolder}/node_modules/.bin/jest", + "args": ["--runInBand", "--no-cache"], + "console": "integratedTerminal" +} +``` + +## Common Issues + +### Issue: Tests timing out + +**Solution**: Increase timeout +```typescript +jest.setTimeout(10000); // 10 seconds +``` + +### Issue: Async tests not waiting + +**Solution**: Always use `async/await` or return promises +```typescript +it('should work', async () => { + await service.asyncMethod(); // Don't forget await! +}); +``` + +### Issue: Mock not working + +**Solution**: Ensure mock is set up before test runs +```typescript +beforeEach(() => { + jest.clearAllMocks(); // Clear previous mock calls + mockService.method.mockResolvedValue('result'); +}); +``` + +## Next Steps + +1. Write tests for new features as you implement them +2. Maintain >70% coverage +3. Run tests before committing +4. Review test failures carefully - they often catch real bugs! + +## Questions? + +- Check existing tests for examples +- See Jest documentation: https://jestjs.io/ +- Ask in team chat \ No newline at end of file diff --git a/stripe_collection_module/docs/UTILS.md b/stripe_collection_module/docs/UTILS.md new file mode 100644 index 0000000..bf1ffe4 --- /dev/null +++ b/stripe_collection_module/docs/UTILS.md @@ -0,0 +1,179 @@ +# Utility Functions + +This directory contains reusable utility functions and helpers used throughout the collection module. + +## Files + +### `error.ts` +Simple error class for collection module errors. + +**Usage:** +```typescript +import ModuleError from './utils/error'; + +throw new ModuleError('Payment failed', { policyId: '123' }); +``` + +### `error-types.ts` +Enhanced error types with categorization and retry logic. + +**Classes:** +- `EnhancedModuleError` - Base error with request tracking +- `ValidationError` - Input validation errors +- `NotFoundError` - Resource not found errors +- `NetworkError` - Network/connectivity errors (retryable) +- `TimeoutError` - Timeout errors (retryable) +- `RateLimitError` - Rate limit errors (retryable) +- `ServerError` - Server-side errors (retryable) + +**Functions:** +- `categorizeError()` - Categorize any error +- `isRetryableError()` - Check if error should be retried +- `formatErrorForLogging()` - Format error for logs + +**Usage:** +```typescript +import { ValidationError, NetworkError } from './utils/error-types'; + +// Non-retryable validation error +throw new ValidationError('Invalid email', { email: user.email }); + +// Retryable network error +throw new NetworkError('Connection failed', { url: apiUrl }); +``` + +### `retry.ts` +Retry logic with exponential backoff and jitter. + +**Usage:** +```typescript +import { retryWithBackoff } from './utils/retry'; + +const result = await retryWithBackoff( + () => stripeClient.getCustomer(customerId), + { + maxRetries: 3, + initialDelay: 1000, + maxDelay: 10000, + shouldRetry: (error) => error.statusCode >= 500, + } +); +``` + +### `timeout.ts` +Add timeout to async operations. + +**Usage:** +```typescript +import { withTimeout } from './utils/timeout'; + +const result = await withTimeout( + slowOperation(), + 5000, // 5 second timeout + 'Operation timed out' +); +``` + +### `logger.ts` +Legacy logger (kept for backwards compatibility). + +**Note:** New code should use `LogService` from the DI container instead. + +```typescript +// โŒ Old approach +import Logger from './utils/logger'; +Logger.info('message'); + +// โœ… New approach +const logService = container.resolve(ServiceToken.LOG_SERVICE); +logService.info('message', 'ComponentName', { context }); +``` + +### `index.ts` +Re-exports commonly used utilities for convenience. + +**Usage:** +```typescript +// Import from utils directly +import { + ModuleError, + retryWithBackoff, + withTimeout +} from './utils'; +``` + +## Best Practices + +### 1. Use Enhanced Errors for Better Error Handling + +```typescript +// โœ… Good - specific error type +throw new ValidationError('Invalid amount', { amount }); + +// โŒ Bad - generic error +throw new Error('Invalid amount'); +``` + +### 2. Add Retry Logic for External APIs + +```typescript +// โœ… Good - retry on transient failures +const customer = await retryWithBackoff( + () => stripeClient.customers.retrieve(customerId), + { maxRetries: 3 } +); + +// โŒ Bad - no retry, fails on transient errors +const customer = await stripeClient.customers.retrieve(customerId); +``` + +### 3. Add Timeouts for Long Operations + +```typescript +// โœ… Good - timeout prevents hanging +const result = await withTimeout( + externalApiCall(), + 30000 // 30 second timeout +); + +// โŒ Bad - could hang indefinitely +const result = await externalApiCall(); +``` + +## Examples + +### Error Handling with Retry + +```typescript +import { retryWithBackoff, NetworkError, isRetryableError } from './utils'; + +try { + const result = await retryWithBackoff( + async () => { + const response = await fetch(apiUrl); + if (!response.ok) { + throw new NetworkError('API call failed', { + status: response.status + }); + } + return response.json(); + }, + { + maxRetries: 3, + shouldRetry: (error) => isRetryableError(error), + } + ); +} catch (error) { + logService.error('Failed after retries', 'ServiceName', { + error: formatErrorForLogging(error), + }); + throw error; +} +``` + +## Related Documentation + +- [Error Handling Best Practices](../../docs/BEST_PRACTICES.md) +- [Testing Guide](../../docs/TESTING.md) +- [Architecture](../../docs/ARCHITECTURE.md) + diff --git a/stripe_collection_module/docs/WEBHOOKS.md b/stripe_collection_module/docs/WEBHOOKS.md new file mode 100644 index 0000000..f38e6c2 --- /dev/null +++ b/stripe_collection_module/docs/WEBHOOKS.md @@ -0,0 +1,678 @@ +# Webhook Setup Guide + +This guide covers setting up and handling webhooks from your payment provider. + +## Overview + +Webhooks allow payment providers to notify your collection module about events like: +- Payment succeeded/failed +- Subscription updated/cancelled +- Refund processed +- Dispute created +- Invoice finalized + +Your collection module receives these webhooks, processes them, and updates Root Platform accordingly. + +--- + +## Architecture + +``` +Payment Provider (Stripe) + โ†“ HTTP POST +Your Lambda Function (API Gateway endpoint) + โ†“ Verify signature +Webhook Handler + โ†“ Route by event type +Event-Specific Controller + โ†“ Process and update +Root Platform API +``` + +--- + +## Setting Up Webhooks + +### Step 1: Deploy Your Lambda Function + +First, deploy your collection module to AWS Lambda (see [DEPLOYMENT.md](./DEPLOYMENT.md)). + +You'll need the API Gateway endpoint URL: +``` +https://xxxxx.execute-api.region.amazonaws.com/default/webhook +``` + +### Step 2: Register Webhook with Provider + +#### Stripe Example + +1. **Go to Stripe Dashboard** + - Navigate to [Developers โ†’ Webhooks](https://dashboard.stripe.com/webhooks) + - Click "Add endpoint" + +2. **Configure Endpoint** + - Endpoint URL: Your API Gateway URL + - Description: "Collection Module Webhooks" + - API Version: Latest + +3. **Select Events** + +Select the events you need to handle: + +**Payment Events:** +- `payment_intent.succeeded` +- `payment_intent.payment_failed` +- `charge.refunded` +- `charge.dispute.created` + +**Invoice Events:** +- `invoice.created` +- `invoice.paid` +- `invoice.payment_failed` +- `invoice.voided` +- `invoice.marked_uncollectible` + +**Subscription Events:** +- `subscription_schedule.updated` +- `customer.subscription.updated` +- `customer.subscription.deleted` + +4. **Get Signing Secret** + - After creating the endpoint, copy the "Signing secret" + - Format: `whsec_xxxxxxxxxxxxx` + - Save this securely + +5. **Update Your Configuration** + +Add the signing secret to your Lambda environment variables: + +```bash +STRIPE_WEBHOOK_SIGNING_SECRET_LIVE=whsec_xxxxx +``` + +Or in `code/env.ts` for local testing: +```typescript +export const STRIPE_WEBHOOK_SIGNING_SECRET_LIVE = 'whsec_xxxxx'; +``` + +### Step 3: Test Webhook Delivery + +1. **Send Test Event from Provider Dashboard** + - In Stripe Dashboard โ†’ Webhooks โ†’ Your endpoint + - Click "Send test webhook" + - Select an event type + - Click "Send test event" + +2. **Check Logs** + +```bash +# AWS CloudWatch +aws logs tail /aws/lambda/your-function-name --follow + +# Or in AWS Console +CloudWatch โ†’ Log Groups โ†’ /aws/lambda/your-function-name +``` + +3. **Verify Response** + - HTTP 200 = Success + - HTTP 400/500 = Error (check logs) + +--- + +## Webhook Security + +### Signature Verification + +**Critical:** Always verify webhook signatures to prevent spoofing. + +#### How It Works + +1. Provider sends webhook with signature header +2. Your code computes expected signature +3. Compare signatures - if they match, webhook is authentic + +#### Implementation + +```typescript +// In webhook-hooks.ts + +import Stripe from 'stripe'; + +export async function processWebhook(event: any) { + const signature = event.headers['stripe-signature']; + const payload = event.body; + + // Verify signature + try { + const webhookEvent = stripe.webhooks.constructEvent( + payload, + signature, + process.env.STRIPE_WEBHOOK_SIGNING_SECRET_LIVE + ); + + // Process the verified event + await handleWebhookEvent(webhookEvent); + + return { + statusCode: 200, + body: JSON.stringify({ received: true }) + }; + } catch (err) { + console.error('Webhook signature verification failed:', err.message); + return { + statusCode: 400, + body: JSON.stringify({ error: 'Invalid signature' }) + }; + } +} +``` + +#### Provider-Specific Verification + +**Stripe:** +```typescript +const event = stripe.webhooks.constructEvent( + payload, + signature, + webhookSecret +); +``` + +**PayPal:** +```typescript +const isValid = paypal.notification.webhookEvent.verify( + headers, + payload, + webhookId +); +``` + +**Square:** +```typescript +const isValid = signature === + crypto.createHmac('sha256', webhookSecret) + .update(payload) + .digest('base64'); +``` + +### Security Best Practices + +- โœ… **Always** verify signatures +- โœ… Use HTTPS endpoints only +- โœ… Validate event structure +- โœ… Log all webhook attempts +- โœ… Rate limit webhook endpoints +- โœ… Use API keys for API Gateway +- โœ… Monitor for suspicious patterns + +--- + +## Handling Webhook Events + +### Event Router Pattern + +```typescript +// In webhook-hooks.ts + +export async function handleWebhookEvent(event: StripeEvent) { + logService.info(`Processing webhook: ${event.type}`, 'WebhookHandler', { + eventId: event.id, + type: event.type + }); + + try { + switch (event.type) { + case 'payment_intent.succeeded': + await processPaymentIntentSucceeded(event); + break; + + case 'payment_intent.payment_failed': + await processPaymentIntentFailed(event); + break; + + case 'invoice.paid': + await processInvoicePaid(event); + break; + + case 'invoice.payment_failed': + await processInvoicePaymentFailed(event); + break; + + case 'charge.refunded': + await processChargeRefunded(event); + break; + + default: + logService.info(`Unhandled event type: ${event.type}`, 'WebhookHandler'); + } + } catch (error) { + logService.error( + `Error processing webhook: ${error.message}`, + 'WebhookHandler', + { eventId: event.id, error } + ); + throw error; // Trigger retry + } +} +``` + +### Event-Specific Controllers + +Create controllers for each event type in `code/controllers/stripe-event-processors/`: + +```typescript +// processInvoicePaidEventController.ts + +export async function processInvoicePaid(event: StripeEvent) { + const invoice = event.data.object as Stripe.Invoice; + + logService.info('Processing invoice.paid event', 'InvoicePaidController', { + invoiceId: invoice.id, + customerId: invoice.customer, + amount: invoice.amount_paid + }); + + try { + // 1. Get policy from Root using metadata + const policyId = invoice.metadata.rootPolicyId; + const policy = await rootService.getPolicy(policyId); + + // 2. Create payment record in Root + await rootService.createPayment({ + policyId: policy.id, + amount: invoice.amount_paid / 100, // Convert cents to dollars + currency: invoice.currency, + status: 'succeeded', + paymentDate: new Date(invoice.status_transitions.paid_at * 1000), + externalId: invoice.payment_intent as string, + metadata: { + invoiceId: invoice.id, + provider: 'stripe' + } + }); + + logService.info('Invoice payment processed successfully', 'InvoicePaidController'); + } catch (error) { + logService.error( + `Failed to process invoice payment: ${error.message}`, + 'InvoicePaidController', + { invoiceId: invoice.id, error } + ); + throw error; + } +} +``` + +--- + +## Local Testing + +### Using Webhook Testing Tools + +#### Option 1: Stripe CLI (Recommended for Stripe) + +```bash +# Install Stripe CLI +brew install stripe/stripe-cli/stripe + +# Login +stripe login + +# Forward webhooks to local endpoint +stripe listen --forward-to http://localhost:3000/webhook + +# Trigger test events +stripe trigger payment_intent.succeeded +stripe trigger invoice.paid +``` + +#### Option 2: ngrok (Works with any provider) + +```bash +# Install ngrok +brew install ngrok + +# Start local server +npm run dev + +# Expose to internet +ngrok http 3000 + +# Use the ngrok URL as webhook endpoint +https://xxxxx.ngrok.io/webhook +``` + +#### Option 3: Mock Webhooks + +Create test files in `__tests__/fixtures/`: + +```typescript +// __tests__/fixtures/stripe-webhooks.ts + +export const mockInvoicePaidEvent = { + id: 'evt_test_123', + type: 'invoice.paid', + data: { + object: { + id: 'in_test_123', + customer: 'cus_test_123', + amount_paid: 5000, + currency: 'usd', + status: 'paid', + metadata: { + rootPolicyId: 'policy_123' + } + } + } +}; +``` + +Then test in your code: + +```typescript +// __tests__/integration/webhook.integration.test.ts + +import { processWebhook } from '../../code/webhook-hooks'; +import { mockInvoicePaidEvent } from '../fixtures/stripe-webhooks'; + +describe('Webhook Processing', () => { + it('should process invoice.paid event', async () => { + const result = await processWebhook({ + body: JSON.stringify(mockInvoicePaidEvent), + headers: { + 'stripe-signature': 'test-signature' + } + }); + + expect(result.statusCode).toBe(200); + }); +}); +``` + +--- + +## Error Handling and Retries + +### Retry Strategy + +Most providers automatically retry failed webhooks: + +**Stripe:** +- Retries for 3 days +- Exponential backoff +- Stops after successful response (2xx) + +**Best Practices:** +- Return 200 immediately after processing +- Use idempotency keys to prevent duplicates +- Log all processing attempts +- Handle duplicate events gracefully + +### Implementing Idempotency + +```typescript +// In your event controller + +const processedEvents = new Set(); + +export async function processInvoicePaid(event: StripeEvent) { + // Check if already processed + if (processedEvents.has(event.id)) { + logService.info('Event already processed, skipping', 'InvoicePaidController', { + eventId: event.id + }); + return; + } + + try { + // Process event + await doActualProcessing(event); + + // Mark as processed + processedEvents.add(event.id); + } catch (error) { + // Don't mark as processed on error - allow retry + throw error; + } +} +``` + +For production, use a persistent store (DynamoDB, Redis) instead of in-memory Set. + +### Error Response Codes + +Return appropriate status codes: + +- **200-299**: Success, don't retry +- **400-499**: Client error, don't retry (except 429) +- **500-599**: Server error, retry + +```typescript +export async function webhookHandler(event: any) { + try { + await processWebhook(event); + return { statusCode: 200, body: 'OK' }; + } catch (error) { + if (error.type === 'validation_error') { + // Don't retry validation errors + return { statusCode: 400, body: error.message }; + } + + // Retry server errors + return { statusCode: 500, body: 'Internal Server Error' }; + } +} +``` + +--- + +## Monitoring Webhooks + +### CloudWatch Metrics + +Track webhook metrics: + +```typescript +// In webhook handler + +import { CloudWatch } from 'aws-sdk'; +const cloudwatch = new CloudWatch(); + +async function recordWebhookMetric(eventType: string, success: boolean) { + await cloudwatch.putMetricData({ + Namespace: 'CollectionModule', + MetricData: [{ + MetricName: 'WebhookProcessed', + Value: success ? 1 : 0, + Unit: 'Count', + Dimensions: [{ + Name: 'EventType', + Value: eventType + }] + }] + }).promise(); +} +``` + +### CloudWatch Alarms + +Create alarms for: +- High error rate +- Slow processing time +- Missing webhooks + +```bash +aws cloudwatch put-metric-alarm \ + --alarm-name webhook-error-rate \ + --alarm-description "Webhook error rate too high" \ + --metric-name WebhookProcessed \ + --namespace CollectionModule \ + --statistic Average \ + --period 300 \ + --threshold 0.9 \ + --comparison-operator LessThanThreshold +``` + +### Webhook Dashboard + +Track webhook health: +- Total webhooks received +- Success/failure rate +- Processing time +- Events by type +- Retry attempts + +--- + +## Debugging Webhooks + +### Common Issues + +#### 1. Signature Verification Failed + +**Symptoms:** +- 400 responses +- "Invalid signature" errors + +**Solutions:** +- Verify webhook secret is correct +- Check you're using raw request body (not parsed JSON) +- Ensure correct header name (`stripe-signature`, `x-square-signature`, etc.) +- Test with provider's CLI tool + +#### 2. Webhooks Timing Out + +**Symptoms:** +- 504 Gateway Timeout +- Provider shows "failed" status + +**Solutions:** +- Increase Lambda timeout +- Optimize slow operations +- Process asynchronously (queue-based) +- Return 200 immediately, process later + +#### 3. Duplicate Events + +**Symptoms:** +- Same event processed multiple times +- Duplicate payments/updates in Root + +**Solutions:** +- Implement idempotency checks +- Use event ID to track processed events +- Check for duplicate external IDs before creating + +#### 4. Missing Webhooks + +**Symptoms:** +- Events happen but webhooks don't arrive +- Provider shows "not delivered" + +**Solutions:** +- Verify endpoint URL is correct and accessible +- Check API Gateway configuration +- Verify Lambda permissions +- Check provider webhook logs + +### Debugging Tips + +1. **Enable Verbose Logging** + +```typescript +logService.debug('Webhook received', 'WebhookHandler', { + headers: event.headers, + body: event.body, + eventType: webhookEvent.type +}); +``` + +2. **Test with Provider Dashboard** + - Send test events + - Check delivery logs + - Review response codes + +3. **Check CloudWatch Logs** + +```bash +aws logs tail /aws/lambda/your-function --follow --filter-pattern "ERROR" +``` + +4. **Use Provider CLI Tools** + +```bash +# Stripe +stripe listen --forward-to http://localhost:3000 +stripe logs tail +``` + +--- + +## Webhook Event Reference + +### Common Events to Handle + +| Event | When to Handle | Action | +|-------|---------------|--------| +| `payment_intent.succeeded` | Payment completed | Create/update payment in Root | +| `payment_intent.payment_failed` | Payment failed | Mark payment as failed in Root | +| `invoice.paid` | Invoice paid | Record payment | +| `invoice.payment_failed` | Invoice payment failed | Update payment status | +| `charge.refunded` | Refund processed | Create refund record | +| `subscription.updated` | Subscription changed | Update policy in Root | +| `subscription.deleted` | Subscription cancelled | Cancel policy in Root | +| `charge.dispute.created` | Dispute opened | Flag payment, notify team | + +### Event Priority + +**High Priority** (process immediately): +- Payment succeeded/failed +- Refunds +- Disputes + +**Medium Priority** (can be delayed): +- Subscription updates +- Invoice created + +**Low Priority** (informational): +- Customer updated +- Payment method updated + +--- + +## Testing Checklist + +- [ ] Webhook endpoint deployed and accessible +- [ ] Signature verification implemented +- [ ] All required events registered with provider +- [ ] Test events sent and processed successfully +- [ ] Error handling tested +- [ ] Idempotency implemented +- [ ] Logging configured +- [ ] CloudWatch alarms set up +- [ ] Retry logic validated +- [ ] Integration with Root Platform tested +- [ ] Documentation updated + +--- + +## Next Steps + +After webhook setup: + +1. **Test End-to-End**: Process real transactions +2. **Monitor Performance**: Watch CloudWatch metrics +3. **Handle Edge Cases**: Test failure scenarios +4. **Document Runbook**: Create incident response guide +5. **Set Up Alerts**: Configure notifications + +--- + +## Resources + +- **Stripe Webhooks**: https://stripe.com/docs/webhooks +- **Webhook Security**: https://stripe.com/docs/webhooks/signatures +- **Stripe CLI**: https://stripe.com/docs/stripe-cli +- **API Gateway**: https://docs.aws.amazon.com/apigateway/ +- **Lambda**: https://docs.aws.amazon.com/lambda/ + diff --git a/stripe_collection_module/jest.config.js b/stripe_collection_module/jest.config.js new file mode 100644 index 0000000..461acac --- /dev/null +++ b/stripe_collection_module/jest.config.js @@ -0,0 +1,41 @@ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + roots: ['/code', '/__tests__'], + testMatch: ['**/__tests__/**/*.test.ts', '**/?(*.)+(spec|test).ts'], + transform: { + '^.+\\.ts$': 'ts-jest', + }, + collectCoverageFrom: [ + 'code/**/*.ts', + '!code/**/*.d.ts', + '!code/env.ts', + '!code/env.sample.ts', + '!code/sample.env.ts', + '!code/main.ts', + '!code/utils/index.ts', + '!code/interfaces/**', + '!code/lifecycle-hooks/index.ts', + ], + coverageDirectory: 'coverage', + coverageReporters: ['text', 'lcov', 'html', 'json-summary'], + coverageThreshold: { + global: { + branches: 70, + functions: 70, + lines: 70, + statements: 70, + }, + }, + moduleFileExtensions: ['ts', 'js', 'json'], + verbose: true, + setupFilesAfterEnv: ['/__tests__/setup.ts'], + testTimeout: 10000, + clearMocks: true, + resetMocks: true, + restoreMocks: true, + + // Note: --forceExit flag is added in package.json scripts to suppress exit warnings + // Uncomment detectOpenHandles below to debug test leaks if needed + // detectOpenHandles: true, +}; diff --git a/stripe_collection_module/package-lock.json b/stripe_collection_module/package-lock.json index ca585ff..aa9ed45 100644 --- a/stripe_collection_module/package-lock.json +++ b/stripe_collection_module/package-lock.json @@ -1,30 +1,39 @@ { - "name": "stripe_collection_module", + "name": "stripe-collection-module", + "version": "1.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { + "name": "stripe-collection-module", + "version": "1.0.0", "dependencies": { "@rootplatform/node-sdk": "^0.0.7", - "joi": "11.4.0", + "joi": "^17.13.3", "moment-timezone": "0.5.40", "node-fetch": "2.6.7", "phone": "2.4.22", - "stripe": "14.5.0" + "stripe": "^19.2.1" }, "devDependencies": { + "@jest/globals": "^30.2.0", + "@types/jest": "^30.0.0", "@types/node": "^20.11.20", "@types/node-fetch": "^2.6.11", - "eslint": "^8.51.0", - "eslint-config-airbnb-typescript": "^17.0.0", - "eslint-config-prettier": "^8.7.0", + "@typescript-eslint/eslint-plugin": "^7.18.0", + "@typescript-eslint/parser": "^7.18.0", + "eslint": "^8.57.0", + "eslint-config-airbnb-typescript": "^18.0.0", + "eslint-config-prettier": "^9.1.0", "eslint-import-resolver-webpack": "^0.13.2", - "eslint-plugin-import": "^2.27.5", - "eslint-plugin-mocha": "^10.1.0", - "eslint-plugin-prettier": "^4.2.1", - "eslint-plugin-sonarjs": "^0.18.0", - "eslint-plugin-unicorn": "^48.0.1", - "prettier": "^2.8.3" + "eslint-plugin-import": "^2.29.1", + "eslint-plugin-mocha": "^10.5.0", + "eslint-plugin-prettier": "^5.2.1", + "eslint-plugin-sonarjs": "^0.25.1", + "eslint-plugin-unicorn": "^55.0.0", + "jest": "^30.2.0", + "prettier": "^3.3.3", + "ts-jest": "^29.4.5" }, "engines": { "node": ">=18.0.0", @@ -125,111 +134,525 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, "node_modules/@babel/code-frame": { - "version": "7.24.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", - "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz", + "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", + "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", + "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", "dev": true, "dependencies": { - "@babel/highlight": "^7.24.2", - "picocolors": "^1.0.0" + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" + }, + "engines": { + "node": ">=6.9.0" }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz", - "integrity": "sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", "dev": true, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/highlight": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.5.tgz", - "integrity": "sha512-8lLmua6AVh/8SLJRRVD6V8p73Hir9w5mJrhE+IPpILG31KKlI9iz5zmBYKcWPS59qSfgP9RaSBQSHHE81WKuEw==", + "node_modules/@babel/helpers": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.24.5", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "node_modules/@babel/parser": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", "dev": true, "dependencies": { - "color-convert": "^1.9.0" + "@babel/types": "^7.28.5" + }, + "bin": { + "parser": "bin/babel-parser.js" }, "engines": { - "node": ">=4" + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", "dev": true, "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "@babel/helper-plugin-utils": "^7.14.5" }, "engines": { - "node": ">=4" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", + "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", "dev": true, "dependencies": { - "color-name": "1.1.3" + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", + "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, "engines": { - "node": ">=0.8.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/highlight/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==", + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, "engines": { - "node": ">=4" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/highlight/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==", + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", "dev": true, "dependencies": { - "has-flag": "^3.0.0" + "@babel/helper-plugin-utils": "^7.14.5" }, "engines": { - "node": ">=4" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", + "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", + "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.5", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "node_modules/@emnapi/core": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.7.0.tgz", + "integrity": "sha512-pJdKGq/1iquWYtv1RRSljZklxHCOCAJFJrImO5ZLKPJVJlVUcs8yFwNQlqS0Lo8xT1VAXXTCZocF9n26FWEKsw==", + "dev": true, + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.1.0", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.0.tgz", + "integrity": "sha512-oAYoQnCYaQZKVS53Fq23ceWMRxq5EhQsE0x0RdQ55jT7wagMu5k+fS39v1fiSLrtrLQlXwVINenqhLMtTrV/1Q==", + "dev": true, + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", + "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", + "dev": true, + "optional": true, + "dependencies": { + "tslib": "^2.4.0" } }, "node_modules/@eslint-community/eslint-utils": { @@ -315,6 +738,19 @@ "resolved": "https://registry.npmjs.org/@exodus/schemasafe/-/schemasafe-1.3.0.tgz", "integrity": "sha512-5Aap/GaRupgNx/feGBwLLTVv8OQFfv3pq2lPRzPg9R+IOBnDgghTGW7l7EuVXOvg5cc/xSAlRW8rBrjIC3Nvqw==" }, + "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==" + }, + "node_modules/@hapi/topo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", + "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.14", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", @@ -370,1317 +806,4161 @@ "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "dev": true }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "dev": true, - "peer": true, "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" }, "engines": { - "node": ">=6.0.0" + "node": ">=12" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", "dev": true, - "peer": true, "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, - "peer": true, - "engines": { - "node": ">=6.0.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", - "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", "dev": true, - "peer": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25" + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true, - "peer": true + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, - "peer": true, "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@jsdevtools/ono": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", - "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==" - }, - "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==", + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", "dev": true, "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" + "ansi-regex": "^6.0.1" }, "engines": { - "node": ">= 8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "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==", + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, "engines": { - "node": ">= 8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "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==", + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", "dev": true, "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" }, "engines": { - "node": ">= 8" + "node": ">=8" } }, - "node_modules/@rootplatform/node-sdk": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/@rootplatform/node-sdk/-/node-sdk-0.0.7.tgz", - "integrity": "sha512-jYPcPQfz7lu2Guea44ZAVNh45elFdLexS4q07LUz8Qv+wUOr7fHbqMo0LI22+fJ61eWMHJ+vYH4dc1XdsCoXQg==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, "dependencies": { - "oazapfts": "^5.1.7" + "sprintf-js": "~1.0.2" } }, - "node_modules/@types/eslint": { - "version": "8.56.10", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz", - "integrity": "sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, - "peer": true, "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/@types/eslint-scope": { - "version": "3.7.7", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", - "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", "dev": true, - "peer": true, "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, - "peer": true + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, - "peer": true - }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "dev": true - }, - "node_modules/@types/node": { - "version": "20.11.20", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.20.tgz", - "integrity": "sha512-7/rR21OS+fq8IyHTgtLkDK949uzsa6n8BkziAKtPVpugIkO6D+/ooXMvzXxDnZrmtXVfjb1bKQafYpb8s89LOg==", "dependencies": { - "undici-types": "~5.26.4" + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@types/node-fetch": { - "version": "2.6.11", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.11.tgz", - "integrity": "sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "dependencies": { - "@types/node": "*", - "form-data": "^4.0.0" + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/@types/normalize-package-data": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", - "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", - "dev": true + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } }, - "node_modules/@types/semver": { - "version": "7.5.8", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", - "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true, - "peer": true + "engines": { + "node": ">=8" + } }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", - "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", + "node_modules/@jest/console": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-30.2.0.tgz", + "integrity": "sha512-+O1ifRjkvYIkBqASKWgLxrpEhQAAE7hY77ALLUufSk5717KfOShg6IbqLmdsLMPdUiFvA2kTs0R7YZy+l0IzZQ==", "dev": true, - "peer": true, "dependencies": { - "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/type-utils": "6.21.0", - "@typescript-eslint/utils": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", - "debug": "^4.3.4", - "graphemer": "^1.4.0", - "ignore": "^5.2.4", - "natural-compare": "^1.4.0", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" + "@jest/types": "30.2.0", + "@types/node": "*", + "chalk": "^4.1.2", + "jest-message-util": "30.2.0", + "jest-util": "30.2.0", + "slash": "^3.0.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/core": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.2.0.tgz", + "integrity": "sha512-03W6IhuhjqTlpzh/ojut/pDB2LPRygyWX8ExpgHtQA8H/3K7+1vKmcINx5UzeOX1se6YEsBsOHQ1CRzf3fOwTQ==", + "dev": true, + "dependencies": { + "@jest/console": "30.2.0", + "@jest/pattern": "30.0.1", + "@jest/reporters": "30.2.0", + "@jest/test-result": "30.2.0", + "@jest/transform": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "exit-x": "^0.2.2", + "graceful-fs": "^4.2.11", + "jest-changed-files": "30.2.0", + "jest-config": "30.2.0", + "jest-haste-map": "30.2.0", + "jest-message-util": "30.2.0", + "jest-regex-util": "30.0.1", + "jest-resolve": "30.2.0", + "jest-resolve-dependencies": "30.2.0", + "jest-runner": "30.2.0", + "jest-runtime": "30.2.0", + "jest-snapshot": "30.2.0", + "jest-util": "30.2.0", + "jest-validate": "30.2.0", + "jest-watcher": "30.2.0", + "micromatch": "^4.0.8", + "pretty-format": "30.2.0", + "slash": "^3.0.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" }, "peerDependencies": { - "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", - "eslint": "^7.0.0 || ^8.0.0" + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" }, "peerDependenciesMeta": { - "typescript": { + "node-notifier": { "optional": true } } }, - "node_modules/@typescript-eslint/parser": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", - "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", + "node_modules/@jest/diff-sequences": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.0.1.tgz", + "integrity": "sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==", + "dev": true, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/environment": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.2.0.tgz", + "integrity": "sha512-/QPTL7OBJQ5ac09UDRa3EQes4gt1FTEG/8jZ/4v5IVzx+Cv7dLxlVIvfvSVRiiX2drWyXeBjkMSR8hvOWSog5g==", "dev": true, - "peer": true, "dependencies": { - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/typescript-estree": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", - "debug": "^4.3.4" + "@jest/fake-timers": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "jest-mock": "30.2.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.2.0.tgz", + "integrity": "sha512-V9yxQK5erfzx99Sf+7LbhBwNWEZ9eZay8qQ9+JSC0TrMR1pMDHLMY+BnVPacWU6Jamrh252/IKo4F1Xn/zfiqA==", + "dev": true, + "dependencies": { + "expect": "30.2.0", + "jest-snapshot": "30.2.0" }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.2.0.tgz", + "integrity": "sha512-1JnRfhqpD8HGpOmQp180Fo9Zt69zNtC+9lR+kT7NVL05tNXIi+QC8Csz7lfidMoVLPD3FnOtcmp0CEFnxExGEA==", + "dev": true, + "dependencies": { + "@jest/get-type": "30.1.0" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", - "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", + "node_modules/@jest/fake-timers": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.2.0.tgz", + "integrity": "sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw==", "dev": true, - "peer": true, "dependencies": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0" + "@jest/types": "30.2.0", + "@sinonjs/fake-timers": "^13.0.0", + "@types/node": "*", + "jest-message-util": "30.2.0", + "jest-mock": "30.2.0", + "jest-util": "30.2.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/get-type": { + "version": "30.1.0", + "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz", + "integrity": "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==", + "dev": true, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.2.0.tgz", + "integrity": "sha512-b63wmnKPaK+6ZZfpYhz9K61oybvbI1aMcIs80++JI1O1rR1vaxHUCNqo3ITu6NU0d4V34yZFoHMn/uoKr/Rwfw==", + "dev": true, + "dependencies": { + "@jest/environment": "30.2.0", + "@jest/expect": "30.2.0", + "@jest/types": "30.2.0", + "jest-mock": "30.2.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@typescript-eslint/type-utils": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", - "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", + "node_modules/@jest/pattern": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.0.1.tgz", + "integrity": "sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==", "dev": true, - "peer": true, "dependencies": { - "@typescript-eslint/typescript-estree": "6.21.0", - "@typescript-eslint/utils": "6.21.0", - "debug": "^4.3.4", - "ts-api-utils": "^1.0.1" + "@types/node": "*", + "jest-regex-util": "30.0.1" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-30.2.0.tgz", + "integrity": "sha512-DRyW6baWPqKMa9CzeiBjHwjd8XeAyco2Vt8XbcLFjiwCOEKOvy82GJ8QQnJE9ofsxCMPjH4MfH8fCWIHHDKpAQ==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "30.2.0", + "@jest/test-result": "30.2.0", + "@jest/transform": "30.2.0", + "@jest/types": "30.2.0", + "@jridgewell/trace-mapping": "^0.3.25", + "@types/node": "*", + "chalk": "^4.1.2", + "collect-v8-coverage": "^1.0.2", + "exit-x": "^0.2.2", + "glob": "^10.3.10", + "graceful-fs": "^4.2.11", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^5.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "30.2.0", + "jest-util": "30.2.0", + "jest-worker": "30.2.0", + "slash": "^3.0.0", + "string-length": "^4.0.2", + "v8-to-istanbul": "^9.0.1" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" }, "peerDependenciesMeta": { - "typescript": { + "node-notifier": { "optional": true } } }, - "node_modules/@typescript-eslint/types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", - "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", + "node_modules/@jest/reporters/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dev": true, - "peer": true, - "engines": { - "node": "^16.0.0 || >=18.0.0" + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", - "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", + "node_modules/@jest/reporters/node_modules/jest-worker": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.2.0.tgz", + "integrity": "sha512-0Q4Uk8WF7BUwqXHuAjc23vmopWJw5WH7w2tqBoUOZpOjW/ZnR44GXXd1r82RvnmI2GZge3ivrYXk/BE2+VtW2g==", "dev": true, - "peer": true, "dependencies": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "9.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" + "@types/node": "*", + "@ungap/structured-clone": "^1.3.0", + "jest-util": "30.2.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.1.1" }, "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@typescript-eslint/utils": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", - "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", + "node_modules/@jest/reporters/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, - "peer": true, "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/typescript-estree": "6.21.0", - "semver": "^7.5.4" + "has-flag": "^4.0.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" + "node": ">=10" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", - "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", + "node_modules/@jest/schemas": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", + "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", "dev": true, - "peer": true, "dependencies": { - "@typescript-eslint/types": "6.21.0", - "eslint-visitor-keys": "^3.4.1" + "@sinclair/typebox": "^0.34.0" }, "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "dev": true - }, - "node_modules/@webassemblyjs/ast": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", - "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", + "node_modules/@jest/snapshot-utils": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.2.0.tgz", + "integrity": "sha512-0aVxM3RH6DaiLcjj/b0KrIBZhSX1373Xci4l3cW5xiUWPctZ59zQ7jj4rqcJQ/Z8JuN/4wX3FpJSa3RssVvCug==", "dev": true, - "peer": true, "dependencies": { - "@webassemblyjs/helper-numbers": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6" + "@jest/types": "30.2.0", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "natural-compare": "^1.4.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", - "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", - "dev": true, - "peer": true - }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", - "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", - "dev": true, - "peer": true - }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz", - "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==", - "dev": true, - "peer": true - }, - "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", - "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", + "node_modules/@jest/source-map": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-30.0.1.tgz", + "integrity": "sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==", "dev": true, - "peer": true, "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.11.6", - "@webassemblyjs/helper-api-error": "1.11.6", - "@xtuc/long": "4.2.2" + "@jridgewell/trace-mapping": "^0.3.25", + "callsites": "^3.1.0", + "graceful-fs": "^4.2.11" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", - "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", - "dev": true, - "peer": true - }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz", - "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==", + "node_modules/@jest/test-result": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.2.0.tgz", + "integrity": "sha512-RF+Z+0CCHkARz5HT9mcQCBulb1wgCP3FBvl9VFokMX27acKphwyQsNuWH3c+ojd1LeWBLoTYoxF0zm6S/66mjg==", "dev": true, - "peer": true, "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/wasm-gen": "1.12.1" + "@jest/console": "30.2.0", + "@jest/types": "30.2.0", + "@types/istanbul-lib-coverage": "^2.0.6", + "collect-v8-coverage": "^1.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", - "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", + "node_modules/@jest/test-sequencer": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.2.0.tgz", + "integrity": "sha512-wXKgU/lk8fKXMu/l5Hog1R61bL4q5GCdT6OJvdAFz1P+QrpoFuLU68eoKuVc4RbrTtNnTL5FByhWdLgOPSph+Q==", "dev": true, - "peer": true, "dependencies": { - "@xtuc/ieee754": "^1.2.0" + "@jest/test-result": "30.2.0", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.2.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", - "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", + "node_modules/@jest/transform": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.2.0.tgz", + "integrity": "sha512-XsauDV82o5qXbhalKxD7p4TZYYdwcaEXC77PPD2HixEFF+6YGppjrAAQurTl2ECWcEomHBMMNS9AH3kcCFx8jA==", "dev": true, - "peer": true, "dependencies": { - "@xtuc/long": "4.2.2" + "@babel/core": "^7.27.4", + "@jest/types": "30.2.0", + "@jridgewell/trace-mapping": "^0.3.25", + "babel-plugin-istanbul": "^7.0.1", + "chalk": "^4.1.2", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.2.0", + "jest-regex-util": "30.0.1", + "jest-util": "30.2.0", + "micromatch": "^4.0.8", + "pirates": "^4.0.7", + "slash": "^3.0.0", + "write-file-atomic": "^5.0.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", - "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", + "node_modules/@jest/types": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", + "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", "dev": true, - "peer": true + "dependencies": { + "@jest/pattern": "30.0.1", + "@jest/schemas": "30.0.5", + "@types/istanbul-lib-coverage": "^2.0.6", + "@types/istanbul-reports": "^3.0.4", + "@types/node": "*", + "@types/yargs": "^17.0.33", + "chalk": "^4.1.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz", - "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==", + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "dev": true, - "peer": true, "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/helper-wasm-section": "1.12.1", - "@webassemblyjs/wasm-gen": "1.12.1", - "@webassemblyjs/wasm-opt": "1.12.1", - "@webassemblyjs/wasm-parser": "1.12.1", - "@webassemblyjs/wast-printer": "1.12.1" + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" } }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz", - "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==", + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", "dev": true, - "peer": true, "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" } }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz", - "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==", + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, - "peer": true, - "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/wasm-gen": "1.12.1", - "@webassemblyjs/wasm-parser": "1.12.1" + "engines": { + "node": ">=6.0.0" } }, - "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz", - "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==", + "node_modules/@jridgewell/source-map": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", "dev": true, "peer": true, "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-api-error": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" } }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz", - "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==", + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "dev": true, - "peer": true, "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@xtuc/long": "4.2.2" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true, - "peer": true + "node_modules/@jsdevtools/ono": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", + "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==" }, - "node_modules/@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", + "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", "dev": true, - "peer": true + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@tybys/wasm-util": "^0.10.0" + } }, - "node_modules/acorn": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", - "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "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, - "bin": { - "acorn": "bin/acorn" + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" }, "engines": { - "node": ">=0.4.0" + "node": ">= 8" } }, - "node_modules/acorn-import-assertions": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", - "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", + "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, - "peer": true, - "peerDependencies": { - "acorn": "^8" + "engines": { + "node": ">= 8" } }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "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, "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "engines": { + "node": ">= 8" } }, - "node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", "dev": true, - "peer": true, - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "optional": true, "engines": { - "node": ">=8" + "node": ">=14" } }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, + "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, "engines": { - "node": ">=8" + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://opencollective.com/pkgr" } }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "node_modules/@rootplatform/node-sdk": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/@rootplatform/node-sdk/-/node-sdk-0.0.7.tgz", + "integrity": "sha512-jYPcPQfz7lu2Guea44ZAVNh45elFdLexS4q07LUz8Qv+wUOr7fHbqMo0LI22+fJ61eWMHJ+vYH4dc1XdsCoXQg==", + "dependencies": { + "oazapfts": "^5.1.7" + } + }, + "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==", + "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==" + }, + "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==" + }, + "node_modules/@sinclair/typebox": { + "version": "0.34.41", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz", + "integrity": "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==", "dev": true }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", - "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.5", - "is-array-buffer": "^3.0.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "type-detect": "4.0.8" } }, - "node_modules/array-includes": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", - "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", + "node_modules/@sinonjs/fake-timers": { + "version": "13.0.5", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz", + "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.4", - "is-string": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "@sinonjs/commons": "^3.0.1" } }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", "dev": true, - "peer": true, - "engines": { - "node": ">=8" + "optional": true, + "dependencies": { + "tslib": "^2.4.0" } }, - "node_modules/array.prototype.find": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/array.prototype.find/-/array.prototype.find-2.2.3.tgz", - "integrity": "sha512-fO/ORdOELvjbbeIfZfzrXFMhYHGofRGqd+am9zm3tZ4GlJINj/pA2eITyfd65Vg6+ZbHd/Cys7stpoRSWtQFdA==", + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" } }, - "node_modules/array.prototype.findlastindex": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz", - "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "@babel/types": "^7.0.0" } }, - "node_modules/array.prototype.flat": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", - "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" } }, - "node_modules/array.prototype.flatmap": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", - "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "@babel/types": "^7.28.2" } }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", - "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", + "node_modules/@types/eslint": { + "version": "8.56.10", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz", + "integrity": "sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==", "dev": true, + "peer": true, "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.2.1", - "get-intrinsic": "^1.2.3", - "is-array-buffer": "^3.0.4", - "is-shared-array-buffer": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "@types/estree": "*", + "@types/json-schema": "*" } }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true - }, - "node_modules/available-typed-arrays": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", "dev": true, + "peer": true, "dependencies": { - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "@types/eslint": "*", + "@types/estree": "*" } }, - "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==", + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true, + "peer": true + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", "dev": true }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", "dev": true, - "peer": true, "dependencies": { - "balanced-match": "^1.0.0" + "@types/istanbul-lib-coverage": "*" } }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", "dev": true, - "peer": true, "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" + "@types/istanbul-lib-report": "*" } }, - "node_modules/browserslist": { - "version": "4.23.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", - "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", + "node_modules/@types/jest": { + "version": "30.0.0", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-30.0.0.tgz", + "integrity": "sha512-XTYugzhuwqWjws0CVz8QpM36+T+Dz5mTEBKhNs/esGLnCIlGdRy+Dq78NRjd7ls7r8BC8ZRMOrKlkO1hU0JOwA==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "peer": true, "dependencies": { - "caniuse-lite": "^1.0.30001587", - "electron-to-chromium": "^1.4.668", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + "expect": "^30.0.0", + "pretty-format": "^30.0.0" } }, - "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==", + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true, "peer": true }, - "node_modules/builtin-modules": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", - "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", - "dev": true, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.11.20", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.20.tgz", + "integrity": "sha512-7/rR21OS+fq8IyHTgtLkDK949uzsa6n8BkziAKtPVpugIkO6D+/ooXMvzXxDnZrmtXVfjb1bKQafYpb8s89LOg==", + "devOptional": true, + "dependencies": { + "undici-types": "~5.26.4" } }, - "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "node_modules/@types/node-fetch": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.11.tgz", + "integrity": "sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==", + "dev": true, "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "@types/node": "*", + "form-data": "^4.0.0" } }, - "node_modules/call-me-maybe": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz", - "integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==" + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "dev": true }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true + }, + "node_modules/@types/yargs": { + "version": "17.0.34", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.34.tgz", + "integrity": "sha512-KExbHVa92aJpw9WDQvzBaGVE2/Pz+pLZQloT2hjL8IqsZnV62rlPOYvNnLmf/L2dyllfVUOVBj64M0z/46eR2A==", "dev": true, - "engines": { - "node": ">=6" + "dependencies": { + "@types/yargs-parser": "*" } }, - "node_modules/caniuse-lite": { - "version": "1.0.30001620", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001620.tgz", - "integrity": "sha512-WJvYsOjd1/BYUY6SNGUosK9DUidBPDTnOARHp3fSmFO1ekdxaY6nKRttEVrfMmYi80ctS0kz1wiWmm14fVc3ew==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "peer": true + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz", + "integrity": "sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/type-utils": "7.18.0", + "@typescript-eslint/utils": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.0" }, "engines": { - "node": ">=10" + "node": "^18.18.0 || >=20.0.0" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^7.0.0", + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/chrome-trace-event": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "node_modules/@typescript-eslint/parser": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.18.0.tgz", + "integrity": "sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==", "dev": true, - "peer": true, + "dependencies": { + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/typescript-estree": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "debug": "^4.3.4" + }, "engines": { - "node": ">=6.0" + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "node_modules/@typescript-eslint/scope-manager": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz", + "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0" + }, "engines": { - "node": ">=8" + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/clean-regexp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/clean-regexp/-/clean-regexp-1.0.0.tgz", - "integrity": "sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==", + "node_modules/@typescript-eslint/type-utils": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.18.0.tgz", + "integrity": "sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==", "dev": true, "dependencies": { - "escape-string-regexp": "^1.0.5" + "@typescript-eslint/typescript-estree": "7.18.0", + "@typescript-eslint/utils": "7.18.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" }, "engines": { - "node": ">=4" + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/clean-regexp/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "node_modules/@typescript-eslint/types": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz", + "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==", "dev": true, "engines": { - "node": ">=0.8.0" + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "node_modules/@typescript-eslint/typescript-estree": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz", + "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==", + "dev": true, "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" }, "engines": { - "node": ">=12" + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/@typescript-eslint/utils": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz", + "integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==", + "dev": true, "dependencies": { - "color-name": "~1.1.4" + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/typescript-estree": "7.18.0" }, "engines": { - "node": ">=7.0.0" + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" } }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "node_modules/@typescript-eslint/visitor-keys": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz", + "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==", "dev": true, "dependencies": { - "delayed-stream": "~1.0.0" + "@typescript-eslint/types": "7.18.0", + "eslint-visitor-keys": "^3.4.3" }, "engines": { - "node": ">= 0.8" + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true, - "peer": true - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", "dev": true }, - "node_modules/confusing-browser-globals": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", - "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==", - "dev": true + "node_modules/@unrs/resolver-binding-android-arm-eabi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", + "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "node_modules/@unrs/resolver-binding-android-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", + "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } + "optional": true, + "os": [ + "android" + ] }, - "node_modules/data-view-buffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", - "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", + "node_modules/@unrs/resolver-binding-darwin-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", + "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "optional": true, + "os": [ + "darwin" + ] }, - "node_modules/data-view-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", - "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", + "node_modules/@unrs/resolver-binding-darwin-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz", + "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "optional": true, + "os": [ + "darwin" + ] }, - "node_modules/data-view-byte-offset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", - "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", + "node_modules/@unrs/resolver-binding-freebsd-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz", + "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "optional": true, + "os": [ + "freebsd" + ] }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz", + "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==", + "cpu": [ + "arm" + ], "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true + "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz", + "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz", + "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "node_modules/@unrs/resolver-binding-linux-arm64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz", + "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz", + "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz", + "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz", + "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz", + "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz", + "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz", + "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-wasm32-wasi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz", + "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==", + "cpu": [ + "wasm32" + ], "dev": true, + "optional": true, "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" + "@napi-rs/wasm-runtime": "^0.2.11" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=14.0.0" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz", + "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==", + "cpu": [ + "arm64" + ], "dev": true, - "engines": { - "node": ">=0.4.0" - } + "optional": true, + "os": [ + "win32" + ] }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz", + "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-x64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz", + "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", + "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", "dev": true, "peer": true, "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" + "@webassemblyjs/helper-numbers": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6" } }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", + "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } + "peer": true }, - "node_modules/electron-to-chromium": { - "version": "1.4.776", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.776.tgz", - "integrity": "sha512-s694bi3+gUzlliqxjPHpa9NRTlhzTgB34aan+pVKZmOTGy2xoZXl+8E1B8i5p5rtev3PKMK/H4asgNejC+YHNg==", + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", + "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", "dev": true, "peer": true }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz", + "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==", + "dev": true, + "peer": true }, - "node_modules/enhanced-resolve": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-0.9.1.tgz", - "integrity": "sha512-kxpoMgrdtkXZ5h0SeraBS1iRntpTpQ3R8ussdb38+UAFnMGX5DDyJXePm+OCHOcoXvHDw7mc2erbJBpDnl7TPw==", + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", + "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", "dev": true, + "peer": true, "dependencies": { - "graceful-fs": "^4.1.2", - "memory-fs": "^0.2.0", - "tapable": "^0.1.8" - }, - "engines": { - "node": ">=0.6" + "@webassemblyjs/floating-point-hex-parser": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@xtuc/long": "4.2.2" } }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", + "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", "dev": true, - "dependencies": { - "is-arrayish": "^0.2.1" - } + "peer": true }, - "node_modules/es-abstract": { - "version": "1.23.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", - "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz", + "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==", "dev": true, + "peer": true, "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "arraybuffer.prototype.slice": "^1.0.3", - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "data-view-buffer": "^1.0.1", - "data-view-byte-length": "^1.0.1", + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/wasm-gen": "1.12.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", + "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", + "dev": true, + "peer": true, + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", + "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", + "dev": true, + "peer": true, + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", + "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", + "dev": true, + "peer": true + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz", + "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==", + "dev": true, + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/helper-wasm-section": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-opt": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1", + "@webassemblyjs/wast-printer": "1.12.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz", + "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==", + "dev": true, + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz", + "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==", + "dev": true, + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz", + "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==", + "dev": true, + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz", + "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==", + "dev": true, + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true, + "peer": true + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true, + "peer": true + }, + "node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-assertions": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", + "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", + "dev": true, + "peer": true, + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peer": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "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, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", + "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", + "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/array.prototype.find": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/array.prototype.find/-/array.prototype.find-2.2.3.tgz", + "integrity": "sha512-fO/ORdOELvjbbeIfZfzrXFMhYHGofRGqd+am9zm3tZ4GlJINj/pA2eITyfd65Vg6+ZbHd/Cys7stpoRSWtQFdA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz", + "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", + "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", + "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", + "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.2.1", + "get-intrinsic": "^1.2.3", + "is-array-buffer": "^3.0.4", + "is-shared-array-buffer": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/babel-jest": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.2.0.tgz", + "integrity": "sha512-0YiBEOxWqKkSQWL9nNGGEgndoeL0ZpWrbLMNL5u/Kaxrli3Eaxlt3ZtIDktEvXt4L/R9r3ODr2zKwGM/2BjxVw==", + "dev": true, + "dependencies": { + "@jest/transform": "30.2.0", + "@types/babel__core": "^7.20.5", + "babel-plugin-istanbul": "^7.0.1", + "babel-preset-jest": "30.2.0", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.11.0 || ^8.0.0-0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-7.0.1.tgz", + "integrity": "sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-instrument": "^6.0.2", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.2.0.tgz", + "integrity": "sha512-ftzhzSGMUnOzcCXd6WHdBGMyuwy15Wnn0iyyWGKgBDLxf9/s5ABuraCSpBX2uG0jUg4rqJnxsLc5+oYBqoxVaA==", + "dev": true, + "dependencies": { + "@types/babel__core": "^7.20.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", + "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", + "dev": true, + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/babel-preset-jest": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-30.2.0.tgz", + "integrity": "sha512-US4Z3NOieAQumwFnYdUWKvUKh8+YSnS/gB3t6YBiz0bskpu7Pine8pPCheNxlPEW4wnUkma2a94YuW2q3guvCQ==", + "dev": true, + "dependencies": { + "babel-plugin-jest-hoist": "30.2.0", + "babel-preset-current-node-syntax": "^1.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.11.0 || ^8.0.0-beta.1" + } + }, + "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==", + "dev": true + }, + "node_modules/baseline-browser-mapping": { + "version": "2.8.24", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.24.tgz", + "integrity": "sha512-uUhTRDPXamakPyghwrUcjaGvvBqGrWvBHReoiULMIpOJVM9IYzQh83Xk2Onx5HlGI2o10NNCzcs9TG/S3TkwrQ==", + "dev": true, + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.27.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.27.0.tgz", + "integrity": "sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "baseline-browser-mapping": "^2.8.19", + "caniuse-lite": "^1.0.30001751", + "electron-to-chromium": "^1.5.238", + "node-releases": "^2.0.26", + "update-browserslist-db": "^1.1.4" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "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==", + "dev": true + }, + "node_modules/builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-me-maybe": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz", + "integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==" + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001753", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001753.tgz", + "integrity": "sha512-Bj5H35MD/ebaOV4iDLqPEtiliTN29qkGtEHCwawWn4cYm+bPJM2NsaP30vtZcnERClMzp52J4+aw2UNbK4o+zw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/ci-info": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.1.tgz", + "integrity": "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.1.0.tgz", + "integrity": "sha512-UX0OwmYRYQQetfrLEZeewIFFI+wSTofC+pMBLNuH3RUuu/xzG1oz84UCEDOSoQlN3fZ4+AzmV50ZYvGqkMh9yA==", + "dev": true + }, + "node_modules/clean-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clean-regexp/-/clean-regexp-1.0.0.tgz", + "integrity": "sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/clean-regexp/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", + "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==", + "dev": true + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "peer": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/confusing-browser-globals": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", + "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==", + "dev": true + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/core-js-compat": { + "version": "3.46.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.46.0.tgz", + "integrity": "sha512-p9hObIIEENxSV8xIu+V68JjSeARg6UVMG5mR+JEUguG3sI6MsiS1njz2jHmyJDvA+8jX/sytkBHup6kxhM9law==", + "dev": true, + "dependencies": { + "browserslist": "^4.26.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/data-view-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", + "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", + "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", + "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/dedent": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.0.tgz", + "integrity": "sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ==", + "dev": true, + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/electron-to-chromium": { + "version": "1.5.245", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.245.tgz", + "integrity": "sha512-rdmGfW47ZhL/oWEJAY4qxRtdly2B98ooTJ0pdEI4jhVLZ6tNf8fPtov2wS1IRKwFJT92le3x4Knxiwzl7cPPpQ==", + "dev": true + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/enhanced-resolve": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-0.9.1.tgz", + "integrity": "sha512-kxpoMgrdtkXZ5h0SeraBS1iRntpTpQ3R8ussdb38+UAFnMGX5DDyJXePm+OCHOcoXvHDw7mc2erbJBpDnl7TPw==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.2.0", + "tapable": "^0.1.8" + }, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.23.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", + "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "arraybuffer.prototype.slice": "^1.0.3", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "data-view-buffer": "^1.0.1", + "data-view-byte-length": "^1.0.1", "data-view-byte-offset": "^1.0.0", "es-define-property": "^1.0.0", "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-set-tostringtag": "^2.0.3", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.4", - "get-symbol-description": "^1.0.2", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.3", - "has-symbols": "^1.0.3", - "hasown": "^2.0.2", - "internal-slot": "^1.0.7", - "is-array-buffer": "^3.0.4", - "is-callable": "^1.2.7", - "is-data-view": "^1.0.1", - "is-negative-zero": "^2.0.3", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.3", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.13", - "is-weakref": "^1.0.2", - "object-inspect": "^1.13.1", - "object-keys": "^1.1.1", - "object.assign": "^4.1.5", - "regexp.prototype.flags": "^1.5.2", - "safe-array-concat": "^1.1.2", - "safe-regex-test": "^1.0.3", - "string.prototype.trim": "^1.2.9", - "string.prototype.trimend": "^1.0.8", - "string.prototype.trimstart": "^1.0.8", - "typed-array-buffer": "^1.0.2", - "typed-array-byte-length": "^1.0.1", - "typed-array-byte-offset": "^1.0.2", - "typed-array-length": "^1.0.6", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.15" + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.0.3", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.6", + "get-intrinsic": "^1.2.4", + "get-symbol-description": "^1.0.2", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.0.3", + "has-symbols": "^1.0.3", + "hasown": "^2.0.2", + "internal-slot": "^1.0.7", + "is-array-buffer": "^3.0.4", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.1", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.3", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.13", + "is-weakref": "^1.0.2", + "object-inspect": "^1.13.1", + "object-keys": "^1.1.1", + "object.assign": "^4.1.5", + "regexp.prototype.flags": "^1.5.2", + "safe-array-concat": "^1.1.2", + "safe-regex-test": "^1.0.3", + "string.prototype.trim": "^1.2.9", + "string.prototype.trimend": "^1.0.8", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.2", + "typed-array-byte-length": "^1.0.1", + "typed-array-byte-offset": "^1.0.2", + "typed-array-length": "^1.0.6", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.15" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.3.tgz", + "integrity": "sha512-i1gCgmR9dCl6Vil6UKPI/trA69s08g/syhiDK9TG0Nf1RJjjFI+AzoWW7sPufzkgYAn861skuCwJa0pIIHYxvg==", + "dev": true, + "peer": true + }, + "node_modules/es-object-atoms": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", + "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", + "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.4", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", + "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es6-promise": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", + "integrity": "sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==" + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.0", + "@humanwhocodes/config-array": "^0.11.14", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-airbnb-base": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz", + "integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==", + "dev": true, + "dependencies": { + "confusing-browser-globals": "^1.0.10", + "object.assign": "^4.1.2", + "object.entries": "^1.1.5", + "semver": "^6.3.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "peerDependencies": { + "eslint": "^7.32.0 || ^8.2.0", + "eslint-plugin-import": "^2.25.2" + } + }, + "node_modules/eslint-config-airbnb-base/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-config-airbnb-typescript": { + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-typescript/-/eslint-config-airbnb-typescript-18.0.0.tgz", + "integrity": "sha512-oc+Lxzgzsu8FQyFVa4QFaVKiitTYiiW3frB9KYW5OWdPrqFc7FzxgB20hP4cHMlr+MBzGcLl3jnCOVOydL9mIg==", + "dev": true, + "dependencies": { + "eslint-config-airbnb-base": "^15.0.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^7.0.0", + "@typescript-eslint/parser": "^7.0.0", + "eslint": "^8.56.0" + } + }, + "node_modules/eslint-config-prettier": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.2.tgz", + "integrity": "sha512-iI1f+D2ViGn+uvv5HuHVUamg8ll4tN+JRHGc6IJi4TP9Kl976C57fzPXgseXNs8v0iA8aSJpHsTWjDb9QJamGQ==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "dev": true, + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/eslint-import-resolver-webpack": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-webpack/-/eslint-import-resolver-webpack-0.13.8.tgz", + "integrity": "sha512-Y7WIaXWV+Q21Rz/PJgUxiW/FTBOWmU8NTLdz+nz9mMoiz5vAev/fOaQxwD7qRzTfE3HSm1qsxZ5uRd7eX+VEtA==", + "dev": true, + "dependencies": { + "array.prototype.find": "^2.2.2", + "debug": "^3.2.7", + "enhanced-resolve": "^0.9.1", + "find-root": "^1.1.0", + "hasown": "^2.0.0", + "interpret": "^1.4.0", + "is-core-module": "^2.13.1", + "is-regex": "^1.1.4", + "lodash": "^4.17.21", + "resolve": "^2.0.0-next.5", + "semver": "^5.7.2" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "eslint-plugin-import": ">=1.4.0", + "webpack": ">=1.11.0" + } + }, + "node_modules/eslint-import-resolver-webpack/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-import-resolver-webpack/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.1.tgz", + "integrity": "sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==", + "dev": true, + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz", + "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.7", + "array.prototype.findlastindex": "^1.2.3", + "array.prototype.flat": "^1.3.2", + "array.prototype.flatmap": "^1.3.2", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.8.0", + "hasown": "^2.0.0", + "is-core-module": "^2.13.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.7", + "object.groupby": "^1.0.1", + "object.values": "^1.1.7", + "semver": "^6.3.1", + "tsconfig-paths": "^3.15.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + } + }, + "node_modules/eslint-plugin-import/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-import/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, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint-plugin-import/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-mocha": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-10.5.0.tgz", + "integrity": "sha512-F2ALmQVPT1GoP27O1JTZGrV9Pqg8k79OeIuvw63UxMtQKREZtmkK1NFgkZQ2TW7L2JSSFKHFPTtHu5z8R9QNRw==", + "dev": true, + "dependencies": { + "eslint-utils": "^3.0.0", + "globals": "^13.24.0", + "rambda": "^7.4.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.4.tgz", + "integrity": "sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.11.7" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-sonarjs": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-0.25.1.tgz", + "integrity": "sha512-5IOKvj/GMBNqjxBdItfotfRHo7w48496GOu1hxdeXuD0mB1JBlDCViiLHETDTfA8pDAVSBimBEQoetRXYceQEw==", + "dev": true, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/eslint-plugin-unicorn": { + "version": "55.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-55.0.0.tgz", + "integrity": "sha512-n3AKiVpY2/uDcGrS3+QsYDkjPfaOrNrsfQxU9nt5nitd9KuvVXrfAvgCO9DYPSfap+Gqjw9EOrXIsBp5tlHZjA==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.24.5", + "@eslint-community/eslint-utils": "^4.4.0", + "ci-info": "^4.0.0", + "clean-regexp": "^1.0.0", + "core-js-compat": "^3.37.0", + "esquery": "^1.5.0", + "globals": "^15.7.0", + "indent-string": "^4.0.0", + "is-builtin-module": "^3.2.1", + "jsesc": "^3.0.2", + "pluralize": "^8.0.0", + "read-pkg-up": "^7.0.1", + "regexp-tree": "^0.1.27", + "regjsparser": "^0.10.0", + "semver": "^7.6.1", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=18.18" + }, + "funding": { + "url": "https://github.com/sindresorhus/eslint-plugin-unicorn?sponsor=1" + }, + "peerDependencies": { + "eslint": ">=8.56.0" + } + }, + "node_modules/eslint-plugin-unicorn/node_modules/globals": { + "version": "15.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", + "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "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, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/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, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit-x": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/exit-x/-/exit-x-0.2.2.tgz", + "integrity": "sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-30.2.0.tgz", + "integrity": "sha512-u/feCi0GPsI+988gU2FLcsHyAHTU0MX1Wg68NhAnN7z/+C5wqG+CY8J53N9ioe8RXgaoz0nBR/TYMf3AycUuPw==", + "dev": true, + "dependencies": { + "@jest/expect-utils": "30.2.0", + "@jest/get-type": "30.1.0", + "jest-matcher-utils": "30.2.0", + "jest-message-util": "30.2.0", + "jest-mock": "30.2.0", + "jest-util": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "dev": true + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "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==", + "dev": true + }, + "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, + "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", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", + "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", + "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true, + "peer": true + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/glob/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, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "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 + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/http2-client": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/http2-client/-/http2-client-1.3.5.tgz", + "integrity": "sha512-EC2utToWl4RKfs5zd36Mxq7nzHHBuomZboI0yYL6Y0RmBgT7Sgkq4rQ0ezFTYoIsSs7Tm9SJe+o2FcAg6GBhGA==" + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/ignore": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/internal-slot": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", + "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.0", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", + "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-builtin-module": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", + "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", + "dev": true, + "dependencies": { + "builtin-modules": "^3.3.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", + "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", + "dev": true, + "dependencies": { + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -1689,755 +4969,1014 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, "dependencies": { - "get-intrinsic": "^1.2.4" + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "node_modules/is-shared-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", + "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7" + }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es-module-lexer": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.3.tgz", - "integrity": "sha512-i1gCgmR9dCl6Vil6UKPI/trA69s08g/syhiDK9TG0Nf1RJjjFI+AzoWW7sPufzkgYAn861skuCwJa0pIIHYxvg==", + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true, - "peer": true + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "node_modules/es-object-atoms": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", - "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", "dev": true, "dependencies": { - "es-errors": "^1.3.0" + "has-tostringtag": "^1.0.0" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es-set-tostringtag": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", - "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", "dev": true, "dependencies": { - "get-intrinsic": "^1.2.4", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.1" + "has-symbols": "^1.0.2" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es-shim-unscopables": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", - "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "node_modules/is-typed-array": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", + "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", "dev": true, "dependencies": { - "hasown": "^2.0.0" + "which-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", "dev": true, "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" + "call-bind": "^1.0.2" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es6-promise": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", - "integrity": "sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==" + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true }, - "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", "dev": true, + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, "engines": { "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", - "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.0", - "@humanwhocodes/config-array": "^0.11.14", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">=10" } }, - "node_modules/eslint-config-airbnb-base": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz", - "integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==", + "node_modules/istanbul-lib-source-maps": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", + "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", "dev": true, "dependencies": { - "confusing-browser-globals": "^1.0.10", - "object.assign": "^4.1.2", - "object.entries": "^1.1.5", - "semver": "^6.3.0" + "@jridgewell/trace-mapping": "^0.3.23", + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" - }, - "peerDependencies": { - "eslint": "^7.32.0 || ^8.2.0", - "eslint-plugin-import": "^2.25.2" + "node": ">=10" } }, - "node_modules/eslint-config-airbnb-base/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", "dev": true, - "bin": { - "semver": "bin/semver.js" + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/eslint-config-airbnb-typescript": { - "version": "17.1.0", - "resolved": "https://registry.npmjs.org/eslint-config-airbnb-typescript/-/eslint-config-airbnb-typescript-17.1.0.tgz", - "integrity": "sha512-GPxI5URre6dDpJ0CtcthSZVBAfI+Uw7un5OYNVxP2EYi3H81Jw701yFP7AU+/vCE7xBtFmjge7kfhhk4+RAiig==", + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "dev": true, "dependencies": { - "eslint-config-airbnb-base": "^15.0.0" + "@isaacs/cliui": "^8.0.2" }, - "peerDependencies": { - "@typescript-eslint/eslint-plugin": "^5.13.0 || ^6.0.0", - "@typescript-eslint/parser": "^5.0.0 || ^6.0.0", - "eslint": "^7.32.0 || ^8.2.0", - "eslint-plugin-import": "^2.25.3" + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/eslint-config-prettier": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz", - "integrity": "sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==", + "node_modules/jest": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-30.2.0.tgz", + "integrity": "sha512-F26gjC0yWN8uAA5m5Ss8ZQf5nDHWGlN/xWZIh8S5SRbsEKBovwZhxGd6LJlbZYxBgCYOtreSUyb8hpXyGC5O4A==", "dev": true, + "dependencies": { + "@jest/core": "30.2.0", + "@jest/types": "30.2.0", + "import-local": "^3.2.0", + "jest-cli": "30.2.0" + }, "bin": { - "eslint-config-prettier": "bin/cli.js" + "jest": "bin/jest.js" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" }, "peerDependencies": { - "eslint": ">=7.0.0" - } - }, - "node_modules/eslint-import-resolver-node": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", - "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", - "dev": true, - "dependencies": { - "debug": "^3.2.7", - "is-core-module": "^2.13.0", - "resolve": "^1.22.4" + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, - "node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "node_modules/jest-changed-files": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-30.2.0.tgz", + "integrity": "sha512-L8lR1ChrRnSdfeOvTrwZMlnWV8G/LLjQ0nG9MBclwWZidA2N5FviRki0Bvh20WRMOX31/JYvzdqTJrk5oBdydQ==", "dev": true, "dependencies": { - "ms": "^2.1.1" + "execa": "^5.1.1", + "jest-util": "30.2.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/eslint-import-resolver-node/node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "node_modules/jest-circus": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-30.2.0.tgz", + "integrity": "sha512-Fh0096NC3ZkFx05EP2OXCxJAREVxj1BcW/i6EWqqymcgYKWjyyDpral3fMxVcHXg6oZM7iULer9wGRFvfpl+Tg==", "dev": true, "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" + "@jest/environment": "30.2.0", + "@jest/expect": "30.2.0", + "@jest/test-result": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "chalk": "^4.1.2", + "co": "^4.6.0", + "dedent": "^1.6.0", + "is-generator-fn": "^2.1.0", + "jest-each": "30.2.0", + "jest-matcher-utils": "30.2.0", + "jest-message-util": "30.2.0", + "jest-runtime": "30.2.0", + "jest-snapshot": "30.2.0", + "jest-util": "30.2.0", + "p-limit": "^3.1.0", + "pretty-format": "30.2.0", + "pure-rand": "^7.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.6" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-cli": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-30.2.0.tgz", + "integrity": "sha512-Os9ukIvADX/A9sLt6Zse3+nmHtHaE6hqOsjQtNiugFTbKRHYIYtZXNGNK9NChseXy7djFPjndX1tL0sCTlfpAA==", + "dev": true, + "dependencies": { + "@jest/core": "30.2.0", + "@jest/test-result": "30.2.0", + "@jest/types": "30.2.0", + "chalk": "^4.1.2", + "exit-x": "^0.2.2", + "import-local": "^3.2.0", + "jest-config": "30.2.0", + "jest-util": "30.2.0", + "jest-validate": "30.2.0", + "yargs": "^17.7.2" }, "bin": { - "resolve": "bin/resolve" + "jest": "bin/jest.js" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, - "node_modules/eslint-import-resolver-webpack": { - "version": "0.13.8", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-webpack/-/eslint-import-resolver-webpack-0.13.8.tgz", - "integrity": "sha512-Y7WIaXWV+Q21Rz/PJgUxiW/FTBOWmU8NTLdz+nz9mMoiz5vAev/fOaQxwD7qRzTfE3HSm1qsxZ5uRd7eX+VEtA==", + "node_modules/jest-config": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-30.2.0.tgz", + "integrity": "sha512-g4WkyzFQVWHtu6uqGmQR4CQxz/CH3yDSlhzXMWzNjDx843gYjReZnMRanjRCq5XZFuQrGDxgUaiYWE8BRfVckA==", "dev": true, "dependencies": { - "array.prototype.find": "^2.2.2", - "debug": "^3.2.7", - "enhanced-resolve": "^0.9.1", - "find-root": "^1.1.0", - "hasown": "^2.0.0", - "interpret": "^1.4.0", - "is-core-module": "^2.13.1", - "is-regex": "^1.1.4", - "lodash": "^4.17.21", - "resolve": "^2.0.0-next.5", - "semver": "^5.7.2" + "@babel/core": "^7.27.4", + "@jest/get-type": "30.1.0", + "@jest/pattern": "30.0.1", + "@jest/test-sequencer": "30.2.0", + "@jest/types": "30.2.0", + "babel-jest": "30.2.0", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "deepmerge": "^4.3.1", + "glob": "^10.3.10", + "graceful-fs": "^4.2.11", + "jest-circus": "30.2.0", + "jest-docblock": "30.2.0", + "jest-environment-node": "30.2.0", + "jest-regex-util": "30.0.1", + "jest-resolve": "30.2.0", + "jest-runner": "30.2.0", + "jest-util": "30.2.0", + "jest-validate": "30.2.0", + "micromatch": "^4.0.8", + "parse-json": "^5.2.0", + "pretty-format": "30.2.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" }, "engines": { - "node": ">= 6" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" }, "peerDependencies": { - "eslint-plugin-import": ">=1.4.0", - "webpack": ">=1.11.0" + "@types/node": "*", + "esbuild-register": ">=3.4.0", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "esbuild-register": { + "optional": true + }, + "ts-node": { + "optional": true + } } }, - "node_modules/eslint-import-resolver-webpack/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "node_modules/jest-config/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dev": true, "dependencies": { - "ms": "^2.1.1" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/eslint-import-resolver-webpack/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "node_modules/jest-diff": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.2.0.tgz", + "integrity": "sha512-dQHFo3Pt4/NLlG5z4PxZ/3yZTZ1C7s9hveiOj+GCN+uT109NC2QgsoVZsVOAvbJ3RgKkvyLGXZV9+piDpWbm6A==", "dev": true, - "bin": { - "semver": "bin/semver" + "dependencies": { + "@jest/diff-sequences": "30.0.1", + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "pretty-format": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/eslint-module-utils": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.1.tgz", - "integrity": "sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==", + "node_modules/jest-docblock": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-30.2.0.tgz", + "integrity": "sha512-tR/FFgZKS1CXluOQzZvNH3+0z9jXr3ldGSD8bhyuxvlVUwbeLOGynkunvlTMxchC5urrKndYiwCFC0DLVjpOCA==", "dev": true, "dependencies": { - "debug": "^3.2.7" + "detect-newline": "^3.1.0" }, "engines": { - "node": ">=4" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/eslint-module-utils/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "node_modules/jest-each": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-30.2.0.tgz", + "integrity": "sha512-lpWlJlM7bCUf1mfmuqTA8+j2lNURW9eNafOy99knBM01i5CQeY5UH1vZjgT9071nDJac1M4XsbyI44oNOdhlDQ==", "dev": true, "dependencies": { - "ms": "^2.1.1" + "@jest/get-type": "30.1.0", + "@jest/types": "30.2.0", + "chalk": "^4.1.2", + "jest-util": "30.2.0", + "pretty-format": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/eslint-plugin-import": { - "version": "2.29.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz", - "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==", + "node_modules/jest-environment-node": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.2.0.tgz", + "integrity": "sha512-ElU8v92QJ9UrYsKrxDIKCxu6PfNj4Hdcktcn0JX12zqNdqWHB0N+hwOnnBBXvjLd2vApZtuLUGs1QSY+MsXoNA==", "dev": true, "dependencies": { - "array-includes": "^3.1.7", - "array.prototype.findlastindex": "^1.2.3", - "array.prototype.flat": "^1.3.2", - "array.prototype.flatmap": "^1.3.2", - "debug": "^3.2.7", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.9", - "eslint-module-utils": "^2.8.0", - "hasown": "^2.0.0", - "is-core-module": "^2.13.1", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.fromentries": "^2.0.7", - "object.groupby": "^1.0.1", - "object.values": "^1.1.7", - "semver": "^6.3.1", - "tsconfig-paths": "^3.15.0" + "@jest/environment": "30.2.0", + "@jest/fake-timers": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "jest-mock": "30.2.0", + "jest-util": "30.2.0", + "jest-validate": "30.2.0" }, "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/eslint-plugin-import/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/jest-haste-map": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.2.0.tgz", + "integrity": "sha512-sQA/jCb9kNt+neM0anSj6eZhLZUIhQgwDt7cPGjumgLM4rXsfb9kpnlacmvZz3Q5tb80nS+oG/if+NBKrHC+Xw==", "dev": true, "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "@jest/types": "30.2.0", + "@types/node": "*", + "anymatch": "^3.1.3", + "fb-watchman": "^2.0.2", + "graceful-fs": "^4.2.11", + "jest-regex-util": "30.0.1", + "jest-util": "30.2.0", + "jest-worker": "30.2.0", + "micromatch": "^4.0.8", + "walker": "^1.0.8" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.3" } }, - "node_modules/eslint-plugin-import/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "node_modules/jest-haste-map/node_modules/jest-worker": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.2.0.tgz", + "integrity": "sha512-0Q4Uk8WF7BUwqXHuAjc23vmopWJw5WH7w2tqBoUOZpOjW/ZnR44GXXd1r82RvnmI2GZge3ivrYXk/BE2+VtW2g==", "dev": true, "dependencies": { - "ms": "^2.1.1" + "@types/node": "*", + "@ungap/structured-clone": "^1.3.0", + "jest-util": "30.2.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.1.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/eslint-plugin-import/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "node_modules/jest-haste-map/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "dependencies": { - "esutils": "^2.0.2" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/eslint-plugin-import/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/jest-leak-detector": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.2.0.tgz", + "integrity": "sha512-M6jKAjyzjHG0SrQgwhgZGy9hFazcudwCNovY/9HPIicmNSBuockPSedAP9vlPK6ONFJ1zfyH/M2/YYJxOz5cdQ==", "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" + "@jest/get-type": "30.1.0", + "pretty-format": "30.2.0" }, "engines": { - "node": "*" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/eslint-plugin-import/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "node_modules/jest-matcher-utils": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.2.0.tgz", + "integrity": "sha512-dQ94Nq4dbzmUWkQ0ANAWS9tBRfqCrn0bV9AMYdOi/MHW726xn7eQmMeRTpX2ViC00bpNaWXq+7o4lIQ3AX13Hg==", "dev": true, - "bin": { - "semver": "bin/semver.js" + "dependencies": { + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "jest-diff": "30.2.0", + "pretty-format": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/eslint-plugin-mocha": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-10.4.3.tgz", - "integrity": "sha512-emc4TVjq5Ht0/upR+psftuz6IBG5q279p+1dSRDeHf+NS9aaerBi3lXKo1SEzwC29hFIW21gO89CEWSvRsi8IQ==", + "node_modules/jest-message-util": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.2.0.tgz", + "integrity": "sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==", "dev": true, "dependencies": { - "eslint-utils": "^3.0.0", - "globals": "^13.24.0", - "rambda": "^7.4.0" + "@babel/code-frame": "^7.27.1", + "@jest/types": "30.2.0", + "@types/stack-utils": "^2.0.3", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "micromatch": "^4.0.8", + "pretty-format": "30.2.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.6" }, "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "eslint": ">=7.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/eslint-plugin-prettier": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz", - "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==", + "node_modules/jest-mock": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.2.0.tgz", + "integrity": "sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw==", "dev": true, "dependencies": { - "prettier-linter-helpers": "^1.0.0" + "@jest/types": "30.2.0", + "@types/node": "*", + "jest-util": "30.2.0" }, "engines": { - "node": ">=12.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "engines": { + "node": ">=6" }, "peerDependencies": { - "eslint": ">=7.28.0", - "prettier": ">=2.0.0" + "jest-resolve": "*" }, "peerDependenciesMeta": { - "eslint-config-prettier": { + "jest-resolve": { "optional": true } } }, - "node_modules/eslint-plugin-sonarjs": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-0.18.0.tgz", - "integrity": "sha512-DJ3osLnt6KFdT5e9ZuIDOjT5A6wUGSLeiJJT03lPgpdD+7CVWlYAw9Goe3bt7SmbFO3Xh89NOCZAuB9XA7bAUQ==", + "node_modules/jest-regex-util": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.0.1.tgz", + "integrity": "sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==", "dev": true, "engines": { - "node": ">=14" - }, - "peerDependencies": { - "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/eslint-plugin-unicorn": { - "version": "48.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-48.0.1.tgz", - "integrity": "sha512-FW+4r20myG/DqFcCSzoumaddKBicIPeFnTrifon2mWIzlfyvzwyqZjqVP7m4Cqr/ZYisS2aiLghkUWaPg6vtCw==", + "node_modules/jest-resolve": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-30.2.0.tgz", + "integrity": "sha512-TCrHSxPlx3tBY3hWNtRQKbtgLhsXa1WmbJEqBlTBrGafd5fiQFByy2GNCEoGR+Tns8d15GaL9cxEzKOO3GEb2A==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.22.5", - "@eslint-community/eslint-utils": "^4.4.0", - "ci-info": "^3.8.0", - "clean-regexp": "^1.0.0", - "esquery": "^1.5.0", - "indent-string": "^4.0.0", - "is-builtin-module": "^3.2.1", - "jsesc": "^3.0.2", - "lodash": "^4.17.21", - "pluralize": "^8.0.0", - "read-pkg-up": "^7.0.1", - "regexp-tree": "^0.1.27", - "regjsparser": "^0.10.0", - "semver": "^7.5.4", - "strip-indent": "^3.0.0" + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.2.0", + "jest-pnp-resolver": "^1.2.3", + "jest-util": "30.2.0", + "jest-validate": "30.2.0", + "slash": "^3.0.0", + "unrs-resolver": "^1.7.11" }, "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sindresorhus/eslint-plugin-unicorn?sponsor=1" - }, - "peerDependencies": { - "eslint": ">=8.44.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "node_modules/jest-resolve-dependencies": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-30.2.0.tgz", + "integrity": "sha512-xTOIGug/0RmIe3mmCqCT95yO0vj6JURrn1TKWlNbhiAefJRWINNPgwVkrVgt/YaerPzY3iItufd80v3lOrFJ2w==", "dev": true, "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" + "jest-regex-util": "30.0.1", + "jest-snapshot": "30.2.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "node_modules/jest-runner": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-30.2.0.tgz", + "integrity": "sha512-PqvZ2B2XEyPEbclp+gV6KO/F1FIFSbIwewRgmROCMBo/aZ6J1w8Qypoj2pEOcg3G2HzLlaP6VUtvwCI8dM3oqQ==", "dev": true, "dependencies": { - "eslint-visitor-keys": "^2.0.0" - }, - "engines": { - "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" + "@jest/console": "30.2.0", + "@jest/environment": "30.2.0", + "@jest/test-result": "30.2.0", + "@jest/transform": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "chalk": "^4.1.2", + "emittery": "^0.13.1", + "exit-x": "^0.2.2", + "graceful-fs": "^4.2.11", + "jest-docblock": "30.2.0", + "jest-environment-node": "30.2.0", + "jest-haste-map": "30.2.0", + "jest-leak-detector": "30.2.0", + "jest-message-util": "30.2.0", + "jest-resolve": "30.2.0", + "jest-runtime": "30.2.0", + "jest-util": "30.2.0", + "jest-watcher": "30.2.0", + "jest-worker": "30.2.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" }, - "peerDependencies": { - "eslint": ">=5" - } - }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true, "engines": { - "node": ">=10" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "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==", + "node_modules/jest-runner/node_modules/jest-worker": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.2.0.tgz", + "integrity": "sha512-0Q4Uk8WF7BUwqXHuAjc23vmopWJw5WH7w2tqBoUOZpOjW/ZnR44GXXd1r82RvnmI2GZge3ivrYXk/BE2+VtW2g==", "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "dependencies": { + "@types/node": "*", + "@ungap/structured-clone": "^1.3.0", + "jest-util": "30.2.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.1.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/jest-runner/node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", "dev": true, "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" } }, - "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/jest-runner/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" + "has-flag": "^4.0.0" }, "engines": { - "node": "*" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "node_modules/jest-runtime": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.2.0.tgz", + "integrity": "sha512-p1+GVX/PJqTucvsmERPMgCPvQJpFt4hFbM+VN3n8TMo47decMUcJbt+rgzwrEme0MQUA/R+1de2axftTHkKckg==", "dev": true, "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" + "@jest/environment": "30.2.0", + "@jest/fake-timers": "30.2.0", + "@jest/globals": "30.2.0", + "@jest/source-map": "30.0.1", + "@jest/test-result": "30.2.0", + "@jest/transform": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "chalk": "^4.1.2", + "cjs-module-lexer": "^2.1.0", + "collect-v8-coverage": "^1.0.2", + "glob": "^10.3.10", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.2.0", + "jest-message-util": "30.2.0", + "jest-mock": "30.2.0", + "jest-regex-util": "30.0.1", + "jest-resolve": "30.2.0", + "jest-snapshot": "30.2.0", + "jest-util": "30.2.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-runtime/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" + "node_modules/jest-runtime/node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-snapshot": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.2.0.tgz", + "integrity": "sha512-5WEtTy2jXPFypadKNpbNkZ72puZCa6UjSr/7djeecHWOu7iYhSXSnHScT8wBz3Rn8Ena5d5RYRcsyKIeqG1IyA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.27.4", + "@babel/generator": "^7.27.5", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/plugin-syntax-typescript": "^7.27.1", + "@babel/types": "^7.27.3", + "@jest/expect-utils": "30.2.0", + "@jest/get-type": "30.1.0", + "@jest/snapshot-utils": "30.2.0", + "@jest/transform": "30.2.0", + "@jest/types": "30.2.0", + "babel-preset-current-node-syntax": "^1.2.0", + "chalk": "^4.1.2", + "expect": "30.2.0", + "graceful-fs": "^4.2.11", + "jest-diff": "30.2.0", + "jest-matcher-utils": "30.2.0", + "jest-message-util": "30.2.0", + "jest-util": "30.2.0", + "pretty-format": "30.2.0", + "semver": "^7.7.2", + "synckit": "^0.11.8" }, "engines": { - "node": ">=4" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "node_modules/jest-util": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.2.0.tgz", + "integrity": "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==", "dev": true, "dependencies": { - "estraverse": "^5.1.0" + "@jest/types": "30.2.0", + "@types/node": "*", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "graceful-fs": "^4.2.11", + "picomatch": "^4.0.2" }, "engines": { - "node": ">=0.10" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "node_modules/jest-validate": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.2.0.tgz", + "integrity": "sha512-FBGWi7dP2hpdi8nBoWxSsLvBFewKAg0+uSQwBaof4Y4DPgBabXgpSYC5/lR7VmnIlSpASmCi/ntRWPbv7089Pw==", "dev": true, "dependencies": { - "estraverse": "^5.2.0" + "@jest/get-type": "30.1.0", + "@jest/types": "30.2.0", + "camelcase": "^6.3.0", + "chalk": "^4.1.2", + "leven": "^3.1.0", + "pretty-format": "30.2.0" }, "engines": { - "node": ">=4.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true, "engines": { - "node": ">=4.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "node_modules/jest-watcher": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-30.2.0.tgz", + "integrity": "sha512-PYxa28dxJ9g777pGm/7PrbnMeA0Jr7osHP9bS7eJy9DuAjMgdGtxgf0uKMyoIsTWAkIbUW5hSDdJ3urmgXBqxg==", "dev": true, + "dependencies": { + "@jest/test-result": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "emittery": "^0.13.1", + "jest-util": "30.2.0", + "string-length": "^4.0.2" + }, "engines": { - "node": ">=0.10.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", "dev": true, "peer": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, "engines": { - "node": ">=0.8.x" + "node": ">= 10.13.0" } }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "node_modules/fast-diff": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", - "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", - "dev": true - }, - "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "peer": true, "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=8.6.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "node_modules/joi": { + "version": "17.13.3", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", + "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", + "dependencies": { + "@hapi/hoek": "^9.3.0", + "@hapi/topo": "^5.1.0", + "@sideway/address": "^4.1.5", + "@sideway/formula": "^3.0.1", + "@sideway/pinpoint": "^2.0.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, - "peer": true, "dependencies": { - "is-glob": "^4.0.1" + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" }, "engines": { - "node": ">= 6" + "node": ">=6" } }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, - "node_modules/fast-safe-stringify": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", - "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true }, - "node_modules/fastq": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "dev": true, "dependencies": { - "reusify": "^1.0.4" + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" } }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, "dependencies": { - "flat-cache": "^3.0.4" - }, + "json-buffer": "3.0.1" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=6" } }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, - "peer": true, "dependencies": { - "to-regex-range": "^5.0.1" + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" }, "engines": { - "node": ">=8" + "node": ">= 0.8.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6.11.5" } }, - "node_modules/find-root": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", - "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", - "dev": true - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" + "p-locate": "^5.0.0" }, "engines": { "node": ">=10" @@ -2446,456 +5985,479 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", - "dev": true, - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, - "node_modules/flatted": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", "dev": true }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, "dependencies": { - "is-callable": "^1.1.3" + "yallist": "^3.0.2" } }, - "node_modules/form-data": { + "node_modules/make-dir": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", "dev": true, "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" + "semver": "^7.5.3" }, "engines": { - "node": ">= 6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "dependencies": { + "tmpl": "1.0.5" } }, - "node_modules/function.prototype.name": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", - "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "node_modules/memory-fs": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.2.0.tgz", + "integrity": "sha512-+y4mDxU4rvXXu5UDSGCGNiesFmwCHuefGMoPCO1WYucNYj7DsLqrFaa2fXVI0H+NNiPTwwzKwspn9yTZqUGqng==", + "dev": true + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "functions-have-names": "^1.2.3" + "braces": "^3.0.3", + "picomatch": "^2.3.1" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8.6" } }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, + "engines": { + "node": ">=8.6" + }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, "engines": { - "node": "6.* || 8.* || >= 10.*" + "node": ">= 0.6" } }, - "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "mime-db": "1.52.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 0.6" } }, - "node_modules/get-symbol-description": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", - "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true, - "dependencies": { - "call-bind": "^1.0.5", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=6" } }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=4" } }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "dependencies": { - "is-glob": "^4.0.3" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=10.13.0" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true, - "peer": true + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "engines": { + "node": ">=16 || 14 >=14.17" } }, - "node_modules/glob/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, - "dependencies": { - "brace-expansion": "^1.1.7" - }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", "engines": { "node": "*" } }, - "node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, + "node_modules/moment-timezone": { + "version": "0.5.40", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.40.tgz", + "integrity": "sha512-tWfmNkRYmBkPJz5mr9GVDn9vRlVZOTe6yqY92rFxiOdWXbjaR0+9LwQnZGGuNR63X456NqmEkbskte8tWL5ePg==", "dependencies": { - "type-fest": "^0.20.2" + "moment": ">= 2.9.0" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "*" } }, - "node_modules/globalthis": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", - "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/napi-postinstall": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz", + "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==", "dev": true, - "dependencies": { - "define-properties": "^1.2.1", - "gopd": "^1.0.1" + "bin": { + "napi-postinstall": "lib/cli.js" }, "engines": { - "node": ">= 0.4" + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://opencollective.com/napi-postinstall" } }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "peer": true, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" + "whatwg-url": "^5.0.0" }, "engines": { - "node": ">=10" + "node": "4.x || >=6.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } } }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "node_modules/node-fetch-h2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/node-fetch-h2/-/node-fetch-h2-2.3.0.tgz", + "integrity": "sha512-ofRW94Ab0T4AOh5Fk8t0h8OBWrmjb0SSB20xh1H8YnPV9EJ+f5AMoYSUQ2zgJ4Iq2HAK0I2l5/Nequ8YzFS3Hg==", "dependencies": { - "get-intrinsic": "^1.1.3" + "http2-client": "^1.2.5" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": "4.x || >=6.0.0" } }, - "node_modules/graceful-fs": { - "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==", + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", "dev": true }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "node_modules/node-readfiles": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/node-readfiles/-/node-readfiles-0.2.0.tgz", + "integrity": "sha512-SU00ZarexNlE4Rjdm83vglt5Y9yiQ+XI1XpflWlb7q7UTN1JUItm69xMeiQCTxtTfnzt+83T8Cx+vI2ED++VDA==", + "dependencies": { + "es6-promise": "^3.2.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", "dev": true }, - "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" } }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/normalize-package-data/node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dependencies": { - "es-define-property": "^1.0.0" + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node_modules/normalize-package-data/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" } }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "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==", + "dev": true, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.10.0" } }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, "dependencies": { - "has-symbols": "^1.0.3" + "path-key": "^3.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=8" + } + }, + "node_modules/oas-kit-common": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/oas-kit-common/-/oas-kit-common-1.0.8.tgz", + "integrity": "sha512-pJTS2+T0oGIwgjGpw7sIRU8RQMcUoKCDWFLdBqKB2BNmGpbBMH2sdqAaOXUg8OzonZHU0L7vfJu1mJFEiYDWOQ==", + "dependencies": { + "fast-safe-stringify": "^2.0.7" + } + }, + "node_modules/oas-linter": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/oas-linter/-/oas-linter-3.2.2.tgz", + "integrity": "sha512-KEGjPDVoU5K6swgo9hJVA/qYGlwfbFx+Kg2QB/kd7rzV5N8N5Mg6PlsoCMohVnQmo+pzJap/F610qTodKzecGQ==", + "dependencies": { + "@exodus/schemasafe": "^1.0.0-rc.2", + "should": "^13.2.1", + "yaml": "^1.10.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/Mermade/oas-kit?sponsor=1" } }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "node_modules/oas-resolver": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/oas-resolver/-/oas-resolver-2.5.6.tgz", + "integrity": "sha512-Yx5PWQNZomfEhPPOphFbZKi9W93CocQj18NlD2Pa4GWZzdZpSJvYwoiuurRI7m3SpcChrnO08hkuQDL3FGsVFQ==", "dependencies": { - "function-bind": "^1.1.2" + "node-fetch-h2": "^2.3.0", + "oas-kit-common": "^1.0.8", + "reftools": "^1.1.9", + "yaml": "^1.10.0", + "yargs": "^17.0.1" }, - "engines": { - "node": ">= 0.4" + "bin": { + "resolve": "resolve.js" + }, + "funding": { + "url": "https://github.com/Mermade/oas-kit?sponsor=1" } }, - "node_modules/hoek": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.3.1.tgz", - "integrity": "sha512-v7E+yIjcHECn973i0xHm4kJkEpv3C8sbYS4344WXbzYqRyiDD7rjnnKo4hsJkejQBAFdRMUGNHySeSPKSH9Rqw==", - "deprecated": "This module has moved and is now available at @hapi/hoek. Please update your dependencies as this version is no longer maintained an may contain bugs and security issues.", - "engines": { - "node": ">=6.0.0" + "node_modules/oas-schema-walker": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/oas-schema-walker/-/oas-schema-walker-1.1.5.tgz", + "integrity": "sha512-2yucenq1a9YPmeNExoUa9Qwrt9RFkjqaMAA1X+U7sbb0AqBeTIdMHky9SQQ6iN94bO5NW0W4TRYXerG+BdAvAQ==", + "funding": { + "url": "https://github.com/Mermade/oas-kit?sponsor=1" } }, - "node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, - "node_modules/http2-client": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/http2-client/-/http2-client-1.3.5.tgz", - "integrity": "sha512-EC2utToWl4RKfs5zd36Mxq7nzHHBuomZboI0yYL6Y0RmBgT7Sgkq4rQ0ezFTYoIsSs7Tm9SJe+o2FcAg6GBhGA==" - }, - "node_modules/ignore": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", - "dev": true, - "engines": { - "node": ">= 4" + "node_modules/oas-validator": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/oas-validator/-/oas-validator-5.0.8.tgz", + "integrity": "sha512-cu20/HE5N5HKqVygs3dt94eYJfBi0TsZvPVXDhbXQHiEityDN+RROTleefoKRKKJ9dFAF2JBkDHgvWj0sjKGmw==", + "dependencies": { + "call-me-maybe": "^1.0.1", + "oas-kit-common": "^1.0.8", + "oas-linter": "^3.2.2", + "oas-resolver": "^2.5.6", + "oas-schema-walker": "^1.1.5", + "reftools": "^1.1.9", + "should": "^13.2.1", + "yaml": "^1.10.0" + }, + "funding": { + "url": "https://github.com/Mermade/oas-kit?sponsor=1" } }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, + "node_modules/oazapfts": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/oazapfts/-/oazapfts-5.1.7.tgz", + "integrity": "sha512-/0gaUUevgIYqZKKtquc0rCkr2HFQyKFLQGsyS6SSuvVXwABEm5FJD/KzPcapB7ulOrmn8ntSbR2kzhr3TOMcNA==", "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" + "@apidevtools/swagger-parser": "^10.1.0", + "lodash": "^4.17.21", + "minimist": "^1.2.8", + "swagger2openapi": "^7.0.8", + "typescript": "^5.3.3" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "bin": { + "oazapfts": "lib/codegen/cli.js" } }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "engines": { - "node": ">=0.8.19" + "node_modules/object-inspect": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "dev": true, "engines": { - "node": ">=8" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" + "node": ">= 0.4" } }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/internal-slot": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", - "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", + "node_modules/object.assign": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", "dev": true, "dependencies": { - "es-errors": "^1.3.0", - "hasown": "^2.0.0", - "side-channel": "^1.0.4" + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/interpret": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", - "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "node_modules/object.entries": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz", + "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==", "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, "engines": { - "node": ">= 0.10" + "node": ">= 0.4" } }, - "node_modules/is-array-buffer": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", - "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -2904,32 +6466,29 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, - "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "node_modules/object.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", "dev": true, "dependencies": { - "has-bigints": "^1.0.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">= 0.4" } }, - "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "node_modules/object.values": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", + "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -2938,13 +6497,22 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-builtin-module": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", - "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, "dependencies": { - "builtin-modules": "^3.3.0" + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" }, "engines": { "node": ">=6" @@ -2953,611 +6521,605 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "node_modules/openapi-types": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz", + "integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==", + "peer": true }, - "node_modules/is-core-module": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "dependencies": { - "hasown": "^2.0.0" + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">= 0.8.0" } }, - "node_modules/is-data-view": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", - "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "dependencies": { - "is-typed-array": "^1.1.13" + "yocto-queue": "^0.1.0" }, "engines": { - "node": ">= 0.4" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "dependencies": { - "has-tostringtag": "^1.0.0" + "p-limit": "^3.0.2" }, "engines": { - "node": ">= 0.4" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=6" } }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" - } + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-negative-zero": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", - "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "peer": true, + "callsites": "^3.0.0" + }, "engines": { - "node": ">=0.12.0" + "node": ">=6" } }, - "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "dev": true, "dependencies": { - "has-tostringtag": "^1.0.0" + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" }, "engines": { - "node": ">= 0.4" + "node": ">=8" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, "engines": { "node": ">=8" } }, - "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.10.0" } }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", - "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, - "dependencies": { - "call-bind": "^1.0.7" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/is-string": { + "node_modules/path-parse": { "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, "dependencies": { - "has-tostringtag": "^1.0.0" + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=16 || 14 >=14.18" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true, - "dependencies": { - "has-symbols": "^1.0.2" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/is-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", - "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", - "dev": true, - "dependencies": { - "which-typed-array": "^1.1.14" - }, + "node_modules/phone": { + "version": "2.4.22", + "resolved": "https://registry.npmjs.org/phone/-/phone-2.4.22.tgz", + "integrity": "sha512-k2f9qkIgcgbbeyFFMHDcCaYdPxq7u71EjmMvD998PEquwDvIT7zmUFe00S4hH9WPjk+IQlw9W/FlHOu1O17Tbw==", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=6.10.0" } }, - "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true + }, + "node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "dev": true, - "dependencies": { - "call-bind": "^1.0.2" + "engines": { + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "engines": { + "node": ">= 6" + } }, - "node_modules/isemail": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/isemail/-/isemail-3.2.0.tgz", - "integrity": "sha512-zKqkK+O+dGqevc93KNsbZ/TqTUFd46MwWjYOoMrjIMZ51eU7DtQG3Wmd9SQQT7i7RVnuTPEiYEWHU3MSbxC1Tg==", + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, "dependencies": { - "punycode": "2.x.x" + "find-up": "^4.0.0" }, "engines": { - "node": ">=4.0.0" + "node": ">=8" } }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "node_modules/jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, - "peer": true, "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": ">= 10.13.0" + "node": ">=8" } }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, - "peer": true, "dependencies": { - "has-flag": "^4.0.0" + "p-locate": "^4.1.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "node": ">=8" } }, - "node_modules/joi": { - "version": "11.4.0", - "resolved": "https://registry.npmjs.org/joi/-/joi-11.4.0.tgz", - "integrity": "sha512-O7Uw+w/zEWgbL6OcHbyACKSj0PkQeUgmehdoXVSxt92QFCq4+1390Rwh5moI2K/OgC7D8RHRZqHZxT2husMJHA==", - "deprecated": "This version has been deprecated in accordance with the hapi support policy (hapi.im/support). Please upgrade to the latest version to get the best features, bug fixes, and security patches. If you are unable to upgrade at this time, paid support is available for older versions (hapi.im/commercial).", + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, "dependencies": { - "hoek": "4.x.x", - "isemail": "3.x.x", - "topo": "2.x.x" + "p-try": "^2.0.0" }, "engines": { - "node": ">=4.0.0" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "node_modules/js-yaml": { + "node_modules/pkg-dir/node_modules/p-locate": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "dependencies": { - "argparse": "^2.0.1" + "p-limit": "^2.2.0" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "engines": { + "node": ">=8" } }, - "node_modules/jsesc": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", - "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "node_modules/pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", "dev": true, - "bin": { - "jsesc": "bin/jsesc" - }, "engines": { - "node": ">=6" + "node": ">=4" } }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "dev": true, + "engines": { + "node": ">= 0.4" + } }, - "node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, - "dependencies": { - "minimist": "^1.2.0" - }, "bin": { - "json5": "lib/cli.js" + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", "dev": true, "dependencies": { - "json-buffer": "3.0.1" + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" } }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "node_modules/pretty-format": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.2.0.tgz", + "integrity": "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==", "dev": true, "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" + "@jest/schemas": "30.0.5", + "ansi-styles": "^5.2.0", + "react-is": "^18.3.1" }, "engines": { - "node": ">= 0.8.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "node_modules/loader-runner": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", - "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, - "peer": true, "engines": { - "node": ">=6.11.5" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/pure-rand": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-7.0.1.tgz", + "integrity": "sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==", "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ] + }, + "node_modules/qs": { + "version": "6.11.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz", + "integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==", "dependencies": { - "p-locate": "^5.0.0" + "side-channel": "^1.0.4" }, "engines": { - "node": ">=10" + "node": ">=0.6" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, - "node_modules/memory-fs": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.2.0.tgz", - "integrity": "sha512-+y4mDxU4rvXXu5UDSGCGNiesFmwCHuefGMoPCO1WYucNYj7DsLqrFaa2fXVI0H+NNiPTwwzKwspn9yTZqUGqng==", + "node_modules/rambda": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/rambda/-/rambda-7.5.0.tgz", + "integrity": "sha512-y/M9weqWAH4iopRd7EHDEQQvpFPHj1AA3oHozE9tfITHUtTR7Z9PSlIRRG2l1GuW7sefC1cXFfIcF+cgnShdBA==", "dev": true }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true, - "peer": true - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, "peer": true, - "engines": { - "node": ">= 8" + "dependencies": { + "safe-buffer": "^5.1.0" } }, - "node_modules/micromatch": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.6.tgz", - "integrity": "sha512-Y4Ypn3oujJYxJcMacVgcs92wofTHxp9FzfDpQON4msDefoC0lb3ETvQLOdLcbhSwU1bz8HrL/1sygfBIHudrkQ==", + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + }, + "node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", "dev": true, - "peer": true, "dependencies": { - "braces": "^3.0.3", - "picomatch": "^4.0.2" + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" }, "engines": { - "node": ">=8.6" + "node": ">=8" } }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "node_modules/read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", "dev": true, + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, "engines": { - "node": ">= 0.6" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "node_modules/read-pkg-up/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "dependencies": { - "mime-db": "1.52.0" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": ">= 0.6" + "node": ">=8" } }, - "node_modules/min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "node_modules/read-pkg-up/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "node_modules/read-pkg-up/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, - "peer": true, "dependencies": { - "brace-expansion": "^2.0.1" + "p-try": "^2.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=6" }, "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/moment": { - "version": "2.30.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", - "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "node_modules/read-pkg-up/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, "engines": { - "node": "*" + "node": ">=8" } }, - "node_modules/moment-timezone": { - "version": "0.5.40", - "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.40.tgz", - "integrity": "sha512-tWfmNkRYmBkPJz5mr9GVDn9vRlVZOTe6yqY92rFxiOdWXbjaR0+9LwQnZGGuNR63X456NqmEkbskte8tWL5ePg==", - "dependencies": { - "moment": ">= 2.9.0" - }, + "node_modules/read-pkg-up/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, "engines": { - "node": "*" + "node": ">=8" } }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true, + "engines": { + "node": ">=8" + } }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true + "node_modules/reftools": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/reftools/-/reftools-1.1.9.tgz", + "integrity": "sha512-OVede/NQE13xBQ+ob5CKd5KyeJYU2YInb1bmV4nRoOfquZPkAkxuOXicSe1PvqIuZZ4kD13sPKBbR7UFDmli6w==", + "funding": { + "url": "https://github.com/Mermade/oas-kit?sponsor=1" + } }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "node_modules/regexp-tree": { + "version": "0.1.27", + "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", + "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==", "dev": true, - "peer": true + "bin": { + "regexp-tree": "bin/regexp-tree" + } }, - "node_modules/node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "node_modules/regexp.prototype.flags": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", + "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", + "dev": true, "dependencies": { - "whatwg-url": "^5.0.0" + "call-bind": "^1.0.6", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "set-function-name": "^2.0.1" }, "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" + "node": ">= 0.4" }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/node-fetch-h2": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/node-fetch-h2/-/node-fetch-h2-2.3.0.tgz", - "integrity": "sha512-ofRW94Ab0T4AOh5Fk8t0h8OBWrmjb0SSB20xh1H8YnPV9EJ+f5AMoYSUQ2zgJ4Iq2HAK0I2l5/Nequ8YzFS3Hg==", + "node_modules/regjsparser": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.10.0.tgz", + "integrity": "sha512-qx+xQGZVsy55CH0a1hiVwHmqjLryfh7wQyF5HO07XJ9f7dQMY/gPQHhlyDkIzJKC+x2fUCpCcUODUUUFrm7SHA==", + "dev": true, "dependencies": { - "http2-client": "^1.2.5" + "jsesc": "~0.5.0" }, - "engines": { - "node": "4.x || >=6.0.0" + "bin": { + "regjsparser": "bin/parser" } }, - "node_modules/node-readfiles": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/node-readfiles/-/node-readfiles-0.2.0.tgz", - "integrity": "sha512-SU00ZarexNlE4Rjdm83vglt5Y9yiQ+XI1XpflWlb7q7UTN1JUItm69xMeiQCTxtTfnzt+83T8Cx+vI2ED++VDA==", - "dependencies": { - "es6-promise": "^3.2.1" + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" } }, - "node_modules/node-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", - "dev": true, - "peer": true + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" + } }, - "node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" } }, - "node_modules/normalize-package-data/node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "node_modules/resolve": { + "version": "2.0.0-next.5", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", "dev": true, "dependencies": { "is-core-module": "^2.13.0", @@ -3571,154 +7133,132 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/normalize-package-data/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/oas-kit-common": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/oas-kit-common/-/oas-kit-common-1.0.8.tgz", - "integrity": "sha512-pJTS2+T0oGIwgjGpw7sIRU8RQMcUoKCDWFLdBqKB2BNmGpbBMH2sdqAaOXUg8OzonZHU0L7vfJu1mJFEiYDWOQ==", - "dependencies": { - "fast-safe-stringify": "^2.0.7" - } - }, - "node_modules/oas-linter": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/oas-linter/-/oas-linter-3.2.2.tgz", - "integrity": "sha512-KEGjPDVoU5K6swgo9hJVA/qYGlwfbFx+Kg2QB/kd7rzV5N8N5Mg6PlsoCMohVnQmo+pzJap/F610qTodKzecGQ==", "dependencies": { - "@exodus/schemasafe": "^1.0.0-rc.2", - "should": "^13.2.1", - "yaml": "^1.10.0" + "resolve-from": "^5.0.0" }, - "funding": { - "url": "https://github.com/Mermade/oas-kit?sponsor=1" + "engines": { + "node": ">=8" } }, - "node_modules/oas-resolver": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/oas-resolver/-/oas-resolver-2.5.6.tgz", - "integrity": "sha512-Yx5PWQNZomfEhPPOphFbZKi9W93CocQj18NlD2Pa4GWZzdZpSJvYwoiuurRI7m3SpcChrnO08hkuQDL3FGsVFQ==", - "dependencies": { - "node-fetch-h2": "^2.3.0", - "oas-kit-common": "^1.0.8", - "reftools": "^1.1.9", - "yaml": "^1.10.0", - "yargs": "^17.0.1" - }, - "bin": { - "resolve": "resolve.js" - }, - "funding": { - "url": "https://github.com/Mermade/oas-kit?sponsor=1" + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" } }, - "node_modules/oas-schema-walker": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/oas-schema-walker/-/oas-schema-walker-1.1.5.tgz", - "integrity": "sha512-2yucenq1a9YPmeNExoUa9Qwrt9RFkjqaMAA1X+U7sbb0AqBeTIdMHky9SQQ6iN94bO5NW0W4TRYXerG+BdAvAQ==", - "funding": { - "url": "https://github.com/Mermade/oas-kit?sponsor=1" + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" } }, - "node_modules/oas-validator": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/oas-validator/-/oas-validator-5.0.8.tgz", - "integrity": "sha512-cu20/HE5N5HKqVygs3dt94eYJfBi0TsZvPVXDhbXQHiEityDN+RROTleefoKRKKJ9dFAF2JBkDHgvWj0sjKGmw==", - "dependencies": { - "call-me-maybe": "^1.0.1", - "oas-kit-common": "^1.0.8", - "oas-linter": "^3.2.2", - "oas-resolver": "^2.5.6", - "oas-schema-walker": "^1.1.5", - "reftools": "^1.1.9", - "should": "^13.2.1", - "yaml": "^1.10.0" - }, - "funding": { - "url": "https://github.com/Mermade/oas-kit?sponsor=1" + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" } }, - "node_modules/oazapfts": { - "version": "5.1.7", - "resolved": "https://registry.npmjs.org/oazapfts/-/oazapfts-5.1.7.tgz", - "integrity": "sha512-/0gaUUevgIYqZKKtquc0rCkr2HFQyKFLQGsyS6SSuvVXwABEm5FJD/KzPcapB7ulOrmn8ntSbR2kzhr3TOMcNA==", + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, "dependencies": { - "@apidevtools/swagger-parser": "^10.1.0", - "lodash": "^4.17.21", - "minimist": "^1.2.8", - "swagger2openapi": "^7.0.8", - "typescript": "^5.3.3" + "glob": "^7.1.3" }, "bin": { - "oazapfts": "lib/codegen/cli.js" - } - }, - "node_modules/object-inspect": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", - "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "rimraf": "bin.js" + }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", - "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", + "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", "dev": true, "dependencies": { - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4", "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" + "isarray": "^2.0.5" }, "engines": { - "node": ">= 0.4" + "node": ">=0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/object.entries": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz", - "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==", + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "peer": true }, - "node_modules/object.fromentries": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", - "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "node_modules/safe-regex-test": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", + "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-regex": "^1.1.4" }, "engines": { "node": ">= 0.4" @@ -3727,616 +7267,810 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/object.groupby": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", - "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", + "node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "dev": true, + "peer": true, "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2" + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" }, "engines": { - "node": ">= 0.4" + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, - "node_modules/object.values": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", - "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", + "node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" + "bin": { + "semver": "bin/semver.js" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=10" } }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, + "peer": true, "dependencies": { - "wrappy": "1" + "randombytes": "^2.1.0" } }, - "node_modules/openapi-types": { - "version": "12.1.3", - "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz", - "integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==", - "peer": true - }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "dev": true, + "node_modules/set-function-length": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz", + "integrity": "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==", "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" + "define-data-property": "^1.1.2", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.1" }, "engines": { - "node": ">= 0.8.0" + "node": ">= 0.4" } }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", "dev": true, "dependencies": { - "yocto-queue": "^0.1.0" + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.4" } }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "dependencies": { - "p-limit": "^3.0.2" + "shebang-regex": "^3.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, + "node_modules/should": { + "version": "13.2.3", + "resolved": "https://registry.npmjs.org/should/-/should-13.2.3.tgz", + "integrity": "sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ==", "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" + "should-equal": "^2.0.0", + "should-format": "^3.0.3", + "should-type": "^1.4.0", + "should-type-adaptors": "^1.0.1", + "should-util": "^1.0.0" } }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, + "node_modules/should-equal": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/should-equal/-/should-equal-2.0.0.tgz", + "integrity": "sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA==", "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" + "should-type": "^1.4.0" + } + }, + "node_modules/should-format": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/should-format/-/should-format-3.0.3.tgz", + "integrity": "sha512-hZ58adtulAk0gKtua7QxevgUaXTTXxIi8t41L3zo9AHvjXO1/7sdLECuHeIN2SRtYXpNkmhoUP2pdeWgricQ+Q==", + "dependencies": { + "should-type": "^1.3.0", + "should-type-adaptors": "^1.0.1" + } + }, + "node_modules/should-type": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/should-type/-/should-type-1.4.0.tgz", + "integrity": "sha512-MdAsTu3n25yDbIe1NeN69G4n6mUnJGtSJHygX3+oN0ZbO3DTiATnf7XnYJdGT42JCXurTb1JI0qOBR65shvhPQ==" + }, + "node_modules/should-type-adaptors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/should-type-adaptors/-/should-type-adaptors-1.1.0.tgz", + "integrity": "sha512-JA4hdoLnN+kebEp2Vs8eBe9g7uy0zbRo+RMcU0EsNy+R+k049Ki+N5tT5Jagst2g7EAja+euFuoXFCa8vIklfA==", + "dependencies": { + "should-type": "^1.3.0", + "should-util": "^1.0.0" + } + }, + "node_modules/should-util": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/should-util/-/should-util-1.0.1.tgz", + "integrity": "sha512-oXF8tfxx5cDk8r2kYqlkUJzZpDBqVY/II2WhvU0n9Y3XYvAYRmeaf1PvvIvTgPnv4KJ+ES5M0PyDq5Jp+Ygy2g==" + }, + "node_modules/side-channel": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.5.tgz", + "integrity": "sha512-QcgiIWV4WV7qWExbN5llt6frQB/lBven9pqliLXfGPB+K9ZYXxDozp0wLkHS24kWCm+6YXH/f0HhnObZnZOBnQ==", + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" }, "engines": { - "node": ">=8" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, "engines": { "node": ">=8" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, - "engines": { - "node": ">=8" + "peer": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" } }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", "dev": true, - "peer": true, - "engines": { - "node": ">=8" + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" } }, - "node_modules/phone": { - "version": "2.4.22", - "resolved": "https://registry.npmjs.org/phone/-/phone-2.4.22.tgz", - "integrity": "sha512-k2f9qkIgcgbbeyFFMHDcCaYdPxq7u71EjmMvD998PEquwDvIT7zmUFe00S4hH9WPjk+IQlw9W/FlHOu1O17Tbw==", - "engines": { - "node": ">=6.10.0" + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" } }, - "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", + "node_modules/spdx-license-ids": { + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz", + "integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==", "dev": true }, - "node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", "dev": true, - "peer": true, - "engines": { - "node": ">=12" + "dependencies": { + "escape-string-regexp": "^2.0.0" }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "engines": { + "node": ">=10" } }, - "node_modules/pluralize": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", - "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", "dev": true, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/possible-typed-array-names": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", - "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", "dev": true, + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, "engines": { - "node": ">= 0.4" + "node": ">=10" } }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, "engines": { - "node": ">= 0.8.0" + "node": ">=8" } }, - "node_modules/prettier": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", - "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "node_modules/string.prototype.trim": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", + "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", "dev": true, - "bin": { - "prettier": "bin-prettier.js" + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.0", + "es-object-atoms": "^1.0.0" }, "engines": { - "node": ">=10.13.0" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/prettier-linter-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "node_modules/string.prototype.trimend": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", + "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", "dev": true, "dependencies": { - "fast-diff": "^1.1.2" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" }, - "engines": { - "node": ">=6.0.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, "engines": { - "node": ">=6" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/qs": { - "version": "6.11.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz", - "integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==", + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dependencies": { - "side-channel": "^1.0.4" + "ansi-regex": "^5.0.1" }, "engines": { - "node": ">=0.6" + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=8" } }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] + "engines": { + "node": ">=4" + } }, - "node_modules/rambda": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/rambda/-/rambda-7.5.0.tgz", - "integrity": "sha512-y/M9weqWAH4iopRd7EHDEQQvpFPHj1AA3oHozE9tfITHUtTR7Z9PSlIRRG2l1GuW7sefC1cXFfIcF+cgnShdBA==", - "dev": true + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", "dev": true, - "peer": true, "dependencies": { - "safe-buffer": "^5.1.0" + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stripe": { + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/stripe/-/stripe-19.3.0.tgz", + "integrity": "sha512-3MbqRkw5LXb4LWP1LgIEYxUAYhYDDU5pcHZj4Xha6VWPnN1wrUmQ7Htsgm8wR584s0hn1aQg1lYD0Hi+F37E5g==", "dependencies": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" + "qs": "^6.11.0" }, "engines": { - "node": ">=8" + "node": ">=16" + }, + "peerDependencies": { + "@types/node": ">=16" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, - "node_modules/read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "dependencies": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" + "has-flag": "^4.0.0" }, "engines": { "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/read-pkg-up/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "node_modules/swagger2openapi": { + "version": "7.0.8", + "resolved": "https://registry.npmjs.org/swagger2openapi/-/swagger2openapi-7.0.8.tgz", + "integrity": "sha512-upi/0ZGkYgEcLeGieoz8gT74oWHA0E7JivX7aN9mAf+Tc7BQoRBvnIGHoPDw+f9TXTW4s6kGYCZJtauP6OYp7g==", + "dependencies": { + "call-me-maybe": "^1.0.1", + "node-fetch": "^2.6.1", + "node-fetch-h2": "^2.3.0", + "node-readfiles": "^0.2.0", + "oas-kit-common": "^1.0.8", + "oas-resolver": "^2.5.6", + "oas-schema-walker": "^1.1.5", + "oas-validator": "^5.0.8", + "reftools": "^1.1.9", + "yaml": "^1.10.0", + "yargs": "^17.0.1" + }, + "bin": { + "boast": "boast.js", + "oas-validate": "oas-validate.js", + "swagger2openapi": "swagger2openapi.js" + }, + "funding": { + "url": "https://github.com/Mermade/oas-kit?sponsor=1" + } + }, + "node_modules/synckit": { + "version": "0.11.11", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.11.tgz", + "integrity": "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==", "dev": true, "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" + "@pkgr/core": "^0.2.9" }, "engines": { - "node": ">=8" + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/synckit" } }, - "node_modules/read-pkg-up/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "node_modules/tapable": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.1.10.tgz", + "integrity": "sha512-jX8Et4hHg57mug1/079yitEKWGB3LCwoxByLsNim89LABq8NqgiX+6iYVOsq0vX8uJHkU+DZ5fnq95f800bEsQ==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/terser": { + "version": "5.31.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.0.tgz", + "integrity": "sha512-Q1JFAoUKE5IMfI4Z/lkE/E6+SwgzO+x4tq4v1AyBLRj8VSYvRO6A/rQrPg1yud4g0En9EKI1TvFRF2tQFcoUkg==", "dev": true, + "peer": true, "dependencies": { - "p-locate": "^4.1.0" + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" }, "engines": { - "node": ">=8" + "node": ">=10" } }, - "node_modules/read-pkg-up/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "node_modules/terser-webpack-plugin": { + "version": "5.3.10", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", + "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", "dev": true, + "peer": true, "dependencies": { - "p-try": "^2.0.0" + "@jridgewell/trace-mapping": "^0.3.20", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.1", + "terser": "^5.26.0" }, "engines": { - "node": ">=6" + "node": ">= 10.13.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } } }, - "node_modules/read-pkg-up/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", "dev": true, "dependencies": { - "p-limit": "^2.2.0" + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" }, "engines": { "node": ">=8" } }, - "node_modules/read-pkg-up/node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "node_modules/test-exclude/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, - "engines": { - "node": ">=8" + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/read-pkg/node_modules/type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "node_modules/test-exclude/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, + "dependencies": { + "brace-expansion": "^1.1.7" + }, "engines": { - "node": ">=8" + "node": "*" } }, - "node_modules/reftools": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/reftools/-/reftools-1.1.9.tgz", - "integrity": "sha512-OVede/NQE13xBQ+ob5CKd5KyeJYU2YInb1bmV4nRoOfquZPkAkxuOXicSe1PvqIuZZ4kD13sPKBbR7UFDmli6w==", - "funding": { - "url": "https://github.com/Mermade/oas-kit?sponsor=1" + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" } }, - "node_modules/regexp-tree": { - "version": "0.1.27", - "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", - "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==", + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/ts-api-utils": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", "dev": true, - "bin": { - "regexp-tree": "bin/regexp-tree" + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" } }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", - "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", + "node_modules/ts-jest": { + "version": "29.4.5", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.5.tgz", + "integrity": "sha512-HO3GyiWn2qvTQA4kTgjDcXiMwYQt68a1Y8+JuLRVpdIzm+UOLSHgl/XqR4c6nzJkq5rOkjc02O2I7P7l/Yof0Q==", "dev": true, "dependencies": { - "call-bind": "^1.0.6", - "define-properties": "^1.2.1", - "es-errors": "^1.3.0", - "set-function-name": "^2.0.1" + "bs-logger": "^0.2.6", + "fast-json-stable-stringify": "^2.1.0", + "handlebars": "^4.7.8", + "json5": "^2.2.3", + "lodash.memoize": "^4.1.2", + "make-error": "^1.3.6", + "semver": "^7.7.3", + "type-fest": "^4.41.0", + "yargs-parser": "^21.1.1" + }, + "bin": { + "ts-jest": "cli.js" }, "engines": { - "node": ">= 0.4" + "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/transform": "^29.0.0 || ^30.0.0", + "@jest/types": "^29.0.0 || ^30.0.0", + "babel-jest": "^29.0.0 || ^30.0.0", + "jest": "^29.0.0 || ^30.0.0", + "jest-util": "^29.0.0 || ^30.0.0", + "typescript": ">=4.3 <6" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/transform": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jest-util": { + "optional": true + } } }, - "node_modules/regjsparser": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.10.0.tgz", - "integrity": "sha512-qx+xQGZVsy55CH0a1hiVwHmqjLryfh7wQyF5HO07XJ9f7dQMY/gPQHhlyDkIzJKC+x2fUCpCcUODUUUFrm7SHA==", + "node_modules/ts-jest/node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, - "dependencies": { - "jsesc": "~0.5.0" - }, "bin": { - "regjsparser": "bin/parser" + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" } }, - "node_modules/regjsparser/node_modules/jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "node_modules/ts-jest/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", "dev": true, - "bin": { - "jsesc": "bin/jsesc" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "engines": { - "node": ">=0.10.0" + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "engines": { - "node": ">=0.10.0" + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" } }, - "node_modules/resolve": { - "version": "2.0.0-next.5", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", - "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "optional": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" + "prelude-ls": "^1.2.1" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">= 0.8.0" } }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", "dev": true, "engines": { "node": ">=4" } }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "node_modules/typed-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", + "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], "dependencies": { - "queue-microtask": "^1.2.2" + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" } }, - "node_modules/safe-array-concat": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", - "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", + "node_modules/typed-array-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", + "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", "dev": true, "dependencies": { "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4", - "has-symbols": "^1.0.3", - "isarray": "^2.0.5" + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" }, "engines": { - "node": ">=0.4" + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "peer": true - }, - "node_modules/safe-regex-test": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", - "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", + "node_modules/typed-array-byte-offset": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", + "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", "dev": true, "dependencies": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-regex": "^1.1.4" + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" }, "engines": { "node": ">= 0.4" @@ -4345,286 +8079,353 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "node_modules/typed-array-length": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", + "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", "dev": true, - "peer": true, "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0" }, "engines": { - "node": ">= 10.13.0" + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", - "dev": true, + "node_modules/typescript": { + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "bin": { - "semver": "bin/semver.js" + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" }, "engines": { - "node": ">=10" + "node": ">=14.17" } }, - "node_modules/serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", "dev": true, - "peer": true, - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/set-function-length": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz", - "integrity": "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==", - "dependencies": { - "define-data-property": "^1.1.2", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.3", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.1" + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" }, "engines": { - "node": ">= 0.4" + "node": ">=0.8.0" } }, - "node_modules/set-function-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", - "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", "dev": true, "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.2" + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" }, - "engines": { - "node": ">= 0.4" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "devOptional": true + }, + "node_modules/unrs-resolver": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz", + "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==", "dev": true, + "hasInstallScript": true, "dependencies": { - "shebang-regex": "^3.0.0" + "napi-postinstall": "^0.3.0" }, - "engines": { - "node": ">=8" + "funding": { + "url": "https://opencollective.com/unrs-resolver" + }, + "optionalDependencies": { + "@unrs/resolver-binding-android-arm-eabi": "1.11.1", + "@unrs/resolver-binding-android-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-x64": "1.11.1", + "@unrs/resolver-binding-freebsd-x64": "1.11.1", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", + "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-musl": "1.11.1", + "@unrs/resolver-binding-wasm32-wasi": "1.11.1", + "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", + "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", + "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" } }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "node_modules/update-browserslist-db": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz", + "integrity": "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==", "dev": true, - "engines": { - "node": ">=8" + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" } }, - "node_modules/should": { - "version": "13.2.3", - "resolved": "https://registry.npmjs.org/should/-/should-13.2.3.tgz", - "integrity": "sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ==", + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dependencies": { - "should-equal": "^2.0.0", - "should-format": "^3.0.3", - "should-type": "^1.4.0", - "should-type-adaptors": "^1.0.1", - "should-util": "^1.0.0" + "punycode": "^2.1.0" } }, - "node_modules/should-equal": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/should-equal/-/should-equal-2.0.0.tgz", - "integrity": "sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA==", + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, "dependencies": { - "should-type": "^1.4.0" + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" } }, - "node_modules/should-format": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/should-format/-/should-format-3.0.3.tgz", - "integrity": "sha512-hZ58adtulAk0gKtua7QxevgUaXTTXxIi8t41L3zo9AHvjXO1/7sdLECuHeIN2SRtYXpNkmhoUP2pdeWgricQ+Q==", + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, "dependencies": { - "should-type": "^1.3.0", - "should-type-adaptors": "^1.0.1" + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" } }, - "node_modules/should-type": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/should-type/-/should-type-1.4.0.tgz", - "integrity": "sha512-MdAsTu3n25yDbIe1NeN69G4n6mUnJGtSJHygX3+oN0ZbO3DTiATnf7XnYJdGT42JCXurTb1JI0qOBR65shvhPQ==" - }, - "node_modules/should-type-adaptors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/should-type-adaptors/-/should-type-adaptors-1.1.0.tgz", - "integrity": "sha512-JA4hdoLnN+kebEp2Vs8eBe9g7uy0zbRo+RMcU0EsNy+R+k049Ki+N5tT5Jagst2g7EAja+euFuoXFCa8vIklfA==", + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, "dependencies": { - "should-type": "^1.3.0", - "should-util": "^1.0.0" + "makeerror": "1.0.12" } }, - "node_modules/should-util": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/should-util/-/should-util-1.0.1.tgz", - "integrity": "sha512-oXF8tfxx5cDk8r2kYqlkUJzZpDBqVY/II2WhvU0n9Y3XYvAYRmeaf1PvvIvTgPnv4KJ+ES5M0PyDq5Jp+Ygy2g==" - }, - "node_modules/side-channel": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.5.tgz", - "integrity": "sha512-QcgiIWV4WV7qWExbN5llt6frQB/lBven9pqliLXfGPB+K9ZYXxDozp0wLkHS24kWCm+6YXH/f0HhnObZnZOBnQ==", + "node_modules/watchpack": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.1.tgz", + "integrity": "sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==", + "dev": true, + "peer": true, "dependencies": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=10.13.0" } }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/webpack": { + "version": "5.91.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.91.0.tgz", + "integrity": "sha512-rzVwlLeBWHJbmgTC/8TvAcu5vpJNII+MelQpylD4jNERPwpBJOE2lEcko1zJX3QJeLjTTAnQxn/OJ8bjDzVQaw==", "dev": true, "peer": true, + "dependencies": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^1.0.5", + "@webassemblyjs/ast": "^1.12.1", + "@webassemblyjs/wasm-edit": "^1.12.1", + "@webassemblyjs/wasm-parser": "^1.12.1", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.9.0", + "browserslist": "^4.21.10", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.16.0", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.2.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.10", + "watchpack": "^2.4.1", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, "engines": { - "node": ">=8" + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } } }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", "dev": true, "peer": true, "engines": { - "node": ">=0.10.0" + "node": ">=10.13.0" } }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "node_modules/webpack/node_modules/enhanced-resolve": { + "version": "5.16.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.1.tgz", + "integrity": "sha512-4U5pNsuDl0EhuZpq46M5xPslstkviJuhrdobaRDBk2Jy2KO37FDAJl4lb2KlNabxT0m4MTK2UHNrsAcphE8nyw==", "dev": true, "peer": true, "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" } }, - "node_modules/spdx-correct": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", - "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "node_modules/webpack/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, + "peer": true, "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" } }, - "node_modules/spdx-exceptions": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", - "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", - "dev": true - }, - "node_modules/spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "node_modules/webpack/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true, - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" + "peer": true, + "engines": { + "node": ">=4.0" } }, - "node_modules/spdx-license-ids": { - "version": "3.0.17", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz", - "integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==", - "dev": true - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, + "node_modules/webpack/node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "peer": true, "engines": { - "node": ">=8" + "node": ">=6" } }, - "node_modules/string.prototype.trim": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", - "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.0", - "es-object-atoms": "^1.0.0" + "isexe": "^2.0.0" }, - "engines": { - "node": ">= 0.4" + "bin": { + "node-which": "bin/node-which" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">= 8" } }, - "node_modules/string.prototype.trimend": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", - "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", - "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "node_modules/which-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", + "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", "dev": true, "dependencies": { + "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -4633,964 +8434,1186 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dependencies": { - "ansi-regex": "^5.0.1" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/write-file-atomic": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", "dev": true, "dependencies": { - "min-indent": "^1.0.0" + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" }, "engines": { - "node": ">=8" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "node_modules/write-file-atomic/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, "engines": { - "node": ">=8" + "node": ">=14" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/stripe": { - "version": "14.5.0", - "resolved": "https://registry.npmjs.org/stripe/-/stripe-14.5.0.tgz", - "integrity": "sha512-MTt0P0VYDAj2VZyZMW41AxcXVs9s06EDFIHYat4UUkluJgnBHo4T4E2byPhnxoJCPvdzWBwBLnhWbSOg//VTpA==", - "dependencies": { - "@types/node": ">=8.1.0", - "qs": "^6.11.0" - }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "engines": { - "node": ">=12.*" + "node": ">=10" } }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dependencies": { - "has-flag": "^4.0.0" + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" }, "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, "engines": { - "node": ">= 0.4" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } - }, - "node_modules/swagger2openapi": { - "version": "7.0.8", - "resolved": "https://registry.npmjs.org/swagger2openapi/-/swagger2openapi-7.0.8.tgz", - "integrity": "sha512-upi/0ZGkYgEcLeGieoz8gT74oWHA0E7JivX7aN9mAf+Tc7BQoRBvnIGHoPDw+f9TXTW4s6kGYCZJtauP6OYp7g==", - "dependencies": { + } + }, + "dependencies": { + "@apidevtools/json-schema-ref-parser": { + "version": "9.0.6", + "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.0.6.tgz", + "integrity": "sha512-M3YgsLjI0lZxvrpeGVk9Ap032W6TPQkH6pRAZz81Ac3WUNF79VQooAFnp8umjvVzUmD93NkogxEwbSce7qMsUg==", + "requires": { + "@jsdevtools/ono": "^7.1.3", "call-me-maybe": "^1.0.1", - "node-fetch": "^2.6.1", - "node-fetch-h2": "^2.3.0", - "node-readfiles": "^0.2.0", - "oas-kit-common": "^1.0.8", - "oas-resolver": "^2.5.6", - "oas-schema-walker": "^1.1.5", - "oas-validator": "^5.0.8", - "reftools": "^1.1.9", - "yaml": "^1.10.0", - "yargs": "^17.0.1" + "js-yaml": "^3.13.1" }, - "bin": { - "boast": "boast.js", - "oas-validate": "oas-validate.js", - "swagger2openapi": "swagger2openapi.js" + "dependencies": { + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + } + } + }, + "@apidevtools/openapi-schemas": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@apidevtools/openapi-schemas/-/openapi-schemas-2.1.0.tgz", + "integrity": "sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==" + }, + "@apidevtools/swagger-methods": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz", + "integrity": "sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==" + }, + "@apidevtools/swagger-parser": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/@apidevtools/swagger-parser/-/swagger-parser-10.1.0.tgz", + "integrity": "sha512-9Kt7EuS/7WbMAUv2gSziqjvxwDbFSg3Xeyfuj5laUODX8o/k/CpsAKiQ8W7/R88eXFTMbJYg6+7uAmOWNKmwnw==", + "requires": { + "@apidevtools/json-schema-ref-parser": "9.0.6", + "@apidevtools/openapi-schemas": "^2.1.0", + "@apidevtools/swagger-methods": "^3.0.2", + "@jsdevtools/ono": "^7.1.3", + "ajv": "^8.6.3", + "ajv-draft-04": "^1.0.0", + "call-me-maybe": "^1.0.1" }, - "funding": { - "url": "https://github.com/Mermade/oas-kit?sponsor=1" + "dependencies": { + "ajv": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz", + "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==", + "requires": { + "fast-deep-equal": "^3.1.3", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.4.1" + } + }, + "ajv-draft-04": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", + "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", + "requires": {} + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + } } }, - "node_modules/tapable": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.1.10.tgz", - "integrity": "sha512-jX8Et4hHg57mug1/079yitEKWGB3LCwoxByLsNim89LABq8NqgiX+6iYVOsq0vX8uJHkU+DZ5fnq95f800bEsQ==", + "@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", "dev": true, - "engines": { - "node": ">=0.6" + "requires": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + } + }, + "@babel/compat-data": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz", + "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", + "dev": true + }, + "@babel/core": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", + "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "dependencies": { + "json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true + }, + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true + } } }, - "node_modules/terser": { - "version": "5.31.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.0.tgz", - "integrity": "sha512-Q1JFAoUKE5IMfI4Z/lkE/E6+SwgzO+x4tq4v1AyBLRj8VSYvRO6A/rQrPg1yud4g0En9EKI1TvFRF2tQFcoUkg==", + "@babel/generator": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", + "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", "dev": true, - "peer": true, - "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.8.2", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" + "requires": { + "@babel/parser": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" } }, - "node_modules/terser-webpack-plugin": { - "version": "5.3.10", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", - "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", + "@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", "dev": true, - "peer": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.20", - "jest-worker": "^27.4.5", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.1", - "terser": "^5.26.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" + "requires": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "uglify-js": { - "optional": true + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true } } }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", "dev": true }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", "dev": true, - "peer": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" + "requires": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" } }, - "node_modules/topo": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/topo/-/topo-2.1.1.tgz", - "integrity": "sha512-ZPrPP5nwzZy1fw9abHQH2k+YarTgp9UMAztcB3MmlcZSif63Eg+az05p6wTDaZmnqpS3Mk7K+2W60iHarlz8Ug==", - "deprecated": "This module has moved and is now available at @hapi/topo. Please update your dependencies as this version is no longer maintained and may contain bugs and security issues.", - "dependencies": { - "hoek": "4.x.x" - }, - "engines": { - "node": ">=6.0.0" + "@babel/helper-module-transforms": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" } }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + "@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true }, - "node_modules/ts-api-utils": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", - "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true + }, + "@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true + }, + "@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true + }, + "@babel/helpers": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", "dev": true, - "peer": true, - "engines": { - "node": ">=16" - }, - "peerDependencies": { - "typescript": ">=4.2.0" + "requires": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" } }, - "node_modules/tsconfig-paths": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", - "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "@babel/parser": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", "dev": true, - "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" + "requires": { + "@babel/types": "^7.28.5" } }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" } }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" } }, - "node_modules/typed-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", - "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", + "@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" } }, - "node_modules/typed-array-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", - "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", + "@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" } }, - "node_modules/typed-array-byte-offset": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", - "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", + "@babel/plugin-syntax-import-attributes": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", + "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "requires": { + "@babel/helper-plugin-utils": "^7.27.1" } }, - "node_modules/typed-array-length": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", - "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", + "@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" } }, - "node_modules/typescript": { - "version": "5.4.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", - "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" + "@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" } }, - "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "@babel/plugin-syntax-jsx": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", + "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "requires": { + "@babel/helper-plugin-utils": "^7.27.1" } }, - "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" - }, - "node_modules/update-browserslist-db": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz", - "integrity": "sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==", + "@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "peer": true, - "dependencies": { - "escalade": "^3.1.2", - "picocolors": "^1.0.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" } }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dependencies": { - "punycode": "^2.1.0" + "@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" } }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", "dev": true, - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" } }, - "node_modules/watchpack": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.1.tgz", - "integrity": "sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==", + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", "dev": true, - "peer": true, - "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" } }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } }, - "node_modules/webpack": { - "version": "5.91.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.91.0.tgz", - "integrity": "sha512-rzVwlLeBWHJbmgTC/8TvAcu5vpJNII+MelQpylD4jNERPwpBJOE2lEcko1zJX3QJeLjTTAnQxn/OJ8bjDzVQaw==", + "@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", "dev": true, - "peer": true, - "dependencies": { - "@types/eslint-scope": "^3.7.3", - "@types/estree": "^1.0.5", - "@webassemblyjs/ast": "^1.12.1", - "@webassemblyjs/wasm-edit": "^1.12.1", - "@webassemblyjs/wasm-parser": "^1.12.1", - "acorn": "^8.7.1", - "acorn-import-assertions": "^1.9.0", - "browserslist": "^4.21.10", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.16.0", - "es-module-lexer": "^1.2.1", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.11", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^3.2.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.10", - "watchpack": "^2.4.1", - "webpack-sources": "^3.2.3" - }, - "bin": { - "webpack": "bin/webpack.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" } }, - "node_modules/webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", "dev": true, - "peer": true, - "engines": { - "node": ">=10.13.0" + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" } }, - "node_modules/webpack/node_modules/enhanced-resolve": { - "version": "5.16.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.1.tgz", - "integrity": "sha512-4U5pNsuDl0EhuZpq46M5xPslstkviJuhrdobaRDBk2Jy2KO37FDAJl4lb2KlNabxT0m4MTK2UHNrsAcphE8nyw==", + "@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", "dev": true, - "peer": true, - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, - "engines": { - "node": ">=10.13.0" + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" } }, - "node_modules/webpack/node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "@babel/plugin-syntax-typescript": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", + "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", "dev": true, - "peer": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" + "requires": { + "@babel/helper-plugin-utils": "^7.27.1" } }, - "node_modules/webpack/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", "dev": true, - "peer": true, - "engines": { - "node": ">=4.0" + "requires": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" } }, - "node_modules/webpack/node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "@babel/traverse": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", + "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", "dev": true, - "peer": true, - "engines": { - "node": ">=6" + "requires": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.5", + "debug": "^4.3.1" } }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" + "@babel/types": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" } }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "@emnapi/core": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.7.0.tgz", + "integrity": "sha512-pJdKGq/1iquWYtv1RRSljZklxHCOCAJFJrImO5ZLKPJVJlVUcs8yFwNQlqS0Lo8xT1VAXXTCZocF9n26FWEKsw==", "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" + "optional": true, + "requires": { + "@emnapi/wasi-threads": "1.1.0", + "tslib": "^2.4.0" } }, - "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "@emnapi/runtime": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.0.tgz", + "integrity": "sha512-oAYoQnCYaQZKVS53Fq23ceWMRxq5EhQsE0x0RdQ55jT7wagMu5k+fS39v1fiSLrtrLQlXwVINenqhLMtTrV/1Q==", "dev": true, - "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "optional": true, + "requires": { + "tslib": "^2.4.0" } }, - "node_modules/which-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", - "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", + "@emnapi/wasi-threads": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", + "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "optional": true, + "requires": { + "tslib": "^2.4.0" } }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", "dev": true, - "engines": { - "node": ">=0.10.0" + "requires": { + "eslint-visitor-keys": "^3.3.0" } }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" + "@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true + }, + "@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.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" }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "dependencies": { + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } } }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "@eslint/js": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", + "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", "dev": true }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "engines": { - "node": ">=10" - } + "@exodus/schemasafe": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@exodus/schemasafe/-/schemasafe-1.3.0.tgz", + "integrity": "sha512-5Aap/GaRupgNx/feGBwLLTVv8OQFfv3pq2lPRzPg9R+IOBnDgghTGW7l7EuVXOvg5cc/xSAlRW8rBrjIC3Nvqw==" }, - "node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "engines": { - "node": ">= 6" + "@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" + }, + "@hapi/topo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", + "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", + "requires": { + "@hapi/hoek": "^9.0.0" } }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" + "@humanwhocodes/config-array": { + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", + "minimatch": "^3.0.5" }, - "engines": { - "node": ">=12" + "dependencies": { + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } } }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "engines": { - "node": ">=12" - } + "@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/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "dev": true + }, + "@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "dev": true, - "engines": { - "node": ">=10" + "requires": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "dependencies": { + "ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true + }, + "ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + } + }, + "strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + }, + "wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "requires": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + } + } } - } - }, - "dependencies": { - "@apidevtools/json-schema-ref-parser": { - "version": "9.0.6", - "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.0.6.tgz", - "integrity": "sha512-M3YgsLjI0lZxvrpeGVk9Ap032W6TPQkH6pRAZz81Ac3WUNF79VQooAFnp8umjvVzUmD93NkogxEwbSce7qMsUg==", + }, + "@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, "requires": { - "@jsdevtools/ono": "^7.1.3", - "call-me-maybe": "^1.0.1", - "js-yaml": "^3.13.1" + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" }, "dependencies": { "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, "requires": { "sprintf-js": "~1.0.2" } }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, "js-yaml": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, "requires": { "argparse": "^1.0.7", "esprima": "^4.0.0" } - } - } - }, - "@apidevtools/openapi-schemas": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@apidevtools/openapi-schemas/-/openapi-schemas-2.1.0.tgz", - "integrity": "sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==" - }, - "@apidevtools/swagger-methods": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz", - "integrity": "sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==" - }, - "@apidevtools/swagger-parser": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/@apidevtools/swagger-parser/-/swagger-parser-10.1.0.tgz", - "integrity": "sha512-9Kt7EuS/7WbMAUv2gSziqjvxwDbFSg3Xeyfuj5laUODX8o/k/CpsAKiQ8W7/R88eXFTMbJYg6+7uAmOWNKmwnw==", - "requires": { - "@apidevtools/json-schema-ref-parser": "9.0.6", - "@apidevtools/openapi-schemas": "^2.1.0", - "@apidevtools/swagger-methods": "^3.0.2", - "@jsdevtools/ono": "^7.1.3", - "ajv": "^8.6.3", - "ajv-draft-04": "^1.0.0", - "call-me-maybe": "^1.0.1" - }, - "dependencies": { - "ajv": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz", - "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==", + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, "requires": { - "fast-deep-equal": "^3.1.3", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.4.1" + "p-locate": "^4.1.0" } }, - "ajv-draft-04": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", - "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", - "requires": {} + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true } } }, - "@babel/code-frame": { - "version": "7.24.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", - "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", + "@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true + }, + "@jest/console": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-30.2.0.tgz", + "integrity": "sha512-+O1ifRjkvYIkBqASKWgLxrpEhQAAE7hY77ALLUufSk5717KfOShg6IbqLmdsLMPdUiFvA2kTs0R7YZy+l0IzZQ==", + "dev": true, + "requires": { + "@jest/types": "30.2.0", + "@types/node": "*", + "chalk": "^4.1.2", + "jest-message-util": "30.2.0", + "jest-util": "30.2.0", + "slash": "^3.0.0" + } + }, + "@jest/core": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.2.0.tgz", + "integrity": "sha512-03W6IhuhjqTlpzh/ojut/pDB2LPRygyWX8ExpgHtQA8H/3K7+1vKmcINx5UzeOX1se6YEsBsOHQ1CRzf3fOwTQ==", "dev": true, "requires": { - "@babel/highlight": "^7.24.2", - "picocolors": "^1.0.0" + "@jest/console": "30.2.0", + "@jest/pattern": "30.0.1", + "@jest/reporters": "30.2.0", + "@jest/test-result": "30.2.0", + "@jest/transform": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "exit-x": "^0.2.2", + "graceful-fs": "^4.2.11", + "jest-changed-files": "30.2.0", + "jest-config": "30.2.0", + "jest-haste-map": "30.2.0", + "jest-message-util": "30.2.0", + "jest-regex-util": "30.0.1", + "jest-resolve": "30.2.0", + "jest-resolve-dependencies": "30.2.0", + "jest-runner": "30.2.0", + "jest-runtime": "30.2.0", + "jest-snapshot": "30.2.0", + "jest-util": "30.2.0", + "jest-validate": "30.2.0", + "jest-watcher": "30.2.0", + "micromatch": "^4.0.8", + "pretty-format": "30.2.0", + "slash": "^3.0.0" } }, - "@babel/helper-validator-identifier": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz", - "integrity": "sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==", + "@jest/diff-sequences": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.0.1.tgz", + "integrity": "sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==", "dev": true }, - "@babel/highlight": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.5.tgz", - "integrity": "sha512-8lLmua6AVh/8SLJRRVD6V8p73Hir9w5mJrhE+IPpILG31KKlI9iz5zmBYKcWPS59qSfgP9RaSBQSHHE81WKuEw==", + "@jest/environment": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.2.0.tgz", + "integrity": "sha512-/QPTL7OBJQ5ac09UDRa3EQes4gt1FTEG/8jZ/4v5IVzx+Cv7dLxlVIvfvSVRiiX2drWyXeBjkMSR8hvOWSog5g==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.24.5", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "@jest/fake-timers": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "jest-mock": "30.2.0" + } + }, + "@jest/expect": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.2.0.tgz", + "integrity": "sha512-V9yxQK5erfzx99Sf+7LbhBwNWEZ9eZay8qQ9+JSC0TrMR1pMDHLMY+BnVPacWU6Jamrh252/IKo4F1Xn/zfiqA==", + "dev": true, + "requires": { + "expect": "30.2.0", + "jest-snapshot": "30.2.0" + } + }, + "@jest/expect-utils": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.2.0.tgz", + "integrity": "sha512-1JnRfhqpD8HGpOmQp180Fo9Zt69zNtC+9lR+kT7NVL05tNXIi+QC8Csz7lfidMoVLPD3FnOtcmp0CEFnxExGEA==", + "dev": true, + "requires": { + "@jest/get-type": "30.1.0" + } + }, + "@jest/fake-timers": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.2.0.tgz", + "integrity": "sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw==", + "dev": true, + "requires": { + "@jest/types": "30.2.0", + "@sinonjs/fake-timers": "^13.0.0", + "@types/node": "*", + "jest-message-util": "30.2.0", + "jest-mock": "30.2.0", + "jest-util": "30.2.0" + } + }, + "@jest/get-type": { + "version": "30.1.0", + "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz", + "integrity": "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==", + "dev": true + }, + "@jest/globals": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.2.0.tgz", + "integrity": "sha512-b63wmnKPaK+6ZZfpYhz9K61oybvbI1aMcIs80++JI1O1rR1vaxHUCNqo3ITu6NU0d4V34yZFoHMn/uoKr/Rwfw==", + "dev": true, + "requires": { + "@jest/environment": "30.2.0", + "@jest/expect": "30.2.0", + "@jest/types": "30.2.0", + "jest-mock": "30.2.0" + } + }, + "@jest/pattern": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.0.1.tgz", + "integrity": "sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==", + "dev": true, + "requires": { + "@types/node": "*", + "jest-regex-util": "30.0.1" + } + }, + "@jest/reporters": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-30.2.0.tgz", + "integrity": "sha512-DRyW6baWPqKMa9CzeiBjHwjd8XeAyco2Vt8XbcLFjiwCOEKOvy82GJ8QQnJE9ofsxCMPjH4MfH8fCWIHHDKpAQ==", + "dev": true, + "requires": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "30.2.0", + "@jest/test-result": "30.2.0", + "@jest/transform": "30.2.0", + "@jest/types": "30.2.0", + "@jridgewell/trace-mapping": "^0.3.25", + "@types/node": "*", + "chalk": "^4.1.2", + "collect-v8-coverage": "^1.0.2", + "exit-x": "^0.2.2", + "glob": "^10.3.10", + "graceful-fs": "^4.2.11", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^5.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "30.2.0", + "jest-util": "30.2.0", + "jest-worker": "30.2.0", + "slash": "^3.0.0", + "string-length": "^4.0.2", + "v8-to-istanbul": "^9.0.1" + }, + "dependencies": { + "glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" } }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "jest-worker": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.2.0.tgz", + "integrity": "sha512-0Q4Uk8WF7BUwqXHuAjc23vmopWJw5WH7w2tqBoUOZpOjW/ZnR44GXXd1r82RvnmI2GZge3ivrYXk/BE2+VtW2g==", "dev": true, "requires": { - "color-name": "1.1.3" + "@types/node": "*", + "@ungap/structured-clone": "^1.3.0", + "jest-util": "30.2.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.1.1" } }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true - }, - "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 - }, "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0" } } } }, - "@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "@jest/schemas": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", + "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", "dev": true, "requires": { - "eslint-visitor-keys": "^3.3.0" + "@sinclair/typebox": "^0.34.0" } }, - "@eslint-community/regexpp": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", - "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", - "dev": true - }, - "@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "@jest/snapshot-utils": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.2.0.tgz", + "integrity": "sha512-0aVxM3RH6DaiLcjj/b0KrIBZhSX1373Xci4l3cW5xiUWPctZ59zQ7jj4rqcJQ/Z8JuN/4wX3FpJSa3RssVvCug==", "dev": true, "requires": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.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" - }, - "dependencies": { - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - } + "@jest/types": "30.2.0", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "natural-compare": "^1.4.0" } }, - "@eslint/js": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", - "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", - "dev": true + "@jest/source-map": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-30.0.1.tgz", + "integrity": "sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.25", + "callsites": "^3.1.0", + "graceful-fs": "^4.2.11" + } }, - "@exodus/schemasafe": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@exodus/schemasafe/-/schemasafe-1.3.0.tgz", - "integrity": "sha512-5Aap/GaRupgNx/feGBwLLTVv8OQFfv3pq2lPRzPg9R+IOBnDgghTGW7l7EuVXOvg5cc/xSAlRW8rBrjIC3Nvqw==" + "@jest/test-result": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.2.0.tgz", + "integrity": "sha512-RF+Z+0CCHkARz5HT9mcQCBulb1wgCP3FBvl9VFokMX27acKphwyQsNuWH3c+ojd1LeWBLoTYoxF0zm6S/66mjg==", + "dev": true, + "requires": { + "@jest/console": "30.2.0", + "@jest/types": "30.2.0", + "@types/istanbul-lib-coverage": "^2.0.6", + "collect-v8-coverage": "^1.0.2" + } }, - "@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "@jest/test-sequencer": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.2.0.tgz", + "integrity": "sha512-wXKgU/lk8fKXMu/l5Hog1R61bL4q5GCdT6OJvdAFz1P+QrpoFuLU68eoKuVc4RbrTtNnTL5FByhWdLgOPSph+Q==", "dev": true, "requires": { - "@humanwhocodes/object-schema": "^2.0.2", - "debug": "^4.3.1", - "minimatch": "^3.0.5" - }, - "dependencies": { - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - } + "@jest/test-result": "30.2.0", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.2.0", + "slash": "^3.0.0" } }, - "@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 + "@jest/transform": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.2.0.tgz", + "integrity": "sha512-XsauDV82o5qXbhalKxD7p4TZYYdwcaEXC77PPD2HixEFF+6YGppjrAAQurTl2ECWcEomHBMMNS9AH3kcCFx8jA==", + "dev": true, + "requires": { + "@babel/core": "^7.27.4", + "@jest/types": "30.2.0", + "@jridgewell/trace-mapping": "^0.3.25", + "babel-plugin-istanbul": "^7.0.1", + "chalk": "^4.1.2", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.2.0", + "jest-regex-util": "30.0.1", + "jest-util": "30.2.0", + "micromatch": "^4.0.8", + "pirates": "^4.0.7", + "slash": "^3.0.0", + "write-file-atomic": "^5.0.1" + } }, - "@humanwhocodes/object-schema": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", - "dev": true + "@jest/types": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", + "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", + "dev": true, + "requires": { + "@jest/pattern": "30.0.1", + "@jest/schemas": "30.0.5", + "@types/istanbul-lib-coverage": "^2.0.6", + "@types/istanbul-reports": "^3.0.4", + "@types/node": "*", + "@types/yargs": "^17.0.33", + "chalk": "^4.1.2" + } }, "@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "requires": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", "dev": true, - "peer": true, "requires": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, @@ -5598,15 +9621,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "peer": true - }, - "@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, - "peer": true + "dev": true }, "@jridgewell/source-map": { "version": "0.3.6", @@ -5620,60 +9635,176 @@ } }, "@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true, - "peer": true + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true }, "@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "@jsdevtools/ono": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", + "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==" + }, + "@napi-rs/wasm-runtime": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", + "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", + "dev": true, + "optional": true, + "requires": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@tybys/wasm-util": "^0.10.0" + } + }, + "@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, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@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 + }, + "@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, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true + }, + "@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 + }, + "@rootplatform/node-sdk": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/@rootplatform/node-sdk/-/node-sdk-0.0.7.tgz", + "integrity": "sha512-jYPcPQfz7lu2Guea44ZAVNh45elFdLexS4q07LUz8Qv+wUOr7fHbqMo0LI22+fJ61eWMHJ+vYH4dc1XdsCoXQg==", + "requires": { + "oazapfts": "^5.1.7" + } + }, + "@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==", + "requires": { + "@hapi/hoek": "^9.0.0" + } + }, + "@sideway/formula": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", + "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==" + }, + "@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==" + }, + "@sinclair/typebox": { + "version": "0.34.41", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz", + "integrity": "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==", + "dev": true + }, + "@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "13.0.5", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz", + "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==", "dev": true, - "peer": true, "requires": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" + "@sinonjs/commons": "^3.0.1" } }, - "@jsdevtools/ono": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", - "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==" + "@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "dev": true, + "optional": true, + "requires": { + "tslib": "^2.4.0" + } }, - "@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==", + "@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", "dev": true, "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" } }, - "@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 + "@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } }, - "@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==", + "@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", "dev": true, "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" } }, - "@rootplatform/node-sdk": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/@rootplatform/node-sdk/-/node-sdk-0.0.7.tgz", - "integrity": "sha512-jYPcPQfz7lu2Guea44ZAVNh45elFdLexS4q07LUz8Qv+wUOr7fHbqMo0LI22+fJ61eWMHJ+vYH4dc1XdsCoXQg==", + "@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, "requires": { - "oazapfts": "^5.1.7" + "@babel/types": "^7.28.2" } }, "@types/eslint": { @@ -5705,6 +9836,40 @@ "dev": true, "peer": true }, + "@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true + }, + "@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "@types/jest": { + "version": "30.0.0", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-30.0.0.tgz", + "integrity": "sha512-XTYugzhuwqWjws0CVz8QpM36+T+Dz5mTEBKhNs/esGLnCIlGdRy+Dq78NRjd7ls7r8BC8ZRMOrKlkO1hU0JOwA==", + "dev": true, + "requires": { + "expect": "^30.0.0", + "pretty-format": "^30.0.0" + } + }, "@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -5722,6 +9887,7 @@ "version": "20.11.20", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.20.tgz", "integrity": "sha512-7/rR21OS+fq8IyHTgtLkDK949uzsa6n8BkziAKtPVpugIkO6D+/ooXMvzXxDnZrmtXVfjb1bKQafYpb8s89LOg==", + "devOptional": true, "requires": { "undici-types": "~5.26.4" } @@ -5742,128 +9908,265 @@ "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", "dev": true }, - "@types/semver": { - "version": "7.5.8", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", - "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", + "@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true + }, + "@types/yargs": { + "version": "17.0.34", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.34.tgz", + "integrity": "sha512-KExbHVa92aJpw9WDQvzBaGVE2/Pz+pLZQloT2hjL8IqsZnV62rlPOYvNnLmf/L2dyllfVUOVBj64M0z/46eR2A==", "dev": true, - "peer": true + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", - "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz", + "integrity": "sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==", "dev": true, - "peer": true, "requires": { - "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/type-utils": "6.21.0", - "@typescript-eslint/utils": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", - "debug": "^4.3.4", + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/type-utils": "7.18.0", + "@typescript-eslint/utils": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", "graphemer": "^1.4.0", - "ignore": "^5.2.4", + "ignore": "^5.3.1", "natural-compare": "^1.4.0", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" + "ts-api-utils": "^1.3.0" } }, "@typescript-eslint/parser": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", - "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.18.0.tgz", + "integrity": "sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==", "dev": true, - "peer": true, "requires": { - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/typescript-estree": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/typescript-estree": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", "debug": "^4.3.4" } }, "@typescript-eslint/scope-manager": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", - "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz", + "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==", "dev": true, - "peer": true, "requires": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0" + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0" } }, "@typescript-eslint/type-utils": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", - "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.18.0.tgz", + "integrity": "sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==", "dev": true, - "peer": true, "requires": { - "@typescript-eslint/typescript-estree": "6.21.0", - "@typescript-eslint/utils": "6.21.0", + "@typescript-eslint/typescript-estree": "7.18.0", + "@typescript-eslint/utils": "7.18.0", "debug": "^4.3.4", - "ts-api-utils": "^1.0.1" + "ts-api-utils": "^1.3.0" } }, "@typescript-eslint/types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", - "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", - "dev": true, - "peer": true + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz", + "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==", + "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", - "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz", + "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==", "dev": true, - "peer": true, "requires": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", - "minimatch": "9.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" } }, "@typescript-eslint/utils": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", - "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz", + "integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==", "dev": true, - "peer": true, "requires": { "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/typescript-estree": "6.21.0", - "semver": "^7.5.4" + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/typescript-estree": "7.18.0" } }, "@typescript-eslint/visitor-keys": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", - "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz", + "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==", "dev": true, - "peer": true, "requires": { - "@typescript-eslint/types": "6.21.0", - "eslint-visitor-keys": "^3.4.1" + "@typescript-eslint/types": "7.18.0", + "eslint-visitor-keys": "^3.4.3" } }, "@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", "dev": true }, + "@unrs/resolver-binding-android-arm-eabi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", + "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==", + "dev": true, + "optional": true + }, + "@unrs/resolver-binding-android-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", + "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==", + "dev": true, + "optional": true + }, + "@unrs/resolver-binding-darwin-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", + "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==", + "dev": true, + "optional": true + }, + "@unrs/resolver-binding-darwin-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz", + "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==", + "dev": true, + "optional": true + }, + "@unrs/resolver-binding-freebsd-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz", + "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==", + "dev": true, + "optional": true + }, + "@unrs/resolver-binding-linux-arm-gnueabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz", + "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==", + "dev": true, + "optional": true + }, + "@unrs/resolver-binding-linux-arm-musleabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz", + "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==", + "dev": true, + "optional": true + }, + "@unrs/resolver-binding-linux-arm64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz", + "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==", + "dev": true, + "optional": true + }, + "@unrs/resolver-binding-linux-arm64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz", + "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==", + "dev": true, + "optional": true + }, + "@unrs/resolver-binding-linux-ppc64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz", + "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==", + "dev": true, + "optional": true + }, + "@unrs/resolver-binding-linux-riscv64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz", + "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==", + "dev": true, + "optional": true + }, + "@unrs/resolver-binding-linux-riscv64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz", + "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==", + "dev": true, + "optional": true + }, + "@unrs/resolver-binding-linux-s390x-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz", + "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==", + "dev": true, + "optional": true + }, + "@unrs/resolver-binding-linux-x64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz", + "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==", + "dev": true, + "optional": true + }, + "@unrs/resolver-binding-linux-x64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz", + "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==", + "dev": true, + "optional": true + }, + "@unrs/resolver-binding-wasm32-wasi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz", + "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==", + "dev": true, + "optional": true, + "requires": { + "@napi-rs/wasm-runtime": "^0.2.11" + } + }, + "@unrs/resolver-binding-win32-arm64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz", + "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==", + "dev": true, + "optional": true + }, + "@unrs/resolver-binding-win32-ia32-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz", + "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==", + "dev": true, + "optional": true + }, + "@unrs/resolver-binding-win32-x64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz", + "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==", + "dev": true, + "optional": true + }, "@webassemblyjs/ast": { "version": "1.12.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", @@ -6080,6 +10383,23 @@ "peer": true, "requires": {} }, + "ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "requires": { + "type-fest": "^0.21.3" + }, + "dependencies": { + "type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true + } + } + }, "ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -6093,6 +10413,24 @@ "color-convert": "^2.0.1" } }, + "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, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "dependencies": { + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + } + } + }, "argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -6127,8 +10465,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "peer": true + "dev": true }, "array.prototype.find": { "version": "2.2.3", @@ -6212,18 +10549,93 @@ "possible-typed-array-names": "^1.0.0" } }, + "babel-jest": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.2.0.tgz", + "integrity": "sha512-0YiBEOxWqKkSQWL9nNGGEgndoeL0ZpWrbLMNL5u/Kaxrli3Eaxlt3ZtIDktEvXt4L/R9r3ODr2zKwGM/2BjxVw==", + "dev": true, + "requires": { + "@jest/transform": "30.2.0", + "@types/babel__core": "^7.20.5", + "babel-plugin-istanbul": "^7.0.1", + "babel-preset-jest": "30.2.0", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "slash": "^3.0.0" + } + }, + "babel-plugin-istanbul": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-7.0.1.tgz", + "integrity": "sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-instrument": "^6.0.2", + "test-exclude": "^6.0.0" + } + }, + "babel-plugin-jest-hoist": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.2.0.tgz", + "integrity": "sha512-ftzhzSGMUnOzcCXd6WHdBGMyuwy15Wnn0iyyWGKgBDLxf9/s5ABuraCSpBX2uG0jUg4rqJnxsLc5+oYBqoxVaA==", + "dev": true, + "requires": { + "@types/babel__core": "^7.20.5" + } + }, + "babel-preset-current-node-syntax": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", + "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", + "dev": true, + "requires": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + } + }, + "babel-preset-jest": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-30.2.0.tgz", + "integrity": "sha512-US4Z3NOieAQumwFnYdUWKvUKh8+YSnS/gB3t6YBiz0bskpu7Pine8pPCheNxlPEW4wnUkma2a94YuW2q3guvCQ==", + "dev": true, + "requires": { + "babel-plugin-jest-hoist": "30.2.0", + "babel-preset-current-node-syntax": "^1.2.0" + } + }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "baseline-browser-mapping": { + "version": "2.8.24", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.24.tgz", + "integrity": "sha512-uUhTRDPXamakPyghwrUcjaGvvBqGrWvBHReoiULMIpOJVM9IYzQh83Xk2Onx5HlGI2o10NNCzcs9TG/S3TkwrQ==", + "dev": true + }, "brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, - "peer": true, "requires": { "balanced-match": "^1.0.0" } @@ -6233,30 +10645,46 @@ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, - "peer": true, "requires": { "fill-range": "^7.1.1" } }, "browserslist": { - "version": "4.23.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", - "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", + "version": "4.27.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.27.0.tgz", + "integrity": "sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw==", + "dev": true, + "requires": { + "baseline-browser-mapping": "^2.8.19", + "caniuse-lite": "^1.0.30001751", + "electron-to-chromium": "^1.5.238", + "node-releases": "^2.0.26", + "update-browserslist-db": "^1.1.4" + } + }, + "bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "requires": { + "fast-json-stable-stringify": "2.x" + } + }, + "bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", "dev": true, - "peer": true, "requires": { - "caniuse-lite": "^1.0.30001587", - "electron-to-chromium": "^1.4.668", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" + "node-int64": "^0.4.0" } }, "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==", - "dev": true, - "peer": true + "dev": true }, "builtin-modules": { "version": "3.3.0", @@ -6287,12 +10715,17 @@ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, "caniuse-lite": { - "version": "1.0.30001620", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001620.tgz", - "integrity": "sha512-WJvYsOjd1/BYUY6SNGUosK9DUidBPDTnOARHp3fSmFO1ekdxaY6nKRttEVrfMmYi80ctS0kz1wiWmm14fVc3ew==", - "dev": true, - "peer": true + "version": "1.0.30001753", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001753.tgz", + "integrity": "sha512-Bj5H35MD/ebaOV4iDLqPEtiliTN29qkGtEHCwawWn4cYm+bPJM2NsaP30vtZcnERClMzp52J4+aw2UNbK4o+zw==", + "dev": true }, "chalk": { "version": "4.1.2", @@ -6304,6 +10737,12 @@ "supports-color": "^7.1.0" } }, + "char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true + }, "chrome-trace-event": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", @@ -6312,9 +10751,15 @@ "peer": true }, "ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.1.tgz", + "integrity": "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==", + "dev": true + }, + "cjs-module-lexer": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.1.0.tgz", + "integrity": "sha512-UX0OwmYRYQQetfrLEZeewIFFI+wSTofC+pMBLNuH3RUuu/xzG1oz84UCEDOSoQlN3fZ4+AzmV50ZYvGqkMh9yA==", "dev": true }, "clean-regexp": { @@ -6344,6 +10789,18 @@ "wrap-ansi": "^7.0.0" } }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true + }, + "collect-v8-coverage": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", + "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==", + "dev": true + }, "color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -6385,10 +10842,25 @@ "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==", "dev": true }, + "convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "core-js-compat": { + "version": "3.46.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.46.0.tgz", + "integrity": "sha512-p9hObIIEENxSV8xIu+V68JjSeARg6UVMG5mR+JEUguG3sI6MsiS1njz2jHmyJDvA+8jX/sytkBHup6kxhM9law==", + "dev": true, + "requires": { + "browserslist": "^4.26.3" + } + }, "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "requires": { "path-key": "^3.1.0", @@ -6438,12 +10910,25 @@ "ms": "2.1.2" } }, + "dedent": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.0.tgz", + "integrity": "sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ==", + "dev": true, + "requires": {} + }, "deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true + }, "define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -6471,12 +10956,17 @@ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "dev": true }, + "detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true + }, "dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "dev": true, - "peer": true, "requires": { "path-type": "^4.0.0" } @@ -6490,12 +10980,23 @@ "esutils": "^2.0.2" } }, + "eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, "electron-to-chromium": { - "version": "1.4.776", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.776.tgz", - "integrity": "sha512-s694bi3+gUzlliqxjPHpa9NRTlhzTgB34aan+pVKZmOTGy2xoZXl+8E1B8i5p5rtev3PKMK/H4asgNejC+YHNg==", - "dev": true, - "peer": true + "version": "1.5.245", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.245.tgz", + "integrity": "sha512-rdmGfW47ZhL/oWEJAY4qxRtdly2B98ooTJ0pdEI4jhVLZ6tNf8fPtov2wS1IRKwFJT92le3x4Knxiwzl7cPPpQ==", + "dev": true + }, + "emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true }, "emoji-regex": { "version": "8.0.0", @@ -6642,9 +11143,9 @@ "integrity": "sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==" }, "escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==" + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==" }, "escape-string-regexp": { "version": "4.0.0", @@ -6740,18 +11241,18 @@ } }, "eslint-config-airbnb-typescript": { - "version": "17.1.0", - "resolved": "https://registry.npmjs.org/eslint-config-airbnb-typescript/-/eslint-config-airbnb-typescript-17.1.0.tgz", - "integrity": "sha512-GPxI5URre6dDpJ0CtcthSZVBAfI+Uw7un5OYNVxP2EYi3H81Jw701yFP7AU+/vCE7xBtFmjge7kfhhk4+RAiig==", + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-typescript/-/eslint-config-airbnb-typescript-18.0.0.tgz", + "integrity": "sha512-oc+Lxzgzsu8FQyFVa4QFaVKiitTYiiW3frB9KYW5OWdPrqFc7FzxgB20hP4cHMlr+MBzGcLl3jnCOVOydL9mIg==", "dev": true, "requires": { "eslint-config-airbnb-base": "^15.0.0" } }, "eslint-config-prettier": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz", - "integrity": "sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==", + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.2.tgz", + "integrity": "sha512-iI1f+D2ViGn+uvv5HuHVUamg8ll4tN+JRHGc6IJi4TP9Kl976C57fzPXgseXNs8v0iA8aSJpHsTWjDb9QJamGQ==", "dev": true, "requires": {} }, @@ -6915,9 +11416,9 @@ } }, "eslint-plugin-mocha": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-10.4.3.tgz", - "integrity": "sha512-emc4TVjq5Ht0/upR+psftuz6IBG5q279p+1dSRDeHf+NS9aaerBi3lXKo1SEzwC29hFIW21gO89CEWSvRsi8IQ==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-10.5.0.tgz", + "integrity": "sha512-F2ALmQVPT1GoP27O1JTZGrV9Pqg8k79OeIuvw63UxMtQKREZtmkK1NFgkZQ2TW7L2JSSFKHFPTtHu5z8R9QNRw==", "dev": true, "requires": { "eslint-utils": "^3.0.0", @@ -6926,42 +11427,52 @@ } }, "eslint-plugin-prettier": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz", - "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==", + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.4.tgz", + "integrity": "sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==", "dev": true, "requires": { - "prettier-linter-helpers": "^1.0.0" + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.11.7" } }, "eslint-plugin-sonarjs": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-0.18.0.tgz", - "integrity": "sha512-DJ3osLnt6KFdT5e9ZuIDOjT5A6wUGSLeiJJT03lPgpdD+7CVWlYAw9Goe3bt7SmbFO3Xh89NOCZAuB9XA7bAUQ==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-0.25.1.tgz", + "integrity": "sha512-5IOKvj/GMBNqjxBdItfotfRHo7w48496GOu1hxdeXuD0mB1JBlDCViiLHETDTfA8pDAVSBimBEQoetRXYceQEw==", "dev": true, "requires": {} }, "eslint-plugin-unicorn": { - "version": "48.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-48.0.1.tgz", - "integrity": "sha512-FW+4r20myG/DqFcCSzoumaddKBicIPeFnTrifon2mWIzlfyvzwyqZjqVP7m4Cqr/ZYisS2aiLghkUWaPg6vtCw==", + "version": "55.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-55.0.0.tgz", + "integrity": "sha512-n3AKiVpY2/uDcGrS3+QsYDkjPfaOrNrsfQxU9nt5nitd9KuvVXrfAvgCO9DYPSfap+Gqjw9EOrXIsBp5tlHZjA==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.22.5", + "@babel/helper-validator-identifier": "^7.24.5", "@eslint-community/eslint-utils": "^4.4.0", - "ci-info": "^3.8.0", + "ci-info": "^4.0.0", "clean-regexp": "^1.0.0", + "core-js-compat": "^3.37.0", "esquery": "^1.5.0", + "globals": "^15.7.0", "indent-string": "^4.0.0", "is-builtin-module": "^3.2.1", "jsesc": "^3.0.2", - "lodash": "^4.17.21", "pluralize": "^8.0.0", "read-pkg-up": "^7.0.1", "regexp-tree": "^0.1.27", "regjsparser": "^0.10.0", - "semver": "^7.5.4", + "semver": "^7.6.1", "strip-indent": "^3.0.0" + }, + "dependencies": { + "globals": { + "version": "15.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", + "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", + "dev": true + } } }, "eslint-scope": { @@ -7050,6 +11561,43 @@ "dev": true, "peer": true }, + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + } + }, + "exit-x": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/exit-x/-/exit-x-0.2.2.tgz", + "integrity": "sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==", + "dev": true + }, + "expect": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-30.2.0.tgz", + "integrity": "sha512-u/feCi0GPsI+988gU2FLcsHyAHTU0MX1Wg68NhAnN7z/+C5wqG+CY8J53N9ioe8RXgaoz0nBR/TYMf3AycUuPw==", + "dev": true, + "requires": { + "@jest/expect-utils": "30.2.0", + "@jest/get-type": "30.1.0", + "jest-matcher-utils": "30.2.0", + "jest-message-util": "30.2.0", + "jest-mock": "30.2.0", + "jest-util": "30.2.0" + } + }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -7062,17 +11610,16 @@ "dev": true }, "fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dev": true, - "peer": true, "requires": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "micromatch": "^4.0.8" }, "dependencies": { "glob-parent": { @@ -7080,7 +11627,6 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, - "peer": true, "requires": { "is-glob": "^4.0.1" } @@ -7113,6 +11659,15 @@ "reusify": "^1.0.4" } }, + "fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "requires": { + "bser": "2.1.1" + } + }, "file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -7127,7 +11682,6 @@ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, - "peer": true, "requires": { "to-regex-range": "^5.0.1" } @@ -7171,7 +11725,25 @@ "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", "dev": true, "requires": { - "is-callable": "^1.1.3" + "is-callable": "^1.1.3" + } + }, + "foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "dependencies": { + "signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true + } } }, "form-data": { @@ -7191,6 +11763,13 @@ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, + "fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "optional": true + }, "function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -7214,6 +11793,12 @@ "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "dev": true }, + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true + }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -7231,6 +11816,18 @@ "hasown": "^2.0.0" } }, + "get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true + }, "get-symbol-description": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", @@ -7317,7 +11914,6 @@ "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, - "peer": true, "requires": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", @@ -7347,6 +11943,19 @@ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, + "handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4", + "wordwrap": "^1.0.0" + } + }, "has-bigints": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", @@ -7394,22 +12003,29 @@ "function-bind": "^1.1.2" } }, - "hoek": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.3.1.tgz", - "integrity": "sha512-v7E+yIjcHECn973i0xHm4kJkEpv3C8sbYS4344WXbzYqRyiDD7rjnnKo4hsJkejQBAFdRMUGNHySeSPKSH9Rqw==" - }, "hosted-git-info": { "version": "2.8.9", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "dev": true }, + "html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, "http2-client": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/http2-client/-/http2-client-1.3.5.tgz", "integrity": "sha512-EC2utToWl4RKfs5zd36Mxq7nzHHBuomZboI0yYL6Y0RmBgT7Sgkq4rQ0ezFTYoIsSs7Tm9SJe+o2FcAg6GBhGA==" }, + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true + }, "ignore": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", @@ -7426,6 +12042,16 @@ "resolve-from": "^4.0.0" } }, + "import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "requires": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + } + }, "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -7559,6 +12185,12 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" }, + "is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true + }, "is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -7578,8 +12210,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "peer": true + "dev": true }, "is-number-object": { "version": "1.0.7", @@ -7615,6 +12246,12 @@ "call-bind": "^1.0.7" } }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true + }, "is-string": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", @@ -7657,20 +12294,568 @@ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", "dev": true }, - "isemail": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/isemail/-/isemail-3.2.0.tgz", - "integrity": "sha512-zKqkK+O+dGqevc93KNsbZ/TqTUFd46MwWjYOoMrjIMZ51eU7DtQG3Wmd9SQQT7i7RVnuTPEiYEWHU3MSbxC1Tg==", - "requires": { - "punycode": "2.x.x" - } - }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, + "istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "requires": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + } + }, + "istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + } + }, + "istanbul-lib-source-maps": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", + "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.23", + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0" + } + }, + "istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "requires": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + } + }, + "jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "requires": { + "@isaacs/cliui": "^8.0.2", + "@pkgjs/parseargs": "^0.11.0" + } + }, + "jest": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-30.2.0.tgz", + "integrity": "sha512-F26gjC0yWN8uAA5m5Ss8ZQf5nDHWGlN/xWZIh8S5SRbsEKBovwZhxGd6LJlbZYxBgCYOtreSUyb8hpXyGC5O4A==", + "dev": true, + "requires": { + "@jest/core": "30.2.0", + "@jest/types": "30.2.0", + "import-local": "^3.2.0", + "jest-cli": "30.2.0" + } + }, + "jest-changed-files": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-30.2.0.tgz", + "integrity": "sha512-L8lR1ChrRnSdfeOvTrwZMlnWV8G/LLjQ0nG9MBclwWZidA2N5FviRki0Bvh20WRMOX31/JYvzdqTJrk5oBdydQ==", + "dev": true, + "requires": { + "execa": "^5.1.1", + "jest-util": "30.2.0", + "p-limit": "^3.1.0" + } + }, + "jest-circus": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-30.2.0.tgz", + "integrity": "sha512-Fh0096NC3ZkFx05EP2OXCxJAREVxj1BcW/i6EWqqymcgYKWjyyDpral3fMxVcHXg6oZM7iULer9wGRFvfpl+Tg==", + "dev": true, + "requires": { + "@jest/environment": "30.2.0", + "@jest/expect": "30.2.0", + "@jest/test-result": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "chalk": "^4.1.2", + "co": "^4.6.0", + "dedent": "^1.6.0", + "is-generator-fn": "^2.1.0", + "jest-each": "30.2.0", + "jest-matcher-utils": "30.2.0", + "jest-message-util": "30.2.0", + "jest-runtime": "30.2.0", + "jest-snapshot": "30.2.0", + "jest-util": "30.2.0", + "p-limit": "^3.1.0", + "pretty-format": "30.2.0", + "pure-rand": "^7.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.6" + } + }, + "jest-cli": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-30.2.0.tgz", + "integrity": "sha512-Os9ukIvADX/A9sLt6Zse3+nmHtHaE6hqOsjQtNiugFTbKRHYIYtZXNGNK9NChseXy7djFPjndX1tL0sCTlfpAA==", + "dev": true, + "requires": { + "@jest/core": "30.2.0", + "@jest/test-result": "30.2.0", + "@jest/types": "30.2.0", + "chalk": "^4.1.2", + "exit-x": "^0.2.2", + "import-local": "^3.2.0", + "jest-config": "30.2.0", + "jest-util": "30.2.0", + "jest-validate": "30.2.0", + "yargs": "^17.7.2" + } + }, + "jest-config": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-30.2.0.tgz", + "integrity": "sha512-g4WkyzFQVWHtu6uqGmQR4CQxz/CH3yDSlhzXMWzNjDx843gYjReZnMRanjRCq5XZFuQrGDxgUaiYWE8BRfVckA==", + "dev": true, + "requires": { + "@babel/core": "^7.27.4", + "@jest/get-type": "30.1.0", + "@jest/pattern": "30.0.1", + "@jest/test-sequencer": "30.2.0", + "@jest/types": "30.2.0", + "babel-jest": "30.2.0", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "deepmerge": "^4.3.1", + "glob": "^10.3.10", + "graceful-fs": "^4.2.11", + "jest-circus": "30.2.0", + "jest-docblock": "30.2.0", + "jest-environment-node": "30.2.0", + "jest-regex-util": "30.0.1", + "jest-resolve": "30.2.0", + "jest-runner": "30.2.0", + "jest-util": "30.2.0", + "jest-validate": "30.2.0", + "micromatch": "^4.0.8", + "parse-json": "^5.2.0", + "pretty-format": "30.2.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "requires": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + } + } + } + }, + "jest-diff": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.2.0.tgz", + "integrity": "sha512-dQHFo3Pt4/NLlG5z4PxZ/3yZTZ1C7s9hveiOj+GCN+uT109NC2QgsoVZsVOAvbJ3RgKkvyLGXZV9+piDpWbm6A==", + "dev": true, + "requires": { + "@jest/diff-sequences": "30.0.1", + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "pretty-format": "30.2.0" + } + }, + "jest-docblock": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-30.2.0.tgz", + "integrity": "sha512-tR/FFgZKS1CXluOQzZvNH3+0z9jXr3ldGSD8bhyuxvlVUwbeLOGynkunvlTMxchC5urrKndYiwCFC0DLVjpOCA==", + "dev": true, + "requires": { + "detect-newline": "^3.1.0" + } + }, + "jest-each": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-30.2.0.tgz", + "integrity": "sha512-lpWlJlM7bCUf1mfmuqTA8+j2lNURW9eNafOy99knBM01i5CQeY5UH1vZjgT9071nDJac1M4XsbyI44oNOdhlDQ==", + "dev": true, + "requires": { + "@jest/get-type": "30.1.0", + "@jest/types": "30.2.0", + "chalk": "^4.1.2", + "jest-util": "30.2.0", + "pretty-format": "30.2.0" + } + }, + "jest-environment-node": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.2.0.tgz", + "integrity": "sha512-ElU8v92QJ9UrYsKrxDIKCxu6PfNj4Hdcktcn0JX12zqNdqWHB0N+hwOnnBBXvjLd2vApZtuLUGs1QSY+MsXoNA==", + "dev": true, + "requires": { + "@jest/environment": "30.2.0", + "@jest/fake-timers": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "jest-mock": "30.2.0", + "jest-util": "30.2.0", + "jest-validate": "30.2.0" + } + }, + "jest-haste-map": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.2.0.tgz", + "integrity": "sha512-sQA/jCb9kNt+neM0anSj6eZhLZUIhQgwDt7cPGjumgLM4rXsfb9kpnlacmvZz3Q5tb80nS+oG/if+NBKrHC+Xw==", + "dev": true, + "requires": { + "@jest/types": "30.2.0", + "@types/node": "*", + "anymatch": "^3.1.3", + "fb-watchman": "^2.0.2", + "fsevents": "^2.3.3", + "graceful-fs": "^4.2.11", + "jest-regex-util": "30.0.1", + "jest-util": "30.2.0", + "jest-worker": "30.2.0", + "micromatch": "^4.0.8", + "walker": "^1.0.8" + }, + "dependencies": { + "jest-worker": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.2.0.tgz", + "integrity": "sha512-0Q4Uk8WF7BUwqXHuAjc23vmopWJw5WH7w2tqBoUOZpOjW/ZnR44GXXd1r82RvnmI2GZge3ivrYXk/BE2+VtW2g==", + "dev": true, + "requires": { + "@types/node": "*", + "@ungap/structured-clone": "^1.3.0", + "jest-util": "30.2.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.1.1" + } + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-leak-detector": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.2.0.tgz", + "integrity": "sha512-M6jKAjyzjHG0SrQgwhgZGy9hFazcudwCNovY/9HPIicmNSBuockPSedAP9vlPK6ONFJ1zfyH/M2/YYJxOz5cdQ==", + "dev": true, + "requires": { + "@jest/get-type": "30.1.0", + "pretty-format": "30.2.0" + } + }, + "jest-matcher-utils": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.2.0.tgz", + "integrity": "sha512-dQ94Nq4dbzmUWkQ0ANAWS9tBRfqCrn0bV9AMYdOi/MHW726xn7eQmMeRTpX2ViC00bpNaWXq+7o4lIQ3AX13Hg==", + "dev": true, + "requires": { + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "jest-diff": "30.2.0", + "pretty-format": "30.2.0" + } + }, + "jest-message-util": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.2.0.tgz", + "integrity": "sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.27.1", + "@jest/types": "30.2.0", + "@types/stack-utils": "^2.0.3", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "micromatch": "^4.0.8", + "pretty-format": "30.2.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.6" + } + }, + "jest-mock": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.2.0.tgz", + "integrity": "sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw==", + "dev": true, + "requires": { + "@jest/types": "30.2.0", + "@types/node": "*", + "jest-util": "30.2.0" + } + }, + "jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "requires": {} + }, + "jest-regex-util": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.0.1.tgz", + "integrity": "sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==", + "dev": true + }, + "jest-resolve": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-30.2.0.tgz", + "integrity": "sha512-TCrHSxPlx3tBY3hWNtRQKbtgLhsXa1WmbJEqBlTBrGafd5fiQFByy2GNCEoGR+Tns8d15GaL9cxEzKOO3GEb2A==", + "dev": true, + "requires": { + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.2.0", + "jest-pnp-resolver": "^1.2.3", + "jest-util": "30.2.0", + "jest-validate": "30.2.0", + "slash": "^3.0.0", + "unrs-resolver": "^1.7.11" + } + }, + "jest-resolve-dependencies": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-30.2.0.tgz", + "integrity": "sha512-xTOIGug/0RmIe3mmCqCT95yO0vj6JURrn1TKWlNbhiAefJRWINNPgwVkrVgt/YaerPzY3iItufd80v3lOrFJ2w==", + "dev": true, + "requires": { + "jest-regex-util": "30.0.1", + "jest-snapshot": "30.2.0" + } + }, + "jest-runner": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-30.2.0.tgz", + "integrity": "sha512-PqvZ2B2XEyPEbclp+gV6KO/F1FIFSbIwewRgmROCMBo/aZ6J1w8Qypoj2pEOcg3G2HzLlaP6VUtvwCI8dM3oqQ==", + "dev": true, + "requires": { + "@jest/console": "30.2.0", + "@jest/environment": "30.2.0", + "@jest/test-result": "30.2.0", + "@jest/transform": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "chalk": "^4.1.2", + "emittery": "^0.13.1", + "exit-x": "^0.2.2", + "graceful-fs": "^4.2.11", + "jest-docblock": "30.2.0", + "jest-environment-node": "30.2.0", + "jest-haste-map": "30.2.0", + "jest-leak-detector": "30.2.0", + "jest-message-util": "30.2.0", + "jest-resolve": "30.2.0", + "jest-runtime": "30.2.0", + "jest-util": "30.2.0", + "jest-watcher": "30.2.0", + "jest-worker": "30.2.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "dependencies": { + "jest-worker": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.2.0.tgz", + "integrity": "sha512-0Q4Uk8WF7BUwqXHuAjc23vmopWJw5WH7w2tqBoUOZpOjW/ZnR44GXXd1r82RvnmI2GZge3ivrYXk/BE2+VtW2g==", + "dev": true, + "requires": { + "@types/node": "*", + "@ungap/structured-clone": "^1.3.0", + "jest-util": "30.2.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.1.1" + } + }, + "source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-runtime": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.2.0.tgz", + "integrity": "sha512-p1+GVX/PJqTucvsmERPMgCPvQJpFt4hFbM+VN3n8TMo47decMUcJbt+rgzwrEme0MQUA/R+1de2axftTHkKckg==", + "dev": true, + "requires": { + "@jest/environment": "30.2.0", + "@jest/fake-timers": "30.2.0", + "@jest/globals": "30.2.0", + "@jest/source-map": "30.0.1", + "@jest/test-result": "30.2.0", + "@jest/transform": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "chalk": "^4.1.2", + "cjs-module-lexer": "^2.1.0", + "collect-v8-coverage": "^1.0.2", + "glob": "^10.3.10", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.2.0", + "jest-message-util": "30.2.0", + "jest-mock": "30.2.0", + "jest-regex-util": "30.0.1", + "jest-resolve": "30.2.0", + "jest-snapshot": "30.2.0", + "jest-util": "30.2.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "dependencies": { + "glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "requires": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + } + }, + "strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true + } + } + }, + "jest-snapshot": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.2.0.tgz", + "integrity": "sha512-5WEtTy2jXPFypadKNpbNkZ72puZCa6UjSr/7djeecHWOu7iYhSXSnHScT8wBz3Rn8Ena5d5RYRcsyKIeqG1IyA==", + "dev": true, + "requires": { + "@babel/core": "^7.27.4", + "@babel/generator": "^7.27.5", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/plugin-syntax-typescript": "^7.27.1", + "@babel/types": "^7.27.3", + "@jest/expect-utils": "30.2.0", + "@jest/get-type": "30.1.0", + "@jest/snapshot-utils": "30.2.0", + "@jest/transform": "30.2.0", + "@jest/types": "30.2.0", + "babel-preset-current-node-syntax": "^1.2.0", + "chalk": "^4.1.2", + "expect": "30.2.0", + "graceful-fs": "^4.2.11", + "jest-diff": "30.2.0", + "jest-matcher-utils": "30.2.0", + "jest-message-util": "30.2.0", + "jest-util": "30.2.0", + "pretty-format": "30.2.0", + "semver": "^7.7.2", + "synckit": "^0.11.8" + } + }, + "jest-util": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.2.0.tgz", + "integrity": "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==", + "dev": true, + "requires": { + "@jest/types": "30.2.0", + "@types/node": "*", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "graceful-fs": "^4.2.11", + "picomatch": "^4.0.2" + } + }, + "jest-validate": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.2.0.tgz", + "integrity": "sha512-FBGWi7dP2hpdi8nBoWxSsLvBFewKAg0+uSQwBaof4Y4DPgBabXgpSYC5/lR7VmnIlSpASmCi/ntRWPbv7089Pw==", + "dev": true, + "requires": { + "@jest/get-type": "30.1.0", + "@jest/types": "30.2.0", + "camelcase": "^6.3.0", + "chalk": "^4.1.2", + "leven": "^3.1.0", + "pretty-format": "30.2.0" + }, + "dependencies": { + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true + } + } + }, + "jest-watcher": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-30.2.0.tgz", + "integrity": "sha512-PYxa28dxJ9g777pGm/7PrbnMeA0Jr7osHP9bS7eJy9DuAjMgdGtxgf0uKMyoIsTWAkIbUW5hSDdJ3urmgXBqxg==", + "dev": true, + "requires": { + "@jest/test-result": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "emittery": "^0.13.1", + "jest-util": "30.2.0", + "string-length": "^4.0.2" + } + }, "jest-worker": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", @@ -7696,13 +12881,15 @@ } }, "joi": { - "version": "11.4.0", - "resolved": "https://registry.npmjs.org/joi/-/joi-11.4.0.tgz", - "integrity": "sha512-O7Uw+w/zEWgbL6OcHbyACKSj0PkQeUgmehdoXVSxt92QFCq4+1390Rwh5moI2K/OgC7D8RHRZqHZxT2husMJHA==", + "version": "17.13.3", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", + "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", "requires": { - "hoek": "4.x.x", - "isemail": "3.x.x", - "topo": "2.x.x" + "@hapi/hoek": "^9.3.0", + "@hapi/topo": "^5.1.0", + "@sideway/address": "^4.1.5", + "@sideway/formula": "^3.0.1", + "@sideway/pinpoint": "^2.0.0" } }, "js-tokens": { @@ -7768,6 +12955,12 @@ "json-buffer": "3.0.1" } }, + "leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true + }, "levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -7805,12 +12998,51 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true + }, "lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "requires": { + "yallist": "^3.0.2" + } + }, + "make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "requires": { + "semver": "^7.5.3" + } + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "requires": { + "tmpl": "1.0.5" + } + }, "memory-fs": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.2.0.tgz", @@ -7821,25 +13053,30 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true, - "peer": true + "dev": true }, "merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "peer": true + "dev": true }, "micromatch": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.6.tgz", - "integrity": "sha512-Y4Ypn3oujJYxJcMacVgcs92wofTHxp9FzfDpQON4msDefoC0lb3ETvQLOdLcbhSwU1bz8HrL/1sygfBIHudrkQ==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, - "peer": true, "requires": { "braces": "^3.0.3", - "picomatch": "^4.0.2" + "picomatch": "^2.3.1" + }, + "dependencies": { + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + } } }, "mime-db": { @@ -7857,6 +13094,12 @@ "mime-db": "1.52.0" } }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, "min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -7864,11 +13107,10 @@ "dev": true }, "minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, - "peer": true, "requires": { "brace-expansion": "^2.0.1" } @@ -7878,6 +13120,12 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" }, + "minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true + }, "moment": { "version": "2.30.1", "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", @@ -7897,6 +13145,12 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, + "napi-postinstall": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz", + "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==", + "dev": true + }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -7907,8 +13161,7 @@ "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true, - "peer": true + "dev": true }, "node-fetch": { "version": "2.6.7", @@ -7926,6 +13179,12 @@ "http2-client": "^1.2.5" } }, + "node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true + }, "node-readfiles": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/node-readfiles/-/node-readfiles-0.2.0.tgz", @@ -7935,11 +13194,10 @@ } }, "node-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", - "dev": true, - "peer": true + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true }, "normalize-package-data": { "version": "2.5.0", @@ -7972,6 +13230,21 @@ } } }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, "oas-kit-common": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/oas-kit-common/-/oas-kit-common-1.0.8.tgz", @@ -8111,6 +13384,15 @@ "wrappy": "1" } }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, "openapi-types": { "version": "12.1.3", "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz", @@ -8155,6 +13437,12 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, + "package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true + }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -8200,12 +13488,29 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "requires": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + } + } + }, "path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "peer": true + "dev": true }, "phone": { "version": "2.4.22", @@ -8213,17 +13518,70 @@ "integrity": "sha512-k2f9qkIgcgbbeyFFMHDcCaYdPxq7u71EjmMvD998PEquwDvIT7zmUFe00S4hH9WPjk+IQlw9W/FlHOu1O17Tbw==" }, "picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true }, "picomatch": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true + }, + "pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, - "peer": true + "requires": { + "find-up": "^4.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + } + } }, "pluralize": { "version": "8.0.0", @@ -8244,9 +13602,9 @@ "dev": true }, "prettier": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", - "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true }, "prettier-linter-helpers": { @@ -8258,11 +13616,36 @@ "fast-diff": "^1.1.2" } }, + "pretty-format": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.2.0.tgz", + "integrity": "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==", + "dev": true, + "requires": { + "@jest/schemas": "30.0.5", + "ansi-styles": "^5.2.0", + "react-is": "^18.3.1" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + } + } + }, "punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==" }, + "pure-rand": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-7.0.1.tgz", + "integrity": "sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==", + "dev": true + }, "qs": { "version": "6.11.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz", @@ -8293,6 +13676,12 @@ "safe-buffer": "^5.1.0" } }, + "react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + }, "read-pkg": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", @@ -8430,6 +13819,23 @@ "supports-preserve-symlinks-flag": "^1.0.0" } }, + "resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "requires": { + "resolve-from": "^5.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + } + } + }, "resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -8503,9 +13909,9 @@ } }, "semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "dev": true }, "serialize-javascript": { @@ -8617,19 +14023,23 @@ "object-inspect": "^1.13.1" } }, + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "peer": true + "dev": true }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "peer": true + "dev": true }, "source-map-support": { "version": "0.5.21", @@ -8679,6 +14089,33 @@ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" }, + "stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "requires": { + "escape-string-regexp": "^2.0.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true + } + } + }, + "string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "requires": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + } + }, "string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -8689,6 +14126,17 @@ "strip-ansi": "^6.0.1" } }, + "string-width-cjs": { + "version": "npm:string-width@4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, "string.prototype.trim": { "version": "1.2.9", "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", @@ -8731,12 +14179,27 @@ "ansi-regex": "^5.0.1" } }, + "strip-ansi-cjs": { + "version": "npm:strip-ansi@6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, "strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true + }, "strip-indent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", @@ -8753,11 +14216,10 @@ "dev": true }, "stripe": { - "version": "14.5.0", - "resolved": "https://registry.npmjs.org/stripe/-/stripe-14.5.0.tgz", - "integrity": "sha512-MTt0P0VYDAj2VZyZMW41AxcXVs9s06EDFIHYat4UUkluJgnBHo4T4E2byPhnxoJCPvdzWBwBLnhWbSOg//VTpA==", + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/stripe/-/stripe-19.3.0.tgz", + "integrity": "sha512-3MbqRkw5LXb4LWP1LgIEYxUAYhYDDU5pcHZj4Xha6VWPnN1wrUmQ7Htsgm8wR584s0hn1aQg1lYD0Hi+F37E5g==", "requires": { - "@types/node": ">=8.1.0", "qs": "^6.11.0" } }, @@ -8794,6 +14256,15 @@ "yargs": "^17.0.1" } }, + "synckit": { + "version": "0.11.11", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.11.tgz", + "integrity": "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==", + "dev": true, + "requires": { + "@pkgr/core": "^0.2.9" + } + }, "tapable": { "version": "0.1.10", "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.1.10.tgz", @@ -8827,30 +14298,59 @@ "terser": "^5.26.0" } }, + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "dependencies": { + "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, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, + "tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true + }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, - "peer": true, "requires": { "is-number": "^7.0.0" } }, - "topo": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/topo/-/topo-2.1.1.tgz", - "integrity": "sha512-ZPrPP5nwzZy1fw9abHQH2k+YarTgp9UMAztcB3MmlcZSif63Eg+az05p6wTDaZmnqpS3Mk7K+2W60iHarlz8Ug==", - "requires": { - "hoek": "4.x.x" - } - }, "tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", @@ -8861,9 +14361,39 @@ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", "dev": true, - "peer": true, "requires": {} }, + "ts-jest": { + "version": "29.4.5", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.5.tgz", + "integrity": "sha512-HO3GyiWn2qvTQA4kTgjDcXiMwYQt68a1Y8+JuLRVpdIzm+UOLSHgl/XqR4c6nzJkq5rOkjc02O2I7P7l/Yof0Q==", + "dev": true, + "requires": { + "bs-logger": "^0.2.6", + "fast-json-stable-stringify": "^2.1.0", + "handlebars": "^4.7.8", + "json5": "^2.2.3", + "lodash.memoize": "^4.1.2", + "make-error": "^1.3.6", + "semver": "^7.7.3", + "type-fest": "^4.41.0", + "yargs-parser": "^21.1.1" + }, + "dependencies": { + "json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true + }, + "type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true + } + } + }, "tsconfig-paths": { "version": "3.15.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", @@ -8876,6 +14406,13 @@ "strip-bom": "^3.0.0" } }, + "tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "optional": true + }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -8885,6 +14422,12 @@ "prelude-ls": "^1.2.1" } }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, "type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", @@ -8948,6 +14491,13 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==" }, + "uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "dev": true, + "optional": true + }, "unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", @@ -8963,17 +14513,45 @@ "undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "devOptional": true + }, + "unrs-resolver": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz", + "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==", + "dev": true, + "requires": { + "@unrs/resolver-binding-android-arm-eabi": "1.11.1", + "@unrs/resolver-binding-android-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-x64": "1.11.1", + "@unrs/resolver-binding-freebsd-x64": "1.11.1", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", + "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-musl": "1.11.1", + "@unrs/resolver-binding-wasm32-wasi": "1.11.1", + "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", + "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", + "@unrs/resolver-binding-win32-x64-msvc": "1.11.1", + "napi-postinstall": "^0.3.0" + } }, "update-browserslist-db": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz", - "integrity": "sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz", + "integrity": "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==", "dev": true, - "peer": true, "requires": { - "escalade": "^3.1.2", - "picocolors": "^1.0.1" + "escalade": "^3.2.0", + "picocolors": "^1.1.1" } }, "uri-js": { @@ -8984,6 +14562,17 @@ "punycode": "^2.1.0" } }, + "v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + } + }, "validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -8994,6 +14583,15 @@ "spdx-expression-parse": "^3.0.0" } }, + "walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "requires": { + "makeerror": "1.0.12" + } + }, "watchpack": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.1.tgz", @@ -9138,6 +14736,12 @@ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true + }, "wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -9148,17 +14752,52 @@ "strip-ansi": "^6.0.0" } }, + "wrap-ansi-cjs": { + "version": "npm:wrap-ansi@7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, + "write-file-atomic": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "dependencies": { + "signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true + } + } + }, "y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, "yaml": { "version": "1.10.2", "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", diff --git a/stripe_collection_module/package.json b/stripe_collection_module/package.json index e832303..c328b7a 100644 --- a/stripe_collection_module/package.json +++ b/stripe_collection_module/package.json @@ -1,32 +1,62 @@ { + "name": "stripe-collection-module", + "version": "1.0.0", + "main": "dist/main.js", + "types": "dist/main.d.ts", + "files": [ + "dist/**/*" + ], "dependencies": { - "@rootplatform/node-sdk": "^0.0.7", - "joi": "11.4.0", + "joi": "^17.13.3", "moment-timezone": "0.5.40", "node-fetch": "2.6.7", "phone": "2.4.22", - "stripe": "14.5.0" + "stripe": "^19.2.1", + "@rootplatform/node-sdk": "^0.0.7" }, "devDependencies": { + "@jest/globals": "^30.2.0", + "@types/jest": "^30.0.0", "@types/node": "^20.11.20", "@types/node-fetch": "^2.6.11", - "eslint": "^8.51.0", - "eslint-config-airbnb-typescript": "^17.0.0", - "eslint-config-prettier": "^8.7.0", + "@typescript-eslint/eslint-plugin": "^7.18.0", + "@typescript-eslint/parser": "^7.18.0", + "eslint": "^8.57.0", + "eslint-config-airbnb-typescript": "^18.0.0", + "eslint-config-prettier": "^9.1.0", "eslint-import-resolver-webpack": "^0.13.2", - "eslint-plugin-import": "^2.27.5", - "eslint-plugin-mocha": "^10.1.0", - "eslint-plugin-prettier": "^4.2.1", - "eslint-plugin-sonarjs": "^0.18.0", - "eslint-plugin-unicorn": "^48.0.1", - "prettier": "^2.8.3" + "eslint-plugin-import": "^2.29.1", + "eslint-plugin-mocha": "^10.5.0", + "eslint-plugin-prettier": "^5.2.1", + "eslint-plugin-sonarjs": "^0.25.1", + "eslint-plugin-unicorn": "^55.0.0", + "jest": "^30.2.0", + "prettier": "^3.3.3", + "ts-jest": "^29.4.5" }, "scripts": { - "lint": "eslint code --ext .js,.jsx,.ts,.tsx --no-error-on-unmatched-pattern", - "lint:fix": "eslint --fix code --ext .js,.jsx,.ts,.tsx --no-error-on-unmatched-pattern --quiet", + "validate": "bash scripts/validate-config.sh", + "lint": "eslint code --ext .js,.jsx,.ts,.tsx", + "lint:fix": "eslint --fix code --ext .js,.jsx,.ts,.tsx", "prettier-check": "npx prettier --check code/", "prettier-write": "prettier --write code/", - "build": "rm -rf ./dist && tsc" + "format": "npm run prettier-write && npm run lint:fix", + "build": "rm -rf ./dist && tsc --project tsconfig.build.json", + "build:watch": "tsc --project tsconfig.build.json --watch", + "clean": "rm -rf ./dist ./coverage", + "test": "jest --forceExit", + "test:unit": "jest --forceExit --testPathPattern='__tests__/(services|core|utils)'", + "test:integration": "jest --forceExit --testPathPattern='__tests__/integration'", + "test:watch": "jest --watch", + "test:coverage": "jest --forceExit --coverage", + "test:ci": "jest --ci --forceExit --coverage --maxWorkers=2", + "precommit": "npm run lint && npm run test", + "prepush": "npm run lint && npm run test && npm run build", + "predeploy": "npm run validate && npm run test && npm run build", + "deploy:sandbox": "bash scripts/deploy.sh sandbox", + "deploy:production": "bash scripts/deploy.sh production", + "deploy:dry-run:sandbox": "bash scripts/deploy.sh --dry-run sandbox", + "deploy:dry-run:production": "bash scripts/deploy.sh --dry-run production" }, "engines": { "npm": ">=8.0.0", diff --git a/stripe_collection_module/scripts/README.md b/stripe_collection_module/scripts/README.md new file mode 100644 index 0000000..511e1d0 --- /dev/null +++ b/stripe_collection_module/scripts/README.md @@ -0,0 +1,310 @@ +# Deployment Scripts + +This directory contains scripts for deploying the Stripe Collection Module to the Root Platform. + +## Quick Start + +### Using npm scripts (recommended) + +```bash +# Deploy to sandbox +npm run deploy:sandbox + +# Deploy to production (requires version tag) +npm run deploy:production v1.0.0 + +# Dry run (show what would happen without executing) +npm run deploy:dry-run production v1.0.0 +``` + +### Using the deploy script directly + +```bash +# Deploy to sandbox +./scripts/deploy.sh sandbox + +# Deploy to sandbox with version tag +./scripts/deploy.sh sandbox v1.0.0 + +# Deploy to production +./scripts/deploy.sh production v1.0.0 + +# Dry run +./scripts/deploy.sh --dry-run production v1.0.0 +``` + +## Prerequisites + +Set these environment variables or pass them as flags: + +```bash +export ROOT_API_KEY="your_api_key_here" +export ROOT_ORG_ID="your_org_id_here" +export ROOT_HOST="https://api.rootplatform.com" # Optional +export CM_KEY="cm_stripe" # Optional +``` + +Or use command-line flags: + +```bash +./scripts/deploy.sh \ + -k "your_api_key" \ + -o "your_org_id" \ + -h "https://api.rootplatform.com" \ + -c "cm_stripe" \ + production v1.0.0 +``` + +## What the Deployment Script Does + +1. **Validates Configuration** - Runs `npm run validate` +2. **Runs Tests** - Executes `npm test` (skip with `--skip-tests`) +3. **Lints Code** - Runs `npm run lint` +4. **Builds** - Compiles TypeScript (skip with `--skip-build`) +5. **Creates Git Tag** - Tags the release (skip with `--skip-tag`) +6. **Publishes to Root Platform** - Calls the Root Platform API + +## Options + +``` +Usage: deploy.sh [OPTIONS] [version] + +Arguments: + environment Target environment: 'sandbox' or 'production' + version Git tag version (e.g., v1.0.0) - optional for sandbox + +Options: + -k, --api-key KEY Root Platform API key + -o, --org-id ID Root organization ID + -h, --host URL Root Platform host + -c, --cm-key KEY Collection module key + --skip-tests Skip running tests + --skip-build Skip build step + --skip-tag Skip creating git tag + --dry-run Show what would be done without executing + --help Show help message +``` + +## Examples + +### Deploy to Sandbox + +```bash +# Simple deployment to sandbox +./scripts/deploy.sh sandbox + +# With version tag +./scripts/deploy.sh sandbox v1.0.0 + +# Skip tests (faster, for quick iterations) +./scripts/deploy.sh --skip-tests sandbox +``` + +### Deploy to Production + +```bash +# Standard production deployment +./scripts/deploy.sh production v1.1.0 + +# With explicit credentials +./scripts/deploy.sh \ + -k "prod_api_key" \ + -o "org_id_123" \ + production v1.1.0 + +# Dry run first (recommended) +./scripts/deploy.sh --dry-run production v1.1.0 +# Review output, then run for real +./scripts/deploy.sh production v1.1.0 +``` + +### Testing and Development + +```bash +# Dry run - see what would happen +./scripts/deploy.sh --dry-run sandbox v1.0.0-beta + +# Skip tests and build for faster iteration +./scripts/deploy.sh --skip-tests --skip-build sandbox + +# Skip git tagging (if managing tags manually) +./scripts/deploy.sh --skip-tag production v1.2.0 +``` + +## Workflow + +### Standard Development Workflow + +1. **Make changes** to your collection module +2. **Test locally** with `npm test` +3. **Deploy to sandbox** for integration testing + ```bash + npm run deploy:sandbox + ``` +4. **Test in sandbox** - verify everything works +5. **Create version tag** and **deploy to production** + ```bash + ./scripts/deploy.sh production v1.0.0 + ``` +6. **Monitor** the deployment in Root Platform dashboard + +### Hotfix Workflow + +1. **Create hotfix branch** + ```bash + git checkout -b hotfix/fix-payment-issue + ``` +2. **Make fix** and test +3. **Deploy directly to sandbox** + ```bash + ./scripts/deploy.sh --skip-tag sandbox + ``` +4. **Verify fix** in sandbox +5. **Merge and deploy to production** + ```bash + git checkout main + git merge hotfix/fix-payment-issue + ./scripts/deploy.sh production v1.0.1 + ``` + +## Environment Variables + +### Required + +- `ROOT_API_KEY` - Your Root Platform API key +- `ROOT_ORG_ID` - Your Root organization ID + +### Optional + +- `ROOT_HOST` - Root Platform API URL (default: https://api.rootplatform.com) +- `CM_KEY` - Collection module key (default: cm_stripe) + +### Setting Up Environment Variables + +Create a `.env` file (gitignored) in the project root: + +```bash +# .env +ROOT_API_KEY=sandbox_sk_abc123... +ROOT_ORG_ID=00000000-0000-0000-0000-000000000001 +ROOT_HOST=https://api.rootplatform.com +CM_KEY=cm_stripe +``` + +Then source it before deploying: + +```bash +source .env +./scripts/deploy.sh sandbox +``` + +Or use a tool like `direnv` for automatic loading. + +## Troubleshooting + +### "ROOT_API_KEY is not set" + +**Solution:** Set the environment variable or use the `-k` flag: +```bash +export ROOT_API_KEY="your_key" +# or +./scripts/deploy.sh -k "your_key" sandbox +``` + +### "Tests failed" + +**Solution:** Fix the failing tests or skip them (not recommended for production): +```bash +./scripts/deploy.sh --skip-tests sandbox +``` + +### "Configuration validation failed" + +**Solution:** Check your `code/env.ts` file has all required values: +```bash +npm run validate +``` + +### "Not a git repository" + +**Solution:** Initialize git or use `--skip-tag`: +```bash +git init +# or +./scripts/deploy.sh --skip-tag sandbox +``` + +### API Call Failed + +**Solution:** +- Verify API key has deployment permissions +- Check organization ID is correct +- Verify collection module key matches Root Platform +- Check Root Platform status page + +## Security Notes + +- **Never commit API keys** to version control +- Use different API keys for sandbox and production +- Store production keys securely (1Password, Vault, etc.) +- Rotate API keys quarterly +- Use read-only keys for monitoring, write keys only for deployment + +## CI/CD Integration + +### GitHub Actions Example + +```yaml +name: Deploy +on: + push: + tags: + - 'v*' + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Setup Node + uses: actions/setup-node@v3 + with: + node-version: '18' + + - name: Install dependencies + working-directory: ./stripe_collection_module + run: npm ci + + - name: Deploy to Sandbox + if: contains(github.ref, '-beta') + working-directory: ./stripe_collection_module + env: + ROOT_API_KEY: ${{ secrets.ROOT_SANDBOX_API_KEY }} + ROOT_ORG_ID: ${{ secrets.ROOT_ORG_ID }} + run: npm run deploy:sandbox + + - name: Deploy to Production + if: "!contains(github.ref, '-beta')" + working-directory: ./stripe_collection_module + env: + ROOT_API_KEY: ${{ secrets.ROOT_PRODUCTION_API_KEY }} + ROOT_ORG_ID: ${{ secrets.ROOT_ORG_ID }} + run: ./scripts/deploy.sh production ${{ github.ref_name }} +``` + +## Related Documentation + +- [Deployment Guide](../docs/DEPLOYMENT.md) - Detailed deployment documentation +- [Setup Guide](../docs/SETUP.md) - Initial setup instructions +- [Testing Guide](../docs/TESTING.md) - Testing strategies + +## Support + +For issues with deployment: +1. Check the [Troubleshooting](#troubleshooting) section above +2. Review logs with `--dry-run` flag +3. Verify credentials and configuration +4. Check Root Platform status page +5. Contact Root Platform support + diff --git a/stripe_collection_module/scripts/deploy.sh b/stripe_collection_module/scripts/deploy.sh new file mode 100755 index 0000000..6d73796 --- /dev/null +++ b/stripe_collection_module/scripts/deploy.sh @@ -0,0 +1,664 @@ +#!/bin/bash + +############################################################################### +# Stripe Collection Module Deployment Script +# +# This script automates the deployment process to the Root Platform. +# It automatically loads configuration from: +# - .root-config.json (Organization ID, Module Key, Host) +# - .root-auth (API Key) +# - code/env.ts (Environment configuration) +# +# Usage: +# ./scripts/deploy.sh [environment] [version] +# +# Examples: +# ./scripts/deploy.sh sandbox # Deploy to sandbox (uses package.json version) +# ./scripts/deploy.sh sandbox v1.0.0 # Deploy to sandbox with specific version +# ./scripts/deploy.sh production # Deploy to production (uses package.json version) +# ./scripts/deploy.sh production v1.0.0 # Deploy to production with specific version +# +# Prerequisites: +# - Run bash ../setup.sh to configure required files +# - Root Platform CLI (rp) installed: npm install -g root-platform-cli +# - Configuration files: .root-config.json, .root-auth, code/env.ts +############################################################################### + +set -e # Exit on error + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Script directory +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_DIR="$(dirname "$SCRIPT_DIR")" + +# Default values +ENVIRONMENT="" +VERSION="" +SKIP_TESTS=false +SKIP_BUILD=false +SKIP_TAG=false +DRY_RUN=false + +# Root Platform configuration (will be loaded from config files) +ROOT_API_KEY="${ROOT_API_KEY:-}" +ROOT_ORG_ID="${ROOT_ORG_ID:-}" +ROOT_HOST="${ROOT_HOST:-}" +CM_KEY="${CM_KEY:-}" + +############################################################################### +# Configuration Loading Functions +############################################################################### + +load_root_config() { + local config_file="$PROJECT_DIR/.root-config.json" + + if [ ! -f "$config_file" ]; then + print_error ".root-config.json not found at $config_file" + print_info "Run the setup script first: bash ../setup.sh" + exit 1 + fi + + # Check if jq is available for JSON parsing + if command -v jq &> /dev/null; then + CM_KEY=$(jq -r '.collectionModuleKey // empty' "$config_file") + ROOT_ORG_ID=$(jq -r '.organizationId // empty' "$config_file") + ROOT_HOST=$(jq -r '.host // empty' "$config_file") + else + # Fallback to grep/sed if jq is not available + CM_KEY=$(grep -o '"collectionModuleKey"[[:space:]]*:[[:space:]]*"[^"]*"' "$config_file" | sed 's/.*"\([^"]*\)"$/\1/') + ROOT_ORG_ID=$(grep -o '"organizationId"[[:space:]]*:[[:space:]]*"[^"]*"' "$config_file" | sed 's/.*"\([^"]*\)"$/\1/') + ROOT_HOST=$(grep -o '"host"[[:space:]]*:[[:space:]]*"[^"]*"' "$config_file" | sed 's/.*"\([^"]*\)"$/\1/') + fi + + # Set defaults if not found + ROOT_HOST="${ROOT_HOST:-https://api.rootplatform.com}" +} + +load_root_auth() { + local auth_file="$PROJECT_DIR/.root-auth" + + if [ ! -f "$auth_file" ]; then + print_error ".root-auth not found at $auth_file" + print_info "Run the setup script first: bash ../setup.sh" + exit 1 + fi + + # Load ROOT_API_KEY from .root-auth file + # shellcheck disable=SC1090 + source "$auth_file" + + if [ -z "$ROOT_API_KEY" ]; then + print_error "ROOT_API_KEY not found in .root-auth" + exit 1 + fi +} + +load_env_config() { + local env_file="$PROJECT_DIR/code/env.ts" + + if [ ! -f "$env_file" ]; then + print_error "env.ts not found at $env_file" + print_info "Run the setup script first: bash ../setup.sh" + exit 1 + fi + + # The env.ts file is TypeScript, but we can still extract values + # We look for specific exports based on the environment + local node_env + node_env=$(grep -o "export const NODE_ENV = '[^']*'" "$env_file" | sed "s/export const NODE_ENV = '\([^']*\)'/\1/") + + if [ -n "$node_env" ]; then + print_info "Detected NODE_ENV in env.ts: $node_env" + fi +} + +load_package_version() { + local package_file="$PROJECT_DIR/package.json" + + if [ ! -f "$package_file" ]; then + print_error "package.json not found at $package_file" + exit 1 + fi + + # Check if jq is available for JSON parsing + if command -v jq &> /dev/null; then + PACKAGE_VERSION=$(jq -r '.version // empty' "$package_file") + else + # Fallback to grep/sed if jq is not available + PACKAGE_VERSION=$(grep -o '"version"[[:space:]]*:[[:space:]]*"[^"]*"' "$package_file" | sed 's/.*"\([^"]*\)"$/\1/') + fi + + if [ -n "$PACKAGE_VERSION" ]; then + # Ensure version has 'v' prefix + if [[ ! "$PACKAGE_VERSION" =~ ^v ]]; then + PACKAGE_VERSION="v${PACKAGE_VERSION}" + fi + fi +} + +############################################################################### +# Helper Functions +############################################################################### + +print_header() { + echo "" + echo -e "${BLUE}โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”${NC}" + echo -e "${BLUE} $1${NC}" + echo -e "${BLUE}โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”${NC}" + echo "" +} + +print_success() { + echo -e "${GREEN}โœ“${NC} $1" +} + +print_error() { + echo -e "${RED}โœ—${NC} $1" +} + +print_warning() { + echo -e "${YELLOW}โš ${NC} $1" +} + +print_info() { + echo -e "${BLUE}โ„น${NC} $1" +} + +print_usage() { + cat << EOF +Usage: $0 [OPTIONS] [version] + +Deployment script for Stripe Collection Module to Root Platform. + +Configuration: + By default, this script automatically loads configuration from: + - .root-config.json (Organization ID, Module Key, Host) + - .root-auth (API Key) + - code/env.ts (Environment configuration) + + Run 'bash ../setup.sh' to configure these files interactively. + +Arguments: + environment Target environment: 'sandbox' or 'production' + version Git tag version (e.g., v1.0.0) - optional, defaults to package.json version + +Options: + -k, --api-key KEY Override Root Platform API key (from .root-auth) + -o, --org-id ID Override Root organization ID (from .root-config.json) + -h, --host URL Override Root Platform host (from .root-config.json) + -c, --cm-key KEY Override Collection module key (from .root-config.json) + --skip-tests Skip running tests + --skip-build Skip build step + --skip-tag Skip creating git tag + --dry-run Show what would be done without executing + --help Show this help message + +Examples: + # Deploy to sandbox (uses package.json version) + $0 sandbox + + # Deploy to sandbox with specific version tag + $0 sandbox v1.0.0 + + # Deploy to production (uses package.json version) + $0 production + + # Deploy to production with specific version + $0 production v1.1.0 + + # Deploy with overridden API key + $0 -k "prod_key_123" production v1.1.0 + + # Dry run for production + $0 --dry-run production + +Configuration Files: + .root-config.json Organization ID, Module Key, API Host + .root-auth Root Platform API Key + code/env.ts Environment-specific configuration + +EOF +} + +############################################################################### +# Parse Arguments +############################################################################### + +while [[ $# -gt 0 ]]; do + case $1 in + -k|--api-key) + ROOT_API_KEY="$2" + shift 2 + ;; + -o|--org-id) + ROOT_ORG_ID="$2" + shift 2 + ;; + -h|--host) + ROOT_HOST="$2" + shift 2 + ;; + -c|--cm-key) + CM_KEY="$2" + shift 2 + ;; + --skip-tests) + SKIP_TESTS=true + shift + ;; + --skip-build) + SKIP_BUILD=true + shift + ;; + --skip-tag) + SKIP_TAG=true + shift + ;; + --dry-run) + DRY_RUN=true + shift + ;; + --help) + print_usage + exit 0 + ;; + sandbox|production) + ENVIRONMENT="$1" + shift + ;; + v*.*.*) + VERSION="$1" + shift + ;; + *) + print_error "Unknown option: $1" + print_usage + exit 1 + ;; + esac +done + +############################################################################### +# Load Configuration from Files +############################################################################### + +print_info "Loading configuration from files..." + +# Load configuration from .root-config.json +if [ -z "$CM_KEY" ] || [ -z "$ROOT_ORG_ID" ] || [ -z "$ROOT_HOST" ]; then + load_root_config + print_info "Loaded configuration from .root-config.json" +fi + +# Load API key from .root-auth +if [ -z "$ROOT_API_KEY" ]; then + load_root_auth + print_info "Loaded API key from .root-auth" +fi + +# Load environment configuration from env.ts +load_env_config + +# Load version from package.json if not provided +if [ -z "$VERSION" ]; then + load_package_version + if [ -n "$PACKAGE_VERSION" ]; then + VERSION="$PACKAGE_VERSION" + print_info "Using version from package.json: $VERSION" + fi +fi + +echo "" + +############################################################################### +# Validate Arguments +############################################################################### + +if [ -z "$ENVIRONMENT" ]; then + print_error "Environment argument is required (sandbox or production)" + print_usage + exit 1 +fi + +if [ "$ENVIRONMENT" != "sandbox" ] && [ "$ENVIRONMENT" != "production" ]; then + print_error "Invalid environment: $ENVIRONMENT (must be 'sandbox' or 'production')" + exit 1 +fi + +if [ "$ENVIRONMENT" = "production" ] && [ -z "$VERSION" ]; then + print_error "Version is required for production deployment" + print_info "Update package.json version and run the script again" + exit 1 +fi + +if [ -z "$ROOT_API_KEY" ]; then + print_error "ROOT_API_KEY is not set" + print_info "Could not load from .root-auth file" + print_info "Set via environment variable or use -k flag" + exit 1 +fi + +if [ -z "$ROOT_ORG_ID" ]; then + print_error "ROOT_ORG_ID is not set" + print_info "Could not load from .root-config.json" + print_info "Set via environment variable or use -o flag" + exit 1 +fi + +if [ -z "$CM_KEY" ]; then + print_error "CM_KEY is not set" + print_info "Could not load from .root-config.json" + print_info "Set via environment variable or use -c flag" + exit 1 +fi + +############################################################################### +# Display Configuration +############################################################################### + +print_header "Deployment Configuration" + +echo "Environment: $ENVIRONMENT" +echo "Version: ${VERSION:-}" +echo "" +echo "Configuration loaded from:" +echo " Root Host: $ROOT_HOST" +echo " Organization ID: $ROOT_ORG_ID" +echo " Module Key: $CM_KEY" +echo " API Key: ${ROOT_API_KEY:0:10}..." +echo "" +echo "Options:" +echo " Skip Tests: $SKIP_TESTS" +echo " Skip Build: $SKIP_BUILD" +echo " Skip Tag: $SKIP_TAG" +echo " Dry Run: $DRY_RUN" + +if [ "$DRY_RUN" = true ]; then + echo "" + print_warning "DRY RUN MODE - No changes will be made" +fi + +############################################################################### +# Confirmation +############################################################################### + +if [ "$ENVIRONMENT" = "production" ] && [ "$DRY_RUN" = false ]; then + echo "" + print_warning "You are about to deploy to PRODUCTION!" + read -p "Are you sure you want to continue? (yes/no): " -r + echo "" + if [[ ! $REPLY =~ ^[Yy][Ee][Ss]$ ]]; then + print_info "Deployment cancelled" + exit 0 + fi +fi + +############################################################################### +# Pre-deployment Checks +############################################################################### + +print_header "Pre-deployment Checks" + +# Check if we're in the right directory +cd "$PROJECT_DIR" +print_success "Changed to project directory: $PROJECT_DIR" + +# Check if node_modules exists +if [ ! -d "node_modules" ]; then + print_error "node_modules not found. Run 'npm install' first." + exit 1 +fi +print_success "Dependencies installed" + +# Check if git repository (if version tagging is needed) +if [ -n "$VERSION" ] && [ "$SKIP_TAG" = false ]; then + if ! git rev-parse --git-dir > /dev/null 2>&1; then + print_error "Not a git repository. Cannot create version tag." + exit 1 + fi + print_success "Git repository detected" + + # Check for uncommitted changes + if ! git diff-index --quiet HEAD --; then + print_warning "You have uncommitted changes" + read -p "Continue anyway? (yes/no): " -r + if [[ ! $REPLY =~ ^[Yy][Ee][Ss]$ ]]; then + exit 0 + fi + fi +fi + +############################################################################### +# Validation +############################################################################### + +print_header "Step 1: Validate Configuration" + +if [ "$DRY_RUN" = false ]; then + if npm run validate; then + print_success "Configuration validated" + else + print_error "Configuration validation failed" + exit 1 + fi +else + print_info "Would run: npm run validate" +fi + +############################################################################### +# Run Tests +############################################################################### + +if [ "$SKIP_TESTS" = false ]; then + print_header "Step 2: Run Tests" + + if [ "$DRY_RUN" = false ]; then + if npm test; then + print_success "All tests passed" + else + print_error "Tests failed" + exit 1 + fi + else + print_info "Would run: npm test" + fi +else + print_header "Step 2: Run Tests" + print_warning "Skipping tests (--skip-tests flag)" +fi + +############################################################################### +# Lint Code +############################################################################### + +print_header "Step 3: Lint Code" + +if [ "$DRY_RUN" = false ]; then + if npm run lint; then + print_success "Code linting passed" + else + print_error "Linting failed" + print_info "Run 'npm run lint:fix' to auto-fix issues" + exit 1 + fi +else + print_info "Would run: npm run lint" +fi + +############################################################################### +# Build +############################################################################### + +if [ "$SKIP_BUILD" = false ]; then + print_header "Step 4: Build" + + if [ "$DRY_RUN" = false ]; then + if npm run build; then + print_success "Build completed successfully" + else + print_error "Build failed" + exit 1 + fi + else + print_info "Would run: npm run build" + fi +else + print_header "Step 4: Build" + print_warning "Skipping build (--skip-build flag)" +fi + +############################################################################### +# Create Git Tag +############################################################################### + +if [ -n "$VERSION" ] && [ "$SKIP_TAG" = false ]; then + print_header "Step 5: Create Git Tag" + + # Check if tag already exists + if git rev-parse "$VERSION" >/dev/null 2>&1; then + print_warning "Tag $VERSION already exists" + read -p "Continue with existing tag? (yes/no): " -r + if [[ ! $REPLY =~ ^[Yy][Ee][Ss]$ ]]; then + exit 0 + fi + else + if [ "$DRY_RUN" = false ]; then + git tag -a "$VERSION" -m "Release $VERSION" + print_success "Created tag: $VERSION" + + read -p "Push tag to remote? (yes/no): " -r + if [[ $REPLY =~ ^[Yy][Ee][Ss]$ ]]; then + git push origin "$VERSION" + print_success "Pushed tag to remote" + fi + else + print_info "Would create tag: $VERSION" + print_info "Would push tag to remote (if confirmed)" + fi + fi +elif [ -n "$VERSION" ]; then + print_header "Step 5: Create Git Tag" + print_warning "Skipping git tag (--skip-tag flag)" +fi + +############################################################################### +# Push Code to Root Platform (using rp CLI) +############################################################################### + +print_header "Step 6: Push Code with rp CLI" + +# Check if rp CLI is installed +if ! command -v rp &> /dev/null; then + print_error "Root Platform CLI (rp) is not installed" + print_info "Install it with: npm install -g root-platform-cli" + exit 1 +fi + +print_info "Using rp CLI to push code..." + +if [ "$DRY_RUN" = false ]; then + # Run rp push and capture output + RP_OUTPUT=$(rp push 2>&1) + RP_EXIT_CODE=$? + + echo "$RP_OUTPUT" + + # Check for errors in output (rp CLI may not always return proper exit codes) + if [ $RP_EXIT_CODE -ne 0 ] || echo "$RP_OUTPUT" | grep -qi "error\|failed\|unauthenticated"; then + print_error "Failed to push code with rp CLI" + exit 1 + fi + + print_success "Code pushed successfully with rp CLI" +else + print_info "Would execute: rp push" +fi + +echo "" + +############################################################################### +# Publish to Root Platform +############################################################################### + +print_header "Step 7: Publish to Root Platform" + +# Determine if sandbox or production +if [ "$ENVIRONMENT" = "sandbox" ]; then + BUMP_SANDBOX="true" +else + BUMP_SANDBOX="false" +fi + +# Build the API URL +API_URL="${ROOT_HOST}/v1/apps/${ROOT_ORG_ID}/insurance/collection-modules/${CM_KEY}/publish?bumpSandbox=${BUMP_SANDBOX}" + +print_info "Publishing to: $ENVIRONMENT" +print_info "API URL: $API_URL" + +if [ "$DRY_RUN" = false ]; then + # Make the API call with Basic Auth (API key as username, no password) + # Root Platform uses the API key as the username with empty password + RESPONSE=$(curl -X POST \ + -u "${ROOT_API_KEY}:" \ + -w "\nHTTP_STATUS:%{http_code}" \ + -s \ + "$API_URL") + + # Extract HTTP status code + HTTP_STATUS=$(echo "$RESPONSE" | grep "HTTP_STATUS:" | cut -d: -f2) + RESPONSE_BODY=$(echo "$RESPONSE" | sed '/HTTP_STATUS:/d') + + # Check response + if [ "$HTTP_STATUS" -ge 200 ] && [ "$HTTP_STATUS" -lt 300 ]; then + print_success "Successfully published to $ENVIRONMENT!" + echo "" + echo "Response:" + echo "$RESPONSE_BODY" | jq . 2>/dev/null || echo "$RESPONSE_BODY" + else + print_error "Deployment failed with status: $HTTP_STATUS" + echo "" + echo "Response:" + echo "$RESPONSE_BODY" + exit 1 + fi +else + print_info "Would execute: curl -X POST -u ':' '$API_URL'" +fi + +############################################################################### +# Post-deployment Steps +############################################################################### + +print_header "Step 8: Post-deployment" + +echo "" +print_success "Deployment to $ENVIRONMENT completed successfully!" +echo "" + +if [ "$DRY_RUN" = false ]; then + print_info "Next steps:" + echo " 1. Verify deployment in Root Platform dashboard" + echo " 2. Check logs for any errors" + echo " 3. Test critical workflows" + echo " 4. Monitor error rates and performance" + + if [ "$ENVIRONMENT" = "sandbox" ]; then + echo "" + print_info "After testing in sandbox, deploy to production with:" + echo " ./scripts/deploy.sh production $VERSION" + fi +else + print_info "This was a dry run. No changes were made." + print_info "Remove --dry-run flag to execute deployment." +fi + +echo "" +print_success "Done! ๐Ÿš€" +echo "" + diff --git a/stripe_collection_module/scripts/validate-config.sh b/stripe_collection_module/scripts/validate-config.sh new file mode 100755 index 0000000..c57000f --- /dev/null +++ b/stripe_collection_module/scripts/validate-config.sh @@ -0,0 +1,162 @@ +#!/bin/bash + +# Configuration Validation Script +# Checks that all required configuration files are set up correctly +# and that the project builds successfully. +# +# Note: This script treats test failures as warnings, not errors. +# Configuration can be valid even if tests are failing. + +set -e + +# Colors +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +info() { echo -e "${BLUE}โ„น${NC} $1"; } +success() { echo -e "${GREEN}โœ“${NC} $1"; } +warning() { echo -e "${YELLOW}โš ${NC} $1"; } +error() { echo -e "${RED}โœ—${NC} $1"; } + +echo "" +echo "โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—" +echo "โ•‘ Configuration Validation โ•‘" +echo "โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•" +echo "" + +ERRORS=0 +WARNINGS=0 + +# Check if env.ts exists +info "Checking environment configuration..." + +if [ ! -f "code/env.ts" ]; then + error "code/env.ts not found!" + echo " Copy code/env.sample.ts to code/env.ts and fill in your values." + ERRORS=$((ERRORS + 1)) +else + success "code/env.ts found" +fi + +# Check if .root-config.json exists +if [ ! -f ".root-config.json" ]; then + error ".root-config.json not found!" + echo " Copy .root-config.sample.json to .root-config.json and configure." + ERRORS=$((ERRORS + 1)) +else + success ".root-config.json found" + + # Validate .root-config.json content + if grep -q "my_collection_module_cm_stripe" .root-config.json; then + warning ".root-config.json contains template placeholder values" + echo " Update collectionModuleKey with your actual module key." + WARNINGS=$((WARNINGS + 1)) + fi + + if grep -q "00000000-0000-0000-0000-000000000001" .root-config.json; then + warning ".root-config.json contains placeholder organization ID" + echo " Update organizationId with your actual Root organization ID." + WARNINGS=$((WARNINGS + 1)) + fi +fi + +# Check for placeholder values in env.ts +if [ -f "code/env.ts" ]; then + info "Checking for placeholder values..." + + PLACEHOLDERS=( + "xxxxx" + "my_collection_module_cm_stripe" + ) + + for PLACEHOLDER in "${PLACEHOLDERS[@]}"; do + if grep -q "$PLACEHOLDER" code/env.ts; then + warning "Found placeholder value in code/env.ts: $PLACEHOLDER" + echo " Replace with actual API keys and configuration values." + WARNINGS=$((WARNINGS + 1)) + fi + done +fi + +# Check TypeScript compilation +info "Checking TypeScript compilation..." +if npm run build > /dev/null 2>&1; then + success "TypeScript compilation successful" +else + error "TypeScript compilation failed" + echo " Run 'npm run build' to see detailed errors." + ERRORS=$((ERRORS + 1)) +fi + +# Check linting +info "Checking linting..." +if npm run lint > /dev/null 2>&1; then + success "Linting passed" +else + warning "Linting failed" + echo " Run 'npm run lint' to see detailed errors." + echo " Run 'npm run lint:fix' to auto-fix some issues." + WARNINGS=$((WARNINGS + 1)) +fi + +# Run tests (non-blocking for configuration validation) +info "Running tests..." +if npm test > /dev/null 2>&1; then + success "All tests passed" +else + warning "Tests failed" + echo " Run 'npm test' to see detailed errors." + echo " Note: Test failures don't prevent deployment, but should be fixed." + WARNINGS=$((WARNINGS + 1)) +fi + +# Check Node version +info "Checking Node version..." +if [ -f ".nvmrc" ]; then + REQUIRED_VERSION=$(cat .nvmrc) + CURRENT_VERSION=$(node -v | sed 's/v//') + + if [[ "$CURRENT_VERSION" == "$REQUIRED_VERSION"* ]]; then + success "Node version $CURRENT_VERSION matches requirement" + else + warning "Node version mismatch" + echo " Required: $REQUIRED_VERSION" + echo " Current: $CURRENT_VERSION" + echo " Run 'nvm use' to switch to the correct version." + WARNINGS=$((WARNINGS + 1)) + fi +fi + +# Summary +echo "" +echo "โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€" +echo " Validation Summary" +echo "โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€" +echo "" + +if [ $ERRORS -eq 0 ] && [ $WARNINGS -eq 0 ]; then + success "All checks passed! No errors or warnings." + echo "" + echo "You're ready to:" + echo " - Deploy to sandbox: npm run deploy:sandbox" + echo " - Deploy to production: npm run deploy:production" + echo "" + exit 0 +elif [ $ERRORS -eq 0 ]; then + warning "Configuration is valid but with $WARNINGS warning(s)." + echo "" + echo "Review warnings above. You can still deploy, but consider fixing them." + echo "" + exit 0 +else + error "Configuration has $ERRORS critical error(s) and $WARNINGS warning(s)." + echo "" + echo "Fix the critical errors above before deploying." + echo "Note: Warnings won't block deployment but should be addressed." + echo "" + exit 1 +fi + diff --git a/stripe_collection_module/tsconfig.build.json b/stripe_collection_module/tsconfig.build.json index 8c174d9..1f11c86 100644 --- a/stripe_collection_module/tsconfig.build.json +++ b/stripe_collection_module/tsconfig.build.json @@ -1,6 +1,18 @@ { "extends": "./tsconfig.json", + "include": ["./code/**/*"], + "exclude": ["./__tests__/**/*"], "compilerOptions": { - "skipLibCheck": false + "lib": ["es2022", "dom"], + "skipLibCheck": true, + "noEmit": false, + "outDir": "./dist", + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "module": "commonjs", + "target": "es2022", + "moduleResolution": "node", + "esModuleInterop": true } } diff --git a/stripe_collection_module/tsconfig.eslint.json b/stripe_collection_module/tsconfig.eslint.json index ab3c206..f795117 100644 --- a/stripe_collection_module/tsconfig.eslint.json +++ b/stripe_collection_module/tsconfig.eslint.json @@ -1,4 +1,7 @@ { "extends": "./tsconfig.json", - "include": ["**/*.ts", "**/*.js"] + "include": ["**/*.ts", "**/*.js", "__tests__/**/*"], + "compilerOptions": { + "types": ["jest"] + } } diff --git a/stripe_collection_module/tsconfig.json b/stripe_collection_module/tsconfig.json index d0760a6..0f1edd3 100644 --- a/stripe_collection_module/tsconfig.json +++ b/stripe_collection_module/tsconfig.json @@ -7,10 +7,15 @@ "es2022" ], "module": "es2022", + "target": "es2022", "skipLibCheck": true, "allowJs": true, "strict": true, "moduleResolution": "Bundler", - "noEmit": true + "downlevelIteration": true, + "noEmit": true, + "types": [ + "jest" + ] } }