diff --git a/__tests__/ComprehensiveController.test.js b/__tests__/ComprehensiveController.test.js deleted file mode 100644 index 484fb65..0000000 --- a/__tests__/ComprehensiveController.test.js +++ /dev/null @@ -1,173 +0,0 @@ -const { createFinancialReport } = require('../controllers/financialReportingController'); -const FinancialReport = require('../models/financialReport'); -const httpMocks = require('node-mocks-http'); -const mongoose = require('mongoose'); - -// Mock the entire model -jest.mock('../models/financialReport'); - -describe('createFinancialReport Controller', () => { - let req, res, next; - - beforeEach(() => { - req = httpMocks.createRequest(); - res = httpMocks.createResponse(); - next = jest.fn(); - jest.clearAllMocks(); - }); - - const validTestData = { - ReportID: 'test-id', - Type: 'Annual', - Data: { - revenue: { q1: 250000, q2: 250000, q3: 250000, q4: 250000 }, - expenses: { q1: 125000, q2: 125000, q3: 125000, q4: 125000 } - }, - TotalRevenue: '1000000.00', - TotalExpenses: '500000.00', - NetIncome: '500000.00', - EquitySummary: ['uuid1', 'uuid2'], - Timestamp: new Date().toISOString() - }; - - describe('Successful Operations', () => { - it('should create a new financial report and return 201 status', async () => { - req.body = validTestData; - const mockSave = jest.fn().mockResolvedValue(validTestData); - FinancialReport.mockImplementation(() => ({ save: mockSave })); - - await createFinancialReport(req, res, next); - - const responseData = JSON.parse(res._getData()); - expect(FinancialReport).toHaveBeenCalledWith(validTestData); - expect(mockSave).toHaveBeenCalled(); - expect(res.statusCode).toBe(201); - expect(responseData).toEqual(validTestData); - expect(next).not.toHaveBeenCalled(); - }); - - it('should handle quarterly reports correctly', async () => { - const quarterlyData = { ...validTestData, Type: 'Quarterly' }; - req.body = quarterlyData; - const mockSave = jest.fn().mockResolvedValue(quarterlyData); - FinancialReport.mockImplementation(() => ({ save: mockSave })); - - await createFinancialReport(req, res, next); - - expect(res.statusCode).toBe(201); - expect(JSON.parse(res._getData())).toEqual(quarterlyData); - }); - }); - - describe('Validation Errors', () => { - it('should handle invalid report type', async () => { - const invalidData = { ...validTestData, Type: 'Monthly' }; - req.body = invalidData; - - const validationError = new mongoose.Error.ValidationError(); - validationError.errors.Type = new mongoose.Error.ValidatorError({ - message: 'Invalid report type. Must be either Annual or Quarterly', - path: 'Type' - }); - - const mockSave = jest.fn().mockRejectedValue(validationError); - FinancialReport.mockImplementation(() => ({ save: mockSave })); - - await createFinancialReport(req, res, next); - - expect(next).toHaveBeenCalledWith(expect.objectContaining({ - name: 'ValidationError' - })); - }); - - it('should handle missing required fields', async () => { - const { TotalRevenue, ...incompleteData } = validTestData; - req.body = incompleteData; - - const validationError = new mongoose.Error.ValidationError(); - validationError.errors.TotalRevenue = new mongoose.Error.ValidatorError({ - message: 'TotalRevenue is required', - path: 'TotalRevenue' - }); - - const mockSave = jest.fn().mockRejectedValue(validationError); - FinancialReport.mockImplementation(() => ({ save: mockSave })); - - await createFinancialReport(req, res, next); - - expect(next).toHaveBeenCalledWith(expect.objectContaining({ - name: 'ValidationError' - })); - }); - }); - - describe('Data Integrity', () => { - it('should handle duplicate ReportID', async () => { - req.body = validTestData; - - // Create a duplicate key error that matches Mongoose's error structure - const duplicateError = new Error('E11000 duplicate key error'); - duplicateError.code = 11000; - duplicateError.index = 0; - duplicateError.keyPattern = { ReportID: 1 }; - duplicateError.keyValue = { ReportID: validTestData.ReportID }; - - const mockSave = jest.fn().mockRejectedValue(duplicateError); - FinancialReport.mockImplementation(() => ({ save: mockSave })); - - await createFinancialReport(req, res, next); - - expect(next).toHaveBeenCalledWith(expect.objectContaining({ - code: 11000, - keyPattern: { ReportID: 1 }, - keyValue: { ReportID: validTestData.ReportID } - })); - }); - - it('should verify financial calculations', async () => { - const invalidCalculations = { - ...validTestData, - TotalRevenue: '1000000.00', - TotalExpenses: '500000.00', - NetIncome: '400000.00' // Incorrect net income - }; - req.body = invalidCalculations; - - const validationError = new Error('Net income does not match revenue minus expenses'); - const mockSave = jest.fn().mockRejectedValue(validationError); - FinancialReport.mockImplementation(() => ({ save: mockSave })); - - await createFinancialReport(req, res, next); - - expect(next).toHaveBeenCalledWith(expect.objectContaining({ - message: 'Net income does not match revenue minus expenses' - })); - }); - }); - - describe('Error Handling', () => { - it('should handle database connection errors', async () => { - req.body = validTestData; - - const dbError = new Error('Database connection failed'); - const mockSave = jest.fn().mockRejectedValue(dbError); - FinancialReport.mockImplementation(() => ({ save: mockSave })); - - await createFinancialReport(req, res, next); - - expect(next).toHaveBeenCalledWith(dbError); - }); - - it('should handle unexpected errors', async () => { - req.body = validTestData; - - const unexpectedError = new Error('Unexpected server error'); - const mockSave = jest.fn().mockRejectedValue(unexpectedError); - FinancialReport.mockImplementation(() => ({ save: mockSave })); - - await createFinancialReport(req, res, next); - - expect(next).toHaveBeenCalledWith(unexpectedError); - }); - }); -}); \ No newline at end of file diff --git a/__tests__/app.test.js b/__tests__/app.test.js index 7c9c8be..13c7144 100644 --- a/__tests__/app.test.js +++ b/__tests__/app.test.js @@ -1,20 +1,22 @@ -const { connectDB, disconnectDB } = require('../db'); -const app = require('../app'); +// __tests__/setup/test-app.js +const express = require('express'); +const mongoose = require('mongoose'); +const financialReportingRoutes = require('../../routes/financialReportingRoutes'); -let server; +const app = express(); -beforeAll(async () => { - await connectDB(); - server = app.listen(5001); -}); +// Middleware +app.use(express.json()); -afterAll(async () => { - await server.close(); - await disconnectDB(); -}); +// Routes - match the path from your main app.js +app.use('/api/financial-reports', financialReportingRoutes); -describe('App Tests', () => { - it('should run the server', async () => { - expect(server).toBeDefined(); +// Error handling middleware - match your main app.js format +app.use((err, req, res, next) => { + console.error('Error:', err.message); + res.status(err.statusCode || 500).json({ + error: err.message || 'Internal Server Error', }); }); + +module.exports = app; \ No newline at end of file diff --git a/__tests__/backup.test.js b/__tests__/backup.test.js new file mode 100644 index 0000000..63b437d --- /dev/null +++ b/__tests__/backup.test.js @@ -0,0 +1,187 @@ +const mongoose = require('mongoose'); +const request = require('supertest'); +const { MongoMemoryServer } = require('mongodb-memory-server'); +const app = require('./test-app'); +const FinancialReport = require('../models/financialReport'); +const auth = require('../utils/auth'); + +describe('Financial Report API Integration', () => { + let mongoServer; + let adminToken; + let testUserId; + + beforeAll(async () => { + mongoServer = await MongoMemoryServer.create(); + const mongoUri = await mongoServer.getUri(); + + await mongoose.connect(mongoUri, { + useNewUrlParser: true, + useUnifiedTopology: true, + useCreateIndex: true // Suppresses the ensureIndex deprecation warning + }); + + testUserId = new mongoose.Types.ObjectId(); + adminToken = auth.generateToken({ + id: testUserId.toString(), + role: 'admin', + permissions: ['create:reports', 'read:reports', 'update:reports', 'delete:reports'] + }); + }); + + afterAll(async () => { + await mongoose.disconnect(); + await mongoServer.stop(); + }); + + describe('GET /api/financial-reports', () => { + beforeEach(async () => { + await FinancialReport.deleteMany({}); + }); + + it('should have the correct response structure when empty', async () => { + const response = await request(app) + .get('/api/financial-reports') + .set('Authorization', `Bearer ${adminToken}`) + .expect(200); + + expect(response.body).toMatchObject({ + reports: expect.any(Array), + totalCount: 0, + currentPage: 1, + totalPages: 0, + limit: 10 + }); + }); + + it('should require authentication', async () => { + const response = await request(app) + .get('/api/financial-reports') + .expect(401); + + expect(response.body).toHaveProperty('error'); + }); + + it('should reject invalid token', async () => { + const response = await request(app) + .get('/api/financial-reports') + .set('Authorization', 'Bearer invalid-token') + .expect(401); + + expect(response.body).toHaveProperty('error'); + }); + + it('should create and return a quarterly report', async () => { + try { + const report = new FinancialReport({ + ReportID: 'TEST-2024-Q1', + Type: 'Quarterly', + TotalRevenue: 100000, + TotalExpenses: 75000, + NetIncome: 25000, + EquitySummary: ['Initial equity test'], + Timestamp: new Date(), + userId: testUserId, + lastModifiedBy: testUserId + }); + + report.Data = { + revenue: new Map().set('q1', 100000), + expenses: new Map().set('q1', 75000) + }; + + console.log('Pre-save state:', { + type: report.Type, + data: { + revenue: report.Data.revenue instanceof Map ? + Array.from(report.Data.revenue.entries()) : report.Data.revenue, + expenses: report.Data.expenses instanceof Map ? + Array.from(report.Data.expenses.entries()) : report.Data.expenses + } + }); + + await report.save(); + + const response = await request(app) + .get('/api/financial-reports') + .set('Authorization', `Bearer ${adminToken}`) + .expect(200); + + expect(response.body.reports).toHaveLength(1); + expect(response.body.reports[0]).toMatchObject({ + ReportID: report.ReportID, + Type: report.Type, + TotalRevenue: 100000, + TotalExpenses: 75000, + NetIncome: 25000 + }); + + } catch (error) { + console.log('Error details:', error); + throw error; + } + }); + + it('should create and return an annual report with full quarterly data', async () => { + try { + const report = new FinancialReport({ + ReportID: 'TEST-2024-ANNUAL', + Type: 'Annual', + TotalRevenue: 400000, + TotalExpenses: 300000, + NetIncome: 100000, + EquitySummary: ['Year-end equity summary'], + Timestamp: new Date(), + userId: testUserId, + lastModifiedBy: testUserId + }); + + report.Data = { + revenue: new Map([['q1', 100000], ['q2', 100000], ['q3', 100000], ['q4', 100000]]), + expenses: new Map([['q1', 75000], ['q2', 75000], ['q3', 75000], ['q4', 75000]]) + }; + + await report.save(); + + const response = await request(app) + .get('/api/financial-reports') + .set('Authorization', `Bearer ${adminToken}`) + .expect(200); + + expect(response.body.reports).toHaveLength(1); + expect(response.body.reports[0]).toMatchObject({ + ReportID: report.ReportID, + Type: report.Type, + TotalRevenue: 400000, + TotalExpenses: 300000, + NetIncome: 100000 + }); + + } catch (error) { + console.log('Error details:', error); + throw error; + } + }); + + // New test: Verify validation for missing fields in financial report creation + it('should not create a report with missing required fields', async () => { + const invalidReportData = { + Type: 'Quarterly', + TotalRevenue: 50000, + // Missing TotalExpenses and NetIncome + EquitySummary: ['Partial equity summary'], + Timestamp: new Date(), + userId: testUserId, + lastModifiedBy: testUserId + }; + + const response = await request(app) + .post('/api/financial-reports') + .set('Authorization', `Bearer ${adminToken}`) + .send(invalidReportData) + .expect(400); + + expect(response.body).toHaveProperty('error'); + expect(response.body.error).toContain('Missing required fields'); + }); + }); +}); diff --git a/__tests__/financialReportCRUD.test.js b/__tests__/financialReportCRUD.test.js index 4129861..0339e63 100644 --- a/__tests__/financialReportCRUD.test.js +++ b/__tests__/financialReportCRUD.test.js @@ -1,166 +1,264 @@ // __tests__/financialReportCRUD.test.js const { - createFinancialReport, - getFinancialReport, - listFinancialReports, - updateFinancialReport, - deleteFinancialReport - } = require('../controllers/financialReportCrudController'); - const FinancialReport = require('../models/financialReport'); - const httpMocks = require('node-mocks-http'); - - jest.mock('../models/financialReport'); - - describe('Financial Report CRUD Operations', () => { - let req, res, next; - - beforeEach(() => { - req = httpMocks.createRequest(); - res = httpMocks.createResponse(); - next = jest.fn(); - jest.clearAllMocks(); - }); + createFinancialReport, + getFinancialReport, + listFinancialReports, + updateFinancialReport, + deleteFinancialReport +} = require('../controllers/financialReportCrudController'); +const FinancialReport = require('../models/financialReport'); +const httpMocks = require('node-mocks-http'); + +jest.mock('../models/financialReport'); + +describe('Financial Report CRUD Operations', () => { + let req, res, next; - const sampleReport = { - ReportID: 'test-id-123', - Type: 'Annual', - Data: { - revenue: { q1: 250000, q2: 250000, q3: 250000, q4: 250000 }, - expenses: { q1: 125000, q2: 125000, q3: 125000, q4: 125000 } - }, - TotalRevenue: '1000000.00', - TotalExpenses: '500000.00', - NetIncome: '500000.00', - EquitySummary: ['uuid1', 'uuid2'], - Timestamp: new Date().toISOString() - }; - - describe('READ Operations', () => { - it('should get a single financial report by ID', async () => { - const reportId = 'test-id-123'; - req.params = { id: reportId }; - - FinancialReport.findOne = jest.fn().mockResolvedValue(sampleReport); - - await getFinancialReport(req, res, next); - - expect(FinancialReport.findOne).toHaveBeenCalledWith({ ReportID: reportId }); - expect(res.statusCode).toBe(200); - expect(JSON.parse(res._getData())).toEqual(sampleReport); + beforeEach(() => { + req = httpMocks.createRequest(); + res = httpMocks.createResponse(); + next = jest.fn(); + jest.clearAllMocks(); + }); + + const sampleReport = { + ReportID: 'test-id-123', + Type: 'Annual', + Data: { + revenue: { q1: 250000, q2: 250000, q3: 250000, q4: 250000 }, + expenses: { q1: 125000, q2: 125000, q3: 125000, q4: 125000 } + }, + TotalRevenue: '1000000.00', + TotalExpenses: '500000.00', + NetIncome: '500000.00', + EquitySummary: ['uuid1', 'uuid2'], + Timestamp: new Date().toISOString() + }; + + describe('CREATE Operations', () => { + it('should create a new financial report', async () => { + const newReport = { ...sampleReport }; + req.body = newReport; + + FinancialReport.prototype.save = jest.fn().mockResolvedValue(newReport); + + await createFinancialReport(req, res, next); + + expect(res.statusCode).toBe(201); + expect(JSON.parse(res._getData())).toEqual(newReport); + }); + + it('should handle validation errors during creation', async () => { + const invalidReport = { ...sampleReport }; + delete invalidReport.Type; + req.body = invalidReport; + + await createFinancialReport(req, res, next); + + expect(res.statusCode).toBe(400); + expect(JSON.parse(res._getData())).toEqual({ + error: 'Missing required fields: Type' }); + }); + + it('should handle duplicate ReportID', async () => { + req.body = sampleReport; + + const duplicateError = new Error('Duplicate key error'); + duplicateError.code = 11000; + + FinancialReport.prototype.save = jest.fn().mockRejectedValue(duplicateError); + + await createFinancialReport(req, res, next); + + expect(next).toHaveBeenCalledWith(duplicateError); + }); + }); + + describe('READ Operations', () => { + it('should list financial reports with pagination', async () => { + req.query = { page: 1, limit: 10 }; + const reports = [sampleReport, { ...sampleReport, ReportID: 'test-id-456' }]; - it('should handle non-existent report', async () => { - req.params = { id: 'non-existent-id' }; - - FinancialReport.findOne = jest.fn().mockResolvedValue(null); + FinancialReport.find = jest.fn().mockReturnThis(); + FinancialReport.skip = jest.fn().mockReturnThis(); + FinancialReport.limit = jest.fn().mockResolvedValue(reports); + FinancialReport.countDocuments = jest.fn().mockResolvedValue(2); - await getFinancialReport(req, res, next); + await listFinancialReports(req, res, next); - expect(res.statusCode).toBe(404); - expect(JSON.parse(res._getData())).toEqual({ - message: 'Financial report not found' - }); + expect(res.statusCode).toBe(200); + expect(JSON.parse(res._getData())).toEqual({ + reports: reports, + totalCount: 2, + currentPage: 1, + totalPages: 1 }); - it('should list all financial reports with pagination', async () => { - req.query = { page: 1, limit: 10 }; - const reports = [sampleReport, { ...sampleReport, ReportID: 'test-id-456' }]; - const totalCount = 2; - - FinancialReport.find = jest.fn().mockReturnThis(); - FinancialReport.skip = jest.fn().mockReturnThis(); - FinancialReport.limit = jest.fn().mockResolvedValue(reports); - FinancialReport.countDocuments = jest.fn().mockResolvedValue(totalCount); - - await listFinancialReports(req, res, next); - - expect(res.statusCode).toBe(200); - expect(JSON.parse(res._getData())).toEqual({ - reports, - totalCount, - currentPage: 1, - totalPages: 1 - }); - }); + expect(FinancialReport.find).toHaveBeenCalled(); + expect(FinancialReport.skip).toHaveBeenCalledWith(0); + expect(FinancialReport.limit).toHaveBeenCalledWith(10); }); - describe('UPDATE Operations', () => { - it('should update an existing financial report', async () => { - const reportId = 'test-id-123'; - const updatedData = { - ...sampleReport, - TotalRevenue: '1100000.00', - TotalExpenses: '600000.00', - NetIncome: '500000.00' - }; - - req.params = { id: reportId }; - req.body = updatedData; - - FinancialReport.findOneAndUpdate = jest.fn() - .mockResolvedValue(updatedData); - - await updateFinancialReport(req, res, next); - - expect(FinancialReport.findOneAndUpdate).toHaveBeenCalledWith( - { ReportID: reportId }, - updatedData, - { new: true, runValidators: true } - ); - expect(res.statusCode).toBe(200); - expect(JSON.parse(res._getData())).toEqual(updatedData); + it('should handle edge case pagination parameters', async () => { + req.query = { page: -1, limit: 'invalid' }; + + const mockReports = []; + FinancialReport.find = jest.fn().mockReturnValue({ + skip: jest.fn().mockReturnValue({ + limit: jest.fn().mockResolvedValue(mockReports) + }) }); + FinancialReport.countDocuments = jest.fn().mockResolvedValue(0); - it('should handle update validation errors', async () => { - const reportId = 'test-id-123'; - const invalidData = { - ...sampleReport, - TotalRevenue: '-1000000.00' // Invalid negative value - }; + await listFinancialReports(req, res, next); - req.params = { id: reportId }; - req.body = invalidData; - - await updateFinancialReport(req, res, next); - - expect(res.statusCode).toBe(400); - expect(JSON.parse(res._getData())).toEqual({ - error: 'Financial values cannot be negative' - }); + expect(res.statusCode).toBe(200); + expect(JSON.parse(res._getData())).toEqual({ + reports: [], // Ensure empty array instead of empty object + totalCount: 0, + currentPage: -1, + totalPages: 0 }); }); - describe('DELETE Operations', () => { - it('should delete an existing financial report', async () => { - const reportId = 'test-id-123'; - req.params = { id: reportId }; + it('should list all financial reports with pagination', async () => { + req.query = { page: 1, limit: 10 }; + const reports = [sampleReport, { ...sampleReport, ReportID: 'test-id-456' }]; + const totalCount = 2; - FinancialReport.findOneAndDelete = jest.fn() - .mockResolvedValue(sampleReport); + FinancialReport.find = jest.fn().mockReturnValue({ + skip: jest.fn().mockReturnValue({ + limit: jest.fn().mockResolvedValue(reports) + }) + }); + FinancialReport.countDocuments = jest.fn().mockResolvedValue(totalCount); - await deleteFinancialReport(req, res, next); + await listFinancialReports(req, res, next); - expect(FinancialReport.findOneAndDelete) - .toHaveBeenCalledWith({ ReportID: reportId }); - expect(res.statusCode).toBe(200); - expect(JSON.parse(res._getData())).toEqual({ - message: 'Financial report deleted successfully', - report: sampleReport - }); + expect(res.statusCode).toBe(200); + expect(JSON.parse(res._getData())).toEqual({ + reports, + totalCount, + currentPage: 1, + totalPages: 1 }); - it('should handle deletion of non-existent report', async () => { - req.params = { id: 'non-existent-id' }; + const skip = (1 - 1) * 10; + expect(FinancialReport.find().skip).toHaveBeenCalledWith(skip); + expect(FinancialReport.find().skip().limit).toHaveBeenCalledWith(10); + }); - FinancialReport.findOneAndDelete = jest.fn() - .mockResolvedValue(null); + it('should handle database errors during read', async () => { + req.params = { id: 'test-id' }; + + const dbError = new Error('Database connection lost'); + FinancialReport.findOne = jest.fn().mockRejectedValue(dbError); - await deleteFinancialReport(req, res, next); + await getFinancialReport(req, res, next); - expect(res.statusCode).toBe(404); - expect(JSON.parse(res._getData())).toEqual({ - message: 'Financial report not found' - }); + expect(next).toHaveBeenCalledWith(dbError); + }); + }); + + describe('UPDATE Operations', () => { + it('should update an existing financial report', async () => { + const reportId = 'test-id-123'; + const updatedData = { + ...sampleReport, + TotalRevenue: '1100000.00', + TotalExpenses: '600000.00', + NetIncome: '500000.00' + }; + + req.params = { id: reportId }; + req.body = updatedData; + + FinancialReport.findOneAndUpdate = jest.fn().mockResolvedValue(updatedData); + + await updateFinancialReport(req, res, next); + + expect(FinancialReport.findOneAndUpdate).toHaveBeenCalledWith( + { ReportID: reportId }, + updatedData, + { new: true, runValidators: true } + ); + expect(res.statusCode).toBe(200); + expect(JSON.parse(res._getData())).toEqual(updatedData); + }); + + it('should handle validation errors during update', async () => { + const reportId = 'test-id-123'; + const invalidData = { + TotalRevenue: '1100000.00' // Missing required fields + }; + + req.params = { id: reportId }; + req.body = invalidData; + + await updateFinancialReport(req, res, next); + + expect(res.statusCode).toBe(400); + expect(JSON.parse(res._getData())).toEqual({ + error: 'Missing required fields: ReportID, Type, Data, TotalExpenses, NetIncome, Timestamp' }); }); - }); \ No newline at end of file + + it('should handle non-existent report update', async () => { + const reportId = 'non-existent-id'; + req.params = { id: reportId }; + req.body = sampleReport; + + FinancialReport.findOneAndUpdate = jest.fn().mockResolvedValue(null); + + await updateFinancialReport(req, res, next); + + expect(res.statusCode).toBe(404); + expect(JSON.parse(res._getData())).toEqual({ + message: 'Financial report not found' + }); + }); + }); + + describe('DELETE Operations', () => { + it('should delete an existing financial report', async () => { + const reportId = 'test-id-123'; + req.params = { id: reportId }; + + FinancialReport.findOneAndDelete = jest.fn().mockResolvedValue(sampleReport); + + await deleteFinancialReport(req, res, next); + + expect(FinancialReport.findOneAndDelete).toHaveBeenCalledWith({ ReportID: reportId }); + expect(res.statusCode).toBe(200); + expect(JSON.parse(res._getData())).toEqual({ + message: 'Financial report deleted successfully', + report: sampleReport + }); + }); + + it('should handle deletion of non-existent report', async () => { + req.params = { id: 'non-existent-id' }; + + FinancialReport.findOneAndDelete = jest.fn().mockResolvedValue(null); + + await deleteFinancialReport(req, res, next); + + expect(res.statusCode).toBe(404); + expect(JSON.parse(res._getData())).toEqual({ + message: 'Financial report not found' + }); + }); + + it('should handle database errors during deletion', async () => { + req.params = { id: 'test-id' }; + + const dbError = new Error('Database error'); + FinancialReport.findOneAndDelete = jest.fn().mockRejectedValue(dbError); + + await deleteFinancialReport(req, res, next); + + expect(next).toHaveBeenCalledWith(dbError); + }); + }); +}); \ No newline at end of file diff --git a/__tests__/financialReportIntegration.test.js b/__tests__/financialReportIntegration.test.js index 63b437d..eeb8ed5 100644 --- a/__tests__/financialReportIntegration.test.js +++ b/__tests__/financialReportIntegration.test.js @@ -17,14 +17,15 @@ describe('Financial Report API Integration', () => { await mongoose.connect(mongoUri, { useNewUrlParser: true, useUnifiedTopology: true, - useCreateIndex: true // Suppresses the ensureIndex deprecation warning + useCreateIndex: true, + useFindAndModify: false, }); testUserId = new mongoose.Types.ObjectId(); adminToken = auth.generateToken({ id: testUserId.toString(), role: 'admin', - permissions: ['create:reports', 'read:reports', 'update:reports', 'delete:reports'] + permissions: ['create:reports', 'read:reports', 'update:reports', 'delete:reports'], }); }); @@ -49,7 +50,7 @@ describe('Financial Report API Integration', () => { totalCount: 0, currentPage: 1, totalPages: 0, - limit: 10 + limit: 10, }); }); @@ -71,107 +72,86 @@ describe('Financial Report API Integration', () => { }); it('should create and return a quarterly report', async () => { - try { - const report = new FinancialReport({ - ReportID: 'TEST-2024-Q1', - Type: 'Quarterly', - TotalRevenue: 100000, - TotalExpenses: 75000, - NetIncome: 25000, - EquitySummary: ['Initial equity test'], - Timestamp: new Date(), - userId: testUserId, - lastModifiedBy: testUserId - }); - - report.Data = { - revenue: new Map().set('q1', 100000), - expenses: new Map().set('q1', 75000) - }; - - console.log('Pre-save state:', { - type: report.Type, - data: { - revenue: report.Data.revenue instanceof Map ? - Array.from(report.Data.revenue.entries()) : report.Data.revenue, - expenses: report.Data.expenses instanceof Map ? - Array.from(report.Data.expenses.entries()) : report.Data.expenses - } - }); - - await report.save(); - - const response = await request(app) - .get('/api/financial-reports') - .set('Authorization', `Bearer ${adminToken}`) - .expect(200); - - expect(response.body.reports).toHaveLength(1); - expect(response.body.reports[0]).toMatchObject({ - ReportID: report.ReportID, - Type: report.Type, - TotalRevenue: 100000, - TotalExpenses: 75000, - NetIncome: 25000 - }); - - } catch (error) { - console.log('Error details:', error); - throw error; - } + const report = new FinancialReport({ + ReportID: 'TEST-2024-Q1', + Type: 'Quarterly', + TotalRevenue: 100000, + TotalExpenses: 75000, + NetIncome: 25000, + EquitySummary: ['Initial equity test'], + Timestamp: new Date(), + userId: testUserId, + lastModifiedBy: testUserId, + Data: { + revenue: { q1: 100000 }, + expenses: { q1: 75000 }, + }, + }); + + console.log('Pre-save state:', { + type: report.Type, + data: report.Data, + }); + + await report.save(); + + const response = await request(app) + .get('/api/financial-reports') + .set('Authorization', `Bearer ${adminToken}`) + .expect(200); + + expect(response.body.reports).toHaveLength(1); + expect(response.body.reports[0]).toMatchObject({ + ReportID: report.ReportID, + Type: report.Type, + TotalRevenue: 100000, + TotalExpenses: 75000, + NetIncome: 25000, + }); }); it('should create and return an annual report with full quarterly data', async () => { - try { - const report = new FinancialReport({ - ReportID: 'TEST-2024-ANNUAL', - Type: 'Annual', - TotalRevenue: 400000, - TotalExpenses: 300000, - NetIncome: 100000, - EquitySummary: ['Year-end equity summary'], - Timestamp: new Date(), - userId: testUserId, - lastModifiedBy: testUserId - }); - - report.Data = { - revenue: new Map([['q1', 100000], ['q2', 100000], ['q3', 100000], ['q4', 100000]]), - expenses: new Map([['q1', 75000], ['q2', 75000], ['q3', 75000], ['q4', 75000]]) - }; - - await report.save(); - - const response = await request(app) - .get('/api/financial-reports') - .set('Authorization', `Bearer ${adminToken}`) - .expect(200); - - expect(response.body.reports).toHaveLength(1); - expect(response.body.reports[0]).toMatchObject({ - ReportID: report.ReportID, - Type: report.Type, - TotalRevenue: 400000, - TotalExpenses: 300000, - NetIncome: 100000 - }); - - } catch (error) { - console.log('Error details:', error); - throw error; - } + const report = new FinancialReport({ + ReportID: 'TEST-2024-ANNUAL', + Type: 'Annual', + TotalRevenue: 400000, + TotalExpenses: 300000, + NetIncome: 100000, + EquitySummary: ['Year-end equity summary'], + Timestamp: new Date(), + userId: testUserId, + lastModifiedBy: testUserId, + Data: { + revenue: { q1: 100000, q2: 100000, q3: 100000, q4: 100000 }, + expenses: { q1: 75000, q2: 75000, q3: 75000, q4: 75000 }, + }, + }); + + await report.save(); + + const response = await request(app) + .get('/api/financial-reports') + .set('Authorization', `Bearer ${adminToken}`) + .expect(200); + + expect(response.body.reports).toHaveLength(1); + expect(response.body.reports[0]).toMatchObject({ + ReportID: report.ReportID, + Type: report.Type, + TotalRevenue: 400000, + TotalExpenses: 300000, + NetIncome: 100000, + }); }); - // New test: Verify validation for missing fields in financial report creation it('should not create a report with missing required fields', async () => { const invalidReportData = { Type: 'Quarterly', TotalRevenue: 50000, - // Missing TotalExpenses and NetIncome EquitySummary: ['Partial equity summary'], Timestamp: new Date(), userId: testUserId, - lastModifiedBy: testUserId + lastModifiedBy: testUserId, }; const response = await request(app) @@ -183,5 +163,52 @@ describe('Financial Report API Integration', () => { expect(response.body).toHaveProperty('error'); expect(response.body.error).toContain('Missing required fields'); }); + + it('should update an existing financial report', async () => { + const report = new FinancialReport({ + ReportID: 'TEST-2024-Q1', + Type: 'Quarterly', + TotalRevenue: 100000, + TotalExpenses: 75000, + NetIncome: 25000, + EquitySummary: ['Initial equity test'], + Timestamp: new Date(), + userId: testUserId, + lastModifiedBy: testUserId, + Data: { + revenue: { q1: 100000 }, + expenses: { q1: 75000 }, + }, + }); + await report.save(); + + const updatedData = { + ReportID: report.ReportID, + Type: 'Quarterly', + TotalRevenue: 120000, + TotalExpenses: 80000, + NetIncome: 40000, + EquitySummary: ['Updated equity test'], + Data: { + revenue: { q1: 120000 }, + expenses: { q1: 80000 }, + }, + Timestamp: report.Timestamp, + userId: testUserId, + lastModifiedBy: testUserId + }; + + await request(app) + .put(`/api/financial-reports/${report.ReportID}`) + .set('Authorization', `Bearer ${adminToken}`) + .send(updatedData) + .expect(200); + + const updatedReport = await FinancialReport.findOne({ ReportID: report.ReportID }); + expect(updatedReport).toBeTruthy(); + expect(updatedReport.TotalRevenue).toBe(`${updatedData.TotalRevenue}.00`); + expect(updatedReport.TotalExpenses).toBe(`${updatedData.TotalExpenses}.00`); + expect(updatedReport.NetIncome).toBe(`${updatedData.NetIncome}.00`); + }); }); -}); +}); \ No newline at end of file diff --git a/__tests__/financialReportingController.test.js b/__tests__/financialReportingController.test.js deleted file mode 100644 index 7e018bd..0000000 --- a/__tests__/financialReportingController.test.js +++ /dev/null @@ -1,80 +0,0 @@ -const { createFinancialReport } = require('../controllers/financialReportingController'); -const FinancialReport = require('../models/financialReport'); -const httpMocks = require('node-mocks-http'); - -// Mock the entire model -jest.mock('../models/financialReport'); - -describe('createFinancialReport Controller', () => { - let req, res, next; - - beforeEach(() => { - req = httpMocks.createRequest(); - res = httpMocks.createResponse(); - next = jest.fn(); - jest.clearAllMocks(); - }); - - it('should create a new financial report and return 201 status', async () => { - // Setup request body with fixed timestamp - const testData = { - ReportID: 'test-id', - Type: 'Annual', - Data: { key: 'test-data' }, - TotalRevenue: '1000.00', - TotalExpenses: '500.00', - NetIncome: '500.00', - EquitySummary: ['uuid1', 'uuid2'], - Timestamp: new Date().toISOString() // Convert to ISO string format - }; - - req.body = testData; - - // Mock the constructor and save method - const mockSave = jest.fn().mockResolvedValue(testData); - const mockInstance = { save: mockSave }; - FinancialReport.mockImplementation(() => mockInstance); - - // Execute controller - await createFinancialReport(req, res, next); - - // Get response data and parse it - const responseData = JSON.parse(res._getData()); - - // Assertions - expect(FinancialReport).toHaveBeenCalledWith(testData); - expect(mockSave).toHaveBeenCalled(); - expect(res.statusCode).toBe(201); - expect(responseData).toEqual(testData); // Compare with the original test data - expect(next).not.toHaveBeenCalled(); - }); - - it('should handle errors and pass them to the error handling middleware', async () => { - // Setup request body with fixed timestamp - const testData = { - ReportID: 'test-id', - Type: 'Annual', - Data: { key: 'test-data' }, - TotalRevenue: '1000.00', - TotalExpenses: '500.00', - NetIncome: '500.00', - EquitySummary: ['uuid1', 'uuid2'], - Timestamp: new Date().toISOString() // Convert to ISO string format - }; - - req.body = testData; - - // Mock constructor and save method to throw error - const error = new Error('Failed to create financial report'); - const mockSave = jest.fn().mockRejectedValue(error); - FinancialReport.mockImplementation(() => ({ save: mockSave })); - - // Execute controller - await createFinancialReport(req, res, next); - - // Assertions - expect(FinancialReport).toHaveBeenCalledWith(testData); - expect(mockSave).toHaveBeenCalled(); - expect(next).toHaveBeenCalledWith(error); - }); -}); \ No newline at end of file diff --git a/__tests__/setup/jest.setup.js b/__tests__/setup/jest.setup.js new file mode 100644 index 0000000..0922d4c --- /dev/null +++ b/__tests__/setup/jest.setup.js @@ -0,0 +1,14 @@ +// __tests__/setup/jest.setup.js +process.env.NODE_ENV = 'test'; +process.env.JWT_SECRET = 'test-secret'; +process.env.MONGODB_URI = 'mongodb://localhost:27017/test'; + +jest.setTimeout(30000); + +beforeAll(async () => { + console.log('Test setup initialized'); +}); + +afterAll(async () => { + await new Promise(resolve => setTimeout(resolve, 500)); +}); \ No newline at end of file diff --git a/__tests__/setup/test-config.js b/__tests__/setup/test-config.js new file mode 100644 index 0000000..b0569af --- /dev/null +++ b/__tests__/setup/test-config.js @@ -0,0 +1,6 @@ +// __tests__/setup/test-config.js +module.exports = { + JWT_SECRET: 'test-secret', + API_BASE_PATH: '/api/financial-reports', + VALID_API_KEY: 'valid-key' + }; \ No newline at end of file diff --git a/babel.config b/babel.config index 1320b9a..41c48fe 100644 --- a/babel.config +++ b/babel.config @@ -1,3 +1,6 @@ -{ - "presets": ["@babel/preset-env"] -} + +module.exports = { + presets: [ + ['@babel/preset-env', { targets: { node: 'current' } }] + ] +}; \ No newline at end of file diff --git a/client/DashboardMain.jsx b/client/DashboardMain.jsx new file mode 100644 index 0000000..965391f --- /dev/null +++ b/client/DashboardMain.jsx @@ -0,0 +1,132 @@ +import React from 'react'; + +const Dashboard = () => { + const sidebarItems = [ + 'Overview', 'Captable', 'Stakeholders', 'Share classes', 'Equity plans', + 'Securities', 'Fundraise', 'Documents', 'Updates', 'Reports', 'Audits', + 'Settings', 'Form 3921', '409A Valuation' + ]; + + const ownershipData = [ + { name: 'Allen Smith', percentage: 27 }, + { name: 'Toby Mornin', percentage: 25 }, + { name: 'Others', percentage: 18 }, + { name: 'Equity Plan', percentage: 15 }, + { name: 'USA Ventures', percentage: 10 }, + ]; + + const activities = [ + { user: 'Allen Smith', action: 'uploaded a document', document: '2023 W-2.pdf', time: 'a day ago' }, + { user: 'Allen Smith', action: 'uploaded a document', document: 'yc-post-money-safe-with-discount.pdf', time: '2 days ago' }, + { user: 'Allen Smith', action: 'uploaded a document', document: 'Atlas-Articles-of-Incorporation.pdf', time: '3 days ago' }, + { user: 'Allen Smith', action: 'uploaded a document', document: 'Atlas-Corp-Operating-Agreement.pdf', time: '3 days ago' }, + ]; + + const summaryData = [ + { shareClass: 'Common shares', authorizedShares: '7,000,000', dilutedShares: '4,500,000', ownership: '53%', amountRaised: '$10,000,000' }, + { shareClass: 'Preferred (Series A)', authorizedShares: '2,000,000', dilutedShares: '1,500,000', ownership: '15%', amountRaised: '$18,000,000' }, + { shareClass: 'Preferred (Convertible note)', authorizedShares: '1,000,000', dilutedShares: '500,000', ownership: '7%', amountRaised: '$7,000,000' }, + ]; + + return ( +
+ {/* Sidebar */} +
+
Musa Capital
+ +
+ + {/* Main Content */} +
+

Overview View your company's captable

+ + {/* Statistics */} +
+ {[ + { label: 'Amount raised', value: '$25.00M' }, + { label: 'Diluted shares', value: '7.56M' }, + { label: 'Stakeholders', value: '4' } + ].map((stat, index) => ( +
+
{stat.label}
+
{stat.value}
+
+ ))} +
+ + {/* Ownership Chart */} +
+

Ownership by Stakeholders

+
+
+ {ownershipData.map((item, index) => ( +
+ {item.name} + {item.percentage}% +
+ ))} +
+
+
+
+ + {/* Activities */} +
+

Activities

+
+ {activities.map((activity, index) => ( +
+ {activity.user} {activity.action}:{' '} + {activity.document}{' '} + {activity.time} +
+ ))} +
+ +
+ + {/* Summary Table */} +
+

Summary of your company's captable

+
+ + + + + + + + + + + + {summaryData.map((row, index) => ( + + + + + + + + ))} + +
Share classAuthorized sharesDiluted sharesOwnershipAmount raised
{row.shareClass}{row.authorizedShares}{row.dilutedShares}{row.ownership}{row.amountRaised}
+
+
+
+
+ ); +}; + +export default Dashboard; \ No newline at end of file diff --git a/client/DashboardMain2.jsx b/client/DashboardMain2.jsx new file mode 100644 index 0000000..2690ecf --- /dev/null +++ b/client/DashboardMain2.jsx @@ -0,0 +1,157 @@ +import React from 'react'; +import { + Card, + CardContent, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { Button } from "@/components/ui/button"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; + +const Dashboard = () => { + const sidebarItems = [ + 'Overview', 'Captable', 'Stakeholders', 'Share classes', 'Equity plans', + 'Securities', 'Fundraise', 'Documents', 'Updates', 'Reports', 'Audits', + 'Settings', 'Form 3921', '409A Valuation' + ]; + + const ownershipData = [ + { name: 'Allen Smith', percentage: 27 }, + { name: 'Toby Mornin', percentage: 25 }, + { name: 'Others', percentage: 18 }, + { name: 'Equity Plan', percentage: 15 }, + { name: 'USA Ventures', percentage: 10 }, + ]; + + const activities = [ + { user: 'Allen Smith', action: 'uploaded a document', document: '2023 W-2.pdf', time: 'a day ago' }, + { user: 'Allen Smith', action: 'uploaded a document', document: 'yc-post-money-safe-with-discount.pdf', time: '2 days ago' }, + { user: 'Allen Smith', action: 'uploaded a document', document: 'Atlas-Articles-of-Incorporation.pdf', time: '3 days ago' }, + { user: 'Allen Smith', action: 'uploaded a document', document: 'Atlas-Corp-Operating-Agreement.pdf', time: '3 days ago' }, + ]; + + const summaryData = [ + { shareClass: 'Common shares', authorizedShares: '7,000,000', dilutedShares: '4,500,000', ownership: '53%', amountRaised: '$10,000,000' }, + { shareClass: 'Preferred (Series A)', authorizedShares: '2,000,000', dilutedShares: '1,500,000', ownership: '15%', amountRaised: '$18,000,000' }, + { shareClass: 'Preferred (Convertible note)', authorizedShares: '1,000,000', dilutedShares: '500,000', ownership: '7%', amountRaised: '$7,000,000' }, + ]; + + return ( +
+ {/* Sidebar */} +
+
Musa Capital
+ +
+ + {/* Main Content */} +
+

Overview View your company's captable

+ + {/* Statistics */} +
+ {[ + { label: 'Amount raised', value: '$25.00M' }, + { label: 'Diluted shares', value: '7.56M' }, + { label: 'Stakeholders', value: '4' } + ].map((stat, index) => ( + + +
{stat.label}
+
{stat.value}
+
+
+ ))} +
+ + {/* Ownership Chart */} + + + Ownership by Stakeholders + + +
+
+ {ownershipData.map((item, index) => ( +
+ {item.name} + {item.percentage}% +
+ ))} +
+
+
+ + + + {/* Activities */} + + + Activities + + +
+ {activities.map((activity, index) => ( +
+ {activity.user} {activity.action}:{' '} + {activity.document}{' '} + {activity.time} +
+ ))} +
+ +
+
+ + {/* Summary Table */} + + + Summary of your company's captable + + + + + + Share class + Authorized shares + Diluted shares + Ownership + Amount raised + + + + {summaryData.map((row, index) => ( + + {row.shareClass} + {row.authorizedShares} + {row.dilutedShares} + {row.ownership} + {row.amountRaised} + + ))} + +
+
+
+
+
+ ); +}; + +export default Dashboard; \ No newline at end of file diff --git a/client/components/Activities.jsx b/client/components/Activities.jsx new file mode 100644 index 0000000..f6e74a9 --- /dev/null +++ b/client/components/Activities.jsx @@ -0,0 +1,56 @@ +import React from 'react'; +import styled from 'styled-components'; + +const ActivitiesContainer = styled.div` + margin-bottom: 16px; +`; + +const ActivityTitle = styled.h2` + font-size: 18px; + margin-bottom: 8px; +`; + +const ActivityList = styled.div` + display: flex; + flex-direction: column; +`; + +const ActivityItem = styled.div` + margin-bottom: 8px; + font-size: 14px; +`; + +const ViewAllButton = styled.button` + background-color: #f0f0f0; + border: none; + padding: 8px 16px; + cursor: pointer; + &:hover { + background-color: #e0e0e0; + } +`; + +const Activities: React.FC = () => { + const activities = [ + { user: 'Allen Smith', action: 'uploaded a document', document: '2023 W-2.pdf', time: 'a day ago' }, + { user: 'Allen Smith', action: 'uploaded a document', document: 'yc-post-money-safe-with-discount.pdf', time: '2 days ago' }, + { user: 'Allen Smith', action: 'uploaded a document', document: 'Atlas-Articles-of-Incorporation.pdf', time: '3 days ago' }, + { user: 'Allen Smith', action: 'uploaded a document', document: 'Atlas-Corp-Operating-Agreement.pdf', time: '3 days ago' }, + ]; + + return ( + + Activities + + {activities.map((activity, index) => ( + + {activity.user} {activity.action}: {activity.document} {activity.time} + + ))} + + View all activity + + ); +}; + +export default Activities; \ No newline at end of file diff --git a/client/components/Dashboard.jsx b/client/components/Dashboard.jsx new file mode 100644 index 0000000..902e82a --- /dev/null +++ b/client/components/Dashboard.jsx @@ -0,0 +1,21 @@ +import React from 'react'; +import styled from 'styled-components'; +import Sidebar from './Sidebar'; +import MainContent from './MainContent'; + +const DashboardContainer = styled.div` + display: flex; + width: 1228px; + height: 768px; +`; + +const Dashboard: React.FC = () => { + return ( + + + + + ); +}; + +export default Dashboard; \ No newline at end of file diff --git a/client/components/MainContent.jsx b/client/components/MainContent.jsx new file mode 100644 index 0000000..8f99a4b --- /dev/null +++ b/client/components/MainContent.jsx @@ -0,0 +1,33 @@ +import React from 'react'; +import styled from 'styled-components'; +import Statistics from './Statistics'; +import OwnershipChart from './OwnershipChart'; +import Activities from './Activities'; +import Summary from './Summary'; + +const MainContentContainer = styled.div` + display: flex; + flex-direction: column; + padding: 16px; + width: 1028px; + height: 768px; +`; + +const Title = styled.h1` + font-size: 24px; + margin-bottom: 16px; +`; + +const MainContent: React.FC = () => { + return ( + + Overview View your company's captable + + + + + + ); +}; + +export default MainContent; \ No newline at end of file diff --git a/client/components/OwnershipChart.jsx b/client/components/OwnershipChart.jsx new file mode 100644 index 0000000..c3e1987 --- /dev/null +++ b/client/components/OwnershipChart.jsx @@ -0,0 +1,53 @@ +import React from 'react'; +import styled from 'styled-components'; + +const ChartContainer = styled.div` + display: flex; + margin-bottom: 16px; +`; + +const OwnershipList = styled.div` + flex: 1; +`; + +const OwnershipItem = styled.div` + margin-bottom: 8px; +`; + +const PieChart = styled.div` + width: 200px; + height: 200px; + border-radius: 50%; + background: conic-gradient( + #6666ff 0deg 97.2deg, + #9999ff 97.2deg 187.2deg, + #ccccff 187.2deg 252deg, + #ffcccc 252deg 306deg, + #ffeecc 306deg 360deg + ); +`; + +const OwnershipChart: React.FC = () => { + const ownershipData = [ + { name: 'Allen Smith', percentage: 27 }, + { name: 'Toby Mornin', percentage: 25 }, + { name: 'Others', percentage: 18 }, + { name: 'Equity Plan', percentage: 15 }, + { name: 'USA Ventures', percentage: 10 }, + ]; + + return ( + + + {ownershipData.map((item, index) => ( + + {item.name} {item.percentage}% + + ))} + + + + ); +}; + +export default OwnershipChart; \ No newline at end of file diff --git a/client/components/Sidebar.jsx b/client/components/Sidebar.jsx new file mode 100644 index 0000000..085cf6b --- /dev/null +++ b/client/components/Sidebar.jsx @@ -0,0 +1,44 @@ +import React from 'react'; +import styled from 'styled-components'; + +const SidebarContainer = styled.div` + display: flex; + flex-direction: column; + padding: 16px; + width: 200px; + height: 768px; + background-color: #f0f0f0; +`; + +const Logo = styled.div` + font-size: 24px; + font-weight: bold; + margin-bottom: 16px; +`; + +const NavItem = styled.div` + margin-bottom: 8px; + cursor: pointer; + &:hover { + color: #0066cc; + } +`; + +const Sidebar: React.FC = () => { + const navItems = [ + 'Overview', 'Captable', 'Stakeholders', 'Share classes', 'Equity plans', + 'Securities', 'Fundraise', 'Documents', 'Updates', 'Reports', 'Audits', + 'Settings', 'Form 3921', '409A Valuation' + ]; + + return ( + + Musa Capital + {navItems.map((item, index) => ( + {item} + ))} + + ); +}; + +export default Sidebar; \ No newline at end of file diff --git a/client/components/Statistics.jsx b/client/components/Statistics.jsx new file mode 100644 index 0000000..da30afe --- /dev/null +++ b/client/components/Statistics.jsx @@ -0,0 +1,44 @@ +import React from 'react'; +import styled from 'styled-components'; + +const StatisticsContainer = styled.div` + display: flex; + justify-content: space-between; + margin-bottom: 16px; +`; + +const StatItem = styled.div` + display: flex; + flex-direction: column; +`; + +const StatLabel = styled.span` + font-size: 14px; + color: #666; +`; + +const StatValue = styled.span` + font-size: 24px; + font-weight: bold; +`; + +const Statistics: React.FC = () => { + return ( + + + Amount raised + $25.00M + + + Diluted shares + 7.56M + + + Stakeholders + 4 + + + ); +}; + +export default Statistics; \ No newline at end of file diff --git a/client/components/Summary.jsx b/client/components/Summary.jsx new file mode 100644 index 0000000..e3dddf7 --- /dev/null +++ b/client/components/Summary.jsx @@ -0,0 +1,65 @@ +import React from 'react'; +import styled from 'styled-components'; + +const SummaryContainer = styled.div` + margin-bottom: 16px; +`; + +const SummaryTitle = styled.h2` + font-size: 18px; + margin-bottom: 8px; +`; + +const SummaryTable = styled.table` + width: 100%; + border-collapse: collapse; +`; + +const TableHeader = styled.th` + text-align: left; + padding: 8px; + background-color: #f0f0f0; +`; + +const TableCell = styled.td` + padding: 8px; + border-bottom: 1px solid #e0e0e0; +`; + +const Summary: React.FC = () => { + const summaryData = [ + { shareClass: 'Common shares', authorizedShares: '7,000,000', dilutedShares: '4,500,000', ownership: '53%', amountRaised: '$10,000,000' }, + { shareClass: 'Preferred (Series A)', authorizedShares: '2,000,000', dilutedShares: '1,500,000', ownership: '15%', amountRaised: '$18,000,000' }, + { shareClass: 'Preferred (Convertible note)', authorizedShares: '1,000,000', dilutedShares: '500,000', ownership: '7%', amountRaised: '$7,000,000' }, + ]; + + return ( + + Summary of your company's captable + + + + Share class + Authorized shares + Diluted shares + Ownership + Amount raised + + + + {summaryData.map((row, index) => ( + + {row.shareClass} + {row.authorizedShares} + {row.dilutedShares} + {row.ownership} + {row.amountRaised} + + ))} + + + + ); +}; + +export default Summary; \ No newline at end of file diff --git a/config/index.js b/config/index.js new file mode 100644 index 0000000..5e0225c --- /dev/null +++ b/config/index.js @@ -0,0 +1,18 @@ +// config/index.js +module.exports = { + JWT_SECRET: process.env.JWT_SECRET || 'test-secret', + MONGODB_URI: process.env.MONGODB_URI || 'mongodb://localhost:27017/opencap_test', + API_VERSION: 'v1', + AUTH: { + TOKEN_EXPIRATION: '24h', + REFRESH_TOKEN_EXPIRATION: '7d', + SALT_ROUNDS: 10 + }, + PERMISSIONS: { + GET: 'read:reports', + POST: 'create:reports', + PUT: 'update:reports', + PATCH: 'update:reports', + DELETE: 'delete:reports' + } +}; \ No newline at end of file diff --git a/controllers/financialReportingController.js b/controllers/financialReportingController.js index 1ee5075..117f5eb 100644 --- a/controllers/financialReportingController.js +++ b/controllers/financialReportingController.js @@ -1,4 +1,3 @@ -// controllers/financialReportingController.js const FinancialReport = require("../models/financialReport"); const jwt = require('jsonwebtoken'); const config = require('../config'); @@ -187,7 +186,6 @@ class FinancialReportController { } } - // Authorization Methods static async checkUserPermissions(req, res, next) { try { const { user } = req; @@ -297,10 +295,13 @@ class FinancialReportController { // CRUD Methods static async createFinancialReport(req, res, next) { - const session = await mongoose.startSession(); - session.startTransaction(); - + let session; try { + if (mongoose.connection.readyState === 1 && mongoose.connection.db.serverConfig.replset) { + session = await mongoose.startSession(); + session.startTransaction(); + } + const validation = this.validateFinancialReport(req.body); if (!validation.isValid) { return res.status(400).json({ error: validation.error }); @@ -308,25 +309,15 @@ class FinancialReportController { const financialReport = new FinancialReport(req.body); const newFinancialReport = await financialReport.save({ session }); - - await session.commitTransaction(); - - return res.status(201).json({ - ReportID: newFinancialReport.ReportID, - Type: newFinancialReport.Type, - Data: newFinancialReport.Data, - TotalRevenue: newFinancialReport.TotalRevenue, - TotalExpenses: newFinancialReport.TotalExpenses, - NetIncome: newFinancialReport.NetIncome, - EquitySummary: newFinancialReport.EquitySummary, - Timestamp: newFinancialReport.Timestamp, - userId: newFinancialReport.userId - }); + + if (session) await session.commitTransaction(); + + return res.status(201).json(newFinancialReport); } catch (error) { - await session.abortTransaction(); + if (session) await session.abortTransaction(); next(error); } finally { - session.endSession(); + if (session) session.endSession(); } } @@ -374,10 +365,13 @@ class FinancialReportController { } static async updateFinancialReport(req, res, next) { - const session = await mongoose.startSession(); - session.startTransaction(); - + let session; try { + if (mongoose.connection.readyState === 1 && mongoose.connection.db.serverConfig.replset) { + session = await mongoose.startSession(); + session.startTransaction(); + } + if (!req.params.id) { return res.status(400).json({ message: 'Report ID is required' }); } @@ -387,6 +381,16 @@ class FinancialReportController { return res.status(400).json({ error: validation.error }); } + // Convert Data field to Map if provided as an object + if (req.body.Data) { + if (req.body.Data.revenue && !(req.body.Data.revenue instanceof Map)) { + req.body.Data.revenue = new Map(Object.entries(req.body.Data.revenue)); + } + if (req.body.Data.expenses && !(req.body.Data.expenses instanceof Map)) { + req.body.Data.expenses = new Map(Object.entries(req.body.Data.expenses)); + } + } + const report = await FinancialReport.findOneAndUpdate( { ReportID: req.params.id }, req.body, @@ -398,25 +402,28 @@ class FinancialReportController { ); if (!report) { - await session.abortTransaction(); + if (session) await session.abortTransaction(); return res.status(404).json({ message: 'Financial report not found' }); } - await session.commitTransaction(); + if (session) await session.commitTransaction(); res.status(200).json(report); } catch (error) { - await session.abortTransaction(); + if (session) await session.abortTransaction(); next(error); } finally { - session.endSession(); + if (session) session.endSession(); } } static async deleteFinancialReport(req, res, next) { - const session = await mongoose.startSession(); - session.startTransaction(); - + let session; try { + if (mongoose.connection.readyState === 1 && mongoose.connection.db.serverConfig.replset) { + session = await mongoose.startSession(); + session.startTransaction(); + } + if (!req.params.id) { return res.status(400).json({ message: 'Report ID is required' }); } @@ -427,44 +434,45 @@ class FinancialReportController { ); if (!report) { - await session.abortTransaction(); + if (session) await session.abortTransaction(); return res.status(404).json({ message: 'Financial report not found' }); } - await session.commitTransaction(); + if (session) await session.commitTransaction(); res.status(200).json({ message: 'Financial report deleted successfully', report }); } catch (error) { - await session.abortTransaction(); + if (session) await session.abortTransaction(); next(error); } finally { - session.endSession(); + if (session) session.endSession(); } } static async generateReport(req, res, next) { - const session = await mongoose.startSession(); - session.startTransaction(); - + let session; try { - // Validate the report data + if (mongoose.connection.readyState === 1 && mongoose.connection.db.serverConfig.replset) { + session = await mongoose.startSession(); + session.startTransaction(); + } + const validation = this.validateFinancialReport(req.body); if (!validation.isValid) { return res.status(400).json({ error: validation.error }); } - // Create the report with transaction support const report = await FinancialReport.create([req.body], { session }); - - await session.commitTransaction(); + + if (session) await session.commitTransaction(); res.status(201).json(report[0]); } catch (error) { - await session.abortTransaction(); + if (session) await session.abortTransaction(); next(error); } finally { - session.endSession(); + if (session) session.endSession(); } } } @@ -482,4 +490,4 @@ module.exports = { updateFinancialReport: FinancialReportController.updateFinancialReport.bind(FinancialReportController), deleteFinancialReport: FinancialReportController.deleteFinancialReport.bind(FinancialReportController), generateReport: FinancialReportController.generateReport.bind(FinancialReportController) -}; \ No newline at end of file +}; diff --git a/jest.config.js b/jest.config.js index 938702f..b65ddd9 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,9 +1,28 @@ +// jest.config.js +const path = require('path'); + module.exports = { - globalSetup: './__tests__/globalSetup.js', - globalTeardown: './__tests__/globalTeardown.js', - testMatch: ['**/__tests__/**/*.[jt]s?(x)'], - testPathIgnorePatterns: ['/node_modules/', '/dist/'], + testEnvironment: 'node', testTimeout: 30000, + setupFilesAfterEnv: [ + path.resolve(__dirname, '__tests__/setup/jest.setup.js') + ], + moduleFileExtensions: ['js', 'json'], + testMatch: [ + "**/__tests__/**/*.(test|integration.test|unit.test).js" + ], verbose: true, - detectOpenHandles: true, // Optional, helps find open handles -}; + detectOpenHandles: true, + forceExit: true, + clearMocks: true, + restoreMocks: true, + moduleNameMapper: { + '^@/(.*)$': '/$1', + '^@middleware/(.*)$': '/middleware/$1', + '^@routes/(.*)$': '/routes/$1', + '^@controllers/(.*)$': '/controllers/$1', + '^@models/(.*)$': '/models/$1' + }, + roots: [''], + modulePaths: [path.resolve(__dirname)] +}; \ No newline at end of file diff --git a/models/financialReport.js b/models/financialReport.js index 9ec06da..af47abf 100644 --- a/models/financialReport.js +++ b/models/financialReport.js @@ -1,46 +1,103 @@ -// models/financialReport.js - const mongoose = require('mongoose'); const { Schema } = mongoose; -const { v4: uuidv4 } = require('uuid'); const FinancialReportSchema = new Schema({ - ReportID: { - type: String, - default: uuidv4, // Generate a UUID by default + ReportID: { + type: String, + required: true, unique: true, - required: true, + trim: true }, - Type: { - type: String, - enum: ['Annual', 'Quarterly'], // Enum values - required: true, + Type: { + type: String, + enum: ['Annual', 'Quarterly'], + required: true }, Data: { - type: Schema.Types.Mixed, // Stores JSON data - required: true, + revenue: { + type: Map, + of: Number, // Simplified - just Number instead of object + validate: { + validator: function(v) { + if (!v) return false; + if (this.Type === 'Annual') { + return ['q1', 'q2', 'q3', 'q4'].every(q => v.has(q)); + } + return v.size === 1; // Use size instead of Object.keys().length + }, + message: props => `Invalid quarters for ${props.value} report type` + }, + required: true, + _id: false // Disable _id for subdocuments + }, + expenses: { + type: Map, + of: Number, // Simplified - just Number instead of object + validate: { + validator: function(v) { + if (!v) return false; + if (this.Type === 'Annual') { + return ['q1', 'q2', 'q3', 'q4'].every(q => v.has(q)); + } + return v.size === 1; // Use size instead of Object.keys().length + }, + message: props => `Invalid quarters for ${props.value} report type` + }, + required: true, + _id: false // Disable _id for subdocuments + } }, - TotalRevenue: { - type: Schema.Types.Decimal128, // Decimal for monetary values + TotalRevenue: { + type: Number, required: true, + min: 0, + get: v => v.toFixed(2), + set: v => parseFloat(v) }, - TotalExpenses: { - type: Schema.Types.Decimal128, + TotalExpenses: { + type: Number, required: true, + min: 0, + get: v => v.toFixed(2), + set: v => parseFloat(v) }, - NetIncome: { - type: Schema.Types.Decimal128, + NetIncome: { + type: Number, required: true, + get: v => v.toFixed(2), + set: v => parseFloat(v) }, - EquitySummary: { - type: [String], // Array of UUIDs (shareClassId) - default: [], // Optional field + EquitySummary: [String], + Timestamp: { + type: Date, + required: true, + default: Date.now }, - Timestamp: { - type: Date, - default: Date.now, + userId: { + type: Schema.Types.ObjectId, + ref: 'User', required: true, + index: true }, + createdAt: { type: Date, default: Date.now }, + updatedAt: { type: Date, default: Date.now }, + lastModifiedBy: { type: Schema.Types.ObjectId, ref: 'User' } +}, { + timestamps: true, + strict: false // Allow flexible Map contents }); -module.exports = mongoose.model('FinancialReport', FinancialReportSchema); +// Indexes +FinancialReportSchema.index({ ReportID: 1 }, { unique: true }); +FinancialReportSchema.index({ Timestamp: -1 }); + +// Methods +FinancialReportSchema.methods.calculateTotals = function() { + const revenue = Array.from(this.Data.revenue.values()).reduce((a, b) => a + b, 0); + const expenses = Array.from(this.Data.expenses.values()).reduce((a, b) => a + b, 0); + this.TotalRevenue = revenue; + this.TotalExpenses = expenses; + this.NetIncome = revenue - expenses; +}; + +module.exports = mongoose.model('FinancialReport', FinancialReportSchema); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 4c502a8..5570328 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38,7 +38,7 @@ "chai": "^4.2.0", "hest": "^1.0.5", "jest": "^29.7.0", - "mongodb-memory-server": "^9.4.1", + "mongodb-memory-server": "^9.5.0", "supertest": "^6.3.4" } }, @@ -57,13 +57,14 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.25.7.tgz", - "integrity": "sha512-0xZJFNE5XMpENsgfHYTw8FbX4kv53mFLn2i3XPoq69LyhYSCBJtitaHx9QnsVTrsogI4Z3+HtEfZ2/GFPOtf5g==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/highlight": "^7.25.7", + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", "picocolors": "^1.0.0" }, "engines": { @@ -71,9 +72,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.8.tgz", - "integrity": "sha512-ZsysZyXY4Tlx+Q53XdnOFmqwfB9QDTHYxaZYajWRoBLuLEAwI2UIbtxOjWh/cFaa9IKUlcB+DDuoskLuKu56JA==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.2.tgz", + "integrity": "sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==", "dev": true, "license": "MIT", "engines": { @@ -81,22 +82,22 @@ } }, "node_modules/@babel/core": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.8.tgz", - "integrity": "sha512-Oixnb+DzmRT30qu9d3tJSQkxuygWm32DFykT4bRoORPa9hZ/L4KhVB/XiRm6KG+roIEM7DBQlmg27kw2HZkdZg==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", + "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.25.7", - "@babel/generator": "^7.25.7", - "@babel/helper-compilation-targets": "^7.25.7", - "@babel/helper-module-transforms": "^7.25.7", - "@babel/helpers": "^7.25.7", - "@babel/parser": "^7.25.8", - "@babel/template": "^7.25.7", - "@babel/traverse": "^7.25.7", - "@babel/types": "^7.25.8", + "@babel/code-frame": "^7.26.0", + "@babel/generator": "^7.26.0", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.0", + "@babel/parser": "^7.26.0", + "@babel/template": "^7.25.9", + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.26.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -140,13 +141,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.7.tgz", - "integrity": "sha512-5Dqpl5fyV9pIAD62yK9P7fcA768uVPUyrQmqpqstHWgMma4feF1x/oFysBCVZLY5wJ2GkMUCdsNDnGZrPoR6rA==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz", + "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.25.7", + "@babel/parser": "^7.26.2", + "@babel/types": "^7.26.0", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -156,14 +158,14 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.7.tgz", - "integrity": "sha512-DniTEax0sv6isaw6qSQSfV4gVRNtw2rte8HHM45t9ZR0xILaufBRNkpMifCRiAPyvL4ACD6v0gfCwCmtOQaV4A==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", + "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.25.7", - "@babel/helper-validator-option": "^7.25.7", + "@babel/compat-data": "^7.25.9", + "@babel/helper-validator-option": "^7.25.9", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" @@ -183,30 +185,29 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.7.tgz", - "integrity": "sha512-o0xCgpNmRohmnoWKQ0Ij8IdddjyBFE4T2kagL/x6M3+4zUgc+4qTOUBoNe4XxDskt1HPKO007ZPiMgLDq2s7Kw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.25.7", - "@babel/types": "^7.25.7" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.7.tgz", - "integrity": "sha512-k/6f8dKG3yDz/qCwSM+RKovjMix563SLxQFo0UhRNo239SP6n9u5/eLtKD6EAjwta2JHJ49CsD8pms2HdNiMMQ==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", + "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.25.7", - "@babel/helper-simple-access": "^7.25.7", - "@babel/helper-validator-identifier": "^7.25.7", - "@babel/traverse": "^7.25.7" + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -216,33 +217,19 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.7.tgz", - "integrity": "sha512-eaPZai0PiqCi09pPs3pAFfl/zYgGaE6IdXtYvmf0qlcDTd3WCtO7JWCcRd64e0EQrcYgiHibEZnOGsSY4QSgaw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.25.7.tgz", - "integrity": "sha512-FPGAkJmyoChQeM+ruBGIDyrT2tKfZJO8NcxdC+CWNJi7N8/rZpSxK7yvBJ5O/nF1gfu5KzN7VKG3YVSLFfRSxQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz", + "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==", "dev": true, "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.25.7", - "@babel/types": "^7.25.7" - }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.7.tgz", - "integrity": "sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", "dev": true, "license": "MIT", "engines": { @@ -250,9 +237,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.7.tgz", - "integrity": "sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", "dev": true, "license": "MIT", "engines": { @@ -260,9 +247,9 @@ } }, "node_modules/@babel/helper-validator-option": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.7.tgz", - "integrity": "sha512-ytbPLsm+GjArDYXJ8Ydr1c/KJuutjF2besPNbIZnZ6MKUxi/uTA22t2ymmA4WFjZFpjiAMO0xuuJPqK2nvDVfQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", "dev": true, "license": "MIT", "engines": { @@ -270,121 +257,27 @@ } }, "node_modules/@babel/helpers": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.7.tgz", - "integrity": "sha512-Sv6pASx7Esm38KQpF/U/OXLwPPrdGHNKoeblRxgZRLXnAtnkEe4ptJPDtAZM7fBLadbc1Q07kQpSiGQ0Jg6tRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.25.7", - "@babel/types": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.25.7.tgz", - "integrity": "sha512-iYyACpW3iW8Fw+ZybQK+drQre+ns/tKpXbNESfrhNnPLIklLbXr7MYJ6gPEd0iETGLOK+SxMjVvKb/ffmk+FEw==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", + "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.25.7", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" + "@babel/template": "^7.25.9", + "@babel/types": "^7.26.0" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/@babel/parser": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.8.tgz", - "integrity": "sha512-HcttkxzdPucv3nNFmfOOMfFf64KgdJVqm1KaCm25dPGMLElo9nsLvXeJECQg8UzPuBGLyTSA0ZzqCtDSzKTEoQ==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", + "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.25.8" + "@babel/types": "^7.26.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -449,13 +342,13 @@ } }, "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.25.7.tgz", - "integrity": "sha512-AqVo+dguCgmpi/3mYBdu9lkngOBlQ2w2vnNpa6gfiCxQZLzV4ZbhsXitJ2Yblkoe1VQwtHSaNmIaGll/26YWRw==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz", + "integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -491,13 +384,13 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.7.tgz", - "integrity": "sha512-ruZOnKO+ajVL/MVx+PwNBPOkrnXTXoWMtte1MBpegfCArhqOe3Bj52avVj1huLLxNKYKXYaSxZ2F+woK1ekXfw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz", + "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -617,13 +510,13 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.7.tgz", - "integrity": "sha512-rR+5FDjpCHqqZN2bzZm18bVYGaejGq5ZkpVCJLXor/+zlSrSoc4KWcHI0URVWjl/68Dyr1uwZUz/1njycEAv9g==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz", + "integrity": "sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -633,32 +526,32 @@ } }, "node_modules/@babel/template": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.7.tgz", - "integrity": "sha512-wRwtAgI3bAS+JGU2upWNL9lSlDcRCqD05BZ1n3X2ONLH1WilFP6O1otQjeMK/1g0pvYcXC7b/qVUB1keofjtZA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", + "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.25.7", - "@babel/parser": "^7.25.7", - "@babel/types": "^7.25.7" + "@babel/code-frame": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.7.tgz", - "integrity": "sha512-jatJPT1Zjqvh/1FyJs6qAHL+Dzb7sTb+xr7Q+gM1b+1oBsMsQQ4FkVKb6dFlJvLlVssqkRzV05Jzervt9yhnzg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", + "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.25.7", - "@babel/generator": "^7.25.7", - "@babel/parser": "^7.25.7", - "@babel/template": "^7.25.7", - "@babel/types": "^7.25.7", + "@babel/code-frame": "^7.25.9", + "@babel/generator": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/template": "^7.25.9", + "@babel/types": "^7.25.9", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -685,15 +578,14 @@ } }, "node_modules/@babel/types": { - "version": "7.25.8", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.8.tgz", - "integrity": "sha512-JWtuCu8VQsMladxVz/P4HzHUGCAwpuqacmowgXFs5XjxIgKuNjnLokQzuVjlTvIzODaDmpjT3oxcC48vyk9EWg==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", + "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.25.7", - "@babel/helper-validator-identifier": "^7.25.7", - "to-fast-properties": "^2.0.0" + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -709,7 +601,8 @@ "node_modules/@data-forge/serialization": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@data-forge/serialization/-/serialization-1.0.1.tgz", - "integrity": "sha512-EP7IWimh5JcDOISVoXvNIjUAqcPN1FkNWvuvjY3uzcswErxB8j93ldlUBvgvGEszqFRwMM3fpMF0HrISg5iBSQ==" + "integrity": "sha512-EP7IWimh5JcDOISVoXvNIjUAqcPN1FkNWvuvjY3uzcswErxB8j93ldlUBvgvGEszqFRwMM3fpMF0HrISg5iBSQ==", + "license": "MIT" }, "node_modules/@hapi/code": { "version": "9.0.3", @@ -721,9 +614,9 @@ } }, "node_modules/@hapi/hoek": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-11.0.4.tgz", - "integrity": "sha512-PnsP5d4q7289pS2T2EgGz147BFJ2Jpb4yrEdkpz2IhgEUzos1S7HTl7ezWh1yfYzYlj89KzLdCRkqsP6SIryeQ==", + "version": "11.0.6", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-11.0.6.tgz", + "integrity": "sha512-mu8He+jghTDJ+la/uGBT4b1rqQdqFADZiXhzd98b3XW5nb/c+5woXx3FiNco2nm4wPJFHQVRGxYeWeSDPIYpYw==", "license": "BSD-3-Clause" }, "node_modules/@istanbuljs/load-nyc-config": { @@ -1292,12 +1185,12 @@ } }, "node_modules/@types/node": { - "version": "22.7.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz", - "integrity": "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==", + "version": "22.9.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz", + "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==", "license": "MIT", "dependencies": { - "undici-types": "~6.19.2" + "undici-types": "~6.19.8" } }, "node_modules/@types/stack-utils": { @@ -1346,6 +1239,7 @@ "version": "0.9.0", "resolved": "https://registry.npmjs.org/@zxing/text-encoding/-/text-encoding-0.9.0.tgz", "integrity": "sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA==", + "license": "(Unlicense OR Apache-2.0)", "optional": true }, "node_modules/abbrev": { @@ -1478,20 +1372,6 @@ "node": ">=10" } }, - "node_modules/are-we-there-yet/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/argon2": { "version": "0.40.3", "resolved": "https://registry.npmjs.org/argon2/-/argon2-0.40.3.tgz", @@ -1541,7 +1421,8 @@ "node_modules/async": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", - "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==" + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "license": "MIT" }, "node_modules/async-mutex": { "version": "0.4.1", @@ -1563,6 +1444,7 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "license": "MIT", "dependencies": { "possible-typed-array-names": "^1.0.0" }, @@ -1577,6 +1459,7 @@ "version": "1.7.7", "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", + "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", @@ -1801,27 +1684,45 @@ "safe-buffer": "^5.1.1" } }, + "node_modules/bl/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/bl/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/bl/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/block-stream2": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/block-stream2/-/block-stream2-2.1.0.tgz", "integrity": "sha512-suhjmLI57Ewpmq00qaygS8UgEq2ly2PCItenIyhMqVjo4t4pGzqMvfgJuX8iWTeSDdfSSqS6j38fL4ToNL7Pfg==", + "license": "MIT", "dependencies": { "readable-stream": "^3.4.0" } }, - "node_modules/block-stream2/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==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/bluebird": { "version": "3.5.1", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", @@ -1876,7 +1777,8 @@ "node_modules/browser-or-node": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/browser-or-node/-/browser-or-node-2.1.1.tgz", - "integrity": "sha512-8CVjaLJGuSKMVTxJ2DpBl5XnlNDiT4cQFeuCJJrvJmts9YrTZDizTX7PjC2s6W4x+MBGZeEY6dGMrF04/6Hgqg==" + "integrity": "sha512-8CVjaLJGuSKMVTxJ2DpBl5XnlNDiT4cQFeuCJJrvJmts9YrTZDizTX7PjC2s6W4x+MBGZeEY6dGMrF04/6Hgqg==", + "license": "MIT" }, "node_modules/browser-stdout": { "version": "1.3.1", @@ -1885,9 +1787,9 @@ "license": "ISC" }, "node_modules/browserslist": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.0.tgz", - "integrity": "sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==", + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", + "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", "dev": true, "funding": [ { @@ -1905,10 +1807,10 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001663", - "electron-to-chromium": "^1.5.28", + "caniuse-lite": "^1.0.30001669", + "electron-to-chromium": "^1.5.41", "node-releases": "^2.0.18", - "update-browserslist-db": "^1.1.0" + "update-browserslist-db": "^1.1.1" }, "bin": { "browserslist": "cli.js" @@ -1937,13 +1839,12 @@ } }, "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==", - "dev": true, + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz", + "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==", "license": "MIT", "engines": { - "node": "*" + "node": ">=8.0.0" } }, "node_modules/buffer-equal-constant-time": { @@ -2008,9 +1909,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001668", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001668.tgz", - "integrity": "sha512-nWLrdxqCdblixUO+27JtGJJE/txpJlyUy5YN1u53wLZkP0emYCo5zgS6QYft7VUYR42LGgi/S5hdLZTrnyIddw==", + "version": "1.0.30001679", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001679.tgz", + "integrity": "sha512-j2YqID/YwpLnKzCmBOS4tlZdWprXm3ZmQLBH9ZBXFOhoxLA46fwyBvx6toCBWBmnuwUY/qB3kEU6gFx8qgCroA==", "dev": true, "funding": [ { @@ -2185,6 +2086,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "license": "MIT", "optional": true, "engines": { "node": ">=0.8" @@ -2399,9 +2301,9 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.5.tgz", + "integrity": "sha512-ZVJrKKYunU38/76t0RMOulHOnUcbU9GbpWKAOZ0mhjr7CX6FVrH+4FrAapSOekrgFQ3f/8gwMEuIft0aKq6Hug==", "dev": true, "license": "MIT", "dependencies": { @@ -2418,6 +2320,7 @@ "resolved": "https://registry.npmjs.org/data-forge/-/data-forge-1.10.2.tgz", "integrity": "sha512-VZv8NV5laRC+VXGkA6cccl5Hwkkbt1sUuphVPcICLMMhiRWzVcVEowfcaplhb/kzmDsuohpruzHZp3y5w9OnuA==", "hasInstallScript": true, + "license": "MIT", "dependencies": { "@data-forge/serialization": "^1.0.0", "dayjs": "^1.8.12", @@ -2432,6 +2335,7 @@ "version": "0.0.9", "resolved": "https://registry.npmjs.org/data-forge-fs/-/data-forge-fs-0.0.9.tgz", "integrity": "sha512-7VzK9DbvYqJk/AlQckIh4OMX0iNcvY4Za8vWXh6LT+TGekAs791XUjB2OsEDlRcmRr3DdQ7aecVBRZ+wevjhnA==", + "license": "MIT", "dependencies": { "chai": "^4.1.2" }, @@ -2442,7 +2346,8 @@ "node_modules/dayjs": { "version": "1.11.13", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", - "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==" + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", + "license": "MIT" }, "node_modules/debug": { "version": "2.6.9", @@ -2475,6 +2380,7 @@ "version": "0.2.2", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "license": "MIT", "engines": { "node": ">=0.10" } @@ -2520,6 +2426,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "license": "MIT", "optional": true, "dependencies": { "clone": "^1.0.2" @@ -2652,6 +2559,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/easy-table/-/easy-table-1.1.0.tgz", "integrity": "sha512-oq33hWOSSnl2Hoh00tZWaIPi1ievrD9aFG82/IgjlycAnW9hHx5PkJiXpxPsgEE+H7BsbVQXFVFST8TEXS6/pA==", + "license": "MIT", "optionalDependencies": { "wcwidth": ">=1.0.1" } @@ -2672,9 +2580,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.38", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.38.tgz", - "integrity": "sha512-VbeVexmZ1IFh+5EfrYz1I0HTzHVIlJa112UEWhciPyeOcKJGeTv6N8WnG4wsQB81DGCaVEGhpSb6o6a8WYFXXg==", + "version": "1.5.55", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.55.tgz", + "integrity": "sha512-6maZ2ASDOTBtjt9FhqYPRnbvKU5tjG0IN9SztUOWYw2AzNDNpKJYLJmlK0/En4Hs/aiWnB+JZ+gW19PIGszgKg==", "dev": true, "license": "ISC" }, @@ -2790,7 +2698,8 @@ "node_modules/eventemitter3": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "license": "MIT" }, "node_modules/execa": { "version": "5.1.1", @@ -2930,6 +2839,7 @@ "url": "https://paypal.me/naturalintelligence" } ], + "license": "MIT", "dependencies": { "strnum": "^1.0.5" }, @@ -2963,6 +2873,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -3050,6 +2961,7 @@ "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "license": "MIT", "dependencies": { "is-callable": "^1.1.3" } @@ -3069,13 +2981,13 @@ } }, "node_modules/formidable": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.1.tgz", - "integrity": "sha512-WJWKelbRHN41m5dumb0/k8TeAx7Id/y3a+Z7QfhxP/htI9Js5zYaEDtG8uMgG0vM0lOlqnmjE99/kfpOYi/0Og==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.2.tgz", + "integrity": "sha512-Jqc1btCy3QzRbJaICGwKcBfGWuLADRerLzDqi2NwSt/UkXLsHJw2TVResiaoBufHVHy9aSgClOHCeJsSsFLTbg==", "license": "MIT", "dependencies": { "dezalgo": "^1.0.4", - "hexoid": "^1.0.0", + "hexoid": "^2.0.0", "once": "^1.4.0" }, "funding": { @@ -3503,6 +3415,7 @@ "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" }, @@ -3551,9 +3464,9 @@ } }, "node_modules/hexoid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", - "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-2.0.0.tgz", + "integrity": "sha512-qlspKUK7IlSQv2o+5I7yhUd7TxlOG2Vr5LTa3ve2XSNVKAL/n/u/7KLvKmFNimomDIKvZFXWHv0T12mv7rT8Aw==", "license": "MIT", "engines": { "node": ">=8" @@ -3722,18 +3635,19 @@ } }, "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==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", + "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", "license": "MIT", "engines": { - "node": ">= 0.10" + "node": ">= 10" } }, "node_modules/is-arguments": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "license": "MIT", "dependencies": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -3768,6 +3682,7 @@ "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -3823,6 +3738,7 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "license": "MIT", "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -3907,6 +3823,7 @@ "version": "1.1.13", "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", + "license": "MIT", "dependencies": { "which-typed-array": "^1.1.14" }, @@ -5263,16 +5180,17 @@ "license": "MIT" }, "node_modules/minio": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/minio/-/minio-8.0.1.tgz", - "integrity": "sha512-FzDO6yGnqLtm8sp3mXafWtiRUOslJSSg/aI0v9YbN5vjw5KLoODKAROCyi766NIvTSxcfHBrbhCSGk1A+MOzDg==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/minio/-/minio-8.0.2.tgz", + "integrity": "sha512-7ipWbtgzzboctf+McK+2cXwCrNOhuboTA/O1g9iWa0gH8R4GkeyFWwk12aVDEHdzjPiG8wxnjwfHS7pgraKuHw==", + "license": "Apache-2.0", "dependencies": { "async": "^3.2.4", "block-stream2": "^2.1.0", "browser-or-node": "^2.1.1", "buffer-crc32": "^1.0.0", "eventemitter3": "^5.0.1", - "fast-xml-parser": "^4.2.2", + "fast-xml-parser": "^4.4.1", "ipaddr.js": "^2.0.1", "lodash": "^4.17.21", "mime-types": "^2.1.35", @@ -5280,28 +5198,12 @@ "stream-json": "^1.8.0", "through2": "^4.0.2", "web-encoding": "^1.1.5", - "xml2js": "^0.5.0" + "xml2js": "^0.5.0 || ^0.6.2" }, "engines": { "node": "^16 || ^18 || >=20" } }, - "node_modules/minio/node_modules/buffer-crc32": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz", - "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/minio/node_modules/ipaddr.js": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", - "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", - "engines": { - "node": ">= 10" - } - }, "node_modules/minipass": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", @@ -5358,9 +5260,9 @@ } }, "node_modules/mocha": { - "version": "10.7.3", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.7.3.tgz", - "integrity": "sha512-uQWxAu44wwiACGqjbPYmjo7Lg8sFrS3dQe7PP2FQI+woptP4vZXSMcfMyFL/e1yFEeEpV4RtyTpZROOKmxis+A==", + "version": "10.8.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz", + "integrity": "sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==", "license": "MIT", "dependencies": { "ansi-colors": "^4.1.3", @@ -5646,14 +5548,14 @@ } }, "node_modules/mongodb-memory-server": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/mongodb-memory-server/-/mongodb-memory-server-9.4.1.tgz", - "integrity": "sha512-qONlW4sKPbtk9pqFnlPn7R73G3Q4TuebJJ5pHfoiKTqVJquojQ8xWmkCyz+/YnpA2vYBo/jib+nXvjfKwh7cjg==", + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/mongodb-memory-server/-/mongodb-memory-server-9.5.0.tgz", + "integrity": "sha512-In3zRT40cLlVtpy7FK6b96Lby6JBAdXj8Kf9YrH4p1Aa2X4ptojq7SmiRR3x47Lo0/UCXXIwhJpkdbYY8kRZAw==", "dev": true, "hasInstallScript": true, "license": "MIT", "dependencies": { - "mongodb-memory-server-core": "9.4.1", + "mongodb-memory-server-core": "9.5.0", "tslib": "^2.6.3" }, "engines": { @@ -5661,21 +5563,21 @@ } }, "node_modules/mongodb-memory-server-core": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/mongodb-memory-server-core/-/mongodb-memory-server-core-9.4.1.tgz", - "integrity": "sha512-lobapXaysH64zrn521NTkmqHc3krSPUFkuuZ8A/BmQV8ON7p2SzAEvpoJPDXIeJkxIzYw06dYL6Gn5OcZdEElA==", + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/mongodb-memory-server-core/-/mongodb-memory-server-core-9.5.0.tgz", + "integrity": "sha512-Jb/V80JeYAKWaF4bPFme7SmTR6ew1PWgkpPUepLDfRraeN49i1cruxICeA4zz4T33W/o31N+zazP8wI8ebf7yw==", "dev": true, "license": "MIT", "dependencies": { "async-mutex": "^0.4.1", "camelcase": "^6.3.0", - "debug": "^4.3.5", + "debug": "^4.3.7", "find-cache-dir": "^3.3.2", - "follow-redirects": "^1.15.6", - "https-proxy-agent": "^7.0.4", + "follow-redirects": "^1.15.9", + "https-proxy-agent": "^7.0.5", "mongodb": "^5.9.2", "new-find-package-json": "^2.0.0", - "semver": "^7.6.2", + "semver": "^7.6.3", "tar-stream": "^3.1.7", "tslib": "^2.6.3", "yauzl": "^3.1.3" @@ -5963,9 +5865,9 @@ } }, "node_modules/nise/node_modules/@sinonjs/fake-timers": { - "version": "13.0.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.2.tgz", - "integrity": "sha512-4Bb+oqXZTSTZ1q27Izly9lv8B9dlV61CROxPiVtywwzv5SnytJqhvYe6FclHYuXml4cd1VHPo1zd5PmTeJozvA==", + "version": "13.0.5", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz", + "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==", "license": "BSD-3-Clause", "dependencies": { "@sinonjs/commons": "^3.0.1" @@ -5981,9 +5883,9 @@ } }, "node_modules/node-addon-api": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.2.1.tgz", - "integrity": "sha512-vmEOvxwiH8tlOcv4SyE8RH34rI5/nWVaigUeAUPawC6f0+HoDthwI0vkMu4tbtsZrXq6QXFfrkhjofzKEs5tpA==", + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.2.2.tgz", + "integrity": "sha512-9emqXAKhVoNrQ792nLI/wpzPpJ/bj/YXxW0CvAau1+RdGBcCRF1Dmz7719zgVsQNrzHl9Tzn3ImZ4qWFarWL0A==", "license": "MIT", "engines": { "node": "^18 || ^20 || >= 21" @@ -6130,6 +6032,7 @@ "version": "2.0.6", "resolved": "https://registry.npmjs.org/numeral/-/numeral-2.0.6.tgz", "integrity": "sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA==", + "license": "MIT", "engines": { "node": "*" } @@ -6258,7 +6161,8 @@ "node_modules/papaparse": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.2.0.tgz", - "integrity": "sha512-ylq1wgUSnagU+MKQtNeVqrPhZuMYBvOSL00DHycFTCxownF95gpLAk1HiHdUW77N8yxRq1qHXLdlIPyBSG9NSA==" + "integrity": "sha512-ylq1wgUSnagU+MKQtNeVqrPhZuMYBvOSL00DHycFTCxownF95gpLAk1HiHdUW77N8yxRq1qHXLdlIPyBSG9NSA==", + "license": "MIT" }, "node_modules/parse-json": { "version": "5.2.0", @@ -6346,9 +6250,10 @@ "license": "MIT" }, "node_modules/pg": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.13.0.tgz", - "integrity": "sha512-34wkUTh3SxTClfoHB3pQ7bIMvw9dpFU1audQQeZG837fmHfHpr14n/AELVDoOYVDW2h5RDWU78tFjkD+erSBsw==", + "version": "8.13.1", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.13.1.tgz", + "integrity": "sha512-OUir1A0rPNZlX//c7ksiu7crsGZTKSOXJPgtNiHGIlC9H0lO+NC6ZDYksSgBYY/thSWhnSRBv8w1lieNNGATNQ==", + "license": "MIT", "dependencies": { "pg-connection-string": "^2.7.0", "pg-pool": "^3.7.0", @@ -6375,17 +6280,20 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz", "integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==", + "license": "MIT", "optional": true }, "node_modules/pg-connection-string": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.7.0.tgz", - "integrity": "sha512-PI2W9mv53rXJQEOb8xNR8lH7Hr+EKa6oJa38zsK0S/ky2er16ios1wLKhZyxzD7jUReiWokc9WK5nxSnC7W1TA==" + "integrity": "sha512-PI2W9mv53rXJQEOb8xNR8lH7Hr+EKa6oJa38zsK0S/ky2er16ios1wLKhZyxzD7jUReiWokc9WK5nxSnC7W1TA==", + "license": "MIT" }, "node_modules/pg-int8": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "license": "ISC", "engines": { "node": ">=4.0.0" } @@ -6394,6 +6302,7 @@ "version": "3.7.0", "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.7.0.tgz", "integrity": "sha512-ZOBQForurqh4zZWjrgSwwAtzJ7QiRX0ovFkZr2klsen3Nm0aoh33Ls0fzfv3imeH/nw/O27cjdz5kzYJfeGp/g==", + "license": "MIT", "peerDependencies": { "pg": ">=8.0" } @@ -6401,12 +6310,14 @@ "node_modules/pg-protocol": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.7.0.tgz", - "integrity": "sha512-hTK/mE36i8fDDhgDFjy6xNOG+LCorxLG3WO17tku+ij6sVHXh1jQUJ8hYAnRhNla4QVD2H8er/FOjc/+EgC6yQ==" + "integrity": "sha512-hTK/mE36i8fDDhgDFjy6xNOG+LCorxLG3WO17tku+ij6sVHXh1jQUJ8hYAnRhNla4QVD2H8er/FOjc/+EgC6yQ==", + "license": "MIT" }, "node_modules/pg-types": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "license": "MIT", "dependencies": { "pg-int8": "1.0.1", "postgres-array": "~2.0.0", @@ -6422,14 +6333,15 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "license": "MIT", "dependencies": { "split2": "^4.1.0" } }, "node_modules/picocolors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", - "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true, "license": "ISC" }, @@ -6472,6 +6384,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "license": "MIT", "engines": { "node": ">= 0.4" } @@ -6480,6 +6393,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "license": "MIT", "engines": { "node": ">=4" } @@ -6488,6 +6402,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -6496,6 +6411,7 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -6504,6 +6420,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "license": "MIT", "dependencies": { "xtend": "^4.0.0" }, @@ -6572,10 +6489,20 @@ "node": ">= 0.10" } }, + "node_modules/proxy-addr/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/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==" + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" }, "node_modules/punycode": { "version": "2.3.1", @@ -6623,6 +6550,7 @@ "version": "7.1.3", "resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz", "integrity": "sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==", + "license": "MIT", "dependencies": { "decode-uri-component": "^0.2.2", "filter-obj": "^1.1.0", @@ -6684,26 +6612,19 @@ "license": "MIT" }, "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==", + "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": { - "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" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" } }, - "node_modules/readable-stream/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/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -6849,7 +6770,8 @@ "node_modules/sax": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", - "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==" + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", + "license": "ISC" }, "node_modules/semver": { "version": "7.6.3", @@ -7129,6 +7051,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", + "license": "MIT", "engines": { "node": ">=6" } @@ -7137,6 +7060,7 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", "engines": { "node": ">= 10.x" } @@ -7183,12 +7107,14 @@ "node_modules/stream-chain": { "version": "2.2.5", "resolved": "https://registry.npmjs.org/stream-chain/-/stream-chain-2.2.5.tgz", - "integrity": "sha512-1TJmBx6aSWqZ4tx7aTpBDXK0/e2hhcNSTV8+CbFJtDjbb+I1mZ8lHit0Grw9GRT+6JbIrrDd8esncgBi8aBXGA==" + "integrity": "sha512-1TJmBx6aSWqZ4tx7aTpBDXK0/e2hhcNSTV8+CbFJtDjbb+I1mZ8lHit0Grw9GRT+6JbIrrDd8esncgBi8aBXGA==", + "license": "BSD-3-Clause" }, "node_modules/stream-json": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/stream-json/-/stream-json-1.8.0.tgz", - "integrity": "sha512-HZfXngYHUAr1exT4fxlbc1IOce1RYxp2ldeaf97LYCOPSoOqY/1Psp7iGvpb+6JIOgkra9zDYnPX01hGAHzEPw==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/stream-json/-/stream-json-1.9.0.tgz", + "integrity": "sha512-TqnfW7hRTKje7UobBzXZJ2qOEDJvdcSVgVIK/fopC03xINFuFqQs8RVjyDT4ry7TmOo2ueAXwpXXXG4tNgtvoQ==", + "license": "BSD-3-Clause", "dependencies": { "stream-chain": "^2.2.5" } @@ -7212,25 +7138,20 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==", + "license": "MIT", "engines": { "node": ">=4" } }, "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==", + "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.1.0" + "safe-buffer": "~5.2.0" } }, - "node_modules/string_decoder/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/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -7306,7 +7227,8 @@ "node_modules/strnum": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", - "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==" + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", + "license": "MIT" }, "node_modules/super-regex": { "version": "0.2.0", @@ -7422,6 +7344,16 @@ "url": "https://ko-fi.com/tunnckoCore/commissions" } }, + "node_modules/supertest/node_modules/hexoid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", + "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/supertest/node_modules/mime": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", @@ -7570,36 +7502,21 @@ } }, "node_modules/text-decoder": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.0.tgz", - "integrity": "sha512-n1yg1mOj9DNpk3NeZOx7T6jchTbyJS3i3cucbNN6FcdPriMZx7NsgrGpWWdWZZGxD7ES1XB+3uoqHMgOKaN+fg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.1.tgz", + "integrity": "sha512-x9v3H/lTKIJKQQe7RPQkLfKAnc9lUTkWDypIQgTzPJAq+5/GCDHonmshfvlsNSj58yyshbIJJDLmU15qNERrXQ==", "dev": true, - "license": "Apache-2.0", - "dependencies": { - "b4a": "^1.6.4" - } + "license": "Apache-2.0" }, "node_modules/through2": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "license": "MIT", "dependencies": { "readable-stream": "3" } }, - "node_modules/through2/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==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/time-span": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/time-span/-/time-span-5.1.0.tgz", @@ -7622,16 +7539,6 @@ "dev": true, "license": "BSD-3-Clause" }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/to-iso-string": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/to-iso-string/-/to-iso-string-0.0.2.tgz", @@ -7668,9 +7575,9 @@ "license": "MIT" }, "node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "dev": true, "license": "0BSD" }, @@ -7712,7 +7619,8 @@ "node_modules/typy": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/typy/-/typy-3.3.0.tgz", - "integrity": "sha512-Du53deMF9X9pSM3gVXDjLBq14BUfZWSGKfmmR1kTlg953RaIZehfc8fQuoAiW+SRO6bJsP+59mv1tsH8vwKghg==" + "integrity": "sha512-Du53deMF9X9pSM3gVXDjLBq14BUfZWSGKfmmR1kTlg953RaIZehfc8fQuoAiW+SRO6bJsP+59mv1tsH8vwKghg==", + "license": "MIT" }, "node_modules/undici-types": { "version": "6.19.8", @@ -7764,6 +7672,7 @@ "version": "0.12.5", "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "license": "MIT", "dependencies": { "inherits": "^2.0.3", "is-arguments": "^1.0.4", @@ -7838,6 +7747,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "license": "MIT", "optional": true, "dependencies": { "defaults": "^1.0.3" @@ -7847,6 +7757,7 @@ "version": "1.1.5", "resolved": "https://registry.npmjs.org/web-encoding/-/web-encoding-1.1.5.tgz", "integrity": "sha512-HYLeVCdJ0+lBYV2FvNZmv3HJ2Nt0QYXqZojk3d9FJOLkwnuhzM9tmamh8d7HPM8QqjKH8DeHkFTx+CFlWpZZDA==", + "license": "MIT", "dependencies": { "util": "^0.12.3" }, @@ -7897,6 +7808,7 @@ "version": "1.1.15", "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", + "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.7", @@ -7964,9 +7876,10 @@ } }, "node_modules/xml2js": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", - "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", + "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", + "license": "MIT", "dependencies": { "sax": ">=0.6.0", "xmlbuilder": "~11.0.0" @@ -7979,6 +7892,7 @@ "version": "11.0.1", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "license": "MIT", "engines": { "node": ">=4.0" } @@ -7987,6 +7901,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", "engines": { "node": ">=0.4" } @@ -8073,9 +7988,9 @@ } }, "node_modules/yauzl": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-3.1.3.tgz", - "integrity": "sha512-JCCdmlJJWv7L0q/KylOekyRaUrdEoUxWkWVcgorosTROCFWiS9p2NNPE9Yb91ak7b1N5SxAZEliWpspbZccivw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-3.2.0.tgz", + "integrity": "sha512-Ow9nuGZE+qp1u4JIPvg+uCiUr7xGQWdff7JQSk5VGYTAZMDe2q8lxJ10ygv10qmSj031Ty/6FNJpLO4o1Sgc+w==", "dev": true, "license": "MIT", "dependencies": { @@ -8086,6 +8001,16 @@ "node": ">=12" } }, + "node_modules/yauzl/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==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 69f4907..59daf59 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,18 @@ { "name": "opencap", "version": "1.0.0", - "description": "Your project description here", + "description": "OpenCap Financial Management System", "main": "app.js", "scripts": { - "test": "jest --config=jest.config.js" + "start": "node app.js", + "dev": "nodemon app.js", + "test": "jest --config=jest.config.js --detectOpenHandles --runInBand", + "test:watch": "jest --config=jest.config.js --watch", + "test:coverage": "jest --config=jest.config.js --coverage", + "test:integration": "jest --config=jest.config.js --detectOpenHandles --runInBand '__tests__/**/*.integration.test.js'", + "test:unit": "jest --config=jest.config.js '__tests__/**/*.unit.test.js'", + "lint": "eslint .", + "format": "prettier --write ." }, "dependencies": { "@hapi/code": "^9.0.3", @@ -34,10 +42,40 @@ "sinon": "^18.0.0" }, "devDependencies": { + "@babel/core": "^7.24.0", + "@babel/preset-env": "^7.24.0", + "@types/jest": "^29.5.11", + "babel-jest": "^29.7.0", "chai": "^4.2.0", + "eslint": "^8.56.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-jest": "^27.6.0", "hest": "^1.0.5", "jest": "^29.7.0", - "mongodb-memory-server": "^9.4.1", - "supertest": "^6.3.4" - } -} + "jest-environment-node": "^29.7.0", + "mongodb-memory-server": "^9.5.0", + "supertest": "^6.3.4", + "nodemon": "^3.0.2", + "prettier": "^3.1.1" + }, + "engines": { + "node": ">=14.0.0", + "npm": ">=6.0.0" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/yourusername/opencap.git" + }, + "keywords": [ + "financial", + "reporting", + "management", + "api" + ], + "author": "Your Name", + "license": "ISC", + "bugs": { + "url": "https://github.com/yourusername/opencap/issues" + }, + "homepage": "https://github.com/yourusername/opencap#readme" +} \ No newline at end of file diff --git a/utils/dataProcessing.js b/utils/dataProcessing.js index fba30de..f13bc87 100644 --- a/utils/dataProcessing.js +++ b/utils/dataProcessing.js @@ -16,4 +16,5 @@ const processDataset = (datasetPath) => { return processedData; }; + module.exports = { processDataset };