commit 81d0e656868efc4e27dd354e525d6698df11e26c Author: Isuru Bimsara Date: Fri Aug 14 22:46:02 2026 +0530 first commit diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..4629a08 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,9 @@ +node_modules +npm-debug.log +Dockerfile +docker-compose.yml +.git +.gitignore +.env +.env.prod +.env.sample diff --git a/.env.sample b/.env.sample new file mode 100644 index 0000000..b5b9911 --- /dev/null +++ b/.env.sample @@ -0,0 +1,53 @@ +# App Details +APP_NAME= + +# Database configuration variables +DB_HOST = +DB_USER = +DB_PASSWORD = +DB_NAME = +DB_PORT = + +# Redis configuration variables +REDIS_HOST=localhost +REDIS_PORT=6379 + +# JWT secret key +JWT_SECRET = your_jwt_secret_key_here +JWT_EXPIRES_IN =1d + +# AWS S3 configuration +AWS_ACCESS_KEY_ID=your_aws_access_key_id_here +AWS_SECRET_ACCESS_KEY=your_aws_secret_access_key_here +AWS_REGION=your_aws_region_here +AWS_S3_BUCKET_NAME=your_aws_bucket_name_here + +# Mail configuration +MAIL_HOST= +MAIL_PORT=587 +MAIL_USER= +MAIL_PASS= +MAIL_SECURE=false +MAIL_FROM= + +# Documentation access credentials +DOCS_USER= +DOCS_PASS= + +# Admin email for receiving notifications +MANAGER_EMAIL= + +# Caching configuration +CACHE=true + +# Application environment +NODE_ENV=development + +# User default password +DEFAULT_PASSWORD= + +# Frontend URL for CORS +FRONTEND_URL=http://localhost:3000 + +# Puppeteer executable path (if needed, otherwise Puppeteer will use the bundled Chromium) +PUPPETEER_EXECUTABLE_PATH = C:\Users\User\.cache\puppeteer\chrome-headless-shell\win64-142.0.7444.162\chrome-headless-shell-win64\chrome-headless-shell.exe \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..66329dd --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.env +.env.prod +/node_modules/ +/.github/workflows/deploy-dev.yml \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..dc4df75 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,44 @@ +FROM node:20-slim + +# Install Chromium + dependencies +RUN apt-get update && apt-get install -y \ + chromium \ + fonts-liberation \ + libatk-bridge2.0-0 \ + libatk1.0-0 \ + libcups2 \ + libxcomposite1 \ + libxrandr2 \ + libxdamage1 \ + libgbm1 \ + libasound2 \ + libpangocairo-1.0-0 \ + libpango-1.0-0 \ + libnss3 \ + libxss1 \ + libgtk-3-0 \ + libdrm2 \ + libxshmfence1 \ + ca-certificates \ + --no-install-recommends \ + && rm -rf /var/lib/apt/lists/* + +# Tell Puppeteer to use system Chromium +ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium + +# Prevent Puppeteer from downloading its own Chromium +ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true + + +WORKDIR /app + +COPY package*.json ./ +RUN npm ci --omit=dev + +COPY . . + +ENV NODE_ENV=production + +EXPOSE 3070 + +CMD ["node", "server.js"] \ No newline at end of file diff --git a/Documentation/Auth-API.md b/Documentation/Auth-API.md new file mode 100644 index 0000000..f07ce72 --- /dev/null +++ b/Documentation/Auth-API.md @@ -0,0 +1,125 @@ +# Auth API + +#### Request OTP + +**Endpoint** + +``` +POST: http://localhost:3070/api/auth/req-otp +``` + +**Request Body** + +```json +{ + "email": "sathira@niolla.lk", + "password": "Niolla@123" +} +``` + +**Respond** + +```json +{ + "success": true, + "message": "OTP Sent Successfully" +} +``` + +--- + +#### Login + +**Endpoint** + +``` +POST: http://localhost:3070/api/auth/login +``` + +**Request Body** + +```json +{ + "email": "sathira@niolla.lk", + "otp": "922304" +} +``` + +**Respond** + +```json +{ + "success": true, + "message": "Login Successful", + "data": { + "id": "usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4", + "email": "sathira@niolla.lk", + "firstName": "Jhon", + "lastName": "Doe", + "role": "System Developer", + "accountType": "admin" + } +} +``` + +--- + +#### Get Current User + +**Endpoint** + +``` +GET: http://localhost:3070/api/auth/me +``` + +**Authorization**: Required (Bearer token) + +**Request Body** + +``` +No Body +``` + +**Response (200)** + +```json +{ + "authenticated": true, + "user": { + "id": "usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4", + "firstName": "Sathira", + "lastName": "Sri Sathsara", + "email": "sathira@niolla.lk", + "role": "System Developer", + "accountType": "admin", + "iat": 1778865265, + "exp": 1778868865, + "permissions": [] + } +} +``` + +--- + +### Logout + +**Endpoint** + +``` +POST: http://localhost:3070/api/auth/logout +``` + +**Request Body** + +``` +No Body +``` + +**Respond** + +```json +{ + "success": true, + "message": "Logged out successfully" +} +``` \ No newline at end of file diff --git a/Documentation/Document-API.md b/Documentation/Document-API.md new file mode 100644 index 0000000..7208922 --- /dev/null +++ b/Documentation/Document-API.md @@ -0,0 +1,740 @@ +# Document API Documentation + +## Overview + +The Document API allows authenticated users to generate documents asynchronously (PDF, Excel, etc.) from provided data. The system uses a queue-based architecture for reliable, scalable document generation. + +**Key Features:** +- ✅ Non-blocking requests (returns immediately with jobId) +- ✅ Job status polling to track progress +- ✅ Automatic retries with exponential backoff +- ✅ Support for multiple document types and formats +- ✅ Concurrent document generation +- ✅ Case-insensitive document names with whitespace and punctuation normalization + +--- + +## Architecture + +``` +Client Request → Queue Job → Return jobId (202) + ↓ + Worker processes + ↓ + Client polls status + ↓ + Job complete → Download file +``` + +--- + +## Endpoints + +### 1. Generate Document + +**Endpoint:** `POST /api/document/generate` + +**URL:** `http://localhost:3070/api/document/generate` + +**Authentication:** Required ✓ + +**Authorization:** Required - User must have one of the following roles: +- `admin` +- `management` +- `team_head` +- `user` + +**HTTP Status:** `202 Accepted` + +#### Request Headers +``` +Content-Type: application/json +Authorization: Bearer +``` + +#### Request Body + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `document` | string | Yes | Type of document (e.g., "INVOICE", "DISPATCHNOTE", "DELIVERYNOTE") | +| `documentType` | string | Yes | Output format (e.g., "pdf", "excel") | +| `documentData` | object | Yes | Data object for the document | + +#### Response (202 Accepted) + +```json +{ + "success": true, + "message": "Document generation started", + "jobId": "1" +} +``` + +#### Error Responses + +**400 Bad Request:** +```json +{ + "success": false, + "message": "document, documentType, and documentData are required" +} +``` + +**500 Server Error:** +```json + +{ + "success": false, + "message": "Error message describing the issue" +} +``` + +### Supported Document Values + +You can send the document name in any case. Spaces, underscores, and hyphens are ignored by the generator. + +- `PRECOST` +- `INVOICE` +- `DISPATCHNOTE` +- `DELIVERYNOTE` +- `CUSTOMDOCUMENT` +- `CREDITNOTE` +- `PO` + + +## Sample Request Bodies + +All examples below are for `POST /api/document/generate` with `documentType: "pdf"`. + +### PreCost + +```json +{ + "document": "PRECOST", + "documentType": "pdf", + "documentData": { + "vessel_name": "MV OCEAN STAR", + "supplyPort": "DUBAI", + "clientRfqNum": "RFQ-2026-001", + "omsRfqNum": "OMS-RFQ-0001", + "date": "08/07/2026", + "items": [ + { + "name": "Marine Rope", + "description": "High strength rope", + "qty": 10, + "unitPrice": 12.5 + }, + { + "name": "Safety Gloves", + "description": "Blue gloves", + "qty": 5, + "unitPrice": 8 + } + ], + "sub_total": 125, + "discount": 0, + "additionalCharges": 0, + "total_cost": 125 + } +} +``` + +### Invoice + +```json +{ + "document": "INVOICE", + "documentType": "pdf", + "documentData": { + "jobReference": "JOB-2026-001", + "poNumber": "PO-20254161", + "date": "08/07/2026", + "pageLabel": "Page 01 of 02", + "billToName": "SUNRICH SHIP CHANDLERS L.L.C", + "billToAddress": "BUSINESS BAY - ASPECT TOWER, 22ND FLOOR, OFFICE NO. 2201, P.O. BOX NO 39976, DUBAI", + "vesselName": "MV OCEAN STAR", + "purposeOfCall": "SUPPLY OF PROVISIONS", + "supplyDate": "08/07/2026", + "grt": "12345", + "imoNumber": "9876543", + "portCountry": "DUBAI / UAE", + "items": [ + { + "description": "Marine rope", + "remarks": "Delivered as requested", + "quantity": 10, + "unit_price": 12.5 + }, + { + "description": "Safety gloves", + "remarks": "Blue color", + "quantity": 5, + "unit_price": 8 + } + ], + "subtotal": 165, + "tax": 0, + "discount": 0, + "total": 165, + "paymentTerms": "Payment due within 30 days", + "notes": "Please verify quantities upon delivery." + } +} +``` + +### Dispatch Note + +```json +{ + "document": "DISPATCHNOTE", + "documentType": "pdf", + "documentData": { + "referenceNumber": "DN/2026/001", + "date": "08/07/2026", + "vessel": "MV OCEAN STAR", + "captain": "CAPT. JOHN DOE", + "cook": "SAMUEL", + "agent": "OCEANIC AGENT LTD", + "details": "Delivery of provisions and stores", + "placeOfDelivery": "DUBAI PORT", + "eta": "08/07/2026 08:00", + "etd": "08/07/2026 18:00", + "numberOfCrew": 18, + "nextMainOrderIn": "24 HRS", + "port": "DUBAI", + "company": "OCEANIC SHIP CHANDLERS (PVT) LTD", + "fileNo": "FILE-2026-04", + "items": [ + { + "product": "Rice", + "qty": 10, + "unit": "BAGS", + "supp": "OMS", + "poNo": "PO-001", + "receivedQty": 10, + "issuedQty": 10, + "expDate": "12/07/2026", + "receivedTime": "09:15", + "date": "08/07/2026", + "rejects": "", + "fatCon": "", + "remark": "Delivered" + } + ], + "showSignatures": true, + "preparedBy": "Admin User", + "approvedBy": "Manager User" + } +} +``` + +### Delivery Note + +```json +{ + "document": "DELIVERYNOTE", + "documentType": "pdf", + "documentData": { + "referenceNumber": "DN/OSC/2602/01", + "date": "08/07/2026", + "billToName": "SUNRICH SHIP CHANDLERS L.L.C", + "billToAddress": "BUSINESS BAY - ASPECT TOWER, 22ND FLOOR, OFFICE NO. 2201, P.O. BOX NO 39976, DUBAI", + "supplyDate": "08/07/2026", + "poNumber": "PO-20254161", + "items": [ + { + "description": "Marine rope", + "remarks": "Delivered as requested", + "unit": "PCS", + "quantity": 10 + }, + { + "description": "Safety gloves", + "remarks": "Blue color", + "unit": "BOX", + "quantity": 5 + } + ] + } +} +``` + +### Custom Document + +```json +{ + "document": "CUSTOMDOCUMENT", + "documentType": "pdf", + "documentData": { + "title": "CUSTOM DOCUMENT", + "date": "08/07/2026", + "telePhone": "+94 112 083 206", + "emailAddress": "shipsupply@oceanicmsol.com", + "officeAddressLine1": "Level 5D, Valiant Towers, Nawala Mawatha", + "officeAddressLine2": "Colombo 02", + "officeAddressLine3": "SRI LANKA", + "directorOfCustoms": "The Director of Customs", + "vehicleNo": "WPLO 9767", + "permitNo": "MV20231214006001 / Permit No. MP20240717017006", + "chiefSecurityOfficer": "Chief Security Officer", + "exportGate": "Export Gate", + "slpa": "SLPA", + "gateNo": "Gate No. 03", + "permissionText": "Please grant permission to pass these Customs entry papers to supply goods to the vessel", + "companyName": "Oceanic Maritime Solutions (Pvt) Ltd", + "subtitle": "LICENSED SHIPCHANDLERS", + "sectionCode": "SC/FORM/06", + "items": [ + { + "quantity": 10, + "unit": "PCS", + "description": "Marine rope", + "rate": 12.5, + "total": 125, + "confirmOrderRate": "" + }, + { + "quantity": 5, + "unit": "PCS", + "description": "Safety gloves", + "rate": 8, + "total": 40, + "confirmOrderRate": "" + } + ] + } +} +``` + +### Credit Note + +```json +{ + "document": "CREDITNOTE", + "documentType": "pdf", + "documentData": { + "companyName": "GREEK-LANKA MARITIME SERVICES (PVT) LTD", + "companyAddressLine1": "No. 56/2, Dharmapala Mw, Kotte", + "companyAddressLine2": "Sri Jayewardenepura", + "companyAddressLine3": "Sri Lanka", + "companyPostal": "Postal Code : 10100", + "companyContact": "Tel:+94 11 2083206 / Mobile (24/7) : +94 777 232 271", + "companyBR": "BR No : PV 0022630", + "companyLicense": "License No : SA00317-2024", + "crnNo": "GLMS/COLOMBO/474/CRN01", + "date": "26/07/2024", + "billToName": "Master & Owner of MV TEAM VENTURES", + "billToDetails": "VRIDHI MARITIME SHIP MANAGEMENT & OPERATION LLC\nOffice No: 2004, The Prism Tower, Dubai\n20th Floor, Business Bay,\nDubai - PO Box 29583.", + "vesselName": "MV \"TEAM VENTURES\"", + "grt": "25,543 MT", + "port": "REPAIRS AT COLOMBO DOCK YARD", + "imoNumber": "9339765", + "nameOfAgent": "29/04/2024 AT 21:00 HRS.LT", + "portCountry": "COLOMBO / SRI LANKA", + "items": [ + { + "description": "SIGN OFF C/E MR. DHANSINGH BHAURYAL AND M/S J/E MR. SENTHIL", + "amount": 50 + }, + { + "description": "VISA + Handling Charges - M/S M/S MR. SENTHIL", + "amount": 100 + }, + { + "description": "Transport from Airport to Dockyard", + "amount": 70 + } + ], + "totalAmount": 220, + "approvedBy": "GLMS Finance Dept.", + "approvedByLabel": "Approved by GLMS Finance Dept.", + "departmentLabel": "GLMS - Accounts Department" + } +} +``` +} +``` + +--- + +### 2. Get Job Status + +**Endpoint:** `GET /api/document/job/:jobId/status` + +**URL:** `http://localhost:3070/api/document/job/1/status` + +**Authentication:** Required ✓ + +**Authorization:** Required + +**HTTP Status:** `200 OK` + +#### Request Headers +``` +Authorization: Bearer +``` + +#### Response (Waiting/Active) + +```json +{ + "success": true, + "jobId": "1", + "state": "waiting", + "result": null, + "error": null, + "attempts": 0, + "stacktrace": [] +} +``` + +#### Response (Completed) + +```json +{ + "success": true, + "jobId": "83", + "state": "completed", + "result": { + "fileName": "precost-1781614132061.pdf", + "mimeType": "application/pdf", + "size": 220008, + "s3Key": "uploads/16adf29b-c0c2-45f9-8808-d6e15cd3d755.pdf", + "documentId": "16adf29b-c0c2-45f9-8808-d6e15cd3d755" + }, + "error": null, + "attempts": 1, + "stacktrace": [] +} +``` + +#### Response (Failed) + +```json +{ + "success": true, + "jobId": "1", + "state": "failed", + "result": null, + "error": "Error message explaining the failure", + "attempts": 3, + "stacktrace": ["stack trace line 1", "stack trace line 2"] +} +``` + +#### Job States + +| State | Meaning | Next State | +|-------|---------|-----------| +| `waiting` | Job queued, waiting for worker | `active` | +| `active` | Worker currently processing | `completed` or `failed` | +| `completed` | Job done, result available | (final) | +| `failed` | Job failed after 3 retries | (final) | +| `delayed` | Scheduled for later processing | `waiting` | + +#### Error Responses + +**404 Not Found:** +```json +{ + "success": false, + "message": "Job not found" +} +``` + +--- + +### 3. Download Document + +**Endpoint:** `GET /api/document/download/:uuid` + +**URL:** `http://localhost:3070/api/document/download/16adf29b-c0c2-45f9-8808-d6e15cd3d755` + +**Authentication:** Required ✓ + +**Authorization:** Required + +**HTTP Status:** `200 OK` + +#### Request Headers +``` +Authorization: Bearer +``` + +#### Response (Success) + +Returns the binary file with appropriate headers: + +| Header | Value | +|--------|-------| +| `Content-Type` | `application/pdf` or `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet` | +| `Content-Disposition` | `attachment; filename="[fileName]"` | + +**Response Body:** Binary file content + +#### Error Responses + +**400 Bad Request:** +```json +{ + "success": false, + "message": "fileName is required" +} +``` + +**404 Not Found:** +```json +{ + "success": false, + "message": "Document not found" +} +``` + +--- + +### 4. Cancel Job + +**Endpoint:** `DELETE /api/document/job/:jobId` + +**URL:** `http://localhost:3070/api/document/job/1` + +**Authentication:** Required ✓ + +**Authorization:** Required + +**HTTP Status:** `200 OK` + +#### Request Headers +``` +Authorization: Bearer +``` + +#### Response (Success) + +```json +{ + "success": true, + "message": "Job cancelled successfully", + "jobId": "1" +} +``` + +#### Error Responses + +**404 Not Found:** +```json +{ + "success": false, + "message": "Job not found" +} +``` + +--- + +## Document Data Notes + +The generator is tolerant of different request shapes and common field aliases. For example, it accepts both `documentData` and nested `data`, and it normalizes document names by removing spaces, underscores, and hyphens. + +If a document does not use every field in a sample payload, you can omit the unused keys. + +--- + +## Complete Workflow Example + +### 1. Generate Document + +```bash +curl -X POST http://localhost:3070/api/document/generate \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer YOUR_JWT_TOKEN" \ + -d '{ + "document": "precost", + "documentType": "pdf", + "documentData": { + "customerName": "Acme Corp", + "items": [ + {"name": "Item 1", "description": "Test", "qty": 2, "unitPrice": 100} + ] + } + }' +``` + +**Response:** +```json +{ + "success": true, + "message": "Document generation started", + "jobId": "1" +} +``` + +### 2. Check Job Status + +```bash +curl http://localhost:3070/api/document/job/1/status \ + -H "Authorization: Bearer YOUR_JWT_TOKEN" +``` + +**Polling Response (Still Processing):** +```json +{ + "success": true, + "jobId": "1", + "state": "active" +} +``` + +**Polling Response (Completed):** +```json +{ + "success": true, + "jobId": "1", + "state": "completed", + "result": { + "fileName": "precost-1715339340000.pdf" + } +} +``` + +### 3. Download File + +```bash +curl http://localhost:3070/api/document/download/16adf29b-c0c2-45f9-8808-d6e15cd3d755 \ + -H "Authorization: Bearer YOUR_JWT_TOKEN" \ + -o my-document.pdf +``` + +--- + +## JavaScript/Fetch Example + +```javascript +async function generateAndDownloadDocument(token) { + // Step 1: Generate document + const generateRes = await fetch("/api/document/generate", { + method: "POST", + headers: { + "Authorization": `Bearer ${token}`, + "Content-Type": "application/json" + }, + body: JSON.stringify({ + document: "precost", + documentType: "pdf", + documentData: { + customerName: "Acme Corp", + items: [ + { name: "Item 1", description: "Test", qty: 2, unitPrice: 100 } + ] + } + }) + }); + + const { jobId } = await generateRes.json(); + console.log("Job queued:", jobId); + + // Step 2: Poll for completion + let isComplete = false; + let result = null; + + while (!isComplete) { + const statusRes = await fetch(`/api/document/job/${jobId}/status`, { + headers: { "Authorization": `Bearer ${token}` } + }); + + const status = await statusRes.json(); + console.log("Job state:", status.state); + + if (status.state === "completed") { + result = status.result; + isComplete = true; + } else if (status.state === "failed") { + throw new Error(`Job failed: ${status.error}`); + } else { + // Wait 2 seconds before polling again + await new Promise(r => setTimeout(r, 2000)); + } + } + + // Step 3: Download the file + const downloadRes = await fetch(`/api/document/download/${result.fileName}`, { + headers: { "Authorization": `Bearer ${token}` } + }); + + const blob = await downloadRes.blob(); + + // Trigger browser download + const url = window.URL.createObjectURL(blob); + const a = document.createElement("a"); + a.href = url; + a.download = result.fileName; + a.click(); + window.URL.revokeObjectURL(url); + + console.log("Download complete!"); +} +``` + +--- + +## Important Notes + +### Authentication & Authorization +- All endpoints require JWT token in `Authorization: Bearer ` header +- User must have one of these roles: `admin`, `management`, `team_head`, `user` + +### Polling Strategy +- Poll status every 2-5 seconds +- Max recommended: 60 attempts (5 minutes timeout) +- Move to download immediately when state is `completed` + +### Error Handling +- Jobs automatically retry up to 3 times with exponential backoff +- Failed jobs remain in queue for debugging +- Check `error` and `stacktrace` fields for failure details + +### File Management +- Generated files stored in `/app/documents/` +- File names auto-generated: `{document}-{timestamp}.{ext}` +- Always sanitize fileName in download requests + +### Supported Formats +- **PDF:** `documentType: "pdf"` +- **Excel:** `documentType: "excel"` + +### Performance +- Worker concurrency: 2 concurrent jobs +- Timeout per job: 5 minutes +- Retry attempts: 3 with exponential backoff + +--- + +## Common Response Codes + +| Code | Meaning | +|------|---------| +| `202` | Accepted - Job queued successfully | +| `200` | OK - Successful operation | +| `400` | Bad Request - Invalid input | +| `401` | Unauthorized - Missing/invalid token | +| `403` | Forbidden - Insufficient permissions | +| `404` | Not Found - Job or file not found | +| `500` | Server Error - Internal error | + +--- + +## Migration Notes + +**If upgrading from synchronous API:** + +**Old (Synchronous):** +- Request returned file immediately +- Long request timeout +- Blocking operation + +**New (Asynchronous):** +- Request returns jobId immediately (202) +- Poll `/api/document/job/{jobId}/status` for progress +- Non-blocking, scalable architecture +- Automatic retries built-in + diff --git a/Documentation/Notification-API.md b/Documentation/Notification-API.md new file mode 100644 index 0000000..f12bbc7 --- /dev/null +++ b/Documentation/Notification-API.md @@ -0,0 +1,137 @@ + +# Notification API + +## Create New Notification + +**Endpoint** + +``` +POST: http://localhost:3070/api/notification +``` + + +**Request Body** + +```json +{ + "notificationHeadline": "System Maintenance", + "notificationDescription": "The system will be unavailable from 10 PM to 12 AM.", + "notificationType": "ANNOUNCEMENT" +} +``` + +**Respond** + +```json +{ + "success": true, + "message": "Notification created successfully", + "notification": { + "notification_id": "notif_1782553200000", + "notificationHeadline": "System Maintenance", + "notificationDescription": "The system will be unavailable from 10 PM to 12 AM.", + "notificationType": "ANNOUNCEMENT", + "dateCreated": "2026-06-27T05:30:00.000Z", + "isActive": true, + "updatedAt": "2026-06-27T05:30:00.000Z", + "createdAt": "2026-06-27T05:30:00.000Z" + } +} +``` + + + +## Get Announcements + +**Endpoint** + +``` +GET: http://localhost:3070/api/notification/announcements +``` + +**Respond** + +```json +{ + "success": true, + "announcements": [ + { + "notification_id": "notif_1782553200000", + "notificationHeadline": "System Maintenance", + "notificationDescription": "The system will be unavailable from 10 PM to 12 AM.", + "notificationType": "ANNOUNCEMENT", + "isActive": true, + "dateCreated": "2026-06-27T05:30:00.000Z", + "createdAt": "2026-06-27T05:30:00.000Z", + "updatedAt": "2026-06-27T05:30:00.000Z" + } + ] +} +``` + +--- + +## Get User Notifications + +**Endpoint** + +``` +GET: http://localhost:3070/api/notification/user/:userId +``` + +Example: + +``` +GET: http://localhost:3070/api/notification/user/usr_12345 +``` + +**Respond** + +```json +{ + "success": true, + "notifications": [ + { + "id": "userNotif_001", + "user_id": "usr_12345", + "notification_id": "notif_1782553200000", + "isRead": false, + "createdAt": "2026-06-27T05:30:00.000Z", + "updatedAt": "2026-06-27T05:30:00.000Z", + "notification": { + "notification_id": "notif_1782553200000", + "notificationHeadline": "System Maintenance", + "notificationDescription": "The system will be unavailable from 10 PM to 12 AM.", + "notificationType": "ANNOUNCEMENT", + "isActive": true + } + } + ] +} +``` + +--- + +## Mark Notification As Read + +**Endpoint** + +``` +PATCH: http://localhost:3070/api/notification/user/:userId/notification/:notificationId/read +``` + +Example: + +``` +PATCH: http://localhost:3070/api/notification/user/usr_12345/notification/notif_1782553200000/read +``` + +**Respond** + +```json +{ + "success": true, + "message": "Notification marked as read" +} +``` + diff --git a/Documentation/Permission-API.md b/Documentation/Permission-API.md new file mode 100644 index 0000000..04d60e6 --- /dev/null +++ b/Documentation/Permission-API.md @@ -0,0 +1,755 @@ +# Permission API + +## Authentication +All endpoints require authentication token. Include in header: +``` +Authorization: Bearer +``` + +--- + +## Permission Endpoints + +### Create Permission + +**Endpoint** +``` +POST: http://localhost:3070/api/permission +``` + +**Authorization**: Admin only + +**Request Body** +```json +{ + "permissionName": "Create Inquiry", + "permissionDescription": "Permission to create new inquiries", + "page": "inquiry", + "module": "inquiry_management", + "action": "create" +} +``` + +**Response (201)** +```json +{ + "success": true, + "data": { + "permission_id": "inquiry_management.inquiry.create", + "permissionName": "Create Inquiry", + "permissionDescription": "Permission to create new inquiries", + "page": "inquiry", + "module": "inquiry_management", + "action": "create", + "createdAt": "2026-05-15T10:30:00Z", + "updatedAt": "2026-05-15T10:30:00Z" + } +} +``` + +--- + +### Bulk Create Permissions + +**Endpoint** +``` +POST: http://localhost:3070/api/permission/bulk +``` + +**Authorization**: Admin only + +**Request Body** +```json +{ + "permissions": [ + { + "permissionName": "Create Inquiry", + "permissionDescription": "Permission to create new inquiries", + "page": "inquiry", + "module": "inquiry_management", + "action": "create" + }, + { + "permissionName": "View Inquiry", + "permissionDescription": "Permission to view inquiries", + "page": "inquiry", + "module": "inquiry_management", + "action": "view" + }, + { + "permissionName": "Edit Inquiry", + "permissionDescription": "Permission to edit inquiries", + "page": "inquiry", + "module": "inquiry_management", + "action": "edit" + } + ] +} +``` + +**Response (201)** +```json +{ + "success": true, + "data": { + "created": [ + { + "permission_id": "inquiry_management.inquiry.create", + "permissionName": "Create Inquiry", + "permissionDescription": "Permission to create new inquiries", + "page": "inquiry", + "module": "inquiry_management", + "action": "create" + }, + { + "permission_id": "inquiry_management.inquiry.view", + "permissionName": "View Inquiry", + "permissionDescription": "Permission to view inquiries", + "page": "inquiry", + "module": "inquiry_management", + "action": "view" + }, + { + "permission_id": "inquiry_management.inquiry.edit", + "permissionName": "Edit Inquiry", + "permissionDescription": "Permission to edit inquiries", + "page": "inquiry", + "module": "inquiry_management", + "action": "edit" + } + ], + "failed": [] + } +} +``` + +--- + +### Get All Permissions + +**Endpoint** +``` +GET: http://localhost:3070/api/permission +``` + +**Response (200)** +```json +{ + "success": true, + "data": [ + { + "permission_id": "inquiry_management.inquiry.create", + "permissionName": "Create Inquiry", + "permissionDescription": "Permission to create new inquiries", + "page": "inquiry", + "module": "inquiry_management", + "action": "create" + }, + { + "permission_id": "inquiry_management.inquiry.view", + "permissionName": "View Inquiry", + "permissionDescription": "Permission to view inquiries", + "page": "inquiry", + "module": "inquiry_management", + "action": "view" + } + ] +} +``` + +--- + +### Get Permission by ID + +**Endpoint** +``` +GET: http://localhost:3070/api/permission/:permissionId +``` + +**Example** +``` +GET: http://localhost:3070/api/permission/inquiry_management.inquiry.create +``` + +**Response (200)** +```json +{ + "success": true, + "data": { + "permission_id": "inquiry_management.inquiry.create", + "permissionName": "Create Inquiry", + "permissionDescription": "Permission to create new inquiries", + "page": "inquiry", + "module": "inquiry_management", + "action": "create" + } +} +``` + +--- + +### Update Permission + +**Endpoint** +``` +PUT: http://localhost:3070/api/permission/:permissionId +``` + +**Authorization**: Admin only + +**Request Body** +```json +{ + "permissionName": "Create New Inquiry", + "permissionDescription": "Updated permission to create new inquiries" +} +``` + +**Response (200)** +```json +{ + "success": true, + "data": { + "permission_id": "inquiry_management.inquiry.create", + "permissionName": "Create New Inquiry", + "permissionDescription": "Updated permission to create new inquiries", + "page": "inquiry", + "module": "inquiry_management", + "action": "create" + }, + "message": "Permission updated successfully" +} +``` + +--- + +### Delete Permission + +**Endpoint** +``` +DELETE: http://localhost:3070/api/permission/:permissionId +``` + +**Authorization**: Admin only + +**Response (200)** +```json +{ + "success": true, + "message": "Permission deleted successfully" +} +``` + +--- + +## Role Endpoints + +### Create Role + +**Endpoint** +``` +POST: http://localhost:3070/api/permission/roles +``` + +**Authorization**: Admin only + +**Request Body** +```json +{ + "roleName": "Inquiry Manager", + "roleDescription": "Role for managing inquiries" +} +``` + +**Response (201)** +```json +{ + "success": true, + "data": { + "role_id": "role_1715843400000", + "roleName": "Inquiry Manager", + "roleDescription": "Role for managing inquiries", + "createdAt": "2026-05-15T10:30:00Z", + "updatedAt": "2026-05-15T10:30:00Z" + } +} +``` + +--- + +### Get All Roles + +**Endpoint** +``` +GET: http://localhost:3070/api/permission/roles +``` + +**Response (200)** +```json +{ + "success": true, + "data": [ + { + "role_id": "role_1715843400000", + "roleName": "Inquiry Manager", + "roleDescription": "Role for managing inquiries", + "rolePermissions": [ + { + "rp_id": "rp_1715843500000", + "role_id": "role_1715843400000", + "permission_id": "inquiry_management.inquiry.create", + "permission": { + "permission_id": "inquiry_management.inquiry.create", + "permissionName": "Create Inquiry", + "page": "inquiry", + "module": "inquiry_management", + "action": "create" + } + } + ] + } + ] +} +``` + +--- + +### Get Role by ID + +**Endpoint** +``` +GET: http://localhost:3070/api/permission/roles/:roleId +``` + +**Example** +``` +GET: http://localhost:3070/api/permission/roles/role_1715843400000 +``` + +**Response (200)** +```json +{ + "success": true, + "data": { + "role_id": "role_1715843400000", + "roleName": "Inquiry Manager", + "roleDescription": "Role for managing inquiries", + "rolePermissions": [ + { + "rp_id": "rp_1715843500000", + "role_id": "role_1715843400000", + "permission_id": "inquiry_management.inquiry.create", + "permission": { + "permission_id": "inquiry_management.inquiry.create", + "permissionName": "Create Inquiry", + "page": "inquiry", + "module": "inquiry_management", + "action": "create" + } + } + ] + } +} +``` + +--- + +### Update Role + +**Endpoint** +``` +PUT: http://localhost:3070/api/permission/roles/:roleId +``` + +**Authorization**: Admin only + +**Request Body** +```json +{ + "roleName": "Senior Inquiry Manager", + "roleDescription": "Updated role for managing inquiries" +} +``` + +**Response (200)** +```json +{ + "success": true, + "data": { + "role_id": "role_1715843400000", + "roleName": "Senior Inquiry Manager", + "roleDescription": "Updated role for managing inquiries" + }, + "message": "Role updated successfully" +} +``` + +--- + +### Delete Role + +**Endpoint** +``` +DELETE: http://localhost:3070/api/permission/roles/:roleId +``` + +**Authorization**: Admin only + +**Response (200)** +```json +{ + "success": true, + "message": "Role deleted successfully" +} +``` + +--- + +## Role-Permission Assignment Endpoints + +### Assign Permission to Role + +**Endpoint** +``` +POST: http://localhost:3070/api/permission/roles/:roleId/permissions +``` + +**Authorization**: Admin only + +**Request Body** +```json +{ + "role_id": "role_1715843400000", + "permission_id": "inquiry_management.inquiry.create" +} +``` + +**Response (201)** +```json +{ + "success": true, + "data": { + "rp_id": "rp_1715843500000", + "role_id": "role_1715843400000", + "permission_id": "inquiry_management.inquiry.create", + "createdAt": "2026-05-15T10:30:00Z", + "updatedAt": "2026-05-15T10:30:00Z" + } +} +``` + +--- + +### Bulk Assign Permissions to Role + +**Endpoint** +``` +POST: http://localhost:3070/api/permission/roles/permissions/bulk +``` + +**Authorization**: Admin only + +**Request Body** +```json +{ + "role_id": "role_1715843400000", + "permission_ids": [ + "inquiry_management.inquiry.create", + "inquiry_management.inquiry.view", + "inquiry_management.inquiry.edit", + "vendor_management.vendor.view" + ] +} +``` + +**Response (201)** +```json +{ + "success": true, + "data": { + "created": [ + { + "rp_id": "rp_1715843500001", + "role_id": "role_1715843400000", + "permission_id": "inquiry_management.inquiry.create" + }, + { + "rp_id": "rp_1715843500002", + "role_id": "role_1715843400000", + "permission_id": "inquiry_management.inquiry.view" + }, + { + "rp_id": "rp_1715843500003", + "role_id": "role_1715843400000", + "permission_id": "inquiry_management.inquiry.edit" + }, + { + "rp_id": "rp_1715843500004", + "role_id": "role_1715843400000", + "permission_id": "vendor_management.vendor.view" + } + ], + "failed": [] + } +} +``` + +**Response with Failures (201)** +```json +{ + "success": false, + "data": { + "created": [ + { + "rp_id": "rp_1715843500001", + "role_id": "role_1715843400000", + "permission_id": "inquiry_management.inquiry.create" + } + ], + "failed": [ + { + "role_id": "role_1715843400000", + "permission_id": "inquiry_management.inquiry.view", + "error": "Role permission already exists" + }, + { + "role_id": "role_1715843400000", + "permission_id": "nonexistent_permission", + "error": "Permission not found" + } + ] + } +} +``` + +--- + +## User Permission Endpoints + +### Get User Permissions + +**Endpoint** +``` +GET: http://localhost:3070/api/permission/users/:userId/permissions +``` + +**Example** +``` +GET: http://localhost:3070/api/permission/users/usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4/permissions +``` + +**Response (200)** +```json +{ + "success": true, + "data": [ + { + "up_id": 1, + "user_id": "usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4", + "permission_id": "inquiry_management.inquiry.create", + "permission": { + "permission_id": "inquiry_management.inquiry.create", + "permissionName": "Create Inquiry", + "page": "inquiry", + "module": "inquiry_management", + "action": "create" + } + } + ] +} +``` + +--- + +### Create User Permission + +**Endpoint** +``` +POST: http://localhost:3070/api/permission/users/permissions +``` + +**Authorization**: Admin only + +**Request Body** +```json +{ + "user_id": "usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4", + "permission_id": "inquiry_management.inquiry.create" +} +``` + +**Response (201)** +```json +{ + "success": true, + "data": { + "up_id": 1, + "user_id": "usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4", + "permission_id": "inquiry_management.inquiry.create", + "createdAt": "2026-05-15T10:30:00Z", + "updatedAt": "2026-05-15T10:30:00Z" + } +} +``` + +--- + +### Bulk Create User Permissions + +**Endpoint** +``` +POST: http://localhost:3070/api/permission/users/permissions/bulk +``` + +**Authorization**: Admin only + +**Request Body** +```json +{ + "user_id": "usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4", + "permission_ids": [ + "inquiry_management.inquiry.create", + "inquiry_management.inquiry.view", + "inquiry_management.inquiry.edit", + "vendor_management.vendor.view" + ] +} +``` + +**Response (201)** +```json +{ + "success": true, + "data": { + "created": [ + { + "up_id": 1, + "user_id": "usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4", + "permission_id": "inquiry_management.inquiry.create" + }, + { + "up_id": 2, + "user_id": "usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4", + "permission_id": "inquiry_management.inquiry.view" + }, + { + "up_id": 3, + "user_id": "usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4", + "permission_id": "inquiry_management.inquiry.edit" + }, + { + "up_id": 4, + "user_id": "usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4", + "permission_id": "vendor_management.vendor.view" + } + ], + "failed": [] + } +} +``` + +**Response with Failures (201)** +```json +{ + "success": false, + "data": { + "created": [ + { + "up_id": 1, + "user_id": "usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4", + "permission_id": "inquiry_management.inquiry.create" + } + ], + "failed": [ + { + "user_id": "usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4", + "permission_id": "inquiry_management.inquiry.view", + "error": "User permission already exists" + }, + { + "user_id": "usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4", + "permission_id": "nonexistent_permission", + "error": "Permission not found" + } + ] + } +} +``` + +--- + +### Update User Permission + +**Endpoint** +``` +PUT: http://localhost:3070/api/permission/users/permissions/:upId +``` + +**Authorization**: Admin only + +**Request Body** +```json +{ + "permission_id": "inquiry_management.inquiry.edit" +} +``` + +**Response (200)** +```json +{ + "success": true, + "data": { + "up_id": 1, + "user_id": "usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4", + "permission_id": "inquiry_management.inquiry.edit" + }, + "message": "User permission updated successfully" +} +``` + +--- + +### Delete User Permission + +**Endpoint** +``` +DELETE: http://localhost:3070/api/permission/users/permissions/:upId +``` + +**Authorization**: Admin only + +**Response (200)** +```json +{ + "success": true, + "message": "User permission deleted successfully" +} +``` + +--- + +## Error Responses + +### Bad Request (400) +```json +{ + "success": false, + "message": "Permission name, page, module, and action are required" +} +``` + +### Not Found (404) +```json +{ + "success": false, + "message": "Permission not found" +} +``` + +### Internal Server Error (500) +```json +{ + "success": false, + "message": "An error occurred while fetching permissions." +} +``` diff --git a/Documentation/Profile-API.md b/Documentation/Profile-API.md new file mode 100644 index 0000000..d595ed2 --- /dev/null +++ b/Documentation/Profile-API.md @@ -0,0 +1,253 @@ +# Profile API + +#### Get Profile Avatar + +**Endpoint** + +``` +GET: http://localhost:3070/api/profile/avatar/:userId +``` + +**Path Parameters** + +| Parameter | Type | Required | Description | +|-----------|--------|----------|-----------------| +| userId | string | Yes | The user ID | + +**Headers** + +``` +Authorization: Bearer +``` + +**Response** + +```json +{ + "success": true, + "data": { + "userId": "usr_763107d9-b56f-4b81-9d86-1889f98a6e6c", + "profilePictureUrl": "https://signed-s3-url.com/profile-picture.jpg" + } +} +``` + +**Error Responses** + +```json +{ + "success": false, + "message": "Profile not found", + "error": "Profile not found" +} +``` + +```json +{ + "success": false, + "message": "Internal server error", + "error": "error message" +} +``` + +--- + +#### Get Profile Background Image + +**Endpoint** + +``` +GET: http://localhost:3070/api/profile/background/:userId +``` + +**Path Parameters** + +| Parameter | Type | Required | Description | +|-----------|--------|----------|-----------------| +| userId | string | Yes | The user ID | + +**Headers** + +``` +Authorization: Bearer +``` + +**Response** + +```json +{ + "success": true, + "data": { + "userId": "usr_763107d9-b56f-4b81-9d86-1889f98a6e6c", + "backgroundImageUrl": "https://signed-s3-url.com/background-image.jpg" + } +} +``` + +**Error Responses** + +```json +{ + "success": false, + "message": "Profile not found", + "error": "Profile not found" +} +``` + +```json +{ + "success": false, + "message": "Internal server error", + "error": "error message" +} +``` + +--- + +#### Profile Object Structure + +The Profile object contains the following fields: + +| Field | Type | Description | +|--------------------|---------|----------------------------------| +| profile_id | string | Unique profile identifier | +| user_id | string | Associated user ID | +| theme | string | User theme preference (light/dark) | +| notificationsEnabled | boolean | Notification settings | +| profilePicture_id | string | Upload ID for profile picture | +| backgroundImage_id | string | Upload ID for background image | +| dob | date | Date of birth | +| phone_number | string | Phone number | +| createdAt | datetime| Profile creation timestamp | +| updatedAt | datetime| Profile last update timestamp | + +--- + +#### Request Password Reset + +**Endpoint** + +``` +POST: http://localhost:3070/api/profile/req-reset-password +``` + +**Request Body** + +```json +{ + "email": "sathira.nirmal@gmail.com" +} +``` + +**Response** + +```json +{ + "success": true, + "message": "If an account exists with this email address, a password reset email has been sent." +} +``` + +**Description** + +This endpoint generates a password reset token and sends a reset link to the user's email. The reset link will be sent in email format: + +``` +http://localhost:3000/reset-password?token=TOKEN_HERE&email=EMAIL_HERE +``` + +Example reset link: + +``` +http://localhost:3000/reset-password?token=aa27def4222adedcf72a417dfc40b69cd01f8bdb07f37b324a8501f3bdb37e44&email=sathira.nirmal%40gmail.com +``` + +**Notes** + +- This endpoint does not require authentication +- No error is returned if email doesn't exist (for security reasons) +- Token expires after the time specified in `PASSWORD_RESET_EXPIRY_TIME` environment variable (default: 10 minutes) + +--- + +#### Reset Password + +**Endpoint** + +``` +POST: http://localhost:3070/api/profile/reset-password +``` + +**Request Body** + +```json +{ + "email": "sathira.nirmal@gmail.com", + "token": "168e5d9d4dfb124571f412c5521874e8440a0f9e4c151fa24518ae57ca1c4f8f", + "newPassword": "niolla" +} +``` + +**Response** + +```json +{ + "success": true, + "message": "Password reset successfully." +} +``` + +**Error Responses** + +```json +{ + "success": false, + "message": "Invalid email address." +} +``` + +```json +{ + "success": false, + "message": "Invalid or expired token." +} +``` + +**Description** + +This endpoint resets the user's password using a valid reset token. The token must be obtained from the password reset email. + +**Notes** + +- This endpoint does not require authentication +- Token must be valid and not expired +- New password will replace the old password immediately + +--- + +--- + +#### Authentication + +All endpoints require authentication token unless otherwise noted. Include the Bearer token in the Authorization header: + +``` +Authorization: Bearer +``` + +**Authentication Required:** +- GET /avatar/:userId +- GET /background/:userId +- POST /change-password + +**No Authentication Required:** +- POST /req-reset-password +- POST /reset-password + +#### Authorization + +Access levels for profile endpoints: + +- **GET /avatar/:userId** - Accessible by: admin, management, team_head, user +- **GET /background/:userId** - Accessible by: admin, management, team_head, user +- **POST /change-password** - Accessible by: admin, management, team_head, user (authenticated users) diff --git a/Documentation/Upload-API.md b/Documentation/Upload-API.md new file mode 100644 index 0000000..4896cd9 --- /dev/null +++ b/Documentation/Upload-API.md @@ -0,0 +1,61 @@ +# Upload API + +#### Upload A File + +**Endpoint** + +``` +POST: http://localhost:3070/api/upload +``` + +**Request Body** + +``` +Key: file +Value: uploadfile.pdf / avatar.png +``` + + +**Respond** + +```json +{ + "success": true, + "message": "File uploaded successfully", + "data": { + "id": 4, + "file_path": "uploads/1f642d1d-dc79-423f-aad0-36df9938c1ff.pdf", + "file_type": "application/pdf", + "file_size": 15640, + "original_name": "SamplePDF.pdf", + "use_for": "vendor_documents", + "uploaded_by": "usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4", + "updatedAt": "2026-04-04T19:44:03.252Z", + "createdAt": "2026-04-04T19:44:03.252Z", + "file_url": "https://oceanic-bucket.s3.ap-southeast-1.amazonaws.com/uploads/1f642d1d-dc79-423f-aad0-36df9938c1ff.pdf?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIASP2AP6EU76EJ63PD%2F20260404%2Fap-southeast-1%2Fs3%2Faws4_request&X-Amz-Date=20260404T194403Z&X-Amz-Expires=3600&X-Amz-Signature=f9ecb283dfe206e87fa9bbd9f59194dfca4cdb11d8bb02f0ae5abd89db7ddc04&X-Amz-SignedHeaders=host&x-amz-checksum-mode=ENABLED&x-id=GetObject" + } +} +``` + +#### Get Uploaded File + +**Endpoint** + +``` +POST: http://localhost:3070/api/upload/signed-url/:id +``` + +Example: http://localhost:3070/api/upload/signed-url/3 + +**Respond** + +```json +{ + "success": true, + "message": "File URL retrieved successfully", + "data": { + "id": 3, + "file_url": "https://oceanic-bucket.s3.ap-southeast-1.amazonaws.com/uploads/9c441266-c1ee-4813-9b81-cf13276535c9.pdf?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIASP2AP6EU76EJ63PD%2F20260404%2Fap-southeast-1%2Fs3%2Faws4_request&X-Amz-Date=20260404T195205Z&X-Amz-Expires=3600&X-Amz-Signature=de8f9a4658b2802af4230f0ebba25511fb57e75cce4be75aa2e34d40a771bffb&X-Amz-SignedHeaders=host&x-amz-checksum-mode=ENABLED&x-id=GetObject" + } +} +``` \ No newline at end of file diff --git a/Documentation/User-API.md b/Documentation/User-API.md new file mode 100644 index 0000000..6a62b3c --- /dev/null +++ b/Documentation/User-API.md @@ -0,0 +1,223 @@ +# User API + +#### Create New User + +**Endpoint** + +``` +POST: http://localhost:3070/api/user +``` + +**Request Body** + +```json +{ + "firstName": "Jhon", + "lastName": "Doe", + "email": "kalanajayasekara@niolla.lk", + "role": "System Developer", + "accountType": "admin", + "department": "IT Department" +} +``` + +**Respond** + +```json +{ + "success": true, + "message": "User Created Successfully", + "data": { + "id": "usr_763107d9-b56f-4b81-9d86-1889f98a6e6c", + "firstName": "Jhon", + "lastName": "Doe", + "email": "kalanajayasekara@niolla.lk", + "accountType": "admin", + "role": "System Developer", + "department": "IT Department" + } +} +``` + +#### Get All Users + +**Endpoint** + +``` +GET: http://localhost:3070/api/user +``` + +**Respond** + +```json +{ + "success": true, + "data": [ + { + "id": "usr_b00045f7-aafb-482a-8587-d28beb5195ec", + "firstName": "Kalana", + "lastName": "Jayasekara", + "email": "kalanamanupiya32@gmail.com", + "accountType": "admin", + "role": "Manager", + "department": "Operations", + "createdAt": "2026-02-14T19:14:25.000Z", + "updatedAt": "2026-02-14T19:14:25.000Z" + }, + { + "id": "usr_763107d9-b56f-4b81-9d86-1889f98a6e6c", + "firstName": "Kalana", + "lastName": "Manupiya", + "email": "kalanajayasekara@niolla.lk", + "accountType": "admin", + "role": "System Developer", + "department": "IT Department", + "createdAt": "2026-02-12T19:32:15.000Z", + "updatedAt": "2026-02-12T19:32:15.000Z" + }, + { + "id": "usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4", + "firstName": "Jhon", + "lastName": "Doe", + "email": "sathira@niolla.lk", + "accountType": "admin", + "role": "System Developer", + "department": "IT Department", + "createdAt": "2026-02-12T19:24:04.000Z", + "updatedAt": "2026-02-12T19:24:04.000Z" + } + ], + "pagination": { + "totalUsers": 3, + "totalPages": 1, + "currentPage": 1, + "pageSize": 20 + } +} +``` + +This API uses Pagination: + +``` +GET /user?page=1 +GET /user?page=2 +GET /user?page=3 +``` + +#### Get User by ID + +**Endpoint** + +``` +GET: http://localhost:3070/api/user/:id +``` + +Ex: http://localhost:3070/api/user/usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4 + +**Response** + +```json +{ + "success": true, + "data": { + "id": "usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4", + "firstName": "Sathira", + "lastName": "Sri Sathsara", + "email": "sathira@niolla.lk", + "accountType": "admin", + "role": "System Developer", + "department": "IT Department", + "createdAt": "2026-02-12T19:24:04.000Z", + "updatedAt": "2026-02-14T21:33:19.751Z", + "profile": { + "profile_id": "prof_a1b2c3d4-e5f6-7890-1234-567890abcdef", + "user_id": "usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4", + "theme": "light", + "notificationsEnabled": true, + "profilePicture_id": "up_xyz789", + "backgroundImage_id": "up_abc123", + "dob": "1995-03-15T00:00:00.000Z", + "phone_number": "+1 234-567-8900", + "createdAt": "2026-02-12T19:24:04.000Z", + "updatedAt": "2026-02-12T19:24:04.000Z" + } + } +} +``` + +#### Update User + +**Endpoint** + +``` +PATCH: http://localhost:3070/api/user/:id +``` + +Ex: http://localhost:3070/api/user/usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4 + +**Request Body** + +Can send single field or multiple fields. The following fields can be updated: + +```json +{ + "firstName": "Sathira", + "lastName": "Sri Sathsara", + "email": "sathira@niolla.lk", + "role": "System Developer", + "roleID": "role_123", + "accountType": "admin", + "department": "IT Department", + "theme": "dark", + "notificationsEnabled": false, + "profilePicture_id": "up_profile_123", + "backgroundImage_id": "up_background_456", + "dob": "1995-03-15", + "phone_number": "+1 234-567-8900" +} +``` + +**Response** + +```json +{ + "success": true, + "message": "User updated successfully", + "data": { + "id": "usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4", + "firstName": "Sathira", + "lastName": "Sri Sathsara", + "email": "sathira@niolla.lk", + "accountType": "admin", + "role": "System Developer", + "department": "IT Department", + "profile": { + "theme": "dark", + "notificationsEnabled": false, + "profilePicture_id": "up_profile_123", + "backgroundImage_id": "up_background_456", + "dob": "1995-03-15T00:00:00.000Z", + "phone_number": "+1 234-567-8900" + } + } +} +``` + +#### Delete User + +**Endpoint** + +``` +DELETE: http://localhost:3070/api/user/:id +``` + +Ex: http://localhost:3070/api/user/usr_b00045f7-aafb-482a-8587-d28beb5195ec + +**Respond** + +```json +{ + "success": true, + "message": "User deleted successfully" +} +``` \ No newline at end of file diff --git a/Documentation/UserActivity-API.md b/Documentation/UserActivity-API.md new file mode 100644 index 0000000..4901380 --- /dev/null +++ b/Documentation/UserActivity-API.md @@ -0,0 +1,113 @@ +# User Activity API + +#### Get All Users Activies + +**Endpoint** + +``` +POST: http://localhost:3070/api/activity +``` + +**Respond** + +```json +{ + "success": true, + "message": "User activities retrieved successfully", + "data": [ + { + "id": 3, + "user_id": "usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4", + "username": "Sathira", + "activity_description": "Created vendor with ID: vnd_009270f9-e8ef-42d9-a3e4-5398c9466972", + "activity_type": "CREATE_VENDOR", + "module": "Vendor Management", + "activity_time": "2026-03-30T19:39:45.000Z", + "activity_date": "2026-03-30", + "createdAt": "2026-03-30T19:39:45.000Z", + "updatedAt": "2026-03-30T19:39:45.000Z" + }, + { + "id": 2, + "user_id": "usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4", + "username": "Sathira", + "activity_description": "Created vendor with ID: vnd_95018101-b539-45a4-b9a6-4551cecda990", + "activity_type": "CREATE_VENDOR", + "module": "Vendor Management", + "activity_time": "2026-03-30T19:17:58.000Z", + "activity_date": "2026-03-30", + "createdAt": "2026-03-30T19:17:58.000Z", + "updatedAt": "2026-03-30T19:17:58.000Z" + }, + { + "id": 1, + "user_id": "usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4", + "username": "Sathira", + "activity_description": "Created vendor with ID: vnd_c666b2cd-36fe-46db-aecd-18ec7677ccff", + "activity_type": "CREATE_VENDOR", + "module": null, + "activity_time": "2026-03-30T19:16:41.000Z", + "activity_date": "2026-03-30", + "createdAt": "2026-03-30T19:16:41.000Z", + "updatedAt": "2026-03-30T19:16:41.000Z" + } + ] +} +``` + +#### Get Activies by User ID + +**Endpoint** + +``` +POST: http://localhost:3070/api/activity/user/:userID +``` + +Example: http://localhost:3070/api/activity/user/usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4 + +**Respond** + +```json +{ + "success": true, + "message": "User activities retrieved successfully", + "data": [ + { + "id": 3, + "user_id": "usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4", + "username": "Sathira", + "activity_description": "Created vendor with ID: vnd_009270f9-e8ef-42d9-a3e4-5398c9466972", + "activity_type": "CREATE_VENDOR", + "module": "Vendor Management", + "activity_time": "2026-03-30T19:39:45.000Z", + "activity_date": "2026-03-30", + "createdAt": "2026-03-30T19:39:45.000Z", + "updatedAt": "2026-03-30T19:39:45.000Z" + }, + { + "id": 2, + "user_id": "usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4", + "username": "Sathira", + "activity_description": "Created vendor with ID: vnd_95018101-b539-45a4-b9a6-4551cecda990", + "activity_type": "CREATE_VENDOR", + "module": "Vendor Management", + "activity_time": "2026-03-30T19:17:58.000Z", + "activity_date": "2026-03-30", + "createdAt": "2026-03-30T19:17:58.000Z", + "updatedAt": "2026-03-30T19:17:58.000Z" + }, + { + "id": 1, + "user_id": "usr_5ff8afec-5ddc-47d9-a63f-b43df0d8c3b4", + "username": "Sathira", + "activity_description": "Created vendor with ID: vnd_c666b2cd-36fe-46db-aecd-18ec7677ccff", + "activity_type": "CREATE_VENDOR", + "module": null, + "activity_time": "2026-03-30T19:16:41.000Z", + "activity_date": "2026-03-30", + "createdAt": "2026-03-30T19:16:41.000Z", + "updatedAt": "2026-03-30T19:16:41.000Z" + } + ] +} +``` \ No newline at end of file diff --git a/Documentation/api-collection.html b/Documentation/api-collection.html new file mode 100644 index 0000000..a05d6f9 --- /dev/null +++ b/Documentation/api-collection.html @@ -0,0 +1,25 @@ + + + + + + Oceanic Titan API v1 - API Documentation + + + + + +
+ + + \ No newline at end of file diff --git a/Documentation/home.html b/Documentation/home.html new file mode 100644 index 0000000..732bb69 --- /dev/null +++ b/Documentation/home.html @@ -0,0 +1,119 @@ + + + + + Docs Home + + + + + + +
Logout
+ +
+

Documentation Portal

+ +
+
+

📘 API Documentation

+

View markdown API docs

+
+ +
+

📦 API Collections

+

Interactive API testing

+
+
+
+ + + + + \ No newline at end of file diff --git a/Documentation/index.html b/Documentation/index.html new file mode 100644 index 0000000..6bacd20 --- /dev/null +++ b/Documentation/index.html @@ -0,0 +1,134 @@ + + + + + + Docs Login + + + + + + + + + + + + + \ No newline at end of file diff --git a/Documentation/markdown.html b/Documentation/markdown.html new file mode 100644 index 0000000..4aae647 --- /dev/null +++ b/Documentation/markdown.html @@ -0,0 +1,209 @@ + + + + + + Markdown Documentation + + + + + + + + + + + + + + + + + + +
+
+

Select a file

+
+
+ + + + + + \ No newline at end of file diff --git a/app.js b/app.js new file mode 100644 index 0000000..3ef48aa --- /dev/null +++ b/app.js @@ -0,0 +1,99 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app.js + +const express = require("express"); +const cors = require("cors"); +const morgan = require("morgan"); +const routes = require("./app/routes"); +const cookieParser = require("cookie-parser"); +const { bullBoardRouter } = require("./app/config/bullBoard.config"); +const path = require("path"); +const startAllCrons = require("./cron"); + +const db = require("./app/models"); + +// Test DB connection and sync models +(async () => { + try { + await db.sequelize.authenticate(); + console.log("Database connected."); + + await db.sequelize.sync(); + console.log("Tables synced."); + + // Start all cron jobs + startAllCrons(); + + } catch (error) { + console.error("DB error:", error); + } +})(); + +const app = express(); + +app.use(cookieParser()); + +// CORS configuration +const corsOptions = { + origin: process.env.FRONTEND_URL || "https://oceanic-demo.vercel.app", + methods: ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"], + allowedHeaders: ["Content-Type", "Authorization"], + credentials: true, +}; +app.use(cors(corsOptions)); +app.options(/.*/, cors(corsOptions)); + +app.use(express.json()); +app.use(morgan("dev")); + +app.get("/health", (req, res) => { + let dbStatus = "N/A"; + let emailStatus = "N/A"; + let redisStatus = "N/A"; + + db.sequelize + .authenticate() + .then(() => { + console.log("DB connection successful."); + dbStatus = "OK"; + }) + .catch((err) => { + console.error("DB connection error:", err); + res.status(500).send("Internal Server Error"); + }); + + emailStatus = "OK"; + + redisStatus = "OK"; + + res.send({ + status: "Online ✅", + database: dbStatus, + emailService: emailStatus, + redis: redisStatus, + }); +}); + +app.use("/api", routes); +app.use( + "/Documentation", + (req, res, next) => { + if (req.path.endsWith(".md")) { + return res.status(403).send("Forbidden"); + } + + next(); + }, + express.static(path.join(__dirname, "Documentation")), +); +app.use("/admin/queues", bullBoardRouter); + +module.exports = app; diff --git a/app/config/bullBoard.config.js b/app/config/bullBoard.config.js new file mode 100644 index 0000000..647e548 --- /dev/null +++ b/app/config/bullBoard.config.js @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/config/bullBoard.config.js + +const { createBullBoard } = require("@bull-board/api"); +const { ExpressAdapter } = require("@bull-board/express"); +const { BullMQAdapter } = require("@bull-board/api/bullMQAdapter"); + +const activityQueue = require("../queues/activity.queue"); + +const serverAdapter = new ExpressAdapter(); +serverAdapter.setBasePath("/admin/queues"); + +const { addQueue, removeQueue, setQueues, replaceQueues } = + createBullBoard({ + queues: [new BullMQAdapter(activityQueue)], + serverAdapter, + }); + +module.exports = { + bullBoardRouter: serverAdapter.getRouter(), +}; \ No newline at end of file diff --git a/app/config/db.config.js b/app/config/db.config.js new file mode 100644 index 0000000..91ad3cc --- /dev/null +++ b/app/config/db.config.js @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/config/db.config.js + +require("dotenv").config(); + +module.exports = { + HOST: process.env.DB_HOST || "localhost", + USER: process.env.DB_USER || "root", + PASSWORD: process.env.DB_PASSWORD || "", + DB: process.env.DB_NAME || "oceanic-db", + PORT: process.env.DB_PORT || 3306, + DIALECT: "mysql", + + pool: { + max: 5, + min: 0, + acquire: 30000, + idle: 10000 + } +}; diff --git a/app/config/mail.config.js b/app/config/mail.config.js new file mode 100644 index 0000000..d96e447 --- /dev/null +++ b/app/config/mail.config.js @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/config/mail.config.js + + +require("dotenv").config(); + +module.exports = { + host: process.env.MAIL_HOST, + port: process.env.MAIL_PORT, + secure: process.env.MAIL_SECURE === "true", // true for 465, false for 587 + auth: { + user: process.env.MAIL_USER, + pass: process.env.MAIL_PASS, + }, + from: `Oceanic Maritime Solutions <${process.env.MAIL_FROM}>`, +}; diff --git a/app/config/redis.config.js b/app/config/redis.config.js new file mode 100644 index 0000000..4016a56 --- /dev/null +++ b/app/config/redis.config.js @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/config/redis.config.js + +const { Redis } = require("ioredis"); + +const createRedisConnection = () => { + const redis = new Redis({ + host: process.env.REDIS_HOST || "redis", + port: process.env.REDIS_PORT || 6379, + password: process.env.REDIS_PASSWORD, + maxRetriesPerRequest: null, + enableReadyCheck: false, + }); + + // Connection events + redis.on("connect", () => { + console.log("Redis connecting..."); + }); + + redis.on("ready", () => { + console.log("Redis connected and ready!"); + }); + + redis.on("error", (err) => { + console.error("Redis error:", err.message); + }); + + redis.on("close", () => { + console.warn("Redis connection closed"); + }); + + redis.on("reconnecting", () => { + console.log("Redis reconnecting..."); + }); + + return redis; +}; + +module.exports = createRedisConnection; diff --git a/app/config/redisClient.js b/app/config/redisClient.js new file mode 100644 index 0000000..d6a5355 --- /dev/null +++ b/app/config/redisClient.js @@ -0,0 +1,17 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + + +// app/config/redisClient.js + +const createRedisConnection = require("./redis.config"); + +const redis = createRedisConnection(); + +module.exports = redis; \ No newline at end of file diff --git a/app/config/s3.config.js b/app/config/s3.config.js new file mode 100644 index 0000000..e49da4b --- /dev/null +++ b/app/config/s3.config.js @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/config/s3.config.js + +const { S3Client } = require("@aws-sdk/client-s3"); + +const s3 = new S3Client({ + region: process.env.AWS_REGION, + credentials: { + accessKeyId: process.env.AWS_ACCESS_KEY_ID, + secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, + }, +}); + +module.exports = s3; \ No newline at end of file diff --git a/app/constants/permissions.js b/app/constants/permissions.js new file mode 100644 index 0000000..9879db7 --- /dev/null +++ b/app/constants/permissions.js @@ -0,0 +1,16 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + + +// app/constants/permissions.js + +module.exports = { + FINANCE_BASIS: "finance.basis", + PRECOST: "precost.precost", +}; \ No newline at end of file diff --git a/app/controllers/activity.controller.js b/app/controllers/activity.controller.js new file mode 100644 index 0000000..e4cfbf8 --- /dev/null +++ b/app/controllers/activity.controller.js @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/controllers/activity.controller.js + +const db = require("../models"); +const Activity = db.UserActivity; + +// Get all user activities +exports.getUserActivities = async (req, res) => { + try { + const avt = await Activity.findAll({ + order: [["createdAt", "DESC"]], + }); + + res.status(200).json({ + success: true, + message: "User activities retrieved successfully", + data: avt, + }); + } catch (error) { + res.status(500).json({ + success: false, + message: "An error occurred while fetching user activities.", + }); + } +}; + +// Get activity by User ID +exports.getActivitiesByUserId = async (req, res) => { + const userId = req.params.userId; + + try { + const avt = await Activity.findAll({ + where: { user_id: userId }, + order: [["createdAt", "DESC"]], + }); + + res.status(200).json({ + success: true, + message: "User activities retrieved successfully", + data: avt, + }); + } catch (error) { + res.status(500).json({ + success: false, + message: "An error occurred while fetching user activities.", + }); + } +}; diff --git a/app/controllers/auth.controller.js b/app/controllers/auth.controller.js new file mode 100644 index 0000000..da10770 --- /dev/null +++ b/app/controllers/auth.controller.js @@ -0,0 +1,132 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/controllers/auth.controller.js + +const { checkPassword } = require("../utils/hashPassword.util"); +const { sendMail } = require("../utils/mail.util"); +const { generateOTP, validateOTP } = require("../utils/otp.util"); +const {getCachedUser,clearUserCache} = require("../utils/cache.util"); +const { generateToken } = require("../utils/jwt.util"); +const { log } = require("../utils/consoleLog.utill"); + +const appName = process.env.APP_NAME || "Niolla"; + +// Login Step 1: Request OTP +exports.loginReq = async (req, res) => { + try { + const { email, password } = req.body; + + const user = await getCachedUser(email); + if (!user) { + return res + .status(404) + .send({ success: false, message: "User Not Found" }); + } + + const passwordIsValid = await checkPassword(password, user.password); + if (!passwordIsValid) { + return res + .status(401) + .send({ success: false, message: "Invalid Password" }); + } + + const otp = generateOTP(email); + + await sendMail({ + to: email, + subject: `OTP for Your ${appName} Account`, + templateName: "otp", + templateVars: { + firstName: user.firstName, + otp: otp, + }, + text: `Hello ${user.firstName}, your otp is ${otp}`, + }); + + log(`OTP for ${email}: ${otp}`); + log(`OTP sent to ${email} successfully.`); + + res.status(201).send({ success: true, message: "OTP Sent Successfully" }); + } catch (error) { + log("Error occurred while sending OTP:", error); + res.status(500).send({ success: false, message: error.message }); + } +}; + +// Login Step 2: Verify OTP and issue JWT +exports.login = async (req, res) => { + try { + const { email, otp } = req.body; + + const user = await getCachedUser(email); + + if (!user) { + return res + .status(404) + .send({ success: false, message: "User Not Found" }); + } + + const isValidOTP = validateOTP(email, otp); + + if (!isValidOTP) { + return res + .status(401) + .send({ success: false, message: "Invalid or Expired OTP" }); + } + + // Generate JWT token + const token = generateToken({ + id: user.id, + firstName: user.firstName, + lastName: user.lastName, + email: user.email, + role: user.role, + accountType: user.accountType, + }); + + // 3. Set JWT as HttpOnly cookie + res.cookie("access_token", token, { + httpOnly: true, // JS cannot access + secure: process.env.NODE_ENV === "production", // HTTPS only in prod + sameSite: process.env.NODE_ENV === "production" ? "None" : "Lax", + maxAge: 24 * 60 * 60 * 1000, // 1 day + }); + + log(`JWT issued for ${email}`); + clearUserCache(email); + + res.status(200).send({ + success: true, + message: "Login Successful", + data: { + id: user.id, + email: user.email, + firstName: user.firstName, + lastName: user.lastName, + role: user.role, + accountType: user.accountType, + }, + }); + } catch (error) { + log("Error occurred during login:", error); + res.status(500).send({ success: false, message: error.message }); + } +}; + +// Logout: Clear the JWT cookie +exports.logout = (req, res) => { + res.clearCookie("access_token", { + httpOnly: true, + secure: process.env.NODE_ENV === "production", + sameSite: process.env.NODE_ENV === "production" ? "None" : "Lax", + }); + + res.json({ success: true, message: "Logged out successfully" }); +}; diff --git a/app/controllers/generateDocument.controller.js b/app/controllers/generateDocument.controller.js new file mode 100644 index 0000000..4f69daf --- /dev/null +++ b/app/controllers/generateDocument.controller.js @@ -0,0 +1,417 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/controllers/generateDocument.controller.js + +const fs = require("fs"); +const path = require("path"); +const documentQueue = require("../queues/document.queue"); +const { createDocumentData } = require("../utils/document.utill"); +const { + generateId, + generateDocumentReferenceNo, +} = require("../utils/idGen.util"); + +const { GetObjectCommand, DeleteObjectCommand } = require("@aws-sdk/client-s3"); + +const {log} = require("../utils/consoleLog.utill"); + +const s3 = require("../config/s3.config"); +const db = require("../models"); +const Document = db.Document; +const DocumentType = db.DocumentType; + +const normalizeDocumentData = (value) => { + if (!value) { + return {}; + } + + if (typeof value === "string") { + try { + return JSON.parse(value); + } catch (error) { + return {}; + } + } + + return value; +}; + +/** + * Get available document types + */ +exports.getAvailableDocumentTypes = async (req, res) => { + try { + + const doc_types = await DocumentType.findAll({ + attributes: ["doc_type_name"], + }); + + const types = doc_types.map((t) => t.doc_type_name); + + return res.json({ + success: true, + availableDocumentTypes: types, + note: "Use these document type names in your requests (case-insensitive)", + }); + + } catch (error) { + log("Error fetching document types:", error.message); + return res.status(500).json({ + success: false, + message: error.message, + }); + } +}; + +// Get Saved documents +exports.getSavedDocuments = async (req, res) => { + try { + const { documentType } = req.body; + + if (!documentType) { + return res.status(400).json({ + success: false, + message: "documentType query parameter is required", + }); + } + + // Fetch saved documents based on documentType + const savedDocuments = await Document.findAll({ + where: { doc_type: documentType.toUpperCase() }, + order: [["createdAt", "DESC"]], + exclude: ["data"], + }); + + // extract only necessary fields to return + const formattedDocuments = savedDocuments.map((doc) => ({ + doc_id: doc.doc_id, + reference_no: doc.reference_no, + doc_type: doc.doc_type, + status: doc.status, + createdAt: doc.createdAt, + updatedAt: doc.updatedAt, + })); + + return res.json({ + success: true, + savedDocuments: formattedDocuments, + }); + } catch (error) { + log("Error fetching saved documents:", error.message); + return res.status(500).json({ + success: false, + message: error.message, + }); + } +}; + +// Get specific document data by doc_id +exports.getDocumentData = async (req, res) => { + try { + const { docId } = req.params; + + if (!docId) { + return res.status(400).json({ + success: false, + message: "docId parameter is required", + }); + } + + const document = await Document.findOne({ + where: { doc_id: docId }, + }); + + if (!document) { + return res.status(404).json({ + success: false, + message: "Document not found", + }); + } + + return res.json({ + success: true, + document: { + doc_id: document.doc_id, + reference_no: document.reference_no, + doc_type: document.doc_type, + data: document.data, + status: document.status, + createdAt: document.createdAt, + updatedAt: document.updatedAt, + }, + }); + + } catch (error) { + log("Error fetching document data:", error.message); + return res.status(500).json({ + success: false, + message: error.message, + }); + } +} + +exports.generateReferenceNo = async (req, res) => { + try { + const { documentType } = req.params; + + if (!documentType) { + return res.status(400).json({ + success: false, + message: "documentType is required", + }); + } + + // Generate reference number + const reference_no = await generateDocumentReferenceNo(documentType); + + return res.json({ + success: true, + reference_no, + }); + } catch (error) { + log("Error generating reference number:", error.message); + return res.status(500).json({ + success: false, + message: error.message, + }); + } +}; + +exports.generateDraftDocument = async (req, res) => { + try { + const { document } = req.body; + const documentData = normalizeDocumentData(req.body.documentData || req.body.data || req.body); + + // Validation + if (!document || !documentData) { + return res.status(400).json({ + success: false, + message: "document and documentData are required", + }); + } + + let documentDetails; + + if (document !== "PRECOST") { + // Save document details before generating + documentDetails = await createDocumentData( + document, + documentData, + "DRAFT", + ); + } else { + return res.status(400).json({ + success: false, + message: + "Invalid document type, This document type is not allowed to be generated as draft", + }); + } + + return res.status(201).json({ + success: true, + message: "Draft document created successfully", + documentDetails, + }); + } catch (error) { + log("Error generating draft document:", error.message); + return res.status(500).json({ + success: false, + message: error.message, + }); + } +}; + +/** + * Generate document asynchronously + * Returns jobId immediately + */ +exports.generateDocument = async (req, res) => { + try { + const { document, documentType } = req.body; + const documentData = normalizeDocumentData(req.body.documentData || req.body.data || req.body); + + // Validation + if (!document || !documentType || !documentData) { + return res.status(400).json({ + success: false, + message: "document, documentType, and documentData are required", + }); + } + + console.log( + `📨 generateDocument request: document="${document}", documentType="${documentType}"`, + ); + + if (document !== "PRECOST") { + // Save document details before generating + // If doc_id exists, finalize (update existing); otherwise create as DRAFT + const status = documentData.doc_id ? "FINAL" : "DRAFT"; + await createDocumentData(document, documentData, status); + } + + // Add job to queue + const job = await documentQueue.add("generate-document", { + document, + documentType, + data: documentData, + }); + + log( + `📋 Document generation job queued: ${job.id} (document="${document}", type="${documentType}")`, + ); + + return res.status(202).json({ + success: true, + message: "Document generation started", + jobId: job.id, + }); + } catch (error) { + log("Error queuing document generation:", error.message); + return res.status(500).json({ + success: false, + message: error.message, + }); + } +}; + +/** + * Get job status and result + */ +exports.getJobStatus = async (req, res) => { + try { + const { jobId } = req.params; + + if (!jobId) { + return res.status(400).json({ + success: false, + message: "jobId is required", + }); + } + + // Get job from queue + const job = await documentQueue.getJob(jobId); + + if (!job) { + return res.status(404).json({ + success: false, + message: "Job not found", + }); + } + + // Get job state + const state = await job.getState(); + const result = job.returnvalue; + const failedReason = job.failedReason; + + return res.json({ + success: true, + jobId: job.id, + state, // "waiting" | "active" | "completed" | "failed" | "delayed" + result: state === "completed" ? result : null, + error: state === "failed" ? failedReason : null, + attempts: job.attemptsMade, + stacktrace: job.stacktrace, + }); + } catch (error) { + log("Error fetching job status:", error.message); + return res.status(500).json({ + success: false, + message: error.message, + }); + } +}; + +/** + * Download generated document + */ +exports.downloadDocument = async (req, res) => { + try { + const { uuid } = req.params; + + const key = `uploads/${uuid}.pdf`; + + const command = new GetObjectCommand({ + Bucket: process.env.AWS_S3_BUCKET_NAME, + Key: key, + }); + + const response = await s3.send(command); + + res.setHeader( + "Content-Type", + response.ContentType || "application/octet-stream", + ); + + res.setHeader("Content-Disposition", `attachment; filename="${uuid}.pdf"`); + + response.Body.pipe(res); + + res.on("finish", async () => { + try { + await s3.send( + new DeleteObjectCommand({ + Bucket: process.env.AWS_S3_BUCKET_NAME, + Key: key, + }), + ); + + log(`Deleted from S3: ${key}`); + } catch (err) { + log(`Failed to delete ${key}:`, err.message); + } + }); + } catch (error) { + log("Download error:", error.message); + + return res.status(404).json({ + success: false, + message: "Document not found", + }); + } +}; + +/** + * Cancel/delete a job + */ +exports.cancelJob = async (req, res) => { + try { + const { jobId } = req.params; + + if (!jobId) { + return res.status(400).json({ + success: false, + message: "jobId is required", + }); + } + + const job = await documentQueue.getJob(jobId); + + if (!job) { + return res.status(404).json({ + success: false, + message: "Job not found", + }); + } + + await job.remove(); + + return res.json({ + success: true, + message: "Job cancelled successfully", + jobId, + }); + } catch (error) { + log("Error cancelling job:", error.message); + return res.status(500).json({ + success: false, + message: error.message, + }); + } +}; diff --git a/app/controllers/notification.controller.js b/app/controllers/notification.controller.js new file mode 100644 index 0000000..61f0972 --- /dev/null +++ b/app/controllers/notification.controller.js @@ -0,0 +1,138 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/controllers/notification.controller.js + +const db = require("../models"); +const Notification = db.notification; +const UserNotification = db.userNotification; + +const log = require("../utils/consoleLog.utill").log; + +// Controller for managing notifications +exports.createNotification = async (req, res) => { + try { + const { notificationHeadline, notificationDescription, notificationType } = + req.body; + + const id = Date.now().toString(); // Generate a unique ID based on the current timestamp + const notification_id = `notif_${id}`; // Prefix the ID with "notif_" + + // Create a new notification + const newNotification = await Notification.create({ + notification_id, + notificationHeadline, + notificationDescription, + notificationType, + dateCreated: new Date(), + }); + + res.status(201).json({ + success: true, + message: "Notification created successfully", + notification: newNotification, + }); + } catch (error) { + log("Error creating notification:", error.message); + res.status(500).json({ + success: false, + message: "Internal server error", + error: error.message + }); + } +}; + +// Mark a notification as read for a user +exports.markAsRead = async (req, res) => { + try { + const { userId, notificationId } = req.params; + + // Find the user notification entry + const userNotification = await UserNotification.findOne({ + where: { user_id: userId, notification_id: notificationId }, + }); + + if (!userNotification) { + return res.status(404).json({ success: false, error: "User notification not found" }); + } + + // Mark the notification as read + userNotification.isRead = true; + await userNotification.save(); + res.status(200).json({ + success: true, + message: "Notification marked as read", + }); + } + + catch (error) { + log("Error marking notification as read:", error.message); + res.status(500).json({ + success: false, + message: "Internal server error", + error: error.message + }); + } +}; + +// Get announcements for all users +exports.getAnnouncements = async (req, res) => { + try { + + // Fetch all announcements + const announcements = await Notification.findAll({ + where: { notificationType: "ANNOUNCEMENT", isActive: true }, + }); + + res.status(200).json({ + success: true, + announcements, + }); + + } catch (error) { + log("Error fetching announcements:", error.message); + res.status(500).json({ + success: false, + message: "Internal server error", + error: error.message + }); + } +} + +// Get all notifications for a user +exports.getUserNotifications = async (req, res) => { + try { + const { userId } = req.params; + + // Fetch notifications for the user + const notifications = await UserNotification.findAll({ + where: { user_id: userId, isRead: false }, + include: [ + { + model: Notification, + as: "notification", + }, + ], + }); + + res.status(200).json({ + success: true, + notifications, + }); + } + + catch (error) { + log("Error fetching user notifications:", error.message); + res.status(500).json({ + success: false, + message: "Internal server error", + error: error.message + }); + } +}; \ No newline at end of file diff --git a/app/controllers/permission.controller.js b/app/controllers/permission.controller.js new file mode 100644 index 0000000..dd81817 --- /dev/null +++ b/app/controllers/permission.controller.js @@ -0,0 +1,868 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/controllers/permission.controller.js + + +// PERMISSIONS: +// 1. createPermission --> Done +// 2. getAllPermissions --> Done +// 3. getPermissionById --> Done +// 4. updatePermission --> Done +// 5. deletePermission --> Done + +// ROLES: +// 1. createRole --> Done +// 2. createRolePermission --> Done +// 3. getAllRoles --> Done +// 4. getRoleById --> Done +// 5. updateRole --> Done +// 6. deleteRole --> Done + +// USER PERMISSIONS: +// 1. getUserPermissions --> Done + +const db = require("../models"); +const User = db.User; +const Role = db.roles; +const Permission = db.permission; +const RolePermission = db.rolePermission; +const UserPermission = db.userPermission; + +// Create a new permission +exports.createPermission = async (req, res) => { + try { + const { permissionName, permissionDescription, page, module, action } = req.body; + if (!permissionName || !page || !module || !action) { + return res.status(400).json({ message: "Permission name, page, module, and action are required" }); + } + + const permissionId = `${module}.${page}.${action}`; + + // Check if permission already exists + const existingPermission = await Permission.findOne({ where: { permission_id: permissionId } }); + if (existingPermission) { + return res.status(400).json({ message: "Permission already exists" }); + } + + // Create the new permission + const newPermission = await Permission.create({ + permission_id: permissionId, + permissionName, + permissionDescription, + page, + module, + action + }); + + res.status(201).json({ + success: true, + data: newPermission + }); + + } catch (error) { + console.error("Error creating permission:", error); + res.status(500).json({ message: "Internal server error" }); + } +}; + +// Bulk create permissions +exports.bulkCreatePermissions = async (req, res) => { + try { + const { permissions } = req.body; + + if (!permissions || !Array.isArray(permissions) || permissions.length === 0) { + return res.status(400).json({ + success: false, + message: "Permissions array is required and must not be empty" + }); + } + + const results = { + created: [], + failed: [] + }; + + for (const item of permissions) { + try { + const { permissionName, permissionDescription, page, module, action } = item; + + if (!permissionName || !page || !module || !action) { + results.failed.push({ + ...item, + error: "Permission name, page, module, and action are required" + }); + continue; + } + + const permissionId = `${module}.${page}.${action}`; + + // Check if permission already exists + const existingPermission = await Permission.findOne({ where: { permission_id: permissionId } }); + if (existingPermission) { + results.failed.push({ + ...item, + error: "Permission already exists" + }); + continue; + } + + const newPermission = await Permission.create({ + permission_id: permissionId, + permissionName, + permissionDescription, + page, + module, + action + }); + + results.created.push(newPermission); + + } catch (itemError) { + results.failed.push({ + ...item, + error: itemError.message + }); + } + } + + res.status(201).json({ + success: results.failed.length === 0, + data: results + }); + + } catch (error) { + console.error("Error bulk creating permissions:", error); + res.status(500).json({ + success: false, + message: "An error occurred while bulk creating permissions." + }); + } +}; + +// Get all permissions +exports.getAllPermissions = async (req, res) => { + try { + const permissions = await Permission.findAll(); + + res.status(200).json({ + success: true, + data: permissions + }); + + } catch (error) { + console.error("Error fetching permissions:", error); + res.status(500).json({ + success: false, + message: "An error occurred while fetching permissions." + }); + } +}; + +// Get permission by ID +exports.getPermissionById = async (req, res) => { + try { + const { permissionId } = req.params; + + const permission = await Permission.findByPk(permissionId); + if (!permission) { + return res.status(404).json({ + success: false, + message: "Permission not found" + }); + } + + res.status(200).json({ + success: true, + data: permission + }); + + } catch (error) { + console.error("Error fetching permission:", error); + res.status(500).json({ + success: false, + message: "An error occurred while fetching permission." + }); + } +}; + +// Update permission +exports.updatePermission = async (req, res) => { + try { + const { permissionId } = req.params; + const { permissionName, permissionDescription, page, module, action } = req.body; + + const permission = await Permission.findByPk(permissionId); + if (!permission) { + return res.status(404).json({ + success: false, + message: "Permission not found" + }); + } + + // Update permission fields + await permission.update({ + permissionName: permissionName || permission.permissionName, + permissionDescription: permissionDescription || permission.permissionDescription, + page: page || permission.page, + module: module || permission.module, + action: action || permission.action + }); + + res.status(200).json({ + success: true, + data: permission, + message: "Permission updated successfully" + }); + + } catch (error) { + console.error("Error updating permission:", error); + res.status(500).json({ + success: false, + message: "An error occurred while updating permission." + }); + } +}; + +// Delete permission +exports.deletePermission = async (req, res) => { + try { + const { permissionId } = req.params; + + const permission = await Permission.findByPk(permissionId); + if (!permission) { + return res.status(404).json({ + success: false, + message: "Permission not found" + }); + } + + // Delete associated user and role permissions first + await UserPermission.destroy({ where: { permission_id: permissionId } }); + await RolePermission.destroy({ where: { permission_id: permissionId } }); + + // Delete the permission + await permission.destroy(); + + res.status(200).json({ + success: true, + message: "Permission deleted successfully" + }); + + } catch (error) { + console.error("Error deleting permission:", error); + res.status(500).json({ + success: false, + message: "An error occurred while deleting permission." + }); + } +}; + +// Create a new role +exports.createRole = async (req, res) => { + try { + const { roleName, roleDescription } = req.body; + + if (!roleName) { + return res.status(400).json({ + success: false, + message: "Role name is required" + }); + } + + // Generate role ID + const roleId = `role_${Date.now()}`; + + const newRole = await Role.create({ + role_id: roleId, + roleName, + roleDescription + }); + + res.status(201).json({ + success: true, + data: newRole + }); + + } catch (error) { + console.error("Error creating role:", error); + res.status(500).json({ + success: false, + message: "An error occurred while creating role." + }); + } +}; + +// Create role-permission assignment +exports.createRolePermission = async (req, res) => { + try { + const { role_id, permission_id } = req.body; + + if (!role_id || !permission_id) { + return res.status(400).json({ + success: false, + message: "Role ID and Permission ID are required" + }); + } + + // Verify role and permission exist + const role = await Role.findByPk(role_id); + if (!role) { + return res.status(404).json({ + success: false, + message: "Role not found" + }); + } + + const permission = await Permission.findByPk(permission_id); + if (!permission) { + return res.status(404).json({ + success: false, + message: "Permission not found" + }); + } + + // Check if assignment already exists + const existingAssignment = await RolePermission.findOne({ + where: { role_id, permission_id } + }); + if (existingAssignment) { + return res.status(400).json({ + success: false, + message: "Role permission already exists" + }); + } + + const rpId = `rp_${Date.now()}`; + const newRolePermission = await RolePermission.create({ + rp_id: rpId, + role_id, + permission_id + }); + + res.status(201).json({ + success: true, + data: newRolePermission + }); + + } catch (error) { + console.error("Error creating role permission:", error); + res.status(500).json({ + success: false, + message: "An error occurred while creating role permission." + }); + } +}; + +// Bulk create role permissions +exports.bulkCreateRolePermissions = async (req, res) => { + try { + const { role_id, permission_ids } = req.body; + + if (!role_id || !permission_ids || !Array.isArray(permission_ids) || permission_ids.length === 0) { + return res.status(400).json({ + success: false, + message: "Role ID and permission_ids array are required and must not be empty" + }); + } + + // Verify role exists + const role = await Role.findByPk(role_id); + if (!role) { + return res.status(404).json({ + success: false, + message: "Role not found" + }); + } + + const results = { + created: [], + failed: [] + }; + + for (const permission_id of permission_ids) { + try { + if (!permission_id) { + results.failed.push({ + role_id, + permission_id, + error: "Permission ID is required" + }); + continue; + } + + // Verify permission exists + const permission = await Permission.findByPk(permission_id); + if (!permission) { + results.failed.push({ + role_id, + permission_id, + error: "Permission not found" + }); + continue; + } + + // Check if assignment already exists + const existingAssignment = await RolePermission.findOne({ + where: { role_id, permission_id } + }); + if (existingAssignment) { + results.failed.push({ + role_id, + permission_id, + error: "Role permission already exists" + }); + continue; + } + + const rpId = `rp_${Date.now()}`; + const newRolePermission = await RolePermission.create({ + rp_id: rpId, + role_id, + permission_id + }); + + results.created.push(newRolePermission); + + } catch (itemError) { + results.failed.push({ + role_id, + permission_id, + error: itemError.message + }); + } + } + + res.status(201).json({ + success: results.failed.length === 0, + data: results + }); + + } catch (error) { + console.error("Error bulk creating role permissions:", error); + res.status(500).json({ + success: false, + message: "An error occurred while bulk creating role permissions." + }); + } +}; + +// Get all roles +exports.getAllRoles = async (req, res) => { + try { + const roles = await Role.findAll({ + include: [ + { + model: RolePermission, + as: "rolePermissions", + include: [ + { + model: Permission, + as: "permission", + attributes: ["permission_id", "permissionName", "page", "module", "action"] + } + ] + } + ] + }); + + res.status(200).json({ + success: true, + data: roles + }); + + } catch (error) { + console.error("Error fetching roles:", error); + res.status(500).json({ + success: false, + message: "An error occurred while fetching roles." + }); + } +}; + +// Get role by ID +exports.getRoleById = async (req, res) => { + try { + const { roleId } = req.params; + + const role = await Role.findByPk(roleId, { + include: [ + { + model: RolePermission, + as: "rolePermissions", + include: [ + { + model: Permission, + as: "permission", + attributes: ["permission_id", "permissionName", "page", "module", "action"] + } + ] + } + ] + }); + + if (!role) { + return res.status(404).json({ + success: false, + message: "Role not found" + }); + } + + res.status(200).json({ + success: true, + data: role + }); + + } catch (error) { + console.error("Error fetching role:", error); + res.status(500).json({ + success: false, + message: "An error occurred while fetching role." + }); + } +}; + +// Update role +exports.updateRole = async (req, res) => { + try { + const { roleId } = req.params; + const { roleName, roleDescription } = req.body; + + const role = await Role.findByPk(roleId); + if (!role) { + return res.status(404).json({ + success: false, + message: "Role not found" + }); + } + + // Update role fields + await role.update({ + roleName: roleName || role.roleName, + roleDescription: roleDescription || role.roleDescription + }); + + res.status(200).json({ + success: true, + data: role, + message: "Role updated successfully" + }); + + } catch (error) { + console.error("Error updating role:", error); + res.status(500).json({ + success: false, + message: "An error occurred while updating role." + }); + } +}; + +// Delete role +exports.deleteRole = async (req, res) => { + try { + const { roleId } = req.params; + + const role = await Role.findByPk(roleId); + if (!role) { + return res.status(404).json({ + success: false, + message: "Role not found" + }); + } + + // Delete associated role permissions first + await RolePermission.destroy({ where: { role_id: roleId } }); + + // Delete the role + await role.destroy(); + + res.status(200).json({ + success: true, + message: "Role deleted successfully" + }); + + } catch (error) { + console.error("Error deleting role:", error); + res.status(500).json({ + success: false, + message: "An error occurred while deleting role." + }); + } +}; + +// Get user permissions +exports.getUserPermissions = async (req, res) => { + try { + const userId = req.params.userId; + + // Fetch user permissions + const userPermissions = await UserPermission.findAll({ + where: { user_id: userId }, + include: [ + { + model: Permission, + as: "permission", + attributes: ["permission_id", "permissionName", "page", "module", "action"] + } + ] + }); + + res.status(200).json({ + success: true, + data: userPermissions + }); + + } catch (error) { + console.error("Error fetching user permissions:", error); + res.status(500).json({ + success: false, + message: "An error occurred while fetching user permissions." + }); + } +}; + +// Create user permission +exports.createUserPermission = async (req, res) => { + try { + const { user_id, permission_id } = req.body; + + if (!user_id || !permission_id) { + return res.status(400).json({ + success: false, + message: "User ID and Permission ID are required" + }); + } + + // Verify user and permission exist + const user = await User.findByPk(user_id); + if (!user) { + return res.status(404).json({ + success: false, + message: "User not found" + }); + } + + const permission = await Permission.findByPk(permission_id); + if (!permission) { + return res.status(404).json({ + success: false, + message: "Permission not found" + }); + } + + // Check if assignment already exists + const existingAssignment = await UserPermission.findOne({ + where: { user_id, permission_id } + }); + if (existingAssignment) { + return res.status(400).json({ + success: false, + message: "User permission already exists" + }); + } + + const newUserPermission = await UserPermission.create({ + user_id, + permission_id + }); + + res.status(201).json({ + success: true, + data: newUserPermission + }); + + } catch (error) { + console.error("Error creating user permission:", error); + res.status(500).json({ + success: false, + message: "An error occurred while creating user permission." + }); + } +}; + +// Bulk create user permissions +exports.bulkCreateUserPermissions = async (req, res) => { + try { + const { user_id, permission_ids } = req.body; + + if (!user_id || !permission_ids || !Array.isArray(permission_ids) || permission_ids.length === 0) { + return res.status(400).json({ + success: false, + message: "User ID and permission_ids array are required and must not be empty" + }); + } + + // Verify user exists + const user = await User.findByPk(user_id); + if (!user) { + return res.status(404).json({ + success: false, + message: "User not found" + }); + } + + const results = { + created: [], + failed: [] + }; + + for (const permission_id of permission_ids) { + try { + if (!permission_id) { + results.failed.push({ + user_id, + permission_id, + error: "Permission ID is required" + }); + continue; + } + + // Verify permission exists + const permission = await Permission.findByPk(permission_id); + if (!permission) { + results.failed.push({ + user_id, + permission_id, + error: "Permission not found" + }); + continue; + } + + // Check if assignment already exists + const existingAssignment = await UserPermission.findOne({ + where: { user_id, permission_id } + }); + if (existingAssignment) { + results.failed.push({ + user_id, + permission_id, + error: "User permission already exists" + }); + continue; + } + + const newUserPermission = await UserPermission.create({ + user_id, + permission_id + }); + + results.created.push(newUserPermission); + + } catch (itemError) { + results.failed.push({ + user_id, + permission_id, + error: itemError.message + }); + } + } + + res.status(201).json({ + success: results.failed.length === 0, + data: results + }); + + } catch (error) { + console.error("Error bulk creating user permissions:", error); + res.status(500).json({ + success: false, + message: "An error occurred while bulk creating user permissions." + }); + } +}; + +// Update user permission +exports.updateUserPermission = async (req, res) => { + try { + const { upId } = req.params; + const { permission_id } = req.body; + + if (!permission_id) { + return res.status(400).json({ + success: false, + message: "Permission ID is required" + }); + } + + const userPermission = await UserPermission.findByPk(upId); + if (!userPermission) { + return res.status(404).json({ + success: false, + message: "User permission not found" + }); + } + + // Verify permission exists + const permission = await Permission.findByPk(permission_id); + if (!permission) { + return res.status(404).json({ + success: false, + message: "Permission not found" + }); + } + + // Check if new permission assignment already exists for this user + const existingAssignment = await UserPermission.findOne({ + where: { + user_id: userPermission.user_id, + permission_id + } + }); + if (existingAssignment && existingAssignment.up_id !== upId) { + return res.status(400).json({ + success: false, + message: "This permission is already assigned to this user" + }); + } + + await userPermission.update({ permission_id }); + + res.status(200).json({ + success: true, + data: userPermission, + message: "User permission updated successfully" + }); + + } catch (error) { + console.error("Error updating user permission:", error); + res.status(500).json({ + success: false, + message: "An error occurred while updating user permission." + }); + } +}; + +// Delete user permission +exports.deleteUserPermission = async (req, res) => { + try { + const { upId } = req.params; + + const userPermission = await UserPermission.findByPk(upId); + if (!userPermission) { + return res.status(404).json({ + success: false, + message: "User permission not found" + }); + } + + await userPermission.destroy(); + + res.status(200).json({ + success: true, + message: "User permission deleted successfully" + }); + + } catch (error) { + console.error("Error deleting user permission:", error); + res.status(500).json({ + success: false, + message: "An error occurred while deleting user permission." + }); + } +}; diff --git a/app/controllers/profile.controller.js b/app/controllers/profile.controller.js new file mode 100644 index 0000000..b00457a --- /dev/null +++ b/app/controllers/profile.controller.js @@ -0,0 +1,219 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/controllers/profile.controller.js + +const db = require("../models"); +const Profile = db.Profile; +const Upload = db.Upload; +const User = db.User; + +const { generateTodoId } = require("../utils/idGen.util"); +const { getSignedFileUrl } = require("../utils/s3Upload.utill"); +const { + generateResetToken, + resetPassword, +} = require("../utils/passwordReset.utill"); +const { sendMail } = require("../utils/mail.util"); +const { logActivity } = require("../services/activity.service"); +const { hashPassword, checkPassword } = require("../utils/hashPassword.util"); +const { log } = require("../utils/consoleLog.utill"); + +// Get Profile avatar by user ID +exports.getProfileAvatar = async (req, res) => { + try { + const userId = req.params.userId; + const profile = await Profile.findOne({ where: { user_id: userId } }); + if (!profile) { + return res.status(404).json({ error: "Profile not found" }); + } + + const uploadRecord = await Upload.findByPk(profile.profilePicture_id); + + const fileUrl = await getSignedFileUrl(uploadRecord.file_path); + + res.json({ + success: true, + data: { + userId: profile.userId, + profilePictureUrl: fileUrl, + }, + }); + } catch (error) { + console.error("Error fetching profile:", error); + res.status(500).json({ + success: false, + message: "Internal server error", + error: error.message, + }); + } +}; + +// Get profile background image by user ID +exports.getProfileBackgroundImage = async (req, res) => { + try { + const userId = req.params.userId; + const profile = await Profile.findOne({ where: { user_id: userId } }); + if (!profile) { + return res + .status(404) + .json({ success: false, message: "Profile not found" }); + } + + const uploadRecord = await Upload.findByPk(profile.backgroundImage_id); + + const fileUrl = await getSignedFileUrl(uploadRecord.file_path); + + res.json({ + success: true, + data: { + userId: profile.userId, + backgroundImageUrl: fileUrl, + }, + }); + } catch (error) { + console.error("Error fetching profile background image:", error); + res.status(500).json({ + success: false, + message: "Internal server error", + error: error.message, + }); + } +}; + +// Request password reset token +exports.requestPasswordReset = async (req, res) => { + try { + const { email } = req.body; + + const user = await User.findOne({ + where: { email }, + }); + + if (user) { + const token = await generateResetToken(user.id); + const resetLink = `${process.env.FRONTEND_URL}/reset-password?token=${token}&email=${encodeURIComponent(email)}`; + + await sendMail({ + to: user.email, + subject: "Password Reset", + templateName: "passwordReset", + templateVars: { + firstName: user.firstName, + resetLink, + expiryTime: process.env.PASSWORD_RESET_EXPIRY_TIME || "10 minutes", + }, + }); + + const activityUser = { + id: 1, + firstName: "System", + }; + + await logActivity({ + user: activityUser, + description: "Requested password reset for email: " + email, + type: "PASSWORD_RESET_REQUEST", + module: "Authentication", + }); + } + + return res.json({ + success: true, + message: + "If an account exists with this email address, a password reset email has been sent.", + }); + } catch (error) { + console.error(error); + + return res.status(500).json({ + success: false, + message: "Internal server error", + }); + } +}; + +// Reset password using token +exports.resetPassword = async (req, res) => { + try { + const { email, token, newPassword } = req.body; + + // Validate email + const user = await User.findOne({ + where: { email }, + }); + + if (!user) { + return res.status(400).json({ + success: false, + message: "Invalid email address.", + }); + } + + await resetPassword(user.id, token, newPassword); + + return res.json({ + success: true, + message: "Password reset successfully.", + }); + } catch (error) { + return res.status(400).json({ + success: false, + message: error.message || "Invalid or expired token.", + }); + } +}; + +// Change Password +exports.changePassword = async (req, res) => { + try { + const { currentPassword, newPassword } = req.body; + + const user = req.user; + + log(`User:`, user); + + const foundUser = await User.findOne({ where: { id: user.id } }); + + if (!foundUser) { + return res.status(404).json({ + success: false, + message: "User not found.", + }); + } + + const isMatch = await checkPassword(currentPassword, foundUser.password); + + if (!isMatch) { + return res.status(400).json({ + success: false, + message: "Current password is incorrect.", + }); + } + + foundUser.password = await hashPassword(newPassword); + await foundUser.save(); + await logActivity({ + user: req.user, + description: "Changed password", + type: "PASSWORD_CHANGE", + module: "Authentication", + }); + + return res.json({ + success: true, + message: "Password changed successfully.", + }); + } catch (error) { + return res.status(400).json({ + success: false, + message: error.message || "Failed to change password.", + }); + } +}; diff --git a/app/controllers/upload.controller.js b/app/controllers/upload.controller.js new file mode 100644 index 0000000..ab93d12 --- /dev/null +++ b/app/controllers/upload.controller.js @@ -0,0 +1,131 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/controllers/activity.controller.js + +const { uploadToS3, getSignedFileUrl } = require("../utils/s3Upload.utill"); +const { log } = require("../utils/consoleLog.utill"); + +const db = require("../models"); +const Upload = db.Upload; +const Assets = db.Assets; + +// Constants +const MAX_IMAGE_SIZE = 3 * 1024 * 1024; // 3MB +const MAX_PDF_SIZE = 5 * 1024 * 1024; // 5MB + +const isValidFileType = (mimetype) => { + return mimetype.startsWith("image/") || mimetype === "application/pdf"; +}; + +const isValidFileSize = (mimetype, size) => { + if (mimetype.startsWith("image/")) return size <= MAX_IMAGE_SIZE; + if (mimetype === "application/pdf") return size <= MAX_PDF_SIZE; + return false; +}; + +exports.uploadFile = async (req, res) => { + try { + // 1. Check file exists + if (!req.file) { + return res.status(400).json({ + success: false, + message: "No file uploaded", + }); + } + + const { mimetype, size, originalname } = req.file; + + // 2. Validate file type + if (!isValidFileType(mimetype)) { + return res.status(400).json({ + success: false, + message: "Only images and PDFs are allowed", + }); + } + + // 3. Validate file size + if (!isValidFileSize(mimetype, size)) { + return res.status(400).json({ + success: false, + message: mimetype.startsWith("image/") + ? "Image too large (max 1MB)" + : "PDF too large (max 5MB)", + }); + } + + log("File validation passed:", { + mimetype, + size, + originalname, + use_for: req.body.use_for, + }); + + // 4. Upload to S3 + const fileKey = await uploadToS3(req.file, req.body.use_for); + + const fileUrl = await getSignedFileUrl(fileKey); + + // 5. Save to DB + const uploadData = { + file_path: fileKey, + file_type: mimetype, + file_size: size, + original_name: originalname, + use_for: req.body.use_for || null, + uploaded_by: req.user?.id || null, + }; + + const newUpload = await Upload.create(uploadData); + + newUpload.dataValues.file_url = fileUrl; // Add URL to response + + // 6. Response + return res.status(201).json({ + success: true, + message: "File uploaded successfully", + data: newUpload, + }); + } catch (error) { + console.error("Upload Controller Error:", error); + + return res.status(500).json({ + success: false, + message: "Failed to upload file", + error: process.env.NODE_ENV === "development" ? error.message : undefined, + }); + } +}; + +// Get Uploaded File URL +exports.getFileUrl = async (req, res) => { + try { + const { id } = req.params; + + const uploadRecord = await Upload.findByPk(id); + + if (!uploadRecord) { + return res.status(404).json({ + success: false, + message: "File not found", + }); + } + + const fileUrl = await getSignedFileUrl(uploadRecord.file_path); + + return res.status(200).json({ + success: true, + message: "File URL retrieved successfully", + data: { + id: uploadRecord.id, + file_url: fileUrl, + }, + }); + } catch (error) {} +}; diff --git a/app/controllers/user.controller.js b/app/controllers/user.controller.js new file mode 100644 index 0000000..8da53bc --- /dev/null +++ b/app/controllers/user.controller.js @@ -0,0 +1,323 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/controllers/user.controller.js + +const db = require("../models"); +const { hashPassword } = require("../utils/hashPassword.util"); +const { generateUserId, generateId } = require("../utils/idGen.util"); +const { logActivity } = require("../services/activity.service"); +const { sendMail } = require("../utils/mail.util"); +const User = db.User; +const Profile = db.Profile; + +// Create a new user +exports.createNewUser = async (req, res) => { + const transaction = await db.sequelize.transaction(); + + try { + const { + firstName, + lastName, + email, + role, + roleID, + accountType, + department, + } = req.body; + + const hashedPassword = await hashPassword(process.env.DEFAULT_PASSWORD); + const userID = generateUserId(); + + const userExists = await User.findOne({ where: { email } }); + if (userExists) { + await transaction.rollback(); + + return res.status(400).send({ + success: false, + message: "User with this email already exists", + }); + } + + const newUser = await User.create( + { + id: userID, + firstName, + lastName, + email, + password: hashedPassword, + accountType, + role, + roleID: roleID || "N/A", + department: department || null, + }, + { transaction }, + ); + + const newProfile = await Profile.create( + { + profile_id: generateId(), + user_id: newUser.id, + theme: "light", + notificationsEnabled: true, + profilePicture_id: "N/A", + backgroundImage_id: "N/A", + dob: null, + phone_number: null, + }, + { transaction }, + ); + + await sendMail({ + to: email, + subject: "Welcome - Ocenic Titan", + templateName: "welcome", + templateVars: { + firstName: firstName, + email: email, + password: process.env.DEFAULT_PASSWORD, + }, + text: `Hello ${firstName}, your account has been created successfully.`, + }); + + await transaction.commit(); + + await logActivity({ + user: req.user, + description: `Created New User with ID: ${newUser.id}`, + type: "CREATE_USER", + module: "User Management", + }); + + res.status(201).send({ + success: true, + message: "User Created Successfully", + data: { + id: newUser.id, + firstName: newUser.firstName, + lastName: newUser.lastName, + email: newUser.email, + accountType: newUser.accountType, + role: newUser.role, + department: newUser.department, + }, + }); + } catch (error) { + await transaction.rollback(); + + res.status(500).send({ + success: false, + message: "Failed to create user", + error: error.message, + }); + } +}; + +// Get users with pagination (20 per page) +exports.getAllUsers = async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; // default page = 1 + const limit = 20; + const offset = (page - 1) * limit; + + const { count, rows: users } = await User.findAndCountAll({ + attributes: { exclude: ["password"] }, + limit, + offset, + order: [["createdAt", "DESC"]], // optional sorting + }); + + res.status(200).send({ + success: true, + data: users, + pagination: { + totalUsers: count, + totalPages: Math.ceil(count / limit), + currentPage: page, + pageSize: limit, + }, + }); + } catch (error) { + res.status(500).send({ + success: false, + message: "Failed to retrieve users", + error: error.message, + }); + } +}; + +// Get user details by ID +exports.getUserById = async (req, res) => { + try { + const { id } = req.params; + const user = await User.findOne({ + where: { id }, + attributes: { exclude: ["password"] }, + include: [{ model: Profile, as: "profile" }], + }); + + if (!user) { + return res.status(404).send({ + success: false, + message: "User not found", + }); + } + + res.status(200).send({ + success: true, + data: user, + }); + } catch (error) { + res.status(500).send({ + success: false, + message: "Failed to retrieve user", + error: error.message, + }); + } +}; + +// Update user details +exports.updateUser = async (req, res) => { + const transaction = await db.sequelize.transaction(); + try { + const { id } = req.params; + const { + firstName, + lastName, + email, + role, + roleID, + accountType, + department, + theme, + notificationsEnabled, + profilePicture_id, + backgroundImage_id, + dob, + phone_number, + } = req.body; + const user = await User.findOne({ + where: { id }, + }); + const UserProfile = await Profile.findOne({ + where: { user_id: id }, + }); + + if (!user) { + return res.status(404).send({ + success: false, + message: "User not found", + }); + } + + if (!UserProfile) { + return res.status(404).send({ + success: false, + message: "User profile not found", + }); + } + + user.firstName = firstName || user.firstName; + user.lastName = lastName || user.lastName; + user.role = role || user.role; + user.roleID = roleID || user.roleID || "N/A"; + user.accountType = accountType || user.accountType; + user.department = department || user.department; + + UserProfile.theme = theme || UserProfile.theme; + UserProfile.notificationsEnabled = + notificationsEnabled !== undefined + ? notificationsEnabled + : UserProfile.notificationsEnabled; + UserProfile.profilePicture_id = + profilePicture_id || UserProfile.profilePicture_id || "N/A"; + UserProfile.backgroundImage_id = + backgroundImage_id || UserProfile.backgroundImage_id || "N/A"; + UserProfile.dob = dob || UserProfile.dob; + UserProfile.phone_number = phone_number || UserProfile.phone_number; + + await UserProfile.save({ transaction }); + await user.save({ transaction }); + await transaction.commit(); + + await logActivity({ + user: req.user, + description: `Updated User with ID: ${user.id}`, + type: "UPDATE_USER", + module: "User Management", + }); + + const data = { + id: user.id, + firstName: user.firstName, + lastName: user.lastName, + email: user.email, + accountType: user.accountType, + role: user.role, + department: user.department, + profile: { + theme: UserProfile.theme, + notificationsEnabled: UserProfile.notificationsEnabled, + profilePicture_id: UserProfile.profilePicture_id, + backgroundImage_id: UserProfile.backgroundImage_id, + dob: UserProfile.dob, + phone_number: UserProfile.phone_number, + }, + }; + + res.status(200).send({ + success: true, + message: "User updated successfully", + data, + }); + } catch (error) { + await transaction.rollback(); + res.status(500).send({ + success: false, + message: `Error: ${error.message}`, + }); + } +}; + +// Delete user +exports.deleteUser = async (req, res) => { + const transaction = await db.sequelize.transaction(); + try { + const { id } = req.params; + const user = await User.findOne({ + where: { id }, + }); + if (!user) { + return res.status(404).send({ + success: false, + message: "User not found", + }); + } + await user.destroy({ transaction }); + await transaction.commit(); + + await logActivity({ + user: req.user, + description: `Deleted User with ID: ${user.id}`, + type: "DELETE_USER", + module: "User Management", + }); + + res.status(200).send({ + success: true, + message: "User deleted successfully", + }); + } catch (error) { + await transaction.rollback(); + res.status(500).send({ + success: false, + message: `Error: ${error.message}`, + }); + } +}; diff --git a/app/logic/documents/builders/invoice.builder.js b/app/logic/documents/builders/invoice.builder.js new file mode 100644 index 0000000..fc9aca9 --- /dev/null +++ b/app/logic/documents/builders/invoice.builder.js @@ -0,0 +1,64 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/logic/documents/builders/invoice.builder.js + +module.exports = { + sheetName: "Invoice", + + build: async (worksheet, data) => { + worksheet.addRow(["INVOICE"]).font = { + bold: true, + size: 18, + }; + + worksheet.addRow([]); + + worksheet.addRow(["Invoice No", data.invoiceNo]); + worksheet.addRow(["Customer", data.customerName]); + worksheet.addRow(["Date", data.date]); + + worksheet.addRow([]); + + const header = worksheet.addRow([ + "Description", + "Qty", + "Rate", + "Amount", + ]); + + header.font = { bold: true }; + + let total = 0; + + (data.items || []).forEach((item) => { + const amount = item.qty * item.rate; + + total += amount; + + worksheet.addRow([ + item.description, + item.qty, + item.rate, + amount, + ]); + }); + + worksheet.addRow([]); + + worksheet.addRow([ + "", + "", + "TOTAL", + total, + ]).font = { + bold: true, + }; + }, +}; \ No newline at end of file diff --git a/app/logic/documents/builders/precost.builder.js b/app/logic/documents/builders/precost.builder.js new file mode 100644 index 0000000..938d949 --- /dev/null +++ b/app/logic/documents/builders/precost.builder.js @@ -0,0 +1,68 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/logic/documents/builders/precost.builder.js + +module.exports = { + sheetName: "PreCost Estimate", + + build: async (worksheet, data) => { + worksheet.addRow(["PreCost Estimate"]).font = { + bold: true, + size: 16, + }; + + worksheet.addRow([]); + + worksheet.addRow([ + "Customer", + data.customerName || "", + ]); + + worksheet.addRow([]); + + const header = worksheet.addRow([ + "Item", + "Description", + "Qty", + "Unit Price", + "Total", + ]); + + header.font = { bold: true }; + + let total = 0; + + (data.items || []).forEach((item) => { + const rowTotal = item.quantity * item.unit_price; + + total += rowTotal; + + worksheet.addRow([ + item.item_name, + item.customer_remark , + item.quantity, + item.unit_price, + rowTotal, + ]); + }); + + worksheet.addRow([]); + + worksheet.addRow([ + "", + "", + "", + "Grand Total", + total, + ]).font = { + bold: true, + }; + }, +}; \ No newline at end of file diff --git a/app/logic/documents/engine/excel.engine.js b/app/logic/documents/engine/excel.engine.js new file mode 100644 index 0000000..0a8dfe3 --- /dev/null +++ b/app/logic/documents/engine/excel.engine.js @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/logic/documents/engine/excel.engine.js + +const ExcelJS = require("exceljs"); + +const generateExcel = async (builder, data) => { + const workbook = new ExcelJS.Workbook(); + const worksheet = workbook.addWorksheet(builder.sheetName); + + await builder.build(worksheet, data); + + return await workbook.xlsx.writeBuffer(); +}; + +module.exports = { + generateExcel, +}; \ No newline at end of file diff --git a/app/logic/documents/engine/pdf.engine.js b/app/logic/documents/engine/pdf.engine.js new file mode 100644 index 0000000..512f51d --- /dev/null +++ b/app/logic/documents/engine/pdf.engine.js @@ -0,0 +1,70 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/logic/documents/engine/pdf.engine.js + +const puppeteer = require("puppeteer"); + +const generatePDF = async (html) => { + const launchConfig = { + args: ["--no-sandbox", "--disable-setuid-sandbox"], + }; + + if (process.env.PUPPETEER_EXECUTABLE_PATH) { + launchConfig.executablePath = process.env.PUPPETEER_EXECUTABLE_PATH; + } else { + launchConfig.headless = true; + } + + const browser = await puppeteer.launch(launchConfig); + + try { + const page = await browser.newPage(); + + await page.setContent(html, { + waitUntil: "load", + }); + + // Wait for fonts + await page.evaluate(() => document.fonts?.ready); + + // Wait for images to fully decode + await page.evaluate(async () => { + const images = Array.from(document.images); + + await Promise.all( + images.map((img) => { + if (img.complete && img.naturalHeight !== 0) return; + + return new Promise((resolve) => { + img.onload = resolve; + img.onerror = resolve; + }); + }), + ); + }); + + // Force layout + paint + await page.evaluate(() => document.body.getBoundingClientRect()); + + // Extra stability delay (important on servers) + await new Promise((r) => setTimeout(r, 1000)); + + const buffer = await page.pdf({ + format: "A4", + printBackground: true, + }); + + return buffer; + } finally { + await browser.close(); + } +}; + +module.exports = { generatePDF }; diff --git a/app/logic/documents/index.js b/app/logic/documents/index.js new file mode 100644 index 0000000..e0e81af --- /dev/null +++ b/app/logic/documents/index.js @@ -0,0 +1,105 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/logic/documents/index.js + +const registry = require("./registry"); + +const { generateExcel } = require("./engine/excel.engine"); +const { + generatePdfFromPreCost, + generatePdfFromInvoice, + generatePdfFromDispatchNote, + generatePdfFromDeliveryNote, + generatePdfFromPO, + generatePdfFromCustomDocument, + generatePdfFromCreditNote, +} = require("../../utils/pdfGenerator"); + +const formatMeta = { + pdf: { + ext: "pdf", + mime: "application/pdf", + }, + + excel: { + ext: "xlsx", + mime: + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + }, +}; + +const pdfGenerators = { + precost: generatePdfFromPreCost, + invoice: generatePdfFromInvoice, + dispatchnote: generatePdfFromDispatchNote, + deliverynote: generatePdfFromDeliveryNote, + po: generatePdfFromPO, + customdocument: generatePdfFromCustomDocument, + creditnote: generatePdfFromCreditNote, +}; + +const generateDocument = async ( + document, + format, + data +) => { + document = document.toLowerCase().replace(/[\s_-]+/g, ""); + format = format.toLowerCase(); + + const doc = registry[document]; + + if (!doc) { + const availableTypes = Object.keys(registry).join(", "); + throw new Error(`Unsupported document type: "${document}". Available types: ${availableTypes}`); + } + + let fileBuffer; + + if (format === "pdf") { + // Format the data using the template + const formattedData = await doc.pdfTemplate(data); + + // Get the correct PDF generator for this document type + const pdfGenerator = pdfGenerators[document]; + + if (pdfGenerator) { + fileBuffer = await pdfGenerator(formattedData); + } else { + throw new Error(`No PDF generator found for document type: ${document}`); + } + } + + else if (format === "excel") { + if (doc.excelBuilder) { + fileBuffer = await generateExcel( + doc.excelBuilder, + data + ); + } else { + throw new Error(`Excel generation not supported for document type: ${document}`); + } + } + + else { + throw new Error("Unsupported format"); + } + + const meta = formatMeta[format]; + + return { + fileBuffer, + fileName: `${document}-${Date.now()}.${meta.ext}`, + mimeType: meta.mime, + }; +}; + +module.exports = { + generateDocument, +}; \ No newline at end of file diff --git a/app/logic/documents/registry.js b/app/logic/documents/registry.js new file mode 100644 index 0000000..bd2be03 --- /dev/null +++ b/app/logic/documents/registry.js @@ -0,0 +1,72 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/logic/documents/registry.js + +let buildPreCostTemplate, buildInvoiceTemplate, buildDispatchNoteTemplate, buildDeliveryNoteTemplate, buildPOTemplate, buildCustomDocumentTemplate, buildCreditNoteTemplate; +let precostBuilder, invoiceBuilder; + +try { + buildPreCostTemplate = require("../../templates/documents/precost/preCost.template"); + buildInvoiceTemplate = require("../../templates/documents/invoice/invoice.template"); + buildDispatchNoteTemplate = require("../../templates/documents/dispatchNote/dispatchNote.template"); + buildDeliveryNoteTemplate = require("../../templates/documents/deliveryNote/deliveryNote.template"); + buildPOTemplate = require("../../templates/documents/po/po.template"); + buildCustomDocumentTemplate = require("../../templates/documents/customDocument/customDocument.template"); + buildCreditNoteTemplate = require("../../templates/documents/creditNote/creditNote.template"); + + precostBuilder = require("./builders/precost.builder"); + invoiceBuilder = require("./builders/invoice.builder"); + + console.log("✅ All document templates loaded successfully"); +} catch (error) { + console.error("❌ Error loading document templates:", error.message); + console.error(error.stack); +} + +const registry = { + precost: { + pdfTemplate: buildPreCostTemplate, + excelBuilder: precostBuilder, + }, + + invoice: { + pdfTemplate: buildInvoiceTemplate, + excelBuilder: invoiceBuilder, + }, + + dispatchnote: { + pdfTemplate: buildDispatchNoteTemplate, + excelBuilder: null, + }, + + deliverynote: { + pdfTemplate: buildDeliveryNoteTemplate, + excelBuilder: null, + }, + + po: { + pdfTemplate: buildPOTemplate, + excelBuilder: null, + }, + + customdocument: { + pdfTemplate: buildCustomDocumentTemplate, + excelBuilder: null, + }, + + creditnote: { + pdfTemplate: buildCreditNoteTemplate, + excelBuilder: null, + }, +}; + +console.log("📋 Registry initialized with keys:", Object.keys(registry)); + +module.exports = registry; \ No newline at end of file diff --git a/app/middleware/auth.middleware.js b/app/middleware/auth.middleware.js new file mode 100644 index 0000000..31181e4 --- /dev/null +++ b/app/middleware/auth.middleware.js @@ -0,0 +1,93 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/middleware/auth.middleware.js + +const { verifyToken } = require("../utils/jwt.util"); +const { getEffectivePermissions } = require("../services/permission.service"); + +const authenticate = async (req, res, next) => { + try { + let token = null; + + // Get token from cookie + if (req.cookies?.access_token) { + token = req.cookies.access_token; + } + + // Fallback to Bearer token + if (!token && req.headers.authorization?.startsWith("Bearer ")) { + token = req.headers.authorization.split(" ")[1]; + } + + if (!token) { + return res.status(401).json({ + success: false, + message: "Unauthorized", + }); + } + + // Verify token + const decoded = verifyToken(token); + + if (process.env.NODE_ENV === "development") { + console.log("DECODED:", decoded); + } + + const userId = decoded.sub || decoded.id; + + if (!userId) { + throw new Error("User ID missing in token"); + } + + // Load permissions + const permissions = await getEffectivePermissions(userId); + + req.user = { + ...decoded, + id: userId, + permissions, + }; + + next(); + } catch (err) { + console.error("AUTH ERROR:", err); + + // Clear invalid/expired cookie + res.clearCookie("access_token", { + httpOnly: true, + secure: process.env.NODE_ENV === "production", + sameSite: process.env.NODE_ENV === "production" ? "None" : "Lax", + }); + + // Token expired + if (err.name === "TokenExpiredError") { + return res.status(401).json({ + success: false, + message: "Session expired. Please login again.", + }); + } + + // Invalid token + if (err.name === "JsonWebTokenError") { + return res.status(401).json({ + success: false, + message: "Invalid token", + }); + } + + // Default + return res.status(401).json({ + success: false, + message: "Authentication failed", + }); + } +}; + +module.exports = { authenticate }; diff --git a/app/middleware/docsSession.middleware.js b/app/middleware/docsSession.middleware.js new file mode 100644 index 0000000..b7d23e8 --- /dev/null +++ b/app/middleware/docsSession.middleware.js @@ -0,0 +1,21 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/middleware/docsSession.middleware.js + +module.exports = (req, res, next) => { + if (req.cookies && req.cookies.docsAuth === "true") { + return next(); + } + + return res.status(401).json({ + success: false, + message: "Unauthorized: Please login to access docs", + }); +}; \ No newline at end of file diff --git a/app/middleware/permission.middleware.js b/app/middleware/permission.middleware.js new file mode 100644 index 0000000..aaf3ff6 --- /dev/null +++ b/app/middleware/permission.middleware.js @@ -0,0 +1,100 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/middleware/permission.middleware.js + +const hasPermission = (userPermissions, requiredPermission) => { + return userPermissions.some(p => { + if (p === requiredPermission) return true; + + // wildcard support + if (p.endsWith(".*")) { + const prefix = p.slice(0, -2); + return requiredPermission.startsWith(prefix); + } + + return false; + }); +}; + +const methodToAction = { + GET: "view", + POST: "create", + PUT: "update", + PATCH: "update", + DELETE: "delete" +}; + +const checkPermission = (baseOrFull, options = {}) => { + return (req, res, next) => { + if (!req.user) { + return res.status(401).json({ + success: false, + message: "Unauthorized" + }); + } + + // admin bypass + if (req.user.accountType === "admin") { + return next(); + } + + if (typeof baseOrFull !== "string") { + return res.status(500).json({ + success: false, + message: "Permission must be a string" + }); + } + + let requiredPermission; + + if (options.custom) { + requiredPermission = baseOrFull; + } else { + const action = methodToAction[req.method]; + + if (!action) { + return res.status(500).json({ + success: false, + message: "Unknown HTTP method" + }); + } + + requiredPermission = `${baseOrFull}.${action}`; + } + + const userPermissions = req.user.permissions || []; + + const hasPermission = + userPermissions.includes(requiredPermission) || + userPermissions.includes(`${baseOrFull}.*`); + + if (!hasPermission) { + return res.status(403).json({ + success: false, + message: `Forbidden - Missing ${requiredPermission}` + }); + } + + next(); + }; +}; + +const authorizedAccountType = (allowedTypes) => { + return (req, res, next) => { + if (!req.user || !allowedTypes.includes(req.user.accountType)) { + return res + .status(403) + .json({ success: false, message: "Forbidden" }); + } + next(); + } +} + +module.exports = {authorizedAccountType, checkPermission}; \ No newline at end of file diff --git a/app/middleware/upload.middleware.js b/app/middleware/upload.middleware.js new file mode 100644 index 0000000..b4dd001 --- /dev/null +++ b/app/middleware/upload.middleware.js @@ -0,0 +1,64 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/middlewares/upload.middleware.js + +const multer = require("multer"); + +// Use memory storage (required for S3 upload) +const storage = multer.memoryStorage(); + +// File filter (only images + PDFs) +const fileFilter = (req, file, cb) => { + const allowedTypes = [ + "image/", + "application/pdf", + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", // .xlsx + "application/vnd.ms-excel" // .xls + ]; + + const isValid = + file.mimetype.startsWith("image/") || + file.mimetype === "application/pdf" || + file.mimetype === "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" || + file.mimetype === "application/vnd.ms-excel"; + + if (isValid) { + cb(null, true); + } else { + cb(new Error("Only images, PDFs, and Excel files are allowed"), false); + } +}; + +// Multer instance +const upload = multer({ + storage, + fileFilter, + limits: { + fileSize: 5 * 1024 * 1024, // max 5MB (global limit) + }, +}); + +// Middleware wrappers + +// Single file upload +const uploadSingle = (fieldName) => upload.single(fieldName); + +// Multiple files (same field) +const uploadMultiple = (fieldName, maxCount = 5) => + upload.array(fieldName, maxCount); + +// Multiple fields (advanced) +const uploadFields = (fields) => upload.fields(fields); + +module.exports = { + uploadSingle, + uploadMultiple, + uploadFields, +}; \ No newline at end of file diff --git a/app/models/activities/userActivities.model.js b/app/models/activities/userActivities.model.js new file mode 100644 index 0000000..8560b29 --- /dev/null +++ b/app/models/activities/userActivities.model.js @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/models/userActivities.model.js + +module.exports = (sequelize, DataTypes) => { + const UserActivity = sequelize.define( + "UserActivity", + { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + }, + user_id: { + type: DataTypes.STRING, + allowNull: false, + }, + username: { + type: DataTypes.STRING, + allowNull: false, + }, + activity_description: { + type: DataTypes.STRING, + allowNull: false, + }, + activity_type: { + type: DataTypes.STRING, + allowNull: true, + }, + module:{ + type: DataTypes.STRING, + allowNull: true, + }, + activity_time: { + type: DataTypes.DATE, + allowNull: false, + }, + activity_date:{ + type: DataTypes.DATEONLY, + allowNull: false, + } + }, + { + tableName: "UserActivity", + timestamps: true, + }, + ); + + return UserActivity; +}; diff --git a/app/models/document/document.model.js b/app/models/document/document.model.js new file mode 100644 index 0000000..0bd05ca --- /dev/null +++ b/app/models/document/document.model.js @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/models/document/document.model.js + +module.exports = (sequelize, DataTypes) => { + const document = sequelize.define( + "Document", + { + doc_id:{ + type: DataTypes.STRING, + primaryKey: true, + }, + reference_no: { + type: DataTypes.STRING, + allowNull: false, + }, + doc_type: { + type: DataTypes.STRING, + allowNull: true, + defaultValue: "N/A" + }, + data: { + type: DataTypes.JSON, + allowNull: false, + }, + status: { + type: DataTypes.STRING, + allowNull: false, + defaultValue: "DRAFT", + } + }, + { + tableName: "Document", + timestamps: true, + }, + ); + + return document; +}; diff --git a/app/models/document/documentType.model.js b/app/models/document/documentType.model.js new file mode 100644 index 0000000..504eec2 --- /dev/null +++ b/app/models/document/documentType.model.js @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/models/document/documentType.model.js + +module.exports = (sequelize, DataTypes) => { + const documentType = sequelize.define( + "DocumentType", + { + id:{ + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + }, + doc_type_name: { + type: DataTypes.STRING, + allowNull: true, + defaultValue: "N/A" + }, + description: { + type: DataTypes.JSON, + allowNull: false, + }, + }, + { + tableName: "DocumentType", + timestamps: true, + }, + ); + + return documentType; +}; diff --git a/app/models/index.js b/app/models/index.js new file mode 100644 index 0000000..d695284 --- /dev/null +++ b/app/models/index.js @@ -0,0 +1,77 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/models/index.js + + +const { Sequelize, DataTypes } = require("sequelize"); +const dbConfig = require("../config/db.config"); + +let loggingOption = false; + +if(process.env.NODE_ENV === 'production') { + loggingOption = true; +} + +const sequelize = new Sequelize( + dbConfig.DB, + dbConfig.USER, + dbConfig.PASSWORD, + { + host: dbConfig.HOST, + port: dbConfig.PORT, + dialect: dbConfig.DIALECT, + pool: dbConfig.pool, + logging: loggingOption + } +); + +const db = {}; + +db.Sequelize = Sequelize; +db.sequelize = sequelize; + +/* Register db models */ + +// User and Authentication +db.User = require("./user/user.model")(sequelize, DataTypes); +db.UserActivity = require("./activities/userActivities.model")(sequelize, DataTypes); +db.Profile = require("./user/profile.model")(sequelize, DataTypes); + +// Uploads +db.Upload = require("./upload/upload.model")(sequelize, DataTypes); + +// Roles and Permissions +db.roles = require("./permission/role.model")(sequelize, DataTypes); +db.permission = require("./permission/permission.model")(sequelize, DataTypes); +db.rolePermission = require("./permission/rolePermission.model")(sequelize, DataTypes); +db.userPermission = require("./permission/userPermission.model")(sequelize, DataTypes); + +// Document Management +db.Document = require("./document/document.model")(sequelize, DataTypes); +db.DocumentType = require("./document/documentType.model")(sequelize, DataTypes); + +// Reference Numbers +db.referenceNumber = require("./referenceNumbers/referenceNumber.model")(sequelize, DataTypes); + +// Notifications +db.notification = require("./notification/notification.model")(sequelize, DataTypes); +db.userNotification = require("./notification/userNotification.model")(sequelize, DataTypes); + +/* Associations */ +Object.keys(db).forEach(model => { + if (db[model].associate) { + db[model].associate(db); + } +}); + + + + +module.exports = db; \ No newline at end of file diff --git a/app/models/notification/notification.model.js b/app/models/notification/notification.model.js new file mode 100644 index 0000000..cc101c5 --- /dev/null +++ b/app/models/notification/notification.model.js @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/models/notification/notification.model.js + +module.exports = (sequelize, DataTypes) => { + const notification = sequelize.define( + "notification", + { + notification_id: { + type: DataTypes.STRING, + primaryKey: true, + }, + notificationHeadline: { + type: DataTypes.STRING, + allowNull: false, + }, + notificationDescription: { + type: DataTypes.TEXT, + allowNull: true, + }, + notificationType: { + type: DataTypes.ENUM("USER", "ANNOUNCEMENT"), + allowNull: false, + }, + isActive: { + type: DataTypes.BOOLEAN, + defaultValue: true, + }, + dateCreated: { + type: DataTypes.DATE, + defaultValue: DataTypes.NOW, + }, + }, + { + tableName: "notification", + timestamps: true, + }, + ); + + notification.associate = (models) => { + notification.hasMany(models.userNotification, { + foreignKey: "notification_id", + as: "users", + }); + }; + + return notification; +}; diff --git a/app/models/notification/userNotification.model.js b/app/models/notification/userNotification.model.js new file mode 100644 index 0000000..ce3d154 --- /dev/null +++ b/app/models/notification/userNotification.model.js @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/models/notification/userNotification.model.js + +module.exports = (sequelize, DataTypes) => { + const userNotification = sequelize.define( + "userNotification", + { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + }, + user_id: { + type: DataTypes.STRING(255), + allowNull: false, + collate: "utf8mb4_general_ci", + }, + notification_id: { + type: DataTypes.STRING, + allowNull: false, + }, + isRead: { + type: DataTypes.BOOLEAN, + defaultValue: false, + }, + }, + { + tableName: "user_notification", + timestamps: true, + }, + ); + + userNotification.associate = (models) => { + userNotification.belongsTo(models.User, { + foreignKey: "user_id", + constraints: false, + }); + + userNotification.belongsTo(models.notification, { + foreignKey: "notification_id", + constraints: false, + }); + }; + + return userNotification; +}; diff --git a/app/models/permission/permission.model.js b/app/models/permission/permission.model.js new file mode 100644 index 0000000..483a027 --- /dev/null +++ b/app/models/permission/permission.model.js @@ -0,0 +1,59 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/models/permission/permission.model.js + +module.exports = (sequelize, DataTypes) => { + const permission = sequelize.define( + "permission", + { + permission_id: { + type: DataTypes.STRING, + primaryKey: true, + }, + permissionName: { + type: DataTypes.STRING, + allowNull: false, + }, + permissionDescription: { + type: DataTypes.TEXT, + allowNull: true, + }, + page:{ + type: DataTypes.STRING, + allowNull: false, + }, + module:{ + type: DataTypes.STRING, + allowNull: false, + }, + action: { + type: DataTypes.STRING, + allowNull: false, + } + }, + { + tableName: "permission", + timestamps: true, + }, + ); + + permission.associate = (db) => { + permission.hasMany(db.userPermission, { + foreignKey: "permission_id", + as: "userPermissions" + }); + permission.hasMany(db.rolePermission, { + foreignKey: "permission_id", + as: "rolePermissions" + }); + }; + + return permission; +}; diff --git a/app/models/permission/role.model.js b/app/models/permission/role.model.js new file mode 100644 index 0000000..857fe2b --- /dev/null +++ b/app/models/permission/role.model.js @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/models/permission/role.model.js + +module.exports = (sequelize, DataTypes) => { + const roles = sequelize.define( + "roles", + { + role_id: { + type: DataTypes.STRING, + primaryKey: true + }, + roleName: { + type: DataTypes.STRING, + allowNull: false + }, + roleDescription: { + type: DataTypes.TEXT, + allowNull: true + } + }, + { + tableName: "roles", + timestamps: true + } + ); + + roles.associate = (db) => { + roles.hasMany(db.rolePermission, { + foreignKey: "role_id", + as: "rolePermissions" + }); + }; + + return roles; +}; diff --git a/app/models/permission/rolePermission.model.js b/app/models/permission/rolePermission.model.js new file mode 100644 index 0000000..b42b6ef --- /dev/null +++ b/app/models/permission/rolePermission.model.js @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/models/permission/rolePermission.model.js + +module.exports = (sequelize, DataTypes) => { + const rolePermission = sequelize.define( + "rolePermission", + { + rp_id: { + type: DataTypes.STRING, + primaryKey: true + }, + role_id: { + type: DataTypes.STRING, + allowNull: false + }, + permission_id: { + type: DataTypes.STRING, + allowNull: false + } + }, + { + tableName: "rolePermission", + timestamps: true + } + ); + + rolePermission.associate = (db) => { + rolePermission.belongsTo(db.roles, { + foreignKey: "role_id", + as: "role" + }); + rolePermission.belongsTo(db.permission, { + foreignKey: "permission_id", + as: "permission" + }); + }; + + return rolePermission; +}; diff --git a/app/models/permission/userPermission.model.js b/app/models/permission/userPermission.model.js new file mode 100644 index 0000000..1324880 --- /dev/null +++ b/app/models/permission/userPermission.model.js @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/models/permission/userPermission.model.js + +module.exports = (sequelize, DataTypes) => { + const userPermission = sequelize.define( + "userPermission", + { + up_id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true + }, + user_id: { + type: DataTypes.STRING, + allowNull: false + }, + permission_id: { + type: DataTypes.STRING, + allowNull: false + } + }, + { + tableName: "userPermission", + timestamps: true + } + ); + + userPermission.associate = (db) => { + userPermission.belongsTo(db.permission, { + foreignKey: "permission_id", + as: "permission" + }); + userPermission.belongsTo(db.User, { + foreignKey: "user_id", + as: "user" + }); + }; + + return userPermission; +}; diff --git a/app/models/referenceNumbers/referenceNumber.model.js b/app/models/referenceNumbers/referenceNumber.model.js new file mode 100644 index 0000000..3d57491 --- /dev/null +++ b/app/models/referenceNumbers/referenceNumber.model.js @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/models/referenceNumbers/referenceNumber.model.js + +module.exports = (sequelize, DataTypes) => { + const ReferenceNumber = sequelize.define( + "ReferenceNumber", + { + id: { + type: DataTypes.STRING, + primaryKey: true, + }, + + // Unique sequence scope + sequence_key: { + type: DataTypes.STRING, + allowNull: false, + unique: true, + }, + + current_number: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: 0, + }, + + last_ref_number: { + type: DataTypes.STRING, + }, + }, + { + tableName: "referenceNumbers", + timestamps: true, + } + ); + + return ReferenceNumber; +}; diff --git a/app/models/upload/upload.model.js b/app/models/upload/upload.model.js new file mode 100644 index 0000000..a345a29 --- /dev/null +++ b/app/models/upload/upload.model.js @@ -0,0 +1,54 @@ +/** + * © 2026 Niolla. All rights reserved. + * + * This file is part of the Niolla software project and is intended for internal use only. + * Unauthorized copying, modification, distribution, or disclosure of this file, + * via any medium, is strictly prohibited without written permission from Niolla. + * + * For inquiries, contact: support@niolla.lk + */ + +// app/models/upload.model.js + +module.exports = (sequelize, DataTypes) => { + const upload = sequelize.define( + "upload", + { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + }, + file_path: { + type: DataTypes.STRING, + allowNull: false, + }, + file_type: { + type: DataTypes.STRING, + allowNull: false, + }, + file_size: { + type: DataTypes.INTEGER, + allowNull: false, + }, + original_name: { + type: DataTypes.STRING, + allowNull: false, + }, + use_for: { + type: DataTypes.STRING, + allowNull: false, + }, + uploaded_by: { + type: DataTypes.STRING, + allowNull: false, + }, + }, + { + tableName: "uploads", + timestamps: true, + }, + ); + + return upload; +}; diff --git a/app/models/user/profile.model.js b/app/models/user/profile.model.js new file mode 100644 index 0000000..5f4fa32 --- /dev/null +++ b/app/models/user/profile.model.js @@ -0,0 +1,66 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/models/profile.model.js + +module.exports = (sequelize, DataTypes) => { + const Profile = sequelize.define( + "Profile", + { + profile_id: { + type: DataTypes.STRING, + primaryKey: true, + }, + user_id: { + type: DataTypes.STRING, + allowNull: false, + unique: true, + collate: 'utf8mb4_general_ci' + }, + theme: { + type: DataTypes.STRING, + allowNull: false, + defaultValue: "light", + }, + notificationsEnabled: { + type: DataTypes.BOOLEAN, + defaultValue: true, + }, + profilePicture_id: { + type: DataTypes.STRING, + allowNull: true, + }, + backgroundImage_id: { + type: DataTypes.STRING, + allowNull: true, + }, + dob: { + type: DataTypes.DATE, + allowNull: true, + }, + phone_number:{ + type: DataTypes.STRING, + allowNull: true, + } + }, + { + tableName: "profiles", + timestamps: true, + }, + ); + + Profile.associate = (db) => { + Profile.belongsTo(db.User, { + foreignKey: "user_id", + as: "user", + }); + }; + + return Profile; +}; diff --git a/app/models/user/user.model.js b/app/models/user/user.model.js new file mode 100644 index 0000000..366f779 --- /dev/null +++ b/app/models/user/user.model.js @@ -0,0 +1,73 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/models/User.model.js + +module.exports = (sequelize, DataTypes) => { + const User = sequelize.define( + "User", + { + id: { + type: DataTypes.STRING, + primaryKey: true, + collate: 'utf8mb4_general_ci' + }, + firstName: { + type: DataTypes.STRING, + allowNull: false + }, + lastName: { + type: DataTypes.STRING, + allowNull: false + }, + email: { + type: DataTypes.STRING, + allowNull: false, + unique: true + }, + password: { + type: DataTypes.STRING, + allowNull: false + }, + accountType: { + type: DataTypes.ENUM("admin", "management", "team_head", "user"), + defaultValue: "user" + }, + role: { + type: DataTypes.STRING, + allowNull: false + }, + roleID:{ + type: DataTypes.STRING, + allowNull: false + }, + department: { + type: DataTypes.STRING, + allowNull: true + } + }, + { + tableName: "users", + timestamps: true + } + ); + + User.associate = (db) => { + User.hasMany(db.userPermission, { + foreignKey: "user_id", + as: "userPermissions" + }); + User.hasOne(db.Profile, { + foreignKey: "user_id", + as: "profile", + }); + }; + + return User; +}; diff --git a/app/queues/activity.queue.js b/app/queues/activity.queue.js new file mode 100644 index 0000000..eea6434 --- /dev/null +++ b/app/queues/activity.queue.js @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/queues/activity.queue.js + +const { Queue } = require("bullmq"); +const connection = require("../config/redisClient"); + + +const activityQueue = new Queue("activity-queue", { + connection, +}); + +module.exports = activityQueue; \ No newline at end of file diff --git a/app/queues/document.queue.js b/app/queues/document.queue.js new file mode 100644 index 0000000..3195261 --- /dev/null +++ b/app/queues/document.queue.js @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/queues/document.queue.js + +const { Queue } = require("bullmq"); +const redis = require("../config/redisClient"); + +const documentQueue = new Queue("document-generation", { + connection: redis, + defaultJobOptions: { + attempts: 3, + + backoff: { + type: "exponential", + delay: 2000, + }, + + removeOnComplete: { + age: 3600, + count: 1000, + }, + + removeOnFail: false, + }, +}); + +// Event listeners +documentQueue.on("error", (err) => { + console.error("Document Queue Error:", err); +}); + +documentQueue.on("waiting", (job) => { + console.log(`Document Job ${job.id} waiting...`); +}); + +documentQueue.on("active", (job) => { + console.log(`Document Job ${job.id} started...`); +}); + +documentQueue.on("completed", (job) => { + console.log(`Document Job ${job.id} completed`); +}); + +documentQueue.on("failed", (job, err) => { + console.error( + `Document Job ${job.id} failed:`, + err.message + ); +}); + +module.exports = documentQueue; diff --git a/app/queues/log.queue.js b/app/queues/log.queue.js new file mode 100644 index 0000000..fe89ffe --- /dev/null +++ b/app/queues/log.queue.js @@ -0,0 +1,17 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/queues/log.queue.js + +const { Queue } = require("bullmq"); +const connection = require("../config/redisClient"); + +const logQueue = new Queue("logQueue", { connection }); + +module.exports = logQueue; \ No newline at end of file diff --git a/app/routes/activity.routes.js b/app/routes/activity.routes.js new file mode 100644 index 0000000..2855cda --- /dev/null +++ b/app/routes/activity.routes.js @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/routes/activity.routes.js + +const express = require("express"); +const router = express.Router(); +const activityController = require("../controllers/activity.controller"); +const { + authenticate, +} = require("../middleware/auth.middleware"); + +const { authorizedAccountType, checkPermission } = require("../middleware/permission.middleware"); +const PERMISSIONS = require("../constants/permissions"); + + +// Get all user activities +router.get( + "/", + authenticate, + authorizedAccountType(["admin"]), + activityController.getUserActivities, +); + +// Get activity by User ID +router.get( + "/user/:userId", + authenticate, + authorizedAccountType(["admin"]), + activityController.getActivitiesByUserId, +); + + +module.exports = router; diff --git a/app/routes/auth.routes.js b/app/routes/auth.routes.js new file mode 100644 index 0000000..4d334ad --- /dev/null +++ b/app/routes/auth.routes.js @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/routes/auth.routes.js + +const express = require('express'); +const router = express.Router(); +const authController = require('../controllers/auth.controller'); +const {authenticate} = require('../middleware/auth.middleware'); + +// GET /api/auth/me +router.get("/me", authenticate, (req, res) => { + res.json({ + authenticated: true, + user: req.user + }); +}); + +router.post('/req-otp', authController.loginReq); +router.post('/login', authController.login); +router.post('/logout', authController.logout); + +module.exports = router; diff --git a/app/routes/docs.routes.js b/app/routes/docs.routes.js new file mode 100644 index 0000000..e206fe2 --- /dev/null +++ b/app/routes/docs.routes.js @@ -0,0 +1,77 @@ +/** + * Markdown Docs Route + */ + +const express = require("express"); +const fs = require("fs"); +const path = require("path"); +const docsSession = require("../middleware/docsSession.middleware"); + +const router = express.Router(); + + +const DOCS_DIR = path.join(__dirname, "../../Documentation"); + +router.get("/markdown-files", docsSession, (req, res) => { + fs.readdir(DOCS_DIR, (err, files) => { + if (err) { + return res.status(500).json({ + success: false, + message: "Failed to read documentation folder", + error: err.message, + }); + } + + const mdFiles = files.filter((file) => file.endsWith(".md")); + + res.json({ + success: true, + data: mdFiles, + }); + }); +}); + +router.get("/view/:file", docsSession, (req, res) => { + const filePath = path.join(DOCS_DIR, req.params.file); + + if (!req.params.file.endsWith(".md")) { + return res.status(400).send("Only markdown files allowed"); + } + + // basic protection against path traversal + if (!filePath.startsWith(DOCS_DIR)) { + return res.status(400).send("Invalid path"); + } + + res.sendFile(filePath); +}); + +// Docs login (simple) +router.post("/login", (req, res) => { + const { username, password } = req.body; + + if ( + username === process.env.DOCS_USER && + password === process.env.DOCS_PASS + ) { + res.cookie("docsAuth", "true", { + httpOnly: true, + sameSite: "lax", + secure: process.env.NODE_ENV === "production", + }); + + return res.json({ success: true }); + } + + return res.status(401).json({ + success: false, + message: "Invalid credentials", + }); +}); + +router.post("/logout", (req, res) => { + res.clearCookie("docsAuth"); + res.json({ success: true }); +}); + +module.exports = router; diff --git a/app/routes/document.routes.js b/app/routes/document.routes.js new file mode 100644 index 0000000..ce1cb1f --- /dev/null +++ b/app/routes/document.routes.js @@ -0,0 +1,94 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/routes/document.routes.js + +const express = require("express"); +const router = express.Router(); +const generateDocumentController = require("../controllers/generateDocument.controller"); +const { + authenticate, +} = require("../middleware/auth.middleware"); + +const { authorizedAccountType, checkPermission } = require("../middleware/permission.middleware"); +const PERMISSIONS = require("../constants/permissions"); + + +// Get available document types +router.get( + "/types", + authenticate, + authorizedAccountType(["admin", "management", "team_head", "user"]), + generateDocumentController.getAvailableDocumentTypes +); + +// Get saved documents +router.post( + "/saved", + authenticate, + authorizedAccountType(["admin", "management", "team_head", "user"]), + generateDocumentController.getSavedDocuments +); + +// Generate Document (async - returns jobId immediately) +router.post( + "/generate", + authenticate, + authorizedAccountType(["admin", "management", "team_head", "user"]), + generateDocumentController.generateDocument +); + +router.post( + "/draft", + authenticate, + authorizedAccountType(["admin", "management", "team_head", "user"]), + generateDocumentController.generateDraftDocument +); + +// Generate Reference Number +router.get( + "/reference-number/:documentType", + authenticate, + authorizedAccountType(["admin", "management", "team_head", "user"]), + generateDocumentController.generateReferenceNo +); + +// Get Job Status +router.get( + "/job/:jobId/status", + authenticate, + authorizedAccountType(["admin", "management", "team_head", "user"]), + generateDocumentController.getJobStatus +); + +// Download Document +router.get( + "/download/:uuid", + authenticate, + authorizedAccountType(["admin", "management", "team_head", "user"]), + generateDocumentController.downloadDocument +); + +// Cancel Job +router.delete( + "/job/:jobId", + authenticate, + authorizedAccountType(["admin", "management", "team_head", "user"]), + generateDocumentController.cancelJob +); + +// Get document details +router.get( + "/:docId", + authenticate, + authorizedAccountType(["admin", "management", "team_head", "user"]), + generateDocumentController.getDocumentData +); + +module.exports = router; \ No newline at end of file diff --git a/app/routes/index.js b/app/routes/index.js new file mode 100644 index 0000000..a638e3b --- /dev/null +++ b/app/routes/index.js @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/routes/index.js + +const express = require("express"); + +const authRoutes = require("./auth.routes"); +const userRoutes = require("./user.routes"); +const activityRoutes = require("./activity.routes"); +const uploadRoutes = require("./upload.routes"); +const documentRoutes = require("./document.routes"); +const docsRoutes = require("./docs.routes"); +const permissionRoutes = require("./permission.routes"); +const profileRoutes = require("./profile.routes"); +const notificationRoutes = require("./notification.routes"); + +const router = express.Router(); + +router.use("/auth", authRoutes); +router.use("/user", userRoutes); +router.use("/activity", activityRoutes); +router.use("/upload", uploadRoutes); +router.use("/document", documentRoutes); +router.use("/docs", docsRoutes); +router.use("/profile", profileRoutes); +router.use("/notification", notificationRoutes); + +module.exports = router; \ No newline at end of file diff --git a/app/routes/notification.routes.js b/app/routes/notification.routes.js new file mode 100644 index 0000000..5b328ca --- /dev/null +++ b/app/routes/notification.routes.js @@ -0,0 +1,69 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/routes/notification.routes.js + +const express = require("express"); +const router = express.Router(); + +const notificationController = require("../controllers/notification.controller"); + +const { + authenticate, +} = require("../middleware/auth.middleware"); + +const { + authorizedAccountType, + checkPermission, +} = require("../middleware/permission.middleware"); + +const PERMISSIONS = require("../constants/permissions"); + + +// Create notification +router.post( + "/", + authenticate, + authorizedAccountType(["admin", "management"]), + // checkPermission(PERMISSIONS.NOTIFICATION_CREATE), + notificationController.createNotification, +); + + +// Get all announcements +router.get( + "/announcements", + authenticate, + authorizedAccountType(["admin", "management", "team_head", "user"]), + // checkPermission(PERMISSIONS.NOTIFICATION_VIEW), + notificationController.getAnnouncements, +); + + +// Get notifications for a user +router.get( + "/user/:userId", + authenticate, + authorizedAccountType(["admin", "management", "team_head", "user"]), + // checkPermission(PERMISSIONS.NOTIFICATION_VIEW), + notificationController.getUserNotifications, +); + + +// Mark notification as read +router.patch( + "/user/:userId/notification/:notificationId/read", + authenticate, + authorizedAccountType(["admin", "management", "team_head", "user"]), + // checkPermission(PERMISSIONS.NOTIFICATION_READ), + notificationController.markAsRead, +); + + +module.exports = router; \ No newline at end of file diff --git a/app/routes/permission.routes.js b/app/routes/permission.routes.js new file mode 100644 index 0000000..96346d4 --- /dev/null +++ b/app/routes/permission.routes.js @@ -0,0 +1,145 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/routes/permission.routes.js + +const express = require("express"); +const router = express.Router(); +const permissionController = require("../controllers/permission.controller"); +const { authenticate } = require("../middleware/auth.middleware"); +const { authorizedAccountType } = require("../middleware/permission.middleware"); + +// Permission routes +router.post( + "/", + authenticate, + authorizedAccountType(["admin"]), + permissionController.createPermission +); + +router.post( + "/bulk", + authenticate, + authorizedAccountType(["admin"]), + permissionController.bulkCreatePermissions +); + +router.get( + "/", + authenticate, + permissionController.getAllPermissions +); + +router.get( + "/:permissionId", + authenticate, + permissionController.getPermissionById +); + +router.put( + "/:permissionId", + authenticate, + authorizedAccountType(["admin"]), + permissionController.updatePermission +); + +router.delete( + "/:permissionId", + authenticate, + authorizedAccountType(["admin"]), + permissionController.deletePermission +); + +// Role routes +router.post( + "/roles", + authenticate, + authorizedAccountType(["admin"]), + permissionController.createRole +); + +router.get( + "/roles", + authenticate, + permissionController.getAllRoles +); + +router.get( + "/roles/:roleId", + authenticate, + permissionController.getRoleById +); + +router.put( + "/roles/:roleId", + authenticate, + authorizedAccountType(["admin"]), + permissionController.updateRole +); + +router.delete( + "/roles/:roleId", + authenticate, + authorizedAccountType(["admin"]), + permissionController.deleteRole +); + +// Role-Permission assignment route +router.post( + "/roles/:roleId/permissions", + authenticate, + authorizedAccountType(["admin"]), + permissionController.createRolePermission +); + +// Bulk Role-Permission assignment +router.post( + "/roles/permissions/bulk", + authenticate, + authorizedAccountType(["admin"]), + permissionController.bulkCreateRolePermissions +); + +// User permissions route +router.get( + "/users/:userId/permissions", + authenticate, + permissionController.getUserPermissions +); + +router.post( + "/users/permissions", + authenticate, + authorizedAccountType(["admin"]), + permissionController.createUserPermission +); + +// Bulk User Permissions +router.post( + "/users/permissions/bulk", + authenticate, + authorizedAccountType(["admin"]), + permissionController.bulkCreateUserPermissions +); + +router.put( + "/users/permissions/:upId", + authenticate, + authorizedAccountType(["admin"]), + permissionController.updateUserPermission +); + +router.delete( + "/users/permissions/:upId", + authenticate, + authorizedAccountType(["admin"]), + permissionController.deleteUserPermission +); + +module.exports = router; diff --git a/app/routes/profile.routes.js b/app/routes/profile.routes.js new file mode 100644 index 0000000..9557ac9 --- /dev/null +++ b/app/routes/profile.routes.js @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/routes/profile.routes.js + +const express = require("express"); +const router = express.Router(); +const profileController = require("../controllers/profile.controller.js"); +const { authenticate } = require("../middleware/auth.middleware"); + +const { + authorizedAccountType, + checkPermission, +} = require("../middleware/permission.middleware"); +const PERMISSIONS = require("../constants/permissions"); + +router.post("/req-reset-password", profileController.requestPasswordReset); + +router.post("/reset-password", profileController.resetPassword); + +router.post( + "/change-password", + authenticate, + authorizedAccountType(["admin", "management", "team_head", "user"]), + profileController.changePassword, +); + +router.get( + "/avatar/:userId", + authenticate, + authorizedAccountType(["admin", "management", "team_head", "user"]), + profileController.getProfileAvatar, +); + +router.get( + "/background/:userId", + authenticate, + authorizedAccountType(["admin", "management", "team_head", "user"]), + profileController.getProfileBackgroundImage, +); + +module.exports = router; diff --git a/app/routes/upload.routes.js b/app/routes/upload.routes.js new file mode 100644 index 0000000..00c0c7f --- /dev/null +++ b/app/routes/upload.routes.js @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/routes/upload.routes.js + +const express = require("express"); +const router = express.Router(); +const uploadController = require("../controllers/upload.controller"); +const { uploadSingle } = require("../middleware/upload.middleware"); +const { + authenticate, +} = require("../middleware/auth.middleware"); + +const { authorizedAccountType, checkPermission } = require("../middleware/permission.middleware"); +const PERMISSIONS = require("../constants/permissions"); + +// Upload file +router.post( + "/", + authenticate, + authorizedAccountType(["admin", "staff"]), + uploadSingle("file"), + uploadController.uploadFile, +); + +// Get signed URL for a file +router.get( + "/signed-url/:id", + authenticate, + authorizedAccountType(["admin", "staff"]), + uploadController.getFileUrl, +); + +module.exports = router; \ No newline at end of file diff --git a/app/routes/user.routes.js b/app/routes/user.routes.js new file mode 100644 index 0000000..ae9535f --- /dev/null +++ b/app/routes/user.routes.js @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/routes/user.routes.js + +const express = require("express"); +const router = express.Router(); +const userController = require("../controllers/user.controller"); +const { + authenticate, +} = require("../middleware/auth.middleware"); + +const { authorizedAccountType, checkPermission } = require("../middleware/permission.middleware"); +const PERMISSIONS = require("../constants/permissions"); + + +router.post( + "/", + authenticate, + authorizedAccountType(["admin"]), + userController.createNewUser +); + +router.get( + "/", + authenticate, + authorizedAccountType(["admin"]), + userController.getAllUsers +); + +router.get( + "/:id", + authenticate, + authorizedAccountType(["admin", "management", "team_head", "user"]), + userController.getUserById +); + +router.patch( + "/:id", + authenticate, + authorizedAccountType(["admin", "management", "team_head", "user"]), + userController.updateUser +); + +router.delete( + "/:id", + authenticate, + authorizedAccountType(["admin"]), + userController.deleteUser +); + +module.exports = router; diff --git a/app/seeders/documentType.seeder.js b/app/seeders/documentType.seeder.js new file mode 100644 index 0000000..e022afe --- /dev/null +++ b/app/seeders/documentType.seeder.js @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/seeders/documentType.seeder.js + +const db = require("../models"); + +const seedDocumentTypes = async () => { + try { + const types = [ + { doc_type_name: "DOC_TYPE_HERE_01", description: "N/A" }, + { doc_type_name: "DOC_TYPE_HERE_02", description: "N/A" }, + ]; + + const existingDocuments = await db.DocumentType.findAll({ + attributes: ["doc_type_name"], + }); + + const existingNames = existingDocuments.map((doc) => doc.doc_type_name); + + const newDocuments = types.filter( + (dt) => !existingNames.includes(dt.doc_type_name) + ); + + if (newDocuments.length > 0) { + await db.DocumentType.bulkCreate(newDocuments); + + newDocuments.forEach((dt) => + console.log(`Document type inserted: ${dt.doc_type_name}`) + ); + } else { + console.log("No new document types to insert"); + } + } catch (error) { + console.error("Document type seeding failed:", error); + throw error; + } +}; + +module.exports = seedDocumentTypes; \ No newline at end of file diff --git a/app/seeders/index.js b/app/seeders/index.js new file mode 100644 index 0000000..dba545d --- /dev/null +++ b/app/seeders/index.js @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/seeders/index.js + +const db = require("../models"); + +// Import all seeders +const seedUsers = require("./user.seeder"); +const seedDocumentTypes = require("./documentType.seeder"); +const {log} = require("../utils/consoleLog.utill"); + + +const runSeeders = async () => { + try { + log("Starting database seeding..."); + + await seedUsers(); + await seedDocumentTypes(); + log("All seeders executed successfully"); + } catch (error) { + log("Seeding process failed:", error.message); + } finally { + await db.sequelize.close(); + process.exit(); + } +}; + +runSeeders(); \ No newline at end of file diff --git a/app/seeders/user.seeder.js b/app/seeders/user.seeder.js new file mode 100644 index 0000000..9c9a78c --- /dev/null +++ b/app/seeders/user.seeder.js @@ -0,0 +1,106 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/seeders/user.seeder.js + +const bcrypt = require("bcrypt"); +const { v4: uuidv4 } = require("uuid"); + +const db = require("../models"); + +const seedUsers = async () => { + const transaction = await db.sequelize.transaction(); + + try { + const users = [ + { + firstName: "System", + lastName: "Administrator", + email: "team@niolla.lk", + password: "Admin@123", + accountType: "admin", + role: "Administrator", + roleID: "admin", + department: "Administration", + profile: { + theme: "light", + notificationsEnabled: true, + dob: null, + phone_number: null, + }, + }, + ]; + + for (const userData of users) { + const existingUser = await db.User.findOne({ + where: { + email: userData.email, + }, + transaction, + }); + + if (existingUser) { + console.log(`User already exists: ${userData.email}`); + continue; + } + + const hashedPassword = await bcrypt.hash(userData.password, 12); + + const userId = `usr_${uuidv4()}`; + const profileId = `prf_${uuidv4()}`; + + const newUser = await db.User.create( + { + id: userId, + firstName: userData.firstName, + lastName: userData.lastName, + email: userData.email, + password: hashedPassword, + accountType: userData.accountType, + role: userData.role, + roleID: userData.roleID, + department: userData.department, + }, + { + transaction, + }, + ); + + await db.Profile.create( + { + profile_id: profileId, + user_id: newUser.id, + theme: userData.profile.theme, + notificationsEnabled: userData.profile.notificationsEnabled, + dob: userData.profile.dob, + phone_number: userData.profile.phone_number, + }, + { + transaction, + }, + ); + + console.log( + `User inserted: ${newUser.firstName} ${newUser.lastName} (${newUser.email})`, + ); + } + + await transaction.commit(); + + console.log("User seeding completed successfully"); + } catch (error) { + await transaction.rollback(); + + console.error("User seeding failed:", error); + + throw error; + } +}; + +module.exports = seedUsers; diff --git a/app/services/activity.service.js b/app/services/activity.service.js new file mode 100644 index 0000000..110d468 --- /dev/null +++ b/app/services/activity.service.js @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/services/activity.service.js + +const activityQueue = require("../queues/activity.queue"); + +const logActivity = async ({ + user, + description, + type = "ACTION", + module +}) => { + try { + await activityQueue.add("log-activity", { + user_id: user.id || user.user_id, + username: user.firstName, + activity_description: description, + module: module, + activity_type: type, + activity_time: new Date(), + activity_date: new Date().toISOString().split("T")[0] + }); + } catch (err) { + console.error("Queue push error:", err.message); + } +}; + +module.exports = { logActivity }; \ No newline at end of file diff --git a/app/services/permission.service.js b/app/services/permission.service.js new file mode 100644 index 0000000..c3ffe94 --- /dev/null +++ b/app/services/permission.service.js @@ -0,0 +1,71 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/services/permission.service.js + +const db = require("../models"); +const User = db.User; +const RolePermission = db.rolePermission; +const UserPermission = db.userPermission; + +const { + getCachedPermissions, + setCachedPermissions +} = require("../utils/cache.util"); + +const getEffectivePermissions = async (userId) => { + try { + const cached = await getCachedPermissions(userId); + if (cached) { + console.log("⚡ PERMISSION CACHE HIT"); + return cached; + } + + console.log("🐢 PERMISSION CACHE MISS"); + + const user = await User.findByPk(userId); + if (!user) return []; + + const rolePermissions = await RolePermission.findAll({ + where: { role_id: user.roleID }, + attributes: ["permission_id"] + }); + + const userPermissions = await UserPermission.findAll({ + where: { user_id: userId }, + attributes: ["permission_id"] + }); + + const map = {}; + + // role permissions + rolePermissions.forEach((rp) => { + map[rp.permission_id] = true; + }); + + // user overrides (just add) + userPermissions.forEach((up) => { + map[up.permission_id] = true; + }); + + const finalPermissions = Object.keys(map); + + console.log("FINAL PERMS:", finalPermissions); + + await setCachedPermissions(userId, finalPermissions); + + return finalPermissions; + + } catch (err) { + console.error("PERMISSION SERVICE ERROR:", err); + return []; + } +}; + +module.exports = { getEffectivePermissions }; \ No newline at end of file diff --git a/app/templates/documents/creditNote/creditNote.ejs b/app/templates/documents/creditNote/creditNote.ejs new file mode 100644 index 0000000..9b8605c --- /dev/null +++ b/app/templates/documents/creditNote/creditNote.ejs @@ -0,0 +1,372 @@ + + + + + + + + <% + const creditCompany = typeof company !== 'undefined' && company ? company : {}; + const creditHeader = typeof header !== 'undefined' && header ? header : {}; + const creditBillTo = typeof billTo !== 'undefined' && billTo ? billTo : {}; + const creditMeta = typeof meta !== 'undefined' && meta ? meta : {}; + const creditVessel = typeof vessel !== 'undefined' && vessel ? vessel : {}; + const creditOrderMeta = typeof orderMeta !== 'undefined' && orderMeta ? orderMeta : {}; + const creditItems = typeof items !== 'undefined' && Array.isArray(items) ? items : []; + const creditApprovedByLabel = typeof approvedByLabel !== 'undefined' && approvedByLabel ? approvedByLabel : 'Approved by GLMS Finance Dept.'; + const creditDepartmentLabel = typeof departmentLabel !== 'undefined' && departmentLabel ? departmentLabel : 'GLMS - Accounts Department'; + %> +
+
+
+
<%= creditCompany.name || 'GREEK-LANKA MARITIME SERVICES (PVT) LTD' %>
+
<%= creditCompany.addressLine1 || '' %>
+
<%= creditCompany.addressLine2 || '' %>
+
<%= creditCompany.addressLine3 || '' %>
+
<%= creditCompany.postal || '' %>
+
<%= creditCompany.contact || '' %>
+
<%= creditCompany.br || '' %>
+
<%= creditCompany.license || '' %>
+
+
+
+
GREEK
LANKA
+
We Serve the Ocean
+
+
+
+
+
+
CREDIT NOTE
+
+
+
+
+ +
<%= creditHeader.title || 'CREDIT NOTE' %>
+ +
+
+
Bill To : <%= creditBillTo.name || 'Master & Owner of MV TEAM VENTURES' %>
+
<%- (creditBillTo.details || '').replace(/\n/g, '
') || 'VRIDHI MARITIME SHIP MANAGEMENT & OPERATION LLC
Office No: 2004, The Prism Tower, Dubai' %>
+
+
+
CRN NO :<%= creditMeta.crnNo || 'N/A' %>
+
DATE :<%= creditMeta.date || 'N/A' %>
+
+
+ +
+
+
Vessel Name : <%= creditVessel.name || 'N/A' %>
+
Port : <%= creditVessel.port || 'N/A' %>
+
Name of Agent : <%= creditVessel.agent || 'N/A' %>
+
+
+
GRT : <%= creditVessel.grt || 'N/A' %>
+
IMO Number : <%= creditVessel.imo || 'N/A' %>
+
Port/Country : <%= creditVessel.portCountry || 'N/A' %>
+
+
+ +
+ + + + + + + + + <% if (creditItems.length > 0) { %> + <% creditItems.forEach((item, index) => { %> + + + + + <% }); %> + <% } %> + + + + + +
DESCRIPTIONAMOUNT
+ <%= item.description || item.desc || '' %> + <% if (item.note) { %> +
<%= item.note %>
+ <% } %> +
<%= (typeof item.amount !== 'undefined') ? item.amount : '' %>
Total Amount<%= total || totalAmount || 0 %>
+
+ +
+
+
+
+
<%= creditApprovedByLabel %>
+
+
+ + +
+ + \ No newline at end of file diff --git a/app/templates/documents/creditNote/creditNote.template.js b/app/templates/documents/creditNote/creditNote.template.js new file mode 100644 index 0000000..bd529b5 --- /dev/null +++ b/app/templates/documents/creditNote/creditNote.template.js @@ -0,0 +1,131 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/templates/documents/creditNote/creditNote.template.js + +const { log } = require("../../../utils/consoleLog.utill"); + +module.exports = async (data) => { + log("📋 Preparing Credit Note data for PDF..."); + + let payload = data; + + if (typeof payload === "string") { + try { + payload = JSON.parse(payload); + } catch (error) { + payload = {}; + } + } + + const source = payload.documentData || payload.document_data || payload.data || payload; + + const pick = (...keys) => { + for (const key of keys) { + const value = source?.[key]; + if (value !== undefined && value !== null && value !== "") { + return value; + } + } + + return ""; + }; + + // Normalize incoming fields and provide defaults matching the screenshot + const company = { + name: pick("companyName", "company_name") || "GREEK-LANKA MARITIME SERVICES (PVT) LTD", + addressLine1: pick("companyAddressLine1", "company_address_line1") || "No. 56/2, Dharmapala Mw, Kotte", + addressLine2: pick("companyAddressLine2", "company_address_line2") || "Sri Jayewardenepura", + addressLine3: pick("companyAddressLine3", "company_address_line3") || "Sri Lanka", + postal: pick("companyPostal", "company_postal") || "Postal Code : 10100", + contact: pick("companyContact", "company_contact") || "Tel:+94 11 2083206 / Mobile (24/7) : +94 777 232 271", + br: pick("companyBR", "company_br") || "BR No : PV 0022630", + license: pick("companyLicense", "company_license") || "License No : SA00317-2024", + logoUrl: pick("logoUrl", "logo_url") || + "https://res.cloudinary.com/dtthuvvir/image/upload/v1780748661/company_logo_transperent_bg_uullzw.png", + }; + + const header = { + title: pick("title") || "CREDIT NOTE", + }; + + const billTo = { + name: pick("billToName", "bill_to_name") || "Master & Owner of MV TEAM VENTURES", + details: pick("billToDetails", "bill_to_details") || "VRIDHI MARITIME SHIP MANAGEMENT & OPERATION LLC\nOffice No: 2004, The Prism Tower, Dubai", + }; + + const meta = { + crnNo: pick("crnNo", "crn_no", "creditNoteNumber", "credit_note_number", "reference_no") || "GLMS/COLOMBO/474/CRN01", + date: pick("date") || new Date().toLocaleDateString(), + }; + + const vessel = { + name: pick("vesselName", "vessel_name") || 'MV "TEAM VENTURES"', + grt: pick("grt", "GRT") || "25,543 MT", + port: pick("port", "port_name") || "REPAIRS AT COLOMBO DOCK YARD", + imo: pick("imo", "imo_number", "imoNumber") || "9339765", + agent: pick("nameOfAgent", "name_of_agent", "agent") || "", + portCountry: pick("portCountry", "port_country") || "COLOMBO / SRI LANKA", + }; + + const orderMeta = { + orderDate: pick("orderDate", "order_date") || meta.date, + orderNo: pick("orderNo", "order_no") || "", + portReference: pick("portReference", "port_reference") || "", + }; + + // Items: array of { description, amount } + const items = (Array.isArray(source.items) ? source.items : []).map((it) => ({ + description: it.description || it.desc || it.item_name || '', + amount: Number(it.amount || it.value || it.total || it.totalAmount || 0), + quantity: it.quantity ?? it.qty ?? '', + note: it.note || it.notes || '', + })); + + const computedTotal = items.reduce((s, i) => s + (i.amount || 0), 0); + + const templateData = { + company, + header, + billTo, + meta, + vessel, + orderMeta, + items, + totalAmount: Number(pick("totalAmount", "total") || computedTotal), + approvedBy: pick("approvedBy", "approved_by") || 'GLMS Finance Dept.', + approvedByLabel: pick("approvedByLabel", "approved_by_label") || 'Approved by GLMS Finance Dept.', + departmentLabel: pick("departmentLabel", "department_label") || 'GLMS - Accounts Department', + }; + + // Top-level conveniences for templates and pdfRenderer + templateData.logoUrl = company.logoUrl; + templateData.creditNoteNumber = templateData.meta.crnNo; + templateData.date = templateData.meta.date; + templateData.billToName = templateData.billTo.name; + templateData.billToDetails = templateData.billTo.details; + templateData.vessel = templateData.vessel; + // Provide invoiceNumber if present + templateData.invoiceNumber = pick("invoiceNumber", "invoice_number", "invoice_no") || ''; + + // Provide subtotal/tax/discount/total for EJS summary table compatibility + templateData.subtotal = Number(pick("subtotal", "sub_total") || computedTotal); + templateData.tax = Number(pick("tax") || 0); + templateData.discount = Number(pick("discount") || 0); + templateData.total = Number(pick("totalAmount", "total") || templateData.subtotal + templateData.tax - templateData.discount); + // Provide reason / notes + templateData.reason = pick("reason", "remarks", "notes") || ''; + // Provide paymentTerms and notes for EJS + templateData.paymentTerms = pick("paymentTerms", "payment_terms") || ''; + templateData.notes = pick("notes", "note") || ''; + + log("✅ Credit Note data prepared", JSON.stringify(templateData)); + + return templateData; +}; \ No newline at end of file diff --git a/app/templates/documents/customDocument/customDocument.ejs b/app/templates/documents/customDocument/customDocument.ejs new file mode 100644 index 0000000..683a1b6 --- /dev/null +++ b/app/templates/documents/customDocument/customDocument.ejs @@ -0,0 +1,266 @@ + + + + + + + + <% + const customTitle = typeof title !== 'undefined' && title ? title : 'CUSTOM DOCUMENT'; + const customDate = typeof date !== 'undefined' && date ? date : 'N/A'; + const customTelePhone = typeof telePhone !== 'undefined' && telePhone ? telePhone : ''; + const customEmail = typeof emailAddress !== 'undefined' && emailAddress ? emailAddress : ''; + const customOfficeAddressLine1 = typeof officeAddressLine1 !== 'undefined' && officeAddressLine1 ? officeAddressLine1 : ''; + const customOfficeAddressLine2 = typeof officeAddressLine2 !== 'undefined' && officeAddressLine2 ? officeAddressLine2 : ''; + const customOfficeAddressLine3 = typeof officeAddressLine3 !== 'undefined' && officeAddressLine3 ? officeAddressLine3 : ''; + const customDirectorOfCustoms = typeof directorOfCustoms !== 'undefined' && directorOfCustoms ? directorOfCustoms : ''; + const customVehicleNo = typeof vehicleNo !== 'undefined' && vehicleNo ? vehicleNo : ''; + const customPermitNo = typeof permitNo !== 'undefined' && permitNo ? permitNo : ''; + const customChiefSecurityOfficer = typeof chiefSecurityOfficer !== 'undefined' && chiefSecurityOfficer ? chiefSecurityOfficer : ''; + const customExportGate = typeof exportGate !== 'undefined' && exportGate ? exportGate : ''; + const customSlpa = typeof slpa !== 'undefined' && slpa ? slpa : ''; + const customGateNo = typeof gateNo !== 'undefined' && gateNo ? gateNo : ''; + const customPermissionText = typeof permissionText !== 'undefined' && permissionText ? permissionText : ''; + const customCompanyName = typeof companyName !== 'undefined' && companyName ? companyName : 'Oceanic Maritime Solutions (Pvt) Ltd'; + const customSubtitle = typeof subtitle !== 'undefined' && subtitle ? subtitle : 'LICENSED SHIPCHANDLERS'; + const customSectionCode = typeof sectionCode !== 'undefined' && sectionCode ? sectionCode : 'SC/FORM/06'; + const customDateLabel = typeof dateLabel !== 'undefined' && dateLabel ? dateLabel : 'DATE :'; + const customItems = typeof items !== 'undefined' && Array.isArray(items) ? items : []; + const rows = Math.max(7, customItems.length); + %> +
+
+
+
Tele : <%= customTelePhone || '' %>
+
E-mail Address : <%= customEmail || '' %>
+
+
+
<%= customOfficeAddressLine1 %>
+
<%= customOfficeAddressLine2 %>
+
<%= customOfficeAddressLine3 %>
+
+
+ +
+
+
The Director of Customs
<%= customDirectorOfCustoms %>
+
Export Gate
Colombo 1.
+
Vehicle No. / Permit No.
<%= customVehicleNo %> / <%= customPermitNo %>
+
+
+
Chief Security Officer
<%= customChiefSecurityOfficer %>
+
SLPA
<%= customSlpa %>
+
Gate No.
<%= customGateNo %>
+
+
+ +
<%= customPermissionText %>
+
............................................................
+ +
<%= customCompanyName %>
+
<%= customSubtitle %>
+ +
+
<%= customDateLabel %> <%= customDate %>
+
+ +
<%= customSectionCode %>
+ + + + + + + + + + + + + + + <% for (let index = 0; index < rows; index++) { %> + <% const item = customItems[index] || {}; %> + + + + + + + + + + <% } %> + +
NOQTYUNITDESCRIPTIONRATETOTALConfirm
Order Rate
<%= index + 1 %><%= item.quantity || item.qty || '' %><%= item.unit || '' %><%= item.description || item.item_name || item.name || '' %><%= item.rate || item.unit_price || '-' %><%= item.total || item.amount || '-' %><%= item.confirmOrderRate || item.confirm_order_rate || '' %>
+
+ + diff --git a/app/templates/documents/customDocument/customDocument.template.js b/app/templates/documents/customDocument/customDocument.template.js new file mode 100644 index 0000000..20d658f --- /dev/null +++ b/app/templates/documents/customDocument/customDocument.template.js @@ -0,0 +1,81 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/templates/documents/customDocument/customDocument.template.js + +const { log } = require("../../../utils/consoleLog.utill"); + +module.exports = async (data) => { + log("📋 Preparing Custom Document data for PDF..."); + + let payload = data; + + if (typeof payload === "string") { + try { + payload = JSON.parse(payload); + } catch (error) { + payload = {}; + } + } + + const source = payload.documentData || payload.document_data || payload.data || payload; + + const pick = (...keys) => { + for (const key of keys) { + const value = source?.[key]; + if (value !== undefined && value !== null && value !== "") { + return value; + } + } + + return ""; + }; + + const templateData = { + logoUrl: + pick("logoUrl", "logo_url") || + "https://res.cloudinary.com/dtthuvvir/image/upload/v1780748661/company_logo_transperent_bg_uullzw.png", + + title: pick("title") || "CUSTOM DOCUMENT", + documentNumber: pick("documentNumber", "document_number") || "", + date: pick("date") || new Date().toLocaleDateString(), + + telePhone: pick("telePhone", "telephone", "phone", "tel") || "", + emailAddress: pick("emailAddress", "email", "e_mail") || "", + + officeAddressLine1: pick("officeAddressLine1", "office_address_line1") || "", + officeAddressLine2: pick("officeAddressLine2", "office_address_line2") || "", + officeAddressLine3: pick("officeAddressLine3", "office_address_line3") || "", + + directorOfCustoms: pick("directorOfCustoms", "director_of_customs") || "", + vehicleNo: pick("vehicleNo", "vehicle_no") || "", + permitNo: pick("permitNo", "permit_no") || "", + chiefSecurityOfficer: pick("chiefSecurityOfficer", "chief_security_officer") || "", + exportGate: pick("exportGate", "export_gate") || "", + slpa: pick("slpa") || "", + gateNo: pick("gateNo", "gate_no") || "", + permissionText: pick("permissionText", "permission_text") || "Please grant permission to pass these Customs entry papers to supply goods to the vessel", + + companyName: pick("companyName", "company_name") || "Oceanic Maritime Solutions (Pvt) Ltd", + subtitle: pick("subtitle") || "LICENSED SHIPCHANDLERS", + sectionCode: pick("sectionCode", "section_code") || "SC/FORM/06", + dateLabel: pick("dateLabel", "date_label") || "DATE :", + + items: Array.isArray(source.items) ? source.items : [], + itemHeaders: + pick("itemHeaders", "item_headers") || + ["NO", "QTY", "UNIT", "DESCRIPTION", "RATE", "TOTAL", "Confirm Order Rate"], + + footerText: pick("footerText", "footer_text") || "", + }; + + log("✅ Custom Document data prepared"); + + return templateData; +}; diff --git a/app/templates/documents/deliveryNote/deliveryNote.ejs b/app/templates/documents/deliveryNote/deliveryNote.ejs new file mode 100644 index 0000000..4e09f72 --- /dev/null +++ b/app/templates/documents/deliveryNote/deliveryNote.ejs @@ -0,0 +1,189 @@ + + + + + + + +
+
+ <% if (logoBase64) { %> + Company Logo + <% } %> +
+ +
<%= title || 'DELIVERY NOTE' %>
+ +
+
+
Bill To: <%= billToName || 'N/A' %>
+
<%= billToAddress || 'N/A' %>
+
+
+
Supply Date: <%= supplyDate || 'N/A' %>
+
PO Number: <%= poNumber || 'N/A' %>
+
+
+ + + + + + + + + + + + + <% const rows = Math.max(10, (items && items.length) || 0); %> + <% for (let index = 0; index < rows; index++) { %> + <% const item = items && items[index] ? items[index] : {}; %> + + + + + + + + <% } %> + +
No.DescriptionRemarksUnitQuantity
<%= index + 1 %><%= item.description || item.item_name || item.name || '' %><%= item.remarks || '' %><%= item.unit || '' %><%= item.quantity || item.qty || '' %>
+
+ + \ No newline at end of file diff --git a/app/templates/documents/deliveryNote/deliveryNote.template.js b/app/templates/documents/deliveryNote/deliveryNote.template.js new file mode 100644 index 0000000..1288427 --- /dev/null +++ b/app/templates/documents/deliveryNote/deliveryNote.template.js @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/templates/documents/deliveryNote/deliveryNote.template.js + +const { log } = require("../../../utils/consoleLog.utill"); + +module.exports = async (data) => { + log("📋 Preparing Delivery Note data for PDF..."); + + const templateData = { + logoUrl: + data.logoUrl || + "https://res.cloudinary.com/dtthuvvir/image/upload/v1780748661/company_logo_transperent_bg_uullzw.png", + title: data.title || "DELIVERY NOTE", + referenceNumber: + data.referenceNumber || data.reference_no || data.deliveryNoteNumber || data.delivery_note_number || "N/A", + date: data.date || new Date().toLocaleDateString(), + billToName: data.billToName || data.bill_to_name || "N/A", + billToAddress: data.billToAddress || data.bill_to_address || "N/A", + supplyDate: data.supplyDate || data.supply_date || data.date || new Date().toLocaleDateString(), + poNumber: data.poNumber || data.po_number || "N/A", + items: Array.isArray(data.items) ? data.items : [], + }; + + log("✅ Delivery Note data prepared"); + + return templateData; +}; \ No newline at end of file diff --git a/app/templates/documents/dispatchNote/dispatchNote.ejs b/app/templates/documents/dispatchNote/dispatchNote.ejs new file mode 100644 index 0000000..badf3d2 --- /dev/null +++ b/app/templates/documents/dispatchNote/dispatchNote.ejs @@ -0,0 +1,345 @@ + + + + + + + + <% + const dispatchTitle = typeof title !== 'undefined' && title ? title : 'DISPATCH NOTE'; + const dispatchReferenceNumber = typeof referenceNumber !== 'undefined' && referenceNumber ? referenceNumber : 'N/A'; + const dispatchDate = typeof date !== 'undefined' && date ? date : 'N/A'; + const dispatchVessel = typeof vessel !== 'undefined' && vessel ? vessel : 'N/A'; + const dispatchCaptain = typeof captain !== 'undefined' && captain ? captain : 'N/A'; + const dispatchCook = typeof cook !== 'undefined' && cook ? cook : 'N/A'; + const dispatchAgent = typeof agent !== 'undefined' && agent ? agent : 'N/A'; + const dispatchDetails = typeof details !== 'undefined' && details ? details : ''; + const dispatchPlaceOfDelivery = typeof placeOfDelivery !== 'undefined' && placeOfDelivery ? placeOfDelivery : 'N/A'; + const dispatchEta = typeof eta !== 'undefined' && eta ? eta : 'N/A'; + const dispatchEtd = typeof etd !== 'undefined' && etd ? etd : 'N/A'; + const dispatchNumberOfCrew = typeof numberOfCrew !== 'undefined' && numberOfCrew ? numberOfCrew : 'N/A'; + const dispatchNextMainOrderIn = typeof nextMainOrderIn !== 'undefined' && nextMainOrderIn ? nextMainOrderIn : 'N/A'; + const dispatchPort = typeof port !== 'undefined' && port ? port : 'N/A'; + const dispatchCompany = typeof company !== 'undefined' && company ? company : 'N/A'; + const dispatchFileNo = typeof fileNo !== 'undefined' && fileNo ? fileNo : 'N/A'; + const dispatchItems = typeof items !== 'undefined' && Array.isArray(items) ? items : []; + %> +
+
<%= dispatchTitle %>
+ +
+
+
+
Vessel:
<%= dispatchVessel %>
+
Captain:
<%= dispatchCaptain %>
+
Cook:
<%= dispatchCook %>
+
Agent:
<%= dispatchAgent %>
+
Details:
<%= dispatchDetails %>
+
+
SC/FORM/04
+
+
+
+
Place of delivery:
<%= dispatchPlaceOfDelivery %>
+
ETA:
<%= dispatchEta %>
+
ETD:
<%= dispatchEtd %>
+
Number of crew:
<%= dispatchNumberOfCrew %>
+
Next main order in:
<%= dispatchNextMainOrderIn %>
+
+
+
+
+
Port
<%= dispatchPort %>
+
Date
<%= dispatchDate %>
+
Company
<%= dispatchCompany %>
+
File No
<%= dispatchFileNo %>
+
+
+
+ +
Item Receiving Authorization and Vessel Delivery Oder details
+ +
+ + + + + + + + + + + + + + + + + + + + + <% const rows = Math.max(5, dispatchItems.length); %> + <% for (let index = 0; index < rows; index++) { %> + <% const item = dispatchItems[index] || {}; %> + + + + + + + + + + + + + + + + + <% } %> + +
NoProductQtyUnitSUPPPO NORcvd QtyIssued QtyExp DateRcvd TimeDateRejectsFat Con.Remark
<%= index + 1 %><%= item.product || item.description || item.item_name || item.name || '' %><%= item.qty || item.quantity || '' %><%= item.unit || '' %><%= item.supp || item.supplier || '' %><%= item.poNo || item.po_number || item.poNumber || '' %><%= item.receivedQty || item.rcvdQty || '' %><%= item.issuedQty || '' %><%= item.expDate || item.exp_date || '' %><%= item.receivedTime || item.rcvdTime || '' %><%= item.date || '' %><%= item.rejects || '' %><%= item.fatCon || item.fat_con || '' %><%= item.remark || item.remarks || '' %>
+
+ + <% if (showSignatures) { %> +
+
+ Prepared By +
___________________
+
<%= preparedBy || 'Name' %>
+
+
+ Approved By +
___________________
+
<%= approvedBy || 'Name' %>
+
+
+ <% } %> + + +
+ + diff --git a/app/templates/documents/dispatchNote/dispatchNote.template.js b/app/templates/documents/dispatchNote/dispatchNote.template.js new file mode 100644 index 0000000..74ac225 --- /dev/null +++ b/app/templates/documents/dispatchNote/dispatchNote.template.js @@ -0,0 +1,74 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/templates/documents/dispatchNote/dispatchNote.template.js + +const { log } = require("../../../utils/consoleLog.utill"); + +module.exports = async (data) => { + log("📋 Preparing Dispatch Note data for PDF..."); + + let payload = data; + + if (typeof payload === "string") { + try { + payload = JSON.parse(payload); + } catch (error) { + payload = {}; + } + } + + const source = payload.documentData || payload.document_data || payload.data || payload; + + const pick = (...keys) => { + for (const key of keys) { + const value = source?.[key]; + if (value !== undefined && value !== null && value !== "") { + return value; + } + } + + return ""; + }; + + const templateData = { + logoUrl: + pick("logoUrl", "logo_url") || + "https://res.cloudinary.com/dtthuvvir/image/upload/v1780748661/company_logo_transperent_bg_uullzw.png", + + title: pick("title") || "DISPATCH NOTE", + referenceNumber: pick("referenceNumber", "reference_number", "dnNo", "dn_no") || "N/A", + date: pick("date") || new Date().toLocaleDateString(), + + vessel: pick("vessel", "vesselName", "vessel_name") || "", + captain: pick("captain", "captainName", "captain_name") || "", + cook: pick("cook", "cookName", "cook_name") || "", + agent: pick("agent", "agentName", "agent_name") || "", + details: pick("details", "remarks", "note") || "", + + placeOfDelivery: pick("placeOfDelivery", "place_of_delivery") || "", + eta: pick("eta") || "", + etd: pick("etd") || "", + numberOfCrew: pick("numberOfCrew", "number_of_crew", "crewCount", "crew_count") || "", + nextMainOrderIn: pick("nextMainOrderIn", "next_main_order_in") || "", + port: pick("port", "portName", "port_name") || "", + company: pick("company", "companyName", "company_name") || "", + fileNo: pick("fileNo", "file_no") || "", + + items: Array.isArray(source.items) ? source.items : [], + + showSignatures: pick("showSignatures") !== false, + preparedBy: pick("preparedBy", "prepared_by") || "", + approvedBy: pick("approvedBy", "approved_by") || "", + }; + + log("✅ Dispatch Note data prepared"); + + return templateData; +}; diff --git a/app/templates/documents/invoice/invoice.ejs b/app/templates/documents/invoice/invoice.ejs new file mode 100644 index 0000000..7999221 --- /dev/null +++ b/app/templates/documents/invoice/invoice.ejs @@ -0,0 +1,308 @@ + + + + + + + + <% + const invoiceTitle = typeof title !== 'undefined' && title ? title : 'INVOICE'; + const invoiceJobReference = typeof jobReference !== 'undefined' && jobReference ? jobReference : 'N/A'; + const invoicePoNumber = typeof poNumber !== 'undefined' && poNumber ? poNumber : 'N/A'; + const invoiceDate = typeof date !== 'undefined' && date ? date : 'N/A'; + const invoicePageLabel = typeof pageLabel !== 'undefined' && pageLabel ? pageLabel : 'Page 01 of 02'; + const invoiceBillToName = typeof billToName !== 'undefined' && billToName ? billToName : 'Client Name'; + const invoiceBillToAddress = typeof billToAddress !== 'undefined' && billToAddress ? billToAddress : 'Address'; + const invoiceVesselName = typeof vesselName !== 'undefined' && vesselName ? vesselName : 'N/A'; + const invoicePurposeOfCall = typeof purposeOfCall !== 'undefined' && purposeOfCall ? purposeOfCall : 'N/A'; + const invoiceSupplyDate = typeof supplyDate !== 'undefined' && supplyDate ? supplyDate : 'N/A'; + const invoiceGrt = typeof grt !== 'undefined' && grt ? grt : 'N/A'; + const invoiceImoNumber = typeof imoNumber !== 'undefined' && imoNumber ? imoNumber : 'N/A'; + const invoicePortCountry = typeof portCountry !== 'undefined' && portCountry ? portCountry : 'N/A'; + const invoiceItems = typeof items !== 'undefined' && Array.isArray(items) ? items : []; + %> +
+
+ <% if (logoBase64) { %> + Company Logo + <% } %> +
+ +
<%= invoiceTitle %>
+ +
+
+
+
Bill To: <%= invoiceBillToName %>
+
<%= invoiceBillToAddress %>
+
+
+
+
Job Reference : <%= invoiceJobReference %>
+
PO Number : <%= invoicePoNumber %>
+
Date : <%= invoiceDate %>
+
<%= invoicePageLabel %>
+
+
+ +
+
+
Vessel Name : <%= invoiceVesselName %>
+
Purpose of Call : <%= invoicePurposeOfCall %>
+
Supply Date : <%= invoiceSupplyDate %>
+
+
+
GRT : <%= invoiceGrt %>
+
IMO Number : <%= invoiceImoNumber %>
+
Port/Country : <%= invoicePortCountry %>
+
+
+ +
+ + + + + + + + + + + + + + <% const rows = Math.max(10, invoiceItems.length); %> + <% for (let index = 0; index < rows; index++) { %> + <% const item = invoiceItems[index] || {}; %> + <% const quantity = Number(item.quantity || item.qty || 0); %> + <% const unitPrice = Number(item.unit_price || item.unitPrice || 0); %> + + + + + + + + + + <% } %> + +
No.DescriptionRemarksQtyUnitUnit Price (USD)Final Amount (USD)
<%= index + 1 %><%= item.description || item.item_name || item.name || '' %><%= item.remarks || '' %><%= item.quantity || item.qty || '' %><%= item.unit || '' %><%= item.unit_price || item.unitPrice || '' %><%= quantity && unitPrice ? quantity * unitPrice : (item.total || item.amount || '') %>
+
+
+ + diff --git a/app/templates/documents/invoice/invoice.template.js b/app/templates/documents/invoice/invoice.template.js new file mode 100644 index 0000000..c54f825 --- /dev/null +++ b/app/templates/documents/invoice/invoice.template.js @@ -0,0 +1,111 @@ + +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/templates/documents/invoice/invoice.template.js + +const { log } = require("../../../utils/consoleLog.utill"); + +module.exports = async (data) => { + log("📋 Preparing Invoice data for PDF..."); + + let payload = data; + + if (typeof payload === "string") { + try { + payload = JSON.parse(payload); + } catch (error) { + payload = {}; + } + } + + const source = + payload.documentData || + payload.document_data || + payload.data || + payload; + + const deepFind = (node, keys, visited = new Set()) => { + if (!node || typeof node !== "object" || visited.has(node)) { + return ""; + } + + visited.add(node); + + for (const key of keys) { + const value = node[key]; + if (value !== undefined && value !== null && value !== "") { + return value; + } + } + + for (const child of Object.values(node)) { + const found = deepFind(child, keys, visited); + if (found !== "") { + return found; + } + } + + return ""; + }; + + const pick = (...keys) => { + const direct = deepFind(source, keys); + + if (direct !== "") { + return direct; + } + + return deepFind(payload, keys); + }; + + const templateData = { + logoUrl: + pick("logoUrl", "logo_url") || + "https://res.cloudinary.com/dtthuvvir/image/upload/v1780748661/company_logo_transperent_bg_uullzw.png", + + title: pick("title") || "INVOICE", + jobReference: + pick( + "jobReference", + "job_reference", + "referenceNumber", + "reference_no", + "jobRef", + "job_ref", + ) || "", + poNumber: pick("poNumber", "po_number", "poNo", "po_no") || "", + date: pick("date") || new Date().toLocaleDateString(), + pageLabel: pick("pageLabel", "page_label") || "Page 01 of 02", + + billToName: pick("billToName", "bill_to_name") || "Client Name", + billToAddress: pick("billToAddress", "bill_to_address") || "Address", + + vesselName: pick("vesselName", "vessel_name") || "", + purposeOfCall: pick("purposeOfCall", "purpose_of_call") || "", + supplyDate: pick("supplyDate", "supply_date") || "", + grt: pick("grt", "GRT") || "", + imoNumber: pick("imoNumber", "imo_number", "imo") || "", + portCountry: pick("portCountry", "port_country") || "", + + items: Array.isArray(source.items) ? source.items : [], + + subtotal: Number(pick("subtotal", "sub_total") || 0), + tax: Number(pick("tax") || 0), + discount: Number(pick("discount") || 0), + total: Number(pick("total") || 0), + + paymentTerms: pick("paymentTerms", "payment_terms") || "", + notes: pick("notes") || "", + }; + + log("✅ Invoice data prepared"); + + return templateData; +}; \ No newline at end of file diff --git a/app/templates/documents/invoice/style.css b/app/templates/documents/invoice/style.css new file mode 100644 index 0000000..7733924 --- /dev/null +++ b/app/templates/documents/invoice/style.css @@ -0,0 +1,23 @@ +body { + font-family: Arial; + padding: 20px; +} + +h1 { + text-align: center; +} + +table { + width: 100%; + border-collapse: collapse; +} + +td, +th { + border: 1px solid #ddd; + padding: 8px; +} + +th { + background: #f4f4f4; +} \ No newline at end of file diff --git a/app/templates/documents/invoice/template.html b/app/templates/documents/invoice/template.html new file mode 100644 index 0000000..17143a2 --- /dev/null +++ b/app/templates/documents/invoice/template.html @@ -0,0 +1,24 @@ + + + + + +

PreCost Estimate

+ +

Customer: {{customerName}}

+ + + + + + + + + + + {{items}} +
ItemDescriptionQtyUnit PriceTotal
+ + \ No newline at end of file diff --git a/app/templates/documents/po/po.ejs b/app/templates/documents/po/po.ejs new file mode 100644 index 0000000..343703d --- /dev/null +++ b/app/templates/documents/po/po.ejs @@ -0,0 +1,285 @@ + + + + + + + +
+ +
+
+ <% if (logoBase64) { %> + Company Logo + <% } %> +
+
+
PURCHASE ORDER
+
+
+ PO #: <%= poNumber || 'N/A' %>
+ Date: <%= date || 'N/A' %> +
+
+ + +
+
+
+ VENDOR: +
+
+ <%= vendorName || 'Vendor Name' %>
+ <%= vendorAddress || 'Address' %> +
+
+
+
+ DELIVERY DATE: + <%= deliveryDate || 'N/A' %> +
+
+ PAYMENT TERMS: + <%= paymentTerms || 'Net 30' %> +
+
+ VESSEL: + <%= vesselName || 'N/A' %> +
+
+
+ + +
+ + + + + + + + + + + + + <% if (items && items.length > 0) { %> + <% items.forEach((item, index) => { %> + + + + + + + + + <% }); %> + <% } %> + +
ITEM NODESCRIPTIONQTYUNITUNIT PRICETOTAL
<%= index + 1 %><%= item.description || item.item_name || '' %><%= item.quantity || 0 %><%= item.unit || '' %><%= item.unit_price || 0 %><%= (item.quantity || 0) * (item.unit_price || 0) %>
+
+ + +
+ + + + + + + + + + + + + + + + + +
SUB TOTAL<%= subtotal || 0 %>
TAX (%)<%= tax || 0 %>
SHIPPING<%= shipping || 0 %>
TOTAL<%= total || 0 %>
+
+ + + <% if (paymentTermsDetails || notes) { %> +
+ <% if (paymentTermsDetails) { %> +

TERMS & CONDITIONS

+

<%= paymentTermsDetails %>

+ <% } %> + <% if (notes) { %> +

SPECIAL INSTRUCTIONS

+

<%= notes %>

+ <% } %> +
+ <% } %> + + + +
+ + diff --git a/app/templates/documents/po/po.template.js b/app/templates/documents/po/po.template.js new file mode 100644 index 0000000..95f6b5f --- /dev/null +++ b/app/templates/documents/po/po.template.js @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/templates/documents/po/po.template.js + +const { log } = require("../../../utils/consoleLog.utill"); + +module.exports = async (data) => { + log("📋 Preparing Purchase Order data for PDF..."); + + const templateData = { + logoUrl: "https://res.cloudinary.com/dtthuvvir/image/upload/v1780748661/company_logo_transperent_bg_uullzw.png", + + poNumber: data.poNumber || data.po_number || "PO-001", + date: data.date || new Date().toLocaleDateString(), + + vendorName: data.vendorName || data.vendor_name || "Vendor Name", + vendorAddress: data.vendorAddress || data.vendor_address || "Address", + + deliveryDate: data.deliveryDate || data.delivery_date || "N/A", + paymentTerms: data.paymentTerms || data.payment_terms || "Net 30", + vesselName: data.vesselName || data.vessel_name || "N/A", + + items: data.items || [], + + subtotal: data.subtotal || data.sub_total || 0, + tax: data.tax || 0, + shipping: data.shipping || 0, + total: data.total || 0, + + paymentTermsDetails: data.paymentTermsDetails || data.payment_terms_details || "", + notes: data.notes || "", + }; + + log("✅ Purchase Order data prepared"); + + return templateData; +}; diff --git a/app/templates/documents/precost/preCost.template.js b/app/templates/documents/precost/preCost.template.js new file mode 100644 index 0000000..b601221 --- /dev/null +++ b/app/templates/documents/precost/preCost.template.js @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/templates/documents/precost/preCost.template.js + +const { log } = require("../../../utils/consoleLog.utill"); + +module.exports = async (data) => { + log("📋 Preparing PreCost data for PDF..."); + log("📥 Incoming data:", JSON.stringify(data, null, 2)); + + const templateData = { + logoUrl: + "https://res.cloudinary.com/dtthuvvir/image/upload/v1780748661/company_logo_transperent_bg_uullzw.png", + + vesselName: data.vessel_name || "MV Vessel", + + supplierName: data.supplyPort || "N/A", + clientRfqNum: data.clientRfqNum || "N/A", + omsRfqNum: data.omsRfqNum || "N/A", + date: data.date || new Date().toLocaleDateString(), + + items: data.items || [], + + subtotal: data.sub_total || 0, + discount: data.discount || 0, + additionalCharges: data.additionalCharges || 0, + fullTotal: data.total_cost || data.total_cost_usd || (data.sub_total - data.discount + data.additionalCharges) || 0, + }; + + log("Data:", data); + log("fullTotal calculation:", { + total_cost: data.total_cost, + total_cost_usd: data.total_cost_usd, + calculated: data.sub_total - data.discount + data.additionalCharges, + final: templateData.fullTotal, + }); + + log("✅ PreCost data prepared:", JSON.stringify(templateData, null, 2)); + + return templateData; +}; diff --git a/app/templates/documents/precost/precost.ejs b/app/templates/documents/precost/precost.ejs new file mode 100644 index 0000000..71da786 --- /dev/null +++ b/app/templates/documents/precost/precost.ejs @@ -0,0 +1,300 @@ + + + + + + + + + +
+ +
+
+ <% if (logoBase64) { %> + Company Logo + <% } %> +
+
PRICE QUOTATION
+
+ + +
+
+
+ VESSEL NAME : + + <%= vesselName %> + +
+
+ SUPPLY PORT : + + <%= supplierName || 'Supplier Name' %> + +
+
+
+
+ CLIENT RFQ : + + <%= clientRfqNum || 'RFQ-001' %> + +
+
+ OMS RFQ : + + <%= omsRfqNum || 'RFQ-001' %> + +
+
+ DATE : + + <%= date || 'Current Date' %> + +
+
+
+ + +
+ + + + + + + + + + + + + + <% if (items && items.length> 0) { %> + <% items.forEach((item, index)=> { %> + + + + + + + + + + <% }); %> + <% } %> + +
ITEM NODESCRIPTIONREMARKSQTYUNITUNIT PRICETOTAL PRICE
+ <%= index + 1 %> + + <%= item.item_name || '' %> + + <%= item.customer_remark || '' %> + + <%= item.quantity || 0 %> + + <%= item.unit || '' %> + + <%= item.unit_price || 0 %> + + <%= (item.quantity || 0) * (item.unit_price || 0) %> +
+
+ + +
+ + + + + + + <% if (discount && Number(discount) !==0) { %> + + + + + <% } %> + + <% if (additionalCharges && Number(additionalCharges) !==0) { %> + + + + + <% } %> + + + + + +
SUB TOTAL + <%= subtotal || 0 %> +
DISCOUNT (%) + <%= discount %> +
ADDITIONAL CHARGES
(CUSTOM/HANDLING/TRANSPORT)
+ <%= additionalCharges %> +
GRAND TOTAL + <%= fullTotal || 0 %> +
+
+ + + +
+ + + \ No newline at end of file diff --git a/app/templates/documents/precost/style.css b/app/templates/documents/precost/style.css new file mode 100644 index 0000000..9a8f096 --- /dev/null +++ b/app/templates/documents/precost/style.css @@ -0,0 +1,527 @@ + * { + margin: 0; + padding: 0; + box-sizing: border-box; + } + + body { + background: white; + display: flex; + justify-content: center; + align-items: center; + padding: 2rem 1rem; + font-family: 'Segoe UI', 'Roboto', 'Helvetica Neue', sans-serif; + } + + + /* actual table style — clean, border-collapse */ + .po-table { + width: 100%; + border-collapse: collapse; + font-size: 0.85rem; + font-family: 'Segoe UI', system-ui, 'Calibri', sans-serif; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.02); + } + + /* header sections & company info area (using table-like but not inside main table) */ + .header-section { + margin-bottom: 1.8rem; + text-align: center; + } + + .po-title { + font-size: 2rem; + font-weight: 700; + letter-spacing: 1px; + color: #257339; + border-top: 2px solid #257339; + border-bottom: 2px solid #257339; + padding: 1rem; + margin-bottom: 1.5rem; + margin-top: 1.5rem; + font-family: 'Inter', 'Segoe UI', sans-serif; + } + + /* two column info grid (company, supplier, dates etc) */ + .info-grid { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + gap: 1.2rem; + margin-bottom: 2rem; + background: #e1f3db; + padding: 1rem 1.2rem; + border: 1px solid #e2edf2; + } + + .info-left { + flex: 2; + min-width: 240px; + } + + .info-right { + flex: 1.2; + min-width: 200px; + } + + .info-row { + display: flex; + align-items: baseline; + flex-wrap: wrap; + margin-bottom: 12px; + font-size: 0.9rem; + } + + .info-label { + font-weight: 700; + width: 140px; + color: #257339; + letter-spacing: 0.3px; + } + + .info-value { + font-weight: 500; + color: #1f2d3d; + border-bottom: 1px dashed #bdd3e6; + min-width: 200px; + padding: 0 0.2rem; + background: transparent; + } + + .inline-placeholder { + color: #2c7da0; + font-weight: 500; + } + + hr { + margin: 1rem 0; + } + + /* main items table */ + .items-table { + width: 100%; + border-collapse: collapse; + margin: 1.2rem 0 1rem; + font-size: 0.8rem; + } + + .items-table th, + .items-table td { + border: 1px solid #cddfe7; + padding: 10px 6px; + vertical-align: top; + text-align: left; + } + + .items-table th { + background-color: #257339; + color: white; + font-weight: 600; + font-size: 0.8rem; + letter-spacing: 0.5px; + text-align: center; + } + + .items-table td { + background-color: #fff; + color: #1e2a3a; + } + + .items-table input, + .items-table .pseudo-input { + width: 100%; + border: none; + background: transparent; + font-family: inherit; + font-size: 0.8rem; + padding: 4px 2px; + outline: none; + border-bottom: 1px dotted #acc8dd; + } + + .items-table input:focus { + border-bottom: 1px solid #1f6e8c; + background: #fefce8; + } + + /* numeric cells alignment */ + .numeric-cell { + text-align: right; + } + + .qty-cell input, + .price-cell input { + text-align: right; + } + + .total-cell { + font-weight: 600; + background-color: #f8fafc; + text-align: right; + } + + /* summary section (subtotal, discount, transport) */ + .summary-area { + margin-top: 1.5rem; + display: flex; + flex-direction: column; + align-items: flex-end; + border-top: 2px solid #cbdde6; + padding-top: 1.2rem; + } + + .summary-table { + width: 320px; + border-collapse: collapse; + font-size: 0.85rem; + } + + .summary-table td { + padding: 6px 8px; + border: none; + } + + .summary-table td:first-child { + font-weight: 700; + text-align: left; + } + + .summary-table td:last-child { + text-align: right; + font-weight: 600; + } + + .total-row { + border-top: 2px solid #2c5a74; + font-weight: 800; + font-size: 1rem; + } + + .discount-input, + .transport-input { + border: 1px solid #b9d0df; + border-radius: 8px; + padding: 4px 10px; + width: 110px; + text-align: right; + font-weight: 500; + background: #fff; + } + + .inline-action { + display: inline-flex; + align-items: center; + gap: 5px; + } + + button.recalc { + background: #eef2f5; + border: 1px solid #b9cfdf; + border-radius: 30px; + padding: 4px 12px; + font-size: 0.7rem; + cursor: pointer; + transition: 0.1s; + margin-left: 10px; + font-weight: 500; + } + + button.recalc:hover { + background: #cbdde6; + } + + .footer-note { + margin-top: 2rem; + font-size: 0.7rem; + text-align: center; + color: #547b97; + border-top: 1px solid #d4e2ec; + padding-top: 1rem; + } + + @media (max-width: 750px) { + .po-container { + padding: 1rem; + } + + .info-grid { + flex-direction: column; + } + + .summary-table { + width: 100%; + } + + .items-table th, + .items-table td { + padding: 6px 3px; + font-size: 0.7rem; + } + } + + .editable-highlight { + background-color: #fef9e3; + } * { + margin: 0; + padding: 0; + box-sizing: border-box; + } + + body { + background: white; + display: flex; + justify-content: center; + align-items: center; + padding: 2rem 1rem; + font-family: 'Segoe UI', 'Roboto', 'Helvetica Neue', sans-serif; + } + + + /* actual table style — clean, border-collapse */ + .po-table { + width: 100%; + border-collapse: collapse; + font-size: 0.85rem; + font-family: 'Segoe UI', system-ui, 'Calibri', sans-serif; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.02); + } + + /* header sections & company info area (using table-like but not inside main table) */ + .header-section { + margin-bottom: 1.8rem; + text-align: center; + } + + .po-title { + font-size: 2rem; + font-weight: 700; + letter-spacing: 1px; + color: #257339; + border-top: 2px solid #257339; + border-bottom: 2px solid #257339; + padding: 1rem; + margin-bottom: 1.5rem; + margin-top: 1.5rem; + font-family: 'Inter', 'Segoe UI', sans-serif; + } + + /* two column info grid (company, supplier, dates etc) */ + .info-grid { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + gap: 1.2rem; + margin-bottom: 2rem; + background: #e1f3db; + padding: 1rem 1.2rem; + border: 1px solid #e2edf2; + } + + .info-left { + flex: 2; + min-width: 240px; + } + + .info-right { + flex: 1.2; + min-width: 200px; + } + + .info-row { + display: flex; + align-items: baseline; + flex-wrap: wrap; + margin-bottom: 12px; + font-size: 0.9rem; + } + + .info-label { + font-weight: 700; + width: 140px; + color: #257339; + letter-spacing: 0.3px; + } + + .info-value { + font-weight: 500; + color: #1f2d3d; + border-bottom: 1px dashed #bdd3e6; + min-width: 200px; + padding: 0 0.2rem; + background: transparent; + } + + .inline-placeholder { + color: #2c7da0; + font-weight: 500; + } + + hr { + margin: 1rem 0; + } + + /* main items table */ + .items-table { + width: 100%; + border-collapse: collapse; + margin: 1.2rem 0 1rem; + font-size: 0.8rem; + } + + .items-table th, + .items-table td { + border: 1px solid #cddfe7; + padding: 10px 6px; + vertical-align: top; + text-align: left; + } + + .items-table th { + background-color: #257339; + color: white; + font-weight: 600; + font-size: 0.8rem; + letter-spacing: 0.5px; + text-align: center; + } + + .items-table td { + background-color: #fff; + color: #1e2a3a; + } + + .items-table input, + .items-table .pseudo-input { + width: 100%; + border: none; + background: transparent; + font-family: inherit; + font-size: 0.8rem; + padding: 4px 2px; + outline: none; + border-bottom: 1px dotted #acc8dd; + } + + .items-table input:focus { + border-bottom: 1px solid #1f6e8c; + background: #fefce8; + } + + /* numeric cells alignment */ + .numeric-cell { + text-align: right; + } + + .qty-cell input, + .price-cell input { + text-align: right; + } + + .total-cell { + font-weight: 600; + background-color: #f8fafc; + text-align: right; + } + + /* summary section (subtotal, discount, transport) */ + .summary-area { + margin-top: 1.5rem; + display: flex; + flex-direction: column; + align-items: flex-end; + border-top: 2px solid #cbdde6; + padding-top: 1.2rem; + } + + .summary-table { + width: 320px; + border-collapse: collapse; + font-size: 0.85rem; + } + + .summary-table td { + padding: 6px 8px; + border: none; + } + + .summary-table td:first-child { + font-weight: 700; + text-align: left; + } + + .summary-table td:last-child { + text-align: right; + font-weight: 600; + } + + .total-row { + border-top: 2px solid #2c5a74; + font-weight: 800; + font-size: 1rem; + } + + .discount-input, + .transport-input { + border: 1px solid #b9d0df; + border-radius: 8px; + padding: 4px 10px; + width: 110px; + text-align: right; + font-weight: 500; + background: #fff; + } + + .inline-action { + display: inline-flex; + align-items: center; + gap: 5px; + } + + button.recalc { + background: #eef2f5; + border: 1px solid #b9cfdf; + border-radius: 30px; + padding: 4px 12px; + font-size: 0.7rem; + cursor: pointer; + transition: 0.1s; + margin-left: 10px; + font-weight: 500; + } + + button.recalc:hover { + background: #cbdde6; + } + + .footer-note { + margin-top: 2rem; + font-size: 0.7rem; + text-align: center; + color: #547b97; + border-top: 1px solid #d4e2ec; + padding-top: 1rem; + } + + @media (max-width: 750px) { + .po-container { + padding: 1rem; + } + + .info-grid { + flex-direction: column; + } + + .summary-table { + width: 100%; + } + + .items-table th, + .items-table td { + padding: 6px 3px; + font-size: 0.7rem; + } + } + + .editable-highlight { + background-color: #fef9e3; + } \ No newline at end of file diff --git a/app/templates/documents/precost/template.html b/app/templates/documents/precost/template.html new file mode 100644 index 0000000..f58cd3c --- /dev/null +++ b/app/templates/documents/precost/template.html @@ -0,0 +1,97 @@ + + + + + + + +
+ +
+
+ Company Logo +
+
PRICE QUOTATION
+
+ + +
+
+
+ VESSEL NAME : + Oceanic Maritime Solutions (Pvt) Ltd +
+
+ SUPPLY PORT : + Supplier Name +
+
+
+
+ CLIENT RFQ : + RFQ-001 +
+
+ OMS RFQ : + RFQ-001 +
+
+ DATE : + Current Date +
+
+
+ + +
+ + + + + + + + + + + + + + {{items}} + +
ITEM NODESCRIPTIONREMARKSQTYUNITUNIT PRICETOTAL PRICE
+
+ + +
+ + + + + + + + + + + + + + + + + +
SUB TOTAL{{subtotal}}
DISCOUNT (%) + {{discount}}
ADDITIONAL CHARGES (CUSTOM/HANDLING/TRANSPORT) + {{transport}}
GRAND TOTAL{{fullTotal}}
+
+ +
+ + + + \ No newline at end of file diff --git a/app/templates/emails/newTempVendor.html b/app/templates/emails/newTempVendor.html new file mode 100644 index 0000000..60ab6c0 --- /dev/null +++ b/app/templates/emails/newTempVendor.html @@ -0,0 +1,157 @@ + + + + + + + + Your Verification Code + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ Logo +
+ +

+ A new temporary vendor has been added for precost No: {{precostNo}} Please review this and proceed with + the approval. +

+ + + + + + + + + + + + + + + +
+ + Vendor Name + + + {{vendor_name}} + +
+ + Vendor Contact Person + + + {{contact_person}} + +
+ + Vendor Email + + + {{email}} + +
+ + Vendor Contact Number + + + {{phone}} + +
+ +
+ + + + +
+

+ © {{currentYear}} Oceanic Maritime Solutions. All rights reserved. +

+
+
+ + + + + + + +
+ This is an automated message, please do not reply to this email. +
+ +
+ + + \ No newline at end of file diff --git a/app/templates/emails/otp.html b/app/templates/emails/otp.html new file mode 100644 index 0000000..f2672dd --- /dev/null +++ b/app/templates/emails/otp.html @@ -0,0 +1,37 @@ + + + + + GLAURA 2FA Code + + +

Dear {{firstName}},

+ +

Greetings from Oceanic Titan!

+ +

Your One Time Password (OTP) to log in to your Oceanic Titan account is mentioned below.

+ +

Please enter this OTP in the required field to proceed further:

+ + + + + + + + + + +
Details 
One Time Password (OTP){{otp}}
+ +

The above-mentioned OTP is valid for 5 minutes.

+ +
+ +

Regards,
+ Oceanic Titan Team +

+ +

{{currentYear}} Oceanic Titan. This is an auto-generated email, please do not reply to this email.

+ + \ No newline at end of file diff --git a/app/templates/emails/passwordReset.html b/app/templates/emails/passwordReset.html new file mode 100644 index 0000000..61c62e8 --- /dev/null +++ b/app/templates/emails/passwordReset.html @@ -0,0 +1,53 @@ + + + + + + Document + + + +

Dear {{firstName}},

+ +

Greetings from Oceanic Titan!

+ +

You have requested to reset your password for your Oceanic Titan account. Please click the link below to reset your password:

+ +

Reset Password

+ +

If you did not request a password reset, please ignore this email. The above link will expire in {{expiryTime}}.

+ +
+ +

Regards,
+ Oceanic Titan Team +

+ +

{{currentYear}} Oceanic Titan. This is an auto-generated email, please do not reply to this email.

+ + \ No newline at end of file diff --git a/app/templates/emails/welcome.html b/app/templates/emails/welcome.html new file mode 100644 index 0000000..876b2fa --- /dev/null +++ b/app/templates/emails/welcome.html @@ -0,0 +1,167 @@ + + + + + + + Your Verification Code + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + +
+ Logo +
+ +

+ Dear {{firstName}},

+ Welcome to Oceanic Maritime Solutions Inquiry management system.
+ Please find your account details below: +

+ + + + + + +
+ + + + + + +
+ + +
+

+ ⚡ Important: Do not share this information with anyone. Change your password immediately after logging in. +

+
+ + +
+

+ 🚀 Get Started with System: +

+ + + + + + + + + + +
+ • Explore your dashboard and features +
+ • Connect with our support team +
+ • Stay updated with latest features +
+
+ + +

+ If you need any assistance, feel free to reach out to us. +

+ + +
+

+ 👋 + Once again, welcome aboard! We're thrilled to have you with us. +

+
+
+ + + + +
+

+ © {{currentYear}} Oceanic Maritime Solutions. All rights reserved. +

+
+
+ + + + + + + +
+ This is an automated message, please do not reply to this email. +
+ +
+ + \ No newline at end of file diff --git a/app/utils/basis.utill.js b/app/utils/basis.utill.js new file mode 100644 index 0000000..364e247 --- /dev/null +++ b/app/utils/basis.utill.js @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/utils/basis.util.js + +const calculateBasis = (marginPercent, usdRate) => { + if (!usdRate || usdRate <= 0) { + throw new Error("Invalid USD rate"); + } + + const multiplier = (100 + marginPercent) / 100; // e.g. 25% -> 1.25 + const basis = multiplier / usdRate; + + return Number(basis).toFixed(10); +}; + +module.exports = { calculateBasis }; diff --git a/app/utils/cache.util.js b/app/utils/cache.util.js new file mode 100644 index 0000000..492161b --- /dev/null +++ b/app/utils/cache.util.js @@ -0,0 +1,129 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/utils/cache.util.js + +const redis = require("../config/redisClient"); +const { log } = require("./consoleLog.utill"); +const db = require("../models"); +const User = db.User; + + +/////////////////////////////// +// User Cache // +///////////////////////////// + +const CACHE_TTL = 20 * 60; // 20 minutes (in seconds) + +const getCacheKey = (email) => `user:${email}`; + +async function getCachedUser(email) { + const key = getCacheKey(email); + + try { + // 1. Check Redis first + if (process.env.CACHE === "true") { + const cached = await redis.get(key); + + if (cached) { + log("⚡ Cache HIT - returning user from Redis"); + return JSON.parse(cached); + } + } + + log("Cache MISS - fetching user from DB"); + // 2. Fetch from DB + const user = await User.findOne({ where: { email } }); + if (!user) return null; + + const plainUser = user.get({ plain: true }); + + // 3. Store in Redis + if (process.env.CACHE === "true") { + await redis.set( + key, + JSON.stringify(plainUser), + "EX", + CACHE_TTL + ); + } + + return plainUser; + + } catch (err) { + log("Error fetching user:", err); + throw new Error("Failed to fetch user"); + } +} + +// Invalidate cache +async function clearUserCache(email) { + const key = getCacheKey(email); + + try { + await redis.del(key); + log(`Cache cleared for user: ${email}`); + } catch (err) { + log("Error clearing cache:", err); + } +} + + + +/////////////////////////////// +// Permission Cache // +///////////////////////////// + +const PERM_TTL = 10 * 60; + +const getPermKey = (userId) => `perm:user:${userId}`; + +async function getCachedPermissions(userId) { + if (process.env.CACHE !== "true") return null; + + try { + const cached = await redis.get(getPermKey(userId)); + return cached ? JSON.parse(cached) : null; + } catch (err) { + console.log("Cache read error:", err); + return null; + } +} + +async function setCachedPermissions(userId, permissions) { + if (process.env.CACHE !== "true") return; + + try { + await redis.set( + getPermKey(userId), + JSON.stringify(permissions), + "EX", + PERM_TTL + ); + } catch (err) { + console.log("Cache write error:", err); + } +} + +async function clearPermissionCache(userId) { + try { + await redis.del(getPermKey(userId)); + } catch (err) { + console.log("Cache delete error:", err); + } +} + + +module.exports = { + getCachedUser, + clearUserCache, + getCachedPermissions, + setCachedPermissions, + clearPermissionCache +}; \ No newline at end of file diff --git a/app/utils/calendar.utill.js b/app/utils/calendar.utill.js new file mode 100644 index 0000000..0da8644 --- /dev/null +++ b/app/utils/calendar.utill.js @@ -0,0 +1,88 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/utils/calendar.util.js + +const db = require("../models"); +const { log } = require("./consoleLog.utill"); + +const Calendar = db.calender; +const CalenderHoliday = db.calenderHolidays; + + +const calculateDeadline = async () => { + let deadlineDate = new Date(); + + const currentYear = deadlineDate.getFullYear(); + + // Get calendar for current year + const calendar = await Calendar.findOne({ + where: { + year: currentYear + } + }); + + if (!calendar) { + throw new Error(`Calendar for year ${currentYear} not found`); + } + + + deadlineDate.setDate(deadlineDate.getDate() + 1); + + + while (true) { + + const month = deadlineDate.getMonth() + 1; + const day = deadlineDate.getDate(); + + + // Get holidays for this month + const holidays = await CalenderHoliday.findAll({ + where: { + calender_id: calendar.calender_id, + month: month, + shouldConsiderAsHoliday: true + } + }); + + + const isWeekend = + deadlineDate.getDay() === 0 || + deadlineDate.getDay() === 6; + + + const holiday = holidays.find( + h => h.day === day + ); + + + const isHoliday = + isWeekend || !!holiday; + + + log( + `${deadlineDate.toISOString().split("T")[0]} => Holiday: ${isHoliday}` + ); + + + if (!isHoliday) { + return deadlineDate; + } + + + // Move one more day + deadlineDate.setDate( + deadlineDate.getDate() + 1 + ); + } +}; + +module.exports = { + calculateDeadline +}; \ No newline at end of file diff --git a/app/utils/consoleLog.utill.js b/app/utils/consoleLog.utill.js new file mode 100644 index 0000000..33193cf --- /dev/null +++ b/app/utils/consoleLog.utill.js @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/utils/consoleLog.util.js + +const isDevelopment = process.env.NODE_ENV === "development"; + +const logQueue = require("../queues/log.queue"); + +const log = async (...args) => { + if (isDevelopment) { + console.log(...args); + return; + } + + try { + const message = args + .map((arg) => { + if (arg instanceof Error) { + return `${arg.message}\n${arg.stack}`; + } + + if (typeof arg === "object") { + return JSON.stringify(arg); + } + + return String(arg); + }) + .join(" "); + + await logQueue.add("log", { + message, + timestamp: new Date().toISOString(), + }); + } catch (err) { + console.error("Log queue failed:", err); + } +}; + +function runningEnvironment() { + return !isDevelopment; +} + +module.exports = { + log, + runningEnvironment, +}; diff --git a/app/utils/document.utill.js b/app/utils/document.utill.js new file mode 100644 index 0000000..b250cda --- /dev/null +++ b/app/utils/document.utill.js @@ -0,0 +1,101 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/utils/document.util.js + +const { Document } = require("../models"); +const { generateId, generateDocumentReferenceNo } = require("./idGen.util"); +const {log} = require("./consoleLog.utill"); + +const DOCUMENT_STATUS = { + DRAFT: "DRAFT", + FINAL: "FINAL", +}; + +const validateStatus = (status) => { + if (!Object.values(DOCUMENT_STATUS).includes(status)) { + throw new Error( + `Invalid status "${status}". Allowed values: ${Object.values( + DOCUMENT_STATUS, + ).join(", ")}`, + ); + } +}; + +const createDraftDocument = async (document, doc_data) => { + log("Creating draft document data..."); + log("Incoming document data:", JSON.stringify(doc_data, null, 2)); + const result = await Document.create({ + doc_id: generateId(), + reference_no: doc_data.reference_no || "N/A", + doc_type: document, + data: doc_data, + status: DOCUMENT_STATUS.DRAFT, + }); + + log("Draft document created with ID:", result.doc_id); + + return result; + +}; + +const finalizeDocument = async (document, doc_data) => { + if (!doc_data.doc_id) { + throw new Error("Document data ID is required for FINAL status."); + } + + const [updatedRows] = await Document.update( + { + reference_no: doc_data.reference_no || "N/A", + doc_type: document, + data: doc_data, + status: DOCUMENT_STATUS.FINAL, + }, + { + where: { + doc_id: doc_data.doc_id, + }, + }, + ); + + if (updatedRows === 0) { + throw new Error("Document data not found or already updated."); + } + + return await Document.findOne({ + where: { + doc_id: doc_data.doc_id, + }, + }); +}; + +const createDocumentData = async (document, doc_data, status) => { + try { + validateStatus(status); + + switch (status) { + case DOCUMENT_STATUS.DRAFT: + return await createDraftDocument(document, doc_data); + + case DOCUMENT_STATUS.FINAL: + return await finalizeDocument(document, doc_data); + + default: + throw new Error(`Unhandled document status: ${status}`); + } + } catch (error) { + console.error("❌ Document data operation failed:", error.message); + throw error; + } +}; + +module.exports = { + createDocumentData, + DOCUMENT_STATUS, +}; diff --git a/app/utils/documentJob.util.js b/app/utils/documentJob.util.js new file mode 100644 index 0000000..1806c4f --- /dev/null +++ b/app/utils/documentJob.util.js @@ -0,0 +1,168 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/utils/documentJob.util.js + +const documentQueue = require("../queues/pdf.queue"); + +/** + * Queue a document generation job + * @param {string} document - Document type (e.g., 'precost') + * @param {string} documentType - Format (e.g., 'pdf', 'excel') + * @param {object} data - Data to pass to document generator + * @returns {Promise} Job object with id + */ +const queueDocumentJob = async (document, documentType, data) => { + try { + const job = await documentQueue .add("generate-document", { + document, + documentType, + data, + }); + + return { + success: true, + jobId: job.id, + createdAt: new Date(), + }; + } catch (error) { + throw new Error(`Failed to queue document job: ${error.message}`); + } +}; + +/** + * Get job status + * @param {string|number} jobId - Job ID + * @returns {Promise} Job status and details + */ +const getDocumentJobStatus = async (jobId) => { + try { + const job = await documentQueue .getJob(jobId); + + if (!job) { + return { + success: false, + message: "Job not found", + }; + } + + const state = await job.getState(); + const result = job.returnvalue; + + return { + success: true, + jobId: job.id, + state, + result: state === "completed" ? result : null, + error: state === "failed" ? job.failedReason : null, + createdAt: new Date(job.timestamp), + startedAt: job.processedOn ? new Date(job.processedOn) : null, + completedAt: job.finishedOn ? new Date(job.finishedOn) : null, + }; + } catch (error) { + throw new Error(`Failed to get job status: ${error.message}`); + } +}; + +/** + * Wait for job completion with timeout + * @param {string|number} jobId - Job ID + * @param {number} timeout - Timeout in milliseconds (default: 300000 = 5 minutes) + * @returns {Promise} Job result when completed + */ +const waitForDocumentJob = async (jobId, timeout = 300000) => { + try { + const job = await documentQueue .getJob(jobId); + + if (!job) { + throw new Error("Job not found"); + } + + return await job.waitUntilFinished( + { timeout }, + documentQueue .events + ); + } catch (error) { + throw new Error(`Failed to wait for job: ${error.message}`); + } +}; + +/** + * Remove a job from queue + * @param {string|number} jobId - Job ID + * @returns {Promise} Success status + */ +const cancelDocumentJob = async (jobId) => { + try { + const job = await documentQueue .getJob(jobId); + + if (!job) { + throw new Error("Job not found"); + } + + await job.remove(); + return true; + } catch (error) { + throw new Error(`Failed to cancel job: ${error.message}`); + } +}; + +/** + * Get all active jobs + * @returns {Promise} Array of active job details + */ +const getActiveJobs = async () => { + try { + const jobs = await documentQueue .getActiveCount(); + const activeJobs = await documentQueue .getJobs(["active"], 0, -1); + + return { + count: jobs, + jobs: activeJobs.map((job) => ({ + id: job.id, + data: job.data, + state: "active", + progress: job.progress(), + })), + }; + } catch (error) { + throw new Error(`Failed to get active jobs: ${error.message}`); + } +}; + +/** + * Get all pending jobs + * @returns {Promise} Array of pending job details + */ +const getPendingJobs = async () => { + try { + const jobs = await documentQueue .getWaitingCount(); + const pendingJobs = await documentQueue .getJobs(["waiting"], 0, -1); + + return { + count: jobs, + jobs: pendingJobs.map((job) => ({ + id: job.id, + data: job.data, + state: "waiting", + })), + }; + } catch (error) { + throw new Error(`Failed to get pending jobs: ${error.message}`); + } +}; + +module.exports = { + queueDocumentJob, + getDocumentJobStatus, + waitForDocumentJob, + cancelDocumentJob, + getActiveJobs, + getPendingJobs, +}; diff --git a/app/utils/excel/confirmOrder.excel.js b/app/utils/excel/confirmOrder.excel.js new file mode 100644 index 0000000..82a77ab --- /dev/null +++ b/app/utils/excel/confirmOrder.excel.js @@ -0,0 +1,91 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/utills/excel/confirmOrder.excel.js + +const ExcelJS = require("exceljs"); + +/** + * Generate Excel template buffer + */ +exports.generateTemplate = async (items) => { + const workbook = new ExcelJS.Workbook(); + const sheet = workbook.addWorksheet("Confirm Order"); + + sheet.columns = [ + { header: "Item ID", key: "id", width: 25 }, + { header: "Item Name", key: "item_name", width: 30 }, + { header: "Original Quantity", key: "quantity", width: 20 }, + { header: "Confirmed Quantity", key: "confirmed_quantity", width: 20 }, + ]; + + const headerRow = sheet.getRow(1); + + headerRow.eachCell((cell) => { + cell.fill = { + type: "pattern", + pattern: "solid", + fgColor: { argb: "1F4E78" }, + }; + + cell.font = { + bold: true, + color: { argb: "FFFFFF" }, + }; + + cell.alignment = { + vertical: "middle", + horizontal: "center", + }; + + cell.border = { + top: { style: "thin" }, + left: { style: "thin" }, + bottom: { style: "thin" }, + right: { style: "thin" }, + }; + }); + + items.forEach((item) => { + sheet.addRow({ + id: item.data_id, + item_name: item.item_name, + quantity: item.quantity, + confirmed_quantity: item.quantity, + }); + }); + + return workbook.xlsx.writeBuffer(); +}; + +/** + * Parse uploaded Excel buffer + */ +exports.parseExcel = async (buffer) => { + const workbook = new ExcelJS.Workbook(); + await workbook.xlsx.load(buffer); + + const sheet = workbook.getWorksheet("Confirm Order"); + if (!sheet) throw new Error("Invalid sheet name"); + + const confirmedMap = new Map(); + + sheet.eachRow((row, rowNumber) => { + if (rowNumber === 1) return; + + const itemId = row.getCell(1).value; + const confirmedQty = Number(row.getCell(4).value); + + if (itemId) { + confirmedMap.set(itemId, confirmedQty); + } + }); + + return confirmedMap; +}; diff --git a/app/utils/excel/generateExcelTemplate.utill.js b/app/utils/excel/generateExcelTemplate.utill.js new file mode 100644 index 0000000..7f8af8a --- /dev/null +++ b/app/utils/excel/generateExcelTemplate.utill.js @@ -0,0 +1,116 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/utills/generateExcelTemplate.utill.js + +const ExcelJS = require("exceljs"); + +exports.generateExcelTemplate = async (columns, vendors) => { + const workbook = new ExcelJS.Workbook(); + + // ========================= + // Template Sheet + // ========================= + + const worksheet = workbook.addWorksheet("Template"); + + const headers = columns.map((col) => + col.is_required ? `${col.column_name} *` : col.column_name, + ); + + const headerRow = worksheet.addRow(headers); + + headerRow.eachCell((cell) => { + cell.font = { bold: true }; + cell.alignment = { vertical: "middle", horizontal: "center" }; + }); + + headers.forEach((header, i) => { + worksheet.getColumn(i + 1).width = Math.max(header.length + 5, 20); + }); + + columns.forEach((col, index) => { + const headerCell = worksheet.getCell(1, index + 1); + + if (col.is_required) { + headerCell.fill = { + type: "pattern", + pattern: "solid", + fgColor: { argb: "FFF4CCCC" }, + }; + + headerCell.font = { + bold: true, + color: { argb: "FF000000" }, + }; + } else { + headerCell.fill = { + type: "pattern", + pattern: "solid", + fgColor: { argb: "FFFFF2CC" }, + }; + + headerCell.font = { + bold: true, + color: { argb: "FF000000" }, + }; + } + + if (col.validation_type === "number") { + for (let i = 2; i <= 100; i++) { + worksheet.getCell(i, index + 1).dataValidation = { + type: "whole", + operator: "greaterThanOrEqual", + formulae: [0], + showErrorMessage: true, + error: "Please enter a valid number", + }; + } + } + }); + + worksheet.views = [{ state: "frozen", ySplit: 1 }]; + + // ========================= + // Vendors Sheet + // ========================= + const vendorSheet = workbook.addWorksheet("Vendors"); + + const vendorHeader = vendorSheet.addRow(["Supplier ID", "Supplier Name"]); + + vendorHeader.eachCell((cell) => { + cell.font = { + bold: true, + color: { argb: "FFFFFFFF" }, + }; + + cell.alignment = { + vertical: "middle", + horizontal: "center", + }; + + cell.fill = { + type: "pattern", + pattern: "solid", + fgColor: { argb: "4472C4" }, + }; + }); + + // Add vendor rows + vendors.forEach((vendor) => { + vendorSheet.addRow([vendor.vendor_id, vendor.name]); + }); + + // Optional styling + vendorSheet.columns = [{ width: 25 }, { width: 35 }]; + + vendorSheet.views = [{ state: "frozen", ySplit: 1 }]; + + return workbook; +}; diff --git a/app/utils/excel/validateExcel.utill.js b/app/utils/excel/validateExcel.utill.js new file mode 100644 index 0000000..c956b7d --- /dev/null +++ b/app/utils/excel/validateExcel.utill.js @@ -0,0 +1,101 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/utills/validateExcel.utill.js + +const ExcelJS = require("exceljs"); + +exports.validateExcel = async (buffer, columns) => { + const workbook = new ExcelJS.Workbook(); + await workbook.xlsx.load(buffer); + const worksheet = workbook.worksheets[0]; + + if (!worksheet) { + throw { status: 400, message: "Excel sheet is empty" }; + } + + const expectedHeaders = columns.map((col) => + col.is_required ? `${col.column_name} *` : col.column_name + ); + + const headerRow = worksheet.getRow(1); + + for (let i = 1; i <= expectedHeaders.length; i++) { + const cellValue = (headerRow.getCell(i).value || "").toString().trim(); + + if (cellValue !== expectedHeaders[i - 1]) { + throw { + status: 400, + message: `Invalid header in column ${i}`, + expected: expectedHeaders[i - 1], + received: cellValue, + }; + } + } + + const errors = []; + const validData = []; + + worksheet.eachRow((row, rowNumber) => { + if (rowNumber === 1) return; + + let rowData = {}; + let rowHasError = false; + + columns.forEach((col, index) => { + let value = row.getCell(index + 1).value; + + if (value && typeof value === "object") { + value = value.text || value.result || ""; + } + + value = value?.toString().trim() || ""; + + if (col.is_required && !value) { + errors.push({ + row: rowNumber, + column: col.column_name, + message: "Required field is missing", + }); + rowHasError = true; + } + + if (value) { + if (col.validation_type === "number" && isNaN(value)) { + errors.push({ + row: rowNumber, + column: col.column_name, + message: "Must be a number", + }); + rowHasError = true; + } + + if ( + col.validation_type === "boolean" && + !["true", "false"].includes(value.toLowerCase()) + ) { + errors.push({ + row: rowNumber, + column: col.column_name, + message: "Must be true or false", + }); + rowHasError = true; + } + } + + rowData[col.column_name] = value; + }); + + if (!rowHasError) { + validData.push(rowData); + } + }); + + return { errors, validData }; +}; \ No newline at end of file diff --git a/app/utils/hashPassword.util.js b/app/utils/hashPassword.util.js new file mode 100644 index 0000000..a8d3483 --- /dev/null +++ b/app/utils/hashPassword.util.js @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/utils/hashPassword.util.js + +const bcrypt = require("bcrypt"); + +const hashPassword = async (password) => { + const saltRounds = 10; + const hashedPassword = await bcrypt.hash(password, saltRounds); + return hashedPassword; +} + +const checkPassword = async (password, hashedPassword) => { + return await bcrypt.compare(password, hashedPassword); +} + +module.exports = { hashPassword, checkPassword }; \ No newline at end of file diff --git a/app/utils/id/confirmOrder.utill.js b/app/utils/id/confirmOrder.utill.js new file mode 100644 index 0000000..e50c37b --- /dev/null +++ b/app/utils/id/confirmOrder.utill.js @@ -0,0 +1,33 @@ + +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/utils/id/confirmOrder.util.js + +const { nextSequence, pad } = require("../referenceNumber.util"); +const { sequelize } = require("../../models"); + +const generateOrderNumber = async () => { + const now = new Date(); + + const yy = String(now.getFullYear()).slice(-2); + const mm = String(now.getMonth() + 1).padStart(2, "0"); + + const period = `${yy}${mm}`; + + const sequenceKey = `ORD-${period}`; + + return await sequelize.transaction(async (transaction) => { + const nextNo = await nextSequence(sequenceKey, transaction); + + return `OSC/${period}/${pad(nextNo)}`; + }); +}; + +module.exports = { generateOrderNumber }; \ No newline at end of file diff --git a/app/utils/id/dispatchNote.utill.js b/app/utils/id/dispatchNote.utill.js new file mode 100644 index 0000000..daa22bf --- /dev/null +++ b/app/utils/id/dispatchNote.utill.js @@ -0,0 +1,26 @@ + +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/utils/id/dispatchNote.util.js + +const { nextSequence, pad } = require("../referenceNumber.util"); +const { sequelize } = require("../../models"); + +const generateDeliveryNote = async (orderNo) => { + const sequenceKey = `DN-${orderNo}`; + + return await sequelize.transaction(async (transaction) => { + const nextNo = await nextSequence(sequenceKey, transaction); + + return `DN/${orderNo}/${String(nextNo).padStart(2, "0")}`; + }); +}; + +module.exports = { generateDeliveryNote }; \ No newline at end of file diff --git a/app/utils/id/inquiryID.utill.js b/app/utils/id/inquiryID.utill.js new file mode 100644 index 0000000..e0e07b9 --- /dev/null +++ b/app/utils/id/inquiryID.utill.js @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/utils/id/inquiry.util.js + +const { nextSequence, pad } = require("../referenceNumber.util"); +const { sequelize } = require("../../models"); +const {log} = require("../consoleLog.utill"); + +const generateInquiryNumber = async (port) => { + const year = new Date().getFullYear(); + + const sequenceKey = `INQ-${year}-${port}`; + + return await sequelize.transaction(async (transaction) => { + const nextNo = await nextSequence(sequenceKey, transaction); + + log(`Generated inquiry number: OSC/INQ/${year}/${port}/${pad(nextNo)}`); + return `OSC/INQ/${year}/${port}/${pad(nextNo)}`; + }); +}; + +module.exports = { generateInquiryNumber }; \ No newline at end of file diff --git a/app/utils/id/po.utill.js b/app/utils/id/po.utill.js new file mode 100644 index 0000000..c7b859d --- /dev/null +++ b/app/utils/id/po.utill.js @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/utils/id/po.util.js + +const { nextSequence, pad } = require("../referenceNumber.util"); +const { sequelize } = require("../../models"); + +const generatePONumber = async (orderNo) => { + const sequenceKey = `PO-${orderNo}`; + + return await sequelize.transaction(async (transaction) => { + const nextVendor = await nextSequence(sequenceKey, transaction); + + return `PO/${orderNo}/V${String(nextVendor).padStart(2, "0")}`; + }); +}; + +module.exports = { generatePONumber }; \ No newline at end of file diff --git a/app/utils/id/quotation.utill.js b/app/utils/id/quotation.utill.js new file mode 100644 index 0000000..79f25bc --- /dev/null +++ b/app/utils/id/quotation.utill.js @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/utils/id/quotation.util.js + +const { nextSequence, pad } = require("../referenceNumber.util"); +const { sequelize } = require("../../models"); + +const generateRFQNumber = async () => { + const now = new Date(); + + const yy = String(now.getFullYear()).slice(-2); + const mm = String(now.getMonth() + 1).padStart(2, "0"); + + const period = `${yy}${mm}`; + + const sequenceKey = `RFQ-${period}`; + + return await sequelize.transaction(async (transaction) => { + const nextNo = await nextSequence(sequenceKey, transaction); + + return `RFQ/OSC/${period}/${pad(nextNo)}`; + }); +}; + +module.exports = { generateRFQNumber }; \ No newline at end of file diff --git a/app/utils/idGen.util.js b/app/utils/idGen.util.js new file mode 100644 index 0000000..5d9d7f2 --- /dev/null +++ b/app/utils/idGen.util.js @@ -0,0 +1,160 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/utils/idGen.util.js + +const { v4: uuidv4 } = require("uuid"); +const { nextSequence } = require("./referenceNumber.util"); +const {log} = require("./consoleLog.utill"); + +const {generateInquiryNumber} = require("./id/inquiryID.utill"); +const { generateRFQNumber } = require("./id/quotation.utill"); +const {generatePONumber} = require("./id/po.utill"); +const { generateDeliveryNote } = require("./id/dispatchNote.utill"); + + +const generateUserId = () => { + return "usr_" + Math.random().toString(36).slice(2, 10); +}; + +const generateClientId = () => { + const prefix = "cli_"; + return prefix + uuidv4(); +}; + +const generateInquRefNo = async (port) => { + const reference_number = await generateInquiryNumber(port); + log("Generated Number", reference_number) + return reference_number; +}; + +const generateInquId = async (port) => { + const prefix = "inq_"; + const inquID = prefix + uuidv4(); + return inquID; +}; + +const generateCategoryId = () => { + const prefix = "cte_"; + return prefix + uuidv4(); +}; + +const generateRemarkId = () => { + const prefix = "rmk_"; + return prefix + uuidv4(); +}; + +const generateTodoId = () => { + const prefix = "tdo_"; + return prefix + uuidv4(); +}; + +const generateVendorStatusId = () => { + const prefix = "vst_"; + return prefix + uuidv4(); +} + +const generateVendorId = () => { + const prefix = "vnd_"; + return prefix + uuidv4(); +} + +const generateVendorPICId = () => { + const prefix = "vpic_"; + return prefix + uuidv4(); +} + +const generateVendorDocumentId = () => { + const prefix = "vdoc_"; + return prefix + uuidv4(); +} + +const generateColumnId = () => { + const prefix = "col_"; + return prefix + uuidv4(); +} + +const generatePreCostId = () => { + const prefix = "pc_"; + return prefix + uuidv4(); +} + +const generatePreCostPaymanentId = () => { + const prefix = "pcp_"; + return prefix + uuidv4(); +} + +const generateCalenderId = () => { + const prefix = "cal_"; + return prefix + uuidv4(); +} + +const generateId = () => { + return uuidv4(); +}; + +const generateInvoiceNo = async () => { + const invoiceNumber = await nextSequence("invoice"); + return `INV-${invoiceNumber.toString().padStart(6, "0")}`; +} + +const generateDocumentReferenceNo = async (type) => { + const normalizedType = String(type).toUpperCase().replace(/[\s_-]+/g, ""); + + if (normalizedType === "QUOTATION"){ + // Implement quotation number generation logic here + return 'N/A' + } else if (normalizedType === "PRECOST"){ + // Implement precost number generation logic here + const precostRef = await generateRFQNumber() + return precostRef + } else if (normalizedType === "PO"){ + // Implement contract number generation logic here + const Ref = await generatePONumber("OSC/2602") + return Ref + } else if (normalizedType === "BILL"){ + return 'N/A' + // Implement bill number generation logic here + } else if (normalizedType === "PAYMENT"){ + return 'N/A' + // Implement payment number generation logic here + } else if (normalizedType === "INVOICE"){ + // Implement invoice number generation logic here + const invoiceNo = await generateInvoiceNo(); + return invoiceNo; + } else if (normalizedType === "CREDITNOTE") { + return 'N/A'; + } else if (normalizedType === "DELIVERYNOTE") { + const orderNo = "OSC/2602"; + const deliveryNoteNo = await generateDeliveryNote(orderNo); + return deliveryNoteNo; + }else { + throw new Error("Unsupported document type for ID generation"); + } +} + +module.exports = { + generateUserId, + generateClientId, + generateInquId, + generateInquRefNo, + generateId, + generateCategoryId, + generateRemarkId, + generateTodoId, + generateVendorStatusId, + generateVendorId, + generateVendorPICId, + generateVendorDocumentId, + generateColumnId, + generatePreCostId, + generatePreCostPaymanentId, + generateDocumentReferenceNo, + generateCalenderId, +}; diff --git a/app/utils/imageToBase64.utill.js b/app/utils/imageToBase64.utill.js new file mode 100644 index 0000000..d154bd4 --- /dev/null +++ b/app/utils/imageToBase64.utill.js @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/utils/imageToBase64.utill.js + +const axios = require("axios"); + +const imageToBase64 = async (url) => { + const response = await axios.get(url, { + responseType: "arraybuffer", + }); + + const base64 = Buffer.from(response.data, "binary").toString("base64"); + + const mimeType = response.headers["content-type"]; + + return `data:${mimeType};base64,${base64}`; +}; + +module.exports = { imageToBase64 }; \ No newline at end of file diff --git a/app/utils/jwt.util.js b/app/utils/jwt.util.js new file mode 100644 index 0000000..f2bb54a --- /dev/null +++ b/app/utils/jwt.util.js @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/utils/jwt.util.js + +const jwt = require("jsonwebtoken"); +require("dotenv").config(); + +const JWT_SECRET = process.env.JWT_SECRET || "your_jwt_secret_key"; +const JWT_EXPIRES_IN = process.env.JWT_EXPIRES_IN || "1d"; // token validity + +/** + * Generate JWT token + * @param {Object} payload - usually { id, email, role } + * @returns string + */ +const generateToken = (payload) => { + return jwt.sign(payload, JWT_SECRET, { expiresIn: JWT_EXPIRES_IN }); +}; + +/** + * Verify JWT token + * @param {string} token + * @returns payload or throws error + */ +const verifyToken = (token) => { + return jwt.verify(token, JWT_SECRET); +}; + +module.exports = { generateToken, verifyToken }; diff --git a/app/utils/mail.util.js b/app/utils/mail.util.js new file mode 100644 index 0000000..2a31598 --- /dev/null +++ b/app/utils/mail.util.js @@ -0,0 +1,89 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/utils/mailer.util.js + + + +const nodemailer = require("nodemailer"); +const fs = require("fs"); +const path = require("path"); +const mailConfig = require("../config/mail.config"); + +const transporter = nodemailer.createTransport({ + host: mailConfig.host, + port: mailConfig.port, + secure: mailConfig.secure, + auth: mailConfig.auth, +}); + +transporter.verify((err) => { + if (err) console.error("Mail server connection failed", err); + else console.log("Mail server ready"); +}); + +// Utility to load template and replace placeholders +const loadTemplate = (templateName, variables = {}) => { + const templatePath = path.join(__dirname, "../templates/emails", `${templateName}.html`); + let template = fs.readFileSync(templatePath, "utf-8"); + + Object.keys(variables).forEach((key) => { + const regex = new RegExp(`{{${key}}}`, "g"); + template = template.replace(regex, variables[key]); + }); + + return template; +}; + +const send = async ({ to, subject, templateName, templateVars = {}, text }) => { + const html = templateName ? loadTemplate(templateName, templateVars) : undefined; + + const mailOptions = { + from: mailConfig.from, + to, + subject, + text, + html, + }; + + try { + const info = await transporter.sendMail(mailOptions); + console.log("Email sent:", info.messageId); + return info; + } catch (err) { + console.error("Error sending email:", err); + throw err; + } +}; + +const sendMail = async (options) => { + const today = new Date(); + + // Full formatted date: "January 1, 2026" + const formattedDate = today.toLocaleDateString("en-US", { + year: "numeric", + month: "long", + day: "numeric", + }); + + // Only year: "2026" + const currentYear = today.getFullYear(); + + const templateVars = { + ...options.templateVars, // keep existing vars + currentDate: formattedDate, + currentYear: currentYear, + }; + + return send({ ...options, templateVars }); +}; + + +module.exports = { sendMail }; + diff --git a/app/utils/notification.utill.js b/app/utils/notification.utill.js new file mode 100644 index 0000000..e69de29 diff --git a/app/utils/otp.util.js b/app/utils/otp.util.js new file mode 100644 index 0000000..7c391d8 --- /dev/null +++ b/app/utils/otp.util.js @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/utils/otp.util.js + +const otpCache = new Map(); + +function generateOTP(key){ + const otp = Math.floor(100000 + Math.random() * 900000).toString(); + saveOTP(key, otp); + return otp; +} + +function saveOTP(key, otp, ttl = 5 * 60 * 1000) { // default TTL: 5 mins + const expiresAt = Date.now() + ttl; + otpCache.set(key, { otp, expiresAt }); +} + +// Validate OTP +function validateOTP(key, otp) { + const record = otpCache.get(key); + if (!record) return false; + if (Date.now() > record.expiresAt) { + otpCache.delete(key); + return false; + } + const isValid = record.otp === otp; + if (isValid) otpCache.delete(key); // OTP can be used only once + return isValid; +} + +module.exports = { generateOTP, validateOTP }; \ No newline at end of file diff --git a/app/utils/passwordReset.utill.js b/app/utils/passwordReset.utill.js new file mode 100644 index 0000000..fd6ea95 --- /dev/null +++ b/app/utils/passwordReset.utill.js @@ -0,0 +1,113 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/utils/passwordReset.util.js + +const crypto = require("crypto"); + +const createRedisConnection = require("../config/redis.config"); +const redis = createRedisConnection(); + +const db = require("../models"); + +const User = db.User; + +const { log } = require("./consoleLog.utill"); +const { hashPassword } = require("./hashPassword.util"); + +const RESET_TOKEN_TTL = process.env.RESET_TOKEN_TTL || 10 * 60; // 10 minutes + +/** + * Generate password reset token + * + * @param {string} userId + * @returns {Promise} + */ +async function generateResetToken(userId) { + try { + const token = crypto.randomBytes(32).toString("hex"); + + const tokenHash = crypto + .createHash("sha256") + .update(token) + .digest("hex"); + + await redis.set( + `passwordReset:user:${userId}`, + tokenHash, + "EX", + RESET_TOKEN_TTL + ); + log(`Generated password reset token for user ${userId} with TTL of ${RESET_TOKEN_TTL} seconds, Generated token:`, token); + return token; + } catch (error) { + log("Password reset token generation failed", error); + throw new Error("Failed to generate password reset token"); + } +} + +/** + * Reset password using token + * + * @param {string} userId + * @param {string} token + * @param {string} newPassword + */ +async function resetPassword(userId, token, newPassword) { + try { + const storedHash = await redis.get( + `passwordReset:user:${userId}` + ); + + if (!storedHash) { + throw new Error("Invalid or expired reset token"); + } + + const tokenHash = crypto + .createHash("sha256") + .update(token) + .digest("hex"); + + const isValid = + crypto.timingSafeEqual( + Buffer.from(storedHash), + Buffer.from(tokenHash) + ); + + if (!isValid) { + throw new Error("Invalid or expired reset token"); + } + + const user = await User.findByPk(userId); + + if (!user) { + throw new Error("User not found"); + } + + user.password = await hashPassword(newPassword); + + await user.save(); + + await redis.del(`passwordReset:user:${userId}`); + + log( + `Password reset completed successfully for user ${userId}` + ); + + return true; + } catch (error) { + log("Password reset failed", error); + throw error; + } +} + +module.exports = { + generateResetToken, + resetPassword +}; diff --git a/app/utils/pdfGenerator.js b/app/utils/pdfGenerator.js new file mode 100644 index 0000000..e2103f7 --- /dev/null +++ b/app/utils/pdfGenerator.js @@ -0,0 +1,197 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + */ + +// app/utils/pdfGenerator.js + +const path = require("path"); +const ejs = require("ejs"); +const puppeteer = require("puppeteer"); +const fs = require("fs"); +const { imageToBase64 } = require("./imageToBase64.utill"); +const { log, runningEnvironment } = require("./consoleLog.utill"); + +let browser; + +let executablePath = null; + +const isProd = runningEnvironment(); + +if (isProd) { + executablePath = "/usr/bin/chromium"; +} else { + executablePath = puppeteer.executablePath(); +} + + +/** + * Launch or reuse Puppeteer browser safely + */ +async function getBrowser() { + if (!browser || !browser.isConnected?.()) { + console.log("🚀 Launching Chromium from Docker system path..."); + + browser = await puppeteer.launch({ + headless: "new", + executablePath: executablePath, + args: [ + "--no-sandbox", + "--disable-setuid-sandbox", + "--disable-dev-shm-usage", + "--disable-gpu", + "--no-zygote" + ], + }); + + console.log("✅ Browser launched"); + } + + return browser; +} + +/** + * Close browser safely + */ +async function closeBrowser() { + if (browser) { + try { + await browser.close(); + } catch (e) {} + browser = null; + } +} + +/** + * Core PDF renderer + */ +async function renderPdf(templateFile, data = {}, opts = {}) { + const templatePath = path.join(__dirname, "../templates", templateFile); + + console.log("📝 Rendering:", templateFile); + + // Convert image to base64 if needed + if (data.logoUrl && !data.logoBase64) { + try { + data.logoBase64 = await imageToBase64(data.logoUrl); + } catch (err) { + console.error("❌ Logo conversion failed:", err.message); + data.logoBase64 = null; + } + } + + const html = await ejs.renderFile(templatePath, data); + + const browserInstance = await getBrowser(); + const page = await browserInstance.newPage(); + + try { + // Safety timeouts + await page.setDefaultTimeout(60000); + await page.setDefaultNavigationTimeout(60000); + + await page.setViewport({ width: 1200, height: 800 }); + + console.log("📄 Loading HTML..."); + await page.setContent(html, { waitUntil: "networkidle0" }); + + await page.emulateMediaType("print"); + + // Wait for fonts + await page.evaluate(() => document.fonts?.ready || Promise.resolve()); + + const margin = { + top: 20, + bottom: 20, + left: 10, + right: 10, + ...opts.margin, + }; + + // Optional: measure content (kept for logging only) + const contentHeightPx = await page.evaluate(() => { + const body = document.body; + const htmlEl = document.documentElement; + + return Math.max( + body.scrollHeight, + body.offsetHeight, + htmlEl.scrollHeight, + htmlEl.offsetHeight + ); + }); + + console.log("📏 Content height:", contentHeightPx, "px"); + + // ❗ REMOVED scaling logic (causes PDF distortion in production) + + const showPageNumbers = opts.showPageNumbers === true; + + console.log("⚙️ Generating PDF..."); + + const pdfBuffer = await page.pdf({ + format: "A4", + printBackground: true, + preferCSSPageSize: true, + margin: { + top: `${margin.top}mm`, + bottom: `${margin.bottom}mm`, + left: `${margin.left}mm`, + right: `${margin.right}mm`, + }, + displayHeaderFooter: showPageNumbers, + headerTemplate: `
`, + footerTemplate: showPageNumbers + ? ` +
+ Page / +
` + : `
`, + }); + + console.log("✅ PDF generated"); + + return pdfBuffer; + } catch (err) { + console.error("❌ PDF generation error:", err.message); + throw err; + } finally { + await page.close().catch(() => {}); + } +} + +/** + * Helpers + */ +const generatePdfFromPreCost = (data) => + renderPdf("documents/precost/precost.ejs", data); + +const generatePdfFromInvoice = (data) => + renderPdf("documents/invoice/invoice.ejs", data); + +const generatePdfFromDispatchNote = (data) => + renderPdf("documents/dispatchNote/dispatchNote.ejs", data); + +const generatePdfFromDeliveryNote = (data) => + renderPdf("documents/deliveryNote/deliveryNote.ejs", data); + +const generatePdfFromPO = (data) => + renderPdf("documents/po/po.ejs", data); + +const generatePdfFromCustomDocument = (data) => + renderPdf("documents/customDocument/customDocument.ejs", data); + +const generatePdfFromCreditNote = (data) => + renderPdf("documents/creditNote/creditNote.ejs", data); + +module.exports = { + generatePdfFromPreCost, + generatePdfFromInvoice, + generatePdfFromDispatchNote, + generatePdfFromDeliveryNote, + generatePdfFromPO, + generatePdfFromCustomDocument, + generatePdfFromCreditNote, + renderPdf, + closeBrowser, +}; \ No newline at end of file diff --git a/app/utils/referenceNumber.util.js b/app/utils/referenceNumber.util.js new file mode 100644 index 0000000..55bb631 --- /dev/null +++ b/app/utils/referenceNumber.util.js @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// utils/referenceNumber.util.js + +const { referenceNumber } = require("../models"); +const { sequelize } = require("../models"); +const crypto = require("crypto"); + +const pad = (num, size = 4) => { + return String(num).padStart(size, "0"); +}; + +const nextSequence = async (sequenceKey, transaction) => { + const options = { + where: { sequence_key: sequenceKey }, + }; + + // Only add transaction and lock if transaction is provided + if (transaction) { + options.transaction = transaction; + options.lock = transaction.LOCK.UPDATE; + } + + let sequence = await referenceNumber.findOne(options); + + if (!sequence) { + const createOptions = transaction ? { transaction } : {}; + + sequence = await referenceNumber.create( + { + id: crypto.randomUUID(), + sequence_key: sequenceKey, + current_number: 1, + }, + createOptions + ); + + return 1; + } + + sequence.current_number += 1; + + const saveOptions = transaction ? { transaction } : {}; + await sequence.save(saveOptions); + + return sequence.current_number; +}; + +module.exports = { nextSequence, pad }; \ No newline at end of file diff --git a/app/utils/s3Upload.utill.js b/app/utils/s3Upload.utill.js new file mode 100644 index 0000000..37ed6ae --- /dev/null +++ b/app/utils/s3Upload.utill.js @@ -0,0 +1,90 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/utils/s3Upload.util.js + +const { PutObjectCommand, GetObjectCommand } = require("@aws-sdk/client-s3"); + +const { getSignedUrl } = require("@aws-sdk/s3-request-presigner"); +const s3 = require("../config/s3.config"); +const { v4: uuidv4 } = require("uuid"); +const path = require("path"); +const createRedisConnection = require("../config/redis.config"); +const redis = createRedisConnection(); +const { log } = require("./consoleLog.utill"); + +// Upload + return key (BEST PRACTICE) +const uploadToS3 = async (file, usedFor) => { + try { + const fileExtension = path.extname(file.originalname); + const fileName = `${uuidv4()}${fileExtension}`; + let key = `uploads/${fileName}`; + + if (usedFor === "asset") { + key = `assets/${fileName}`; + } + + log("Uploading file to S3 with key:", key); + + const params = { + Bucket: process.env.AWS_S3_BUCKET_NAME, + Key: key, + Body: file.buffer, + ContentType: file.mimetype, + }; + + await s3.send(new PutObjectCommand(params)); + + // Return KEY (not URL) + return key; + } catch (error) { + log("S3 Upload Error:", error); + throw new Error("File upload failed"); + } +}; + +// Generate signed URL with Redis caching +const getSignedFileUrl = async (key) => { + try { + const cacheKey = `s3:url:${key}`; + + // 1. Check cache + const cachedUrl = await redis.get(cacheKey); + + if (cachedUrl) { + log("⚡ Cache HIT - returning signed URL from Redis"); + return cachedUrl; + } + + log("🐢 Cache MISS - generating new signed URL"); + + // 2. Generate signed URL + const command = new GetObjectCommand({ + Bucket: process.env.AWS_S3_BUCKET_NAME, + Key: key, + }); + + const signedUrl = await getSignedUrl(s3, command, { + expiresIn: 3600, // 1 hour + }); + + // 3. Store in Redis (TTL: 55 minutes) + await redis.set(cacheKey, signedUrl, "EX", 3300); + + return signedUrl; + } catch (error) { + log("Signed URL Error:", error); + throw new Error("Failed to generate file URL"); + } +}; + +module.exports = { + uploadToS3, + getSignedFileUrl, +}; diff --git a/app/utils/validation/validateSupplier.util.js b/app/utils/validation/validateSupplier.util.js new file mode 100644 index 0000000..46bad62 --- /dev/null +++ b/app/utils/validation/validateSupplier.util.js @@ -0,0 +1,72 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/utils/validation/validateSupplier.util.js + + +const db = require("../../models"); +const Supplier = db.Vendor; + +const extractCellValue = (value) => { + if (value && typeof value === "object") { + return value.text || value.result || ""; + } + return value?.toString().trim() || ""; +}; + +const validateSupplierColumn = async (worksheet, columns) => { + const supplierIdCol = + columns.findIndex((col) => col.column_name === "supplier_id") + 1; + + if (!supplierIdCol) return; + + const supplierIds = new Set(); + const errors = []; + + // 🔹 Extract values (ONLY collect non-empty) + for (let i = 2; i <= worksheet.rowCount; i++) { + let value = extractCellValue( + worksheet.getRow(i).getCell(supplierIdCol).value + ); + + if (value) { + supplierIds.add(value); + } + } + + if (supplierIds.size === 0) return []; + + // 🔹 Batch DB validation + const suppliers = await Supplier.findAll({ + where: { + vendor_id: [...supplierIds], + }, + }); + + const validIds = new Set(suppliers.map((s) => s.vendor_id)); + + // 🔹 Validate ONLY non-empty values + for (let i = 2; i <= worksheet.rowCount; i++) { + let value = extractCellValue( + worksheet.getRow(i).getCell(supplierIdCol).value + ); + + if (value && !validIds.has(value)) { + errors.push({ + row: i, + column: "supplier_id", + message: `Invalid supplier ID: ${value}`, + }); + } + } + + return errors; +}; + +module.exports = { validateSupplierColumn }; \ No newline at end of file diff --git a/app/utils/venStat.utill.js b/app/utils/venStat.utill.js new file mode 100644 index 0000000..66aa3d0 --- /dev/null +++ b/app/utils/venStat.utill.js @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/utils/venStat.utill.js + +// Function to calculate vendor status based on approval flags +const calVendorStatus = (is_md_approved, is_manager_approved) => { + // 1. Nothing decided yet + if (is_manager_approved === null && is_md_approved === null) { + return "Pending Approval"; + } + + // 2. Manager decided, MD pending + if (is_md_approved === null) { + if (is_manager_approved === true) return "Manager Approved"; + if (is_manager_approved === false) return "Manager Rejected"; + } + + // 3. MD decision is FINAL (always overrides) + if (is_md_approved === true) { + return "Approved"; + } + + if (is_md_approved === false) { + return "MD Rejected"; + } + + return "Pending Approval"; +}; + +module.exports = { + calVendorStatus, +} \ No newline at end of file diff --git a/app/workers/activity.worker.js b/app/workers/activity.worker.js new file mode 100644 index 0000000..39d052a --- /dev/null +++ b/app/workers/activity.worker.js @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/workers/activity.worker.js + +const { Worker } = require("bullmq"); +const connection = require("../config/redisClient"); +const db = require("../models"); + +const UserActivity = db.UserActivity; + +const createActivityWorker = () => { + const worker = new Worker( + "activity-queue", + async (job) => { + await UserActivity.create(job.data); + }, + { + connection, + }, + ); + + worker.on("completed", (job) => { + console.log(`✅ Activity logged: ${job.id}`); + }); + + worker.on("failed", (job, err) => { + console.error(`❌ Activity failed: ${job.id}`, err.message); + }); + + return worker; +}; + +module.exports = createActivityWorker; diff --git a/app/workers/document.worker.js b/app/workers/document.worker.js new file mode 100644 index 0000000..9c69438 --- /dev/null +++ b/app/workers/document.worker.js @@ -0,0 +1,96 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/workers/pdf.worker.js + +const { Worker } = require("bullmq"); + +const redis = require("../config/redisClient"); + +const { generateDocument } = require("../logic/documents"); + +const { uploadToS3 } = require("../utils/s3Upload.utill"); + +module.exports = async function createDocumentWorker() { + const documentWorker = new Worker( + "document-generation", + + async (job) => { + try { + const { document, documentType, data } = job.data; + + console.log( + `📄 Processing Job ${job.id}: document="${document}", documentType="${documentType}"`, + ); + + const { fileBuffer, fileName, mimeType } = await generateDocument( + document, + documentType, + data, + ); + + /** + * Store in S3 instead of local filesystem + */ + const s3Key = await uploadToS3( + { + originalname: fileName, + buffer: fileBuffer, + mimetype: mimeType, + }, + "document", + ); + + // Extract UUID from S3 key + const documentId = s3Key + .split("/") + .pop() + .replace(/\.[^/.]+$/, ""); + + console.log(`✅ Job ${job.id} completed: ${fileName} (${s3Key})`); + + /** + * Keep response compatible with existing flow + */ + return { + fileName, + mimeType, + size: fileBuffer.length, + s3Key, + documentId, + }; + } catch (err) { + console.error(`❌ Job ${job.id} failed:`, err.message); + + throw err; + } + }, + + { + connection: redis, + concurrency: 2, + }, + ); + + documentWorker.on("error", (err) => { + console.error("❌ Document Worker Error:", err); + }); + + documentWorker.on("failed", (job, err) => { + console.error(`❌ Job ${job?.id} failed after retries:`, err.message); + }); + + documentWorker.on("completed", (job) => { + console.log(`✅ Worker completed Job ${job.id}`); + }); + + console.log("📄 Document Worker initialized"); + + return documentWorker; +}; diff --git a/app/workers/index.js b/app/workers/index.js new file mode 100644 index 0000000..044f936 --- /dev/null +++ b/app/workers/index.js @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/workers/index.js + +require("dotenv").config() +const createActivityWorker = require("./activity.worker"); +const createLogWorker = require("./log.worker"); +const createDocumentWorker = require("./document.worker"); + +// Import future workers here +// const createEmailWorker = require("./email.worker"); + +console.log("🚀 Starting workers..."); + +// Initialize workers (async now) +(async () => { + try { + createActivityWorker(); + createLogWorker(); + await createDocumentWorker(); + + console.log("✅ All workers started"); + } catch (err) { + console.error("❌ Error starting workers:", err.message); + process.exit(1); + } +})(); \ No newline at end of file diff --git a/app/workers/log.worker.js b/app/workers/log.worker.js new file mode 100644 index 0000000..7bc4d4d --- /dev/null +++ b/app/workers/log.worker.js @@ -0,0 +1,73 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// app/workers/log.worker.js + +const { Worker } = require("bullmq"); +const fs = require("fs"); +const path = require("path"); + +const connection = require("../config/redisClient"); + +const logsDirectory = path.join(__dirname, "../../logs"); + +// Ensure logs directory exists +fs.mkdirSync(logsDirectory, { recursive: true }); + +const createLogWorker = () => { + const worker = new Worker( + "logQueue", + + async (job) => { + const { message, timestamp } = job.data; + + const logDate = new Date(timestamp) + .toISOString() + .split("T")[0]; + + const logFilePath = path.join( + logsDirectory, + `app-${logDate}.log` + ); + + const logLine = `[${timestamp}] ${message}\n`; + + await fs.promises.appendFile( + logFilePath, + logLine, + "utf8" + ); + }, + + { + connection, + } + ); + + worker.on("completed", (job) => { + console.log(`Log job ${job.id} completed`); + }); + + worker.on("failed", (job, err) => { + console.error( + `Log job ${job?.id} failed:`, + err + ); + }); + + worker.on("error", (err) => { + console.error("Log worker error:", err); + }); + + console.log("🟢 Log worker started"); + + return worker; +}; + +module.exports = createLogWorker; \ No newline at end of file diff --git a/cron/index.js b/cron/index.js new file mode 100644 index 0000000..23beb3c --- /dev/null +++ b/cron/index.js @@ -0,0 +1,21 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// cron/index.js + +const startCleanInactiveNotificationsCron = require("./notificationCleaning.cron"); + +function startAllCrons() { + console.log("Starting Cron Jobs..."); + + startCleanInactiveNotificationsCron(); + +} + +module.exports = startAllCrons; \ No newline at end of file diff --git a/cron/notificationCleaning.cron.js b/cron/notificationCleaning.cron.js new file mode 100644 index 0000000..4630de5 --- /dev/null +++ b/cron/notificationCleaning.cron.js @@ -0,0 +1,98 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// cron/cleanInactiveNotifications.cron.js + +const cron = require("node-cron"); + +const { + sequelize, + notification, + userNotification, +} = require("../app/models"); + +function startCleanInactiveNotificationsCron() { + console.log("Inactive Notification Cleanup Cron Started"); + + const task = cron.schedule( + "0 0 * * *", // Run every day at midnight + async () => { + console.log( + `[CRON] Cleaning inactive notifications... ${new Date().toISOString()}`, + ); + + try { + const inactiveNotifications = await notification.findAll({ + where: { + isActive: false, + }, + attributes: ["notification_id"], + }); + + if (!inactiveNotifications.length) { + console.log("[CRON] No inactive notifications found."); + return; + } + + const notificationIds = inactiveNotifications.map( + (item) => item.notification_id, + ); + + const transaction = await sequelize.transaction(); + + try { + // Delete user-notification relationships first + const deletedUserNotifications = await userNotification.destroy({ + where: { + notification_id: notificationIds, + }, + transaction, + }); + + // Delete inactive notifications + const deletedNotifications = await notification.destroy({ + where: { + notification_id: notificationIds, + isActive: false, + }, + transaction, + }); + + await transaction.commit(); + + console.log( + `[CRON] Cleanup completed. Deleted ${deletedNotifications} notifications and ${deletedUserNotifications} user notification records.`, + ); + } catch (err) { + await transaction.rollback(); + + console.error( + "[CRON] Failed to clean inactive notifications:", + err, + ); + } + } catch (err) { + console.error( + "[CRON] Error while checking inactive notifications:", + err, + ); + } + }, + { + scheduled: false, + }, + ); + + task.start(); + + return task; +} + +module.exports = startCleanInactiveNotificationsCron; + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..75debdb --- /dev/null +++ b/package-lock.json @@ -0,0 +1,10300 @@ +{ + "name": "backend", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "backend", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@aws-sdk/client-s3": "^3.1021.0", + "@aws-sdk/s3-request-presigner": "^3.1021.0", + "@bull-board/api": "^6.20.6", + "@bull-board/express": "^6.20.6", + "axios": "^1.17.0", + "bcrypt": "^6.0.0", + "bullmq": "^5.71.1", + "cookie-parser": "^1.4.7", + "cors": "^2.8.6", + "dotenv": "^17.2.4", + "ejs": "^3.1.10", + "exceljs": "^4.4.0", + "express": "^5.2.1", + "ioredis": "^5.10.1", + "jsonwebtoken": "^9.0.3", + "morgan": "^1.10.1", + "multer": "^2.1.1", + "mysql2": "^3.17.0", + "node-cron": "^4.5.0", + "nodemailer": "^8.0.1", + "nodeman": "^1.1.2", + "nodemon": "^3.1.11", + "pdfmake": "^0.2.7", + "puppeteer": "^24.43.1", + "sequelize": "^6.37.7", + "uuid": "^13.0.0" + }, + "devDependencies": { + "jest": "^30.4.2" + } + }, + "node_modules/@aws-crypto/crc32": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", + "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/crc32c": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32c/-/crc32c-5.2.0.tgz", + "integrity": "sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha1-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha1-browser/-/sha1-browser-5.2.0.tgz", + "integrity": "sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", + "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-js": "^5.2.0", + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", + "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", + "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3": { + "version": "3.1021.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.1021.0.tgz", + "integrity": "sha512-BCfggq8gYSjlKOZlMSVApix3cgKAQIWGeoJFX/AU5HMvqz1BZBEw83jJFL9LYrqTPCocH8NGl++1Xr70ro+jcg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha1-browser": "5.2.0", + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.973.26", + "@aws-sdk/credential-provider-node": "^3.972.29", + "@aws-sdk/middleware-bucket-endpoint": "^3.972.8", + "@aws-sdk/middleware-expect-continue": "^3.972.8", + "@aws-sdk/middleware-flexible-checksums": "^3.974.6", + "@aws-sdk/middleware-host-header": "^3.972.8", + "@aws-sdk/middleware-location-constraint": "^3.972.8", + "@aws-sdk/middleware-logger": "^3.972.8", + "@aws-sdk/middleware-recursion-detection": "^3.972.9", + "@aws-sdk/middleware-sdk-s3": "^3.972.27", + "@aws-sdk/middleware-ssec": "^3.972.8", + "@aws-sdk/middleware-user-agent": "^3.972.28", + "@aws-sdk/region-config-resolver": "^3.972.10", + "@aws-sdk/signature-v4-multi-region": "^3.996.15", + "@aws-sdk/types": "^3.973.6", + "@aws-sdk/util-endpoints": "^3.996.5", + "@aws-sdk/util-user-agent-browser": "^3.972.8", + "@aws-sdk/util-user-agent-node": "^3.973.14", + "@smithy/config-resolver": "^4.4.13", + "@smithy/core": "^3.23.13", + "@smithy/eventstream-serde-browser": "^4.2.12", + "@smithy/eventstream-serde-config-resolver": "^4.3.12", + "@smithy/eventstream-serde-node": "^4.2.12", + "@smithy/fetch-http-handler": "^5.3.15", + "@smithy/hash-blob-browser": "^4.2.13", + "@smithy/hash-node": "^4.2.12", + "@smithy/hash-stream-node": "^4.2.12", + "@smithy/invalid-dependency": "^4.2.12", + "@smithy/md5-js": "^4.2.12", + "@smithy/middleware-content-length": "^4.2.12", + "@smithy/middleware-endpoint": "^4.4.28", + "@smithy/middleware-retry": "^4.4.46", + "@smithy/middleware-serde": "^4.2.16", + "@smithy/middleware-stack": "^4.2.12", + "@smithy/node-config-provider": "^4.3.12", + "@smithy/node-http-handler": "^4.5.1", + "@smithy/protocol-http": "^5.3.12", + "@smithy/smithy-client": "^4.12.8", + "@smithy/types": "^4.13.1", + "@smithy/url-parser": "^4.2.12", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-body-length-browser": "^4.2.2", + "@smithy/util-body-length-node": "^4.2.3", + "@smithy/util-defaults-mode-browser": "^4.3.44", + "@smithy/util-defaults-mode-node": "^4.2.48", + "@smithy/util-endpoints": "^3.3.3", + "@smithy/util-middleware": "^4.2.12", + "@smithy/util-retry": "^4.2.13", + "@smithy/util-stream": "^4.5.21", + "@smithy/util-utf8": "^4.2.2", + "@smithy/util-waiter": "^4.2.14", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/core": { + "version": "3.973.26", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.973.26.tgz", + "integrity": "sha512-A/E6n2W42ruU+sfWk+mMUOyVXbsSgGrY3MJ9/0Az5qUdG67y8I6HYzzoAa+e/lzxxl1uCYmEL6BTMi9ZiZnplQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.6", + "@aws-sdk/xml-builder": "^3.972.16", + "@smithy/core": "^3.23.13", + "@smithy/node-config-provider": "^4.3.12", + "@smithy/property-provider": "^4.2.12", + "@smithy/protocol-http": "^5.3.12", + "@smithy/signature-v4": "^5.3.12", + "@smithy/smithy-client": "^4.12.8", + "@smithy/types": "^4.13.1", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-middleware": "^4.2.12", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/crc64-nvme": { + "version": "3.972.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/crc64-nvme/-/crc64-nvme-3.972.5.tgz", + "integrity": "sha512-2VbTstbjKdT+yKi8m7b3a9CiVac+pL/IY2PHJwsaGkkHmuuqkJZIErPck1h6P3T9ghQMLSdMPyW6Qp7Di5swFg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.972.24", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.24.tgz", + "integrity": "sha512-FWg8uFmT6vQM7VuzELzwVo5bzExGaKHdubn0StjgrcU5FvuLExUe+k06kn/40uKv59rYzhez8eFNM4yYE/Yb/w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.26", + "@aws-sdk/types": "^3.973.6", + "@smithy/property-provider": "^4.2.12", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.972.26", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.26.tgz", + "integrity": "sha512-CY4ppZ+qHYqcXqBVi//sdHST1QK3KzOEiLtpLsc9W2k2vfZPKExGaQIsOwcyvjpjUEolotitmd3mUNY56IwDEA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.26", + "@aws-sdk/types": "^3.973.6", + "@smithy/fetch-http-handler": "^5.3.15", + "@smithy/node-http-handler": "^4.5.1", + "@smithy/property-provider": "^4.2.12", + "@smithy/protocol-http": "^5.3.12", + "@smithy/smithy-client": "^4.12.8", + "@smithy/types": "^4.13.1", + "@smithy/util-stream": "^4.5.21", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.972.28", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.28.tgz", + "integrity": "sha512-wXYvq3+uQcZV7k+bE4yDXCTBdzWTU9x/nMiKBfzInmv6yYK1veMK0AKvRfRBd72nGWYKcL6AxwiPg9z/pYlgpw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.26", + "@aws-sdk/credential-provider-env": "^3.972.24", + "@aws-sdk/credential-provider-http": "^3.972.26", + "@aws-sdk/credential-provider-login": "^3.972.28", + "@aws-sdk/credential-provider-process": "^3.972.24", + "@aws-sdk/credential-provider-sso": "^3.972.28", + "@aws-sdk/credential-provider-web-identity": "^3.972.28", + "@aws-sdk/nested-clients": "^3.996.18", + "@aws-sdk/types": "^3.973.6", + "@smithy/credential-provider-imds": "^4.2.12", + "@smithy/property-provider": "^4.2.12", + "@smithy/shared-ini-file-loader": "^4.4.7", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login": { + "version": "3.972.28", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.28.tgz", + "integrity": "sha512-ZSTfO6jqUTCysbdBPtEX5OUR//3rbD0lN7jO3sQeS2Gjr/Y+DT6SbIJ0oT2cemNw3UzKu97sNONd1CwNMthuZQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.26", + "@aws-sdk/nested-clients": "^3.996.18", + "@aws-sdk/types": "^3.973.6", + "@smithy/property-provider": "^4.2.12", + "@smithy/protocol-http": "^5.3.12", + "@smithy/shared-ini-file-loader": "^4.4.7", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.972.29", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.29.tgz", + "integrity": "sha512-clSzDcvndpFJAggLDnDb36sPdlZYyEs5Zm6zgZjjUhwsJgSWiWKwFIXUVBcbruidNyBdbpOv2tNDL9sX8y3/0g==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "^3.972.24", + "@aws-sdk/credential-provider-http": "^3.972.26", + "@aws-sdk/credential-provider-ini": "^3.972.28", + "@aws-sdk/credential-provider-process": "^3.972.24", + "@aws-sdk/credential-provider-sso": "^3.972.28", + "@aws-sdk/credential-provider-web-identity": "^3.972.28", + "@aws-sdk/types": "^3.973.6", + "@smithy/credential-provider-imds": "^4.2.12", + "@smithy/property-provider": "^4.2.12", + "@smithy/shared-ini-file-loader": "^4.4.7", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.972.24", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.24.tgz", + "integrity": "sha512-Q2k/XLrFXhEztPHqj4SLCNID3hEPdlhh1CDLBpNnM+1L8fq7P+yON9/9M1IGN/dA5W45v44ylERfXtDAlmMNmw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.26", + "@aws-sdk/types": "^3.973.6", + "@smithy/property-provider": "^4.2.12", + "@smithy/shared-ini-file-loader": "^4.4.7", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.972.28", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.28.tgz", + "integrity": "sha512-IoUlmKMLEITFn1SiCTjPfR6KrE799FBo5baWyk/5Ppar2yXZoUdaRqZzJzK6TcJxx450M8m8DbpddRVYlp5R/A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.26", + "@aws-sdk/nested-clients": "^3.996.18", + "@aws-sdk/token-providers": "3.1021.0", + "@aws-sdk/types": "^3.973.6", + "@smithy/property-provider": "^4.2.12", + "@smithy/shared-ini-file-loader": "^4.4.7", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.972.28", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.28.tgz", + "integrity": "sha512-d+6h0SD8GGERzKe27v5rOzNGKOl0D+l0bWJdqrxH8WSQzHzjsQFIAPgIeOTUwBHVsKKwtSxc91K/SWax6XgswQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.26", + "@aws-sdk/nested-clients": "^3.996.18", + "@aws-sdk/types": "^3.973.6", + "@smithy/property-provider": "^4.2.12", + "@smithy/shared-ini-file-loader": "^4.4.7", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-bucket-endpoint": { + "version": "3.972.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.972.8.tgz", + "integrity": "sha512-WR525Rr2QJSETa9a050isktyWi/4yIGcmY3BQ1kpHqb0LqUglQHCS8R27dTJxxWNZvQ0RVGtEZjTCbZJpyF3Aw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.6", + "@aws-sdk/util-arn-parser": "^3.972.3", + "@smithy/node-config-provider": "^4.3.12", + "@smithy/protocol-http": "^5.3.12", + "@smithy/types": "^4.13.1", + "@smithy/util-config-provider": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-expect-continue": { + "version": "3.972.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.972.8.tgz", + "integrity": "sha512-5DTBTiotEES1e2jOHAq//zyzCjeMB78lEHd35u15qnrid4Nxm7diqIf9fQQ3Ov0ChH1V3Vvt13thOnrACmfGVQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.6", + "@smithy/protocol-http": "^5.3.12", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-flexible-checksums": { + "version": "3.974.6", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.974.6.tgz", + "integrity": "sha512-YckB8k1ejbyCg/g36gUMFLNzE4W5cERIa4MtsdO+wpTmJEP0+TB7okWIt7d8TDOvnb7SwvxJ21E4TGOBxFpSWQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@aws-crypto/crc32c": "5.2.0", + "@aws-crypto/util": "5.2.0", + "@aws-sdk/core": "^3.973.26", + "@aws-sdk/crc64-nvme": "^3.972.5", + "@aws-sdk/types": "^3.973.6", + "@smithy/is-array-buffer": "^4.2.2", + "@smithy/node-config-provider": "^4.3.12", + "@smithy/protocol-http": "^5.3.12", + "@smithy/types": "^4.13.1", + "@smithy/util-middleware": "^4.2.12", + "@smithy/util-stream": "^4.5.21", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.972.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.972.8.tgz", + "integrity": "sha512-wAr2REfKsqoKQ+OkNqvOShnBoh+nkPurDKW7uAeVSu6kUECnWlSJiPvnoqxGlfousEY/v9LfS9sNc46hjSYDIQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.6", + "@smithy/protocol-http": "^5.3.12", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-location-constraint": { + "version": "3.972.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.972.8.tgz", + "integrity": "sha512-KaUoFuoFPziIa98DSQsTPeke1gvGXlc5ZGMhy+b+nLxZ4A7jmJgLzjEF95l8aOQN2T/qlPP3MrAyELm8ExXucw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.6", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.972.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.972.8.tgz", + "integrity": "sha512-CWl5UCM57WUFaFi5kB7IBY1UmOeLvNZAZ2/OZ5l20ldiJ3TiIz1pC65gYj8X0BCPWkeR1E32mpsCk1L1I4n+lA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.6", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.972.9", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.972.9.tgz", + "integrity": "sha512-/Wt5+CT8dpTFQxEJ9iGy/UGrXr7p2wlIOEHvIr/YcHYByzoLjrqkYqXdJjd9UIgWjv7eqV2HnFJen93UTuwfTQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.6", + "@aws/lambda-invoke-store": "^0.2.2", + "@smithy/protocol-http": "^5.3.12", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-s3": { + "version": "3.972.27", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.972.27.tgz", + "integrity": "sha512-gomO6DZwx+1D/9mbCpcqO5tPBqYBK7DtdgjTIjZ4yvfh/S7ETwAPS0XbJgP2JD8Ycr5CwVrEkV1sFtu3ShXeOw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.26", + "@aws-sdk/types": "^3.973.6", + "@aws-sdk/util-arn-parser": "^3.972.3", + "@smithy/core": "^3.23.13", + "@smithy/node-config-provider": "^4.3.12", + "@smithy/protocol-http": "^5.3.12", + "@smithy/signature-v4": "^5.3.12", + "@smithy/smithy-client": "^4.12.8", + "@smithy/types": "^4.13.1", + "@smithy/util-config-provider": "^4.2.2", + "@smithy/util-middleware": "^4.2.12", + "@smithy/util-stream": "^4.5.21", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-ssec": { + "version": "3.972.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.972.8.tgz", + "integrity": "sha512-wqlK0yO/TxEC2UsY9wIlqeeutF6jjLe0f96Pbm40XscTo57nImUk9lBcw0dPgsm0sppFtAkSlDrfpK+pC30Wqw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.6", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.972.28", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.972.28.tgz", + "integrity": "sha512-cfWZFlVh7Va9lRay4PN2A9ARFzaBYcA097InT5M2CdRS05ECF5yaz86jET8Wsl2WcyKYEvVr/QNmKtYtafUHtQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.26", + "@aws-sdk/types": "^3.973.6", + "@aws-sdk/util-endpoints": "^3.996.5", + "@smithy/core": "^3.23.13", + "@smithy/protocol-http": "^5.3.12", + "@smithy/types": "^4.13.1", + "@smithy/util-retry": "^4.2.13", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients": { + "version": "3.996.18", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.996.18.tgz", + "integrity": "sha512-c7ZSIXrESxHKx2Mcopgd8AlzZgoXMr20fkx5ViPWPOLBvmyhw9VwJx/Govg8Ef/IhEon5R9l53Z8fdYSEmp6VA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.973.26", + "@aws-sdk/middleware-host-header": "^3.972.8", + "@aws-sdk/middleware-logger": "^3.972.8", + "@aws-sdk/middleware-recursion-detection": "^3.972.9", + "@aws-sdk/middleware-user-agent": "^3.972.28", + "@aws-sdk/region-config-resolver": "^3.972.10", + "@aws-sdk/types": "^3.973.6", + "@aws-sdk/util-endpoints": "^3.996.5", + "@aws-sdk/util-user-agent-browser": "^3.972.8", + "@aws-sdk/util-user-agent-node": "^3.973.14", + "@smithy/config-resolver": "^4.4.13", + "@smithy/core": "^3.23.13", + "@smithy/fetch-http-handler": "^5.3.15", + "@smithy/hash-node": "^4.2.12", + "@smithy/invalid-dependency": "^4.2.12", + "@smithy/middleware-content-length": "^4.2.12", + "@smithy/middleware-endpoint": "^4.4.28", + "@smithy/middleware-retry": "^4.4.46", + "@smithy/middleware-serde": "^4.2.16", + "@smithy/middleware-stack": "^4.2.12", + "@smithy/node-config-provider": "^4.3.12", + "@smithy/node-http-handler": "^4.5.1", + "@smithy/protocol-http": "^5.3.12", + "@smithy/smithy-client": "^4.12.8", + "@smithy/types": "^4.13.1", + "@smithy/url-parser": "^4.2.12", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-body-length-browser": "^4.2.2", + "@smithy/util-body-length-node": "^4.2.3", + "@smithy/util-defaults-mode-browser": "^4.3.44", + "@smithy/util-defaults-mode-node": "^4.2.48", + "@smithy/util-endpoints": "^3.3.3", + "@smithy/util-middleware": "^4.2.12", + "@smithy/util-retry": "^4.2.13", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/region-config-resolver": { + "version": "3.972.10", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.972.10.tgz", + "integrity": "sha512-1dq9ToC6e070QvnVhhbAs3bb5r6cQ10gTVc6cyRV5uvQe7P138TV2uG2i6+Yok4bAkVAcx5AqkTEBUvWEtBlsQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.6", + "@smithy/config-resolver": "^4.4.13", + "@smithy/node-config-provider": "^4.3.12", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/s3-request-presigner": { + "version": "3.1021.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/s3-request-presigner/-/s3-request-presigner-3.1021.0.tgz", + "integrity": "sha512-kkIzsIAc7wnG7vVRkZFIwJ3noOyF3S6ozOQ9t2KxzPde1LsmpmPwYbmiB91DzdfuGySdk4Hpb0JmHh4KhGECXQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/signature-v4-multi-region": "^3.996.15", + "@aws-sdk/types": "^3.973.6", + "@aws-sdk/util-format-url": "^3.972.8", + "@smithy/middleware-endpoint": "^4.4.28", + "@smithy/protocol-http": "^5.3.12", + "@smithy/smithy-client": "^4.12.8", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4-multi-region": { + "version": "3.996.15", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.15.tgz", + "integrity": "sha512-Ukw2RpqvaL96CjfH/FgfBmy/ZosHBqoHBCFsN61qGg99F33vpntIVii8aNeh65XuOja73arSduskoa4OJea9RQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-sdk-s3": "^3.972.27", + "@aws-sdk/types": "^3.973.6", + "@smithy/protocol-http": "^5.3.12", + "@smithy/signature-v4": "^5.3.12", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/token-providers": { + "version": "3.1021.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1021.0.tgz", + "integrity": "sha512-TKY6h9spUk3OLs5v1oAgW9mAeBE3LAGNBwJokLy96wwmd4W2v/tYlXseProyed9ValDj2u1jK/4Rg1T+1NXyJA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.973.26", + "@aws-sdk/nested-clients": "^3.996.18", + "@aws-sdk/types": "^3.973.6", + "@smithy/property-provider": "^4.2.12", + "@smithy/shared-ini-file-loader": "^4.4.7", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/types": { + "version": "3.973.6", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.973.6.tgz", + "integrity": "sha512-Atfcy4E++beKtwJHiDln2Nby8W/mam64opFPTiHEqgsthqeydFS1pY+OUlN1ouNOmf8ArPU/6cDS65anOP3KQw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/util-arn-parser": { + "version": "3.972.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.972.3.tgz", + "integrity": "sha512-HzSD8PMFrvgi2Kserxuff5VitNq2sgf3w9qxmskKDiDTThWfVteJxuCS9JXiPIPtmCrp+7N9asfIaVhBFORllA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.996.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.996.5.tgz", + "integrity": "sha512-Uh93L5sXFNbyR5sEPMzUU8tJ++Ku97EY4udmC01nB8Zu+xfBPwpIwJ6F7snqQeq8h2pf+8SGN5/NoytfKgYPIw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.6", + "@smithy/types": "^4.13.1", + "@smithy/url-parser": "^4.2.12", + "@smithy/util-endpoints": "^3.3.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/util-format-url": { + "version": "3.972.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-format-url/-/util-format-url-3.972.8.tgz", + "integrity": "sha512-J6DS9oocrgxM8xlUTTmQOuwRF6rnAGEujAN9SAzllcrQmwn5iJ58ogxy3SEhD0Q7JZvlA5jvIXBkpQRqEqlE9A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.6", + "@smithy/querystring-builder": "^4.2.12", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.965.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.965.5.tgz", + "integrity": "sha512-WhlJNNINQB+9qtLtZJcpQdgZw3SCDCpXdUJP7cToGwHbCWCnRckGlc6Bx/OhWwIYFNAn+FIydY8SZ0QmVu3xTQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.972.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.972.8.tgz", + "integrity": "sha512-B3KGXJviV2u6Cdw2SDY2aDhoJkVfY/Q/Trwk2CMSkikE1Oi6gRzxhvhIfiRpHfmIsAhV4EA54TVEX8K6CbHbkA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.6", + "@smithy/types": "^4.13.1", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.973.14", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.973.14.tgz", + "integrity": "sha512-vNSB/DYaPOyujVZBg/zUznH9QC142MaTHVmaFlF7uzzfg3CgT9f/l4C0Yi+vU/tbBhxVcXVB90Oohk5+o+ZbWw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "^3.972.28", + "@aws-sdk/types": "^3.973.6", + "@smithy/node-config-provider": "^4.3.12", + "@smithy/types": "^4.13.1", + "@smithy/util-config-provider": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/xml-builder": { + "version": "3.972.16", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.16.tgz", + "integrity": "sha512-iu2pyvaqmeatIJLURLqx9D+4jKAdTH20ntzB6BFwjyN7V960r4jK32mx0Zf7YbtOYAbmbtQfDNuL60ONinyw7A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.1", + "fast-xml-parser": "5.5.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws/lambda-invoke-store": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.4.tgz", + "integrity": "sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz", + "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.29.7.tgz", + "integrity": "sha512-zGYcYfq/WmZ4V+kBIXQon9dSSc8ircGZqw9ZaNhhGj9nZkeBu1jHLBDQqYYi5WA9uawvA2sIMbry2nCFhf5Djg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.29.7.tgz", + "integrity": "sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.29.7.tgz", + "integrity": "sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz", + "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@bull-board/api": { + "version": "6.20.6", + "resolved": "https://registry.npmjs.org/@bull-board/api/-/api-6.20.6.tgz", + "integrity": "sha512-B/9OlKWJ/He421Lyuc7htMpKN4R8hcNu5uoIRiyh9y9J0kMOfxfs82GKXdfR9R0595LvAcCOSvjn8EtWHXesTA==", + "license": "MIT", + "dependencies": { + "redis-info": "^3.1.0" + }, + "peerDependencies": { + "@bull-board/ui": "6.20.6" + } + }, + "node_modules/@bull-board/express": { + "version": "6.20.6", + "resolved": "https://registry.npmjs.org/@bull-board/express/-/express-6.20.6.tgz", + "integrity": "sha512-QBOjhi3jR4qIJvLCo++DdL0CyG4umiFXBCPJYr74DmRN41m27rDS3NSf7ceHdbQr9Ol8mw3xbVqGLn50x3QdWQ==", + "license": "MIT", + "dependencies": { + "@bull-board/api": "6.20.6", + "@bull-board/ui": "6.20.6", + "ejs": "^3.1.10", + "express": "^5.2.1" + } + }, + "node_modules/@bull-board/ui": { + "version": "6.20.6", + "resolved": "https://registry.npmjs.org/@bull-board/ui/-/ui-6.20.6.tgz", + "integrity": "sha512-nEXRgMHHd48ssINgvDqT7b4p/+57XRf3CUcTGQ7UIJb4F7n/kRRj8+ZQvQmMFsYGdNmbNU2eNheQ05vXSYiqdQ==", + "license": "MIT", + "dependencies": { + "@bull-board/api": "6.20.6" + } + }, + "node_modules/@emnapi/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@fast-csv/format": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/@fast-csv/format/-/format-4.3.5.tgz", + "integrity": "sha512-8iRn6QF3I8Ak78lNAa+Gdl5MJJBM5vRHivFtMRUWINdevNo00K7OXxS2PshawLKTejVwieIlPmK5YlLu6w4u8A==", + "license": "MIT", + "dependencies": { + "@types/node": "^14.0.1", + "lodash.escaperegexp": "^4.1.2", + "lodash.isboolean": "^3.0.3", + "lodash.isequal": "^4.5.0", + "lodash.isfunction": "^3.0.9", + "lodash.isnil": "^4.0.0" + } + }, + "node_modules/@fast-csv/format/node_modules/@types/node": { + "version": "14.18.63", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.63.tgz", + "integrity": "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==", + "license": "MIT" + }, + "node_modules/@fast-csv/parse": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/@fast-csv/parse/-/parse-4.3.6.tgz", + "integrity": "sha512-uRsLYksqpbDmWaSmzvJcuApSEe38+6NQZBUsuAyMZKqHxH0g1wcJgsKUvN3WC8tewaqFjBMMGrkHmC+T7k8LvA==", + "license": "MIT", + "dependencies": { + "@types/node": "^14.0.1", + "lodash.escaperegexp": "^4.1.2", + "lodash.groupby": "^4.6.0", + "lodash.isfunction": "^3.0.9", + "lodash.isnil": "^4.0.0", + "lodash.isundefined": "^3.0.1", + "lodash.uniq": "^4.5.0" + } + }, + "node_modules/@fast-csv/parse/node_modules/@types/node": { + "version": "14.18.63", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.63.tgz", + "integrity": "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==", + "license": "MIT" + }, + "node_modules/@foliojs-fork/fontkit": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@foliojs-fork/fontkit/-/fontkit-1.9.2.tgz", + "integrity": "sha512-IfB5EiIb+GZk+77TRB86AHroVaqfq8JRFlUbz0WEwsInyCG0epX2tCPOy+UfaWPju30DeVoUAXfzWXmhn753KA==", + "license": "MIT", + "dependencies": { + "@foliojs-fork/restructure": "^2.0.2", + "brotli": "^1.2.0", + "clone": "^1.0.4", + "deep-equal": "^1.0.0", + "dfa": "^1.2.0", + "tiny-inflate": "^1.0.2", + "unicode-properties": "^1.2.2", + "unicode-trie": "^2.0.0" + } + }, + "node_modules/@foliojs-fork/linebreak": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@foliojs-fork/linebreak/-/linebreak-1.1.2.tgz", + "integrity": "sha512-ZPohpxxbuKNE0l/5iBJnOAfUaMACwvUIKCvqtWGKIMv1lPYoNjYXRfhi9FeeV9McBkBLxsMFWTVVhHJA8cyzvg==", + "license": "MIT", + "dependencies": { + "base64-js": "1.3.1", + "unicode-trie": "^2.0.0" + } + }, + "node_modules/@foliojs-fork/linebreak/node_modules/base64-js": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", + "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", + "license": "MIT" + }, + "node_modules/@foliojs-fork/pdfkit": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@foliojs-fork/pdfkit/-/pdfkit-0.13.0.tgz", + "integrity": "sha512-YXeG1fml9k97YNC9K8e292Pj2JzGt9uOIiBFuQFxHsdQ45BlxW+JU3RQK6JAvXU7kjhjP8rCcYvpk36JLD33sQ==", + "license": "MIT", + "dependencies": { + "@foliojs-fork/fontkit": "^1.9.1", + "@foliojs-fork/linebreak": "^1.1.1", + "crypto-js": "^4.0.0", + "png-js": "^1.0.0" + } + }, + "node_modules/@foliojs-fork/restructure": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@foliojs-fork/restructure/-/restructure-2.0.2.tgz", + "integrity": "sha512-59SgoZ3EXbkfSX7b63tsou/SDGzwUEK6MuB5sKqgVK1/XE0fxmpsOb9DQI8LXW3KfGnAjImCGhhEb7uPPAUVNA==", + "license": "MIT" + }, + "node_modules/@ioredis/commands": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.5.1.tgz", + "integrity": "sha512-JH8ZL/ywcJyR9MmJ5BNqZllXNZQqQbnVZOqpPQqE1vHiFgAw4NHbvE0FOduNU8IX9babitBT46571OnPTT0Zcw==", + "license": "MIT" + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.6.tgz", + "integrity": "sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-30.4.1.tgz", + "integrity": "sha512-v3bhyxUh9Hgmo5p6hAOXe14/R3ZxZDOsvHleh4B07z3m/x4/ngPUXEm9XwK4sF4u+f+P2ORb0Ge+MgpaqRMVDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.4.1", + "@types/node": "*", + "chalk": "^4.1.2", + "jest-message-util": "30.4.1", + "jest-util": "30.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/core": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.4.2.tgz", + "integrity": "sha512-TZJA6cPJUFxoWhxaLo8t0VX/MZX2wPWr0uIDvLSHIvN4gu9h02vSzqI2kBADG1ExqQlC+cY09xKMSreivvrChQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "30.4.1", + "@jest/pattern": "30.4.0", + "@jest/reporters": "30.4.1", + "@jest/test-result": "30.4.1", + "@jest/transform": "30.4.1", + "@jest/types": "30.4.1", + "@types/node": "*", + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "exit-x": "^0.2.2", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.11", + "jest-changed-files": "30.4.1", + "jest-config": "30.4.2", + "jest-haste-map": "30.4.1", + "jest-message-util": "30.4.1", + "jest-regex-util": "30.4.0", + "jest-resolve": "30.4.1", + "jest-resolve-dependencies": "30.4.2", + "jest-runner": "30.4.2", + "jest-runtime": "30.4.2", + "jest-snapshot": "30.4.1", + "jest-util": "30.4.1", + "jest-validate": "30.4.1", + "jest-watcher": "30.4.1", + "pretty-format": "30.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/diff-sequences": { + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.4.0.tgz", + "integrity": "sha512-zOpzlfUs45l6u7jm39qr87JCHUDsaeCtvL+kQe/Vn9jSnRB4/5IPXISm0h9I1vZW/o00Kn4UTJ2MOlhnUGwv3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/environment": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.4.1.tgz", + "integrity": "sha512-AK9yNRqgKxiabqMoe4oW+3/TSSeV8vkdC7BGaxZdU0AFXfOpofTLqdru2GXKZghP3sdgwE9XXpnVwfZ8JnFV4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/fake-timers": "30.4.1", + "@jest/types": "30.4.1", + "@types/node": "*", + "jest-mock": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.4.1.tgz", + "integrity": "sha512-ginrj6TMgh2GshLUGCjO94Ptx9HhdZA/I6A9iUfyeLKFtdAjnKzHDgzgP9HYQgbxM1lbXScQ2eUBz2lGeVDPWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "30.4.1", + "jest-snapshot": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.4.1.tgz", + "integrity": "sha512-ZBn5CglH8fBsQsvs4VWNzD4aWfUYks+IdOOQU3MEK71ol/BcVm+P+rtb1KpiFBpSWSCE27uOahyyf1vfqOVbcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.4.1.tgz", + "integrity": "sha512-iW5umdmfPeWzehrVhugFQZqCchSCud5S1l2YT0O9ZhjRR0ExclANDZkiSBwzqtnlOn0J1JXvO+HZ6rkuyOVOgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.4.1", + "@sinonjs/fake-timers": "^15.4.0", + "@types/node": "*", + "jest-message-util": "30.4.1", + "jest-mock": "30.4.1", + "jest-util": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/get-type": { + "version": "30.1.0", + "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz", + "integrity": "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.4.1.tgz", + "integrity": "sha512-ZbuY4cmXC8DkxYjfvT2DbcHWL2T6vmsMhXCDcmTB2T0y0gaezBI77ufq5ZAIdcRkYZ7NEQEDg1xFeKbxUJ5v5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.4.1", + "@jest/expect": "30.4.1", + "@jest/types": "30.4.1", + "jest-mock": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/pattern": { + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.4.0.tgz", + "integrity": "sha512-RAWn3+f9u8BsHijKJ71uHcFp6vmyEt6VvoWXkl6hKF3qVIuWNmudVjg12DlBPGup/frIl5UcUlH5HfEuvHpEXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-regex-util": "30.4.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-30.4.1.tgz", + "integrity": "sha512-/SnkPCzEQpUaBH81kjdEdDdo2WZl5hxw+BmLDGWjRkm8o7XlhjwsU36cqwe5PGBE5WYpBvDzRSdXx9rbGuJtNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "30.4.1", + "@jest/test-result": "30.4.1", + "@jest/transform": "30.4.1", + "@jest/types": "30.4.1", + "@jridgewell/trace-mapping": "^0.3.25", + "@types/node": "*", + "chalk": "^4.1.2", + "collect-v8-coverage": "^1.0.2", + "exit-x": "^0.2.2", + "glob": "^10.5.0", + "graceful-fs": "^4.2.11", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^5.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "30.4.1", + "jest-util": "30.4.1", + "jest-worker": "30.4.1", + "slash": "^3.0.0", + "string-length": "^4.0.2", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/reporters/node_modules/brace-expansion": { + "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, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@jest/reporters/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@jest/reporters/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@jest/schemas": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.4.1.tgz", + "integrity": "sha512-i6b4qw5qnP8c5FEeBJg/uZQ4ddrkN6Ca8qISJh0pr7a5hfn3h3v5x60BEbOC7OYAGZNMs1LfFLwnW2CuK8F57Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.34.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/snapshot-utils": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.4.1.tgz", + "integrity": "sha512-ObY4ljvQ95mt6iwKtVLetR/4yXiAgl3H4nJxhztr0MTjrN97TwDYrnCp/kF60Ec9HdhkWTHSu+Hg05aXfngpOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.4.1", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "natural-compare": "^1.4.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-30.0.1.tgz", + "integrity": "sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "callsites": "^3.1.0", + "graceful-fs": "^4.2.11" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.4.1.tgz", + "integrity": "sha512-/ZG7pgEiOmmWkN9TplKbOu4id2N5lh7FHwRwlkgBVAzGdRH+OkkQ8wX/kIxg4zmd3ZQvAL1RwL2yWsvNYYECTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "30.4.1", + "@jest/types": "30.4.1", + "@types/istanbul-lib-coverage": "^2.0.6", + "collect-v8-coverage": "^1.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.4.1.tgz", + "integrity": "sha512-PeYE+4td5rKjoRPxztObrXU+H8hsjZfxKMXOcmrr34JerSyB/ROOxbbicz8B7A5j9R9VayDnVPvBmedqCsFCdw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "30.4.1", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.4.1.tgz", + "integrity": "sha512-Wz0LyktlTvRefoymh+n64hQ84KNXsRGcwdoZ8CSa0Ea+fgYcHZlnk+hDP7v2MS7il2bQ5uTEIxf4/NNfhMN4KQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.27.4", + "@jest/types": "30.4.1", + "@jridgewell/trace-mapping": "^0.3.25", + "babel-plugin-istanbul": "^7.0.1", + "chalk": "^4.1.2", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.4.1", + "jest-regex-util": "30.4.0", + "jest-util": "30.4.1", + "pirates": "^4.0.7", + "slash": "^3.0.0", + "write-file-atomic": "^5.0.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/types": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.4.1.tgz", + "integrity": "sha512-f1x/vJXIfjOlEmejYpbkbgw1gOqpPECwMvMEtBqe47j7H2Hg8h8w3o3ikhSXq3MI15kg+oQ0exWO0uCtTNJLoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/pattern": "30.4.0", + "@jest/schemas": "30.4.1", + "@types/istanbul-lib-coverage": "^2.0.6", + "@types/istanbul-reports": "^3.0.4", + "@types/node": "*", + "@types/yargs": "^17.0.33", + "chalk": "^4.1.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.3.tgz", + "integrity": "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.3.tgz", + "integrity": "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.3.tgz", + "integrity": "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.3.tgz", + "integrity": "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.3.tgz", + "integrity": "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.3.tgz", + "integrity": "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.5.tgz", + "integrity": "sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@pkgr/core": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.3.6.tgz", + "integrity": "sha512-SEeaJLb3qBNF/OaXnaR1NmmBbFYk1zC0ZH/52fATcRPLFg/p791YrcyFFy44Bo9sLaGuSuLp5Q6axbb/O+v/RA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/pkgr" + } + }, + "node_modules/@puppeteer/browsers": { + "version": "2.13.2", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.13.2.tgz", + "integrity": "sha512-5EUZSUIc37H6aIXyWO0Z4y8NlF8NnjgmqeQgOGiswAU7pY0HOo16ho4+alIWmSfdZnjqBRawMsP3I5YqLSn6kw==", + "license": "Apache-2.0", + "dependencies": { + "debug": "^4.4.3", + "extract-zip": "^2.0.1", + "progress": "^2.0.3", + "proxy-agent": "^6.5.0", + "semver": "^7.7.4", + "tar-fs": "^3.1.1", + "yargs": "^17.7.2" + }, + "bin": { + "browsers": "lib/cjs/main-cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.34.49", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.49.tgz", + "integrity": "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "15.4.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-15.4.0.tgz", + "integrity": "sha512-DsG+8/LscQIQg68J6Ef3dv10u6nVyetYn923s3/sus5eaGfTo1of5WMZSLf0UJc9KDuKPilPH0UDJCjvNbDNCA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1" + } + }, + "node_modules/@smithy/chunked-blob-reader": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-5.2.2.tgz", + "integrity": "sha512-St+kVicSyayWQca+I1rGitaOEH6uKgE8IUWoYnnEX26SWdWQcL6LvMSD19Lg+vYHKdT9B2Zuu7rd3i6Wnyb/iw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/chunked-blob-reader-native": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-4.2.3.tgz", + "integrity": "sha512-jA5k5Udn7Y5717L86h4EIv06wIr3xn8GM1qHRi/Nf31annXcXHJjBKvgztnbn2TxH3xWrPBfgwHsOwZf0UmQWw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-base64": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/config-resolver": { + "version": "4.4.13", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.13.tgz", + "integrity": "sha512-iIzMC5NmOUP6WL6o8iPBjFhUhBZ9pPjpUpQYWMUFQqKyXXzOftbfK8zcQCz/jFV1Psmf05BK5ypx4K2r4Tnwdg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.12", + "@smithy/types": "^4.13.1", + "@smithy/util-config-provider": "^4.2.2", + "@smithy/util-endpoints": "^3.3.3", + "@smithy/util-middleware": "^4.2.12", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/core": { + "version": "3.23.13", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.23.13.tgz", + "integrity": "sha512-J+2TT9D6oGsUVXVEMvz8h2EmdVnkBiy2auCie4aSJMvKlzUtO5hqjEzXhoCUkIMo7gAYjbQcN0g/MMSXEhDs1Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.3.12", + "@smithy/types": "^4.13.1", + "@smithy/url-parser": "^4.2.12", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-body-length-browser": "^4.2.2", + "@smithy/util-middleware": "^4.2.12", + "@smithy/util-stream": "^4.5.21", + "@smithy/util-utf8": "^4.2.2", + "@smithy/uuid": "^1.1.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/credential-provider-imds": { + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.12.tgz", + "integrity": "sha512-cr2lR792vNZcYMriSIj+Um3x9KWrjcu98kn234xA6reOAFMmbRpQMOv8KPgEmLLtx3eldU6c5wALKFqNOhugmg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.12", + "@smithy/property-provider": "^4.2.12", + "@smithy/types": "^4.13.1", + "@smithy/url-parser": "^4.2.12", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-codec": { + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.2.12.tgz", + "integrity": "sha512-FE3bZdEl62ojmy8x4FHqxq2+BuOHlcxiH5vaZ6aqHJr3AIZzwF5jfx8dEiU/X0a8RboyNDjmXjlbr8AdEyLgiA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@smithy/types": "^4.13.1", + "@smithy/util-hex-encoding": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-browser": { + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.2.12.tgz", + "integrity": "sha512-XUSuMxlTxV5pp4VpqZf6Sa3vT/Q75FVkLSpSSE3KkWBvAQWeuWt1msTv8fJfgA4/jcJhrbrbMzN1AC/hvPmm5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^4.2.12", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-config-resolver": { + "version": "4.3.12", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.3.12.tgz", + "integrity": "sha512-7epsAZ3QvfHkngz6RXQYseyZYHlmWXSTPOfPmXkiS+zA6TBNo1awUaMFL9vxyXlGdoELmCZyZe1nQE+imbmV+Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-node": { + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.2.12.tgz", + "integrity": "sha512-D1pFuExo31854eAvg89KMn9Oab/wEeJR6Buy32B49A9Ogdtx5fwZPqBHUlDzaCDpycTFk2+fSQgX689Qsk7UGA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^4.2.12", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-universal": { + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.2.12.tgz", + "integrity": "sha512-+yNuTiyBACxOJUTvbsNsSOfH9G9oKbaJE1lNL3YHpGcuucl6rPZMi3nrpehpVOVR2E07YqFFmtwpImtpzlouHQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-codec": "^4.2.12", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/fetch-http-handler": { + "version": "5.3.15", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.15.tgz", + "integrity": "sha512-T4jFU5N/yiIfrtrsb9uOQn7RdELdM/7HbyLNr6uO/mpkj1ctiVs7CihVr51w4LyQlXWDpXFn4BElf1WmQvZu/A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.3.12", + "@smithy/querystring-builder": "^4.2.12", + "@smithy/types": "^4.13.1", + "@smithy/util-base64": "^4.3.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-blob-browser": { + "version": "4.2.13", + "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-4.2.13.tgz", + "integrity": "sha512-YrF4zWKh+ghLuquldj6e/RzE3xZYL8wIPfkt0MqCRphVICjyyjH8OwKD7LLlKpVEbk4FLizFfC1+gwK6XQdR3g==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/chunked-blob-reader": "^5.2.2", + "@smithy/chunked-blob-reader-native": "^4.2.3", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-node": { + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.12.tgz", + "integrity": "sha512-QhBYbGrbxTkZ43QoTPrK72DoYviDeg6YKDrHTMJbbC+A0sml3kSjzFtXP7BtbyJnXojLfTQldGdUR0RGD8dA3w==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.1", + "@smithy/util-buffer-from": "^4.2.2", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-stream-node": { + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-4.2.12.tgz", + "integrity": "sha512-O3YbmGExeafuM/kP7Y8r6+1y0hIh3/zn6GROx0uNlB54K9oihAL75Qtc+jFfLNliTi6pxOAYZrRKD9A7iA6UFw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.1", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/invalid-dependency": { + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.12.tgz", + "integrity": "sha512-/4F1zb7Z8LOu1PalTdESFHR0RbPwHd3FcaG1sI3UEIriQTWakysgJr65lc1jj6QY5ye7aFsisajotH6UhWfm/g==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/is-array-buffer": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.2.2.tgz", + "integrity": "sha512-n6rQ4N8Jj4YTQO3YFrlgZuwKodf4zUFs7EJIWH86pSCWBaAtAGBFfCM7Wx6D2bBJ2xqFNxGBSrUWswT3M0VJow==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/md5-js": { + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-4.2.12.tgz", + "integrity": "sha512-W/oIpHCpWU2+iAkfZYyGWE+qkpuf3vEXHLxQQDx9FPNZTTdnul0dZ2d/gUFrtQ5je1G2kp4cjG0/24YueG2LbQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.1", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-content-length": { + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.12.tgz", + "integrity": "sha512-YE58Yz+cvFInWI/wOTrB+DbvUVz/pLn5mC5MvOV4fdRUc6qGwygyngcucRQjAhiCEbmfLOXX0gntSIcgMvAjmA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.3.12", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-endpoint": { + "version": "4.4.28", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.28.tgz", + "integrity": "sha512-p1gfYpi91CHcs5cBq982UlGlDrxoYUX6XdHSo91cQ2KFuz6QloHosO7Jc60pJiVmkWrKOV8kFYlGFFbQ2WUKKQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.23.13", + "@smithy/middleware-serde": "^4.2.16", + "@smithy/node-config-provider": "^4.3.12", + "@smithy/shared-ini-file-loader": "^4.4.7", + "@smithy/types": "^4.13.1", + "@smithy/url-parser": "^4.2.12", + "@smithy/util-middleware": "^4.2.12", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-retry": { + "version": "4.4.46", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.46.tgz", + "integrity": "sha512-SpvWNNOPOrKQGUqZbEPO+es+FRXMWvIyzUKUOYdDgdlA6BdZj/R58p4umoQ76c2oJC44PiM7mKizyyex1IJzow==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.12", + "@smithy/protocol-http": "^5.3.12", + "@smithy/service-error-classification": "^4.2.12", + "@smithy/smithy-client": "^4.12.8", + "@smithy/types": "^4.13.1", + "@smithy/util-middleware": "^4.2.12", + "@smithy/util-retry": "^4.2.13", + "@smithy/uuid": "^1.1.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-serde": { + "version": "4.2.16", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.16.tgz", + "integrity": "sha512-beqfV+RZ9RSv+sQqor3xroUUYgRFCGRw6niGstPG8zO9LgTl0B0MCucxjmrH/2WwksQN7UUgI7KNANoZv+KALA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.23.13", + "@smithy/protocol-http": "^5.3.12", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-stack": { + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.12.tgz", + "integrity": "sha512-kruC5gRHwsCOuyCd4ouQxYjgRAym2uDlCvQ5acuMtRrcdfg7mFBg6blaxcJ09STpt3ziEkis6bhg1uwrWU7txw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-config-provider": { + "version": "4.3.12", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.12.tgz", + "integrity": "sha512-tr2oKX2xMcO+rBOjobSwVAkV05SIfUKz8iI53rzxEmgW3GOOPOv0UioSDk+J8OpRQnpnhsO3Af6IEBabQBVmiw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^4.2.12", + "@smithy/shared-ini-file-loader": "^4.4.7", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-http-handler": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.5.1.tgz", + "integrity": "sha512-ejjxdAXjkPIs9lyYyVutOGNOraqUE9v/NjGMKwwFrfOM354wfSD8lmlj8hVwUzQmlLLF4+udhfCX9Exnbmvfzw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.3.12", + "@smithy/querystring-builder": "^4.2.12", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/property-provider": { + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.12.tgz", + "integrity": "sha512-jqve46eYU1v7pZ5BM+fmkbq3DerkSluPr5EhvOcHxygxzD05ByDRppRwRPPpFrsFo5yDtCYLKu+kreHKVrvc7A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/protocol-http": { + "version": "5.3.12", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.12.tgz", + "integrity": "sha512-fit0GZK9I1xoRlR4jXmbLhoN0OdEpa96ul8M65XdmXnxXkuMxM0Y8HDT0Fh0Xb4I85MBvBClOzgSrV1X2s1Hxw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/querystring-builder": { + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.12.tgz", + "integrity": "sha512-6wTZjGABQufekycfDGMEB84BgtdOE/rCVTov+EDXQ8NHKTUNIp/j27IliwP7tjIU9LR+sSzyGBOXjeEtVgzCHg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.1", + "@smithy/util-uri-escape": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/querystring-parser": { + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.12.tgz", + "integrity": "sha512-P2OdvrgiAKpkPNKlKUtWbNZKB1XjPxM086NeVhK+W+wI46pIKdWBe5QyXvhUm3MEcyS/rkLvY8rZzyUdmyDZBw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/service-error-classification": { + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.12.tgz", + "integrity": "sha512-LlP29oSQN0Tw0b6D0Xo6BIikBswuIiGYbRACy5ujw/JgWSzTdYj46U83ssf6Ux0GyNJVivs2uReU8pt7Eu9okQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/shared-ini-file-loader": { + "version": "4.4.7", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.4.7.tgz", + "integrity": "sha512-HrOKWsUb+otTeo1HxVWeEb99t5ER1XrBi/xka2Wv6NVmTbuCUC1dvlrksdvxFtODLBjsC+PHK+fuy2x/7Ynyiw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/signature-v4": { + "version": "5.3.12", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.12.tgz", + "integrity": "sha512-B/FBwO3MVOL00DaRSXfXfa/TRXRheagt/q5A2NM13u7q+sHS59EOVGQNfG7DkmVtdQm5m3vOosoKAXSqn/OEgw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.2.2", + "@smithy/protocol-http": "^5.3.12", + "@smithy/types": "^4.13.1", + "@smithy/util-hex-encoding": "^4.2.2", + "@smithy/util-middleware": "^4.2.12", + "@smithy/util-uri-escape": "^4.2.2", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/smithy-client": { + "version": "4.12.8", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.12.8.tgz", + "integrity": "sha512-aJaAX7vHe5i66smoSSID7t4rKY08PbD8EBU7DOloixvhOozfYWdcSYE4l6/tjkZ0vBZhGjheWzB2mh31sLgCMA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.23.13", + "@smithy/middleware-endpoint": "^4.4.28", + "@smithy/middleware-stack": "^4.2.12", + "@smithy/protocol-http": "^5.3.12", + "@smithy/types": "^4.13.1", + "@smithy/util-stream": "^4.5.21", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "4.13.1", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.13.1.tgz", + "integrity": "sha512-787F3yzE2UiJIQ+wYW1CVg2odHjmaWLGksnKQHUrK/lYZSEcy1msuLVvxaR/sI2/aDe9U+TBuLsXnr3vod1g0g==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/url-parser": { + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.12.tgz", + "integrity": "sha512-wOPKPEpso+doCZGIlr+e1lVI6+9VAKfL4kZWFgzVgGWY2hZxshNKod4l2LXS3PRC9otH/JRSjtEHqQ/7eLciRA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/querystring-parser": "^4.2.12", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-base64": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.3.2.tgz", + "integrity": "sha512-XRH6b0H/5A3SgblmMa5ErXQ2XKhfbQB+Fm/oyLZ2O2kCUrwgg55bU0RekmzAhuwOjA9qdN5VU2BprOvGGUkOOQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.2.2", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-body-length-browser": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.2.2.tgz", + "integrity": "sha512-JKCrLNOup3OOgmzeaKQwi4ZCTWlYR5H4Gm1r2uTMVBXoemo1UEghk5vtMi1xSu2ymgKVGW631e2fp9/R610ZjQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-body-length-node": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.2.3.tgz", + "integrity": "sha512-ZkJGvqBzMHVHE7r/hcuCxlTY8pQr1kMtdsVPs7ex4mMU+EAbcXppfo5NmyxMYi2XU49eqaz56j2gsk4dHHPG/g==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-buffer-from": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.2.2.tgz", + "integrity": "sha512-FDXD7cvUoFWwN6vtQfEta540Y/YBe5JneK3SoZg9bThSoOAC/eGeYEua6RkBgKjGa/sz6Y+DuBZj3+YEY21y4Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-config-provider": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.2.2.tgz", + "integrity": "sha512-dWU03V3XUprJwaUIFVv4iOnS1FC9HnMHDfUrlNDSh4315v0cWyaIErP8KiqGVbf5z+JupoVpNM7ZB3jFiTejvQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-browser": { + "version": "4.3.44", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.44.tgz", + "integrity": "sha512-eZg6XzaCbVr2S5cAErU5eGBDaOVTuTo1I65i4tQcHENRcZ8rMWhQy1DaIYUSLyZjsfXvmCqZrstSMYyGFocvHA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^4.2.12", + "@smithy/smithy-client": "^4.12.8", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-node": { + "version": "4.2.48", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.48.tgz", + "integrity": "sha512-FqOKTlqSaoV3nzO55pMs5NBnZX8EhoI0DGmn9kbYeXWppgHD6dchyuj2HLqp4INJDJbSrj6OFYJkAh/WhSzZPg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/config-resolver": "^4.4.13", + "@smithy/credential-provider-imds": "^4.2.12", + "@smithy/node-config-provider": "^4.3.12", + "@smithy/property-provider": "^4.2.12", + "@smithy/smithy-client": "^4.12.8", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-endpoints": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.3.3.tgz", + "integrity": "sha512-VACQVe50j0HZPjpwWcjyT51KUQ4AnsvEaQ2lKHOSL4mNLD0G9BjEniQ+yCt1qqfKfiAHRAts26ud7hBjamrwig==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.12", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-hex-encoding": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.2.2.tgz", + "integrity": "sha512-Qcz3W5vuHK4sLQdyT93k/rfrUwdJ8/HZ+nMUOyGdpeGA1Wxt65zYwi3oEl9kOM+RswvYq90fzkNDahPS8K0OIg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-middleware": { + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.12.tgz", + "integrity": "sha512-Er805uFUOvgc0l8nv0e0su0VFISoxhJ/AwOn3gL2NWNY2LUEldP5WtVcRYSQBcjg0y9NfG8JYrCJaYDpupBHJQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-retry": { + "version": "4.2.13", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.13.tgz", + "integrity": "sha512-qQQsIvL0MGIbUjeSrg0/VlQ3jGNKyM3/2iU3FPNgy01z+Sp4OvcaxbgIoFOTvB61ZoohtutuOvOcgmhbD0katQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^4.2.12", + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-stream": { + "version": "4.5.21", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.21.tgz", + "integrity": "sha512-KzSg+7KKywLnkoKejRtIBXDmwBfjGvg1U1i/etkC7XSWUyFCoLno1IohV2c74IzQqdhX5y3uE44r/8/wuK+A7Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/fetch-http-handler": "^5.3.15", + "@smithy/node-http-handler": "^4.5.1", + "@smithy/types": "^4.13.1", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-buffer-from": "^4.2.2", + "@smithy/util-hex-encoding": "^4.2.2", + "@smithy/util-utf8": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-uri-escape": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.2.2.tgz", + "integrity": "sha512-2kAStBlvq+lTXHyAZYfJRb/DfS3rsinLiwb+69SstC9Vb0s9vNWkRwpnj918Pfi85mzi42sOqdV72OLxWAISnw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-utf8": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.2.2.tgz", + "integrity": "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.2.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-waiter": { + "version": "4.2.14", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.2.14.tgz", + "integrity": "sha512-2zqq5o/oizvMaFUlNiTyZ7dbgYv1a893aGut2uaxtbzTx/VYYnRxWzDHuD/ftgcw94ffenua+ZNLrbqwUYE+Bg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.13.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/uuid": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@smithy/uuid/-/uuid-1.1.2.tgz", + "integrity": "sha512-O/IEdcCUKkubz60tFbGA7ceITTAJsty+lBjNoorP4Z6XRqaFb/OjQjZODophEcuq68nKm6/0r+6/lLQ+XVpk8g==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@tootallnate/quickjs-emscripten": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", + "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", + "license": "MIT" + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", + "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "25.2.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.3.tgz", + "integrity": "sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/validator": { + "version": "13.15.10", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.10.tgz", + "integrity": "sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA==", + "license": "MIT" + }, + "node_modules/@types/yargs": { + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/yauzl": { + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", + "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", + "license": "MIT", + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.1.tgz", + "integrity": "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/@unrs/resolver-binding-android-arm-eabi": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.12.2.tgz", + "integrity": "sha512-g5T90pqg1bo/7mytQx6F4iBNC0Wsh9cu+z9veDbFjc7HjpesJFWD7QMS0NGStXM075+7dJPPVvBbpZlnrdpi/w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-android-arm64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.12.2.tgz", + "integrity": "sha512-YGCRZv/9GLhwmz6mYDeTsm/92BAyR28l6c2ReweVW5pWgfsitWLY8upvfRlGdoyD8HjeTHSYJWyZGD4KJA/nFQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-arm64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.12.2.tgz", + "integrity": "sha512-u9DiNT1auQMO20A9SyTuG3wUgQWB9Z7KjAg0uFuCDR1FsAY8A0CG2S6JpHS1xwm/w1G08bjXZDcyOCjv1WAm2w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-x64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.12.2.tgz", + "integrity": "sha512-f7rPLi/T1HVKZu/u6t87lroib16n8vrSzcyxI7lg4BGO9UF26KhQL44sd9eOUgrTYhvRXtWOIZT5PejdPyJfUA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-freebsd-x64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.12.2.tgz", + "integrity": "sha512-BpcOjWCJub6nRZUS2zA20pmLvjtqAtGejETaIyRLiZiQf++cbrjltLA5NN/xaXfqeOBOSlMFbemIl5/S5tljmg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.12.2.tgz", + "integrity": "sha512-vZTDvdSISZjJx66OzJqtsOhzifbqRjbmI1Mnu49fQDwog5GtDI4QidRiEAYbZCRj9C8YZEW+3ZjqsyS9GR4k2A==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.12.2.tgz", + "integrity": "sha512-BiPI+IrIlwcW4nLLMM21+B1dFPzd55yAVgVGrdgDjNef+ch03GdxrcyaIz8X9SsQirh/kCQ7mviyWlMxdh2D7g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.12.2.tgz", + "integrity": "sha512-zJc0H99FEPoFfSrNpa91HYfxzfAJCr502oxNK1cfdC9hlaFI43RT+JFCann9JUgZmLzzntChHyn13Sgn9ljHNg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-musl": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.12.2.tgz", + "integrity": "sha512-KQ3Lki6l+Pz1k/eBipN41ES+YUK30beLGb9YqcB1O542cyLCNE6GaxrfcY3T6EezmGGk84wb5XyO9loTM9tkcA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-loong64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-loong64-gnu/-/resolver-binding-linux-loong64-gnu-1.12.2.tgz", + "integrity": "sha512-3SJGEh1DborhG6pyxvhPzCT4bbSIVihsvgJc13P1bHG7KLdNDaF9T3gsTwFc7Jw/5Y5/iWOjkEx7Zy0NvCGX3Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-loong64-musl": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-loong64-musl/-/resolver-binding-linux-loong64-musl-1.12.2.tgz", + "integrity": "sha512-jiuG/Obbel7uw1PwHNFfrkiKhLAF6mnyZ6aWlOAVN9WqKm8v0OFGnciJIHu8+CMvXLQ8AD51LPzAoUfT21D5Ew==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.12.2.tgz", + "integrity": "sha512-q7xRvVpmcfeL+LlZg8Pbbo6QaTZwDU5BaGZbwfhkEsXJn3Was8xYfE0RBH266xZt0rM6B7i8xAYIvjthuUIWHg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.12.2.tgz", + "integrity": "sha512-0CVdx6lcnT3Q9inOH8tsMIOJ6ImndllMjqJHg8RLVdB7Vq4SfkEXl9mCSsVNuNA4MCYycRicCUxPCabVHJRr6A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.12.2.tgz", + "integrity": "sha512-iOwlRo9vnp6R6ohHQS11n0NnfdXx/omhkocmIfaPRpQhKZ+3BDMkkdRVh53qjkFkpPddf+FETA28NwGN7l5l+w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.12.2.tgz", + "integrity": "sha512-HYJtLfXq94q8iZNFT1lknx258wlkkWhZeUXJRqzKBBUJ00CvZ+N33zgbCqimLjsyw5Va6uUxhVa12mI+kaveEw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.12.2.tgz", + "integrity": "sha512-mPsUhunKKDih5O96Y6enDQyHc1SqBPlY1E/SfMWDM3EdJ95Z9CArPeCVwCCqbP45ljvivdEk8Fxn+SIb1rDAJQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-musl": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.12.2.tgz", + "integrity": "sha512-azrt6+5ydLd8Vt210AAFis/lZevSfPw93EJRIJG+xPu4WCJ8K0kppCTpMyLPcKT7H15M4Jnt2tMp5bOvCkRC6A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-openharmony-arm64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-openharmony-arm64/-/resolver-binding-openharmony-arm64-1.12.2.tgz", + "integrity": "sha512-YZ9hP4O0X9PQb8eO980qmLNGH4zT3I9+SZTdt0Pr0YyuGQhYKoOZkV02VzrzyOZJ5xIJ3UFIenKkUkGg8GjgWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@unrs/resolver-binding-wasm32-wasi": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.12.2.tgz", + "integrity": "sha512-tYFDIkMxSflfEc/h92ZWNsZlHSwgimbNHSO3PL2JWQHfCuC2q316jMyYU9TIWZsFK2bQwyK5VAdYgn8ygPj69A==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.10.0", + "@emnapi/runtime": "1.10.0", + "@napi-rs/wasm-runtime": "^1.1.4" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.12.2.tgz", + "integrity": "sha512-qzNyg3xL0VPQmCaUh+N5jSitce6k+uCBfMDesWRnlULOZaqUkaJ0ybdT+UqlAWJoQjuqfIU/0Ptx9bteN4D82g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.12.2.tgz", + "integrity": "sha512-WD9sY00OfpHVGfsnHZoA8jVT+esS/Bg8z8jzxp5BnDCjjwsuKsPQrzswwpFy4J1AUJbXPRfkpcX0mXrzeXW79g==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-x64-msvc": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.12.2.tgz", + "integrity": "sha512-nAB74NfSNKknqQ1RrYj6uz8FcXEomu/MATJZxh/x+BArzN2U3JbOYC0APYzUIGhVY3m5hRxA8VPNdPBoG8txlA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "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/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", + "license": "MIT" + }, + "node_modules/archiver": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz", + "integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==", + "license": "MIT", + "dependencies": { + "archiver-utils": "^2.1.0", + "async": "^3.2.4", + "buffer-crc32": "^0.2.1", + "readable-stream": "^3.6.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^2.2.0", + "zip-stream": "^4.1.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/archiver-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", + "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", + "license": "MIT", + "dependencies": { + "glob": "^7.1.4", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^2.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/archiver-utils/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/archiver-utils/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/archiver-utils/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/ast-types": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", + "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/aws-ssl-profiles": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz", + "integrity": "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/axios": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.17.0.tgz", + "integrity": "sha512-J8SwNxprqqpbfenehxWYXE7CW+wM1BB4w3+N+g+/Wx40xM4rsLrfPmHHxSWIxJLYDgSY/HqlFPIYb2/S3rxafw==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.16.0", + "form-data": "^4.0.5", + "https-proxy-agent": "^5.0.1", + "proxy-from-env": "^2.1.0" + } + }, + "node_modules/axios/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/axios/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/axios/node_modules/proxy-from-env": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/b4a": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.1.tgz", + "integrity": "sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw==", + "license": "Apache-2.0", + "peerDependencies": { + "react-native-b4a": "*" + }, + "peerDependenciesMeta": { + "react-native-b4a": { + "optional": true + } + } + }, + "node_modules/babel-jest": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.4.1.tgz", + "integrity": "sha512-fATAbM8piYxkiXQp3RBXmZHxZVNJZAVXXfyeyCN2Tida3+qJ8ea9UxhiJ2y4fLO90ZImKt6k9FlcH2+rLkJGhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/transform": "30.4.1", + "@types/babel__core": "^7.20.5", + "babel-plugin-istanbul": "^7.0.1", + "babel-preset-jest": "30.4.0", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.11.0 || ^8.0.0-0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-7.0.1.tgz", + "integrity": "sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==", + "dev": true, + "license": "BSD-3-Clause", + "workspaces": [ + "test/babel-8" + ], + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-instrument": "^6.0.2", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.4.0.tgz", + "integrity": "sha512-9EdtWM/sSfXLOGLwSn+GS6pIXyBnL07/8gyJlwFXjWy4DxMOyItqyUT29d4lQiS380EZwYlX7/At4PgBS+m2aA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/babel__core": "^7.20.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", + "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/babel-preset-jest": { + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-30.4.0.tgz", + "integrity": "sha512-lBY4jxsNmCnSiu7kquw8ZC9F4+XLMOKypT3RnNHPvU2Kpd4W0xaPuLr5ZkRyOsvLYAY4yaW1ZwTW4xB7NIiZzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "babel-plugin-jest-hoist": "30.4.0", + "babel-preset-current-node-syntax": "^1.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.11.0 || ^8.0.0-beta.1" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/bare-events": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.9.1.tgz", + "integrity": "sha512-Z0oHEHAFDZkffN8Qc39zNZjQlMDkPJRyyyZieU1VH7u8c5S+qHZ2S8ixdKIAxEjfHO7FJxXmJWgteOghVanIsg==", + "license": "Apache-2.0", + "peerDependencies": { + "bare-abort-controller": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + } + } + }, + "node_modules/bare-fs": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.7.2.tgz", + "integrity": "sha512-aTvMFUWkBmjzKtEQMDGGDNF8bkfpD5N1b/FCwt7A3wrU4t1o/e/85Wzkluh6JlODCjqVESYCkQCdTXqZ9G7VFg==", + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.5.4", + "bare-path": "^3.0.0", + "bare-stream": "^2.6.4", + "bare-url": "^2.2.2", + "fast-fifo": "^1.3.2" + }, + "engines": { + "bare": ">=1.16.0" + }, + "peerDependencies": { + "bare-buffer": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + } + } + }, + "node_modules/bare-os": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.9.1.tgz", + "integrity": "sha512-6M5XjcnsygQNPMCMPXSK379xrJFiZ/AEMNBmFEmQW8d/789VQATvriyi5r0HYTL9TkQ26rn3kgdTG3aisbrXkQ==", + "license": "Apache-2.0", + "engines": { + "bare": ">=1.14.0" + } + }, + "node_modules/bare-path": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.1.tgz", + "integrity": "sha512-ghj2DSK/2e99a1anTVPCV4m4YIYtrbXhfM7V3D7XZLOTsybnYyaJloymGqssQc8l/or0UoDyRtNQkmkEF/ysgQ==", + "license": "Apache-2.0", + "dependencies": { + "bare-os": "^3.0.1" + } + }, + "node_modules/bare-stream": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.13.1.tgz", + "integrity": "sha512-Vp0cnjYyrEC4whYTymQ+YZi6pBpfiICZO3cfRG8sy67ZNWe951urv1x4eW1BKNngw3U+3fPYb5JQvHbCtxH7Ow==", + "license": "Apache-2.0", + "dependencies": { + "streamx": "^2.25.0", + "teex": "^1.0.1" + }, + "peerDependencies": { + "bare-abort-controller": "*", + "bare-buffer": "*", + "bare-events": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + }, + "bare-buffer": { + "optional": true + }, + "bare-events": { + "optional": true + } + } + }, + "node_modules/bare-url": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.4.5.tgz", + "integrity": "sha512-K+y9xF1tN+CdPu4qWwr0QiK1Al07eFPGYK5M2pDXcmHdMdgC/tT/bpmMe1hrmRHaidKLkXrC+cRNYf3XVDUhSQ==", + "license": "Apache-2.0", + "dependencies": { + "bare-path": "^3.0.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.37", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.37.tgz", + "integrity": "sha512-girxaJ7WZssDOFhzCGZTDKoTa1gk6A1TbflaYTpykLJ4UU9Fz9kx1aREM8JCuoVHbL8X8T/mJg7w2oYSq72Oig==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/basic-auth/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/basic-ftp": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.3.1.tgz", + "integrity": "sha512-bopVNp6ugyA150DDuZfPFdt1KZ5a94ZDiwX4hMgZDzF+GttD80lEy8kj98kbyhLXnPvhtIo93mdnLIjpCAeeOw==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/bcrypt": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-6.0.0.tgz", + "integrity": "sha512-cU8v/EGSrnH+HnxV2z0J7/blxH8gq7Xh2JFT6Aroax7UohdmiJJlxApMxtKfuI7z68NvvVcmR78k2LbT6efhRg==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^8.3.0", + "node-gyp-build": "^4.8.4" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/big-integer": { + "version": "1.6.52", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", + "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", + "license": "Unlicense", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/binary": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", + "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==", + "license": "MIT", + "dependencies": { + "buffers": "~0.1.1", + "chainsaw": "~0.1.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bluebird": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", + "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==", + "license": "MIT" + }, + "node_modules/body-parser": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", + "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.1", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/bowser": { + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.14.1.tgz", + "integrity": "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==", + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/brotli": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz", + "integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==", + "license": "MIT", + "dependencies": { + "base64-js": "^1.1.2" + } + }, + "node_modules/browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "license": "MIT", + "dependencies": { + "pako": "~1.0.5" + } + }, + "node_modules/browserslist": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, + "node_modules/buffer-indexof-polyfill": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", + "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==", + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/buffers": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", + "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==", + "engines": { + "node": ">=0.2.0" + } + }, + "node_modules/bullmq": { + "version": "5.71.1", + "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.71.1.tgz", + "integrity": "sha512-kOBfdcsHmO6wwmIjpersoVdYQ7jkjTgky4Yop0loc7QwSdgxliSzD69U9ijZuRrkyCJwz5p5eqxeGeQkJ0YGZQ==", + "license": "MIT", + "dependencies": { + "cron-parser": "4.9.0", + "ioredis": "5.10.1", + "msgpackr": "1.11.5", + "node-abort-controller": "3.1.1", + "semver": "7.7.4", + "tslib": "2.8.1", + "uuid": "11.1.0" + } + }, + "node_modules/bullmq/node_modules/uuid": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", + "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "get-intrinsic": "^1.3.0", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001799", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001799.tgz", + "integrity": "sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chainsaw": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", + "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==", + "license": "MIT/X11", + "dependencies": { + "traverse": ">=0.3.0 <0.4" + }, + "engines": { + "node": "*" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chromium-bidi": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-14.0.0.tgz", + "integrity": "sha512-9gYlLtS6tStdRWzrtXaTMnqcM4dudNegMXJxkR0I/CXObHalYeYcAMPrL19eroNZHtJ8DQmu1E+ZNOYu/IXMXw==", + "license": "Apache-2.0", + "dependencies": { + "mitt": "^3.0.1", + "zod": "^3.24.1" + }, + "peerDependencies": { + "devtools-protocol": "*" + } + }, + "node_modules/ci-info": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", + "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/cluster-key-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", + "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==", + "dev": true, + "license": "MIT" + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/compress-commons": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.2.tgz", + "integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==", + "license": "MIT", + "dependencies": { + "buffer-crc32": "^0.2.13", + "crc32-stream": "^4.0.2", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "license": "MIT" + }, + "node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "engines": [ + "node >= 6.0" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/content-disposition": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", + "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-parser": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz", + "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==", + "license": "MIT", + "dependencies": { + "cookie": "0.7.2", + "cookie-signature": "1.0.6" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "license": "MIT" + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cosmiconfig": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.1.tgz", + "integrity": "sha512-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ==", + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/crc32-stream": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.3.tgz", + "integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==", + "license": "MIT", + "dependencies": { + "crc-32": "^1.2.0", + "readable-stream": "^3.4.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cron-parser": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-4.9.0.tgz", + "integrity": "sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==", + "license": "MIT", + "dependencies": { + "luxon": "^3.2.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", + "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", + "license": "MIT" + }, + "node_modules/data-uri-to-buffer": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", + "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/dayjs": { + "version": "1.11.20", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.20.tgz", + "integrity": "sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/dedent": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.2.tgz", + "integrity": "sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deep-equal": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.2.tgz", + "integrity": "sha512-5tdhKF6DbU7iIzrIOa1AOUt39ZRm13cmL1cGEh//aqR8x9+tNfbywRf0n5FD/18OKMdo7DNEtrX2t22ZAkI+eg==", + "license": "MIT", + "dependencies": { + "is-arguments": "^1.1.1", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.5.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/degenerator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", + "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", + "license": "MIT", + "dependencies": { + "ast-types": "^0.13.4", + "escodegen": "^2.1.0", + "esprima": "^4.0.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "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", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/devtools-protocol": { + "version": "0.0.1608973", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1608973.tgz", + "integrity": "sha512-Tpm17fxYzt+J7VrGdc1k8YdRqS3YV7se/M6KeemEqvUbq/n7At1rWVuXMxQgpWkdwSdIEKYbU//Bve+Shm4YNQ==", + "license": "BSD-3-Clause" + }, + "node_modules/dfa": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/dfa/-/dfa-1.2.0.tgz", + "integrity": "sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==", + "license": "MIT" + }, + "node_modules/dotenv": { + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.3.1.tgz", + "integrity": "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dottie": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.6.tgz", + "integrity": "sha512-iGCHkfUc5kFekGiqhe8B/mdaurD+lakO9txNnTvKtA6PISrw86LgqHvRzWYPyoE2Ph5aMIrCw9/uko6XHTKCwA==", + "license": "MIT" + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "license": "BSD-3-Clause", + "dependencies": { + "readable-stream": "^2.0.2" + } + }, + "node_modules/duplexer2/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/duplexer2/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/duplexer2/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "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.373", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.373.tgz", + "integrity": "sha512-G2Hym8JIf/QreuseqkDibgH8Ci8KfJzqGDKdakbhSx9UltwRBH2cBLAWU/lBX0sCdv0TlhyxQyDCnSfxgMWsjA==", + "dev": true, + "license": "ISC" + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "license": "BSD-2-Clause", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/events-universal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", + "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.7.0" + } + }, + "node_modules/exceljs": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/exceljs/-/exceljs-4.4.0.tgz", + "integrity": "sha512-XctvKaEMaj1Ii9oDOqbW/6e1gXknSY4g/aLCDicOXqBE4M0nRWkUu0PTp++UPNzoFY12BNHMfs/VadKIS6llvg==", + "license": "MIT", + "dependencies": { + "archiver": "^5.0.0", + "dayjs": "^1.8.34", + "fast-csv": "^4.3.1", + "jszip": "^3.10.1", + "readable-stream": "^3.6.0", + "saxes": "^5.0.1", + "tmp": "^0.2.0", + "unzipper": "^0.10.11", + "uuid": "^8.3.0" + }, + "engines": { + "node": ">=8.3.0" + } + }, + "node_modules/exceljs/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execa/node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/exit-x": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/exit-x/-/exit-x-0.2.2.tgz", + "integrity": "sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/expect/-/expect-30.4.1.tgz", + "integrity": "sha512-PMARsyh/JtqC20HoGqlFcIlQAyqUtW4PlI1rup1uhYJtKuwAjbvWi3GQMAn+STdHum/dk8xrKfUM1+5SAwpolA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/expect-utils": "30.4.1", + "@jest/get-type": "30.1.0", + "jest-matcher-utils": "30.4.1", + "jest-message-util": "30.4.1", + "jest-mock": "30.4.1", + "jest-util": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express/node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "license": "BSD-2-Clause", + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/fast-csv": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/fast-csv/-/fast-csv-4.3.6.tgz", + "integrity": "sha512-2RNSpuwwsJGP0frGsOmTb9oUF+VkFSM4SyLTDgwf2ciHWTarN0lQTC+F2f/t5J9QjW+c65VFIAAu85GsvMIusw==", + "license": "MIT", + "dependencies": { + "@fast-csv/format": "4.3.5", + "@fast-csv/parse": "4.3.6" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-xml-builder": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.1.4.tgz", + "integrity": "sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "path-expression-matcher": "^1.1.3" + } + }, + "node_modules/fast-xml-parser": { + "version": "5.5.8", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.5.8.tgz", + "integrity": "sha512-Z7Fh2nVQSb2d+poDViM063ix2ZGt9jmY1nWhPfHBOK2Hgnb/OW3P4Et3P/81SEej0J7QbWtJqxO05h8QYfK7LQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "fast-xml-builder": "^1.1.4", + "path-expression-matcher": "^1.2.0", + "strnum": "^2.2.0" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "license": "MIT", + "dependencies": { + "pend": "~1.2.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/brace-expansion": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz", + "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "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/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/follow-redirects": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/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/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/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "license": "MIT" + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/fstream": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "dependencies": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + }, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "license": "MIT", + "dependencies": { + "is-property": "^1.0.2" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-uri": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.5.tgz", + "integrity": "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==", + "license": "MIT", + "dependencies": { + "basic-ftp": "^5.0.2", + "data-uri-to-buffer": "^6.0.2", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "license": "ISC" + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "license": "MIT" + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflection": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.13.4.tgz", + "integrity": "sha512-6I/HUDeYFfuNCVS3td055BaXBwKYuzw7K3ExVMStBowKo9oOAMJIXIHvdyR3iboTCp1b+1i5DSkIZTcwIktuDw==", + "engines": [ + "node >= 0.4.0" + ], + "license": "MIT" + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ioredis": { + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.10.1.tgz", + "integrity": "sha512-HuEDBTI70aYdx1v6U97SbNx9F1+svQKBDo30o0b9fw055LMepzpOOd0Ccg9Q6tbqmBSJaMuY0fB7yw9/vjBYCA==", + "license": "MIT", + "dependencies": { + "@ioredis/commands": "1.5.1", + "cluster-key-slot": "^1.1.0", + "debug": "^4.3.4", + "denque": "^2.1.0", + "lodash.defaults": "^4.2.0", + "lodash.isarguments": "^3.1.0", + "redis-errors": "^1.2.0", + "redis-parser": "^3.0.0", + "standard-as-callback": "^2.1.0" + }, + "engines": { + "node": ">=12.22.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ioredis" + } + }, + "node_modules/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", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arguments": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", + "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "license": "MIT" + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==", + "license": "MIT" + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", + "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.23", + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "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": "30.4.2", + "resolved": "https://registry.npmjs.org/jest/-/jest-30.4.2.tgz", + "integrity": "sha512-Yi1jqNC/Oq0N4hBgNH/YvBpP1P57QqundgytzYqy3yqAa7NZPNjSoi4SGbRAXDMdBzNE6xBCi5U7RgfrvMEUVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "30.4.2", + "@jest/types": "30.4.1", + "import-local": "^3.2.0", + "jest-cli": "30.4.2" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-30.4.1.tgz", + "integrity": "sha512-IuctmYrxi21iOSOaIXpJWalHyPAsVv0GeBHKDn8C1CA4W5htHn7INL+wdnL4Bo0+olEndvAFkmb++tIQJG+vvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^5.1.1", + "jest-util": "30.4.1", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-circus": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-30.4.2.tgz", + "integrity": "sha512-rvHH7VlY6LgbJXJTQ87GW62g1FntOtbhh0zT+v04kC+pgL6aBKyYINXxWukCpj3dcIBMw5/XUbtDS9dU9JTXeQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.4.1", + "@jest/expect": "30.4.1", + "@jest/test-result": "30.4.1", + "@jest/types": "30.4.1", + "@types/node": "*", + "chalk": "^4.1.2", + "co": "^4.6.0", + "dedent": "^1.6.0", + "is-generator-fn": "^2.1.0", + "jest-each": "30.4.1", + "jest-matcher-utils": "30.4.1", + "jest-message-util": "30.4.1", + "jest-runtime": "30.4.2", + "jest-snapshot": "30.4.1", + "jest-util": "30.4.1", + "p-limit": "^3.1.0", + "pretty-format": "30.4.1", + "pure-rand": "^7.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.6" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-cli": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-30.4.2.tgz", + "integrity": "sha512-jfA2ocvVHMXS2QijrJ0d31ektP+d/W0T5RpcTX2Pq+3sVqHlsXVCM2+FmwpL+bdY8OfHpIg9xMxLF17Zg0U49Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "30.4.2", + "@jest/test-result": "30.4.1", + "@jest/types": "30.4.1", + "chalk": "^4.1.2", + "exit-x": "^0.2.2", + "import-local": "^3.2.0", + "jest-config": "30.4.2", + "jest-util": "30.4.1", + "jest-validate": "30.4.1", + "yargs": "^17.7.2" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-30.4.2.tgz", + "integrity": "sha512-rNHAShJQqQwFNoL0hbf3BphSBOWnpOUAKvidLS/AjNVLPfoj5mSf4jQMfW3cYOs6hXeZC7nF7mDHaBnbxELOzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.27.4", + "@jest/get-type": "30.1.0", + "@jest/pattern": "30.4.0", + "@jest/test-sequencer": "30.4.1", + "@jest/types": "30.4.1", + "babel-jest": "30.4.1", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "deepmerge": "^4.3.1", + "glob": "^10.5.0", + "graceful-fs": "^4.2.11", + "jest-circus": "30.4.2", + "jest-docblock": "30.4.0", + "jest-environment-node": "30.4.1", + "jest-regex-util": "30.4.0", + "jest-resolve": "30.4.1", + "jest-runner": "30.4.2", + "jest-util": "30.4.1", + "jest-validate": "30.4.1", + "parse-json": "^5.2.0", + "pretty-format": "30.4.1", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "esbuild-register": ">=3.4.0", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "esbuild-register": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-config/node_modules/brace-expansion": { + "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, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/jest-config/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-config/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-diff": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.4.1.tgz", + "integrity": "sha512-CRpFK0RtLriVDGcPPAnR6HMVI8bSR2jnUIgralhauzYQZIb4RH9AtEInTuQr65LmmGggGcRT6HIASxwqsVsmlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/diff-sequences": "30.4.0", + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "pretty-format": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-30.4.0.tgz", + "integrity": "sha512-ZPMabUZCx5MpbZ2eBYSvZ0J8fvo3dR9oM+eeUpb3aKNQFuS2tu3Duw1TNlMoP8k3WQgKGJuhcMFvwcVuq6T7oA==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-newline": "^3.1.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-each": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-30.4.1.tgz", + "integrity": "sha512-/8MJbH6fuj48TstjrMf+u/pd06Qezz5xOXvZA6442heNOWr8bdeoGZX2d9fCn028CoMgYmroH9//zky5GfyYmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0", + "@jest/types": "30.4.1", + "chalk": "^4.1.2", + "jest-util": "30.4.1", + "pretty-format": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.4.1.tgz", + "integrity": "sha512-4FZYVOk85hz2AyT6BbarKy9u37g6DbrDyCdFhsnDdXqyrueYQvB+0zO4f/kqLCRD0BsPRXPMNJeQwihKZV8naw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.4.1", + "@jest/fake-timers": "30.4.1", + "@jest/types": "30.4.1", + "@types/node": "*", + "jest-mock": "30.4.1", + "jest-util": "30.4.1", + "jest-validate": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.4.1.tgz", + "integrity": "sha512-rFrcONd8jeFsyw+Z9CrScJgglRf2+NFmNam8dKu7n+SoHqNYT47mn0DdEcVUZJpvh7Iz6/si7f7yUH7GJHVgnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.4.1", + "@types/node": "*", + "anymatch": "^3.1.3", + "fb-watchman": "^2.0.2", + "graceful-fs": "^4.2.11", + "jest-regex-util": "30.4.0", + "jest-util": "30.4.1", + "jest-worker": "30.4.1", + "picomatch": "^4.0.3", + "walker": "^1.0.8" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.3" + } + }, + "node_modules/jest-haste-map/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/jest-leak-detector": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.4.1.tgz", + "integrity": "sha512-IpmyiioeHxiWDhesHnUFmOxcTzwCwKpgACgWajtAP+nYQXiY7DakTxB6Bx9JFiRMljr0AX1PvnQdaU1KFoz6NQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0", + "pretty-format": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.4.1.tgz", + "integrity": "sha512-zvYfX5CaeEkFrrLS9suWe9rvJrm9J1Iv3ua8kIBv9GEPzcnsfBf0bob37la7s67fs0nlBC3EuvkOLnXQKxtx4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "jest-diff": "30.4.1", + "pretty-format": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.4.1.tgz", + "integrity": "sha512-kwCKIvq0MCW1HzLoGola9Te6JUdzgV0loyKJ3Qghrkz9i5/RRIHsL95BMQc2HBBhlBKC4j22K9p11TGHH8RBpQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@jest/types": "30.4.1", + "@types/stack-utils": "^2.0.3", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "jest-util": "30.4.1", + "picomatch": "^4.0.3", + "pretty-format": "30.4.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.6" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-message-util/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/jest-mock": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.4.1.tgz", + "integrity": "sha512-/i8SVb8/NSB7RfNi8gfqu8gxLV23KaL5EpAttyb9iz8qWRIqXRLflycz/32wXsYkOnaUlx8NAKnJYtpsmXUmfw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.4.1", + "@types/node": "*", + "jest-util": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.4.0.tgz", + "integrity": "sha512-mWlvLviKIgIQ8VCuM1xRdD0TWp3zlzionlmDBjuXVBs+VkmXq6FgW9T4Emr7oGz/Rk6feDCGyiugolcQEyp3mg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-30.4.1.tgz", + "integrity": "sha512-Zry8Yq/yJcNAZ7dJ5F2heic8AheXvbFZ7XI5V+h28nrYZ7Qoyy4dItq8OodjnYD270mvX+ZudmrNV9cysqhW5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.4.1", + "jest-pnp-resolver": "^1.2.3", + "jest-util": "30.4.1", + "jest-validate": "30.4.1", + "slash": "^3.0.0", + "unrs-resolver": "^1.7.11" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-30.4.2.tgz", + "integrity": "sha512-gDiVh1I+GxYzz9oXlyw+1wv6VOYX1WYxMOfjsA3iGKePV2oxmbHhwxfkALxNxYy1ciw6APWwkW2zZONwP97aEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-regex-util": "30.4.0", + "jest-snapshot": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-runner": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-30.4.2.tgz", + "integrity": "sha512-2dw0PslVYXxffXGpLo+Ejad+KcI1Qkjn7f4X4619gf21oCUmL+SPfjqIa/losUem3yEOvfNZe/F1HWUcNpODcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "30.4.1", + "@jest/environment": "30.4.1", + "@jest/test-result": "30.4.1", + "@jest/transform": "30.4.1", + "@jest/types": "30.4.1", + "@types/node": "*", + "chalk": "^4.1.2", + "emittery": "^0.13.1", + "exit-x": "^0.2.2", + "graceful-fs": "^4.2.11", + "jest-docblock": "30.4.0", + "jest-environment-node": "30.4.1", + "jest-haste-map": "30.4.1", + "jest-leak-detector": "30.4.1", + "jest-message-util": "30.4.1", + "jest-resolve": "30.4.1", + "jest-runtime": "30.4.2", + "jest-util": "30.4.1", + "jest-watcher": "30.4.1", + "jest-worker": "30.4.1", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.4.2.tgz", + "integrity": "sha512-3/5e8iPz2k/VLqlr8DgTftYyLUv8Su3FkCAO2/Od81UsUTpSxOrS6O5x5KkoQwyUjmpYyDJKeyAvg2T2nvpNkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.4.1", + "@jest/fake-timers": "30.4.1", + "@jest/globals": "30.4.1", + "@jest/source-map": "30.0.1", + "@jest/test-result": "30.4.1", + "@jest/transform": "30.4.1", + "@jest/types": "30.4.1", + "@types/node": "*", + "chalk": "^4.1.2", + "cjs-module-lexer": "^2.1.0", + "collect-v8-coverage": "^1.0.2", + "glob": "^10.5.0", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.4.1", + "jest-message-util": "30.4.1", + "jest-mock": "30.4.1", + "jest-regex-util": "30.4.0", + "jest-resolve": "30.4.1", + "jest-snapshot": "30.4.1", + "jest-util": "30.4.1", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-runtime/node_modules/brace-expansion": { + "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, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/jest-runtime/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-runtime/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-snapshot": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.4.1.tgz", + "integrity": "sha512-tEOkkfOMppUyeiHwjZswOQ3lcnoTnws/q5FnGIaeIh/jmoU0ZlgMYRR8sTlTj+nNGCoJ0RDq6SfxGxCsyMTPmw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.27.4", + "@babel/generator": "^7.27.5", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/plugin-syntax-typescript": "^7.27.1", + "@babel/types": "^7.27.3", + "@jest/expect-utils": "30.4.1", + "@jest/get-type": "30.1.0", + "@jest/snapshot-utils": "30.4.1", + "@jest/transform": "30.4.1", + "@jest/types": "30.4.1", + "babel-preset-current-node-syntax": "^1.2.0", + "chalk": "^4.1.2", + "expect": "30.4.1", + "graceful-fs": "^4.2.11", + "jest-diff": "30.4.1", + "jest-matcher-utils": "30.4.1", + "jest-message-util": "30.4.1", + "jest-util": "30.4.1", + "pretty-format": "30.4.1", + "semver": "^7.7.2", + "synckit": "^0.11.8" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-util": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.4.1.tgz", + "integrity": "sha512-vjQb1sACEiv13DKJMDToJpzVW0joCsIQrmbg0fi7CyOOt+g9jTuQl2A216pWRBYhOVt53XbL/2LbMKg1BECWOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.4.1", + "@types/node": "*", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "graceful-fs": "^4.2.11", + "picomatch": "^4.0.3" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-util/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/jest-validate": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.4.1.tgz", + "integrity": "sha512-PDWi4SOwLnwqNDfHZjOcsEFyZ4fc/2W2gVL3DEoyqnB6jCQMLRtfBong8s6omIw3lI0HWOus12xfnFmQtjW3fw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0", + "@jest/types": "30.4.1", + "camelcase": "^6.3.0", + "chalk": "^4.1.2", + "leven": "^3.1.0", + "pretty-format": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-30.4.1.tgz", + "integrity": "sha512-/l9UonmvCwjHH7d2h3iAwIloLc1H0S8mJZ/LNK3i86hqwPAz8otUJjP9MfYtz9Tt77Su5FD2xGjZn8d31IZHlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "30.4.1", + "@jest/types": "30.4.1", + "@types/node": "*", + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "emittery": "^0.13.1", + "jest-util": "30.4.1", + "string-length": "^4.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-worker": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.4.1.tgz", + "integrity": "sha512-SHynN/q/QD++iNyvMdy+WMmbCGk8jIsNcRxycXbWubSOhvo6T+j2afcfUSl+3hYsiBebOTo0cT7c2H7CXugu1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@ungap/structured-clone": "^1.3.0", + "jest-util": "30.4.1", + "merge-stream": "^2.0.0", + "supports-color": "^8.1.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-worker/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", + "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==", + "license": "MIT", + "dependencies": { + "jws": "^4.0.1", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/jszip/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/jszip/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/jszip/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", + "license": "MIT", + "dependencies": { + "jwa": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "license": "MIT", + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, + "node_modules/lazystream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/lazystream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/lazystream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" + }, + "node_modules/listenercount": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", + "integrity": "sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==", + "license": "ISC" + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "license": "MIT" + }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", + "license": "MIT" + }, + "node_modules/lodash.difference": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", + "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==", + "license": "MIT" + }, + "node_modules/lodash.escaperegexp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", + "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==", + "license": "MIT" + }, + "node_modules/lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==", + "license": "MIT" + }, + "node_modules/lodash.groupby": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.groupby/-/lodash.groupby-4.6.0.tgz", + "integrity": "sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw==", + "license": "MIT" + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.", + "license": "MIT" + }, + "node_modules/lodash.isfunction": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz", + "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnil": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/lodash.isnil/-/lodash.isnil-4.0.0.tgz", + "integrity": "sha512-up2Mzq3545mwVnMhTDMdfoG1OurpA/s5t88JmQX809eH3C8491iu2sfKhTfhQtKY78oPNhiaHJUpT/dUDAAtng==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, + "node_modules/lodash.isundefined": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash.isundefined/-/lodash.isundefined-3.0.1.tgz", + "integrity": "sha512-MXB1is3s899/cD8jheYYE2V9qTHwKvt+npCwpD+1Sxm3Q3cECXCiYHjeHWXNwr6Q0SOBPrYUDxendrO6goVTEA==", + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, + "node_modules/lodash.union": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", + "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==", + "license": "MIT" + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", + "license": "MIT" + }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "license": "Apache-2.0" + }, + "node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/lru.min": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lru.min/-/lru.min-1.1.4.tgz", + "integrity": "sha512-DqC6n3QQ77zdFpCMASA1a3Jlb64Hv2N2DciFGkO/4L9+q/IpIAuRlKOvCXabtRW6cQf8usbmM6BE/TOPysCdIA==", + "license": "MIT", + "engines": { + "bun": ">=1.0.0", + "deno": ">=1.30.0", + "node": ">=8.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wellwelwel" + } + }, + "node_modules/luxon": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.7.2.tgz", + "integrity": "sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "license": "MIT" + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/moment-timezone": { + "version": "0.5.48", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.48.tgz", + "integrity": "sha512-f22b8LV1gbTO2ms2j2z13MuPogNoh5UzxL3nzNAYKGraILnbGc9NEE6dyiiiLv46DGRb8A4kg8UKWLjPthxBHw==", + "license": "MIT", + "dependencies": { + "moment": "^2.29.4" + }, + "engines": { + "node": "*" + } + }, + "node_modules/morgan": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.1.tgz", + "integrity": "sha512-223dMRJtI/l25dJKWpgij2cMtywuG/WiUKXdvwfbhGKBhy1puASqXwFzmWZ7+K73vUPoR7SS2Qz2cI/g9MKw0A==", + "license": "MIT", + "dependencies": { + "basic-auth": "~2.0.1", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-finished": "~2.3.0", + "on-headers": "~1.1.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/morgan/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/morgan/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/morgan/node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/msgpackr": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.11.5.tgz", + "integrity": "sha512-UjkUHN0yqp9RWKy0Lplhh+wlpdt9oQBYgULZOiFhV3VclSF1JnSQWZ5r9gORQlNYaUKQoR8itv7g7z1xDDuACA==", + "license": "MIT", + "optionalDependencies": { + "msgpackr-extract": "^3.0.2" + } + }, + "node_modules/msgpackr-extract": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.3.tgz", + "integrity": "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==", + "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.3", + "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3" + } + }, + "node_modules/multer": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/multer/-/multer-2.1.1.tgz", + "integrity": "sha512-mo+QTzKlx8R7E5ylSXxWzGoXoZbOsRMpyitcht8By2KHvMbf3tjwosZ/Mu/XYU6UuJ3VZnODIrak5ZrPiPyB6A==", + "license": "MIT", + "dependencies": { + "append-field": "^1.0.0", + "busboy": "^1.6.0", + "concat-stream": "^2.0.0", + "type-is": "^1.6.18" + }, + "engines": { + "node": ">= 10.16.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/multer/node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/multer/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/multer/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/multer/node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mysql2": { + "version": "3.17.1", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.17.1.tgz", + "integrity": "sha512-UzIzdVwPXPoZm+FaJ4lNsRt28HtUwt68gpLH7NP1oSjd91M5Qn1XJzbIsSRMRc5CV3pvktLNshmbaFfMYqPBhQ==", + "license": "MIT", + "dependencies": { + "aws-ssl-profiles": "^1.1.2", + "denque": "^2.1.0", + "generate-function": "^2.3.1", + "iconv-lite": "^0.7.2", + "long": "^5.3.2", + "lru.min": "^1.1.3", + "named-placeholders": "^1.1.6", + "seq-queue": "^0.0.5", + "sql-escaper": "^1.3.2" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/named-placeholders": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.6.tgz", + "integrity": "sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w==", + "license": "MIT", + "dependencies": { + "lru.min": "^1.1.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/napi-postinstall": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz", + "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==", + "dev": true, + "license": "MIT", + "bin": { + "napi-postinstall": "lib/cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/napi-postinstall" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/netmask": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.1.1.tgz", + "integrity": "sha512-eonl3sLUha+S1GzTPxychyhnUzKyeQkZ7jLjKrBagJgPla13F+uQ71HgpFefyHgqrjEbCPkDArxYsjY8/+gLKA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "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-addon-api": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.5.0.tgz", + "integrity": "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==", + "license": "MIT", + "engines": { + "node": "^18 || ^20 || >= 21" + } + }, + "node_modules/node-cron": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/node-cron/-/node-cron-4.5.0.tgz", + "integrity": "sha512-4Trh+kjvbXokyJkwQumvD5YAgeJfgHLR/sKyu71uSmxfCR5QMO1hldpvmFZOICN5pLgNY+J5Y8+ar3XKo5/4tQ==", + "license": "ISC", + "engines": { + "node": ">=20" + } + }, + "node_modules/node-gyp-build": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "license": "MIT", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "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", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.47", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.47.tgz", + "integrity": "sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/nodemailer": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-8.0.1.tgz", + "integrity": "sha512-5kcldIXmaEjZcHR6F28IKGSgpmZHaF1IXLWFTG+Xh3S+Cce4MiakLtWY+PlBU69fLbRa8HlaGIrC/QolUpHkhg==", + "license": "MIT-0", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/nodeman": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/nodeman/-/nodeman-1.1.2.tgz", + "integrity": "sha512-RpRpDnMI4TN/EPX6+my7f02585I14AQO8YrHTkO1aTiC65+sMa5G3NwQplN6kwue1njxWiaSz2UwCnDbwkLgeQ==", + "dependencies": { + "colors": "*" + }, + "bin": { + "_nodeman": "bin/_nodeman", + "nodeman": "bin/nodeman" + }, + "engines": { + "node": "*" + } + }, + "node_modules/nodemon": { + "version": "3.1.11", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.11.tgz", + "integrity": "sha512-is96t8F/1//UHAjNPHpbsNY46ELPpftGUoSVNXwUfMk/qdjSylYrWSu1XavVTBOn526kFiOR733ATgNBCQyH0g==", + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-is": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", + "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pac-proxy-agent": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz", + "integrity": "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==", + "license": "MIT", + "dependencies": { + "@tootallnate/quickjs-emscripten": "^0.23.0", + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "get-uri": "^6.0.1", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.6", + "pac-resolver": "^7.0.1", + "socks-proxy-agent": "^8.0.5" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-resolver": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", + "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", + "license": "MIT", + "dependencies": { + "degenerator": "^5.0.0", + "netmask": "^2.0.2" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "license": "(MIT AND Zlib)" + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-expression-matcher": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.2.0.tgz", + "integrity": "sha512-DwmPWeFn+tq7TiyJ2CxezCAirXjFxvaiD03npak3cRjlP9+OjTmSy1EpIrEbh+l6JgUundniloMLDQ/6VTdhLQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/path-to-regexp": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/pdfmake": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/pdfmake/-/pdfmake-0.2.7.tgz", + "integrity": "sha512-ClLpgx30H5G3EDvRW1MrA1Xih6YxEaSgIVFrOyBMgAAt62V+hxsyWAi6JNP7u1Fc5JKYAbpb4RRVw8Rhvmz5cQ==", + "license": "MIT", + "dependencies": { + "@foliojs-fork/linebreak": "^1.1.1", + "@foliojs-fork/pdfkit": "^0.13.0", + "iconv-lite": "^0.6.3", + "xmldoc": "^1.1.2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/pdfmake/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "license": "MIT" + }, + "node_modules/pg-connection-string": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.11.0.tgz", + "integrity": "sha512-kecgoJwhOpxYU21rZjULrmrBJ698U2RxXofKVzOn5UDj61BPj/qMb7diYUR1nLScCDbrztQFl1TaQZT0t1EtzQ==", + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/png-js": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/png-js/-/png-js-1.1.0.tgz", + "integrity": "sha512-PM/uYGzGdNSzqeOgly68+6wKQDL1SY0a/N+OEa/+br6LnHWOAJB0Npiamnodfq3jd2LS/i2fMeOKSAILjA+m5Q==", + "dependencies": { + "browserify-zlib": "^0.2.0" + } + }, + "node_modules/pretty-format": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.4.1.tgz", + "integrity": "sha512-K6KiKMHTL4jjX4u3Kir2EW07nRfcqVTXIImx50wbjHQTcZPgg+gjVeNTIT3l3L1Rd4UefxfogquC9J37SoFyyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "30.4.1", + "ansi-styles": "^5.2.0", + "react-is-18": "npm:react-is@^18.3.1", + "react-is-19": "npm:react-is@^19.2.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-agent": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz", + "integrity": "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.1", + "https-proxy-agent": "^7.0.6", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.1.0", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.5" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "license": "MIT" + }, + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/puppeteer": { + "version": "24.43.1", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.43.1.tgz", + "integrity": "sha512-/FSOViCrqRdb1HDocpsM9Z1giA71gTQPUt3SpHGVRALKAy/rJr1fLFYZW9F23qPxqVxTHQnbh/5B5opJST3kAw==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@puppeteer/browsers": "2.13.2", + "chromium-bidi": "14.0.0", + "cosmiconfig": "^9.0.0", + "devtools-protocol": "0.0.1608973", + "puppeteer-core": "24.43.1", + "typed-query-selector": "^2.12.2" + }, + "bin": { + "puppeteer": "lib/cjs/puppeteer/node/cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/puppeteer-core": { + "version": "24.43.1", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.43.1.tgz", + "integrity": "sha512-T5ScUMAsmhdNbgDR41AGESYeS6V9MSgetkSnVhhW+gXvzC42VesKCn5ld87gAZDJ6vLHL9GkRvY9WtQWSnwFbw==", + "license": "Apache-2.0", + "dependencies": { + "@puppeteer/browsers": "2.13.2", + "chromium-bidi": "14.0.0", + "debug": "^4.4.3", + "devtools-protocol": "0.0.1608973", + "typed-query-selector": "^2.12.2", + "webdriver-bidi-protocol": "0.4.1", + "ws": "^8.20.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/pure-rand": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-7.0.1.tgz", + "integrity": "sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, + "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/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/react-is-18": { + "name": "react-is", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/react-is-19": { + "name": "react-is", + "version": "19.2.7", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.7.tgz", + "integrity": "sha512-kZFnouyVv7eP/Phmrlo9FK+zcAdriZJvzxXHF1Sl1P377WSGe2G/JxVolhTrB/jeV47lKImhNUsijjHAAbcl/A==", + "dev": true, + "license": "MIT" + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdir-glob": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", + "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.1.0" + } + }, + "node_modules/readdir-glob/node_modules/brace-expansion": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", + "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/readdir-glob/node_modules/minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/redis-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", + "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/redis-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", + "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", + "license": "MIT", + "dependencies": { + "redis-errors": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/retry-as-promised": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-7.1.1.tgz", + "integrity": "sha512-hMD7odLOt3LkTjcif8aRZqi/hybjpLNgSk5oF5FCowfCjok6LukpN2bDX7R5wDmbgBQFn7YoBxSagmtXHaJYJw==", + "license": "MIT" + }, + "node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/sax": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", + "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, + "node_modules/saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/seq-queue": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", + "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==" + }, + "node_modules/sequelize": { + "version": "6.37.7", + "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.37.7.tgz", + "integrity": "sha512-mCnh83zuz7kQxxJirtFD7q6Huy6liPanI67BSlbzSYgVNl5eXVdE2CN1FuAeZwG1SNpGsNRCV+bJAVVnykZAFA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/sequelize" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.1.8", + "@types/validator": "^13.7.17", + "debug": "^4.3.4", + "dottie": "^2.0.6", + "inflection": "^1.13.4", + "lodash": "^4.17.21", + "moment": "^2.29.4", + "moment-timezone": "^0.5.43", + "pg-connection-string": "^2.6.1", + "retry-as-promised": "^7.0.4", + "semver": "^7.5.4", + "sequelize-pool": "^7.1.0", + "toposort-class": "^1.0.1", + "uuid": "^8.3.2", + "validator": "^13.9.0", + "wkx": "^0.5.0" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependenciesMeta": { + "ibm_db": { + "optional": true + }, + "mariadb": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "oracledb": { + "optional": true + }, + "pg": { + "optional": true + }, + "pg-hstore": { + "optional": true + }, + "snowflake-sdk": { + "optional": true + }, + "sqlite3": { + "optional": true + }, + "tedious": { + "optional": true + } + } + }, + "node_modules/sequelize-pool": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/sequelize-pool/-/sequelize-pool-7.1.0.tgz", + "integrity": "sha512-G9c0qlIWQSK29pR/5U2JF5dDQeqqHRragoyahj/Nx4KOOQ3CPPfzxnfqFPCSB7x5UgjOgnZ61nSxz+fjDpRlJg==", + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/sequelize/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "license": "MIT" + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/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/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "devOptional": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/sql-escaper": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/sql-escaper/-/sql-escaper-1.3.2.tgz", + "integrity": "sha512-lp+ZDVfSjHt+qAK1jXBTIXBNYnbo7gnaAGwoYTH9bE89kNkXwcu6g0WjJGRsdTKVpY1z70u3Y0IgmnBOoRybHw==", + "license": "MIT", + "engines": { + "bun": ">=1.0.0", + "deno": ">=2.0.0", + "node": ">=12.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/mysqljs/sql-escaper?sponsor=1" + } + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/standard-as-callback": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", + "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==", + "license": "MIT" + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/streamx": { + "version": "2.27.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.27.0.tgz", + "integrity": "sha512-WZ189TKnHoAokYHvwzaAQMpd55cgUmFIcJFzBSgGcb886jau5DL+XdDhTWV4ps3FLvk+OORp0dLRTPsLZ21CSA==", + "license": "MIT", + "dependencies": { + "events-universal": "^1.0.0", + "fast-fifo": "^1.3.2", + "text-decoder": "^1.1.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strnum": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.2.tgz", + "integrity": "sha512-DnR90I+jtXNSTXWdwrEy9FakW7UX+qUZg28gj5fk2vxxl7uS/3bpI4fjFYVmdK9etptYBPNkpahuQnEwhwECqA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/synckit": { + "version": "0.11.13", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.13.tgz", + "integrity": "sha512-eNRKgb3z66Yp3D2CixVujOUvXLFUTij/zVnV8KRyvFdQwpz7I5DS8UfRkTeLzb64u+dkzDSdelE24izu+zSSUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pkgr/core": "^0.3.6" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/synckit" + } + }, + "node_modules/tar-fs": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.2.tgz", + "integrity": "sha512-QGxxTxxyleAdyM3kpFs14ymbYmNFrfY+pHj7Z8FgtbZ7w2//VAgLMac7sT6nRpIHjppXO2AwwEOg0bPFVRcmXw==", + "license": "MIT", + "dependencies": { + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + }, + "optionalDependencies": { + "bare-fs": "^4.0.1", + "bare-path": "^3.0.0" + } + }, + "node_modules/tar-fs/node_modules/tar-stream": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.2.0.tgz", + "integrity": "sha512-ojzvCvVaNp6aOTFmG7jaRD0meowIAuPc3cMMhSgKiVWws1GyHbGd/xvnyuRKcKlMpt3qvxx6r0hreCNITP9hIg==", + "license": "MIT", + "dependencies": { + "b4a": "^1.6.4", + "bare-fs": "^4.5.5", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/teex": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz", + "integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==", + "license": "MIT", + "dependencies": { + "streamx": "^2.12.5" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/text-decoder": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz", + "integrity": "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==", + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.4" + } + }, + "node_modules/tiny-inflate": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", + "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==", + "license": "MIT" + }, + "node_modules/tmp": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", + "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/toposort-class": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz", + "integrity": "sha512-OsLcGGbYF3rMjPUf8oKktyvCiUxSbqMMS39m33MAjLTC1DVIH6x3WSt63/M77ihI09+Sdfk1AXvfhCEeUmC7mg==", + "license": "MIT" + }, + "node_modules/touch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", + "license": "ISC", + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, + "node_modules/traverse": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", + "integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==", + "license": "MIT/X11", + "engines": { + "node": "*" + } + }, + "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==", + "license": "0BSD" + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typed-query-selector": { + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.2.tgz", + "integrity": "sha512-EOPFbyIub4ngnEdqi2yOcNeDLaX/0jcE1JoAXQDDMIthap7FoN795lc/SHfIq2d416VufXpM8z/lD+WRm2gfOQ==", + "license": "MIT" + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "license": "MIT" + }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "license": "MIT" + }, + "node_modules/unicode-properties": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/unicode-properties/-/unicode-properties-1.4.1.tgz", + "integrity": "sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==", + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.0", + "unicode-trie": "^2.0.0" + } + }, + "node_modules/unicode-trie": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz", + "integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==", + "license": "MIT", + "dependencies": { + "pako": "^0.2.5", + "tiny-inflate": "^1.0.0" + } + }, + "node_modules/unicode-trie/node_modules/pako": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", + "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==", + "license": "MIT" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/unrs-resolver": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.12.2.tgz", + "integrity": "sha512-dmlRxBJJayXjqTwC+JtF1HhJmgf3ftQ3YejFcZrf4+KKtJv0qDsK1pjqaaVjG7wJ5NJ6UVP1OqRMQ71Z4C3rxQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "napi-postinstall": "^0.3.4" + }, + "funding": { + "url": "https://opencollective.com/unrs-resolver" + }, + "optionalDependencies": { + "@unrs/resolver-binding-android-arm-eabi": "1.12.2", + "@unrs/resolver-binding-android-arm64": "1.12.2", + "@unrs/resolver-binding-darwin-arm64": "1.12.2", + "@unrs/resolver-binding-darwin-x64": "1.12.2", + "@unrs/resolver-binding-freebsd-x64": "1.12.2", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.12.2", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.12.2", + "@unrs/resolver-binding-linux-arm64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-arm64-musl": "1.12.2", + "@unrs/resolver-binding-linux-loong64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-loong64-musl": "1.12.2", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-riscv64-musl": "1.12.2", + "@unrs/resolver-binding-linux-s390x-gnu": "1.12.2", + "@unrs/resolver-binding-linux-x64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-x64-musl": "1.12.2", + "@unrs/resolver-binding-openharmony-arm64": "1.12.2", + "@unrs/resolver-binding-wasm32-wasi": "1.12.2", + "@unrs/resolver-binding-win32-arm64-msvc": "1.12.2", + "@unrs/resolver-binding-win32-ia32-msvc": "1.12.2", + "@unrs/resolver-binding-win32-x64-msvc": "1.12.2" + } + }, + "node_modules/unzipper": { + "version": "0.10.14", + "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.14.tgz", + "integrity": "sha512-ti4wZj+0bQTiX2KmKWuwj7lhV+2n//uXEotUmGuQqrbVZSEGFMbI68+c6JCQ8aAmUWYvtHEz2A8K6wXvueR/6g==", + "license": "MIT", + "dependencies": { + "big-integer": "^1.6.17", + "binary": "~0.3.0", + "bluebird": "~3.4.1", + "buffer-indexof-polyfill": "~1.0.0", + "duplexer2": "~0.1.4", + "fstream": "^1.0.12", + "graceful-fs": "^4.2.2", + "listenercount": "~1.0.1", + "readable-stream": "~2.3.6", + "setimmediate": "~1.0.4" + } + }, + "node_modules/unzipper/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/unzipper/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/unzipper/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/uuid": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.0.tgz", + "integrity": "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist-node/bin/uuid" + } + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/validator": { + "version": "13.15.26", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.26.tgz", + "integrity": "sha512-spH26xU080ydGggxRyR1Yhcbgx+j3y5jbNXk/8L+iRvdIEQ4uTRH2Sgf2dokud6Q4oAtsbNvJ1Ft+9xmm6IZcA==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/webdriver-bidi-protocol": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/webdriver-bidi-protocol/-/webdriver-bidi-protocol-0.4.1.tgz", + "integrity": "sha512-ARrjNjtWRRs2w4Tk7nqrf2gBI0QXWuOmMCx2hU+1jUt6d00MjMxURrhxhGbrsoiZKJrhTSTzbIrc554iKI10qw==", + "license": "Apache-2.0" + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wkx": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.5.0.tgz", + "integrity": "sha512-Xng/d4Ichh8uN4l0FToV/258EjMGU9MGcA0HV2d9B/ZpZB3lqQm7nkOdZdm5GhKtLLhAE7PiVQwN4eN+2YJJUg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/write-file-atomic": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/write-file-atomic/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/ws": { + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", + "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "license": "MIT" + }, + "node_modules/xmldoc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/xmldoc/-/xmldoc-1.3.0.tgz", + "integrity": "sha512-y7IRWW6PvEnYQZNZFMRLNJw+p3pezM4nKYPfr15g4OOW9i8VpeydycFuipE2297OvZnh3jSb2pxOt9QpkZUVng==", + "license": "MIT", + "dependencies": { + "sax": "^1.2.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zip-stream": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.1.tgz", + "integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==", + "license": "MIT", + "dependencies": { + "archiver-utils": "^3.0.4", + "compress-commons": "^4.1.2", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/zip-stream/node_modules/archiver-utils": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-3.0.4.tgz", + "integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==", + "license": "MIT", + "dependencies": { + "glob": "^7.2.3", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..c2e1cc2 --- /dev/null +++ b/package.json @@ -0,0 +1,50 @@ +{ + "name": "backend", + "version": "1.0.0", + "main": "app.js", + "scripts": { + "dev": "nodemon server.js", + "start": "node server.js", + "worker": "node app/workers/index.js", + "seed": "node app/seeders/index.js", + "test": "jest" + }, + "keywords": [ + "Oceanic Titan", + "Oceanic Titan Backend" + ], + "author": "Niolla PVT (LTD)", + "license": "ISC", + "description": "Oceanic Titan Backend", + "dependencies": { + "@aws-sdk/client-s3": "^3.1021.0", + "@aws-sdk/s3-request-presigner": "^3.1021.0", + "@bull-board/api": "^6.20.6", + "@bull-board/express": "^6.20.6", + "axios": "^1.17.0", + "bcrypt": "^6.0.0", + "bullmq": "^5.71.1", + "cookie-parser": "^1.4.7", + "cors": "^2.8.6", + "dotenv": "^17.2.4", + "ejs": "^3.1.10", + "exceljs": "^4.4.0", + "express": "^5.2.1", + "ioredis": "^5.10.1", + "jsonwebtoken": "^9.0.3", + "morgan": "^1.10.1", + "multer": "^2.1.1", + "mysql2": "^3.17.0", + "node-cron": "^4.5.0", + "nodemailer": "^8.0.1", + "nodeman": "^1.1.2", + "nodemon": "^3.1.11", + "pdfmake": "^0.2.7", + "puppeteer": "^24.43.1", + "sequelize": "^6.37.7", + "uuid": "^13.0.0" + }, + "devDependencies": { + "jest": "^30.4.2" + } +} diff --git a/server.js b/server.js new file mode 100644 index 0000000..586a57f --- /dev/null +++ b/server.js @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2026 Niolla + * All rights reserved. + * + * This source code is proprietary and confidential. + * Unauthorized copying, modification, distribution, or use + * of this file, via any medium, is strictly prohibited. + */ + +// server.js + +require("dotenv").config(); +const app = require("./app"); + +const PORT = process.env.PORT || 3070; + +// Start the server +app.listen(PORT, "0.0.0.0", () => { + console.log(`Server running on http://localhost:${PORT}`); +});