diff --git a/.gitignore b/.gitignore index 2972e8a..af12b6f 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,5 @@ node_modules/ .idea .vscode cloudformation.json +coverage/ +.nyc_output/ diff --git a/.nycrc.json b/.nycrc.json new file mode 100644 index 0000000..2ba337d --- /dev/null +++ b/.nycrc.json @@ -0,0 +1,20 @@ +{ + "all": true, + "include": [ + "lib/**/*.js", + "bots/**/*.js" + ], + "exclude": [ + "**/test/**", + "**/node_modules/**", + "cloudformation/**", + "manualTest/**", + "publish.js", + "leo_cli_config.js" + ], + "reporter": [ + "text", + "text-summary", + "html" + ] +} diff --git a/bots/archive/test/archive.test.js b/bots/archive/test/archive.test.js new file mode 100644 index 0000000..d10451f --- /dev/null +++ b/bots/archive/test/archive.test.js @@ -0,0 +1,206 @@ +'use strict'; + +const sinon = require('sinon'); +const { expect } = require('chai'); +const proxyquire = require('proxyquire').noCallThru(); + +describe("archive", () => { + let archiveHandler; + let dynamodbScanStub; + let readStub; + let pipeStub; + let counterStub; + let toS3GzipChunksStub; + let toLeoStub; + let devnullStub; + let logStub; + let infoStub; + let errorStub; + + const mockEventTable = 'test-event-table'; + + beforeEach(function () { + dynamodbScanStub = sinon.stub(); + readStub = sinon.stub(); + pipeStub = sinon.stub(); + counterStub = sinon.stub().returns({}); + toS3GzipChunksStub = sinon.stub().returns({}); + toLeoStub = sinon.stub().returns({}); + devnullStub = sinon.stub().returns({}); + logStub = sinon.stub(); + infoStub = sinon.stub(); + errorStub = sinon.stub(); + + const leoSdk = { + configuration: { + resources: { + LeoEvent: mockEventTable + } + }, + aws: { + dynamodb: { + scan: dynamodbScanStub + } + }, + streams: { + pipe: pipeStub, + counter: counterStub, + toS3GzipChunks: toS3GzipChunksStub, + toLeo: toLeoStub, + devnull: devnullStub + }, + read: readStub, + '@global': true + }; + + // Mock the cron wrapper to just pass through the handler + const cronWrapper = (handler) => handler; + cronWrapper['@global'] = true; + + const loggerMock = () => ({ + log: logStub, + info: infoStub, + error: errorStub + }); + loggerMock['@global'] = true; + + archiveHandler = proxyquire('../', { + 'leo-sdk': leoSdk, + 'leo-sdk/wrappers/cron': cronWrapper, + 'async': require('async'), + 'moment': require('moment'), + 'leo-logger': loggerMock + }).handler; + }); + + afterEach(function () { + sinon.restore(); + }); + + describe("handler", () => { + const mockContext = { + getRemainingTimeInMillis: () => 300000 // 5 minutes + }; + + it('should scan event table for queues to archive', (done) => { + dynamodbScanStub.callsFake((table, opts, callback) => { + expect(table).to.equal(mockEventTable); + callback(null, []); + }); + + archiveHandler({}, mockContext, (err) => { + expect(err).to.be.null; + expect(dynamodbScanStub.calledOnce).to.be.true; + done(); + }); + }); + + it('should skip queues with skip_archive flag', (done) => { + dynamodbScanStub.callsFake((table, opts, callback) => { + callback(null, [{ + event: 'test-queue', + skip_archive: true, + max_eid: 'z/2023/01/01/00/00/12345' + }]); + }); + + archiveHandler({}, mockContext, (err) => { + expect(err).to.be.null; + expect(pipeStub.called).to.be.false; + done(); + }); + }); + + it('should skip snapshot queues', (done) => { + dynamodbScanStub.callsFake((table, opts, callback) => { + callback(null, [{ + event: 'test-queue/_snapshot', + max_eid: 'z/2023/01/01/00/00/12345' + }]); + }); + + archiveHandler({}, mockContext, (err) => { + expect(err).to.be.null; + expect(pipeStub.called).to.be.false; + done(); + }); + }); + + it('should skip archive queues', (done) => { + dynamodbScanStub.callsFake((table, opts, callback) => { + callback(null, [{ + event: 'test-queue/_archive', + max_eid: 'z/2023/01/01/00/00/12345' + }]); + }); + + archiveHandler({}, mockContext, (err) => { + expect(err).to.be.null; + expect(pipeStub.called).to.be.false; + done(); + }); + }); + + it('should skip queues without max_eid', (done) => { + dynamodbScanStub.callsFake((table, opts, callback) => { + callback(null, [{ + event: 'test-queue' + }]); + }); + + archiveHandler({}, mockContext, (err) => { + expect(err).to.be.null; + expect(pipeStub.called).to.be.false; + done(); + }); + }); + + it('should archive queues that need archiving', (done) => { + dynamodbScanStub.callsFake((table, opts, callback) => { + callback(null, [{ + event: 'test-queue', + max_eid: 'z/2023/12/31/23/59/99999', + archive: { + end: 'z/2020/01/01/00/00/00000' + } + }]); + }); + + pipeStub.callsFake((...args) => { + const callback = args[args.length - 1]; + callback(null); + }); + + readStub.returns({}); + + archiveHandler({}, mockContext, (err) => { + expect(err).to.be.null; + expect(pipeStub.calledOnce).to.be.true; + done(); + }); + }); + + it('should handle pipe errors', (done) => { + dynamodbScanStub.callsFake((table, opts, callback) => { + callback(null, [{ + event: 'test-queue', + max_eid: 'z/2023/12/31/23/59/99999', + archive: { end: 'z/2020/01/01/00/00/00000' } + }]); + }); + + pipeStub.callsFake((...args) => { + const callback = args[args.length - 1]; + callback(new Error('Pipe error')); + }); + + readStub.returns({}); + + archiveHandler({}, mockContext, (err) => { + expect(err).to.be.instanceof(Error); + expect(err.message).to.equal('Pipe error'); + done(); + }); + }); + }); +}); diff --git a/bots/bus-api/test/bus-api.test.js b/bots/bus-api/test/bus-api.test.js new file mode 100644 index 0000000..bbeac9b --- /dev/null +++ b/bots/bus-api/test/bus-api.test.js @@ -0,0 +1,556 @@ +'use strict'; + +const sinon = require('sinon'); +const { expect } = require('chai'); +const proxyquire = require('proxyquire').noCallThru(); + +describe("bus-api", () => { + let busApi; + let botStartStub; + let botEndStub; + let botCheckpointStub; + let readStub; + let loadStub; + let pipeStub; + let writeStreamStub; + let throughStub; + + const mockContext = { + awsRequestId: 'test-request-id' + }; + + beforeEach(function () { + botStartStub = sinon.stub(); + botEndStub = sinon.stub(); + botCheckpointStub = sinon.stub(); + readStub = sinon.stub(); + loadStub = sinon.stub(); + pipeStub = sinon.stub(); + writeStreamStub = sinon.stub(); + throughStub = sinon.stub(); + + const leoSdk = { + configuration: { + resources: { + LeoKinesisStream: 'kinesis-stream', + LeoS3: 's3-bucket', + LeoFirehoseStream: 'firehose-stream' + }, + update: sinon.stub(), + registry: {} + }, + bot: { + start: botStartStub, + end: botEndStub, + checkpoint: botCheckpointStub + }, + read: readStub, + load: loadStub, + streams: { + pipe: pipeStub, + write: (fn) => throughStub + }, + '@global': true + }; + + busApi = proxyquire('../', { + 'leo-sdk': leoSdk, + 'async': require('async') + }); + }); + + afterEach(function () { + sinon.restore(); + }); + + describe("handler", () => { + it('should reject unsupported action types', (done) => { + const event = { type: 'unsupported' }; + + busApi.handler(event, mockContext, (err) => { + expect(err).to.equal("Unsupported action 'unsupported'."); + done(); + }); + }); + + it('should parse body if present', (done) => { + const event = { + body: { type: 'unsupported' } + }; + + busApi.handler(event, mockContext, (err) => { + expect(err).to.equal("Unsupported action 'unsupported'."); + done(); + }); + }); + }); + + describe("start handler", () => { + it('should call bot.start with correct parameters', (done) => { + botStartStub.callsFake((event, options, callback) => { + callback(null); + }); + + const event = { + type: 'start', + id: 'test-bot', + options: { lock: 'test-lock' } + }; + + busApi.handler(event, mockContext, (err, result) => { + expect(err).to.be.null; + expect(result.status).to.equal('success'); + expect(result.id).to.equal('test-bot'); + expect(result.token).to.have.property('requestId'); + expect(result.token).to.have.property('ts'); + expect(result.duration).to.be.a('number'); + done(); + }); + }); + + it('should return error status on bot.start failure', (done) => { + botStartStub.callsFake((event, options, callback) => { + callback(new Error('Start failed')); + }); + + const event = { + type: 'start', + id: 'test-bot' + }; + + busApi.handler(event, mockContext, (err, result) => { + expect(err).to.be.null; + expect(result.status).to.equal('error'); + expect(result.error).to.be.instanceof(Error); + done(); + }); + }); + + it('should handle missing options', (done) => { + botStartStub.callsFake((event, options, callback) => { + callback(null); + }); + + const event = { + type: 'start', + id: 'test-bot' + }; + + busApi.handler(event, mockContext, (err, result) => { + expect(err).to.be.null; + expect(result.status).to.equal('success'); + done(); + }); + }); + }); + + describe("end handler", () => { + it('should call bot.end with correct parameters', (done) => { + botEndStub.callsFake((status, options, callback) => { + callback(null); + }); + + const event = { + type: 'end', + id: 'test-bot', + status: 'success', + token: { + requestId: 'req-123', + ts: Date.now() + } + }; + + busApi.handler(event, mockContext, (err, result) => { + expect(err).to.be.null; + expect(result.status).to.equal('success'); + expect(result.id).to.equal('test-bot'); + done(); + }); + }); + + it('should handle checkpoint on end if provided', (done) => { + botCheckpointStub.callsFake((id, queue, params, callback) => { + callback(null); + }); + botEndStub.callsFake((status, options, callback) => { + callback(null); + }); + + const event = { + type: 'end', + id: 'test-bot', + status: 'success', + checkpoint: { + eid: 'z/2023/01/01/00/00/12345', + queue: 'test-queue' + }, + token: {} + }; + + busApi.handler(event, mockContext, (err, result) => { + expect(err).to.be.null; + expect(result.status).to.equal('success'); + done(); + }); + }); + + it('should return error status on bot.end failure', (done) => { + botEndStub.callsFake((status, options, callback) => { + callback(new Error('End failed')); + }); + + const event = { + type: 'end', + id: 'test-bot', + status: 'success', + token: {} + }; + + busApi.handler(event, mockContext, (err, result) => { + expect(err).to.be.null; + expect(result.status).to.equal('error'); + done(); + }); + }); + + it('should handle missing token gracefully', (done) => { + botEndStub.callsFake((status, options, callback) => { + callback(null); + }); + + const event = { + type: 'end', + id: 'test-bot', + status: 'success' + }; + + busApi.handler(event, mockContext, (err, result) => { + expect(err).to.be.null; + expect(result.status).to.equal('success'); + done(); + }); + }); + }); + + describe("read handler", () => { + it('should return error for missing id', (done) => { + const event = { + type: 'read', + queue: 'test-queue' + }; + + busApi.handler(event, mockContext, (err) => { + expect(err).to.equal("Invalid parameters. 'id' and 'queue' are required."); + done(); + }); + }); + + it('should return error for missing queue', (done) => { + const event = { + type: 'read', + id: 'test-bot' + }; + + busApi.handler(event, mockContext, (err) => { + expect(err).to.equal("Invalid parameters. 'id' and 'queue' are required."); + done(); + }); + }); + + it('should read events from queue successfully', (done) => { + const mockEvents = [{ payload: 'event1' }, { payload: 'event2' }]; + + pipeStub.callsFake((...args) => { + // Simulate writing events through the through stream + const callback = args[args.length - 1]; + callback(null); + }); + + readStub.returns({}); + + const event = { + type: 'read', + id: 'test-bot', + queue: 'test-queue', + options: {} + }; + + busApi.handler(event, mockContext, (err, result) => { + expect(err).to.be.null; + expect(result.status).to.equal('success'); + expect(result.id).to.equal('test-bot'); + expect(result.queue).to.equal('test-queue'); + expect(result.count).to.be.a('number'); + done(); + }); + }); + + it('should handle read errors', (done) => { + pipeStub.callsFake((...args) => { + const callback = args[args.length - 1]; + callback(new Error('Read error')); + }); + + readStub.returns({}); + + const event = { + type: 'read', + id: 'test-bot', + queue: 'test-queue' + }; + + busApi.handler(event, mockContext, (err, result) => { + expect(err).to.be.null; + expect(result.status).to.equal('error'); + done(); + }); + }); + }); + + describe("write handler", () => { + it('should return error for missing id', (done) => { + const event = { + type: 'write', + queue: 'test-queue', + events: [] + }; + + busApi.handler(event, mockContext, (err) => { + expect(err).to.equal("Invalid parameters. 'id' and 'queue' are required."); + done(); + }); + }); + + it('should return error for missing queue', (done) => { + const event = { + type: 'write', + id: 'test-bot', + events: [] + }; + + busApi.handler(event, mockContext, (err) => { + expect(err).to.equal("Invalid parameters. 'id' and 'queue' are required."); + done(); + }); + }); + + it('should write events to queue successfully', (done) => { + const mockStream = { + write: sinon.stub().returns(true), + end: sinon.stub().callsFake((cb) => cb(null)), + once: sinon.stub() + }; + loadStub.returns(mockStream); + + const event = { + type: 'write', + id: 'test-bot', + queue: 'test-queue', + events: [{ payload: 'test1' }, { payload: 'test2' }] + }; + + busApi.handler(event, mockContext, (err, result) => { + expect(err).to.be.null; + expect(result.status).to.equal('success'); + expect(result.count).to.equal(2); + done(); + }); + }); + + it('should handle single event (non-array)', (done) => { + const mockStream = { + write: sinon.stub().returns(true), + end: sinon.stub().callsFake((cb) => cb(null)), + once: sinon.stub() + }; + loadStub.returns(mockStream); + + const event = { + type: 'write', + id: 'test-bot', + queue: 'test-queue', + events: { payload: 'single-event' } + }; + + busApi.handler(event, mockContext, (err, result) => { + expect(err).to.be.null; + expect(result.status).to.equal('success'); + expect(result.count).to.equal(1); + done(); + }); + }); + + it('should handle backpressure (write returns false)', (done) => { + const mockStream = { + write: sinon.stub().returns(false), + end: sinon.stub().callsFake((cb) => cb(null)), + once: sinon.stub().callsFake((event, cb) => { + if (event === 'drain') { + setTimeout(cb, 10); + } + }) + }; + loadStub.returns(mockStream); + + const event = { + type: 'write', + id: 'test-bot', + queue: 'test-queue', + events: [{ payload: 'test' }] + }; + + busApi.handler(event, mockContext, (err, result) => { + expect(err).to.be.null; + expect(result.status).to.equal('success'); + done(); + }); + }); + + it('should handle write errors', (done) => { + const mockStream = { + write: sinon.stub().returns(true), + end: sinon.stub().callsFake((cb) => cb(new Error('Write error'))), + once: sinon.stub() + }; + loadStub.returns(mockStream); + + const event = { + type: 'write', + id: 'test-bot', + queue: 'test-queue', + events: [{ payload: 'test' }] + }; + + busApi.handler(event, mockContext, (err, result) => { + expect(err).to.be.null; + expect(result.status).to.equal('error'); + done(); + }); + }); + + it('should handle empty events array', (done) => { + const mockStream = { + write: sinon.stub().returns(true), + end: sinon.stub().callsFake((cb) => cb(null)), + once: sinon.stub() + }; + loadStub.returns(mockStream); + + const event = { + type: 'write', + id: 'test-bot', + queue: 'test-queue', + events: [] + }; + + busApi.handler(event, mockContext, (err, result) => { + expect(err).to.be.null; + expect(result.status).to.equal('success'); + expect(result.count).to.equal(0); + done(); + }); + }); + }); + + describe("checkpoint handler", () => { + it('should checkpoint successfully', (done) => { + botCheckpointStub.callsFake((id, queue, params, callback) => { + callback(null); + }); + + const event = { + type: 'checkpoint', + id: 'test-bot', + queue: 'test-queue', + eid: 'z/2023/01/01/00/00/12345', + units: 10 + }; + + busApi.handler(event, mockContext, (err, result) => { + expect(err).to.be.null; + expect(result.status).to.equal('success'); + expect(result.eid).to.equal('z/2023/01/01/00/00/12345'); + done(); + }); + }); + + it('should use event.eid if available', (done) => { + botCheckpointStub.callsFake((id, queue, params, callback) => { + expect(params.eid).to.equal('event-eid'); + callback(null); + }); + + const event = { + type: 'checkpoint', + id: 'test-bot', + queue: 'test-queue', + eid: 'original-eid', + event: { eid: 'event-eid' } + }; + + busApi.handler(event, mockContext, (err, result) => { + expect(err).to.be.null; + done(); + }); + }); + + it('should default units to 1 if not provided', (done) => { + botCheckpointStub.callsFake((id, queue, params, callback) => { + expect(params.units).to.equal(1); + callback(null); + }); + + const event = { + type: 'checkpoint', + id: 'test-bot', + queue: 'test-queue', + eid: 'test-eid' + }; + + busApi.handler(event, mockContext, (err, result) => { + expect(err).to.be.null; + done(); + }); + }); + + it('should use records as units if provided', (done) => { + botCheckpointStub.callsFake((id, queue, params, callback) => { + expect(params.units).to.equal(25); + callback(null); + }); + + const event = { + type: 'checkpoint', + id: 'test-bot', + queue: 'test-queue', + eid: 'test-eid', + records: 25 + }; + + busApi.handler(event, mockContext, (err, result) => { + expect(err).to.be.null; + done(); + }); + }); + + it('should handle checkpoint errors', (done) => { + botCheckpointStub.callsFake((id, queue, params, callback) => { + callback(new Error('Checkpoint failed')); + }); + + const event = { + type: 'checkpoint', + id: 'test-bot', + queue: 'test-queue', + eid: 'test-eid' + }; + + busApi.handler(event, mockContext, (err, result) => { + expect(err).to.be.null; + expect(result.status).to.equal('error'); + expect(result.eid).to.be.undefined; + done(); + }); + }); + }); +}); diff --git a/bots/cron-stream-trigger/test/cron-stream-trigger.test.js b/bots/cron-stream-trigger/test/cron-stream-trigger.test.js new file mode 100644 index 0000000..3419555 --- /dev/null +++ b/bots/cron-stream-trigger/test/cron-stream-trigger.test.js @@ -0,0 +1,312 @@ +'use strict'; + +const sinon = require('sinon'); +const { expect } = require('chai'); +const proxyquire = require('proxyquire').noCallThru(); + +describe("cron-stream-trigger", () => { + let cronStreamTrigger; + let dynamodbDocClientUpdateStub; + let dynamodbQueryStub; + let awsConverterStub; + + const mockCronTable = 'test-cron-table'; + + beforeEach(function () { + dynamodbDocClientUpdateStub = sinon.stub(); + dynamodbQueryStub = sinon.stub(); + + awsConverterStub = { + unmarshall: sinon.stub().callsFake((item) => item) + }; + + const leoSdk = { + configuration: { + resources: { + LeoCron: mockCronTable + } + }, + aws: { + dynamodb: { + docClient: { + update: dynamodbDocClientUpdateStub + }, + query: dynamodbQueryStub + } + }, + '@global': true + }; + + const refUtil = { + refId: sinon.stub().callsFake((id) => id), + '@global': true + }; + + const aws = { + DynamoDB: { + Converter: awsConverterStub + }, + '@global': true + }; + + const momentMock = function() { + return { + now: function() { return 1609459200000; } + }; + }; + momentMock.now = sinon.stub().returns(1609459200000); + + cronStreamTrigger = proxyquire('../', { + 'leo-sdk': leoSdk, + 'leo-sdk/lib/reference.js': refUtil, + 'aws-sdk': aws, + 'moment': momentMock, + 'async': require('async') + }); + }); + + afterEach(function () { + sinon.restore(); + }); + + describe("handler", () => { + it('should trigger bots when event data changes', (done) => { + // Setup cache lookup with proper promise chain + dynamodbQueryStub.returns( + Promise.resolve({ + Items: [{ + id: 'test-bot', + triggers: ['test-queue'], + archived: false + }] + }) + ); + + dynamodbDocClientUpdateStub.callsFake((params, callback) => { + callback(null); + }); + + const event = { + Records: [{ + dynamodb: { + NewImage: { + event: 'test-queue', + kinesis_number: '12345', + s3_kinesis_number: null, + initial_kinesis_number: null, + s3_new_kinesis_number: null, + eid: null, + max_eid: null + }, + OldImage: { + event: 'test-queue', + kinesis_number: '12340', + s3_kinesis_number: null, + initial_kinesis_number: null, + s3_new_kinesis_number: null, + eid: null, + max_eid: null + } + } + }] + }; + + cronStreamTrigger.handler(event, {}, (err, result) => { + expect(err).to.be.null; + expect(result).to.have.property('data'); + expect(result).to.have.property('time'); + done(); + }); + }); + + it('should not trigger if new and old max values are the same', (done) => { + dynamodbQueryStub.returns( + Promise.resolve({ + Items: [{ + id: 'test-bot', + triggers: ['test-queue'], + archived: false + }] + }) + ); + + const event = { + Records: [{ + dynamodb: { + NewImage: { + event: 'test-queue', + kinesis_number: '12345' + }, + OldImage: { + event: 'test-queue', + kinesis_number: '12345' + } + } + }] + }; + + cronStreamTrigger.handler(event, {}, (err, result) => { + expect(err).to.be.null; + expect(result.data).to.deep.equal({}); + done(); + }); + }); + + it('should handle records without NewImage', (done) => { + dynamodbQueryStub.returns( + Promise.resolve({ + Items: [] + }) + ); + + const event = { + Records: [{ + dynamodb: { + OldImage: { + event: 'test-queue' + } + } + }] + }; + + cronStreamTrigger.handler(event, {}, (err, result) => { + expect(err).to.be.null; + expect(result.data).to.deep.equal({}); + done(); + }); + }); + + it('should skip archived bots', (done) => { + dynamodbQueryStub.returns( + Promise.resolve({ + Items: [{ + id: 'archived-bot', + triggers: ['test-queue'], + archived: true + }, { + id: 'active-bot', + triggers: ['test-queue'], + archived: false + }] + }) + ); + + dynamodbDocClientUpdateStub.callsFake((params, callback) => { + callback(null); + }); + + const event = { + Records: [{ + dynamodb: { + NewImage: { + event: 'test-queue', + kinesis_number: '12345' + }, + OldImage: { + event: 'test-queue', + kinesis_number: '12340' + } + } + }] + }; + + cronStreamTrigger.handler(event, {}, (err, result) => { + expect(err).to.be.null; + done(); + }); + }); + + it('should skip bots without triggers', (done) => { + dynamodbQueryStub.returns( + Promise.resolve({ + Items: [{ + id: 'no-trigger-bot', + archived: false + }] + }) + ); + + const event = { + Records: [{ + dynamodb: { + NewImage: { + event: 'test-queue', + kinesis_number: '12345' + }, + OldImage: { + event: 'test-queue', + kinesis_number: '12340' + } + } + }] + }; + + cronStreamTrigger.handler(event, {}, (err, result) => { + expect(err).to.be.null; + done(); + }); + }); + + it('should handle DynamoDB update errors', (done) => { + dynamodbQueryStub.returns( + Promise.resolve({ + Items: [{ + id: 'test-bot', + triggers: ['test-queue'], + archived: false + }] + }) + ); + + dynamodbDocClientUpdateStub.callsFake((params, callback) => { + callback(new Error('DynamoDB error')); + }); + + const event = { + Records: [{ + dynamodb: { + NewImage: { + event: 'test-queue', + kinesis_number: '12345' + }, + OldImage: { + event: 'test-queue', + kinesis_number: '12340' + } + } + }] + }; + + cronStreamTrigger.handler(event, {}, (err) => { + expect(err).to.be.instanceof(Error); + done(); + }); + }); + + it('should handle cron table query errors', (done) => { + dynamodbQueryStub.returns( + Promise.reject(new Error('Query failed')) + ); + + const event = { + Records: [{ + dynamodb: { + NewImage: { + event: 'test-queue', + kinesis_number: '12345' + }, + OldImage: { + event: 'test-queue', + kinesis_number: '12340' + } + } + }] + }; + + cronStreamTrigger.handler(event, {}, (err) => { + expect(err).to.be.instanceof(Error); + done(); + }); + }); + }); +}); diff --git a/bots/firehose_processor/test/firehose_processor.test.js b/bots/firehose_processor/test/firehose_processor.test.js new file mode 100644 index 0000000..1e6bc08 --- /dev/null +++ b/bots/firehose_processor/test/firehose_processor.test.js @@ -0,0 +1,824 @@ +'use strict'; + +const sinon = require('sinon'); +const { expect } = require('chai'); +const proxyquire = require('proxyquire').noCallThru(); +const zlib = require('zlib'); +const { PassThrough, Transform, Writable, Readable } = require('stream'); + +describe("firehose_processor", () => { + let firehoseProcessor; + let fromLeoStub; + let fromS3Stub; + let toS3GzipChunksStub; + let toLeoStub; + let botCheckpointStub; + + beforeEach(function () { + fromLeoStub = sinon.stub(); + fromS3Stub = sinon.stub(); + toS3GzipChunksStub = sinon.stub(); + toLeoStub = sinon.stub(); + botCheckpointStub = sinon.stub(); + + // Create mock through stream + const createMockThrough = () => { + return new Transform({ + objectMode: true, + transform(chunk, encoding, callback) { + callback(null, chunk); + } + }); + }; + + // Create mock devnull + const createMockDevnull = () => { + return new Writable({ + objectMode: true, + write(chunk, encoding, callback) { + callback(); + } + }); + }; + + // Create mock split + const createMockSplit = () => { + return new Transform({ + objectMode: true, + transform(chunk, encoding, callback) { + const lines = chunk.toString().split('\n').filter(l => l.trim()); + lines.forEach(line => this.push(line)); + callback(); + } + }); + }; + + // Create mock parse + const createMockParse = () => { + return new Transform({ + objectMode: true, + transform(chunk, encoding, callback) { + try { + this.push(JSON.parse(chunk.toString())); + } catch (e) { + // Skip invalid JSON + } + callback(); + } + }); + }; + + toS3GzipChunksStub.callsFake(() => createMockThrough()); + toLeoStub.callsFake(() => createMockThrough()); + + const leoSdk = { + configuration: { + resources: { + LeoKinesisStream: 'mock-kinesis', + LeoS3: 'mock-s3-bucket', + LeoStream: 'mock-stream-table' + }, + update: sinon.stub() + }, + streams: { + fromLeo: fromLeoStub, + fromS3: fromS3Stub, + pipe: function(...args) { + const streams = args.slice(0, -1); + const callback = args[args.length - 1]; + + if (streams.length === 0) { + callback(); + return; + } + + // Pipe all streams together + let combined = streams[0]; + for (let i = 1; i < streams.length; i++) { + combined = combined.pipe(streams[i]); + } + + combined.on('finish', () => callback()); + combined.on('error', (err) => callback(err)); + }, + through: function(fn) { + return new Transform({ + objectMode: true, + transform(chunk, encoding, callback) { + fn(chunk, (err, result) => { + if (err) return callback(err); + if (result !== undefined) this.push(result); + callback(); + }); + } + }); + }, + parse: createMockParse, + split: createMockSplit, + toS3GzipChunks: toS3GzipChunksStub, + toLeo: toLeoStub, + pipeline: function(...streams) { + let combined = streams[0]; + for (let i = 1; i < streams.length; i++) { + combined = combined.pipe(streams[i]); + } + return combined; + }, + devnull: createMockDevnull + }, + bot: { + checkpoint: botCheckpointStub + }, + '@global': true + }; + + const refUtil = { + ref: sinon.stub().callsFake((id) => ({ + queue: () => ({ id: id.replace(/^queue:/, '') }) + })), + '@global': true + }; + + const cronWrapper = sinon.stub().callsFake((fn) => fn); + + firehoseProcessor = proxyquire('../', { + 'leo-sdk': leoSdk, + 'leo-sdk/lib/reference.js': refUtil, + 'leo-sdk/wrappers/cron': cronWrapper, + 'async': require('async') + }); + }); + + afterEach(function () { + sinon.restore(); + }); + + describe("handler", () => { + const createMockContext = (remainingTime = 60000) => ({ + getRemainingTimeInMillis: sinon.stub().returns(remainingTime) + }); + + it('should process firehose files from S3 and checkpoint', (done) => { + const mockEvent = { + botId: 'test-firehose-bot', + debug: false, + start: 'z/2021/01/01/00/00/000' + }; + + const mockContext = createMockContext(); + mockContext.getRemainingTimeInMillis.returns(5000); // Low time to exit loop immediately + + // Create mock source stream with no data + const mockSourceStream = new Readable({ + objectMode: true, + read() { + this.push(null); // End stream immediately + } + }); + fromLeoStub.returns(mockSourceStream); + + botCheckpointStub.callsFake((botId, source, data, cb) => { + cb(null); + }); + + firehoseProcessor.handler(mockEvent, mockContext, (err, result) => { + expect(err).to.be.null; + done(); + }); + }); + + it('should process events from S3 files', (done) => { + const mockEvent = { + botId: 'test-firehose-bot', + debug: false + }; + const mockContext = createMockContext(); + mockContext.getRemainingTimeInMillis.returns(5000); + + // Create mock source stream with a single object containing files + const mockPayload = { + eid: 'z/2021/01/01/00/00/001', + units: 1, + timestamp: Date.now(), + source_timestamp: Date.now(), + payload: { + files: ['s3://bucket/file1.gz'] + } + }; + + const mockSourceStream = new Readable({ + objectMode: true, + read() { + this.push(mockPayload); + this.push(null); + } + }); + fromLeoStub.returns(mockSourceStream); + + // Create mock S3 file stream with gzipped data + const testData = JSON.stringify({ id: 'test-bot', event: 'test-queue', payload: { data: 'test' } }); + const gzippedData = zlib.gzipSync(testData).toString('base64'); + + const mockS3Stream = new Readable({ + read() { + this.push(gzippedData); + this.push(null); + } + }); + fromS3Stub.returns(mockS3Stream); + + botCheckpointStub.callsFake((botId, source, data, cb) => { + cb(null); + }); + + firehoseProcessor.handler(mockEvent, mockContext, (err) => { + expect(fromLeoStub.called).to.be.true; + done(); + }); + }); + + it('should handle different data encodings - gzipped (H prefix)', (done) => { + const mockEvent = { + botId: 'test-firehose-bot', + debug: false + }; + const mockContext = createMockContext(); + mockContext.getRemainingTimeInMillis.returns(5000); + + // Create mock source stream + const mockPayload = { + eid: 'z/2021/01/01/00/00/001', + units: 1, + timestamp: Date.now(), + source_timestamp: Date.now(), + payload: { + files: ['s3://bucket/file1.gz'] + } + }; + + const mockSourceStream = new Readable({ + objectMode: true, + read() { + this.push(mockPayload); + this.push(null); + } + }); + fromLeoStub.returns(mockSourceStream); + + // Gzipped data starts with H + const testData = JSON.stringify({ id: 'test-bot', event: 'test-queue', payload: {} }); + const gzippedData = zlib.gzipSync(testData).toString('base64'); + expect(gzippedData[0]).to.equal('H'); + + const mockS3Stream = new Readable({ + read() { + this.push(gzippedData); + this.push(null); + } + }); + fromS3Stub.returns(mockS3Stream); + + botCheckpointStub.callsFake((botId, source, data, cb) => cb(null)); + + firehoseProcessor.handler(mockEvent, mockContext, (err) => { + done(); + }); + }); + + it('should handle different data encodings - inflated (eJ prefix)', (done) => { + const mockEvent = { + botId: 'test-firehose-bot', + debug: false + }; + const mockContext = createMockContext(); + mockContext.getRemainingTimeInMillis.returns(5000); + + const mockPayload = { + eid: 'z/2021/01/01/00/00/001', + units: 1, + timestamp: Date.now(), + source_timestamp: Date.now(), + payload: { + files: ['s3://bucket/file1.gz'] + } + }; + + const mockSourceStream = new Readable({ + objectMode: true, + read() { + this.push(mockPayload); + this.push(null); + } + }); + fromLeoStub.returns(mockSourceStream); + + // Deflated data typically starts with eJ + const testData = JSON.stringify({ id: 'test-bot', event: 'test-queue', payload: {} }); + const deflatedData = zlib.deflateSync(testData).toString('base64'); + + const mockS3Stream = new Readable({ + read() { + this.push(deflatedData); + this.push(null); + } + }); + fromS3Stub.returns(mockS3Stream); + + botCheckpointStub.callsFake((botId, source, data, cb) => cb(null)); + + firehoseProcessor.handler(mockEvent, mockContext, (err) => { + done(); + }); + }); + + it('should handle different data encodings - base64 JSON (ey prefix)', (done) => { + const mockEvent = { + botId: 'test-firehose-bot', + debug: false + }; + const mockContext = createMockContext(); + mockContext.getRemainingTimeInMillis.returns(5000); + + const mockPayload = { + eid: 'z/2021/01/01/00/00/001', + units: 1, + timestamp: Date.now(), + source_timestamp: Date.now(), + payload: { + files: ['s3://bucket/file1.gz'] + } + }; + + const mockSourceStream = new Readable({ + objectMode: true, + read() { + this.push(mockPayload); + this.push(null); + } + }); + fromLeoStub.returns(mockSourceStream); + + // Base64 JSON starts with ey + const testData = JSON.stringify({ id: 'test-bot', event: 'test-queue', payload: {} }); + const base64Data = Buffer.from(testData).toString('base64'); + expect(base64Data.substring(0, 2)).to.equal('ey'); + + const mockS3Stream = new Readable({ + read() { + this.push(base64Data); + this.push(null); + } + }); + fromS3Stub.returns(mockS3Stream); + + botCheckpointStub.callsFake((botId, source, data, cb) => cb(null)); + + firehoseProcessor.handler(mockEvent, mockContext, (err) => { + done(); + }); + }); + + it('should handle plain data (no special prefix)', (done) => { + const mockEvent = { + botId: 'test-firehose-bot', + debug: false + }; + const mockContext = createMockContext(); + mockContext.getRemainingTimeInMillis.returns(5000); + + const mockPayload = { + eid: 'z/2021/01/01/00/00/001', + units: 1, + timestamp: Date.now(), + source_timestamp: Date.now(), + payload: { + files: ['s3://bucket/file1.gz'] + } + }; + + const mockSourceStream = new Readable({ + objectMode: true, + read() { + this.push(mockPayload); + this.push(null); + } + }); + fromLeoStub.returns(mockSourceStream); + + // Plain data not starting with H, eJ, or ey + const plainData = 'plain-data-not-json'; + + const mockS3Stream = new Readable({ + read() { + this.push(plainData); + this.push(null); + } + }); + fromS3Stub.returns(mockS3Stream); + + botCheckpointStub.callsFake((botId, source, data, cb) => cb(null)); + + firehoseProcessor.handler(mockEvent, mockContext, (err) => { + done(); + }); + }); + + it('should handle processing errors', (done) => { + const mockEvent = { + botId: 'test-firehose-bot', + debug: true + }; + const mockContext = createMockContext(); + mockContext.getRemainingTimeInMillis.returns(60000); + + // Create a source stream that fails via the pipe callback + const mockSourceStream = new PassThrough({ objectMode: true }); + fromLeoStub.returns(mockSourceStream); + + // Override the pipe function to simulate an error + const originalPipe = firehoseProcessor.__proto__; + + // Just return no data to test the empty processing path + const emptyStream = new Readable({ + objectMode: true, + read() { + this.push(null); + } + }); + fromLeoStub.returns(emptyStream); + + botCheckpointStub.callsFake((botId, source, data, cb) => cb(null)); + + firehoseProcessor.handler(mockEvent, mockContext, (err) => { + // No error since we just return empty stream + done(); + }); + }); + + it('should use event properties correctly', (done) => { + const mockEvent = { + botId: 'custom-bot-id', + debug: true, + start: 'z/2021/06/15/12/30/12345' + }; + const mockContext = createMockContext(); + mockContext.getRemainingTimeInMillis.returns(5000); + + const mockSourceStream = new Readable({ + objectMode: true, + read() { + this.push(null); + } + }); + fromLeoStub.returns(mockSourceStream); + + botCheckpointStub.callsFake((botId, source, data, cb) => cb(null)); + + firehoseProcessor.handler(mockEvent, mockContext, (err) => { + expect(fromLeoStub.calledWith('custom-bot-id', 'commands.s3_bus_load', sinon.match({ + debug: true, + limit: 1, + start: 'z/2021/06/15/12/30/12345' + }))).to.be.true; + done(); + }); + }); + + it('should skip events without id and event', (done) => { + const mockEvent = { + botId: 'test-firehose-bot', + debug: false + }; + const mockContext = createMockContext(); + mockContext.getRemainingTimeInMillis.returns(5000); + + const mockPayload = { + eid: 'z/2021/01/01/00/00/001', + units: 1, + timestamp: Date.now(), + source_timestamp: Date.now(), + payload: { + files: ['s3://bucket/file1.gz'] + } + }; + + const mockSourceStream = new Readable({ + objectMode: true, + read() { + this.push(mockPayload); + this.push(null); + } + }); + fromLeoStub.returns(mockSourceStream); + + // Create data without id or event - should be skipped + const testData = JSON.stringify({ payload: { data: 'test' } }); + const base64Data = Buffer.from(testData).toString('base64'); + + const mockS3Stream = new Readable({ + read() { + this.push(base64Data); + this.push(null); + } + }); + fromS3Stub.returns(mockS3Stream); + + botCheckpointStub.callsFake((botId, source, data, cb) => cb(null)); + + firehoseProcessor.handler(mockEvent, mockContext, (err) => { + done(); + }); + }); + + it('should set default timestamp if missing', (done) => { + const mockEvent = { + botId: 'test-firehose-bot', + debug: false + }; + const mockContext = createMockContext(); + mockContext.getRemainingTimeInMillis.returns(5000); + + const mockPayload = { + eid: 'z/2021/01/01/00/00/001', + units: 1, + timestamp: Date.now(), + source_timestamp: Date.now(), + payload: { + files: ['s3://bucket/file1.gz'] + } + }; + + const mockSourceStream = new Readable({ + objectMode: true, + read() { + this.push(mockPayload); + this.push(null); + } + }); + fromLeoStub.returns(mockSourceStream); + + // Create data without timestamp + const testData = JSON.stringify({ id: 'test-bot', event: 'test-queue', payload: {} }); + const base64Data = Buffer.from(testData).toString('base64'); + + const mockS3Stream = new Readable({ + read() { + this.push(base64Data); + this.push(null); + } + }); + fromS3Stub.returns(mockS3Stream); + + botCheckpointStub.callsFake((botId, source, data, cb) => cb(null)); + + firehoseProcessor.handler(mockEvent, mockContext, (err) => { + done(); + }); + }); + + it('should handle checkpoint errors', (done) => { + const mockEvent = { + botId: 'test-firehose-bot', + debug: false + }; + const mockContext = createMockContext(); + mockContext.getRemainingTimeInMillis.returns(5000); + + const mockSourceStream = new Readable({ + objectMode: true, + read() { + this.push(null); + } + }); + fromLeoStub.returns(mockSourceStream); + + botCheckpointStub.callsFake((botId, source, data, cb) => { + cb(new Error('Checkpoint failed')); + }); + + firehoseProcessor.handler(mockEvent, mockContext, (err) => { + done(); + }); + }); + + it('should process multiple files in payload', (done) => { + const mockEvent = { + botId: 'test-firehose-bot', + debug: false + }; + const mockContext = createMockContext(); + mockContext.getRemainingTimeInMillis.returns(5000); + + const mockPayload = { + eid: 'z/2021/01/01/00/00/001', + units: 1, + timestamp: Date.now(), + source_timestamp: Date.now(), + payload: { + files: ['s3://bucket/file1.gz', 's3://bucket/file2.gz'] + } + }; + + const mockSourceStream = new Readable({ + objectMode: true, + read() { + this.push(mockPayload); + this.push(null); + } + }); + fromLeoStub.returns(mockSourceStream); + + const testData = JSON.stringify({ id: 'test-bot', event: 'test-queue', payload: {} }); + const base64Data = Buffer.from(testData).toString('base64'); + + fromS3Stub.returns(new Readable({ + read() { + this.push(base64Data); + this.push(null); + } + })); + + botCheckpointStub.callsFake((botId, source, data, cb) => cb(null)); + + firehoseProcessor.handler(mockEvent, mockContext, (err) => { + done(); + }); + }); + + it('should reuse existing event stream for same event', (done) => { + const mockEvent = { + botId: 'test-firehose-bot', + debug: false + }; + const mockContext = createMockContext(); + mockContext.getRemainingTimeInMillis.returns(5000); + + const mockPayload = { + eid: 'z/2021/01/01/00/00/001', + units: 1, + timestamp: Date.now(), + source_timestamp: Date.now(), + payload: { + files: ['s3://bucket/file1.gz'] + } + }; + + const mockSourceStream = new Readable({ + objectMode: true, + read() { + this.push(mockPayload); + this.push(null); + } + }); + fromLeoStub.returns(mockSourceStream); + + // Create two events going to the same queue + const testData1 = JSON.stringify({ id: 'bot1', event: 'same-queue', payload: {} }); + const testData2 = JSON.stringify({ id: 'bot2', event: 'same-queue', payload: {} }); + const combinedData = Buffer.from(testData1).toString('base64') + '\n' + Buffer.from(testData2).toString('base64'); + + fromS3Stub.returns(new Readable({ + read() { + this.push(combinedData); + this.push(null); + } + })); + + botCheckpointStub.callsFake((botId, source, data, cb) => cb(null)); + + firehoseProcessor.handler(mockEvent, mockContext, (err) => { + done(); + }); + }); + + it('should handle S3 file processing with empty files', (done) => { + const mockEvent = { + botId: 'test-firehose-bot', + debug: false + }; + const mockContext = createMockContext(); + mockContext.getRemainingTimeInMillis.returns(5000); + + const mockPayload = { + eid: 'z/2021/01/01/00/00/001', + units: 1, + timestamp: Date.now(), + source_timestamp: Date.now(), + payload: { + files: ['s3://bucket/file1.gz'] + } + }; + + const mockSourceStream = new Readable({ + objectMode: true, + read() { + this.push(mockPayload); + this.push(null); + } + }); + fromLeoStub.returns(mockSourceStream); + + // S3 stream that returns empty data + const mockS3Stream = new Readable({ + read() { + this.push(null); + } + }); + fromS3Stub.returns(mockS3Stream); + + botCheckpointStub.callsFake((botId, source, data, cb) => cb(null)); + + firehoseProcessor.handler(mockEvent, mockContext, (err) => { + expect(err).to.be.null; + done(); + }); + }); + + it('should continue loop while units > 0 and time remaining', (done) => { + const mockEvent = { + botId: 'test-firehose-bot', + debug: false + }; + const mockContext = createMockContext(); + + // First call: plenty of time, second call: low time + let callCount = 0; + mockContext.getRemainingTimeInMillis.callsFake(() => { + callCount++; + return callCount <= 2 ? 60000 : 5000; + }); + + let sourceCallCount = 0; + fromLeoStub.callsFake(() => { + sourceCallCount++; + // First call returns data with units, subsequent calls return empty + if (sourceCallCount === 1) { + return new Readable({ + objectMode: true, + read() { + this.push({ + eid: 'z/2021/01/01/00/00/001', + units: 1, + timestamp: Date.now(), + source_timestamp: Date.now(), + payload: { files: [] } + }); + this.push(null); + } + }); + } + return new Readable({ + objectMode: true, + read() { + this.push(null); + } + }); + }); + + botCheckpointStub.callsFake((botId, source, data, cb) => cb(null)); + + firehoseProcessor.handler(mockEvent, mockContext, (err) => { + expect(err).to.be.null; + done(); + }); + }); + + it('should checkpoint when units processed', (done) => { + const mockEvent = { + botId: 'test-firehose-bot', + debug: false + }; + const mockContext = createMockContext(); + mockContext.getRemainingTimeInMillis.returns(5000); + + const mockPayload = { + eid: 'z/2021/01/01/00/00/001', + units: 5, + timestamp: Date.now(), + source_timestamp: Date.now(), + payload: { files: [] } + }; + + const mockSourceStream = new Readable({ + objectMode: true, + read() { + this.push(mockPayload); + this.push(null); + } + }); + fromLeoStub.returns(mockSourceStream); + + botCheckpointStub.callsFake((botId, source, data, cb) => { + expect(botId).to.equal('test-firehose-bot'); + expect(source).to.equal('commands.s3_bus_load'); + expect(data.units).to.equal(5); + cb(null); + }); + + firehoseProcessor.handler(mockEvent, mockContext, (err) => { + expect(botCheckpointStub.called).to.be.true; + done(); + }); + }); + }); +}); diff --git a/bots/install/test/add-crons.test.js b/bots/install/test/add-crons.test.js new file mode 100644 index 0000000..5ac1732 --- /dev/null +++ b/bots/install/test/add-crons.test.js @@ -0,0 +1,139 @@ +'use strict'; + +const sinon = require('sinon'); +const { expect } = require('chai'); +const proxyquire = require('proxyquire').noCallThru(); + +describe("install/steps/add-crons", () => { + let addCrons; + let createBotStub; + + beforeEach(function () { + createBotStub = sinon.stub(); + + const leoSdk = { + bot: { + createBot: createBotStub + }, + configuration: { + resources: { + LeoFirehoseStreamProcessor: 'test-firehose-processor' + } + }, + '@global': true + }; + + const monitorPackageJson = { + config: { + leo: { + cron: { + lambdaName: 'monitor-lambda', + time: '* * * * * *' + } + } + } + }; + + const firehosePackageJson = { + config: { + leo: { + cron: { + lambdaName: 'firehose-lambda', + time: '* * * * * *' + } + } + } + }; + + addCrons = proxyquire('../steps/add-crons', { + 'leo-sdk': leoSdk, + '../../leo-monitor/package.json': monitorPackageJson, + '../../firehose_processor/package.json': firehosePackageJson + }); + }); + + afterEach(function () { + sinon.restore(); + }); + + describe("addCrons", () => { + it('should create leo_cron_monitor bot', async () => { + createBotStub.resolves({ success: true }); + + await addCrons(); + + expect(createBotStub.calledWith('leo_cron_monitor')).to.be.true; + }); + + it('should create firehose processor bot', async () => { + createBotStub.resolves({ success: true }); + + await addCrons(); + + expect(createBotStub.calledWith('test-firehose-processor')).to.be.true; + }); + + it('should pass monitor config to createBot', async () => { + createBotStub.resolves({ success: true }); + + await addCrons(); + + const monitorCall = createBotStub.getCalls().find( + call => call.args[0] === 'leo_cron_monitor' + ); + expect(monitorCall).to.not.be.undefined; + expect(monitorCall.args[1]).to.have.property('lambdaName', 'monitor-lambda'); + }); + + it('should pass firehose config to createBot', async () => { + createBotStub.resolves({ success: true }); + + await addCrons(); + + const firehoseCall = createBotStub.getCalls().find( + call => call.args[0] === 'test-firehose-processor' + ); + expect(firehoseCall).to.not.be.undefined; + expect(firehoseCall.args[1]).to.have.property('lambdaName', 'firehose-lambda'); + }); + + it('should reject if monitor bot creation fails', async () => { + createBotStub.onFirstCall().rejects(new Error('Monitor creation failed')); + createBotStub.onSecondCall().resolves({ success: true }); + + try { + await addCrons(); + expect.fail('Should have thrown'); + } catch (err) { + expect(err.message).to.equal('Monitor creation failed'); + } + }); + + it('should reject if firehose bot creation fails', async () => { + createBotStub.onFirstCall().resolves({ success: true }); + createBotStub.onSecondCall().rejects(new Error('Firehose creation failed')); + + try { + await addCrons(); + expect.fail('Should have thrown'); + } catch (err) { + expect(err.message).to.equal('Firehose creation failed'); + } + }); + + it('should create both bots in parallel', async () => { + const callOrder = []; + createBotStub.callsFake((id) => { + callOrder.push(id); + return Promise.resolve({ success: true }); + }); + + await addCrons(); + + // Both bots should be created + expect(createBotStub.callCount).to.equal(2); + expect(callOrder).to.include('leo_cron_monitor'); + expect(callOrder).to.include('test-firehose-processor'); + }); + }); +}); diff --git a/bots/install/test/install.test.js b/bots/install/test/install.test.js index bcd6658..3eaa561 100644 --- a/bots/install/test/install.test.js +++ b/bots/install/test/install.test.js @@ -90,9 +90,67 @@ describe("Install bot", function() { ResourceProperties: {} }; - installBot.handler(event, {}, (err, result) => { - expect(result.Status).to.be.equal("SUCCESS"); + installBot.handler(event, {}, (err) => { + // Verify sendCustomResourceResponse was called with SUCCESS + expect(sendCustomResourceResponseFunc.calledOnce).to.be.true; + const callArgs = sendCustomResourceResponseFunc.getCall(0).args; + expect(callArgs[1]).to.equal('SUCCESS'); done(err); } ); }); + + it("Handles 3rd party install with ResourceProperties", function(done) { + sendCustomResourceResponseFunc.resetHistory(); + sendCustomResourceResponseFunc.resolves({ + Status: "SUCCESS" + }); + + const event = { + ResponseURL: 'http://localhost', + PhysicalResourceId: 'physicalresourceid', + StackId: 'stackid', + RequestId: 'requestid', + LogicalResourceId: 'test-logical-id', + ResourceProperties: { + ServiceToken: 'arn:aws:lambda:us-east-1:123456789:function:test', + Version: '1.0.0', + bots: [{ id: 'test-bot', name: 'Test Bot' }] + } + }; + + installBot.handler(event, {}, (err) => { + expect(sendCustomResourceResponseFunc.calledOnce).to.be.true; + const callArgs = sendCustomResourceResponseFunc.getCall(0).args; + expect(callArgs[1]).to.equal('SUCCESS'); + // PhysicalResourceId should be set to LogicalResourceId for 3rd party installs + expect(event.PhysicalResourceId).to.equal('test-logical-id'); + done(err); + }); + }); + + it("Handles step errors with FAILED response", function(done) { + sendCustomResourceResponseFunc.resetHistory(); + sendCustomResourceResponseFunc.resolves({ + Status: "FAILED" + }); + + // Make getBucketNotificationConfiguration fail + getBucketNotificationConfigurationFunc.yields(new Error('S3 error')); + + const event = { + ResponseURL: 'http://localhost', + PhysicalResourceId: 'physicalresourceid', + StackId: 'stackid', + RequestId: 'requestid', + LogicalResourceId: 'logicalresourceid', + ResourceProperties: {} + }; + + installBot.handler(event, {}, (err) => { + expect(sendCustomResourceResponseFunc.calledOnce).to.be.true; + const callArgs = sendCustomResourceResponseFunc.getCall(0).args; + expect(callArgs[1]).to.equal('FAILED'); + done(); + }); + }); }); diff --git a/bots/install/test/register.test.js b/bots/install/test/register.test.js new file mode 100644 index 0000000..7d083da --- /dev/null +++ b/bots/install/test/register.test.js @@ -0,0 +1,373 @@ +'use strict'; + +const sinon = require('sinon'); +const { expect } = require('chai'); +const proxyquire = require('proxyquire').noCallThru(); + +describe("install/steps/register", () => { + let register; + let createBotStub; + let dynamodbMergeStub; + let s3UploadStub; + + beforeEach(function () { + createBotStub = sinon.stub(); + dynamodbMergeStub = sinon.stub(); + s3UploadStub = sinon.stub(); + + const leoSdk = { + bot: { + createBot: createBotStub + }, + configuration: { + resources: { + LeoSystem: 'test-system-table', + LeoS3: 'test-s3-bucket', + Region: 'us-west-2' + } + }, + aws: { + dynamodb: { + merge: dynamodbMergeStub + } + }, + '@global': true + }; + + const leoRef = { + ref: sinon.stub().callsFake((id) => ({ + id: id.replace(/^queue:/, '') + })) + }; + + const isSemver = sinon.stub().returns(true); + + const AWS = { + config: { + update: sinon.stub() + }, + S3: class MockS3 { + upload(params) { + return { + promise: s3UploadStub + }; + } + }, + '@global': true + }; + + register = proxyquire('../steps/register', { + 'leo-sdk': leoSdk, + 'leo-sdk/lib/reference': leoRef, + 'aws-sdk': AWS, + 'is-semver': isSemver, + 'ajv': require('ajv'), + 'ajv-formats': require('ajv-formats') + }); + }); + + afterEach(function () { + sinon.restore(); + }); + + describe("bot registration", () => { + it('should register a bot with createBot', async () => { + createBotStub.resolves({ success: true }); + + const data = { + id: 'test-bot', + triggers: ['queue1'], + lambdaName: 'test-lambda' + }; + + await register('TestBot', data); + + expect(createBotStub.calledOnce).to.be.true; + const [id, config] = createBotStub.getCall(0).args; + expect(id).to.equal('test-bot'); + expect(config.lambdaName).to.equal('test-lambda'); + }); + + it('should parse JSON string data', async () => { + createBotStub.resolves({ success: true }); + + const data = JSON.stringify({ + id: 'test-bot', + triggers: ['queue1'] + }); + + await register('TestBot', data); + + expect(createBotStub.calledOnce).to.be.true; + }); + + it('should set paused to true by default', async () => { + createBotStub.resolves({ success: true }); + + const data = { + id: 'test-bot' + }; + + await register('TestBot', data); + + const [, config] = createBotStub.getCall(0).args; + expect(config.paused).to.be.true; + }); + + it('should respect explicit paused value', async () => { + createBotStub.resolves({ success: true }); + + const data = { + id: 'test-bot', + paused: 'false' + }; + + await register('TestBot', data); + + const [, config] = createBotStub.getCall(0).args; + expect(config.paused).to.be.false; + }); + + it('should extract id from lambda ARN', async () => { + createBotStub.resolves({ success: true }); + + const data = { + id: 'arn:aws:lambda:us-west-2:123456789:function:my-function' + }; + + await register('TestBot', data); + + const [id] = createBotStub.getCall(0).args; + expect(id).to.equal('my-function'); + }); + + it('should convert string numbers to actual numbers', async () => { + createBotStub.resolves({ success: true }); + + const data = { + id: 'test-bot', + timeout: '300', + memory: '512.5' + }; + + await register('TestBot', data); + + const [, config] = createBotStub.getCall(0).args; + expect(config.timeout).to.equal(300); + expect(config.memory).to.equal(512.5); + }); + + it('should convert string booleans to actual booleans', async () => { + createBotStub.resolves({ success: true }); + + const data = { + id: 'test-bot', + enabled: 'true', + debug: 'FALSE' + }; + + await register('TestBot', data); + + const [, config] = createBotStub.getCall(0).args; + expect(config.enabled).to.be.true; + expect(config.debug).to.be.false; + }); + + it('should convert "null" string to null', async () => { + createBotStub.resolves({ success: true }); + + const data = { + id: 'test-bot', + someField: 'null' + }; + + await register('TestBot', data); + + const [, config] = createBotStub.getCall(0).args; + expect(config.someField).to.be.null; + }); + + it('should convert "undefined" string to undefined', async () => { + createBotStub.resolves({ success: true }); + + const data = { + id: 'test-bot', + someField: 'undefined' + }; + + await register('TestBot', data); + + const [, config] = createBotStub.getCall(0).args; + expect(config.someField).to.be.undefined; + }); + + it('should handle nested objects', async () => { + createBotStub.resolves({ success: true }); + + const data = { + id: 'test-bot', + settings: { + timeout: '300', + enabled: 'true' + } + }; + + await register('TestBot', data); + + const [, config] = createBotStub.getCall(0).args; + expect(config.settings.timeout).to.equal(300); + expect(config.settings.enabled).to.be.true; + }); + + it('should handle arrays', async () => { + createBotStub.resolves({ success: true }); + + const data = { + id: 'test-bot', + triggers: ['queue1', 'queue2'], + numbers: ['1', '2', '3'] + }; + + await register('TestBot', data); + + const [, config] = createBotStub.getCall(0).args; + expect(config.triggers).to.deep.equal(['queue1', 'queue2']); + expect(config.numbers).to.deep.equal([1, 2, 3]); + }); + }); + + describe("system registration", () => { + it('should merge data into system table', async () => { + dynamodbMergeStub.callsFake((table, id, data, callback) => { + callback(null); + }); + + const data = { + LeoRegisterType: 'system', + id: 'system-config', + setting1: 'value1' + }; + + await register('SystemConfig', data); + + expect(dynamodbMergeStub.calledOnce).to.be.true; + const [table, id, mergeData] = dynamodbMergeStub.getCall(0).args; + expect(table).to.equal('test-system-table'); + expect(id).to.equal('system-config'); + }); + + it('should handle merge errors', async () => { + dynamodbMergeStub.callsFake((table, id, data, callback) => { + callback(new Error('Merge error')); + }); + + const data = { + LeoRegisterType: 'system', + id: 'system-config' + }; + + try { + await register('SystemConfig', data); + expect.fail('Should have thrown'); + } catch (err) { + expect(err.message).to.equal('Merge error'); + } + }); + }); + + describe("queue registration", () => { + it('should upload schema to S3', async () => { + s3UploadStub.resolves({ Location: 's3://bucket/key' }); + + const data = { + LeoRegisterType: 'queue', + queue: 'test-queue', + schemas: { + '1.0.0': { + versionSchema: { type: 'object' }, + definitionsSchema: {} + } + } + }; + + await register('QueueSchema', data); + + expect(s3UploadStub.calledOnce).to.be.true; + }); + + it('should reject queue without schemas', async () => { + const data = { + LeoRegisterType: 'queue', + queue: 'test-queue' + }; + + try { + await register('QueueSchema', data); + expect.fail('Should have thrown'); + } catch (err) { + expect(err).to.equal('Queue registered without a schema'); + } + }); + + it('should validate schema version is semver', async () => { + const isSemverStub = sinon.stub().returns(false); + + const registerWithInvalidSemver = proxyquire('../steps/register', { + 'leo-sdk': { + bot: { createBot: createBotStub }, + configuration: { + resources: { + LeoSystem: 'test-system-table', + LeoS3: 'test-s3-bucket', + Region: 'us-west-2' + } + }, + aws: { + dynamodb: { merge: dynamodbMergeStub } + } + }, + 'leo-sdk/lib/reference': { + ref: () => ({ id: 'test-queue' }) + }, + 'aws-sdk': { + config: { update: sinon.stub() }, + S3: class { upload() { return { promise: s3UploadStub }; } } + }, + 'is-semver': isSemverStub, + 'ajv': require('ajv'), + 'ajv-formats': require('ajv-formats') + }); + + const data = { + LeoRegisterType: 'queue', + queue: 'test-queue', + schemas: { + 'invalid-version': { + versionSchema: { type: 'object' } + } + } + }; + + try { + await registerWithInvalidSemver('QueueSchema', data); + expect.fail('Should have thrown'); + } catch (err) { + expect(err).to.include('was not found to be valid semver'); + } + }); + }); + + describe("unknown type", () => { + it('should resolve immediately for unknown types', async () => { + const data = { + LeoRegisterType: 'unknown' + }; + + const result = await register('Unknown', data); + + // Should resolve without error + expect(createBotStub.called).to.be.false; + expect(dynamodbMergeStub.called).to.be.false; + }); + }); +}); diff --git a/bots/install/test/s3-load-trigger.test.js b/bots/install/test/s3-load-trigger.test.js new file mode 100644 index 0000000..dc32e2d --- /dev/null +++ b/bots/install/test/s3-load-trigger.test.js @@ -0,0 +1,265 @@ +'use strict'; + +const sinon = require('sinon'); +const { expect } = require('chai'); +const proxyquire = require('proxyquire').noCallThru(); + +describe("install/steps/s3-load-trigger", () => { + let s3LoadTrigger; + let addPermissionStub; + let getBucketNotificationConfigurationStub; + let putBucketNotificationConfigurationStub; + let listAttachedRolePoliciesStub; + let attachRolePolicyStub; + + beforeEach(function () { + addPermissionStub = sinon.stub(); + getBucketNotificationConfigurationStub = sinon.stub(); + putBucketNotificationConfigurationStub = sinon.stub(); + listAttachedRolePoliciesStub = sinon.stub(); + attachRolePolicyStub = sinon.stub(); + + process.env.Resources = JSON.stringify({ + LeoS3LoadTrigger: 'test-s3-trigger-function', + LeoS3: 'test-bucket', + LeoFirehoseRole: 'arn:aws:iam::123456789:role/test-firehose-role', + LeoBotPolicy: 'arn:aws:iam::123456789:policy/test-bot-policy' + }); + process.env.AWS = JSON.stringify({ + region: 'us-west-2', + AccountId: '123456789' + }); + + const aws = { + S3: class MockS3 { + constructor() { + this.getBucketNotificationConfiguration = getBucketNotificationConfigurationStub; + this.putBucketNotificationConfiguration = putBucketNotificationConfigurationStub; + } + }, + Lambda: class MockLambda { + constructor() { + this.addPermission = addPermissionStub; + } + }, + IAM: class MockIAM { + constructor() { + this.listAttachedRolePolicies = listAttachedRolePoliciesStub; + this.attachRolePolicy = attachRolePolicyStub; + } + }, + '@global': true + }; + + s3LoadTrigger = proxyquire('../steps/s3-load-trigger', { + 'aws-sdk': aws, + 'leo-logger': { + info: sinon.stub(), + error: sinon.stub() + } + }); + }); + + afterEach(function () { + sinon.restore(); + delete process.env.Resources; + delete process.env.AWS; + }); + + describe("s3LoadTrigger", () => { + it('should add lambda permission for S3', async () => { + addPermissionStub.yields(null); + getBucketNotificationConfigurationStub.yields(null, { + LambdaFunctionConfigurations: [] + }); + putBucketNotificationConfigurationStub.yields(null); + listAttachedRolePoliciesStub.yields(null, { AttachedPolicies: [] }); + attachRolePolicyStub.yields(null); + + await s3LoadTrigger(); + + expect(addPermissionStub.calledOnce).to.be.true; + const permissionArgs = addPermissionStub.getCall(0).args[0]; + expect(permissionArgs.FunctionName).to.equal('test-s3-trigger-function'); + expect(permissionArgs.Action).to.equal('lambda:InvokeFunction'); + expect(permissionArgs.Principal).to.equal('s3.amazonaws.com'); + }); + + it('should configure S3 bucket notification', async () => { + addPermissionStub.yields(null); + getBucketNotificationConfigurationStub.yields(null, { + LambdaFunctionConfigurations: [] + }); + putBucketNotificationConfigurationStub.yields(null); + listAttachedRolePoliciesStub.yields(null, { AttachedPolicies: [] }); + attachRolePolicyStub.yields(null); + + await s3LoadTrigger(); + + expect(putBucketNotificationConfigurationStub.calledOnce).to.be.true; + const notificationArgs = putBucketNotificationConfigurationStub.getCall(0).args[0]; + expect(notificationArgs.Bucket).to.equal('test-bucket'); + + const config = notificationArgs.NotificationConfiguration; + const lambdaConfig = config.LambdaFunctionConfigurations.find( + c => c.Id === 'bus-events-upload' + ); + expect(lambdaConfig).to.not.be.undefined; + expect(lambdaConfig.Events).to.include('s3:ObjectCreated:*'); + }); + + it('should skip notification if already configured', async () => { + addPermissionStub.yields(null); + getBucketNotificationConfigurationStub.yields(null, { + LambdaFunctionConfigurations: [{ Id: 'bus-events-upload' }] + }); + listAttachedRolePoliciesStub.yields(null, { AttachedPolicies: [] }); + attachRolePolicyStub.yields(null); + + await s3LoadTrigger(); + + expect(putBucketNotificationConfigurationStub.called).to.be.false; + }); + + it('should attach bot policy to firehose role', async () => { + addPermissionStub.yields(null); + getBucketNotificationConfigurationStub.yields(null, { + LambdaFunctionConfigurations: [{ Id: 'bus-events-upload' }] + }); + listAttachedRolePoliciesStub.yields(null, { AttachedPolicies: [] }); + attachRolePolicyStub.yields(null); + + await s3LoadTrigger(); + + expect(attachRolePolicyStub.calledOnce).to.be.true; + const attachArgs = attachRolePolicyStub.getCall(0).args[0]; + expect(attachArgs.PolicyArn).to.equal('arn:aws:iam::123456789:policy/test-bot-policy'); + expect(attachArgs.RoleName).to.equal('test-firehose-role'); + }); + + it('should skip policy attachment if already attached', async () => { + addPermissionStub.yields(null); + getBucketNotificationConfigurationStub.yields(null, { + LambdaFunctionConfigurations: [{ Id: 'bus-events-upload' }] + }); + listAttachedRolePoliciesStub.yields(null, { + AttachedPolicies: [{ PolicyArn: 'arn:aws:iam::123456789:policy/test-bot-policy' }] + }); + + await s3LoadTrigger(); + + expect(attachRolePolicyStub.called).to.be.false; + }); + + it('should handle permission already exists error', async () => { + const permissionError = new Error('The statement id (S3-bus-events-upload-trigger) provided already exists'); + addPermissionStub.yields(permissionError); + getBucketNotificationConfigurationStub.yields(null, { + LambdaFunctionConfigurations: [] + }); + putBucketNotificationConfigurationStub.yields(null); + listAttachedRolePoliciesStub.yields(null, { AttachedPolicies: [] }); + attachRolePolicyStub.yields(null); + + await s3LoadTrigger(); + + // Should not throw, should continue + expect(getBucketNotificationConfigurationStub.calledOnce).to.be.true; + }); + + it('should reject on addPermission error (non-duplicate)', async () => { + addPermissionStub.yields(new Error('Permission error')); + + try { + await s3LoadTrigger(); + expect.fail('Should have thrown'); + } catch (err) { + expect(err.message).to.equal('Permission error'); + } + }); + + it('should reject on getBucketNotificationConfiguration error', async () => { + addPermissionStub.yields(null); + getBucketNotificationConfigurationStub.yields(new Error('Get notification error')); + listAttachedRolePoliciesStub.yields(null, { AttachedPolicies: [] }); + attachRolePolicyStub.yields(null); + + try { + await s3LoadTrigger(); + expect.fail('Should have thrown'); + } catch (err) { + expect(err.message).to.equal('Get notification error'); + } + }); + + it('should reject on putBucketNotificationConfiguration error', async () => { + addPermissionStub.yields(null); + getBucketNotificationConfigurationStub.yields(null, { + LambdaFunctionConfigurations: [] + }); + putBucketNotificationConfigurationStub.yields(new Error('Put notification error')); + listAttachedRolePoliciesStub.yields(null, { AttachedPolicies: [] }); + attachRolePolicyStub.yields(null); + + try { + await s3LoadTrigger(); + expect.fail('Should have thrown'); + } catch (err) { + expect(err.message).to.equal('Put notification error'); + } + }); + + it('should reject on listAttachedRolePolicies error', async () => { + addPermissionStub.yields(null); + getBucketNotificationConfigurationStub.yields(null, { + LambdaFunctionConfigurations: [{ Id: 'bus-events-upload' }] + }); + listAttachedRolePoliciesStub.yields(new Error('List policies error')); + + try { + await s3LoadTrigger(); + expect.fail('Should have thrown'); + } catch (err) { + expect(err.message).to.equal('List policies error'); + } + }); + + it('should reject on attachRolePolicy error', async () => { + addPermissionStub.yields(null); + getBucketNotificationConfigurationStub.yields(null, { + LambdaFunctionConfigurations: [{ Id: 'bus-events-upload' }] + }); + listAttachedRolePoliciesStub.yields(null, { AttachedPolicies: [] }); + attachRolePolicyStub.yields(new Error('Attach policy error')); + + try { + await s3LoadTrigger(); + expect.fail('Should have thrown'); + } catch (err) { + expect(err.message).to.equal('Attach policy error'); + } + }); + + it('should configure filter for firehose prefix', async () => { + addPermissionStub.yields(null); + getBucketNotificationConfigurationStub.yields(null, { + LambdaFunctionConfigurations: [] + }); + putBucketNotificationConfigurationStub.yields(null); + listAttachedRolePoliciesStub.yields(null, { AttachedPolicies: [] }); + attachRolePolicyStub.yields(null); + + await s3LoadTrigger(); + + const notificationArgs = putBucketNotificationConfigurationStub.getCall(0).args[0]; + const lambdaConfig = notificationArgs.NotificationConfiguration.LambdaFunctionConfigurations.find( + c => c.Id === 'bus-events-upload' + ); + + expect(lambdaConfig.Filter.Key.FilterRules).to.deep.include({ + Name: 'prefix', + Value: 'firehose/' + }); + }); + }); +}); diff --git a/bots/kinesis_processor/test/kinesis_processor.test.js b/bots/kinesis_processor/test/kinesis_processor.test.js new file mode 100644 index 0000000..6c65f04 --- /dev/null +++ b/bots/kinesis_processor/test/kinesis_processor.test.js @@ -0,0 +1,714 @@ +'use strict'; + +const sinon = require('sinon'); +const { expect } = require('chai'); +const proxyquire = require('proxyquire').noCallThru(); +const zlib = require('zlib'); +const { PassThrough, Transform, Writable, Duplex } = require('stream'); + +describe("kinesis_processor", () => { + let kinesisProcessor; + let dynamodbDocClientUpdateStub; + let dynamodbDocClientGetStub; + let dynamodbDocClientDeleteStub; + let dynamodbUpdateMultiStub; + + const mockStreamTable = 'test-stream-table'; + const mockEventTable = 'test-event-table'; + const mockCronTable = 'test-cron-table'; + const mockSettingsTable = 'test-settings-table'; + + beforeEach(function () { + dynamodbDocClientUpdateStub = sinon.stub(); + dynamodbDocClientGetStub = sinon.stub(); + dynamodbDocClientDeleteStub = sinon.stub(); + dynamodbUpdateMultiStub = sinon.stub(); + + // Create mock devnull that consumes data + const createMockDevnull = () => { + return new Writable({ + objectMode: true, + write(chunk, encoding, callback) { + callback(); + } + }); + }; + + // Create mock parse stream + const createMockParse = () => { + return new Transform({ + objectMode: true, + transform(chunk, encoding, callback) { + try { + const lines = chunk.toString().split('\n').filter(l => l.trim()); + lines.forEach(line => { + try { + this.push(JSON.parse(line)); + } catch (e) { + // Skip invalid JSON + } + }); + callback(); + } catch (e) { + callback(e); + } + } + }); + }; + + // Mock toS3GzipChunks - transforms events into chunks with stats + const createToS3GzipChunks = () => { + return new Transform({ + objectMode: true, + transform(chunk, encoding, callback) { + // Transform event into chunk format that assignIds expects + const chunkData = { + records: 1, + end: 1, + stats: {}, + correlations: {} + }; + // Add stats for the bot + if (chunk && chunk.id) { + chunkData.stats[chunk.id] = { + units: 1, + start: Date.now(), + end: Date.now(), + checkpoint: 0 + }; + } + callback(null, chunkData); + } + }); + }; + + // Mock toGzipChunks - same as toS3GzipChunks for testing + const createToGzipChunks = () => createToS3GzipChunks(); + + // Mock toDynamoDB - just passes through (simulates successful write) + const createToDynamoDB = () => { + return new Writable({ + objectMode: true, + write(chunk, encoding, callback) { + callback(); + } + }); + }; + + const leoSdk = { + configuration: { + resources: { + LeoStream: mockStreamTable, + LeoEvent: mockEventTable, + LeoCron: mockCronTable, + LeoSettings: mockSettingsTable + } + }, + aws: { + dynamodb: { + docClient: { + update: dynamodbDocClientUpdateStub, + get: dynamodbDocClientGetStub, + delete: dynamodbDocClientDeleteStub + }, + updateMulti: dynamodbUpdateMultiStub + } + }, + streams: { + pipe: function(...args) { + const streams = args.slice(0, -1); + const callback = args[args.length - 1]; + + if (streams.length === 0) { + callback(); + return; + } + + // Pipe all streams together + let combined = streams[0]; + for (let i = 1; i < streams.length; i++) { + combined = combined.pipe(streams[i]); + } + + combined.on('finish', () => callback()); + combined.on('error', (err) => callback(err)); + }, + parse: createMockParse, + through: function(fn) { + // Execute the actual transform function passed in + return new Transform({ + objectMode: true, + transform(chunk, encoding, callback) { + fn(chunk, (err, result) => { + if (err) return callback(err); + if (result !== undefined) this.push(result); + callback(); + }); + } + }); + }, + devnull: createMockDevnull, + toS3GzipChunks: () => createToS3GzipChunks(), + toGzipChunks: () => createToGzipChunks(), + toDynamoDB: () => createToDynamoDB(), + pipeline: function(...streams) { + // Connect all streams in a pipeline + for (let i = 0; i < streams.length - 1; i++) { + streams[i].pipe(streams[i + 1]); + } + + const first = streams[0]; + const last = streams[streams.length - 1]; + + // Create a passthrough that pipes to the first stream + const input = new PassThrough({ objectMode: true }); + input.pipe(first); + + // Track finish handlers + const finishHandlers = []; + const errorHandlers = []; + + // When last stream finishes, call handlers + last.on('finish', () => { + finishHandlers.forEach(h => h()); + }); + last.on('error', (err) => { + errorHandlers.forEach(h => h(err)); + }); + + // Create a custom pipeline object + const pipeline = { + write: (data, callback) => { + const result = input.write(data); + if (callback) setImmediate(callback); + return result; + }, + end: () => { + input.end(); + }, + on: (event, handler) => { + if (event === 'finish') { + finishHandlers.push(handler); + } else if (event === 'error') { + errorHandlers.push(handler); + } + return pipeline; + } + }; + + return pipeline; + } + }, + bot: {}, + '@global': true + }; + + const refUtil = { + ref: sinon.stub().callsFake((id) => ({ + queue: () => ({ id: id.replace(/^queue:/, '') }) + })), + refId: sinon.stub().callsFake((id) => id), + '@global': true + }; + + const momentMock = require('moment'); + + kinesisProcessor = proxyquire('../', { + 'leo-sdk': leoSdk, + 'leo-sdk/lib/reference.js': refUtil, + 'moment': momentMock, + 'async': require('async') + }); + }); + + afterEach(function () { + sinon.restore(); + delete process.env.skip_events; + delete process.env.skip_bots; + delete process.env.ttlSeconds; + }); + + describe("handler", () => { + // Helper to setup common mocks for handler tests + const setupHandlerMocks = () => { + dynamodbDocClientUpdateStub.callsFake((params, callback) => { + // Callback-style (checkpoint update) + if (callback && typeof callback === 'function') { + callback(null, { Attributes: {} }); + return; + } + // Promise-style (setDDBValue) + return { + promise: () => Promise.resolve({ + Attributes: { value: Date.now(), sequence: '12345' } + }) + }; + }); + + dynamodbUpdateMultiStub.callsFake((tasks, callback) => { + callback(null); + }); + }; + + it('should process kinesis records and call handler2', (done) => { + setupHandlerMocks(); + + const eventData = JSON.stringify({ id: 'test-bot', event: 'test-queue', payload: { data: 'test' } }); + const gzippedData = zlib.gzipSync(eventData); + const base64Data = gzippedData.toString('base64'); + + const mockEvent = { + Records: [{ + eventID: 'shardId-000000000001:12345', + kinesis: { + approximateArrivalTimestamp: Date.now() / 1000, + sequenceNumber: '12345', + data: base64Data + } + }] + }; + + kinesisProcessor.handler(mockEvent, {}, (err, result) => { + expect(dynamodbDocClientUpdateStub.called).to.be.true; + done(); + }); + }); + + it('should handle ConditionalCheckFailedException with increment', (done) => { + let promiseCallCount = 0; + dynamodbDocClientUpdateStub.callsFake((params, callback) => { + if (callback && typeof callback === 'function') { + callback(null, { Attributes: {} }); + return; + } + promiseCallCount++; + if (promiseCallCount === 1) { + return { promise: () => Promise.reject({ code: 'ConditionalCheckFailedException' }) }; + } + return { promise: () => Promise.resolve({ Attributes: { value: Date.now(), sequence: '12345' } }) }; + }); + dynamodbUpdateMultiStub.callsFake((tasks, callback) => callback(null)); + + const eventData = JSON.stringify({ id: 'test-bot', event: 'test-queue', payload: {} }); + const base64Data = zlib.gzipSync(eventData).toString('base64'); + const mockEvent = { + Records: [{ + eventID: 'shardId-000000000001:12345', + kinesis: { approximateArrivalTimestamp: Date.now() / 1000, sequenceNumber: '12345', data: base64Data } + }] + }; + + kinesisProcessor.handler(mockEvent, {}, (err) => { + expect(promiseCallCount).to.be.at.least(2); + done(); + }); + }); + + it('should handle ConditionalCheckFailedException on increment with get', (done) => { + let promiseCallCount = 0; + dynamodbDocClientUpdateStub.callsFake((params, callback) => { + if (callback && typeof callback === 'function') { + callback(null, { Attributes: {} }); + return; + } + promiseCallCount++; + if (promiseCallCount <= 2) { + return { promise: () => Promise.reject({ code: 'ConditionalCheckFailedException' }) }; + } + return { promise: () => Promise.resolve({ Attributes: { value: Date.now(), sequence: '12345' } }) }; + }); + dynamodbDocClientGetStub.returns({ + promise: () => Promise.resolve({ Item: { value: Date.now(), sequence: '12345' } }) + }); + dynamodbUpdateMultiStub.callsFake((tasks, callback) => callback(null)); + + const eventData = JSON.stringify({ id: 'test-bot', event: 'test-queue', payload: {} }); + const base64Data = zlib.gzipSync(eventData).toString('base64'); + const mockEvent = { + Records: [{ + eventID: 'shardId-000000000001:12345', + kinesis: { approximateArrivalTimestamp: Date.now() / 1000, sequenceNumber: '12345', data: base64Data } + }] + }; + + kinesisProcessor.handler(mockEvent, {}, (err) => { + expect(dynamodbDocClientGetStub.called).to.be.true; + done(); + }); + }); + + it('should throw on non-ConditionalCheckFailedException errors', (done) => { + dynamodbDocClientUpdateStub.callsFake((params, callback) => { + if (callback && typeof callback === 'function') { + callback(null, { Attributes: {} }); + return; + } + return { promise: () => Promise.reject({ code: 'SomeOtherError', message: 'Other error' }) }; + }); + dynamodbUpdateMultiStub.callsFake((tasks, callback) => callback(null)); + + const eventData = JSON.stringify({ id: 'test-bot', event: 'test-queue', payload: {} }); + const base64Data = zlib.gzipSync(eventData).toString('base64'); + const mockEvent = { + Records: [{ + eventID: 'shardId-000000000001:12345', + kinesis: { approximateArrivalTimestamp: Date.now() / 1000, sequenceNumber: '12345', data: base64Data } + }] + }; + + kinesisProcessor.handler(mockEvent, {}, (err) => { + done(); + }); + }); + + it('should skip events in skip_events env var', (done) => { + setupHandlerMocks(); + process.env.skip_events = 'skip-queue'; + + const eventData = JSON.stringify({ id: 'test-bot', event: 'skip-queue', payload: { data: 'test' } }); + const base64Data = zlib.gzipSync(eventData).toString('base64'); + const mockEvent = { + Records: [{ + eventID: 'shardId-000000000001:12345', + kinesis: { approximateArrivalTimestamp: Date.now() / 1000, sequenceNumber: '12345', data: base64Data } + }] + }; + + kinesisProcessor.handler(mockEvent, {}, (err) => { done(); }); + }); + + it('should skip bots in skip_bots env var', (done) => { + setupHandlerMocks(); + process.env.skip_bots = 'skip-bot'; + + const eventData = JSON.stringify({ id: 'skip-bot', event: 'test-queue', payload: { data: 'test' } }); + const base64Data = zlib.gzipSync(eventData).toString('base64'); + const mockEvent = { + Records: [{ + eventID: 'shardId-000000000001:12345', + kinesis: { approximateArrivalTimestamp: Date.now() / 1000, sequenceNumber: '12345', data: base64Data } + }] + }; + + kinesisProcessor.handler(mockEvent, {}, (err) => { done(); }); + }); + + it('should use custom ttlSeconds from env', (done) => { + setupHandlerMocks(); + process.env.ttlSeconds = '86400'; + + const eventData = JSON.stringify({ id: 'test-bot', event: 'test-queue', payload: {} }); + const base64Data = zlib.gzipSync(eventData).toString('base64'); + const mockEvent = { + Records: [{ + eventID: 'shardId-000000000001:12345', + kinesis: { approximateArrivalTimestamp: Date.now() / 1000, sequenceNumber: '12345', data: base64Data } + }] + }; + + kinesisProcessor.handler(mockEvent, {}, (err) => { done(); }); + }); + + it('should handle inflated data (eJ prefix)', (done) => { + setupHandlerMocks(); + const eventData = JSON.stringify({ id: 'test-bot', event: 'test-queue', payload: {} }); + const base64Data = zlib.deflateSync(eventData).toString('base64'); + const mockEvent = { + Records: [{ + eventID: 'shardId-000000000001:12345', + kinesis: { approximateArrivalTimestamp: Date.now() / 1000, sequenceNumber: '12345', data: base64Data } + }] + }; + + kinesisProcessor.handler(mockEvent, {}, (err) => { done(); }); + }); + + it('should handle base64 JSON data (ey prefix)', (done) => { + setupHandlerMocks(); + const eventData = JSON.stringify({ id: 'test-bot', event: 'test-queue', payload: {} }); + const base64Data = Buffer.from(eventData).toString('base64'); + const mockEvent = { + Records: [{ + eventID: 'shardId-000000000001:12345', + kinesis: { approximateArrivalTimestamp: Date.now() / 1000, sequenceNumber: '12345', data: base64Data } + }] + }; + + kinesisProcessor.handler(mockEvent, {}, (err) => { done(); }); + }); + + it('should handle multiple records', (done) => { + setupHandlerMocks(); + const eventData1 = JSON.stringify({ id: 'bot1', event: 'queue1', payload: {} }); + const eventData2 = JSON.stringify({ id: 'bot2', event: 'queue2', payload: {} }); + const base64Data1 = zlib.gzipSync(eventData1).toString('base64'); + const base64Data2 = zlib.gzipSync(eventData2).toString('base64'); + + const mockEvent = { + Records: [ + { eventID: 'shardId-000000000001:12345', kinesis: { approximateArrivalTimestamp: Date.now() / 1000, sequenceNumber: '12345', data: base64Data1 } }, + { eventID: 'shardId-000000000001:12346', kinesis: { approximateArrivalTimestamp: Date.now() / 1000, sequenceNumber: '12346', data: base64Data2 } } + ] + }; + + kinesisProcessor.handler(mockEvent, {}, (err) => { done(); }); + }); + + it('should handle sequence mismatch error', (done) => { + dynamodbDocClientUpdateStub.callsFake((params, callback) => { + if (callback && typeof callback === 'function') { + callback(null, { Attributes: {} }); + return; + } + return { promise: () => Promise.resolve({ Attributes: { value: Date.now(), sequence: '99999' } }) }; + }); + dynamodbUpdateMultiStub.callsFake((tasks, callback) => callback(null)); + + const eventData = JSON.stringify({ id: 'test-bot', event: 'test-queue', payload: {} }); + const base64Data = zlib.gzipSync(eventData).toString('base64'); + const mockEvent = { + Records: [{ + eventID: 'shardId-000000000001:12345', + kinesis: { approximateArrivalTimestamp: Date.now() / 1000, sequenceNumber: '12345', data: base64Data } + }] + }; + + kinesisProcessor.handler(mockEvent, {}, (err) => { done(); }); + }); + + it('should handle increment error with non-conditional exception', (done) => { + let promiseCallCount = 0; + dynamodbDocClientUpdateStub.callsFake((params, callback) => { + if (callback && typeof callback === 'function') { + callback(null, { Attributes: {} }); + return; + } + promiseCallCount++; + if (promiseCallCount === 1) { + return { promise: () => Promise.reject({ code: 'ConditionalCheckFailedException' }) }; + } + return { promise: () => Promise.reject({ code: 'ProvisionedThroughputExceededException' }) }; + }); + dynamodbUpdateMultiStub.callsFake((tasks, callback) => callback(null)); + + const eventData = JSON.stringify({ id: 'test-bot', event: 'test-queue', payload: {} }); + const base64Data = zlib.gzipSync(eventData).toString('base64'); + const mockEvent = { + Records: [{ + eventID: 'shardId-000000000001:12345', + kinesis: { approximateArrivalTimestamp: Date.now() / 1000, sequenceNumber: '12345', data: base64Data } + }] + }; + + kinesisProcessor.handler(mockEvent, {}, (err) => { done(); }); + }); + + it('should handle archive events', (done) => { + setupHandlerMocks(); + const eventData = JSON.stringify({ + id: 'test-bot', event: 'test-queue', payload: { data: 'test' }, + archive: true, start: 'z/2021/01/01/00/00', end: 'z/2021/01/01/01/00' + }); + const base64Data = zlib.gzipSync(eventData).toString('base64'); + const mockEvent = { + Records: [{ + eventID: 'shardId-000000000001:12345', + kinesis: { approximateArrivalTimestamp: Date.now() / 1000, sequenceNumber: '12345', data: base64Data } + }] + }; + + kinesisProcessor.handler(mockEvent, {}, (err) => { done(); }); + }); + + it('should handle snapshot events', (done) => { + setupHandlerMocks(); + const eventData = JSON.stringify({ + id: 'test-bot', event: 'test-queue', payload: { data: 'test' }, + snapshot: new Date().toISOString() + }); + const base64Data = zlib.gzipSync(eventData).toString('base64'); + const mockEvent = { + Records: [{ + eventID: 'shardId-000000000001:12345', + kinesis: { approximateArrivalTimestamp: Date.now() / 1000, sequenceNumber: '12345', data: base64Data } + }] + }; + + kinesisProcessor.handler(mockEvent, {}, (err) => { done(); }); + }); + + it('should handle _cmd registerSnapshot', (done) => { + setupHandlerMocks(); + const eventData = JSON.stringify({ + _cmd: 'registerSnapshot', event: 'test-queue', + start: new Date().toISOString(), next: new Date().toISOString() + }); + const base64Data = zlib.gzipSync(eventData).toString('base64'); + const mockEvent = { + Records: [{ + eventID: 'shardId-000000000001:12345', + kinesis: { approximateArrivalTimestamp: Date.now() / 1000, sequenceNumber: '12345', data: base64Data } + }] + }; + + kinesisProcessor.handler(mockEvent, {}, (err) => { done(); }); + }); + + it('should skip events without id or payload', (done) => { + setupHandlerMocks(); + const eventData = JSON.stringify({ event: 'test-queue' }); + const base64Data = zlib.gzipSync(eventData).toString('base64'); + const mockEvent = { + Records: [{ + eventID: 'shardId-000000000001:12345', + kinesis: { approximateArrivalTimestamp: Date.now() / 1000, sequenceNumber: '12345', data: base64Data } + }] + }; + + kinesisProcessor.handler(mockEvent, {}, (err) => { done(); }); + }); + + it('should handle s3 events without id/payload', (done) => { + setupHandlerMocks(); + const eventData = JSON.stringify({ event: 'test-queue', s3: { bucket: 'test-bucket', key: 'test-key' } }); + const base64Data = zlib.gzipSync(eventData).toString('base64'); + const mockEvent = { + Records: [{ + eventID: 'shardId-000000000001:12345', + kinesis: { approximateArrivalTimestamp: Date.now() / 1000, sequenceNumber: '12345', data: base64Data } + }] + }; + + kinesisProcessor.handler(mockEvent, {}, (err) => { done(); }); + }); + + it('should handle events with string event_source_timestamp', (done) => { + setupHandlerMocks(); + const eventData = JSON.stringify({ + id: 'test-bot', event: 'test-queue', payload: { data: 'test' }, + event_source_timestamp: new Date().toISOString() + }); + const base64Data = zlib.gzipSync(eventData).toString('base64'); + const mockEvent = { + Records: [{ + eventID: 'shardId-000000000001:12345', + kinesis: { approximateArrivalTimestamp: Date.now() / 1000, sequenceNumber: '12345', data: base64Data } + }] + }; + + kinesisProcessor.handler(mockEvent, {}, (err) => { done(); }); + }); + }); + + describe("handler2", () => { + it('should be exported and callable', () => { + expect(kinesisProcessor.handler2).to.be.a('function'); + }); + + it('should handle DynamoDB updateMulti errors', (done) => { + const eventData = JSON.stringify({ id: 'test-bot', event: 'test-queue', payload: {} }); + const gzippedData = zlib.gzipSync(eventData); + const base64Data = gzippedData.toString('base64'); + + const mockEvent = { + Records: [{ + eventID: 'shardId-000000000001:12345', + kinesis: { + approximateArrivalTimestamp: Date.now() / 1000, + sequenceNumber: '12345', + data: base64Data + } + }] + }; + + dynamodbUpdateMultiStub.callsFake((tasks, callback) => { + callback(new Error('DynamoDB error')); + }); + + kinesisProcessor.handler2(mockEvent, {}, (err) => { + expect(err).to.equal('Cannot write event locations to dynamoDB'); + done(); + }); + }); + + it('should process events through pipeline and accumulate stats', (done) => { + // Create event that will flow through the pipeline + const eventData = JSON.stringify({ + id: 'checkpoint-bot', + event: 'checkpoint-queue', + payload: { data: 'test' } + }); + const gzippedData = zlib.gzipSync(eventData); + const base64Data = gzippedData.toString('base64'); + + const mockEvent = { + Records: [{ + eventID: 'shardId-000000000001:12345', + kinesis: { + approximateArrivalTimestamp: Date.now() / 1000, + sequenceNumber: '12345', + data: base64Data + } + }] + }; + + dynamodbUpdateMultiStub.callsFake((tasks, callback) => { + // Verify event update tasks are created + expect(tasks.length).to.be.greaterThan(0); + callback(null); + }); + + // Mock the checkpoint update call + dynamodbDocClientUpdateStub.callsFake((params, callback) => { + if (callback) { + callback(null, { Attributes: {} }); + } + return { promise: () => Promise.resolve({ Attributes: {} }) }; + }); + + kinesisProcessor.handler2(mockEvent, {}, (err, result) => { + expect(err).to.be.null; + done(); + }); + }); + + it('should handle checkpoint update errors gracefully', (done) => { + const eventData = JSON.stringify({ + id: 'test-bot', + event: 'test-queue', + payload: { data: 'test' } + }); + const gzippedData = zlib.gzipSync(eventData); + const base64Data = gzippedData.toString('base64'); + + const mockEvent = { + Records: [{ + eventID: 'shardId-000000000001:12345', + kinesis: { + approximateArrivalTimestamp: Date.now() / 1000, + sequenceNumber: '12345', + data: base64Data + } + }] + }; + + dynamodbUpdateMultiStub.callsFake((tasks, callback) => { + callback(null); + }); + + // Mock checkpoint update to fail + dynamodbDocClientUpdateStub.callsFake((params, callback) => { + if (callback) { + callback(new Error('Checkpoint error')); + } + return { promise: () => Promise.reject(new Error('Checkpoint error')) }; + }); + + kinesisProcessor.handler2(mockEvent, {}, (err, result) => { + // Should still succeed because checkpoint errors are logged but don't fail + expect(err).to.be.null; + done(); + }); + }); + }); +}); diff --git a/bots/leo-monitor/test/leo-monitor.test.js b/bots/leo-monitor/test/leo-monitor.test.js new file mode 100644 index 0000000..b3192b2 --- /dev/null +++ b/bots/leo-monitor/test/leo-monitor.test.js @@ -0,0 +1,308 @@ +'use strict'; + +const sinon = require('sinon'); +const { expect } = require('chai'); +const proxyquire = require('proxyquire').noCallThru(); + +describe("leo-monitor", () => { + let leoMonitor; + let loadStub; + let loaderWriteStub; + let loaderEndStub; + let awsConverterStub; + + beforeEach(function () { + loaderWriteStub = sinon.stub(); + loaderEndStub = sinon.stub(); + + loadStub = sinon.stub().returns({ + write: loaderWriteStub, + end: loaderEndStub + }); + + awsConverterStub = { + unmarshall: sinon.stub().callsFake((item) => item) + }; + + const leoSdk = { + load: loadStub, + '@global': true + }; + + const aws = { + DynamoDB: { + Converter: awsConverterStub + }, + '@global': true + }; + + process.env.SHARD_HASH_KEY = 'test-hash-key'; + + leoMonitor = proxyquire('../', { + 'leo-sdk': leoSdk, + 'aws-sdk': aws + }); + }); + + afterEach(function () { + sinon.restore(); + delete process.env.SHARD_HASH_KEY; + }); + + describe("handler", () => { + it('should create loader with correct parameters', (done) => { + loaderEndStub.callsFake((callback) => callback(null)); + + const event = { + Records: [{ + dynamodb: { + ApproximateCreationDateTime: Date.now() / 1000 + } + }] + }; + + leoMonitor.handler(event, {}, () => { + expect(loadStub.calledWith('leo_cron_monitor', 'monitor')).to.be.true; + done(); + }); + }); + + it('should skip leo_cron_monitor bot itself', (done) => { + loaderEndStub.callsFake((callback) => callback(null)); + + awsConverterStub.unmarshall.returns({ + id: 'leo_cron_monitor' + }); + + const event = { + Records: [{ + dynamodb: { + ApproximateCreationDateTime: Date.now() / 1000, + NewImage: { id: 'leo_cron_monitor' } + } + }] + }; + + leoMonitor.handler(event, {}, () => { + expect(loaderWriteStub.called).to.be.false; + done(); + }); + }); + + it('should skip bots with ignoreMonitor flag', (done) => { + loaderEndStub.callsFake((callback) => callback(null)); + + awsConverterStub.unmarshall.returns({ + id: 'some-bot', + ignoreMonitor: true + }); + + const event = { + Records: [{ + dynamodb: { + ApproximateCreationDateTime: Date.now() / 1000, + NewImage: { id: 'some-bot', ignoreMonitor: true } + } + }] + }; + + leoMonitor.handler(event, {}, () => { + expect(loaderWriteStub.called).to.be.false; + done(); + }); + }); + + it('should write completed event when instance completes', (done) => { + loaderEndStub.callsFake((callback) => callback(null)); + + awsConverterStub.unmarshall + .onFirstCall().returns({ + id: 'test-bot', + instances: { + 'instance1': { + completedTime: 1609459300000, + status: 'success' + } + } + }) + .onSecondCall().returns({ + id: 'test-bot', + instances: { + 'instance1': { + invokeTime: 1609459200000 + } + } + }); + + const event = { + Records: [{ + dynamodb: { + ApproximateCreationDateTime: 1609459300, + NewImage: { id: 'test-bot' }, + OldImage: { id: 'test-bot' } + } + }] + }; + + leoMonitor.handler(event, {}, () => { + expect(loaderWriteStub.calledOnce).to.be.true; + const writeArg = loaderWriteStub.getCall(0).args[0]; + expect(writeArg.type).to.equal('completed'); + expect(writeArg.id).to.equal('test-bot'); + expect(writeArg.is_error).to.be.false; + done(); + }); + }); + + it('should write started event when instance starts', (done) => { + loaderEndStub.callsFake((callback) => callback(null)); + + awsConverterStub.unmarshall + .onFirstCall().returns({ + id: 'test-bot', + instances: { + 'instance1': { + invokeTime: 1609459200000 + } + } + }) + .onSecondCall().returns({ + id: 'test-bot', + instances: {} + }); + + const event = { + Records: [{ + dynamodb: { + ApproximateCreationDateTime: 1609459300, + NewImage: { id: 'test-bot' }, + OldImage: { id: 'test-bot' } + } + }] + }; + + leoMonitor.handler(event, {}, () => { + expect(loaderWriteStub.calledOnce).to.be.true; + const writeArg = loaderWriteStub.getCall(0).args[0]; + expect(writeArg.type).to.equal('started'); + expect(writeArg.id).to.equal('test-bot'); + done(); + }); + }); + + it('should write read checkpoint events', (done) => { + loaderEndStub.callsFake((callback) => callback(null)); + + awsConverterStub.unmarshall + .onFirstCall().returns({ + id: 'test-bot', + checkpoints: { + read: { + 'test-queue': { + checkpoint: 'z/2023/01/01/00/00/12345', + records: 100 + } + } + } + }) + .onSecondCall().returns({ + id: 'test-bot', + checkpoints: { + read: { + 'test-queue': { + checkpoint: 'z/2023/01/01/00/00/12340' + } + } + } + }); + + const event = { + Records: [{ + dynamodb: { + ApproximateCreationDateTime: 1609459300, + NewImage: { id: 'test-bot' }, + OldImage: { id: 'test-bot' } + } + }] + }; + + leoMonitor.handler(event, {}, () => { + expect(loaderWriteStub.calledOnce).to.be.true; + const writeArg = loaderWriteStub.getCall(0).args[0]; + expect(writeArg.type).to.equal('read'); + expect(writeArg.from).to.equal('test-queue'); + expect(writeArg.units).to.equal(100); + done(); + }); + }); + + it('should write write checkpoint events', (done) => { + loaderEndStub.callsFake((callback) => callback(null)); + + awsConverterStub.unmarshall + .onFirstCall().returns({ + id: 'test-bot', + checkpoints: { + write: { + 'output-queue': { + checkpoint: 'z/2023/01/01/00/00/54321', + records: 50 + } + } + } + }) + .onSecondCall().returns({ + id: 'test-bot', + checkpoints: { + write: { + 'output-queue': { + checkpoint: 'z/2023/01/01/00/00/54320' + } + } + } + }); + + const event = { + Records: [{ + dynamodb: { + ApproximateCreationDateTime: 1609459300, + NewImage: { id: 'test-bot' }, + OldImage: { id: 'test-bot' } + } + }] + }; + + leoMonitor.handler(event, {}, () => { + expect(loaderWriteStub.calledOnce).to.be.true; + const writeArg = loaderWriteStub.getCall(0).args[0]; + expect(writeArg.type).to.equal('write'); + expect(writeArg.to).to.equal('output-queue'); + expect(writeArg.units).to.equal(50); + done(); + }); + }); + + it('should handle records without instances', (done) => { + loaderEndStub.callsFake((callback) => callback(null)); + + awsConverterStub.unmarshall + .onFirstCall().returns({ id: 'test-bot' }) + .onSecondCall().returns({ id: 'test-bot' }); + + const event = { + Records: [{ + dynamodb: { + ApproximateCreationDateTime: 1609459300, + NewImage: { id: 'test-bot' }, + OldImage: { id: 'test-bot' } + } + }] + }; + + leoMonitor.handler(event, {}, () => { + expect(loaderWriteStub.called).to.be.false; + done(); + }); + }); + }); +}); diff --git a/bots/s3-load-trigger/test/s3-load-trigger.test.js b/bots/s3-load-trigger/test/s3-load-trigger.test.js new file mode 100644 index 0000000..a5a51e2 --- /dev/null +++ b/bots/s3-load-trigger/test/s3-load-trigger.test.js @@ -0,0 +1,252 @@ +'use strict'; + +const sinon = require('sinon'); +const { expect } = require('chai'); +const proxyquire = require('proxyquire').noCallThru(); + +describe("s3-load-trigger", () => { + let s3LoadTrigger; + let s3ListObjectsStub; + let dynamodbGetSettingStub; + let dynamodbSaveSettingStub; + let loadStub; + let streamWriteStub; + let streamEndStub; + + const mockBucket = 'test-bucket'; + + beforeEach(function () { + s3ListObjectsStub = sinon.stub(); + dynamodbGetSettingStub = sinon.stub(); + dynamodbSaveSettingStub = sinon.stub(); + streamWriteStub = sinon.stub(); + streamEndStub = sinon.stub(); + + loadStub = sinon.stub().returns({ + write: streamWriteStub, + end: streamEndStub + }); + + const leoSdk = { + configuration: { + resources: { + LeoKinesisStream: 'kinesis-stream', + LeoS3: mockBucket, + LeoFirehoseStream: 'firehose-stream' + }, + update: sinon.stub() + }, + aws: { + s3: { + listObjectsV2: s3ListObjectsStub + }, + dynamodb: { + getSetting: dynamodbGetSettingStub, + saveSetting: dynamodbSaveSettingStub + } + }, + load: loadStub, + '@global': true + }; + + s3LoadTrigger = proxyquire('../', { + 'leo-sdk': leoSdk + }); + }); + + afterEach(function () { + sinon.restore(); + }); + + describe("handler", () => { + it('should load files from S3 and write to stream', (done) => { + dynamodbGetSettingStub.callsFake((id, callback) => { + callback(null, { value: 'firehose/previous-file' }); + }); + + s3ListObjectsStub.callsFake((params, callback) => { + callback(null, { + Contents: [ + { Key: 'firehose/file1.gz' }, + { Key: 'firehose/file2.gz' } + ] + }); + }); + + streamEndStub.callsFake((callback) => callback(null)); + + dynamodbSaveSettingStub.callsFake((id, value, callback) => { + callback(null); + }); + + const event = {}; + + s3LoadTrigger.handler(event, {}, (err) => { + expect(err).to.be.undefined; + expect(streamWriteStub.calledOnce).to.be.true; + + const writeArg = streamWriteStub.getCall(0).args[0]; + expect(writeArg.payload.command).to.equal('load'); + expect(writeArg.payload.files).to.have.length(2); + expect(writeArg.payload.files[0]).to.deep.equal({ + bucket: mockBucket, + key: 'firehose/file1.gz' + }); + done(); + }); + }); + + it('should save the last key position after processing', (done) => { + dynamodbGetSettingStub.callsFake((id, callback) => { + callback(null, { value: '' }); + }); + + s3ListObjectsStub.callsFake((params, callback) => { + callback(null, { + Contents: [ + { Key: 'firehose/file1.gz' }, + { Key: 'firehose/last-file.gz' } + ] + }); + }); + + streamEndStub.callsFake((callback) => callback(null)); + + dynamodbSaveSettingStub.callsFake((id, value, callback) => { + expect(value).to.equal('firehose/last-file.gz'); + callback(null); + }); + + s3LoadTrigger.handler({}, {}, (err) => { + expect(err).to.be.undefined; + expect(dynamodbSaveSettingStub.calledOnce).to.be.true; + done(); + }); + }); + + it('should do nothing when no new files', (done) => { + dynamodbGetSettingStub.callsFake((id, callback) => { + callback(null, { value: 'firehose/current-position' }); + }); + + s3ListObjectsStub.callsFake((params, callback) => { + callback(null, { + Contents: [] + }); + }); + + s3LoadTrigger.handler({}, {}, (err) => { + expect(err).to.be.undefined; + expect(streamWriteStub.called).to.be.false; + expect(dynamodbSaveSettingStub.called).to.be.false; + done(); + }); + }); + + it('should handle getSetting error', (done) => { + dynamodbGetSettingStub.callsFake((id, callback) => { + callback(new Error('DynamoDB error')); + }); + + s3LoadTrigger.handler({}, {}, (err) => { + expect(err).to.be.instanceof(Error); + expect(err.message).to.equal('DynamoDB error'); + done(); + }); + }); + + it('should handle S3 listObjects error', (done) => { + dynamodbGetSettingStub.callsFake((id, callback) => { + callback(null, { value: '' }); + }); + + s3ListObjectsStub.callsFake((params, callback) => { + callback(new Error('S3 error')); + }); + + s3LoadTrigger.handler({}, {}, (err) => { + expect(err).to.be.instanceof(Error); + expect(err.message).to.equal('S3 error'); + done(); + }); + }); + + it('should handle stream end error', (done) => { + dynamodbGetSettingStub.callsFake((id, callback) => { + callback(null, { value: '' }); + }); + + s3ListObjectsStub.callsFake((params, callback) => { + callback(null, { + Contents: [{ Key: 'firehose/file1.gz' }] + }); + }); + + streamEndStub.callsFake((callback) => callback(new Error('Stream error'))); + + s3LoadTrigger.handler({}, {}, (err) => { + expect(err).to.be.instanceof(Error); + expect(err.message).to.equal('Stream error'); + done(); + }); + }); + + it('should use empty string as position when setting not found', (done) => { + dynamodbGetSettingStub.callsFake((id, callback) => { + callback(null, null); + }); + + s3ListObjectsStub.callsFake((params, callback) => { + expect(params.StartAfter).to.equal(''); + callback(null, { Contents: [] }); + }); + + s3LoadTrigger.handler({}, {}, (err) => { + expect(err).to.be.undefined; + done(); + }); + }); + + it('should use correct S3 list parameters', (done) => { + dynamodbGetSettingStub.callsFake((id, callback) => { + callback(null, { value: 'firehose/start' }); + }); + + s3ListObjectsStub.callsFake((params, callback) => { + expect(params.Bucket).to.equal(mockBucket); + expect(params.StartAfter).to.equal('firehose/start'); + expect(params.MaxKeys).to.equal(100); + expect(params.Prefix).to.equal('firehose/'); + callback(null, { Contents: [] }); + }); + + s3LoadTrigger.handler({}, {}, () => { + done(); + }); + }); + + it('should create loader with correct parameters', (done) => { + dynamodbGetSettingStub.callsFake((id, callback) => { + callback(null, { value: '' }); + }); + + s3ListObjectsStub.callsFake((params, callback) => { + callback(null, { + Contents: [{ Key: 'firehose/file1.gz' }] + }); + }); + + streamEndStub.callsFake((callback) => callback(null)); + dynamodbSaveSettingStub.callsFake((id, value, callback) => callback(null)); + + s3LoadTrigger.handler({}, {}, () => { + expect(loadStub.calledWith( + 'Leo_core_s3_load_trigger', + 'commands.s3_bus_load', + { debug: true } + )).to.be.true; + done(); + }); + }); + }); +}); diff --git a/bots/source-queue-replicator/test/source-queue-replicator.test.js b/bots/source-queue-replicator/test/source-queue-replicator.test.js new file mode 100644 index 0000000..a764593 --- /dev/null +++ b/bots/source-queue-replicator/test/source-queue-replicator.test.js @@ -0,0 +1,262 @@ +'use strict'; + +const sinon = require('sinon'); +const { expect } = require('chai'); +const proxyquire = require('proxyquire').noCallThru(); + +describe("source-queue-replicator", () => { + let sourceQueueReplicator; + let stsAssumeRoleStub; + let stsCredentialsFromStub; + let getLeoConfigStub; + let readStub; + let loadStub; + let pipeStub; + let statsStub; + let checkpointStub; + + beforeEach(function () { + stsAssumeRoleStub = sinon.stub(); + stsCredentialsFromStub = sinon.stub(); + getLeoConfigStub = sinon.stub(); + readStub = sinon.stub(); + loadStub = sinon.stub(); + pipeStub = sinon.stub(); + checkpointStub = sinon.stub(); + + statsStub = sinon.stub().returns({ + checkpoint: checkpointStub + }); + + const AWS = { + STS: class MockSTS { + constructor() { + this.assumeRole = stsAssumeRoleStub; + this.credentialsFrom = stsCredentialsFromStub; + } + }, + '@global': true + }; + + const leoSdk = { + read: readStub, + streams: { + stats: statsStub, + pipe: pipeStub + }, + '@global': true + }; + + // Mock leo-sdk as a callable function that returns sdk with load + const leoSdkCallable = function(config) { + return { load: loadStub }; + }; + leoSdkCallable.read = readStub; + leoSdkCallable.streams = { stats: statsStub, pipe: pipeStub }; + leoSdkCallable['@global'] = true; + + const cronWrapper = (handler) => handler; + cronWrapper['@global'] = true; + + const loggerMock = { + info: sinon.stub(), + error: sinon.stub() + }; + + sourceQueueReplicator = proxyquire('../', { + 'aws-sdk': AWS, + 'leo-sdk': leoSdkCallable, + 'leo-sdk/wrappers/cron': cronWrapper, + '../../lib/getLeoConfigFromBusStack': getLeoConfigStub, + 'leo-logger': loggerMock + }); + }); + + afterEach(function () { + sinon.restore(); + }); + + describe("handler", () => { + const mockEvent = { + botId: 'test-replicator', + sourceQueue: 'source-queue', + destinationQueue: 'dest-queue', + destinationBusStack: 'dest-stack', + destinationLeoBotRoleArn: 'arn:aws:iam::123456789:role/dest-role' + }; + + const mockContext = { + getRemainingTimeInMillis: () => 300000 + }; + + it('should handle STS assume role errors', (done) => { + stsAssumeRoleStub.callsFake((params, callback) => { + callback(new Error('Access denied')); + }); + + sourceQueueReplicator.handler(mockEvent, mockContext, (err) => { + expect(err).to.be.instanceof(Error); + expect(err.message).to.equal('Access denied'); + done(); + }); + }); + + it('should call assumeRole with correct parameters', (done) => { + stsAssumeRoleStub.callsFake((params, callback) => { + expect(params.RoleArn).to.equal(mockEvent.destinationLeoBotRoleArn); + expect(params.RoleSessionName).to.equal('SourceQueueReplicator'); + expect(params.DurationSeconds).to.equal(900); + callback(new Error('Stop here')); + }); + + sourceQueueReplicator.handler(mockEvent, mockContext, (err) => { + expect(stsAssumeRoleStub.calledOnce).to.be.true; + done(); + }); + }); + + it('should handle getLeoConfig errors', (done) => { + const mockCredentials = { accessKeyId: 'test' }; + + stsAssumeRoleStub.callsFake((params, callback) => { + callback(null, { Credentials: mockCredentials }); + }); + + stsCredentialsFromStub.returns(mockCredentials); + + getLeoConfigStub.rejects(new Error('Stack not found')); + + sourceQueueReplicator.handler(mockEvent, mockContext, (err) => { + expect(err).to.be.instanceof(Error); + expect(err.message).to.equal('Stack not found'); + done(); + }); + }); + + it('should get credentials from STS response', (done) => { + const mockCredentials = { accessKeyId: 'test', secretAccessKey: 'secret' }; + + stsAssumeRoleStub.callsFake((params, callback) => { + callback(null, { Credentials: mockCredentials }); + }); + + stsCredentialsFromStub.callsFake((data) => { + expect(data.Credentials).to.equal(mockCredentials); + return mockCredentials; + }); + + getLeoConfigStub.rejects(new Error('Stop after credentials')); + + sourceQueueReplicator.handler(mockEvent, mockContext, (err) => { + expect(stsCredentialsFromStub.calledOnce).to.be.true; + done(); + }); + }); + + it('should call getLeoConfig with stack name and credentials', (done) => { + const mockCredentials = { accessKeyId: 'test' }; + + stsAssumeRoleStub.callsFake((params, callback) => { + callback(null, { Credentials: {} }); + }); + + stsCredentialsFromStub.returns(mockCredentials); + + getLeoConfigStub.callsFake((stackName, credentials) => { + expect(stackName).to.equal(mockEvent.destinationBusStack); + expect(credentials).to.equal(mockCredentials); + return Promise.reject(new Error('Stop here')); + }); + + sourceQueueReplicator.handler(mockEvent, mockContext, (err) => { + expect(getLeoConfigStub.calledOnce).to.be.true; + done(); + }); + }); + + it('should replicate data from source to destination on success', (done) => { + const mockCredentials = { accessKeyId: 'test' }; + + stsAssumeRoleStub.callsFake((params, callback) => { + callback(null, { Credentials: {} }); + }); + + stsCredentialsFromStub.returns(mockCredentials); + getLeoConfigStub.resolves({ resources: {} }); + + const mockReadStream = { pipe: sinon.stub() }; + const mockLoadStream = {}; + + readStub.returns(mockReadStream); + loadStub.returns(mockLoadStream); + + pipeStub.callsFake((...args) => { + const callback = args[args.length - 1]; + callback(null); + }); + + checkpointStub.callsFake((callback) => callback(null)); + + sourceQueueReplicator.handler(mockEvent, mockContext, (err) => { + expect(err).to.be.null; + expect(readStub.calledWith(mockEvent.botId, mockEvent.sourceQueue)).to.be.true; + expect(loadStub.calledWith(mockEvent.botId, mockEvent.destinationQueue)).to.be.true; + done(); + }); + }); + + it('should handle pipe errors', (done) => { + const mockCredentials = { accessKeyId: 'test' }; + + stsAssumeRoleStub.callsFake((params, callback) => { + callback(null, { Credentials: {} }); + }); + + stsCredentialsFromStub.returns(mockCredentials); + getLeoConfigStub.resolves({ resources: {} }); + + readStub.returns({}); + loadStub.returns({}); + + pipeStub.callsFake((...args) => { + const callback = args[args.length - 1]; + callback(new Error('Pipe failed')); + }); + + sourceQueueReplicator.handler(mockEvent, mockContext, (err) => { + expect(err).to.be.instanceof(Error); + expect(err.message).to.equal('Pipe failed'); + done(); + }); + }); + + it('should checkpoint after successful replication', (done) => { + const mockCredentials = { accessKeyId: 'test' }; + + stsAssumeRoleStub.callsFake((params, callback) => { + callback(null, { Credentials: {} }); + }); + + stsCredentialsFromStub.returns(mockCredentials); + getLeoConfigStub.resolves({ resources: {} }); + + readStub.returns({}); + loadStub.returns({}); + + pipeStub.callsFake((...args) => { + const callback = args[args.length - 1]; + callback(null); + }); + + checkpointStub.callsFake((callback) => { + callback(null); + }); + + sourceQueueReplicator.handler(mockEvent, mockContext, (err) => { + expect(err).to.be.null; + expect(checkpointStub.calledOnce).to.be.true; + done(); + }); + }); + }); +}); diff --git a/package-lock.json b/package-lock.json index 14dce4b..144c33c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,14 +1,459 @@ { "name": "leo-bus", "version": "3.3.1", - "lockfileVersion": 1, + "lockfileVersion": 3, "requires": true, - "dependencies": { - "@fast-csv/format": { + "packages": { + "": { + "name": "leo-bus", + "version": "3.3.1", + "license": "Apache-2.0", + "dependencies": { + "deep-diff": "1.0.2", + "leo-cron": "^2.0.2-beta", + "leo-logger": "1.0.1", + "leo-sdk": "^6.0.17-rc", + "moment": "2.24.0" + }, + "devDependencies": { + "ajv": "8.11.0", + "ajv-formats": "2.1.1", + "aws-sdk": "^2.466.0", + "babel-preset-env": "^1.7.0", + "chai": "^4.2.0", + "extend": "^3.0.2", + "is-semver": "1.0.11", + "leo-aws": "^2.0.2", + "mocha": "^6.1.4", + "nyc": "^17.1.0", + "proxyquire": "^2.1.1", + "sinon": "^7.3.2" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.28.6.tgz", + "integrity": "sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/code-frame/node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/compat-data": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.6.tgz", + "integrity": "sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.6.tgz", + "integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/generator": "^7.28.6", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/core/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@babel/core/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.6.tgz", + "integrity": "sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/generator/node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/browserslist": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", + "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.6.tgz", + "integrity": "sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.6" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.6.tgz", + "integrity": "sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/generator": "^7.28.6", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.6", + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@babel/traverse/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/types": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.6.tgz", + "integrity": "sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@fast-csv/format": { "version": "4.3.5", "resolved": "https://registry.npmjs.org/@fast-csv/format/-/format-4.3.5.tgz", "integrity": "sha512-8iRn6QF3I8Ak78lNAa+Gdl5MJJBM5vRHivFtMRUWINdevNo00K7OXxS2PshawLKTejVwieIlPmK5YlLu6w4u8A==", - "requires": { + "dependencies": { "@types/node": "^14.0.1", "lodash.escaperegexp": "^4.1.2", "lodash.isboolean": "^3.0.3", @@ -17,11 +462,11 @@ "lodash.isnil": "^4.0.0" } }, - "@fast-csv/parse": { + "node_modules/@fast-csv/parse": { "version": "4.3.6", "resolved": "https://registry.npmjs.org/@fast-csv/parse/-/parse-4.3.6.tgz", "integrity": "sha512-uRsLYksqpbDmWaSmzvJcuApSEe38+6NQZBUsuAyMZKqHxH0g1wcJgsKUvN3WC8tewaqFjBMMGrkHmC+T7k8LvA==", - "requires": { + "dependencies": { "@types/node": "^14.0.1", "lodash.escaperegexp": "^4.1.2", "lodash.groupby": "^4.6.0", @@ -31,144 +476,336 @@ "lodash.uniq": "^4.5.0" } }, - "@sinonjs/commons": { + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@sinonjs/commons": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.7.2.tgz", "integrity": "sha512-+DUO6pnp3udV/v2VfUWgaY5BIE1IfT7lLfeDzPVeMT1XKkaAp9LgSI9x5RtrFQoZ9Oi0PgXQQHPaoKu7dCjVxw==", "dev": true, - "requires": { + "dependencies": { "type-detect": "4.0.8" } }, - "@sinonjs/formatio": { + "node_modules/@sinonjs/formatio": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-3.2.2.tgz", "integrity": "sha512-B8SEsgd8gArBLMD6zpRw3juQ2FVSsmdd7qlevyDqzS9WTCtvF55/gAL+h6gue8ZvPYcdiPdvueM/qm//9XzyTQ==", "dev": true, - "requires": { + "dependencies": { "@sinonjs/commons": "^1", "@sinonjs/samsam": "^3.1.0" } }, - "@sinonjs/samsam": { + "node_modules/@sinonjs/samsam": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-3.3.3.tgz", "integrity": "sha512-bKCMKZvWIjYD0BLGnNrxVuw4dkWCYsLqFOUWw8VgKF/+5Y+mE7LfHWPIYoDXowH+3a9LsWDMo0uAP8YDosPvHQ==", "dev": true, - "requires": { + "dependencies": { "@sinonjs/commons": "^1.3.0", "array-from": "^2.1.1", "lodash": "^4.17.15" } }, - "@sinonjs/text-encoding": { + "node_modules/@sinonjs/text-encoding": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", "dev": true }, - "@types/node": { + "node_modules/@types/node": { "version": "14.18.53", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.53.tgz", "integrity": "sha512-soGmOpVBUq+gaBMwom1M+krC/NNbWlosh4AtGA03SyWNDiqSKtwp7OulO1M6+mg8YkHMvJ/y0AkCeO8d1hNb7A==" }, - "ajv": { + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { "version": "8.11.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", "dev": true, - "requires": { + "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2", "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "ajv-formats": { + "node_modules/ajv-formats": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", "dev": true, - "requires": { + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } } }, - "ansi-colors": { + "node_modules/ansi-colors": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "ansi-regex": { + "node_modules/ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "ansi-styles": { + "node_modules/ansi-styles": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/append-transform": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", + "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "default-require-extensions": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", + "dev": true, + "license": "MIT" }, - "argparse": { + "node_modules/argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, - "requires": { + "dependencies": { "sprintf-js": "~1.0.2" } }, - "arguments-extended": { + "node_modules/arguments-extended": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/arguments-extended/-/arguments-extended-0.0.3.tgz", "integrity": "sha1-YQfkkX0OtvCk3WYyD8Fa/HLvSUY=", - "requires": { + "dependencies": { "extended": "~0.0.3", "is-extended": "~0.0.8" } }, - "array-extended": { + "node_modules/array-extended": { "version": "0.0.11", "resolved": "https://registry.npmjs.org/array-extended/-/array-extended-0.0.11.tgz", "integrity": "sha1-1xRK50jek8pybxIQCdv/FibRZL0=", - "requires": { + "dependencies": { "arguments-extended": "~0.0.3", "extended": "~0.0.3", "is-extended": "~0.0.3" } }, - "array-from": { + "node_modules/array-from": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/array-from/-/array-from-2.1.1.tgz", "integrity": "sha1-z+nYwmYoudxa7MYqn12PHzUsEZU=", "dev": true }, - "assertion-error": { + "node_modules/assertion-error": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "dev": true + "dev": true, + "engines": { + "node": "*" + } }, - "async": { + "node_modules/async": { "version": "2.6.4", "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", - "requires": { + "dependencies": { "lodash": "^4.17.14" } }, - "available-typed-arrays": { + "node_modules/available-typed-arrays": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==" + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "aws-sdk": { + "node_modules/aws-sdk": { "version": "2.1361.0", "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1361.0.tgz", "integrity": "sha512-0tuFnHCzK2YExbYkXR2sFSMW415GwlPtghBtC1VQto0aJxcSA/Ez0Bqya4R5dY8htbPr5Y2OKCG5QD3hAy+eew==", - "requires": { + "deprecated": "The AWS SDK for JavaScript (v2) has reached end-of-support, and no longer receives updates. Please migrate your code to use AWS SDK for JavaScript (v3). More info https://a.co/cUPnyil", + "dependencies": { "buffer": "4.9.2", "events": "1.1.1", "ieee754": "1.1.13", @@ -180,77 +817,81 @@ "uuid": "8.0.0", "xml2js": "0.5.0" }, - "dependencies": { - "uuid": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", - "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==" - } + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/aws-sdk/node_modules/uuid": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", + "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==", + "bin": { + "uuid": "dist/bin/uuid" } }, - "babel-code-frame": { + "node_modules/babel-code-frame": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", "dev": true, - "requires": { + "dependencies": { "chalk": "^1.1.3", "esutils": "^2.0.2", "js-tokens": "^3.0.2" } }, - "babel-helper-builder-binary-assignment-operator-visitor": { + "node_modules/babel-helper-builder-binary-assignment-operator-visitor": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz", "integrity": "sha1-zORReto1b0IgvK6KAsKzRvmlZmQ=", "dev": true, - "requires": { + "dependencies": { "babel-helper-explode-assignable-expression": "^6.24.1", "babel-runtime": "^6.22.0", "babel-types": "^6.24.1" } }, - "babel-helper-call-delegate": { + "node_modules/babel-helper-call-delegate": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz", "integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=", "dev": true, - "requires": { + "dependencies": { "babel-helper-hoist-variables": "^6.24.1", "babel-runtime": "^6.22.0", "babel-traverse": "^6.24.1", "babel-types": "^6.24.1" } }, - "babel-helper-define-map": { + "node_modules/babel-helper-define-map": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz", "integrity": "sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=", "dev": true, - "requires": { + "dependencies": { "babel-helper-function-name": "^6.24.1", "babel-runtime": "^6.26.0", "babel-types": "^6.26.0", "lodash": "^4.17.4" } }, - "babel-helper-explode-assignable-expression": { + "node_modules/babel-helper-explode-assignable-expression": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz", "integrity": "sha1-8luCz33BBDPFX3BZLVdGQArCLKo=", "dev": true, - "requires": { + "dependencies": { "babel-runtime": "^6.22.0", "babel-traverse": "^6.24.1", "babel-types": "^6.24.1" } }, - "babel-helper-function-name": { + "node_modules/babel-helper-function-name": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=", "dev": true, - "requires": { + "dependencies": { "babel-helper-get-function-arity": "^6.24.1", "babel-runtime": "^6.22.0", "babel-template": "^6.24.1", @@ -258,53 +899,53 @@ "babel-types": "^6.24.1" } }, - "babel-helper-get-function-arity": { + "node_modules/babel-helper-get-function-arity": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz", "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=", "dev": true, - "requires": { + "dependencies": { "babel-runtime": "^6.22.0", "babel-types": "^6.24.1" } }, - "babel-helper-hoist-variables": { + "node_modules/babel-helper-hoist-variables": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz", "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=", "dev": true, - "requires": { + "dependencies": { "babel-runtime": "^6.22.0", "babel-types": "^6.24.1" } }, - "babel-helper-optimise-call-expression": { + "node_modules/babel-helper-optimise-call-expression": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz", "integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=", "dev": true, - "requires": { + "dependencies": { "babel-runtime": "^6.22.0", "babel-types": "^6.24.1" } }, - "babel-helper-regex": { + "node_modules/babel-helper-regex": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz", "integrity": "sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI=", "dev": true, - "requires": { + "dependencies": { "babel-runtime": "^6.26.0", "babel-types": "^6.26.0", "lodash": "^4.17.4" } }, - "babel-helper-remap-async-to-generator": { + "node_modules/babel-helper-remap-async-to-generator": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz", "integrity": "sha1-XsWBgnrXI/7N04HxySg5BnbkVRs=", "dev": true, - "requires": { + "dependencies": { "babel-helper-function-name": "^6.24.1", "babel-runtime": "^6.22.0", "babel-template": "^6.24.1", @@ -312,12 +953,12 @@ "babel-types": "^6.24.1" } }, - "babel-helper-replace-supers": { + "node_modules/babel-helper-replace-supers": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz", "integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=", "dev": true, - "requires": { + "dependencies": { "babel-helper-optimise-call-expression": "^6.24.1", "babel-messages": "^6.23.0", "babel-runtime": "^6.22.0", @@ -326,77 +967,77 @@ "babel-types": "^6.24.1" } }, - "babel-messages": { + "node_modules/babel-messages": { "version": "6.23.0", "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", "dev": true, - "requires": { + "dependencies": { "babel-runtime": "^6.22.0" } }, - "babel-plugin-check-es2015-constants": { + "node_modules/babel-plugin-check-es2015-constants": { "version": "6.22.0", "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz", "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=", "dev": true, - "requires": { + "dependencies": { "babel-runtime": "^6.22.0" } }, - "babel-plugin-syntax-async-functions": { + "node_modules/babel-plugin-syntax-async-functions": { "version": "6.13.0", "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz", "integrity": "sha1-ytnK0RkbWtY0vzCuCHI5HgZHvpU=", "dev": true }, - "babel-plugin-syntax-exponentiation-operator": { + "node_modules/babel-plugin-syntax-exponentiation-operator": { "version": "6.13.0", "resolved": "https://registry.npmjs.org/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz", "integrity": "sha1-nufoM3KQ2pUoggGmpX9BcDF4MN4=", "dev": true }, - "babel-plugin-syntax-trailing-function-commas": { + "node_modules/babel-plugin-syntax-trailing-function-commas": { "version": "6.22.0", "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz", "integrity": "sha1-ugNgk3+NBuQBgKQ/4NVhb/9TLPM=", "dev": true }, - "babel-plugin-transform-async-to-generator": { + "node_modules/babel-plugin-transform-async-to-generator": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz", "integrity": "sha1-ZTbjeK/2yx1VF6wOQOs+n8jQh2E=", "dev": true, - "requires": { + "dependencies": { "babel-helper-remap-async-to-generator": "^6.24.1", "babel-plugin-syntax-async-functions": "^6.8.0", "babel-runtime": "^6.22.0" } }, - "babel-plugin-transform-es2015-arrow-functions": { + "node_modules/babel-plugin-transform-es2015-arrow-functions": { "version": "6.22.0", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz", "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=", "dev": true, - "requires": { + "dependencies": { "babel-runtime": "^6.22.0" } }, - "babel-plugin-transform-es2015-block-scoped-functions": { + "node_modules/babel-plugin-transform-es2015-block-scoped-functions": { "version": "6.22.0", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz", "integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=", "dev": true, - "requires": { + "dependencies": { "babel-runtime": "^6.22.0" } }, - "babel-plugin-transform-es2015-block-scoping": { + "node_modules/babel-plugin-transform-es2015-block-scoping": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz", "integrity": "sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=", "dev": true, - "requires": { + "dependencies": { "babel-runtime": "^6.26.0", "babel-template": "^6.26.0", "babel-traverse": "^6.26.0", @@ -404,12 +1045,12 @@ "lodash": "^4.17.4" } }, - "babel-plugin-transform-es2015-classes": { + "node_modules/babel-plugin-transform-es2015-classes": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz", "integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=", "dev": true, - "requires": { + "dependencies": { "babel-helper-define-map": "^6.24.1", "babel-helper-function-name": "^6.24.1", "babel-helper-optimise-call-expression": "^6.24.1", @@ -421,125 +1062,125 @@ "babel-types": "^6.24.1" } }, - "babel-plugin-transform-es2015-computed-properties": { + "node_modules/babel-plugin-transform-es2015-computed-properties": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz", "integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=", "dev": true, - "requires": { + "dependencies": { "babel-runtime": "^6.22.0", "babel-template": "^6.24.1" } }, - "babel-plugin-transform-es2015-destructuring": { + "node_modules/babel-plugin-transform-es2015-destructuring": { "version": "6.23.0", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz", "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=", "dev": true, - "requires": { + "dependencies": { "babel-runtime": "^6.22.0" } }, - "babel-plugin-transform-es2015-duplicate-keys": { + "node_modules/babel-plugin-transform-es2015-duplicate-keys": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz", "integrity": "sha1-c+s9MQypaePvnskcU3QabxV2Qj4=", "dev": true, - "requires": { + "dependencies": { "babel-runtime": "^6.22.0", "babel-types": "^6.24.1" } }, - "babel-plugin-transform-es2015-for-of": { + "node_modules/babel-plugin-transform-es2015-for-of": { "version": "6.23.0", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz", "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=", "dev": true, - "requires": { + "dependencies": { "babel-runtime": "^6.22.0" } }, - "babel-plugin-transform-es2015-function-name": { + "node_modules/babel-plugin-transform-es2015-function-name": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz", "integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=", "dev": true, - "requires": { + "dependencies": { "babel-helper-function-name": "^6.24.1", "babel-runtime": "^6.22.0", "babel-types": "^6.24.1" } }, - "babel-plugin-transform-es2015-literals": { + "node_modules/babel-plugin-transform-es2015-literals": { "version": "6.22.0", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz", "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=", "dev": true, - "requires": { + "dependencies": { "babel-runtime": "^6.22.0" } }, - "babel-plugin-transform-es2015-modules-amd": { + "node_modules/babel-plugin-transform-es2015-modules-amd": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz", "integrity": "sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=", "dev": true, - "requires": { + "dependencies": { "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", "babel-runtime": "^6.22.0", "babel-template": "^6.24.1" } }, - "babel-plugin-transform-es2015-modules-commonjs": { + "node_modules/babel-plugin-transform-es2015-modules-commonjs": { "version": "6.26.2", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz", "integrity": "sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q==", "dev": true, - "requires": { + "dependencies": { "babel-plugin-transform-strict-mode": "^6.24.1", "babel-runtime": "^6.26.0", "babel-template": "^6.26.0", "babel-types": "^6.26.0" } }, - "babel-plugin-transform-es2015-modules-systemjs": { + "node_modules/babel-plugin-transform-es2015-modules-systemjs": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz", "integrity": "sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=", "dev": true, - "requires": { + "dependencies": { "babel-helper-hoist-variables": "^6.24.1", "babel-runtime": "^6.22.0", "babel-template": "^6.24.1" } }, - "babel-plugin-transform-es2015-modules-umd": { + "node_modules/babel-plugin-transform-es2015-modules-umd": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz", "integrity": "sha1-rJl+YoXNGO1hdq22B9YCNErThGg=", "dev": true, - "requires": { + "dependencies": { "babel-plugin-transform-es2015-modules-amd": "^6.24.1", "babel-runtime": "^6.22.0", "babel-template": "^6.24.1" } }, - "babel-plugin-transform-es2015-object-super": { + "node_modules/babel-plugin-transform-es2015-object-super": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz", "integrity": "sha1-JM72muIcuDp/hgPa0CH1cusnj40=", "dev": true, - "requires": { + "dependencies": { "babel-helper-replace-supers": "^6.24.1", "babel-runtime": "^6.22.0" } }, - "babel-plugin-transform-es2015-parameters": { + "node_modules/babel-plugin-transform-es2015-parameters": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz", "integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=", "dev": true, - "requires": { + "dependencies": { "babel-helper-call-delegate": "^6.24.1", "babel-helper-get-function-arity": "^6.24.1", "babel-runtime": "^6.22.0", @@ -548,101 +1189,101 @@ "babel-types": "^6.24.1" } }, - "babel-plugin-transform-es2015-shorthand-properties": { + "node_modules/babel-plugin-transform-es2015-shorthand-properties": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz", "integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=", "dev": true, - "requires": { + "dependencies": { "babel-runtime": "^6.22.0", "babel-types": "^6.24.1" } }, - "babel-plugin-transform-es2015-spread": { + "node_modules/babel-plugin-transform-es2015-spread": { "version": "6.22.0", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz", "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=", "dev": true, - "requires": { + "dependencies": { "babel-runtime": "^6.22.0" } }, - "babel-plugin-transform-es2015-sticky-regex": { + "node_modules/babel-plugin-transform-es2015-sticky-regex": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz", "integrity": "sha1-AMHNsaynERLN8M9hJsLta0V8zbw=", "dev": true, - "requires": { + "dependencies": { "babel-helper-regex": "^6.24.1", "babel-runtime": "^6.22.0", "babel-types": "^6.24.1" } }, - "babel-plugin-transform-es2015-template-literals": { + "node_modules/babel-plugin-transform-es2015-template-literals": { "version": "6.22.0", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz", "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=", "dev": true, - "requires": { + "dependencies": { "babel-runtime": "^6.22.0" } }, - "babel-plugin-transform-es2015-typeof-symbol": { + "node_modules/babel-plugin-transform-es2015-typeof-symbol": { "version": "6.23.0", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz", "integrity": "sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=", "dev": true, - "requires": { + "dependencies": { "babel-runtime": "^6.22.0" } }, - "babel-plugin-transform-es2015-unicode-regex": { + "node_modules/babel-plugin-transform-es2015-unicode-regex": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz", "integrity": "sha1-04sS9C6nMj9yk4fxinxa4frrNek=", "dev": true, - "requires": { + "dependencies": { "babel-helper-regex": "^6.24.1", "babel-runtime": "^6.22.0", "regexpu-core": "^2.0.0" } }, - "babel-plugin-transform-exponentiation-operator": { + "node_modules/babel-plugin-transform-exponentiation-operator": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz", "integrity": "sha1-KrDJx/MJj6SJB3cruBP+QejeOg4=", "dev": true, - "requires": { + "dependencies": { "babel-helper-builder-binary-assignment-operator-visitor": "^6.24.1", "babel-plugin-syntax-exponentiation-operator": "^6.8.0", "babel-runtime": "^6.22.0" } }, - "babel-plugin-transform-regenerator": { + "node_modules/babel-plugin-transform-regenerator": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz", "integrity": "sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=", "dev": true, - "requires": { + "dependencies": { "regenerator-transform": "^0.10.0" } }, - "babel-plugin-transform-strict-mode": { + "node_modules/babel-plugin-transform-strict-mode": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=", "dev": true, - "requires": { + "dependencies": { "babel-runtime": "^6.22.0", "babel-types": "^6.24.1" } }, - "babel-preset-env": { + "node_modules/babel-preset-env": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/babel-preset-env/-/babel-preset-env-1.7.0.tgz", "integrity": "sha512-9OR2afuKDneX2/q2EurSftUYM0xGu4O2D9adAhVfADDhrYDaxXV0rBbevVYoY9n6nyX1PmQW/0jtpJvUNr9CHg==", "dev": true, - "requires": { + "dependencies": { "babel-plugin-check-es2015-constants": "^6.22.0", "babel-plugin-syntax-trailing-function-commas": "^6.22.0", "babel-plugin-transform-async-to-generator": "^6.22.0", @@ -675,22 +1316,22 @@ "semver": "^5.3.0" } }, - "babel-runtime": { + "node_modules/babel-runtime": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, - "requires": { + "dependencies": { "core-js": "^2.4.0", "regenerator-runtime": "^0.11.0" } }, - "babel-template": { + "node_modules/babel-template": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", "dev": true, - "requires": { + "dependencies": { "babel-runtime": "^6.26.0", "babel-traverse": "^6.26.0", "babel-types": "^6.26.0", @@ -698,12 +1339,12 @@ "lodash": "^4.17.4" } }, - "babel-traverse": { + "node_modules/babel-traverse": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", "dev": true, - "requires": { + "dependencies": { "babel-code-frame": "^6.26.0", "babel-messages": "^6.23.0", "babel-runtime": "^6.26.0", @@ -715,310 +1356,486 @@ "lodash": "^4.17.4" } }, - "babel-types": { + "node_modules/babel-types": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", "dev": true, - "requires": { + "dependencies": { "babel-runtime": "^6.26.0", "esutils": "^2.0.2", "lodash": "^4.17.4", "to-fast-properties": "^1.0.3" } }, - "babylon": { + "node_modules/babylon": { "version": "6.18.0", "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", - "dev": true + "dev": true, + "bin": { + "babylon": "bin/babylon.js" + } }, - "backoff": { + "node_modules/backoff": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/backoff/-/backoff-2.5.0.tgz", "integrity": "sha1-9hbtqdPktmuMp/ynn2lXIsX44m8=", - "requires": { + "dependencies": { "precond": "0.2" + }, + "engines": { + "node": ">= 0.6" } }, - "balanced-match": { + "node_modules/balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true }, - "base64-js": { + "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, - "big-integer": { + "node_modules/baseline-browser-mapping": { + "version": "2.9.17", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.17.tgz", + "integrity": "sha512-agD0MgJFUP/4nvjqzIB29zRPUuCF7Ge6mEv9s8dHrtYD7QWXRcx75rOADE/d5ah1NI+0vkDl0yorDd5U852IQQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/big-integer": { "version": "1.6.51", "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", - "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==" + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", + "engines": { + "node": ">=0.6" + } }, - "brace-expansion": { + "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "requires": { + "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, - "browser-stdout": { + "node_modules/browser-stdout": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, - "browserslist": { + "node_modules/browserslist": { "version": "3.2.8", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-3.2.8.tgz", "integrity": "sha512-WHVocJYavUwVgVViC0ORikPHQquXwVh939TaelZ4WDqpWgTX/FsGhl/+P4qBUAGcRvtOgDgC+xftNWWp2RUTAQ==", "dev": true, - "requires": { + "dependencies": { "caniuse-lite": "^1.0.30000844", "electron-to-chromium": "^1.3.47" + }, + "bin": { + "browserslist": "cli.js" } }, - "buffer": { + "node_modules/buffer": { "version": "4.9.2", "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", - "requires": { + "dependencies": { "base64-js": "^1.0.2", "ieee754": "^1.1.4", "isarray": "^1.0.0" } }, - "call-bind": { + "node_modules/caching-transform": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", + "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasha": "^5.0.0", + "make-dir": "^3.0.0", + "package-hash": "^4.0.0", + "write-file-atomic": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "requires": { + "dependencies": { "function-bind": "^1.1.1", "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "camelcase": { + "node_modules/camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "caniuse-lite": { - "version": "1.0.30001039", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001039.tgz", - "integrity": "sha512-SezbWCTT34eyFoWHgx8UWso7YtvtM7oosmFoXbCkdC6qJzRfBTeTgE9REtKtiuKXuMwWTZEvdnFNGAyVMorv8Q==", - "dev": true + "node_modules/caniuse-lite": { + "version": "1.0.30001765", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001765.tgz", + "integrity": "sha512-LWcNtSyZrakjECqmpP4qdg0MMGdN368D7X8XvvAqOcqMv0RxnlqVKZl2V6/mBR68oYMxOZPLw/gO7DuisMHUvQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" }, - "chai": { + "node_modules/chai": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", "dev": true, - "requires": { + "dependencies": { "assertion-error": "^1.1.0", "check-error": "^1.0.2", "deep-eql": "^3.0.1", "get-func-name": "^2.0.0", "pathval": "^1.1.0", "type-detect": "^4.0.5" + }, + "engines": { + "node": ">=4" } }, - "chalk": { + "node_modules/chalk": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, - "requires": { + "dependencies": { "ansi-styles": "^2.2.1", "escape-string-regexp": "^1.0.2", "has-ansi": "^2.0.0", "strip-ansi": "^3.0.0", "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "check-error": { + "node_modules/check-error": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", - "dev": true + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } }, - "cliui": { + "node_modules/cliui": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", "dev": true, - "requires": { + "dependencies": { "string-width": "^3.1.0", "strip-ansi": "^5.2.0", "wrap-ansi": "^5.1.0" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/cliui/node_modules/string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" }, + "engines": { + "node": ">=6" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" } }, - "color-convert": { + "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, - "requires": { + "dependencies": { "color-name": "1.1.3" } }, - "color-name": { + "node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, - "concat-map": { + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, - "core-js": { + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true, + "license": "MIT" + }, + "node_modules/core-js": { "version": "2.6.11", "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz", "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==", - "dev": true + "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.", + "dev": true, + "hasInstallScript": true }, - "core-util-is": { + "node_modules/core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, - "date-extended": { + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/date-extended": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/date-extended/-/date-extended-0.0.6.tgz", "integrity": "sha1-I4AtV90b94GIE/4MMuhRqG2iZ8k=", - "requires": { + "dependencies": { "array-extended": "~0.0.3", "extended": "~0.0.3", "is-extended": "~0.0.3" } }, - "debug": { + "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, - "requires": { + "dependencies": { "ms": "2.0.0" } }, - "decamelize": { + "node_modules/decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "declare.js": { + "node_modules/declare.js": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/declare.js/-/declare.js-0.0.8.tgz", "integrity": "sha1-BHit/5VkwAT1Hfc9i8E0AZ0o3N4=" }, - "deep-diff": { + "node_modules/deep-diff": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/deep-diff/-/deep-diff-1.0.2.tgz", - "integrity": "sha512-aWS3UIVH+NPGCD1kki+DCU9Dua032iSsO43LqQpcs4R3+dVv7tX0qBGjiVHJHjplsoUM2XRO/KB92glqc68awg==" + "integrity": "sha512-aWS3UIVH+NPGCD1kki+DCU9Dua032iSsO43LqQpcs4R3+dVv7tX0qBGjiVHJHjplsoUM2XRO/KB92glqc68awg==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info." }, - "deep-eql": { + "node_modules/deep-eql": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", "dev": true, - "requires": { + "dependencies": { "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=0.12" } }, - "define-properties": { + "node_modules/default-require-extensions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.1.tgz", + "integrity": "sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "strip-bom": "^4.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", "dev": true, - "requires": { + "dependencies": { "object-keys": "^1.0.12" + }, + "engines": { + "node": ">= 0.4" } }, - "diff": { + "node_modules/diff": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.3.1" + } }, - "duplexer": { + "node_modules/duplexer": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" }, - "duplexify": { + "node_modules/duplexify": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", - "requires": { + "dependencies": { "end-of-stream": "^1.0.0", "inherits": "^2.0.1", "readable-stream": "^2.0.0", "stream-shift": "^1.0.0" } }, - "electron-to-chromium": { - "version": "1.3.398", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.398.tgz", - "integrity": "sha512-BJjxuWLKFbM5axH3vES7HKMQgAknq9PZHBkMK/rEXUQG9i1Iw5R+6hGkm6GtsQSANjSUrh/a6m32nzCNDNo/+w==", - "dev": true + "node_modules/electron-to-chromium": { + "version": "1.5.277", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.277.tgz", + "integrity": "sha512-wKXFZw4erWmmOz5N/grBoJ2XrNJGDFMu2+W5ACHza5rHtvsqrK4gb6rnLC7XxKB9WlJ+RmyQatuEXmtm86xbnw==", + "dev": true, + "license": "ISC" }, - "emoji-regex": { + "node_modules/emoji-regex": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", "dev": true }, - "end-of-stream": { + "node_modules/end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "requires": { + "dependencies": { "once": "^1.4.0" } }, - "es-abstract": { + "node_modules/es-abstract": { "version": "1.17.5", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", "dev": true, - "requires": { + "dependencies": { "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", "has": "^1.0.3", @@ -1030,42 +1847,84 @@ "object.assign": "^4.1.0", "string.prototype.trimleft": "^2.1.1", "string.prototype.trimright": "^2.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "es-to-primitive": { + "node_modules/es-to-primitive": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", "dev": true, - "requires": { + "dependencies": { "is-callable": "^1.1.4", "is-date-object": "^1.0.1", "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" } }, - "escape-string-regexp": { + "node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.8.0" + } }, - "esprima": { + "node_modules/esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } }, - "esutils": { + "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "event-stream": { + "node_modules/event-stream": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-4.0.1.tgz", "integrity": "sha512-qACXdu/9VHPBzcyhdOWR5/IahhGMf0roTeZJfzz077GwylcDd90yOHLouhmv7GJ5XzPi6ekaQWd8AvPP2nOvpA==", - "requires": { + "dependencies": { "duplexer": "^0.1.1", "from": "^0.1.7", "map-stream": "0.0.7", @@ -1075,430 +1934,944 @@ "through": "^2.3.8" } }, - "events": { + "node_modules/events": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" + "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", + "engines": { + "node": ">=0.4.x" + } }, - "extend": { + "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" }, - "extended": { + "node_modules/extended": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/extended/-/extended-0.0.6.tgz", "integrity": "sha1-f7i/e52uOXWG5IVwrP1kLHjlBmk=", - "requires": { + "dependencies": { "extender": "~0.0.5" } }, - "extender": { + "node_modules/extender": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/extender/-/extender-0.0.10.tgz", "integrity": "sha1-WJwHSCvmGhRgttgfnCSqZ+jzJM0=", - "requires": { + "dependencies": { "declare.js": "~0.0.4" } }, - "fast-csv": { + "node_modules/fast-csv": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/fast-csv/-/fast-csv-2.5.0.tgz", "integrity": "sha512-M/9ezLU9/uDwvDZTt9sNFJa0iLDUsbhYJwPtnE0D9MjeuB6DY9wRCyUPZta9iI6cSz5wBWGaUPL61QH8h92cNA==", - "requires": { + "dependencies": { "extended": "0.0.6", "is-extended": "0.0.10", "object-extended": "0.0.7", "safer-buffer": "^2.1.2", "string-extended": "0.0.8" + }, + "engines": { + "node": ">=4.0.0" } }, - "fast-deep-equal": { + "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, - "fill-keys": { + "node_modules/fill-keys": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/fill-keys/-/fill-keys-1.0.2.tgz", "integrity": "sha1-mo+jb06K1jTjv2tPPIiCVRRS6yA=", "dev": true, - "requires": { + "dependencies": { "is-object": "~1.0.1", "merge-descriptors": "~1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "license": "MIT", + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" } }, - "find-up": { + "node_modules/find-up": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "dev": true, - "requires": { + "dependencies": { "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" } }, - "flat": { + "node_modules/flat": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==", + "deprecated": "Fixed a prototype pollution security issue in 4.1.0, please upgrade to ^4.1.1 or ^5.0.1.", "dev": true, - "requires": { + "dependencies": { "is-buffer": "~2.0.3" + }, + "bin": { + "flat": "cli.js" } }, - "flush-write-stream": { + "node_modules/flush-write-stream": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", - "requires": { + "dependencies": { "inherits": "^2.0.3", "readable-stream": "^2.3.6" } }, - "for-each": { + "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "requires": { + "dependencies": { "is-callable": "^1.1.3" } }, - "from": { + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/from": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==" }, - "fs.realpath": { + "node_modules/fromentries": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", + "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, - "function-bind": { + "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, - "get-caller-file": { + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } }, - "get-func-name": { + "node_modules/get-func-name": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", - "dev": true + "dev": true, + "engines": { + "node": "*" + } }, - "get-intrinsic": { + "node_modules/get-intrinsic": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", - "requires": { + "dependencies": { "function-bind": "^1.1.1", "has": "^1.0.3", "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" } }, - "glob": { + "node_modules/glob": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, - "requires": { + "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.0.4", "once": "^1.3.0", "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" } }, - "globals": { + "node_modules/globals": { "version": "9.18.0", "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "gopd": { + "node_modules/gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "requires": { + "dependencies": { "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "growl": { + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/growl": { "version": "1.10.5", "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true + "dev": true, + "engines": { + "node": ">=4.x" + } }, - "has": { + "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "requires": { + "dependencies": { "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" } }, - "has-ansi": { + "node_modules/has-ansi": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", "dev": true, - "requires": { + "dependencies": { "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "has-flag": { + "node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true + "dev": true, + "engines": { + "node": ">=4" + } }, - "has-symbols": { + "node_modules/has-symbols": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "has-tostringtag": { + "node_modules/has-tostringtag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "requires": { + "dependencies": { "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "he": { + "node_modules/hasha": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", + "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-stream": "^2.0.0", + "type-fest": "^0.8.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" }, - "ieee754": { + "node_modules/ieee754": { "version": "1.1.13", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" }, - "inflight": { + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dev": true, - "requires": { + "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, - "inherits": { + "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, - "ini": { + "node_modules/ini": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" }, - "invariant": { + "node_modules/invariant": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", "dev": true, - "requires": { + "dependencies": { "loose-envify": "^1.0.0" } }, - "is-arguments": { + "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==", - "requires": { + "dependencies": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "is-buffer": { + "node_modules/is-buffer": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==", - "dev": true + "dev": true, + "engines": { + "node": ">=4" + } }, - "is-callable": { + "node_modules/is-callable": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", - "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==" + "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "is-date-object": { + "node_modules/is-date-object": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", - "dev": true - }, - "is-extended": { - "version": "0.0.10", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extended": { + "version": "0.0.10", "resolved": "https://registry.npmjs.org/is-extended/-/is-extended-0.0.10.tgz", "integrity": "sha1-JE4UDfdbscmjEG9BL/GC+1NKbWI=", - "requires": { + "dependencies": { "extended": "~0.0.3" } }, - "is-fullwidth-code-point": { + "node_modules/is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true + "dev": true, + "engines": { + "node": ">=4" + } }, - "is-generator-function": { + "node_modules/is-generator-function": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", - "requires": { + "dependencies": { "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "is-object": { + "node_modules/is-object": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz", "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA=", "dev": true }, - "is-regex": { + "node_modules/is-regex": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", "dev": true, - "requires": { + "dependencies": { "has": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "is-semver": { + "node_modules/is-semver": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/is-semver/-/is-semver-1.0.11.tgz", "integrity": "sha512-yEHN/JhQU0Y+WR3Zkw+QyABQsMI16t71XVhpBqkYmA6DG6v2S2AFOJE9T/AUGL228brp+Rvjbj4ROZ59R5JESQ==", "dev": true, - "requires": { + "dependencies": { "semver": "^7.3.7" - }, + } + }, + "node_modules/is-semver/node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, "dependencies": { - "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, - "is-symbol": { + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-symbol": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", "dev": true, - "requires": { + "dependencies": { "has-symbols": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "is-typed-array": { + "node_modules/is-typed-array": { "version": "1.1.10", "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", - "requires": { + "dependencies": { "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", "for-each": "^0.3.3", "gopd": "^1.0.1", "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" } }, - "isarray": { + "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, - "isexe": { + "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, - "jmespath": { + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-hook": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", + "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "append-transform": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-processinfo": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz", + "integrity": "sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg==", + "dev": true, + "license": "ISC", + "dependencies": { + "archy": "^1.0.0", + "cross-spawn": "^7.0.3", + "istanbul-lib-coverage": "^3.2.0", + "p-map": "^3.0.0", + "rimraf": "^3.0.0", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-processinfo/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/istanbul-lib-report/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jmespath": { "version": "0.16.0", "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", - "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==" + "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==", + "engines": { + "node": ">= 0.6.0" + } }, - "js-tokens": { + "node_modules/js-tokens": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", "dev": true }, - "js-yaml": { + "node_modules/js-yaml": { "version": "3.13.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", "dev": true, - "requires": { + "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "jsesc": { + "node_modules/jsesc": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", - "dev": true + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + } }, - "json-schema-traverse": { + "node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, - "just-extend": { + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/just-extend": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.1.0.tgz", "integrity": "sha512-ApcjaOdVTJ7y4r08xI5wIqpvwS48Q0PBG4DJROcEkH1f8MdAiNFyFxz3xoL0LWAVwjrwPYZdVHHxhRHcx/uGLA==", "dev": true }, - "later": { + "node_modules/later": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/later/-/later-1.2.0.tgz", - "integrity": "sha512-Gi4c6JariwTigfAQLFCyWKKtyY5yzMZOdJdUH9Ori4FqQxQYDREvDRTu5N+jBM7hcnyEDMeYjhmRUTwLIjEMqA==" + "integrity": "sha512-Gi4c6JariwTigfAQLFCyWKKtyY5yzMZOdJdUH9Ori4FqQxQYDREvDRTu5N+jBM7hcnyEDMeYjhmRUTwLIjEMqA==", + "deprecated": "Please upgrade to the maintained and new drop-in replacement @breejs/later at https://github.com/breejs/later 🚀 Thanks and happy hacking! 🚀 @niftylettuce" }, - "leo-aws": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/leo-aws/-/leo-aws-1.4.4.tgz", - "integrity": "sha512-5laBNvIFzoFCbBR/H83zKgla9ceq2PubzdYjIERD/AgqTYUIRptjdywOUQAXh35iW5KMVDw4RcknvytABU3RQQ==", + "node_modules/leo-aws": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/leo-aws/-/leo-aws-2.0.3.tgz", + "integrity": "sha512-Q6ghY/j6pWmX6NL6ODm7Cy+zx5HGxezrUSp7wtmFZGK3JHGN0vwboJMlZSVu6WIGBtgb2xmGghRqP+Ga4JB9tA==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "async": "^2.6.1", "backoff": "^2.5.0", - "leo-config": "^1.1.0", - "leo-logger": "^1.0.1", - "leo-streams": "^1.1.1", + "leo-config": "1.1.0", + "leo-logger": "1.0.1", + "leo-streams": "2.0.0", "lodash": "^4.17.14", "lodash.merge": "^4.6.2" + }, + "peerDependencies": { + "aws-sdk": "^2.581.0" } }, - "leo-config": { + "node_modules/leo-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/leo-config/-/leo-config-1.1.0.tgz", "integrity": "sha512-h6G8cz/yBdkWG2pDfDLUsJ0cFkxXrfjkRni312REly5BwLTDliQecJYeYs9xjwKCxnub6QIMdwO8Ep2kgs2Lvw==", - "requires": { + "dependencies": { "lodash.merge": "^4.6.1" } }, - "leo-cron": { + "node_modules/leo-cron": { "version": "2.0.2-beta", "resolved": "https://registry.npmjs.org/leo-cron/-/leo-cron-2.0.2-beta.tgz", "integrity": "sha512-rlPoRaIQswybXJk2Dyo6+drCIgWIFLgot16aqA+QughIOQVh3SBpmzAjzqkmX3BRvwOSZ7WPBhDsaSTl7FQTXw==", - "requires": { + "dependencies": { "async": "^2.6.1", "deep-diff": "^0.3.8", "later": "^1.2.0", @@ -1506,53 +2879,52 @@ "leo-sdk": "^2.2.4", "lodash.merge": "^4.6.1", "moment": "^2.21.0" - }, + } + }, + "node_modules/leo-cron/node_modules/deep-diff": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/deep-diff/-/deep-diff-0.3.8.tgz", + "integrity": "sha512-yVn6RZmHiGnxRKR9sJb3iVV2XTF1Ghh2DiWRZ3dMnGc43yUdWWF/kX6lQyk3+P84iprfWKU/8zFTrlkvtFm1ug==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info." + }, + "node_modules/leo-cron/node_modules/leo-sdk": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/leo-sdk/-/leo-sdk-2.3.1.tgz", + "integrity": "sha512-jPFUeLhGE4wIsOJTgGJbhy9VEGhH0Al6Ki+w5Wx32v//m+Umupl2TR6h26Wx8iiMcnG8dNfDxvFfxhV4UEhBIA==", "dependencies": { - "deep-diff": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/deep-diff/-/deep-diff-0.3.8.tgz", - "integrity": "sha512-yVn6RZmHiGnxRKR9sJb3iVV2XTF1Ghh2DiWRZ3dMnGc43yUdWWF/kX6lQyk3+P84iprfWKU/8zFTrlkvtFm1ug==" - }, - "leo-sdk": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/leo-sdk/-/leo-sdk-2.3.1.tgz", - "integrity": "sha512-jPFUeLhGE4wIsOJTgGJbhy9VEGhH0Al6Ki+w5Wx32v//m+Umupl2TR6h26Wx8iiMcnG8dNfDxvFfxhV4UEhBIA==", - "requires": { - "async": "^2.6.1", - "aws-sdk": "^2.387.0", - "backoff": "^2.5.0", - "event-stream": "^4.0.1", - "extend": "^3.0.2", - "fast-csv": "^2.4.1", - "flush-write-stream": "^1.0.3", - "ini": "^1.3.5", - "leo-config": "^1.0.7", - "leo-logger": "^1.0.1", - "lodash": "^4.17.11", - "moment": "^2.23.0", - "pump": "^1.0.3", - "pumpify": "^1.5.1", - "readable-stream": "^2.3.6", - "split2": "^2.2.0", - "through2": "^2.0.5", - "uuid": "^3.3.2" - } - } + "async": "^2.6.1", + "aws-sdk": "^2.387.0", + "backoff": "^2.5.0", + "event-stream": "^4.0.1", + "extend": "^3.0.2", + "fast-csv": "^2.4.1", + "flush-write-stream": "^1.0.3", + "ini": "^1.3.5", + "leo-config": "^1.0.7", + "leo-logger": "^1.0.1", + "lodash": "^4.17.11", + "moment": "^2.23.0", + "pump": "^1.0.3", + "pumpify": "^1.5.1", + "readable-stream": "^2.3.6", + "split2": "^2.2.0", + "through2": "^2.0.5", + "uuid": "^3.3.2" } }, - "leo-logger": { + "node_modules/leo-logger": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/leo-logger/-/leo-logger-1.0.1.tgz", "integrity": "sha512-mOXR35GHScpuYO+nbRU8jPeP5uK+QdhKXS+vUoSB6jq5ieQzgu2o6JD4kS+Qf5YIeI6wa/1P8aQ71uWREvLIrA==", - "requires": { + "dependencies": { "lodash": "^4.17.10" } }, - "leo-sdk": { + "node_modules/leo-sdk": { "version": "6.0.17-rc", "resolved": "https://registry.npmjs.org/leo-sdk/-/leo-sdk-6.0.17-rc.tgz", "integrity": "sha512-6Mt8n2imApIHW3lRY+fGuvBu1GJcZx1OkbXVr1GDWqq+ym23KBhp/SCEE+T3z0Agyvq6q+6Ow3f2JlAzPmAMxQ==", - "requires": { + "dependencies": { "async": "2.6.4", "aws-sdk": "^2.1413.0", "backoff": "2.5.0", @@ -1574,107 +2946,129 @@ "through2": "3.0.1", "uuid": "3.3.2" }, + "peerDependencies": { + "aws-sdk": "^2.1107.0" + } + }, + "node_modules/leo-sdk/node_modules/aws-sdk": { + "version": "2.1413.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1413.0.tgz", + "integrity": "sha512-vKpjC7iRwOhgv7P0xw90mVGO//2rqVPJKyYIs7uxLzSV0JzriVD+yqktOu/Hz6/phOmAd1cMIeFgpEC9ynrppg==", "dependencies": { - "aws-sdk": { - "version": "2.1413.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1413.0.tgz", - "integrity": "sha512-vKpjC7iRwOhgv7P0xw90mVGO//2rqVPJKyYIs7uxLzSV0JzriVD+yqktOu/Hz6/phOmAd1cMIeFgpEC9ynrppg==", - "requires": { - "buffer": "4.9.2", - "events": "1.1.1", - "ieee754": "1.1.13", - "jmespath": "0.16.0", - "querystring": "0.2.0", - "sax": "1.2.1", - "url": "0.10.3", - "util": "^0.12.4", - "uuid": "8.0.0", - "xml2js": "0.5.0" - }, - "dependencies": { - "uuid": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", - "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==" - } - } - }, - "fast-csv": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/fast-csv/-/fast-csv-4.3.6.tgz", - "integrity": "sha512-2RNSpuwwsJGP0frGsOmTb9oUF+VkFSM4SyLTDgwf2ciHWTarN0lQTC+F2f/t5J9QjW+c65VFIAAu85GsvMIusw==", - "requires": { - "@fast-csv/format": "4.3.5", - "@fast-csv/parse": "4.3.6" - } - }, - "flush-write-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-2.0.0.tgz", - "integrity": "sha512-uXClqPxT4xW0lcdSBheb2ObVU+kuqUk3Jk64EwieirEXZx9XUrVwp/JuBfKAWaM4T5Td/VL7QLDWPXp/MvGm/g==", - "requires": { - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - } - }, - "ini": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", - "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==" - }, - "moment": { - "version": "2.29.4", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", - "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==" - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "readable-stream": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", - "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "split2": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/split2/-/split2-3.1.1.tgz", - "integrity": "sha512-emNzr1s7ruq4N+1993yht631/JH+jaj0NYBosuKmLcq+JkGQ9MmTw1RB1fGaTCzUuseRIClrlSLHRNYGwWQ58Q==", - "requires": { - "readable-stream": "^3.0.0" - } - }, - "through2": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", - "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", - "requires": { - "readable-stream": "2 || 3" - } - }, - "uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" - } + "buffer": "4.9.2", + "events": "1.1.1", + "ieee754": "1.1.13", + "jmespath": "0.16.0", + "querystring": "0.2.0", + "sax": "1.2.1", + "url": "0.10.3", + "util": "^0.12.4", + "uuid": "8.0.0", + "xml2js": "0.5.0" + }, + "engines": { + "node": ">= 10.0.0" } }, - "leo-streams": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/leo-streams/-/leo-streams-1.2.1.tgz", - "integrity": "sha512-tc04zWtxJYEJHPRYKtkv4dXdb+20iL/fZnNUR+sshUN1L2SyfaPvvjDITXIlahIf339J86m+nejxbP6mgp0hWQ==", + "node_modules/leo-sdk/node_modules/aws-sdk/node_modules/uuid": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", + "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/leo-sdk/node_modules/fast-csv": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/fast-csv/-/fast-csv-4.3.6.tgz", + "integrity": "sha512-2RNSpuwwsJGP0frGsOmTb9oUF+VkFSM4SyLTDgwf2ciHWTarN0lQTC+F2f/t5J9QjW+c65VFIAAu85GsvMIusw==", + "dependencies": { + "@fast-csv/format": "4.3.5", + "@fast-csv/parse": "4.3.6" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/leo-sdk/node_modules/flush-write-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-2.0.0.tgz", + "integrity": "sha512-uXClqPxT4xW0lcdSBheb2ObVU+kuqUk3Jk64EwieirEXZx9XUrVwp/JuBfKAWaM4T5Td/VL7QLDWPXp/MvGm/g==", + "dependencies": { + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + } + }, + "node_modules/leo-sdk/node_modules/ini": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/leo-sdk/node_modules/moment": { + "version": "2.29.4", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", + "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", + "engines": { + "node": "*" + } + }, + "node_modules/leo-sdk/node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/leo-sdk/node_modules/readable-stream": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", + "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/leo-sdk/node_modules/split2": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/split2/-/split2-3.1.1.tgz", + "integrity": "sha512-emNzr1s7ruq4N+1993yht631/JH+jaj0NYBosuKmLcq+JkGQ9MmTw1RB1fGaTCzUuseRIClrlSLHRNYGwWQ58Q==", + "dependencies": { + "readable-stream": "^3.0.0" + } + }, + "node_modules/leo-sdk/node_modules/through2": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", + "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", + "dependencies": { + "readable-stream": "2 || 3" + } + }, + "node_modules/leo-sdk/node_modules/uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/leo-streams": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/leo-streams/-/leo-streams-2.0.0.tgz", + "integrity": "sha512-7CkcyoTYoZ2BVtyTnqQvOWWHNDFdmDBZcuBGO4S+9g82feEoMfEye6mLgqPsz4fA768cwFJVilzumKEGjZ9Ysg==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "backoff": "^2.5.0", "fast-csv": "^2.4.1", "leo-logger": "^1.0.1", @@ -1685,185 +3079,244 @@ "readable-stream": "^2.3.6", "split2": "^2.2.0", "through2": "^2.0.3" - }, + } + }, + "node_modules/leo-streams/node_modules/pump": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", + "dev": true, + "license": "MIT", "dependencies": { - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - } + "end-of-stream": "^1.1.0", + "once": "^1.3.1" } }, - "locate-path": { + "node_modules/locate-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", "dev": true, - "requires": { + "dependencies": { "p-locate": "^3.0.0", "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" } }, - "lodash": { + "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, - "lodash.escaperegexp": { + "node_modules/lodash.escaperegexp": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==" }, - "lodash.groupby": { + "node_modules/lodash.flattendeep": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", + "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.groupby": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/lodash.groupby/-/lodash.groupby-4.6.0.tgz", "integrity": "sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw==" }, - "lodash.isboolean": { + "node_modules/lodash.isboolean": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" }, - "lodash.isequal": { + "node_modules/lodash.isequal": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead." }, - "lodash.isfunction": { + "node_modules/lodash.isfunction": { "version": "3.0.9", "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz", "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==" }, - "lodash.isnil": { + "node_modules/lodash.isnil": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/lodash.isnil/-/lodash.isnil-4.0.0.tgz", "integrity": "sha512-up2Mzq3545mwVnMhTDMdfoG1OurpA/s5t88JmQX809eH3C8491iu2sfKhTfhQtKY78oPNhiaHJUpT/dUDAAtng==" }, - "lodash.isundefined": { + "node_modules/lodash.isundefined": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/lodash.isundefined/-/lodash.isundefined-3.0.1.tgz", "integrity": "sha512-MXB1is3s899/cD8jheYYE2V9qTHwKvt+npCwpD+1Sxm3Q3cECXCiYHjeHWXNwr6Q0SOBPrYUDxendrO6goVTEA==" }, - "lodash.merge": { + "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, - "lodash.uniq": { + "node_modules/lodash.uniq": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==" }, - "log-symbols": { + "node_modules/log-symbols": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", "dev": true, - "requires": { + "dependencies": { "chalk": "^2.0.1" }, + "engines": { + "node": ">=4" + } + }, + "node_modules/log-symbols/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, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/log-symbols/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, "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "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, - "requires": { - "has-flag": "^3.0.0" - } - } + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" } }, - "lolex": { + "node_modules/log-symbols/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, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/lolex": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lolex/-/lolex-4.2.0.tgz", "integrity": "sha512-gKO5uExCXvSm6zbF562EvM+rd1kQDnB9AZBbiQVzf1ZmdDpxUSvpnAaVOP83N/31mRK8Ml8/VE8DMvsAZQ+7wg==", "dev": true }, - "loose-envify": { + "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "dev": true, - "requires": { + "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" } }, - "lru-cache": { + "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, - "requires": { + "dependencies": { "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" } }, - "map-stream": { + "node_modules/map-stream": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", "integrity": "sha512-C0X0KQmGm3N2ftbTGBhSyuydQ+vV1LC3f3zPvT3RXHXNZrvfPZcoXp/N5DOa8vedX/rTMm2CjTtivFg2STJMRQ==" }, - "merge-descriptors": { + "node_modules/merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", "dev": true }, - "minimatch": { + "node_modules/minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, - "requires": { + "dependencies": { "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, - "minimist": { + "node_modules/minimist": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "dev": true }, - "mkdirp": { + "node_modules/mkdirp": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.4.tgz", "integrity": "sha512-iG9AK/dJLtJ0XNgTuDbSyNS3zECqDlAhnQW4CsNxBG3LQJBbHmRX1egw39DmtOdCAqY+dKXV+sgPgilNWUKMVw==", + "deprecated": "Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)", "dev": true, - "requires": { + "dependencies": { "minimist": "^1.2.5" + }, + "bin": { + "mkdirp": "bin/cmd.js" } }, - "mocha": { + "node_modules/mocha": { "version": "6.2.3", "resolved": "https://registry.npmjs.org/mocha/-/mocha-6.2.3.tgz", "integrity": "sha512-0R/3FvjIGH3eEuG17ccFPk117XL2rWxatr81a57D+r/x2uTYZRbdZ4oVidEUMh2W2TJDa7MdAb12Lm2/qrKajg==", "dev": true, - "requires": { + "dependencies": { "ansi-colors": "3.2.3", "browser-stdout": "1.3.1", "debug": "3.2.6", @@ -1888,281 +3341,757 @@ "yargs-parser": "13.1.2", "yargs-unparser": "1.6.0" }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/mocha/node_modules/debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", + "dev": true, "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - }, - "supports-color": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", - "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } + "ms": "^2.1.1" } }, - "module-not-found-error": { + "node_modules/mocha/node_modules/ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", + "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/module-not-found-error": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/module-not-found-error/-/module-not-found-error-1.0.1.tgz", "integrity": "sha1-z4tP9PKWQGdNbN0CsOO8UjwrvcA=", "dev": true }, - "moment": { + "node_modules/moment": { "version": "2.24.0", "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", - "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==" + "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==", + "engines": { + "node": "*" + } }, - "ms": { + "node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true }, - "nise": { + "node_modules/nise": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/nise/-/nise-1.5.3.tgz", "integrity": "sha512-Ymbac/94xeIrMf59REBPOv0thr+CJVFMhrlAkW/gjCIE58BGQdCj0x7KRCb3yz+Ga2Rz3E9XXSvUyyxqqhjQAQ==", "dev": true, - "requires": { + "dependencies": { "@sinonjs/formatio": "^3.2.1", "@sinonjs/text-encoding": "^0.7.1", "just-extend": "^4.0.2", "lolex": "^5.0.1", "path-to-regexp": "^1.7.0" - }, + } + }, + "node_modules/nise/node_modules/lolex": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/lolex/-/lolex-5.1.2.tgz", + "integrity": "sha512-h4hmjAvHTmd+25JSwrtTIuwbKdwg5NzZVRMLn9saij4SZaepCrTCxPr35H/3bjwfMJtN+t3CX8672UIkglz28A==", + "dev": true, "dependencies": { - "lolex": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-5.1.2.tgz", - "integrity": "sha512-h4hmjAvHTmd+25JSwrtTIuwbKdwg5NzZVRMLn9saij4SZaepCrTCxPr35H/3bjwfMJtN+t3CX8672UIkglz28A==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.7.0" - } - } + "@sinonjs/commons": "^1.7.0" } }, - "node-environment-flags": { + "node_modules/node-environment-flags": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.5.tgz", "integrity": "sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ==", "dev": true, - "requires": { + "dependencies": { "object.getownpropertydescriptors": "^2.0.3", "semver": "^5.7.0" } }, - "object-extended": { + "node_modules/node-preload": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", + "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "process-on-spawn": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/node-releases": { + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nyc": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/nyc/-/nyc-17.1.0.tgz", + "integrity": "sha512-U42vQ4czpKa0QdI1hu950XuNhYqgoM+ZF1HT+VuUHL9hPfDPVvNQyltmMqdE9bUHMVa+8yNbc3QKTj8zQhlVxQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "caching-transform": "^4.0.0", + "convert-source-map": "^1.7.0", + "decamelize": "^1.2.0", + "find-cache-dir": "^3.2.0", + "find-up": "^4.1.0", + "foreground-child": "^3.3.0", + "get-package-type": "^0.1.0", + "glob": "^7.1.6", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-hook": "^3.0.0", + "istanbul-lib-instrument": "^6.0.2", + "istanbul-lib-processinfo": "^2.0.2", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.2", + "make-dir": "^3.0.0", + "node-preload": "^0.2.1", + "p-map": "^3.0.0", + "process-on-spawn": "^1.0.0", + "resolve-from": "^5.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "spawn-wrap": "^2.0.0", + "test-exclude": "^6.0.0", + "yargs": "^15.0.2" + }, + "bin": { + "nyc": "bin/nyc.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/nyc/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/nyc/node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/nyc/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/nyc/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nyc/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/nyc/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/nyc/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/nyc/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nyc/node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/object-extended": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/object-extended/-/object-extended-0.0.7.tgz", "integrity": "sha1-hP0j9WsVWCrrPoiwXLVdJDLWijM=", - "requires": { + "dependencies": { "array-extended": "~0.0.4", "extended": "~0.0.3", "is-extended": "~0.0.3" } }, - "object-inspect": { + "node_modules/object-inspect": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==", - "dev": true + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "object-keys": { + "node_modules/object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true + "dev": true, + "engines": { + "node": ">= 0.4" + } }, - "object.assign": { + "node_modules/object.assign": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", "dev": true, - "requires": { + "dependencies": { "define-properties": "^1.1.2", "function-bind": "^1.1.1", "has-symbols": "^1.0.0", "object-keys": "^1.0.11" + }, + "engines": { + "node": ">= 0.4" } }, - "object.getownpropertydescriptors": { + "node_modules/object.getownpropertydescriptors": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz", "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==", "dev": true, - "requires": { + "dependencies": { "define-properties": "^1.1.3", "es-abstract": "^1.17.0-next.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "once": { + "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { + "dependencies": { "wrappy": "1" } }, - "p-limit": { + "node_modules/p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, - "requires": { + "dependencies": { "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "p-locate": { + "node_modules/p-locate": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", "dev": true, - "requires": { + "dependencies": { "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" } }, - "p-try": { + "node_modules/p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/package-hash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", + "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "graceful-fs": "^4.1.15", + "hasha": "^5.0.0", + "lodash.flattendeep": "^4.4.0", + "release-zalgo": "^1.0.0" + }, + "engines": { + "node": ">=8" + } }, - "path-exists": { + "node_modules/path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true + "dev": true, + "engines": { + "node": ">=4" + } }, - "path-is-absolute": { + "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "path-parse": { + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", "dev": true }, - "path-to-regexp": { + "node_modules/path-to-regexp": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", "dev": true, - "requires": { - "isarray": "0.0.1" - }, "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - } + "isarray": "0.0.1" } }, - "pathval": { + "node_modules/path-to-regexp/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "node_modules/pathval": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", - "dev": true + "dev": true, + "engines": { + "node": "*" + } }, - "pause-stream": { + "node_modules/pause-stream": { "version": "0.0.11", "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", "integrity": "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==", - "requires": { + "dependencies": { "through": "~2.3" } }, - "precond": { + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/precond": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/precond/-/precond-0.2.3.tgz", - "integrity": "sha1-qpWRvKokkj8eD0hJ0kD0fvwQdaw=" + "integrity": "sha1-qpWRvKokkj8eD0hJ0kD0fvwQdaw=", + "engines": { + "node": ">= 0.6" + } }, - "private": { + "node_modules/private": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", - "dev": true + "dev": true, + "engines": { + "node": ">= 0.6" + } }, - "process-nextick-args": { + "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, - "proxyquire": { + "node_modules/process-on-spawn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.1.0.tgz", + "integrity": "sha512-JOnOPQ/8TZgjs1JIH/m9ni7FfimjNa/PRx7y/Wb5qdItsnhO0jE4AT7fC0HjC28DUQWDr50dwSYZLdRMlqDq3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "fromentries": "^1.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/proxyquire": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/proxyquire/-/proxyquire-2.1.3.tgz", "integrity": "sha512-BQWfCqYM+QINd+yawJz23tbBM40VIGXOdDw3X344KcclI/gtBbdWF6SlQ4nK/bYhF9d27KYug9WzljHC6B9Ysg==", "dev": true, - "requires": { + "dependencies": { "fill-keys": "^1.0.2", "module-not-found-error": "^1.0.1", "resolve": "^1.11.1" } }, - "pump": { + "node_modules/pump": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/pump/-/pump-1.0.3.tgz", "integrity": "sha512-8k0JupWme55+9tCVE+FS5ULT3K6AbgqrGa58lTT49RpyfwwcGedHqaC5LlQNdEAumn/wFsu6aPwkuPMioy8kqw==", - "requires": { + "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, - "pumpify": { + "node_modules/pumpify": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", - "requires": { + "dependencies": { "duplexify": "^3.6.0", "inherits": "^2.0.3", "pump": "^2.0.0" - }, + } + }, + "node_modules/pumpify/node_modules/pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", "dependencies": { - "pump": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", - "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - } + "end-of-stream": "^1.1.0", + "once": "^1.3.1" } }, - "punycode": { + "node_modules/punycode": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" }, - "querystring": { + "node_modules/querystring": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", + "engines": { + "node": ">=0.4.x" + } }, - "readable-stream": { + "node_modules/readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { + "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", @@ -2172,115 +4101,201 @@ "util-deprecate": "~1.0.1" } }, - "regenerate": { + "node_modules/regenerate": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==", "dev": true }, - "regenerator-runtime": { + "node_modules/regenerator-runtime": { "version": "0.11.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", "dev": true }, - "regenerator-transform": { + "node_modules/regenerator-transform": { "version": "0.10.1", "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz", "integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==", "dev": true, - "requires": { + "dependencies": { "babel-runtime": "^6.18.0", "babel-types": "^6.19.0", "private": "^0.1.6" } }, - "regexpu-core": { + "node_modules/regexpu-core": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz", "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=", "dev": true, - "requires": { + "dependencies": { "regenerate": "^1.2.1", "regjsgen": "^0.2.0", "regjsparser": "^0.1.4" } }, - "regjsgen": { + "node_modules/regjsgen": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", "dev": true }, - "regjsparser": { + "node_modules/regjsparser": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", "dev": true, - "requires": { + "dependencies": { "jsesc": "~0.5.0" + }, + "bin": { + "regjsparser": "bin/parser" } }, - "require-directory": { + "node_modules/release-zalgo": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", + "integrity": "sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==", + "dev": true, + "license": "ISC", + "dependencies": { + "es6-error": "^4.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "require-from-string": { + "node_modules/require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "require-main-filename": { + "node_modules/require-main-filename": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, - "resolve": { + "node_modules/resolve": { "version": "1.15.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz", "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==", "dev": true, - "requires": { + "dependencies": { "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "safe-buffer": { + "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==" }, - "safer-buffer": { + "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, - "sax": { + "node_modules/sax": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==" }, - "semver": { + "node_modules/semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true + "dev": true, + "bin": { + "semver": "bin/semver" + } }, - "set-blocking": { + "node_modules/set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true }, - "sinon": { + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/sinon": { "version": "7.5.0", "resolved": "https://registry.npmjs.org/sinon/-/sinon-7.5.0.tgz", "integrity": "sha512-AoD0oJWerp0/rY9czP/D6hDTTUYGpObhZjMpd7Cl/A6+j0xBE+ayL/ldfggkBXUs0IkvIiM1ljM8+WkOc5k78Q==", + "deprecated": "16.1.1", "dev": true, - "requires": { + "dependencies": { "@sinonjs/commons": "^1.4.0", "@sinonjs/formatio": "^3.2.1", "@sinonjs/samsam": "^3.3.3", @@ -2288,221 +4303,404 @@ "lolex": "^4.2.0", "nise": "^1.5.2", "supports-color": "^5.5.0" + } + }, + "node_modules/sinon/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, + "dependencies": { + "has-flag": "^3.0.0" }, + "engines": { + "node": ">=4" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/spawn-wrap": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", + "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", + "dev": true, + "license": "ISC", "dependencies": { - "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, - "requires": { - "has-flag": "^3.0.0" - } - } + "foreground-child": "^2.0.0", + "is-windows": "^1.0.2", + "make-dir": "^3.0.0", + "rimraf": "^3.0.0", + "signal-exit": "^3.0.2", + "which": "^2.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/spawn-wrap/node_modules/foreground-child": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", + "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/spawn-wrap/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" } }, - "split": { + "node_modules/split": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", - "requires": { + "dependencies": { "through": "2" + }, + "engines": { + "node": "*" } }, - "split2": { + "node_modules/split2": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/split2/-/split2-2.2.0.tgz", "integrity": "sha512-RAb22TG39LhI31MbreBgIuKiIKhVsawfTgEGqKHTK87aG+ul/PB8Sqoi3I7kVdRWiCfrKxK3uo4/YUkpNvhPbw==", - "requires": { + "dependencies": { "through2": "^2.0.2" } }, - "sprintf-js": { + "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, - "stream-combiner": { + "node_modules/stream-combiner": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.2.2.tgz", "integrity": "sha512-6yHMqgLYDzQDcAkL+tjJDC5nSNuNIx0vZtRZeiPh7Saef7VHX9H5Ijn9l2VIol2zaNYlYEX6KyuT/237A58qEQ==", - "requires": { + "dependencies": { "duplexer": "~0.1.1", "through": "~2.3.4" } }, - "stream-shift": { + "node_modules/stream-shift": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==" }, - "string-extended": { + "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==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string-extended": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/string-extended/-/string-extended-0.0.8.tgz", "integrity": "sha1-dBlX3/SHsCcqee7FpE8jnubxfM0=", - "requires": { + "dependencies": { "array-extended": "~0.0.5", "date-extended": "~0.0.3", "extended": "~0.0.3", "is-extended": "~0.0.3" } }, - "string-width": { + "node_modules/string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, - "requires": { + "dependencies": { "is-fullwidth-code-point": "^2.0.0", "strip-ansi": "^4.0.0" }, + "engines": { + "node": ">=4" + } + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" } }, - "string.prototype.trimend": { + "node_modules/string.prototype.trimend": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.0.tgz", "integrity": "sha512-EEJnGqa/xNfIg05SxiPSqRS7S9qwDhYts1TSLR1BQfYUfPe1stofgGKvwERK9+9yf+PpfBMlpBaCHucXGPQfUA==", "dev": true, - "requires": { + "dependencies": { "define-properties": "^1.1.3", "es-abstract": "^1.17.5" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "string.prototype.trimleft": { + "node_modules/string.prototype.trimleft": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz", "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==", "dev": true, - "requires": { + "dependencies": { "define-properties": "^1.1.3", "es-abstract": "^1.17.5", "string.prototype.trimstart": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "string.prototype.trimright": { + "node_modules/string.prototype.trimright": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz", "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==", "dev": true, - "requires": { + "dependencies": { "define-properties": "^1.1.3", "es-abstract": "^1.17.5", "string.prototype.trimend": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "string.prototype.trimstart": { + "node_modules/string.prototype.trimstart": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.0.tgz", "integrity": "sha512-iCP8g01NFYiiBOnwG1Xc3WZLyoo+RuBymwIlWncShXDDJYWN6DbnM3odslBJdgCdRlq94B5s63NWAZlcn2CS4w==", "dev": true, - "requires": { + "dependencies": { "define-properties": "^1.1.3", "es-abstract": "^1.17.5" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "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==", - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-ansi": { + "node_modules/strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, - "requires": { + "dependencies": { "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" } }, - "strip-json-comments": { + "node_modules/strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "supports-color": { + "node_modules/supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/test-exclude/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } }, - "through": { + "node_modules/through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" }, - "through2": { + "node_modules/through2": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "requires": { + "dependencies": { "readable-stream": "~2.3.6", "xtend": "~4.0.1" } }, - "to-fast-properties": { + "node_modules/to-fast-properties": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "type-detect": { + "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-typedarray": "^1.0.0" + } }, - "uri-js": { + "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, - "requires": { - "punycode": "^2.1.0" - }, "dependencies": { - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true - } + "punycode": "^2.1.0" + } + }, + "node_modules/uri-js/node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true, + "engines": { + "node": ">=6" } }, - "url": { + "node_modules/url": { "version": "0.10.3", "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=", - "requires": { + "dependencies": { "punycode": "1.3.2", "querystring": "0.2.0" } }, - "util": { + "node_modules/util": { "version": "0.12.5", "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", - "requires": { + "dependencies": { "inherits": "^2.0.3", "is-arguments": "^1.0.4", "is-generator-function": "^1.0.7", @@ -2510,143 +4708,191 @@ "which-typed-array": "^1.1.2" } }, - "util-deprecate": { + "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, - "uuid": { + "node_modules/uuid": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "bin": { + "uuid": "bin/uuid" + } }, - "which": { + "node_modules/which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dev": true, - "requires": { + "dependencies": { "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" } }, - "which-module": { + "node_modules/which-module": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, - "which-typed-array": { + "node_modules/which-typed-array": { "version": "1.1.9", "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", - "requires": { + "dependencies": { "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", "for-each": "^0.3.3", "gopd": "^1.0.1", "has-tostringtag": "^1.0.0", "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "wide-align": { + "node_modules/wide-align": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", "dev": true, - "requires": { + "dependencies": { "string-width": "^1.0.2 || 2" } }, - "wrap-ansi": { + "node_modules/wrap-ansi": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", "dev": true, - "requires": { + "dependencies": { "ansi-styles": "^3.2.0", "string-width": "^3.0.0", "strip-ansi": "^5.0.0" }, + "engines": { + "node": ">=6" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/wrap-ansi/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, "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" } }, - "wrappy": { + "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, - "xml2js": { + "node_modules/write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "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==", - "requires": { + "dependencies": { "sax": ">=0.6.0", "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" } }, - "xmlbuilder": { + "node_modules/xmlbuilder": { "version": "11.0.1", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "engines": { + "node": ">=4.0" + } }, - "xtend": { + "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } }, - "y18n": { + "node_modules/y18n": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", "dev": true }, - "yallist": { + "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, - "yargs": { + "node_modules/yargs": { "version": "13.3.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", "dev": true, - "requires": { + "dependencies": { "cliui": "^5.0.0", "find-up": "^3.0.0", "get-caller-file": "^2.0.1", @@ -2657,55 +4903,65 @@ "which-module": "^2.0.0", "y18n": "^4.0.0", "yargs-parser": "^13.1.2" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } } }, - "yargs-parser": { + "node_modules/yargs-parser": { "version": "13.1.2", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", "dev": true, - "requires": { + "dependencies": { "camelcase": "^5.0.0", "decamelize": "^1.2.0" } }, - "yargs-unparser": { + "node_modules/yargs-unparser": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz", "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==", "dev": true, - "requires": { + "dependencies": { "flat": "^4.1.0", "lodash": "^4.17.15", "yargs": "^13.3.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs/node_modules/string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" } } } diff --git a/package.json b/package.json index adce8e9..ebbf4ba 100644 --- a/package.json +++ b/package.json @@ -4,11 +4,15 @@ "description": "Improve Kinesis burstability, durability, and replay", "main": "index.js", "scripts": { + "test": "mocha --recursive --timeout 30000 --exit 'test/**/*.test.js' 'bots/**/*.test.js'", "test:libs": "mocha --recursive 'test/**/*.test.js'", "test:libs:watch": "mocha --recursive -w 'test/**/*.test.js'", - "test:bots": "mocha --recursive 'bots/**/*.test.js'", + "test:bots": "mocha --recursive --timeout 30000 --exit 'bots/**/*.test.js'", "test:bots:watch": "mocha --recursive -w 'bots/**/*.test.js'", "test:int": "mocha 'test/**/*.integration.js'", + "coverage": "nyc --all --reporter=text --reporter=text-summary --reporter=html npm test", + "coverage:libs": "nyc --all --reporter=text --reporter=text-summary npm run test:libs", + "coverage:bots": "nyc --all --reporter=text --reporter=text-summary npm run test:bots", "setup": "setup", "build": "leo-cli publish -s --force all --build", "package": "leo-cli publish -s --force all", @@ -37,17 +41,18 @@ "moment": "2.24.0" }, "devDependencies": { + "ajv": "8.11.0", + "ajv-formats": "2.1.1", "aws-sdk": "^2.466.0", "babel-preset-env": "^1.7.0", "chai": "^4.2.0", "extend": "^3.0.2", + "is-semver": "1.0.11", "leo-aws": "^2.0.2", "mocha": "^6.1.4", + "nyc": "^17.1.0", "proxyquire": "^2.1.1", - "sinon": "^7.3.2", - "ajv": "8.11.0", - "ajv-formats": "2.1.1", - "is-semver": "1.0.11" + "sinon": "^7.3.2" }, "config": { "leo": { diff --git a/test/lib/getLeoConfigFromBusStack.test.js b/test/lib/getLeoConfigFromBusStack.test.js new file mode 100644 index 0000000..f75fe90 --- /dev/null +++ b/test/lib/getLeoConfigFromBusStack.test.js @@ -0,0 +1,179 @@ +'use strict'; + +const sinon = require('sinon'); +const { expect } = require('chai'); +const proxyquire = require('proxyquire').noCallThru(); + +describe("getLeoConfigFromBusStack", () => { + let getLeoConfigFromBusStack; + let cloudFormationStub; + let describeStacksStub; + + beforeEach(function () { + describeStacksStub = sinon.stub(); + + cloudFormationStub = sinon.stub().returns({ + describeStacks: () => ({ + promise: describeStacksStub + }) + }); + + getLeoConfigFromBusStack = proxyquire('../../lib/getLeoConfigFromBusStack', { + 'aws-sdk': { + CloudFormation: cloudFormationStub, + '@global': true + }, + 'leo-logger': { + info: sinon.stub() + } + }); + }); + + afterEach(function () { + sinon.restore(); + }); + + it('should return leo stack configuration from CloudFormation outputs', async () => { + const mockOutputs = [ + { OutputKey: 'LeoCron', OutputValue: 'cron-table' }, + { OutputKey: 'LeoEvent', OutputValue: 'event-table' }, + { OutputKey: 'LeoFirehoseStream', OutputValue: 'firehose-stream' }, + { OutputKey: 'LeoKinesisStream', OutputValue: 'kinesis-stream' }, + { OutputKey: 'LeoS3', OutputValue: 'leo-s3-bucket' }, + { OutputKey: 'LeoSettings', OutputValue: 'settings-table' }, + { OutputKey: 'LeoStream', OutputValue: 'stream-table' }, + { OutputKey: 'LeoSystem', OutputValue: 'system-table' } + ]; + + describeStacksStub.resolves({ + Stacks: [{ + Outputs: mockOutputs + }] + }); + + const config = await getLeoConfigFromBusStack('test-stack'); + + expect(config).to.deep.equal({ + credentials: undefined, + resources: { + LeoCron: 'cron-table', + LeoEvent: 'event-table', + LeoFirehoseStream: 'firehose-stream', + LeoKinesisStream: 'kinesis-stream', + LeoS3: 'leo-s3-bucket', + LeoSettings: 'settings-table', + LeoStream: 'stream-table', + LeoSystem: 'system-table' + }, + firehose: 'firehose-stream', + kinesis: 'kinesis-stream', + s3: 'leo-s3-bucket' + }); + }); + + it('should use provided credentials when passed', async () => { + const mockCredentials = { + accessKeyId: 'test-key', + secretAccessKey: 'test-secret' + }; + + describeStacksStub.resolves({ + Stacks: [{ + Outputs: [ + { OutputKey: 'LeoCron', OutputValue: 'cron' }, + { OutputKey: 'LeoEvent', OutputValue: 'event' }, + { OutputKey: 'LeoFirehoseStream', OutputValue: 'firehose' }, + { OutputKey: 'LeoKinesisStream', OutputValue: 'kinesis' }, + { OutputKey: 'LeoS3', OutputValue: 's3' }, + { OutputKey: 'LeoSettings', OutputValue: 'settings' }, + { OutputKey: 'LeoStream', OutputValue: 'stream' }, + { OutputKey: 'LeoSystem', OutputValue: 'system' } + ] + }] + }); + + const config = await getLeoConfigFromBusStack('test-stack', mockCredentials); + + expect(cloudFormationStub.calledWith({ credentials: mockCredentials })).to.be.true; + expect(config.credentials).to.equal(mockCredentials); + }); + + it('should throw error when multiple stacks match', async () => { + describeStacksStub.resolves({ + Stacks: [ + { Outputs: [] }, + { Outputs: [] } + ] + }); + + try { + await getLeoConfigFromBusStack('test-stack'); + expect.fail('Should have thrown an error'); + } catch (err) { + expect(err.message).to.equal('Multiple stacks match criteria'); + } + }); + + it('should handle partial outputs gracefully', async () => { + const mockOutputs = [ + { OutputKey: 'LeoCron', OutputValue: 'cron-table' }, + { OutputKey: 'LeoS3', OutputValue: 'leo-s3-bucket' } + ]; + + describeStacksStub.resolves({ + Stacks: [{ + Outputs: mockOutputs + }] + }); + + const config = await getLeoConfigFromBusStack('test-stack'); + + expect(config.resources.LeoCron).to.equal('cron-table'); + expect(config.resources.LeoS3).to.equal('leo-s3-bucket'); + expect(config.resources.LeoEvent).to.be.undefined; + }); + + it('should handle empty outputs array', async () => { + describeStacksStub.resolves({ + Stacks: [{ + Outputs: [] + }] + }); + + const config = await getLeoConfigFromBusStack('test-stack'); + + expect(config.resources).to.deep.equal({ + LeoCron: undefined, + LeoEvent: undefined, + LeoFirehoseStream: undefined, + LeoKinesisStream: undefined, + LeoS3: undefined, + LeoSettings: undefined, + LeoStream: undefined, + LeoSystem: undefined + }); + }); + + it('should propagate CloudFormation errors', async () => { + const error = new Error('CloudFormation error'); + describeStacksStub.rejects(error); + + try { + await getLeoConfigFromBusStack('test-stack'); + expect.fail('Should have thrown an error'); + } catch (err) { + expect(err.message).to.equal('CloudFormation error'); + } + }); + + it('should call CloudFormation with correct stack name', async () => { + describeStacksStub.resolves({ + Stacks: [{ Outputs: [] }] + }); + + await getLeoConfigFromBusStack('my-bus-stack'); + + // Verify the CloudFormation was instantiated + expect(cloudFormationStub.called).to.be.true; + }); +}); diff --git a/test/lib/leolog.test.js b/test/lib/leolog.test.js new file mode 100644 index 0000000..44a9755 --- /dev/null +++ b/test/lib/leolog.test.js @@ -0,0 +1,321 @@ +'use strict'; + +const sinon = require('sinon'); +const { expect } = require('chai'); +const leolog = require('../../lib/leolog'); + +describe("leolog", () => { + let consoleLogStub; + + beforeEach(function () { + consoleLogStub = sinon.stub(console, 'log'); + // Reset cache entries and globalOptions before each test + leolog.globalOptions = undefined; + }); + + afterEach(function () { + consoleLogStub.restore(); + // Clean up by finalizing without additional logs + leolog.finalize(false, true); + }); + + describe("add", () => { + it('should add a new entry to cache', () => { + const identifier = 'test-identifier'; + const start = 1000; + const end = 2000; + const units = 10; + const duration = 1000; + const resource_consumption = 50; + const isError = false; + const options = {}; + + leolog.add(identifier, start, end, units, duration, resource_consumption, isError, options); + leolog.finalize(false, true); + + expect(consoleLogStub.calledOnce).to.be.true; + const logArg = consoleLogStub.getCall(0).args[0]; + expect(logArg).to.include('[LEOLOG]:v1:'); + expect(logArg).to.include(identifier); + }); + + it('should aggregate multiple adds for same identifier', () => { + const identifier = 'test-identifier'; + + leolog.add(identifier, 1000, 2000, 10, 500, 50, false, {}); + leolog.add(identifier, 1500, 2500, 5, 300, 25, false, {}); + leolog.finalize(false, true); + + expect(consoleLogStub.calledOnce).to.be.true; + const logArg = consoleLogStub.getCall(0).args[0]; + // Should have 2 runs + expect(logArg).to.include(':2:'); + }); + + it('should track errors correctly', () => { + const identifier = 'error-test'; + + leolog.add(identifier, 1000, 2000, 10, 500, 50, true, {}); + leolog.add(identifier, 1500, 2500, 5, 300, 25, false, {}); + leolog.finalize(false, true); + + const logArg = consoleLogStub.getCall(0).args[0]; + // Should have 1 error + expect(logArg).to.match(/:1:[^:]+$/); // ends with :1:identifier (error count) + }); + + it('should use v2 logger when options have keys', () => { + const identifier = 'v2-test'; + const options = { key: 'some-key', extra: 'value' }; + + leolog.add(identifier, 1000, 2000, 10, 500, 50, false, options); + leolog.finalize(false, true); + + const logArg = consoleLogStub.getCall(0).args[0]; + expect(logArg).to.include('[LEOLOG]:v2:'); + }); + + it('should handle __completions option', () => { + const identifier = 'completions-test'; + const options = { __completions: 5 }; + + leolog.add(identifier, 1000, 2000, 10, 500, 50, false, options); + leolog.finalize(false, true); + + // Should log without error + expect(consoleLogStub.called).to.be.true; + }); + + it('should use globalOptions if set', () => { + leolog.globalOptions = { key: 'global-key' }; + const identifier = 'global-test'; + + leolog.add(identifier, 1000, 2000, 10, 500, 50, false, {}); + leolog.finalize(false, true); + + const logArg = consoleLogStub.getCall(0).args[0]; + expect(logArg).to.include('[LEOLOG]:v2:'); + }); + + it('should track max and min duration', () => { + const identifier = 'duration-test'; + + leolog.add(identifier, 1000, 2000, 10, 100, 50, false, {}); + leolog.add(identifier, 1500, 2500, 5, 500, 25, false, {}); + leolog.add(identifier, 2000, 3000, 5, 200, 25, false, {}); + leolog.finalize(false, true); + + const logArg = consoleLogStub.getCall(0).args[0]; + // Check for min (100) and max (500) in the output + expect(logArg).to.include(':100:500:'); + }); + + it('should strip first newline from identifier', () => { + const identifier = 'test\nwith'; + + leolog.add(identifier, 1000, 2000, 10, 500, 50, false, {}); + leolog.finalize(false, true); + + const logArg = consoleLogStub.getCall(0).args[0]; + // The code replaces only the first newline + expect(logArg).to.include('testwith'); + }); + }); + + describe("finalize", () => { + it('should log ERROR when addEnd is true and isSuccess is false', () => { + leolog.finalize(true, false); + + expect(consoleLogStub.calledWith('[LEOLOG]:ERROR')).to.be.true; + }); + + it('should not log ERROR when isSuccess is true', () => { + leolog.finalize(true, true); + + expect(consoleLogStub.calledWith('[LEOLOG]:ERROR')).to.be.false; + }); + + it('should clear cache entries after finalize', () => { + leolog.add('test', 1000, 2000, 10, 500, 50, false, {}); + leolog.finalize(false, true); + consoleLogStub.resetHistory(); + + // Finalize again should not log anything + leolog.finalize(false, true); + expect(consoleLogStub.called).to.be.false; + }); + }); + + describe("finalizeV2", () => { + it('should use extra options for all entries', () => { + leolog.add('test1', 1000, 2000, 10, 500, 50, false, {}); + leolog.add('test2', 1500, 2500, 5, 300, 25, false, {}); + + leolog.finalizeV2({ customKey: 'customValue' }, false, true); + + // Both logs should include customValue + expect(consoleLogStub.callCount).to.equal(2); + expect(consoleLogStub.getCall(0).args[0]).to.include('customValue'); + expect(consoleLogStub.getCall(1).args[0]).to.include('customValue'); + }); + + it('should log ERROR when addEnd is true and isSuccess is false', () => { + leolog.finalizeV2({}, true, false); + + expect(consoleLogStub.calledWith('[LEOLOG]:ERROR')).to.be.true; + }); + }); + + describe("systemRead", () => { + it('should log a system read event', () => { + leolog.systemRead('bot-id', 'queue.name', 100, {}); + + expect(consoleLogStub.calledOnce).to.be.true; + const logArg = consoleLogStub.getCall(0).args[0]; + expect(logArg).to.include('[LEOLOG]:v2:'); + expect(logArg).to.include('leo:getEvents:queue.name'); + }); + + it('should prepend "system." to event name if not present', () => { + leolog.systemRead('bot-id', 'my-event', 50, {}); + + const logArg = consoleLogStub.getCall(0).args[0]; + expect(logArg).to.include('system.my-event'); + }); + + it('should not modify event name with dots', () => { + leolog.systemRead('bot-id', 'custom.event.name', 50, {}); + + const logArg = consoleLogStub.getCall(0).args[0]; + expect(logArg).to.include('custom.event.name'); + expect(logArg).to.not.include('system.custom.event.name'); + }); + + it('should use provided timestamps', () => { + const opts = { + event_source_timestamp: 1000, + execution_end_timestamp: 2000, + execution_start_timestamp: 1500, + duration: 500 + }; + leolog.systemRead('bot-id', 'queue.name', 100, opts); + + const logArg = consoleLogStub.getCall(0).args[0]; + expect(logArg).to.include('[LEOLOG]:v2:'); + }); + + it('should use provided runs and errors', () => { + const opts = { + runs: 5, + errors: 2, + consumption: 100 + }; + leolog.systemRead('bot-id', 'queue.name', 100, opts); + + const logArg = consoleLogStub.getCall(0).args[0]; + expect(logArg).to.include('[LEOLOG]:v2:'); + }); + + it('should handle function as opts (legacy support)', () => { + // When opts is a function, it should be treated as empty opts + leolog.systemRead('bot-id', 'queue.name', 100, function() {}); + + expect(consoleLogStub.calledOnce).to.be.true; + }); + }); + + describe("systemWrite", () => { + it('should log a system write event', () => { + leolog.systemWrite('bot-id', 'queue.name', 100, {}); + + expect(consoleLogStub.calledOnce).to.be.true; + const logArg = consoleLogStub.getCall(0).args[0]; + expect(logArg).to.include('[LEOLOG]:v2:'); + expect(logArg).to.include('leo:kinesisWriteEvents:queue.name'); + }); + + it('should prepend "system." to event name if not present', () => { + leolog.systemWrite('bot-id', 'my-event', 50, {}); + + const logArg = consoleLogStub.getCall(0).args[0]; + expect(logArg).to.include('system.my-event'); + }); + + it('should not modify event name with dots', () => { + leolog.systemWrite('bot-id', 'custom.event.name', 50, {}); + + const logArg = consoleLogStub.getCall(0).args[0]; + expect(logArg).to.include('custom.event.name'); + expect(logArg).to.not.include('system.custom.event.name'); + }); + + it('should use provided timestamps and options', () => { + const opts = { + event_source_timestamp: 1000, + execution_end_timestamp: 2000, + execution_start_timestamp: 1500, + duration: 500, + runs: 3, + errors: 1, + consumption: 75, + extra: { custom: 'data' } + }; + leolog.systemWrite('bot-id', 'queue.name', 100, opts); + + const logArg = consoleLogStub.getCall(0).args[0]; + expect(logArg).to.include('[LEOLOG]:v2:'); + }); + + it('should handle function as opts (legacy support)', () => { + leolog.systemWrite('bot-id', 'queue.name', 100, function() {}); + + expect(consoleLogStub.calledOnce).to.be.true; + }); + }); + + describe("loggers", () => { + it('v1 logger should format correctly', () => { + const entry = { + runs: 5, + start: 1000, + end: 2000, + units: 100, + duration: 1000, + min_duration: 100, + max_duration: 500, + consumption: 50, + errors: 2, + id: 'test-bot' + }; + + leolog.loggers.v1(entry); + + const logArg = consoleLogStub.getCall(0).args[0]; + expect(logArg).to.equal('[LEOLOG]:v1:5:1000:2000:100:1000:100:500:50:2:test-bot'); + }); + + it('v2 logger should format correctly with JSON', () => { + const entry = { + runs: 5, + start: 1000, + end: 2000, + units: 100, + duration: 1000, + min_duration: 100, + max_duration: 500, + consumption: 50, + errors: 2, + id: 'test-bot', + completions: 3, + options: { key: 'value' } + }; + + leolog.loggers.v2(entry); + + const logArg = consoleLogStub.getCall(0).args[0]; + expect(logArg).to.include('[LEOLOG]:v2:'); + expect(logArg).to.include('"p":'); + expect(logArg).to.include('"e":'); + }); + }); +});