diff --git a/backend/.env.example b/backend/.env.example index b9db779..1a44180 100644 --- a/backend/.env.example +++ b/backend/.env.example @@ -47,6 +47,8 @@ AWS_REGION=us-east-1 # VAULT_ADDR=https://vault.example.com:8200 # VAULT_TOKEN=s.xxxxxxxxxxxxxxxx # VAULT_TRANSIT_PATH=transit/keys/stellar-keys +# Local AES-256-GCM fallback key (64-char hex, 32 bytes) used when Vault is unreachable +# VAULT_FALLBACK_KEY= # Signing Key (Public key for SEP-1 info endpoint) # This is the public key used for signing, NOT a private key @@ -116,6 +118,9 @@ REDIS_URL=redis://localhost:6379 # FEATURE_FLAG_MULTISIG=true # FEATURE_FLAG_BATCH_PAYMENTS=true +# Email (SendGrid with Nodemailer fallback) +# SENDGRID_API_KEY=SG.xxxxxxxxxxxxxxxx + # Note: NEVER commit plaintext private keys to this file. # Use KEY_MANAGEMENT_BACKEND to encrypt keys at rest. # For local development, use test keys only. diff --git a/backend/package.json b/backend/package.json index 0c8d2a4..df2614e 100644 --- a/backend/package.json +++ b/backend/package.json @@ -32,21 +32,25 @@ }, "dependencies": { "@aws-sdk/client-kms": "^3.500.0", + "@bull-board/api": "^5.21.0", + "@bull-board/express": "^5.21.0", "@prisma/client": "^6.19.2", + "@sendgrid/mail": "^8.1.3", "@stellar/stellar-sdk": "^14.6.1", - "cron-parser": "^4.9.0", "@types/node-cron": "^3.0.11", "@types/pdfkit": "^0.17.6", + "bullmq": "^5.7.0", "cors": "^2.8.5", + "cron-parser": "^4.9.0", "dotenv": "^16.4.5", "express": "^4.19.2", "express-rate-limit": "^7.1.0", "ioredis": "^5.3.0", "jsonwebtoken": "^9.0.3", "multer": "^1.4.5-lts.2", - "node-cron": "^3.0.3", - "nodemailer": "^6.10.1", "node-cron": "^4.2.1", + "node-vault": "^0.10.2", + "nodemailer": "^6.10.1", "pdfkit": "^0.18.0", "prom-client": "^15.1.0", "rate-limit-redis": "^4.0.0", @@ -60,12 +64,12 @@ "devDependencies": { "@types/cors": "^2.8.19", "@types/express": "^4.17.25", - "@types/node-cron": "^3.0.11", "@types/jest": "^29.5.12", "@types/jsonwebtoken": "^9.0.10", "@types/multer": "^1.4.12", - "@types/nodemailer": "^6.4.17", "@types/node": "^20.11.30", + "@types/node-cron": "^3.0.11", + "@types/nodemailer": "^6.4.17", "@types/supertest": "^6.0.2", "@types/swagger-jsdoc": "^6.0.4", "@types/swagger-ui-express": "^4.1.8", diff --git a/backend/src/api/routes/queue-dashboard.route.ts b/backend/src/api/routes/queue-dashboard.route.ts new file mode 100644 index 0000000..11ae9d1 --- /dev/null +++ b/backend/src/api/routes/queue-dashboard.route.ts @@ -0,0 +1,20 @@ +import { Router } from 'express'; +import { createBullBoard } from '@bull-board/api'; +import { BullMQAdapter } from '@bull-board/api/bullMQAdapter'; +import { ExpressAdapter } from '@bull-board/express'; +import { Queue } from 'bullmq'; +import { queueConnection, QUEUE_NAMES } from '../../config/queue'; + +const serverAdapter = new ExpressAdapter(); +serverAdapter.setBasePath('/api/queue-dashboard'); + +const queues = Object.values(QUEUE_NAMES).map( + (name) => new BullMQAdapter(new Queue(name, { connection: queueConnection })) +); + +createBullBoard({ queues, serverAdapter }); + +const router = Router(); +router.use('/', serverAdapter.getRouter()); + +export default router; diff --git a/backend/src/config/queue.test.ts b/backend/src/config/queue.test.ts new file mode 100644 index 0000000..a77513d --- /dev/null +++ b/backend/src/config/queue.test.ts @@ -0,0 +1,33 @@ +import { queueConnection } from './queue'; + +describe('BullMQ queue connection resiliency (#361)', () => { + it('has maxRetriesPerRequest set to null for BullMQ compatibility', () => { + expect(queueConnection.maxRetriesPerRequest).toBeNull(); + }); + + it('has enableReadyCheck disabled', () => { + expect(queueConnection.enableReadyCheck).toBe(false); + }); + + it('retryStrategy returns capped delay', () => { + const strategy = queueConnection.retryStrategy as (times: number) => number; + expect(strategy(1)).toBe(100); + expect(strategy(10)).toBe(1000); + expect(strategy(100)).toBe(5000); + }); + + it('reconnectOnError returns true for READONLY errors', () => { + const fn = queueConnection.reconnectOnError as (err: Error) => boolean; + expect(fn(new Error('READONLY command not allowed'))).toBe(true); + }); + + it('reconnectOnError returns true for ECONNRESET errors', () => { + const fn = queueConnection.reconnectOnError as (err: Error) => boolean; + expect(fn(new Error('ECONNRESET'))).toBe(true); + }); + + it('reconnectOnError returns false for unrelated errors', () => { + const fn = queueConnection.reconnectOnError as (err: Error) => boolean; + expect(fn(new Error('WRONGTYPE'))).toBe(false); + }); +}); diff --git a/backend/src/config/queue.ts b/backend/src/config/queue.ts index 4b0c629..413d962 100644 --- a/backend/src/config/queue.ts +++ b/backend/src/config/queue.ts @@ -1,16 +1,18 @@ import { QueueOptions, WorkerOptions, JobsOptions } from 'bullmq'; -import { redis } from '../lib/redis'; -/** - * BullMQ Queue Configuration - */ - -// Redis connection for BullMQ +// Redis connection for BullMQ with resiliency settings (#361) export const queueConnection = { host: process.env.REDIS_HOST || 'localhost', port: parseInt(process.env.REDIS_PORT || '6379', 10), password: process.env.REDIS_PASSWORD, db: parseInt(process.env.REDIS_DB || '0', 10), + maxRetriesPerRequest: null, + enableReadyCheck: false, + retryStrategy: (times: number) => Math.min(times * 100, 5000), + reconnectOnError: (err: Error) => { + const targetErrors = ['READONLY', 'ECONNRESET', 'ETIMEDOUT']; + return targetErrors.some((e) => err.message.includes(e)); + }, }; // Priority mapping — must be declared before jobTypeConfigs to allow direct references diff --git a/backend/src/index.ts b/backend/src/index.ts index 6b3ae7b..5c16eb5 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -28,6 +28,7 @@ import { notificationService } from './services/notification.service'; import { createEmailProvider, ConsoleSmsProvider, ConsolePushProvider } from './lib/notifications/providers'; import { NotificationType } from '@prisma/client'; import { validateKmsConfigOnStartup } from './lib/key-management.service'; +import queueDashboardRouter from './api/routes/queue-dashboard.route'; // Initialize Notification Engine notificationService.registerProvider(NotificationType.EMAIL, createEmailProvider()); @@ -144,6 +145,9 @@ app.use('/metrics', publicLimiter, metricsRouter); app.use('/api/recurring-payments', recurringPaymentsRouter); +// BullMQ queue monitoring dashboard (#362) — admin-only in production +app.use('/api/queue-dashboard', queueDashboardRouter); + // Global error handling middleware (must be last) app.use(errorHandler); diff --git a/backend/src/lib/email.service.test.ts b/backend/src/lib/email.service.test.ts new file mode 100644 index 0000000..7d1a521 --- /dev/null +++ b/backend/src/lib/email.service.test.ts @@ -0,0 +1,67 @@ +import { sendEmail, EmailPayload } from './email.service'; + +const payload: EmailPayload = { + to: 'user@example.com', + from: 'noreply@anchorpoint.app', + subject: 'Test', + text: 'Hello', +}; + +jest.mock('@sendgrid/mail', () => ({ + setApiKey: jest.fn(), + send: jest.fn(), +})); + +jest.mock('nodemailer', () => ({ + createTransport: jest.fn().mockReturnValue({ + sendMail: jest.fn().mockResolvedValue({}), + }), +})); + +import sgMail from '@sendgrid/mail'; +import nodemailer from 'nodemailer'; + +describe('sendEmail', () => { + beforeEach(() => { + jest.clearAllMocks(); + delete process.env.SENDGRID_API_KEY; + delete process.env.SMTP_HOST; + }); + + it('sends via SendGrid when SENDGRID_API_KEY is set', async () => { + process.env.SENDGRID_API_KEY = 'SG.test'; + (sgMail.send as jest.Mock).mockResolvedValue([{ statusCode: 202 }]); + + await sendEmail(payload); + + expect(sgMail.setApiKey).toHaveBeenCalledWith('SG.test'); + expect(sgMail.send).toHaveBeenCalledTimes(1); + }); + + it('falls back to Nodemailer when SendGrid fails', async () => { + process.env.SENDGRID_API_KEY = 'SG.test'; + process.env.SMTP_HOST = 'smtp.example.com'; + (sgMail.send as jest.Mock).mockRejectedValue(new Error('SendGrid error')); + const mockSendMail = jest.fn().mockResolvedValue({}); + (nodemailer.createTransport as jest.Mock).mockReturnValue({ sendMail: mockSendMail }); + + await sendEmail(payload); + + expect(mockSendMail).toHaveBeenCalledTimes(1); + }); + + it('sends via Nodemailer when only SMTP_HOST is set', async () => { + process.env.SMTP_HOST = 'smtp.example.com'; + const mockSendMail = jest.fn().mockResolvedValue({}); + (nodemailer.createTransport as jest.Mock).mockReturnValue({ sendMail: mockSendMail }); + + await sendEmail(payload); + + expect(mockSendMail).toHaveBeenCalledTimes(1); + expect(sgMail.send).not.toHaveBeenCalled(); + }); + + it('does not throw when no transport is configured', async () => { + await expect(sendEmail(payload)).resolves.not.toThrow(); + }); +}); diff --git a/backend/src/lib/email.service.ts b/backend/src/lib/email.service.ts new file mode 100644 index 0000000..fb1eece --- /dev/null +++ b/backend/src/lib/email.service.ts @@ -0,0 +1,65 @@ +import sgMail from '@sendgrid/mail'; +import nodemailer from 'nodemailer'; +import logger from '../utils/logger'; + +export interface EmailPayload { + to: string; + from: string; + subject: string; + text: string; + html?: string; +} + +async function sendViaSendGrid(payload: EmailPayload): Promise { + sgMail.setApiKey(process.env.SENDGRID_API_KEY!); + await sgMail.send({ + to: payload.to, + from: payload.from, + subject: payload.subject, + text: payload.text, + html: payload.html, + }); +} + +async function sendViaNodemailer(payload: EmailPayload): Promise { + const transporter = nodemailer.createTransport({ + host: process.env.SMTP_HOST, + port: parseInt(process.env.SMTP_PORT || '587', 10), + secure: parseInt(process.env.SMTP_PORT || '587', 10) === 465, + auth: + process.env.SMTP_USER && process.env.SMTP_PASS + ? { user: process.env.SMTP_USER, pass: process.env.SMTP_PASS } + : undefined, + }); + + await transporter.sendMail({ + from: payload.from, + to: payload.to, + subject: payload.subject, + text: payload.text, + html: payload.html, + }); +} + +export async function sendEmail(payload: EmailPayload): Promise { + if (process.env.SENDGRID_API_KEY) { + try { + await sendViaSendGrid(payload); + logger.debug('Email sent via SendGrid', { to: payload.to }); + return; + } catch (err: unknown) { + logger.warn('SendGrid delivery failed, falling back to Nodemailer', { + to: payload.to, + error: err instanceof Error ? err.message : String(err), + }); + } + } + + if (process.env.SMTP_HOST) { + await sendViaNodemailer(payload); + logger.debug('Email sent via Nodemailer', { to: payload.to }); + return; + } + + logger.info('No email transport configured; email not sent', { to: payload.to, subject: payload.subject }); +} diff --git a/backend/src/lib/key-management.service.test.ts b/backend/src/lib/key-management.service.test.ts index cee9718..ca69542 100644 --- a/backend/src/lib/key-management.service.test.ts +++ b/backend/src/lib/key-management.service.test.ts @@ -1,10 +1,3 @@ -/** - * Key Management Service Tests - * - * Tests for encrypted key storage and retrieval. - * All vault/KMS calls are mocked to avoid external dependencies. - */ - import { KeyManagementError, KeyManagementErrorType, @@ -12,18 +5,22 @@ import { } from './key-management.types'; import { createKeyManagementService, initializeKeyManagement, getKeyManagementService } from './key-management.service'; -// Mock AWS SDK +const mockKmsSend = jest.fn(); + jest.mock('@aws-sdk/client-kms', () => ({ - KMSClient: jest.fn().mockImplementation(() => ({ - send: jest.fn(), - })), - EncryptCommand: jest.fn().mockImplementation((params) => params), - DecryptCommand: jest.fn().mockImplementation((params) => params), - DescribeKeyCommand: jest.fn().mockImplementation((params) => params), - GetKeyRotationStatusCommand: jest.fn().mockImplementation((params) => params), - EnableKeyRotationCommand: jest.fn().mockImplementation((params) => params), + KMSClient: jest.fn().mockImplementation(() => ({ send: mockKmsSend })), + EncryptCommand: jest.fn().mockImplementation((p) => p), + DecryptCommand: jest.fn().mockImplementation((p) => p), + DescribeKeyCommand: jest.fn().mockImplementation((p) => p), + GetKeyRotationStatusCommand: jest.fn().mockImplementation((p) => p), + EnableKeyRotationCommand: jest.fn().mockImplementation((p) => p), })); +const KMS_CONFIG = { + backend: 'aws-kms' as const, + keyArn: 'arn:aws:kms:us-east-1:123456789012:key/12345678', +}; + describe('Key Management Service', () => { beforeEach(() => { jest.clearAllMocks(); @@ -32,257 +29,107 @@ describe('Key Management Service', () => { describe('AWS KMS Implementation', () => { describe('encryptKey', () => { it('should encrypt a plaintext key successfully', async () => { - const mockKmsClient = { - send: jest.fn().mockResolvedValue({ - CiphertextBlob: Buffer.from('encrypted-data'), - KeyId: 'arn:aws:kms:us-east-1:123456789012:key/12345678', - }), - }; - - // Mock the KMS client - jest.doMock('@aws-sdk/client-kms', () => ({ - KMSClient: jest.fn().mockImplementation(() => mockKmsClient), - EncryptCommand: jest.fn().mockImplementation((params) => params), - })); - - const config = { - backend: 'aws-kms' as const, - keyArn: 'arn:aws:kms:us-east-1:123456789012:key/12345678', - }; - - const service = createKeyManagementService(config); - const plaintext = 'SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; + mockKmsSend.mockResolvedValue({ + CiphertextBlob: Buffer.from('encrypted-data'), + KeyId: KMS_CONFIG.keyArn, + }); - const result = await service.encryptKey(plaintext); + const service = createKeyManagementService(KMS_CONFIG); + const result = await service.encryptKey('SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'); expect(result).toHaveProperty('ciphertext'); expect(result).toHaveProperty('keyVersion'); - expect(result).toHaveProperty('algorithm'); - expect(result).toHaveProperty('timestamp'); expect(result.algorithm).toBe('AES-256-GCM'); }); it('should throw error if plaintext is empty', async () => { - const config = { - backend: 'aws-kms' as const, - keyArn: 'arn:aws:kms:us-east-1:123456789012:key/12345678', - }; - - const service = createKeyManagementService(config); - + const service = createKeyManagementService(KMS_CONFIG); await expect(service.encryptKey('')).rejects.toThrow(KeyManagementError); }); it('should retry on transient errors', async () => { - const mockKmsClient = { - send: jest - .fn() - .mockRejectedValueOnce(new Error('ThrottlingException')) - .mockResolvedValueOnce({ - CiphertextBlob: Buffer.from('encrypted-data'), - KeyId: 'arn:aws:kms:us-east-1:123456789012:key/12345678', - }), - }; - - jest.doMock('@aws-sdk/client-kms', () => ({ - KMSClient: jest.fn().mockImplementation(() => mockKmsClient), - EncryptCommand: jest.fn().mockImplementation((params) => params), - })); - - const config = { - backend: 'aws-kms' as const, - keyArn: 'arn:aws:kms:us-east-1:123456789012:key/12345678', - }; - - const service = createKeyManagementService(config); - const plaintext = 'SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; + const throttleErr = Object.assign(new Error('ThrottlingException'), { name: 'ThrottlingException' }); + mockKmsSend + .mockRejectedValueOnce(throttleErr) + .mockResolvedValueOnce({ CiphertextBlob: Buffer.from('encrypted-data'), KeyId: KMS_CONFIG.keyArn }); - const result = await service.encryptKey(plaintext); + const service = createKeyManagementService(KMS_CONFIG); + const result = await service.encryptKey('SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'); expect(result).toHaveProperty('ciphertext'); - expect(mockKmsClient.send).toHaveBeenCalledTimes(2); + expect(mockKmsSend).toHaveBeenCalledTimes(2); }); it('should fail on permanent errors without retry', async () => { - const mockKmsClient = { - send: jest.fn().mockRejectedValue({ - name: 'AccessDeniedException', - message: 'User is not authorized', - }), - }; - - jest.doMock('@aws-sdk/client-kms', () => ({ - KMSClient: jest.fn().mockImplementation(() => mockKmsClient), - EncryptCommand: jest.fn().mockImplementation((params) => params), - })); - - const config = { - backend: 'aws-kms' as const, - keyArn: 'arn:aws:kms:us-east-1:123456789012:key/12345678', - }; - - const service = createKeyManagementService(config); - const plaintext = 'SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; + mockKmsSend.mockRejectedValue(Object.assign(new Error('User is not authorized'), { name: 'AccessDeniedException' })); - await expect(service.encryptKey(plaintext)).rejects.toThrow(KeyManagementError); + const service = createKeyManagementService(KMS_CONFIG); + await expect(service.encryptKey('SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA')).rejects.toThrow(KeyManagementError); + expect(mockKmsSend).toHaveBeenCalledTimes(1); }); }); describe('decryptKey', () => { + const encrypted: EncryptedKey = { + ciphertext: Buffer.from('encrypted-data').toString('base64'), + keyVersion: KMS_CONFIG.keyArn, + algorithm: 'AES-256-GCM', + timestamp: Date.now(), + }; + it('should decrypt a ciphertext key successfully', async () => { const plaintext = 'SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; - const mockKmsClient = { - send: jest.fn().mockResolvedValue({ - Plaintext: Buffer.from(plaintext, 'utf-8'), - }), - }; - - jest.doMock('@aws-sdk/client-kms', () => ({ - KMSClient: jest.fn().mockImplementation(() => mockKmsClient), - DecryptCommand: jest.fn().mockImplementation((params) => params), - })); - - const config = { - backend: 'aws-kms' as const, - keyArn: 'arn:aws:kms:us-east-1:123456789012:key/12345678', - }; - - const service = createKeyManagementService(config); - const encrypted: EncryptedKey = { - ciphertext: Buffer.from('encrypted-data').toString('base64'), - keyVersion: 'arn:aws:kms:us-east-1:123456789012:key/12345678', - algorithm: 'AES-256-GCM', - timestamp: Date.now(), - }; + mockKmsSend.mockResolvedValue({ Plaintext: Buffer.from(plaintext, 'utf-8') }); + const service = createKeyManagementService(KMS_CONFIG); const result = await service.decryptKey(encrypted); expect(result).toBe(plaintext); }); it('should throw error if ciphertext is empty', async () => { - const config = { - backend: 'aws-kms' as const, - keyArn: 'arn:aws:kms:us-east-1:123456789012:key/12345678', - }; - - const service = createKeyManagementService(config); - const encrypted: EncryptedKey = { - ciphertext: '', - keyVersion: 'arn:aws:kms:us-east-1:123456789012:key/12345678', - algorithm: 'AES-256-GCM', - timestamp: Date.now(), - }; - - await expect(service.decryptKey(encrypted)).rejects.toThrow(KeyManagementError); + const service = createKeyManagementService(KMS_CONFIG); + await expect(service.decryptKey({ ...encrypted, ciphertext: '' })).rejects.toThrow(KeyManagementError); }); it('should not log plaintext key material', async () => { const plaintext = 'SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; - const mockKmsClient = { - send: jest.fn().mockResolvedValue({ - Plaintext: Buffer.from(plaintext, 'utf-8'), - }), - }; - - jest.doMock('@aws-sdk/client-kms', () => ({ - KMSClient: jest.fn().mockImplementation(() => mockKmsClient), - DecryptCommand: jest.fn().mockImplementation((params) => params), - })); - - const config = { - backend: 'aws-kms' as const, - keyArn: 'arn:aws:kms:us-east-1:123456789012:key/12345678', - }; - - const service = createKeyManagementService(config); - const encrypted: EncryptedKey = { - ciphertext: Buffer.from('encrypted-data').toString('base64'), - keyVersion: 'arn:aws:kms:us-east-1:123456789012:key/12345678', - algorithm: 'AES-256-GCM', - timestamp: Date.now(), - }; - - // Mock logger to verify no plaintext is logged - const loggerSpy = jest.spyOn(console, 'log').mockImplementation(); + mockKmsSend.mockResolvedValue({ Plaintext: Buffer.from(plaintext, 'utf-8') }); - await service.decryptKey(encrypted); + const service = createKeyManagementService(KMS_CONFIG); + const logSpy = jest.spyOn(console, 'log').mockImplementation(); - // Verify plaintext was not logged - const logCalls = loggerSpy.mock.calls.map((call) => call[0]?.toString() || ''); - expect(logCalls.join('')).not.toContain(plaintext); + await service.decryptKey(encrypted); - loggerSpy.mockRestore(); + const logged = logSpy.mock.calls.map((c) => String(c[0])).join(''); + expect(logged).not.toContain(plaintext); + logSpy.mockRestore(); }); }); describe('isHealthy', () => { it('should return true when KMS is healthy', async () => { - const mockKmsClient = { - send: jest.fn().mockResolvedValue({ - KeyMetadata: { KeyId: 'arn:aws:kms:us-east-1:123456789012:key/12345678' }, - }), - }; - - jest.doMock('@aws-sdk/client-kms', () => ({ - KMSClient: jest.fn().mockImplementation(() => mockKmsClient), - DescribeKeyCommand: jest.fn().mockImplementation((params) => params), - })); - - const config = { - backend: 'aws-kms' as const, - keyArn: 'arn:aws:kms:us-east-1:123456789012:key/12345678', - }; - - const service = createKeyManagementService(config); - const result = await service.isHealthy(); - - expect(result).toBe(true); + mockKmsSend.mockResolvedValue({ KeyMetadata: { KeyId: KMS_CONFIG.keyArn } }); + + const service = createKeyManagementService(KMS_CONFIG); + expect(await service.isHealthy()).toBe(true); }); it('should return false when KMS is unavailable', async () => { - const mockKmsClient = { - send: jest.fn().mockRejectedValue(new Error('Connection refused')), - }; + mockKmsSend.mockRejectedValue(new Error('Connection refused')); - jest.doMock('@aws-sdk/client-kms', () => ({ - KMSClient: jest.fn().mockImplementation(() => mockKmsClient), - DescribeKeyCommand: jest.fn().mockImplementation((params) => params), - })); - - const config = { - backend: 'aws-kms' as const, - keyArn: 'arn:aws:kms:us-east-1:123456789012:key/12345678', - }; - - const service = createKeyManagementService(config); - const result = await service.isHealthy(); - - expect(result).toBe(false); + const service = createKeyManagementService(KMS_CONFIG); + expect(await service.isHealthy()).toBe(false); }); }); describe('rotateEncryptionKey', () => { it('should enable rotation when not already enabled', async () => { - const mockKmsClient = { - send: jest - .fn() - .mockResolvedValueOnce({ KeyRotationEnabled: false }) - .mockResolvedValueOnce({}), - }; - - jest.doMock('@aws-sdk/client-kms', () => ({ - KMSClient: jest.fn().mockImplementation(() => mockKmsClient), - GetKeyRotationStatusCommand: jest.fn().mockImplementation((params) => params), - EnableKeyRotationCommand: jest.fn().mockImplementation((params) => params), - })); - - const config = { - backend: 'aws-kms' as const, - keyArn: 'arn:aws:kms:us-east-1:123456789012:key/12345678', - }; - - const service = createKeyManagementService(config); + mockKmsSend + .mockResolvedValueOnce({ KeyRotationEnabled: false }) + .mockResolvedValueOnce({}); + + const service = createKeyManagementService(KMS_CONFIG); const result = await service.rotateEncryptionKey(); expect(result.rotated).toBe(true); @@ -291,22 +138,9 @@ describe('Key Management Service', () => { }); it('should skip enable when rotation is already active', async () => { - const mockKmsClient = { - send: jest.fn().mockResolvedValue({ KeyRotationEnabled: true }), - }; - - jest.doMock('@aws-sdk/client-kms', () => ({ - KMSClient: jest.fn().mockImplementation(() => mockKmsClient), - GetKeyRotationStatusCommand: jest.fn().mockImplementation((params) => params), - EnableKeyRotationCommand: jest.fn().mockImplementation((params) => params), - })); - - const config = { - backend: 'aws-kms' as const, - keyArn: 'arn:aws:kms:us-east-1:123456789012:key/12345678', - }; - - const service = createKeyManagementService(config); + mockKmsSend.mockResolvedValue({ KeyRotationEnabled: true }); + + const service = createKeyManagementService(KMS_CONFIG); const result = await service.rotateEncryptionKey(); expect(result.rotated).toBe(false); @@ -317,96 +151,114 @@ describe('Key Management Service', () => { describe('Error Handling', () => { it('should create KeyManagementError with correct type', () => { - const error = new KeyManagementError( - KeyManagementErrorType.VAULT_UNAVAILABLE, - 'Vault is down' - ); - + const error = new KeyManagementError(KeyManagementErrorType.VAULT_UNAVAILABLE, 'Vault is down'); expect(error).toBeInstanceOf(Error); expect(error.type).toBe(KeyManagementErrorType.VAULT_UNAVAILABLE); - expect(error.message).toBe('Vault is down'); }); it('should include details in error', () => { const details = { statusCode: 503 }; - const error = new KeyManagementError( - KeyManagementErrorType.VAULT_UNAVAILABLE, - 'Vault is down', - details - ); - + const error = new KeyManagementError(KeyManagementErrorType.VAULT_UNAVAILABLE, 'Vault is down', details); expect(error.details).toEqual(details); }); it('should never include plaintext key in error message', () => { const plaintext = 'SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; - const error = new KeyManagementError( - KeyManagementErrorType.DECRYPTION_FAILED, - 'Failed to decrypt key' - ); - + const error = new KeyManagementError(KeyManagementErrorType.DECRYPTION_FAILED, 'Failed to decrypt key'); expect(error.message).not.toContain(plaintext); }); }); describe('Service Initialization', () => { it('should throw error if service not initialized', () => { - // Reset the singleton jest.resetModules(); - - expect(() => { - getKeyManagementService(); - }).toThrow(KeyManagementError); + expect(() => getKeyManagementService()).toThrow(KeyManagementError); }); it('should initialize service with AWS KMS config', () => { - const config = { - backend: 'aws-kms' as const, - keyArn: 'arn:aws:kms:us-east-1:123456789012:key/12345678', - }; - - initializeKeyManagement(config); - const service = getKeyManagementService(); - - expect(service).toBeDefined(); + initializeKeyManagement(KMS_CONFIG); + expect(getKeyManagementService()).toBeDefined(); }); }); describe('Encrypt/Decrypt Round Trip', () => { it('should successfully encrypt and decrypt a key', async () => { const plaintext = 'SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; - const mockKmsClient = { - send: jest - .fn() - .mockResolvedValueOnce({ - CiphertextBlob: Buffer.from('encrypted-data'), - KeyId: 'arn:aws:kms:us-east-1:123456789012:key/12345678', - }) - .mockResolvedValueOnce({ - Plaintext: Buffer.from(plaintext, 'utf-8'), - }), - }; + mockKmsSend + .mockResolvedValueOnce({ CiphertextBlob: Buffer.from('encrypted-data'), KeyId: KMS_CONFIG.keyArn }) + .mockResolvedValueOnce({ Plaintext: Buffer.from(plaintext, 'utf-8') }); - jest.doMock('@aws-sdk/client-kms', () => ({ - KMSClient: jest.fn().mockImplementation(() => mockKmsClient), - EncryptCommand: jest.fn().mockImplementation((params) => params), - DecryptCommand: jest.fn().mockImplementation((params) => params), - })); - - const config = { - backend: 'aws-kms' as const, - keyArn: 'arn:aws:kms:us-east-1:123456789012:key/12345678', - }; - - const service = createKeyManagementService(config); - - // Encrypt + const service = createKeyManagementService(KMS_CONFIG); const encrypted = await service.encryptKey(plaintext); - - // Decrypt const decrypted = await service.decryptKey(encrypted); expect(decrypted).toBe(plaintext); }); }); }); + +describe('Vault Transit Fallback (#370)', () => { + const FALLBACK_KEY = 'a'.repeat(64); + const vaultConfig = { + backend: 'vault' as const, + address: 'http://vault:8200', + token: 'test-token', + transitPath: 'transit', + }; + + beforeEach(() => { + process.env.VAULT_FALLBACK_KEY = FALLBACK_KEY; + }); + + afterEach(() => { + delete process.env.VAULT_FALLBACK_KEY; + jest.resetModules(); + }); + + it('falls back to local AES-256-GCM when Vault is unreachable on encrypt', async () => { + jest.mock('node-vault', () => + jest.fn().mockImplementation(() => ({ + write: jest.fn().mockRejectedValue(Object.assign(new Error('connect ECONNREFUSED'), { code: 'ECONNREFUSED' })), + })) + ); + + const { createKeyManagementService: create } = await import('./key-management.service'); + const service = create(vaultConfig); + const result = await service.encryptKey('secret-key'); + + expect(result.keyVersion).toBe('local'); + expect(result.ciphertext).toMatch(/^local:/); + }); + + it('decrypts a locally-encrypted ciphertext without contacting Vault', async () => { + jest.mock('node-vault', () => + jest.fn().mockImplementation(() => ({ + write: jest.fn().mockRejectedValue(Object.assign(new Error('connect ECONNREFUSED'), { code: 'ECONNREFUSED' })), + })) + ); + + const { createKeyManagementService: create } = await import('./key-management.service'); + const service = create(vaultConfig); + + const plaintext = 'my-stellar-secret'; + const encrypted = await service.encryptKey(plaintext); + const decrypted = await service.decryptKey(encrypted); + + expect(decrypted).toBe(plaintext); + }); + + it('does not fall back when VAULT_FALLBACK_KEY is absent', async () => { + delete process.env.VAULT_FALLBACK_KEY; + + jest.mock('node-vault', () => + jest.fn().mockImplementation(() => ({ + write: jest.fn().mockRejectedValue(Object.assign(new Error('connect ECONNREFUSED'), { code: 'ECONNREFUSED' })), + })) + ); + + const { createKeyManagementService: create } = await import('./key-management.service'); + const service = create(vaultConfig); + + await expect(service.encryptKey('secret')).rejects.toThrow(); + }); +}); diff --git a/backend/src/lib/key-management.service.ts b/backend/src/lib/key-management.service.ts index 100305c..1c5e396 100644 --- a/backend/src/lib/key-management.service.ts +++ b/backend/src/lib/key-management.service.ts @@ -290,8 +290,49 @@ class AwsKmsService implements IKeyManagementService { } } +// Local AES-256-GCM fallback used when Vault Transit is unavailable. +// The fallback key is derived from VAULT_FALLBACK_KEY env var (32-byte hex). +// Security: fallback ciphertexts are prefixed with "local:" to distinguish them. +import { createCipheriv, createDecipheriv, randomBytes } from 'crypto'; + +const LOCAL_FALLBACK_PREFIX = 'local:'; +const ALGO = 'aes-256-gcm'; + +function getFallbackKey(): Buffer { + const hex = process.env.VAULT_FALLBACK_KEY; + if (!hex || hex.length !== 64) { + throw new KeyManagementError( + KeyManagementErrorType.INVALID_CONFIG, + 'VAULT_FALLBACK_KEY must be a 64-char hex string (32 bytes)' + ); + } + return Buffer.from(hex, 'hex'); +} + +function localEncrypt(plaintext: string): string { + const key = getFallbackKey(); + const iv = randomBytes(12); + const cipher = createCipheriv(ALGO, key, iv); + const encrypted = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]); + const tag = cipher.getAuthTag(); + return LOCAL_FALLBACK_PREFIX + Buffer.concat([iv, tag, encrypted]).toString('base64'); +} + +function localDecrypt(ciphertext: string): string { + const key = getFallbackKey(); + const buf = Buffer.from(ciphertext.slice(LOCAL_FALLBACK_PREFIX.length), 'base64'); + const iv = buf.subarray(0, 12); + const tag = buf.subarray(12, 28); + const data = buf.subarray(28); + const decipher = createDecipheriv(ALGO, key, iv); + decipher.setAuthTag(tag); + return Buffer.concat([decipher.update(data), decipher.final()]).toString('utf8'); +} + /** - * HashiCorp Vault Implementation + * HashiCorp Vault Implementation with local AES-256-GCM fallback (#370). + * When Vault Transit is unreachable and VAULT_FALLBACK_KEY is set, operations + * fall back to local encryption so the service remains available. */ class VaultService implements IKeyManagementService { private vaultClient: any; @@ -302,7 +343,6 @@ class VaultService implements IKeyManagementService { constructor(config: VaultConfig) { this.transitPath = config.transitPath; - // Lazy load Vault client try { // eslint-disable-next-line @typescript-eslint/no-var-requires const VaultClient = require('node-vault'); @@ -318,11 +358,23 @@ class VaultService implements IKeyManagementService { } } - /** - * Encrypt a plaintext key using Vault Transit engine - * - * Security Note: Plaintext is never logged or persisted. - */ + private isTransientError(error: any): boolean { + return ( + error.statusCode === 429 || + error.statusCode === 503 || + error.code === 'ECONNREFUSED' || + error.code === 'ETIMEDOUT' + ); + } + + private isVaultUnavailable(error: any): boolean { + return ( + error.code === 'ECONNREFUSED' || + error.code === 'ETIMEDOUT' || + error.statusCode === 503 + ); + } + async encryptKey(plaintext: string): Promise { if (!plaintext) { throw new KeyManagementError( @@ -337,9 +389,7 @@ class VaultService implements IKeyManagementService { try { const response = await this.vaultClient.write( `${this.transitPath}/encrypt/stellar-keys`, - { - plaintext: Buffer.from(plaintext, 'utf-8').toString('base64'), - } + { plaintext: Buffer.from(plaintext, 'utf-8').toString('base64') } ); logger.debug('Key encrypted successfully via Vault'); @@ -353,33 +403,27 @@ class VaultService implements IKeyManagementService { } catch (error: any) { lastError = error; - // Check if error is transient - const isTransient = - error.statusCode === 429 || - error.statusCode === 503 || - error.code === 'ECONNREFUSED' || - error.code === 'ETIMEDOUT'; - - if (isTransient && attempt < this.maxRetries) { + if (this.isTransientError(error) && attempt < this.maxRetries) { const delay = this.retryDelayMs * Math.pow(2, attempt - 1); logger.debug(`Vault encryption transient error, retrying in ${delay}ms`); await this.delay(delay); continue; } - - // Permanent error or final attempt break; } } - // Determine error type - let errorType = KeyManagementErrorType.ENCRYPTION_FAILED; - if (lastError?.statusCode === 403) { - errorType = KeyManagementErrorType.UNAUTHORIZED; - } else if (lastError?.statusCode === 404) { - errorType = KeyManagementErrorType.KEY_NOT_FOUND; + // Fallback to local AES-256-GCM when Vault is unreachable + if (lastError && this.isVaultUnavailable(lastError) && process.env.VAULT_FALLBACK_KEY) { + logger.warn('Vault unavailable — falling back to local AES-256-GCM encryption'); + const ciphertext = localEncrypt(plaintext); + return { ciphertext, keyVersion: 'local', algorithm: 'AES-256-GCM', timestamp: Date.now() }; } + let errorType = KeyManagementErrorType.ENCRYPTION_FAILED; + if ((lastError as any)?.statusCode === 403) errorType = KeyManagementErrorType.UNAUTHORIZED; + else if ((lastError as any)?.statusCode === 404) errorType = KeyManagementErrorType.KEY_NOT_FOUND; + throw new KeyManagementError( errorType, `Failed to encrypt key via Vault after ${this.maxRetries} attempts`, @@ -387,12 +431,6 @@ class VaultService implements IKeyManagementService { ); } - /** - * Decrypt a ciphertext key using Vault Transit engine - * - * Security Note: Returned plaintext must be scoped to minimum lifetime. - * Never store in cache, logs, or pass to logging functions. - */ async decryptKey(encrypted: EncryptedKey): Promise { if (!encrypted.ciphertext) { throw new KeyManagementError( @@ -401,51 +439,40 @@ class VaultService implements IKeyManagementService { ); } + // Route locally-encrypted ciphertexts directly to local decryption + if (encrypted.ciphertext.startsWith(LOCAL_FALLBACK_PREFIX)) { + logger.debug('Decrypting locally-encrypted key via AES-256-GCM fallback'); + return localDecrypt(encrypted.ciphertext); + } + let lastError: Error | null = null; for (let attempt = 1; attempt <= this.maxRetries; attempt++) { try { const response = await this.vaultClient.write( `${this.transitPath}/decrypt/stellar-keys`, - { - ciphertext: encrypted.ciphertext, - } + { ciphertext: encrypted.ciphertext } ); const plaintext = Buffer.from(response.data.plaintext, 'base64').toString('utf-8'); - logger.debug('Key decrypted successfully via Vault'); - return plaintext; } catch (error: any) { lastError = error; - // Check if error is transient - const isTransient = - error.statusCode === 429 || - error.statusCode === 503 || - error.code === 'ECONNREFUSED' || - error.code === 'ETIMEDOUT'; - - if (isTransient && attempt < this.maxRetries) { + if (this.isTransientError(error) && attempt < this.maxRetries) { const delay = this.retryDelayMs * Math.pow(2, attempt - 1); logger.debug(`Vault decryption transient error, retrying in ${delay}ms`); await this.delay(delay); continue; } - - // Permanent error or final attempt break; } } - // Determine error type let errorType = KeyManagementErrorType.DECRYPTION_FAILED; - if (lastError?.statusCode === 403) { - errorType = KeyManagementErrorType.UNAUTHORIZED; - } else if (lastError?.statusCode === 400) { - errorType = KeyManagementErrorType.INVALID_KEY_FORMAT; - } + if ((lastError as any)?.statusCode === 403) errorType = KeyManagementErrorType.UNAUTHORIZED; + else if ((lastError as any)?.statusCode === 400) errorType = KeyManagementErrorType.INVALID_KEY_FORMAT; throw new KeyManagementError( errorType, @@ -454,30 +481,18 @@ class VaultService implements IKeyManagementService { ); } - /** - * Get key by reference from Vault KV store - */ async getKeyByReference(keyRef: string): Promise { try { const response = await this.vaultClient.read(`secret/data/${keyRef}`); return response.data.data.key; } catch (error: any) { if (error.statusCode === 404) { - throw new KeyManagementError( - KeyManagementErrorType.KEY_NOT_FOUND, - `Key not found in Vault: ${keyRef}` - ); + throw new KeyManagementError(KeyManagementErrorType.KEY_NOT_FOUND, `Key not found in Vault: ${keyRef}`); } - throw new KeyManagementError( - KeyManagementErrorType.VAULT_UNAVAILABLE, - `Failed to retrieve key from Vault: ${error.message}` - ); + throw new KeyManagementError(KeyManagementErrorType.VAULT_UNAVAILABLE, `Failed to retrieve key from Vault: ${error.message}`); } } - /** - * Health check for Vault - */ async isHealthy(): Promise { try { await this.vaultClient.health(); @@ -488,10 +503,6 @@ class VaultService implements IKeyManagementService { } } - /** - * Rotate the Vault Transit engine encryption key to a new version. - * Previous versions remain available for decryption. - */ async rotateEncryptionKey(): Promise { const timestamp = Date.now(); @@ -501,7 +512,6 @@ class VaultService implements IKeyManagementService { ); const keyVersion = response.data?.latest_version?.toString() ?? 'unknown'; - logger.info(`Vault Transit key rotated successfully (version ${keyVersion})`); return { @@ -516,11 +526,8 @@ class VaultService implements IKeyManagementService { logger.error(`Vault key rotation failed: ${error?.message ?? error}`); let errorType = KeyManagementErrorType.ENCRYPTION_FAILED; - if (error?.statusCode === 403) { - errorType = KeyManagementErrorType.UNAUTHORIZED; - } else if (error?.statusCode === 404) { - errorType = KeyManagementErrorType.KEY_NOT_FOUND; - } + if (error?.statusCode === 403) errorType = KeyManagementErrorType.UNAUTHORIZED; + else if (error?.statusCode === 404) errorType = KeyManagementErrorType.KEY_NOT_FOUND; throw new KeyManagementError( errorType, diff --git a/backend/src/lib/key-management.types.ts b/backend/src/lib/key-management.types.ts index 135022d..f1481bb 100644 --- a/backend/src/lib/key-management.types.ts +++ b/backend/src/lib/key-management.types.ts @@ -6,7 +6,6 @@ */ export enum KeyManagementErrorType { - /** Vault/KMS service is unavailable */ VAULT_UNAVAILABLE = 'VAULT_UNAVAILABLE', /** Requested key not found in vault/KMS */ KEY_NOT_FOUND = 'KEY_NOT_FOUND', diff --git a/backend/src/services/admin-email.service.ts b/backend/src/services/admin-email.service.ts index 9c880e3..3430780 100644 --- a/backend/src/services/admin-email.service.ts +++ b/backend/src/services/admin-email.service.ts @@ -1,7 +1,6 @@ import { config } from '../config/env'; -import { smtpService } from '../lib/smtp.service'; -import { createSmtpTransporter, isSmtpConfigured } from '../lib/smtp/create-transporter'; import logger from '../utils/logger'; +import { sendEmail } from '../lib/email.service'; export interface PasswordResetEmailInput { to: string; @@ -17,9 +16,8 @@ export class SmtpAdminEmailService implements AdminEmailService { async sendPasswordResetEmail(input: PasswordResetEmailInput): Promise { const resetUrl = `${config.ADMIN_PASSWORD_RESET_URL_BASE}?token=${encodeURIComponent(input.token)}`; - await smtpService.sendMail({ - if (!isSmtpConfigured()) { - logger.info('SMTP not configured; password reset email logged for development', { + if (!config.SMTP_HOST && !process.env.SENDGRID_API_KEY) { + logger.info('No email transport configured; password reset email logged for development', { to: input.to, resetUrl, expiresAt: input.expiresAt.toISOString(), @@ -27,10 +25,8 @@ export class SmtpAdminEmailService implements AdminEmailService { return; } - const transporter = createSmtpTransporter(); - - await transporter.sendMail({ - from: config.SMTP_FROM, + await sendEmail({ + from: config.SMTP_FROM || 'noreply@anchorpoint.app', to: input.to, subject: 'AnchorPoint Admin Password Reset', text: [ diff --git a/package-lock.json b/package-lock.json index c2c2cb0..6a3e12e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,10 +22,14 @@ "version": "1.0.0", "dependencies": { "@aws-sdk/client-kms": "^3.500.0", + "@bull-board/api": "^5.21.0", + "@bull-board/express": "^5.21.0", "@prisma/client": "^6.19.2", + "@sendgrid/mail": "^8.1.3", "@stellar/stellar-sdk": "^14.6.1", "@types/node-cron": "^3.0.11", "@types/pdfkit": "^0.17.6", + "bullmq": "^5.7.0", "cors": "^2.8.5", "cron-parser": "^4.9.0", "dotenv": "^16.4.5", @@ -35,6 +39,7 @@ "jsonwebtoken": "^9.0.3", "multer": "^1.4.5-lts.2", "node-cron": "^4.2.1", + "node-vault": "^0.10.2", "nodemailer": "^6.10.1", "pdfkit": "^0.18.0", "prom-client": "^15.1.0", @@ -1124,6 +1129,39 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "node_modules/@bull-board/api": { + "version": "5.21.0", + "resolved": "https://registry.npmjs.org/@bull-board/api/-/api-5.21.0.tgz", + "integrity": "sha512-27tjptwgRgP1G5jT+POjiZZOP3LgdIM4XdfEWfa6t5E0CYImL4EjmdiFo5lhbHhYKZ842VhIpHuNcPk8nY3K9A==", + "license": "MIT", + "dependencies": { + "redis-info": "^3.0.8" + }, + "peerDependencies": { + "@bull-board/ui": "5.21.0" + } + }, + "node_modules/@bull-board/express": { + "version": "5.21.0", + "resolved": "https://registry.npmjs.org/@bull-board/express/-/express-5.21.0.tgz", + "integrity": "sha512-iBPBJq8KYebYrN4YvdSvEfOxjYYJfWycilAfNDSikyI3rJKOBRq34BmDnQj6Jn1ytssBb+vvZ35+bCSbbhFB3w==", + "license": "MIT", + "dependencies": { + "@bull-board/api": "5.21.0", + "@bull-board/ui": "5.21.0", + "ejs": "^3.1.10", + "express": "^4.19.2" + } + }, + "node_modules/@bull-board/ui": { + "version": "5.21.0", + "resolved": "https://registry.npmjs.org/@bull-board/ui/-/ui-5.21.0.tgz", + "integrity": "sha512-eH8QQwIHgCXxNEmlg9EZr3fSvno/bdbgBGfSQO5s9c9n9eDEaKX46ambKSPvgFPtwSdiV1AYQEa/3fGSebVIxg==", + "license": "MIT", + "dependencies": { + "@bull-board/api": "5.21.0" + } + }, "node_modules/@chromatic-com/storybook": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/@chromatic-com/storybook/-/storybook-3.2.7.tgz", @@ -2403,6 +2441,84 @@ "react": ">=16" } }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.4.tgz", + "integrity": "sha512-LCkGo6JDfaBhgST7UpPWgNgLINpcpabaHfyz5OBx75nUYxBsaEPxjnyNjWpeb/xBup/682QnBfRBy2/LvPutZQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.4.tgz", + "integrity": "sha512-zExlW9zUJKZH/tOtVMttwjKa4Xm/3KcNjnE3dPN92uCktwavMxpgCA3MoJK/DOnTWsQgo224OaST27/mPNAf+w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.4.tgz", + "integrity": "sha512-Tg3yX65f5GbtXLkrYEHE5oibZG9epyYWas7FogTTEJeDEF9JlXJzKgXaNhT3UXlTOeA+AfZpYZYZ0uPj7Cfquw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.4.tgz", + "integrity": "sha512-dgX0P/9wGPJeHFBG+ZmhgE6bmtMt7NP5CRBGyyktpopdk/mW4POnrpQsSLtKI1dwpc+pPLuXHDh6vvskyQE/sw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.4.tgz", + "integrity": "sha512-8TNXMEjJc3QEy7R/x1INhgiU+XakDAFUzBhaz7+Rbrs8NH5UQeHQxxmzsSBJGyV6I1jW79undiQm8tOI+D+8FQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.4.tgz", + "integrity": "sha512-CmCXPQrkbwExx3j946/PtHWHbYJiCRBRDl4BlkRQcJB/YOwQxJRTpoo7aTsortjgoJ1x7opzTSxn7C+ASSLVjQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@noble/ciphers": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz", @@ -2513,6 +2629,77 @@ "node": ">=14" } }, + "node_modules/@postman/form-data": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@postman/form-data/-/form-data-3.1.1.tgz", + "integrity": "sha512-vjh8Q2a8S6UCm/KKs31XFJqEEgmbjBmpPNVV2eVav6905wyFAwaUOBGA1NPBI4ERH9MMZc6w0umFgM6WbEPMdg==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@postman/form-data/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@postman/form-data/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@postman/tough-cookie": { + "version": "4.1.3-postman.1", + "resolved": "https://registry.npmjs.org/@postman/tough-cookie/-/tough-cookie-4.1.3-postman.1.tgz", + "integrity": "sha512-txpgUqZOnWYnUHZpHjkfb0IwVH4qJmyq77pPnJLlfhMtdCLMFTEeQHlzQiK906aaNCe4NEB5fGJHo9uzGbFMeA==", + "license": "BSD-3-Clause", + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@postman/tough-cookie/node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/@postman/tunnel-agent": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@postman/tunnel-agent/-/tunnel-agent-0.6.8.tgz", + "integrity": "sha512-2U42SmZW5G+suEcS++zB94sBWNO4qD4bvETGFRFDTqSpYl5ksfjcPqzYpgQgXgUmb6dfz+fAGbkcRamounGm0w==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, "node_modules/@prisma/client": { "version": "6.19.3", "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.19.3.tgz", @@ -2939,6 +3126,44 @@ "integrity": "sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==", "hasInstallScript": true }, + "node_modules/@sendgrid/client": { + "version": "8.1.6", + "resolved": "https://registry.npmjs.org/@sendgrid/client/-/client-8.1.6.tgz", + "integrity": "sha512-/BHu0hqwXNHr2aLhcXU7RmmlVqrdfrbY9KpaNj00KZHlVOVoRxRVrpOCabIB+91ISXJ6+mLM9vpaVUhK6TwBWA==", + "license": "MIT", + "dependencies": { + "@sendgrid/helpers": "^8.0.0", + "axios": "^1.12.0" + }, + "engines": { + "node": ">=12.*" + } + }, + "node_modules/@sendgrid/helpers": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@sendgrid/helpers/-/helpers-8.0.0.tgz", + "integrity": "sha512-Ze7WuW2Xzy5GT5WRx+yEv89fsg/pgy3T1E3FS0QEx0/VvRmigMZ5qyVGhJz4SxomegDkzXv/i0aFPpHKN8qdAA==", + "license": "MIT", + "dependencies": { + "deepmerge": "^4.2.2" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/@sendgrid/mail": { + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/@sendgrid/mail/-/mail-8.1.3.tgz", + "integrity": "sha512-Wg5iKSUOER83/cfY6rbPa+o3ChnYzWwv1OcsR8gCV8SKi+sUPIMroildimlnb72DBkQxcbylxng1W7f0RIX7MQ==", + "license": "MIT", + "dependencies": { + "@sendgrid/client": "^8.1.3", + "@sendgrid/helpers": "^8.0.0" + }, + "engines": { + "node": ">=12.*" + } + }, "node_modules/@sinclair/typebox": { "version": "0.27.10", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz", @@ -4818,6 +5043,24 @@ "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", "dev": true }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, "node_modules/assertion-error": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", @@ -4901,6 +5144,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, + "node_modules/aws4": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz", + "integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==", + "license": "MIT" + }, "node_modules/axe-core": { "version": "4.11.4", "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.11.4.tgz", @@ -5048,8 +5306,7 @@ "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 + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/base32.js": { "version": "0.1.0", @@ -5090,6 +5347,15 @@ "node": ">=6.0.0" } }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "license": "BSD-3-Clause", + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, "node_modules/better-opn": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/better-opn/-/better-opn-3.0.2.tgz", @@ -5128,6 +5394,12 @@ "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.2.tgz", "integrity": "sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw==" }, + "node_modules/bluebird": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz", + "integrity": "sha512-UfFSr22dmHPQqPP9XWHRhq+gWnHCYguQGkXQlbyPtW5qTnhFWA8/iXg765tH0cAjy7l/zPJ1aBTO0g5XgA7kvQ==", + "license": "MIT" + }, "node_modules/body-parser": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", @@ -5160,7 +5432,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", - "dev": true, "dependencies": { "balanced-match": "^1.0.0" } @@ -5286,6 +5557,21 @@ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" }, + "node_modules/bullmq": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.7.0.tgz", + "integrity": "sha512-FmslKEuYllrKVnhijjnu//s0bJsUbF17dzaIkyfU5t/G38AyZKpbIVw9Po7S3Yt8ya6WdvcjpCdx3/ZUJxSlzw==", + "license": "MIT", + "dependencies": { + "cron-parser": "^4.6.0", + "ioredis": "^5.3.2", + "msgpackr": "^1.10.1", + "node-abort-controller": "^3.1.1", + "semver": "^7.5.4", + "tslib": "^2.0.0", + "uuid": "^9.0.0" + } + }, "node_modules/busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -5457,6 +5743,12 @@ } ] }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", + "license": "Apache-2.0" + }, "node_modules/chai": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", @@ -5971,6 +6263,18 @@ "resolved": "dashboard", "link": true }, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/date-fns": { "version": "2.30.0", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", @@ -6037,7 +6341,6 @@ "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" } @@ -6136,6 +6439,16 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=8" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -6251,6 +6564,16 @@ "dev": true, "license": "MIT" }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", + "license": "MIT", + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", @@ -6274,6 +6597,21 @@ "fast-check": "^3.23.1" } }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "license": "Apache-2.0", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/electron-to-chromium": { "version": "1.5.364", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.364.tgz", @@ -6979,6 +7317,21 @@ "integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==", "devOptional": true }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", + "engines": [ + "node >=0.6.0" + ], + "license": "MIT" + }, "node_modules/fast-check": { "version": "3.23.2", "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.23.2.tgz", @@ -7145,6 +7498,27 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/filelist": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.6.tgz", + "integrity": "sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==", + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/filesize": { "version": "10.1.6", "resolved": "https://registry.npmjs.org/filesize/-/filesize-10.1.6.tgz", @@ -7303,6 +7677,15 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, "node_modules/form-data": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", @@ -7518,6 +7901,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0" + } + }, "node_modules/giget": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/giget/-/giget-2.0.0.tgz", @@ -7750,6 +8142,20 @@ "url": "https://opencollective.com/express" } }, + "node_modules/http-signature": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.4.0.tgz", + "integrity": "sha512-G5akfn7eKbpDN+8nPS/cb57YeA1jLTVxjpCj7tmm3QKPdyDy7T+qSC40e9ptydSWvkwjSXw1VbkpyEm39ukeAg==", + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^2.0.2", + "sshpk": "^1.18.0" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/https-proxy-agent": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", @@ -7911,6 +8317,15 @@ "url": "https://opencollective.com/ioredis" } }, + "node_modules/ip-address": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz", + "integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -8133,6 +8548,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "license": "MIT" + }, "node_modules/is-wsl": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", @@ -8156,6 +8577,12 @@ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", + "license": "MIT" + }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", @@ -8248,6 +8675,23 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/jake": { + "version": "10.9.4", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz", + "integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==", + "license": "Apache-2.0", + "dependencies": { + "async": "^3.2.6", + "filelist": "^1.0.4", + "picocolors": "^1.1.1" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", @@ -8818,6 +9262,12 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", + "license": "MIT" + }, "node_modules/jsdoc-type-pratt-parser": { "version": "4.8.0", "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.8.0.tgz", @@ -8852,6 +9302,12 @@ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "license": "(AFL-2.1 OR BSD-3-Clause)" + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -8864,6 +9320,12 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "license": "ISC" + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -8910,6 +9372,21 @@ "npm": ">=6" } }, + "node_modules/jsprim": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz", + "integrity": "sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==", + "engines": [ + "node >=0.6.0" + ], + "license": "MIT", + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + } + }, "node_modules/jwa": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", @@ -9027,8 +9504,7 @@ "node_modules/lodash": { "version": "4.18.1", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", - "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", - "dev": true + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==" }, "node_modules/lodash.includes": { "version": "4.3.0", @@ -9385,6 +9861,37 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, + "node_modules/msgpackr": { + "version": "1.11.12", + "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.11.12.tgz", + "integrity": "sha512-RBdJ1Un7yGlXWajrkxcSa93nvQ0w4zBf60c0yYv7YtBelP8H2FA7XsfBbMHtXKXUMUxH7zV3Zuozh+kUQWhHvg==", + "license": "MIT", + "optionalDependencies": { + "msgpackr-extract": "^3.0.2" + } + }, + "node_modules/msgpackr-extract": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.4.tgz", + "integrity": "sha512-4kmO/MdyUIkLIvTPr8VHLil4AtoKIoniWPIEk5+CDy0xnWC84azhSFmuJ7PxZdsYtiP5kEeQsORAVIeMgxT+Hw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "node-gyp-build-optional-packages": "5.2.2" + }, + "bin": { + "download-msgpackr-prebuilds": "bin/download-prebuilds.js" + }, + "optionalDependencies": { + "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.4", + "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.4", + "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.4", + "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.4", + "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.4", + "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.4" + } + }, "node_modules/multer": { "version": "1.4.5-lts.2", "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.2.tgz", @@ -9442,6 +9949,15 @@ "node": ">= 0.6" } }, + "node_modules/mustache": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", + "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", + "license": "MIT", + "bin": { + "mustache": "bin/mustache" + } + }, "node_modules/mz": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", @@ -9490,6 +10006,12 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, + "node_modules/node-abort-controller": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", + "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==", + "license": "MIT" + }, "node_modules/node-cron": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/node-cron/-/node-cron-4.2.1.tgz", @@ -9504,6 +10026,21 @@ "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", "devOptional": true }, + "node_modules/node-gyp-build-optional-packages": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz", + "integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==", + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.1" + }, + "bin": { + "node-gyp-build-optional-packages": "bin.js", + "node-gyp-build-optional-packages-optional": "optional.js", + "node-gyp-build-optional-packages-test": "build-test.js" + } + }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -9519,6 +10056,21 @@ "node": ">=18" } }, + "node_modules/node-vault": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/node-vault/-/node-vault-0.10.2.tgz", + "integrity": "sha512-//uc9/YImE7Dx0QHdwMiAzLaOumiKUnOUP8DymgtkZ8nsq6/V2LKvEu6kw91Lcruw8lWUfj4DO7CIXNPRWBuuA==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.4", + "mustache": "^4.2.0", + "postman-request": "^2.88.1-postman.33", + "tv4": "^1.3.0" + }, + "engines": { + "node": ">= 16.0.0" + } + }, "node_modules/nodemailer": { "version": "6.10.1", "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.10.1.tgz", @@ -9656,6 +10208,15 @@ "integrity": "sha512-+6vJA3L98yv+IdfKGZHBNiGW5KHn22e/JwID0Strsz8h4S/csAu/OuICwxrg44k5MRiZHWIo8XXuJgQTriRP4w==", "devOptional": true }, + "node_modules/oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -10272,6 +10833,83 @@ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "dev": true }, + "node_modules/postman-request": { + "version": "2.88.1-postman.48", + "resolved": "https://registry.npmjs.org/postman-request/-/postman-request-2.88.1-postman.48.tgz", + "integrity": "sha512-E32FGh8ig2KDvzo4Byi7Ibr+wK2gNKPSqXoNsvjdCHgDBxSK4sCUwv+aa3zOBUwfiibPImHMy0WdlDSSCTqTuw==", + "license": "Apache-2.0", + "dependencies": { + "@postman/form-data": "~3.1.1", + "@postman/tough-cookie": "~4.1.3-postman.1", + "@postman/tunnel-agent": "^0.6.8", + "aws-sign2": "~0.7.0", + "aws4": "^1.12.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "http-signature": "~1.4.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "^2.1.35", + "oauth-sign": "~0.9.0", + "qs": "~6.14.1", + "safe-buffer": "^5.1.2", + "socks-proxy-agent": "^8.0.5", + "stream-length": "^1.0.2", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">= 16" + } + }, + "node_modules/postman-request/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/postman-request/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/postman-request/node_modules/qs": { + "version": "6.14.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", + "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/postman-request/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -10392,6 +11030,18 @@ "node": ">=10" } }, + "node_modules/psl": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", + "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "funding": { + "url": "https://github.com/sponsors/lupomontero" + } + }, "node_modules/pstree.remy": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", @@ -10402,7 +11052,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, "engines": { "node": ">=6" } @@ -10437,6 +11086,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "license": "MIT" + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -10686,6 +11341,15 @@ "node": ">=4" } }, + "node_modules/redis-info": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/redis-info/-/redis-info-3.1.0.tgz", + "integrity": "sha512-ER4L9Sh/vm63DkIE0bkSjxluQlioBiBgf5w1UuldaW/3vPcecdljVDisZhmnCMvsxHNiARTTDDHGg9cGwTfrKg==", + "license": "MIT", + "dependencies": { + "lodash": "^4.17.11" + } + }, "node_modules/redis-parser": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", @@ -10714,6 +11378,12 @@ "node": ">=0.10.0" } }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "license": "MIT" + }, "node_modules/resolve": { "version": "1.22.12", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", @@ -11188,6 +11858,53 @@ "node": ">=8" } }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.9.tgz", + "integrity": "sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw==", + "license": "MIT", + "dependencies": { + "ip-address": "^10.1.1", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/socks-proxy-agent/node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -11227,6 +11944,31 @@ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, + "node_modules/sshpk": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", + "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", + "license": "MIT", + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/stack-trace": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", @@ -11296,6 +12038,15 @@ } } }, + "node_modules/stream-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stream-length/-/stream-length-1.0.2.tgz", + "integrity": "sha512-aI+qKFiwoDV4rsXiS7WRoCt+v2RX1nUj17+KJC5r2gfh5xoSJIfP6Y3Do/HtvesFcTSWthIuJ3l1cvKQY/+nZg==", + "license": "WTFPL", + "dependencies": { + "bluebird": "^2.6.2" + } + }, "node_modules/streamsearch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", @@ -12099,14 +12850,32 @@ "dev": true, "license": "MIT", "engines": { - "node": ">=4" + "node": ">=4" + } + }, + "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==" + }, + "node_modules/tv4": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/tv4/-/tv4-1.3.0.tgz", + "integrity": "sha512-afizzfpJgvPr+eDkREK4MxJ/+r8nEEHcmitwgnPUqpaP+FpwQyadnxNoSACbgc/b1LsZYtODGoPiFxQrgJgjvw==", + "license": [ + { + "type": "Public Domain", + "url": "http://geraintluff.github.io/tv4/LICENSE.txt" + }, + { + "type": "MIT", + "url": "http://jsonary.com/LICENSE.txt" + } + ], + "engines": { + "node": ">= 0.8.0" } }, - "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==" - }, "node_modules/tween-functions": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/tween-functions/-/tween-functions-1.2.0.tgz", @@ -12114,6 +12883,12 @@ "dev": true, "license": "BSD" }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", + "license": "Unlicense" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -12331,6 +13106,16 @@ "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.11.tgz", "integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==" }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "license": "MIT", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, "node_modules/util": { "version": "0.12.5", "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", @@ -12399,6 +13184,26 @@ "node": ">= 0.8" } }, + "node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", + "engines": [ + "node >=0.6.0" + ], + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "node_modules/verror/node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "license": "MIT" + }, "node_modules/vite": { "version": "6.4.1", "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", @@ -13507,6 +14312,33 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "@bull-board/api": { + "version": "5.21.0", + "resolved": "https://registry.npmjs.org/@bull-board/api/-/api-5.21.0.tgz", + "integrity": "sha512-27tjptwgRgP1G5jT+POjiZZOP3LgdIM4XdfEWfa6t5E0CYImL4EjmdiFo5lhbHhYKZ842VhIpHuNcPk8nY3K9A==", + "requires": { + "redis-info": "^3.0.8" + } + }, + "@bull-board/express": { + "version": "5.21.0", + "resolved": "https://registry.npmjs.org/@bull-board/express/-/express-5.21.0.tgz", + "integrity": "sha512-iBPBJq8KYebYrN4YvdSvEfOxjYYJfWycilAfNDSikyI3rJKOBRq34BmDnQj6Jn1ytssBb+vvZ35+bCSbbhFB3w==", + "requires": { + "@bull-board/api": "5.21.0", + "@bull-board/ui": "5.21.0", + "ejs": "^3.1.10", + "express": "^4.19.2" + } + }, + "@bull-board/ui": { + "version": "5.21.0", + "resolved": "https://registry.npmjs.org/@bull-board/ui/-/ui-5.21.0.tgz", + "integrity": "sha512-eH8QQwIHgCXxNEmlg9EZr3fSvno/bdbgBGfSQO5s9c9n9eDEaKX46ambKSPvgFPtwSdiV1AYQEa/3fGSebVIxg==", + "requires": { + "@bull-board/api": "5.21.0" + } + }, "@chromatic-com/storybook": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/@chromatic-com/storybook/-/storybook-3.2.7.tgz", @@ -14321,6 +15153,42 @@ "@types/mdx": "^2.0.0" } }, + "@msgpackr-extract/msgpackr-extract-darwin-arm64": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.4.tgz", + "integrity": "sha512-LCkGo6JDfaBhgST7UpPWgNgLINpcpabaHfyz5OBx75nUYxBsaEPxjnyNjWpeb/xBup/682QnBfRBy2/LvPutZQ==", + "optional": true + }, + "@msgpackr-extract/msgpackr-extract-darwin-x64": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.4.tgz", + "integrity": "sha512-zExlW9zUJKZH/tOtVMttwjKa4Xm/3KcNjnE3dPN92uCktwavMxpgCA3MoJK/DOnTWsQgo224OaST27/mPNAf+w==", + "optional": true + }, + "@msgpackr-extract/msgpackr-extract-linux-arm": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.4.tgz", + "integrity": "sha512-Tg3yX65f5GbtXLkrYEHE5oibZG9epyYWas7FogTTEJeDEF9JlXJzKgXaNhT3UXlTOeA+AfZpYZYZ0uPj7Cfquw==", + "optional": true + }, + "@msgpackr-extract/msgpackr-extract-linux-arm64": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.4.tgz", + "integrity": "sha512-dgX0P/9wGPJeHFBG+ZmhgE6bmtMt7NP5CRBGyyktpopdk/mW4POnrpQsSLtKI1dwpc+pPLuXHDh6vvskyQE/sw==", + "optional": true + }, + "@msgpackr-extract/msgpackr-extract-linux-x64": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.4.tgz", + "integrity": "sha512-8TNXMEjJc3QEy7R/x1INhgiU+XakDAFUzBhaz7+Rbrs8NH5UQeHQxxmzsSBJGyV6I1jW79undiQm8tOI+D+8FQ==", + "optional": true + }, + "@msgpackr-extract/msgpackr-extract-win32-x64": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.4.tgz", + "integrity": "sha512-CmCXPQrkbwExx3j946/PtHWHbYJiCRBRDl4BlkRQcJB/YOwQxJRTpoo7aTsortjgoJ1x7opzTSxn7C+ASSLVjQ==", + "optional": true + }, "@noble/ciphers": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz", @@ -14391,6 +15259,57 @@ "dev": true, "optional": true }, + "@postman/form-data": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@postman/form-data/-/form-data-3.1.1.tgz", + "integrity": "sha512-vjh8Q2a8S6UCm/KKs31XFJqEEgmbjBmpPNVV2eVav6905wyFAwaUOBGA1NPBI4ERH9MMZc6w0umFgM6WbEPMdg==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "dependencies": { + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "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==", + "requires": { + "mime-db": "1.52.0" + } + } + } + }, + "@postman/tough-cookie": { + "version": "4.1.3-postman.1", + "resolved": "https://registry.npmjs.org/@postman/tough-cookie/-/tough-cookie-4.1.3-postman.1.tgz", + "integrity": "sha512-txpgUqZOnWYnUHZpHjkfb0IwVH4qJmyq77pPnJLlfhMtdCLMFTEeQHlzQiK906aaNCe4NEB5fGJHo9uzGbFMeA==", + "requires": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "dependencies": { + "universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==" + } + } + }, + "@postman/tunnel-agent": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@postman/tunnel-agent/-/tunnel-agent-0.6.8.tgz", + "integrity": "sha512-2U42SmZW5G+suEcS++zB94sBWNO4qD4bvETGFRFDTqSpYl5ksfjcPqzYpgQgXgUmb6dfz+fAGbkcRamounGm0w==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, "@prisma/client": { "version": "6.19.3", "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.19.3.tgz", @@ -14633,6 +15552,32 @@ "resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.4.0.tgz", "integrity": "sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==" }, + "@sendgrid/client": { + "version": "8.1.6", + "resolved": "https://registry.npmjs.org/@sendgrid/client/-/client-8.1.6.tgz", + "integrity": "sha512-/BHu0hqwXNHr2aLhcXU7RmmlVqrdfrbY9KpaNj00KZHlVOVoRxRVrpOCabIB+91ISXJ6+mLM9vpaVUhK6TwBWA==", + "requires": { + "@sendgrid/helpers": "^8.0.0", + "axios": "^1.12.0" + } + }, + "@sendgrid/helpers": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@sendgrid/helpers/-/helpers-8.0.0.tgz", + "integrity": "sha512-Ze7WuW2Xzy5GT5WRx+yEv89fsg/pgy3T1E3FS0QEx0/VvRmigMZ5qyVGhJz4SxomegDkzXv/i0aFPpHKN8qdAA==", + "requires": { + "deepmerge": "^4.2.2" + } + }, + "@sendgrid/mail": { + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/@sendgrid/mail/-/mail-8.1.3.tgz", + "integrity": "sha512-Wg5iKSUOER83/cfY6rbPa+o3ChnYzWwv1OcsR8gCV8SKi+sUPIMroildimlnb72DBkQxcbylxng1W7f0RIX7MQ==", + "requires": { + "@sendgrid/client": "^8.1.3", + "@sendgrid/helpers": "^8.0.0" + } + }, "@sinclair/typebox": { "version": "0.27.10", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz", @@ -15996,6 +16941,19 @@ "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", "dev": true }, + "asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==" + }, "assertion-error": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", @@ -16042,6 +17000,16 @@ "possible-typed-array-names": "^1.0.0" } }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==" + }, + "aws4": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz", + "integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==" + }, "axe-core": { "version": "4.11.4", "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.11.4.tgz", @@ -16157,7 +17125,10 @@ "version": "file:backend", "requires": { "@aws-sdk/client-kms": "^3.500.0", + "@bull-board/api": "^5.21.0", + "@bull-board/express": "^5.21.0", "@prisma/client": "^6.19.2", + "@sendgrid/mail": "^8.1.3", "@stellar/stellar-sdk": "^14.6.1", "@types/cors": "^2.8.19", "@types/express": "^4.17.25", @@ -16175,6 +17146,7 @@ "@types/winston": "^2.4.4", "@typescript-eslint/eslint-plugin": "^7.3.1", "@typescript-eslint/parser": "^7.3.1", + "bullmq": "^5.7.0", "cors": "^2.8.5", "cron-parser": "^4.9.0", "dotenv": "^16.4.5", @@ -16187,6 +17159,7 @@ "jsonwebtoken": "^9.0.3", "multer": "^1.4.5-lts.2", "node-cron": "^4.2.1", + "node-vault": "^0.10.2", "nodemailer": "^6.10.1", "nodemon": "^3.1.0", "pdfkit": "^0.18.0", @@ -16219,8 +17192,7 @@ "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 + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "base32.js": { "version": "0.1.0", @@ -16238,6 +17210,14 @@ "integrity": "sha512-wbPvpyjJPC0zdfdKXxqEL3Ea+bOMD/87X4lftiJkkaBiuG6ALQy1SLmEd7BSmVCuwCQsBrCamgBoLyfFDD1EPg==", "dev": true }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "requires": { + "tweetnacl": "^0.14.3" + } + }, "better-opn": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/better-opn/-/better-opn-3.0.2.tgz", @@ -16263,6 +17243,11 @@ "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.2.tgz", "integrity": "sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw==" }, + "bluebird": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz", + "integrity": "sha512-UfFSr22dmHPQqPP9XWHRhq+gWnHCYguQGkXQlbyPtW5qTnhFWA8/iXg765tH0cAjy7l/zPJ1aBTO0g5XgA7kvQ==" + }, "body-parser": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", @@ -16288,7 +17273,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", - "dev": true, "requires": { "balanced-match": "^1.0.0" } @@ -16374,6 +17358,20 @@ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" }, + "bullmq": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.7.0.tgz", + "integrity": "sha512-FmslKEuYllrKVnhijjnu//s0bJsUbF17dzaIkyfU5t/G38AyZKpbIVw9Po7S3Yt8ya6WdvcjpCdx3/ZUJxSlzw==", + "requires": { + "cron-parser": "^4.6.0", + "ioredis": "^5.3.2", + "msgpackr": "^1.10.1", + "node-abort-controller": "^3.1.1", + "semver": "^7.5.4", + "tslib": "^2.0.0", + "uuid": "^9.0.0" + } + }, "busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -16482,6 +17480,11 @@ "integrity": "sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==", "dev": true }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" + }, "chai": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", @@ -16876,6 +17879,14 @@ "vite": "^6.4.1" } }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", + "requires": { + "assert-plus": "^1.0.0" + } + }, "date-fns": { "version": "2.30.0", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", @@ -16915,8 +17926,7 @@ "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 + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==" }, "deepmerge-ts": { "version": "7.1.5", @@ -17030,6 +18040,12 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" }, + "detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "optional": true + }, "detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -17119,6 +18135,15 @@ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, "ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", @@ -17142,6 +18167,14 @@ "fast-check": "^3.23.1" } }, + "ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "requires": { + "jake": "^10.8.5" + } + }, "electron-to-chromium": { "version": "1.5.364", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.364.tgz", @@ -17673,6 +18706,16 @@ "integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==", "devOptional": true }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==" + }, "fast-check": { "version": "3.23.2", "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.23.2.tgz", @@ -17794,6 +18837,24 @@ "flat-cache": "^3.0.4" } }, + "filelist": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.6.tgz", + "integrity": "sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==", + "requires": { + "minimatch": "^5.0.1" + }, + "dependencies": { + "minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, "filesize": { "version": "10.1.6", "resolved": "https://registry.npmjs.org/filesize/-/filesize-10.1.6.tgz", @@ -17899,6 +18960,11 @@ } } }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==" + }, "form-data": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", @@ -18037,6 +19103,14 @@ "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "dev": true }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "requires": { + "assert-plus": "^1.0.0" + } + }, "giget": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/giget/-/giget-2.0.0.tgz", @@ -18201,6 +19275,16 @@ "toidentifier": "~1.0.1" } }, + "http-signature": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.4.0.tgz", + "integrity": "sha512-G5akfn7eKbpDN+8nPS/cb57YeA1jLTVxjpCj7tmm3QKPdyDy7T+qSC40e9ptydSWvkwjSXw1VbkpyEm39ukeAg==", + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^2.0.2", + "sshpk": "^1.18.0" + } + }, "https-proxy-agent": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", @@ -18302,6 +19386,11 @@ "standard-as-callback": "2.1.0" } }, + "ip-address": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz", + "integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==" + }, "ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -18439,6 +19528,11 @@ "which-typed-array": "^1.1.16" } }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" + }, "is-wsl": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", @@ -18458,6 +19552,11 @@ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" + }, "istanbul-lib-coverage": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", @@ -18528,6 +19627,16 @@ "@isaacs/cliui": "^9.0.0" } }, + "jake": { + "version": "10.9.4", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz", + "integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==", + "requires": { + "async": "^3.2.6", + "filelist": "^1.0.4", + "picocolors": "^1.1.1" + } + }, "jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", @@ -18967,6 +20076,11 @@ "argparse": "^2.0.1" } }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" + }, "jsdoc-type-pratt-parser": { "version": "4.8.0", "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.8.0.tgz", @@ -18991,6 +20105,11 @@ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, + "json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" + }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -19003,6 +20122,11 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" + }, "json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -19036,6 +20160,17 @@ "semver": "^7.5.4" } }, + "jsprim": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz", + "integrity": "sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + } + }, "jwa": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", @@ -19131,8 +20266,7 @@ "lodash": { "version": "4.18.1", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", - "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", - "dev": true + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==" }, "lodash.includes": { "version": "4.3.0", @@ -19405,6 +20539,29 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, + "msgpackr": { + "version": "1.11.12", + "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.11.12.tgz", + "integrity": "sha512-RBdJ1Un7yGlXWajrkxcSa93nvQ0w4zBf60c0yYv7YtBelP8H2FA7XsfBbMHtXKXUMUxH7zV3Zuozh+kUQWhHvg==", + "requires": { + "msgpackr-extract": "^3.0.2" + } + }, + "msgpackr-extract": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.4.tgz", + "integrity": "sha512-4kmO/MdyUIkLIvTPr8VHLil4AtoKIoniWPIEk5+CDy0xnWC84azhSFmuJ7PxZdsYtiP5kEeQsORAVIeMgxT+Hw==", + "optional": true, + "requires": { + "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.4", + "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.4", + "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.4", + "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.4", + "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.4", + "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.4", + "node-gyp-build-optional-packages": "5.2.2" + } + }, "multer": { "version": "1.4.5-lts.2", "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.2.tgz", @@ -19448,6 +20605,11 @@ } } }, + "mustache": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", + "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==" + }, "mz": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", @@ -19481,6 +20643,11 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, + "node-abort-controller": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", + "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==" + }, "node-cron": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/node-cron/-/node-cron-4.2.1.tgz", @@ -19492,6 +20659,15 @@ "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", "devOptional": true }, + "node-gyp-build-optional-packages": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz", + "integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==", + "optional": true, + "requires": { + "detect-libc": "^2.0.1" + } + }, "node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -19504,6 +20680,17 @@ "integrity": "sha512-GYVXHE2KnrzAfsAjl4uP++evGFCrAU1jta4ubEjIG7YWt/64Gqv66a30yKwWczVjA6j3bM4nBwH7Pk1JmDHaxQ==", "dev": true }, + "node-vault": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/node-vault/-/node-vault-0.10.2.tgz", + "integrity": "sha512-//uc9/YImE7Dx0QHdwMiAzLaOumiKUnOUP8DymgtkZ8nsq6/V2LKvEu6kw91Lcruw8lWUfj4DO7CIXNPRWBuuA==", + "requires": { + "debug": "^4.3.4", + "mustache": "^4.2.0", + "postman-request": "^2.88.1-postman.33", + "tv4": "^1.3.0" + } + }, "nodemailer": { "version": "6.10.1", "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.10.1.tgz", @@ -19602,6 +20789,11 @@ } } }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -20002,6 +21194,61 @@ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "dev": true }, + "postman-request": { + "version": "2.88.1-postman.48", + "resolved": "https://registry.npmjs.org/postman-request/-/postman-request-2.88.1-postman.48.tgz", + "integrity": "sha512-E32FGh8ig2KDvzo4Byi7Ibr+wK2gNKPSqXoNsvjdCHgDBxSK4sCUwv+aa3zOBUwfiibPImHMy0WdlDSSCTqTuw==", + "requires": { + "@postman/form-data": "~3.1.1", + "@postman/tough-cookie": "~4.1.3-postman.1", + "@postman/tunnel-agent": "^0.6.8", + "aws-sign2": "~0.7.0", + "aws4": "^1.12.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "http-signature": "~1.4.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "^2.1.35", + "oauth-sign": "~0.9.0", + "qs": "~6.14.1", + "safe-buffer": "^5.1.2", + "socks-proxy-agent": "^8.0.5", + "stream-length": "^1.0.2", + "uuid": "^8.3.2" + }, + "dependencies": { + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "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==", + "requires": { + "mime-db": "1.52.0" + } + }, + "qs": { + "version": "6.14.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", + "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", + "requires": { + "side-channel": "^1.1.0" + } + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + } + } + }, "prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -20081,6 +21328,14 @@ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==" }, + "psl": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", + "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", + "requires": { + "punycode": "^2.3.1" + } + }, "pstree.remy": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", @@ -20090,8 +21345,7 @@ "punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==" }, "pure-rand": { "version": "6.1.0", @@ -20107,6 +21361,11 @@ "side-channel": "^1.1.0" } }, + "querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, "queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -20294,6 +21553,14 @@ "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==" }, + "redis-info": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/redis-info/-/redis-info-3.1.0.tgz", + "integrity": "sha512-ER4L9Sh/vm63DkIE0bkSjxluQlioBiBgf5w1UuldaW/3vPcecdljVDisZhmnCMvsxHNiARTTDDHGg9cGwTfrKg==", + "requires": { + "lodash": "^4.17.11" + } + }, "redis-parser": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", @@ -20313,6 +21580,11 @@ "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==" }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" + }, "resolve": { "version": "1.22.12", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", @@ -20631,6 +21903,37 @@ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true }, + "smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==" + }, + "socks": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.9.tgz", + "integrity": "sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw==", + "requires": { + "ip-address": "^10.1.1", + "smart-buffer": "^4.2.0" + } + }, + "socks-proxy-agent": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", + "requires": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "dependencies": { + "agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==" + } + } + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -20664,6 +21967,22 @@ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, + "sshpk": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", + "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, "stack-trace": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", @@ -20705,6 +22024,14 @@ "@storybook/core": "8.6.18" } }, + "stream-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stream-length/-/stream-length-1.0.2.tgz", + "integrity": "sha512-aI+qKFiwoDV4rsXiS7WRoCt+v2RX1nUj17+KJC5r2gfh5xoSJIfP6Y3Do/HtvesFcTSWthIuJ3l1cvKQY/+nZg==", + "requires": { + "bluebird": "^2.6.2" + } + }, "streamsearch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", @@ -21262,12 +22589,22 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, + "tv4": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/tv4/-/tv4-1.3.0.tgz", + "integrity": "sha512-afizzfpJgvPr+eDkREK4MxJ/+r8nEEHcmitwgnPUqpaP+FpwQyadnxNoSACbgc/b1LsZYtODGoPiFxQrgJgjvw==" + }, "tween-functions": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/tween-functions/-/tween-functions-1.2.0.tgz", "integrity": "sha512-PZBtLYcCLtEcjL14Fzb1gSxPBeL7nWvGhO5ZFPGqziCcr8uvHp0NDmdjBchp6KHL+tExcg0m3NISmKxhU394dA==", "dev": true }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" + }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -21416,6 +22753,15 @@ "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.11.tgz", "integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==" }, + "url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, "util": { "version": "0.12.5", "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", @@ -21466,6 +22812,23 @@ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + }, + "dependencies": { + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==" + } + } + }, "vite": { "version": "6.4.1", "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz",