diff --git a/integration-tests/cli/test/execute-job.test.ts b/integration-tests/cli/test/execute-job.test.ts deleted file mode 100644 index 076d041d8..000000000 --- a/integration-tests/cli/test/execute-job.test.ts +++ /dev/null @@ -1,191 +0,0 @@ -import test from 'ava'; -import { writeFileSync } from 'node:fs'; -import { rm, mkdir } from 'node:fs/promises'; -import path from 'node:path'; -import run from '../src/run'; -import { assertLog, extractLogs, getJSON } from '../src/util'; - -const jobsPath = path.resolve('test/fixtures'); -const tmpPath = path.resolve('tmp'); - -// Note that these tests are STATEFUL -// Ensure the repo is clean and clear before these tests run -test.before(async () => { - await mkdir('tmp', { recursive: true }); - await run('openfn repo clean -f --log none'); -}); - -test.afterEach(async () => { - try { - await rm('test/fixtures/output.json'); - } catch (e) {} - try { - await rm('tmp/.', { recursive: true }); - } catch (e) {} -}); - -test.serial(`openfn ${jobsPath}/simple.js -ia common`, async (t) => { - await run(t.title); - - const out = getJSON(); - t.is(out.data.count, 42); -}); - -// Auto-install not needed here because common was installed in the previous test -test.serial( - `openfn ${jobsPath}/simple.js -a @openfn/language-common`, - async (t) => { - await run(t.title); - - const out = getJSON(); - t.is(out.data.count, 42); - } -); - -test.serial( - `openfn ${jobsPath}/simple.js -a common -o ${tmpPath}/o.json`, - async (t) => { - await run(t.title); - - const out = getJSON(`${tmpPath}/o.json`); - t.is(out.data.count, 42); - } -); - -test.serial(`openfn ${jobsPath}/simple.js -a common -O`, async (t) => { - const { stdout } = await run(t.title); - - await t.throws(() => getJSON()); - t.regex(stdout, /Result:/); - t.regex(stdout, /42/); -}); - -// TOOD also failing because I've broken exit codes -test.serial.skip( - `openfn ${jobsPath}/simple.js -a common --ignore-imports`, - async (t) => { - const { err } = await run(t.title); - t.regex(err.message, /(fn is not defined)/); - } -); - -// TODO I think I've broken the exit code so we don't exit with an error now... -test.serial.skip( - `openfn ${jobsPath}/simple.js -a common --ignore-imports=fn`, - async (t) => { - const { stdout, err } = await run(t.title); - t.regex(err.message, /(fn is not defined)/); - - // t.regex(stdout, /CRITICAL ERROR!/); - // t.regex(stdout, /ReferenceError/); - // t.regex(stdout, /fn is not defined/);; - } -); - -test.serial( - `openfn ${jobsPath}/simple.js -a @openfn/language-common -s ${tmpPath}/state.json`, - async (t) => { - await writeFileSync(`${tmpPath}/state.json`, '{ "data": { "count": 2 } }'); - await run(t.title); - - const out = getJSON(); - t.is(out.data.count, 4); - } -); - -test.serial( - `openfn ${jobsPath}/simple.js -a @openfn/language-common -S "{ \\"data\\": { \\"count\\": 6 } }"`, - async (t) => { - await run(t.title); - - const out = getJSON(); - t.is(out.data.count, 12); - } -); - -test.serial( - `openfn ${jobsPath}/chuck.js -ia @openfn/language-http`, - async (t) => { - await run(t.title); - - const out = getJSON(); - t.truthy(out.data.value); - t.regex(out.data.value, /chuck norris/i); - } -); - -test.serial( - `openfn ${jobsPath}/log.js -a @openfn/language-common --sanitize=obfuscate`, - async (t) => { - const { stdout } = await run(t.title); - - const jobLog = stdout.split(/\n/).find((l) => l.startsWith('[JOB]')); - - t.truthy(jobLog); - t.true(jobLog.endsWith('[object]')); - } -); - -test.serial( - `openfn ${jobsPath}/log.js -a @openfn/language-common --sanitize=obfuscate --log-json`, - async (t) => { - const { stdout } = await run(t.title); - - const stdlogs = extractLogs(stdout); - - const log = stdlogs.find( - ({ message, name }) => name === 'JOB' && message[0] === '[object]' - ); - t.truthy(log); - } -); - -test.serial( - `openfn ${jobsPath}/log.js -a @openfn/language-common --sanitize=remove`, - async (t) => { - const { stdout } = await run(t.title); - - const jobLog = stdout.split(/\n/).find((l) => l.startsWith('[JOB]')); - - t.falsy(jobLog); - } -); - -test.serial( - `openfn ${jobsPath}/log.js -a @openfn/language-common --sanitize=remove --log-json`, - async (t) => { - const { stdout } = await run(t.title); - - const stdlogs = extractLogs(stdout); - - const log = stdlogs.find(({ name }) => name === 'JOB'); - t.falsy(log); - } -); - -test.serial( - `openfn ${jobsPath}/log.js -a @openfn/language-common --sanitize=summarize`, - async (t) => { - const { stdout } = await run(t.title); - - const jobLog = stdout.split(/\n/).find((l) => l.startsWith('[JOB]')); - t.truthy(jobLog); - t.true(jobLog.endsWith('(object with keys configuration, data)')); - } -); - -test.serial( - `openfn ${jobsPath}/log.js -a @openfn/language-common --sanitize=summarize --log-json`, - async (t) => { - const { stdout } = await run(t.title); - - const stdlogs = extractLogs(stdout); - - const log = stdlogs.find( - ({ message, name }) => - name === 'JOB' && - message[0] === '(object with keys configuration, data)' - ); - t.truthy(log); - } -); diff --git a/integration-tests/cli/test/fixtures/chuck.js b/integration-tests/cli/test/fixtures/chuck.js deleted file mode 100644 index 8fdc55f58..000000000 --- a/integration-tests/cli/test/fixtures/chuck.js +++ /dev/null @@ -1,3 +0,0 @@ -get('https://api.chucknorris.io/jokes/random'); - -fn((state) => ({ data: { value: state.data.value } })); diff --git a/integration-tests/cli/test/fixtures/http.js b/integration-tests/cli/test/fixtures/http.js new file mode 100644 index 000000000..1279cf9dc --- /dev/null +++ b/integration-tests/cli/test/fixtures/http.js @@ -0,0 +1 @@ +get('https://jsonplaceholder.typicode.com/todos/1'); diff --git a/integration-tests/worker/src/init.ts b/integration-tests/worker/src/init.ts new file mode 100644 index 000000000..19777be5d --- /dev/null +++ b/integration-tests/worker/src/init.ts @@ -0,0 +1,36 @@ +import path from 'node:path'; +import crypto from 'node:crypto'; + +import createLightningServer from '@openfn/lightning-mock'; +import createEngine from '@openfn/engine-multi'; +import createWorkerServer from '@openfn/ws-worker'; +import { createMockLogger } from '@openfn/logger'; + +export const randomPort = () => parseInt(2000 + Math.random() * 1000); + +export const initLightning = (port = 4000) => { + // TODO the lightning mock right now doesn't use the secret + // but we may want to add tests against this + return createLightningServer({ port }); +}; + +export const initWorker = async (lightningPort, engineArgs = {}) => { + const workerPort = randomPort(); + + const engine = await createEngine({ + // logger: createLogger('engine', { level: 'debug' }), + logger: createMockLogger(), + repoDir: path.resolve('./tmp/repo/default'), + ...engineArgs, + }); + + const worker = createWorkerServer(engine, { + logger: createMockLogger(), + // logger: createLogger('worker', { level: 'debug' }), + port: workerPort, + lightning: `ws://localhost:${lightningPort}/worker`, + secret: crypto.randomUUID(), + }); + + return { engine, worker }; +}; diff --git a/integration-tests/worker/test/exit-reasons.test.ts b/integration-tests/worker/test/exit-reasons.test.ts new file mode 100644 index 000000000..6be55871e --- /dev/null +++ b/integration-tests/worker/test/exit-reasons.test.ts @@ -0,0 +1,55 @@ +import test from 'ava'; +import crypto from 'node:crypto'; +import path from 'node:path'; + +import { initLightning, initWorker } from '../src/init'; + +let lightning; +let worker; + +test.before(async () => { + const lightningPort = 4321; + + lightning = initLightning(lightningPort); + + ({ worker } = await initWorker(lightningPort, { + repoDir: path.resolve('tmp/openfn/repo/exit-reason'), + })); +}); + +test.after(async () => { + lightning.destroy(); + await worker.destroy(); +}); + +const run = async (attempt) => { + return new Promise(async (done) => { + lightning.on('attempt:complete', (evt) => { + if (attempt.id === evt.attemptId) { + done(evt.payload); + } + }); + + lightning.enqueueAttempt(attempt); + }); +}; + +test('crash: syntax error', async (t) => { + const attempt = { + id: crypto.randomUUID(), + jobs: [ + { + adaptor: '@openfn/language-common@latest', + body: 'fn(() => throw "e")', + }, + ], + }; + + const result = await run(attempt); + + const { reason, error_type, error_message } = result; + + t.is(reason, 'crash'); + t.is(error_type, 'CompileError'); + t.regex(error_message, /Unexpected token \(1:9\)$/); +}); diff --git a/integration-tests/worker/test/integration.test.ts b/integration-tests/worker/test/integration.test.ts index e4e13afe6..a776dd230 100644 --- a/integration-tests/worker/test/integration.test.ts +++ b/integration-tests/worker/test/integration.test.ts @@ -3,74 +3,31 @@ import path from 'node:path'; import crypto from 'node:crypto'; import Koa from 'koa'; -import createLightningServer from '@openfn/lightning-mock'; - -import createEngine from '@openfn/engine-multi'; -import createWorkerServer from '@openfn/ws-worker'; - -import createLogger, { createMockLogger } from '@openfn/logger'; +import { initLightning, initWorker, randomPort } from '../src/init'; let lightning; let worker; let engine; +let lightningPort; + +test.before(async () => { + lightningPort = randomPort(); + lightning = initLightning(lightningPort); + ({ worker, engine } = await initWorker(lightningPort, { + maxWorkers: 1, + purge: false, + repoDir: path.resolve('tmp/openfn/repo/integration'), + })); +}); -test.afterEach(async () => { +test.after(async () => { lightning.destroy(); await worker.destroy(); }); -const initLightning = () => { - // TODO the lightning mock right now doesn't use the secret - // but we may want to add tests against this - lightning = createLightningServer({ port: 9999 }); -}; - -const initWorker = async (engineArgs = {}) => { - engine = await createEngine({ - // logger: createLogger('engine', { level: 'debug' }), - logger: createMockLogger(), - repoDir: path.resolve('./tmp/repo'), - ...engineArgs, - }); - - worker = createWorkerServer(engine, { - logger: createMockLogger(), - // logger: createLogger('worker', { level: 'debug' }), - port: 2222, - lightning: 'ws://localhost:9999/worker', - secret: crypto.randomUUID(), - }); -}; - -test('should connect to lightning', (t) => { - return new Promise((done) => { - initLightning(); - lightning.on('socket:connect', () => { - t.pass('connection recieved'); - done(); - }); - initWorker(); - }); -}); - -test('should join attempts queue channel', (t) => { - return new Promise((done) => { - initLightning(); - lightning.on('socket:channel-join', ({ channel }) => { - if (channel === 'worker:queue') { - t.pass('joined channel'); - done(); - } - }); - initWorker(); - }); -}); - test('should run a simple job with no compilation or adaptor', (t) => { return new Promise(async (done) => { - initLightning(); - - lightning.on('attempt:complete', (evt) => { + lightning.once('attempt:complete', (evt) => { // This will fetch the final dataclip from the attempt const result = lightning.getResult('a1'); t.deepEqual(result, { data: { answer: 42 } }); @@ -79,8 +36,6 @@ test('should run a simple job with no compilation or adaptor', (t) => { done(); }); - await initWorker(); - lightning.enqueueAttempt({ id: 'a1', jobs: [ @@ -95,11 +50,9 @@ test('should run a simple job with no compilation or adaptor', (t) => { test('run a job with autoinstall of common', (t) => { return new Promise(async (done) => { - initLightning(); - let autoinstallEvent; - lightning.on('attempt:complete', (evt) => { + lightning.once('attempt:complete', (evt) => { try { t.truthy(autoinstallEvent); t.is(autoinstallEvent.module, '@openfn/language-common'); @@ -118,8 +71,6 @@ test('run a job with autoinstall of common', (t) => { } }); - await initWorker(); - // listen to events for this attempt engine.listen('a33', { 'autoinstall-complete': (evt) => { @@ -142,10 +93,8 @@ test('run a job with autoinstall of common', (t) => { // this depends on prior test! test('run a job which does NOT autoinstall common', (t) => { - return new Promise(async (done, _fail) => { - initLightning(); - - lightning.on('attempt:complete', (evt) => { + return new Promise(async (done) => { + lightning.once('attempt:complete', () => { try { // This will fetch the final dataclip from the attempt const result = lightning.getResult('a10'); @@ -158,8 +107,6 @@ test('run a job which does NOT autoinstall common', (t) => { } }); - await initWorker(); - // listen to events for this attempt engine.listen('a10', { 'autoinstall-complete': (evt) => { @@ -195,13 +142,11 @@ test('run a job with initial state (with data)', (t) => { ], }; - initLightning(); - const initialState = { data: { name: 'Professor X' } }; lightning.addDataclip('s1', initialState); - lightning.on('attempt:complete', () => { + lightning.once('attempt:complete', () => { const result = lightning.getResult(attempt.id); t.deepEqual(result, { ...initialState, @@ -209,8 +154,6 @@ test('run a job with initial state (with data)', (t) => { done(); }); - await initWorker(); - // TODO: is there any way I can test the worker behaviour here? // I think I can listen to load-state right? // well, not really, not yet, not from the worker @@ -233,13 +176,11 @@ test('run a job with initial state (no top level keys)', (t) => { ], }; - initLightning(); - const initialState = { name: 'Professor X' }; lightning.addDataclip('s1', initialState); - lightning.on('attempt:complete', () => { + lightning.once('attempt:complete', () => { const result = lightning.getResult(attempt.id); t.deepEqual(result, { ...initialState, @@ -248,8 +189,6 @@ test('run a job with initial state (no top level keys)', (t) => { done(); }); - await initWorker(); - // TODO: is there any way I can test the worker behaviour here? // I think I can listen to load-state right? // well, not really, not yet, not from the worker @@ -271,9 +210,7 @@ test('run a http adaptor job', (t) => { ], }; - initLightning(); - - lightning.on('attempt:complete', () => { + lightning.once('attempt:complete', () => { const result = lightning.getResult(attempt.id); t.truthy(result.response); @@ -290,8 +227,6 @@ test('run a http adaptor job', (t) => { done(); }); - await initWorker(); - lightning.enqueueAttempt(attempt); }); }); @@ -360,7 +295,6 @@ test.skip('run a job with credentials', (t) => { done(); }); - await initWorker(); lightning.enqueueAttempt(attempt); }); }); @@ -377,24 +311,17 @@ test('blacklist a non-openfn adaptor', (t) => { ], }; - initLightning(); - - // At the moment the error comes back to on complete - lightning.on('attempt:complete', (event) => { + lightning.once('attempt:complete', (event) => { const { payload } = event; t.is(payload.reason, 'crash'); // TODO actually this should be a kill t.is(payload.error_message, 'module blacklisted: lodash'); done(); }); - await initWorker(); - lightning.enqueueAttempt(attempt); }); }); -test.todo('return some kind of error on compilation error'); - // test('run a job with complex behaviours (initial state, branching)', (t) => { // const attempt = { // id: 'a1', @@ -430,13 +357,12 @@ test.todo('return some kind of error on compilation error'); // }); // }); +// TODO this probably needs to move out into another test file +// not going to do it now as I've changed too much too quickly already... test('stateful adaptor should create a new client for each attempt', (t) => { return new Promise(async (done) => { - const engineArgs = { - repoDir: path.resolve('./dummy-repo'), - maxWorkers: 1, - purge: false, - }; + // We want to create our own special worker here + await worker.destroy(); const attempt1 = { id: crypto.randomUUID(), @@ -457,7 +383,6 @@ test('stateful adaptor should create a new client for each attempt', (t) => { }; let results = {}; - initLightning(); lightning.on('attempt:complete', (evt) => { const id = evt.attemptId; results[id] = lightning.getResult(id); @@ -481,9 +406,17 @@ test('stateful adaptor should create a new client for each attempt', (t) => { // done() // }) - await initWorker(engineArgs); + const engineArgs = { + repoDir: path.resolve('./dummy-repo'), + maxWorkers: 1, + purge: false, + }; + await initWorker(lightningPort, engineArgs); lightning.enqueueAttempt(attempt1); lightning.enqueueAttempt(attempt2); }); }); + +// REMEMBER the default worker was destroyed at this point! +// If you want to use a worker, you'll have to create your own diff --git a/integration-tests/worker/test/server.test.ts b/integration-tests/worker/test/server.test.ts new file mode 100644 index 000000000..52848a3ff --- /dev/null +++ b/integration-tests/worker/test/server.test.ts @@ -0,0 +1,45 @@ +import test from 'ava'; + +import { initLightning, initWorker } from '../src/init'; + +let lightning; +let worker; + +test.afterEach(async () => { + lightning.destroy(); + await worker.destroy(); +}); + +let portgen = 3000; + +const getPort = () => ++portgen; + +test('should connect to lightning', (t) => { + return new Promise(async (done) => { + const port = getPort(); + lightning = initLightning(port); + + lightning.on('socket:connect', () => { + t.pass('connection recieved'); + done(); + }); + + ({ worker } = await initWorker(port)); + }); +}); + +test('should join attempts queue channel', (t) => { + return new Promise(async (done) => { + const port = getPort(); + lightning = initLightning(port); + + lightning.on('socket:channel-join', ({ channel }) => { + if (channel === 'worker:queue') { + t.pass('joined channel'); + done(); + } + }); + + ({ worker } = await initWorker(port)); + }); +}); diff --git a/packages/engine-multi/src/api/autoinstall.ts b/packages/engine-multi/src/api/autoinstall.ts index d5fd0356f..eb47defd7 100644 --- a/packages/engine-multi/src/api/autoinstall.ts +++ b/packages/engine-multi/src/api/autoinstall.ts @@ -88,8 +88,6 @@ const autoinstall = async (context: ExecutionContext): Promise => { }) .catch((e) => { const duration = Date.now() - startTime; - console.log('AUTOINSTALL ERROR'); - console.log(e); context.emit(AUTOINSTALL_ERROR, { module: name, version: version!, diff --git a/packages/engine-multi/test/errors.test.ts b/packages/engine-multi/test/errors.test.ts new file mode 100644 index 000000000..165afe419 --- /dev/null +++ b/packages/engine-multi/test/errors.test.ts @@ -0,0 +1,70 @@ +import test from 'ava'; +import createEngine from '../src/engine'; +import { createMockLogger } from '@openfn/logger'; +import { WORKFLOW_ERROR } from '../src/events'; + +let engine; + +test.before(async () => { + const logger = createMockLogger('', { level: 'debug' }); + + const options = { + logger, + repoDir: '.', + autoinstall: { + // disable autoinstall + handleIsInstalled: async () => true, + }, + maxWorkers: 1, + }; + + // This uses the real runtime and real worker + engine = await createEngine(options); +}); + +// This should exit gracefully with a compile error +test('syntax error: missing bracket', (t) => { + return new Promise((done) => { + const plan = { + id: 'a', + jobs: [ + { + id: 'x', + // This is subtle syntax error + expression: 'fn((s) => { return s )', + }, + ], + }; + + engine.execute(plan).on(WORKFLOW_ERROR, (evt) => { + t.is(evt.type, 'CompileError'); + // compilation happens in the main thread + t.is(evt.threadId, '-'); + t.is(evt.message, 'x: Unexpected token (1:21)'); + done(); + }); + }); +}); + +test('syntax error: illegal throw', (t) => { + return new Promise((done) => { + const plan = { + id: 'b', + jobs: [ + { + id: 'z', + // This is also subtle syntax error + expression: 'fn(() => throw "e")', + }, + ], + }; + + engine.execute(plan).on(WORKFLOW_ERROR, (evt) => { + t.is(evt.type, 'CompileError'); + // compilation happens in the main thread + t.is(evt.threadId, '-'); + t.is(evt.message, 'z: Unexpected token (1:9)'); + done(); + }); + }); +}); diff --git a/packages/ws-worker/src/api/execute.ts b/packages/ws-worker/src/api/execute.ts index a276d9937..ffcd6cee0 100644 --- a/packages/ws-worker/src/api/execute.ts +++ b/packages/ws-worker/src/api/execute.ts @@ -81,7 +81,7 @@ export function execute( options: AttemptOptions = {}, onComplete = (_result: any) => {} ) { - logger.info('execute...'); + logger.info('executing ', plan.id); const state = createAttemptState(plan, options); @@ -139,7 +139,7 @@ export function execute( Promise.resolve() // Optionally resolve initial state .then(async () => { - // TODO we need to remove this from here nad let the runtime take care of it through + // TODO we need to remove this from here and let the runtime take care of it through // the resolver. See https://github.com/OpenFn/kit/issues/403 if (typeof plan.initialState === 'string') { logger.debug('loading dataclip', plan.initialState); diff --git a/packages/ws-worker/test.js b/packages/ws-worker/test.js new file mode 100644 index 000000000..708a372bf --- /dev/null +++ b/packages/ws-worker/test.js @@ -0,0 +1,17 @@ +import { Socket } from 'phoenix'; +import { WebSocket } from 'ws'; + +const socket = new Socket('http://localhost:4000/worker', { + transport: WebSocket, +}); + +socket.onOpen(() => { + console.log('OPEN'); +}); + +socket.onError((e) => { + console.log('ERROR'); + console.log(e); +}); + +socket.connect(); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3aae0f3be..3c352c711 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -146,12 +146,6 @@ importers: specifier: ^5.1.6 version: 5.1.6 - integration-tests/worker/tmp/repo: - dependencies: - '@openfn/language-common_latest': - specifier: npm:@openfn/language-common@^1.11.1 - version: /@openfn/language-common@1.11.1 - packages/cli: dependencies: '@inquirer/prompts': @@ -1336,11 +1330,6 @@ packages: heap: 0.2.7 dev: false - /@fastify/busboy@2.0.0: - resolution: {integrity: sha512-JUFJad5lv7jxj926GPgymrWQxxjPYuJNiNjNMzqT+HiuP6Vl3dk5xzG+8sTX96np0ZAluvaMzPsjhHZ5rNuNQQ==} - engines: {node: '>=14'} - dev: false - /@inquirer/checkbox@1.3.5: resolution: {integrity: sha512-ZznkPU+8XgNICKkqaoYENa0vTw9jeToEHYyG5gUKpGmY+4PqPTsvLpSisOt9sukLkYzPRkpSCHREgJLqbCG3Fw==} engines: {node: '>=14.18.0'} @@ -1606,21 +1595,6 @@ packages: semver: 7.5.4 dev: true - /@openfn/language-common@1.11.1: - resolution: {integrity: sha512-pyi2QymdF9NmUYJX/Bsv5oBy7TvzICfKcnCqutq412HYq2KTGKDO2dMWloDrxrH1kuzG+4XkSn0ZUom36b3KAA==} - dependencies: - ajv: 8.12.0 - axios: 1.1.3 - csv-parse: 5.5.2 - csvtojson: 2.0.10 - date-fns: 2.30.0 - jsonpath-plus: 4.0.0 - lodash: 4.17.21 - undici: 5.27.2 - transitivePeerDependencies: - - debug - dev: false - /@openfn/language-common@1.7.5: resolution: {integrity: sha512-QivV3v5Oq5fb4QMopzyqUUh+UGHaFXBdsGr6RCmu6bFnGXdJdcQ7GpGpW5hKNq29CkmE23L/qAna1OLr4rP/0w==} dependencies: @@ -2082,15 +2056,6 @@ packages: clean-stack: 4.2.0 indent-string: 5.0.0 - /ajv@8.12.0: - resolution: {integrity: sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==} - dependencies: - fast-deep-equal: 3.1.3 - json-schema-traverse: 1.0.0 - require-from-string: 2.0.2 - uri-js: 4.4.1 - dev: false - /ansi-colors@4.1.3: resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} engines: {node: '>=6'} @@ -2251,6 +2216,7 @@ packages: /asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + dev: true /atob@2.1.2: resolution: {integrity: sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==} @@ -2398,6 +2364,7 @@ packages: proxy-from-env: 1.1.0 transitivePeerDependencies: - debug + dev: true /b4a@1.6.1: resolution: {integrity: sha512-AsKjNhz72yxteo/0EtQEiwkMUgk/tGmycXlbG4g3Ard2/ULtNLUykGOkeK0egmN27h0xMAhb76jYccW+XTBExA==} @@ -2478,10 +2445,6 @@ packages: readable-stream: 4.2.0 dev: true - /bluebird@3.7.2: - resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==} - dev: false - /blueimp-md5@2.19.0: resolution: {integrity: sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w==} @@ -2878,6 +2841,7 @@ packages: engines: {node: '>= 0.8'} dependencies: delayed-stream: 1.0.0 + dev: true /commander@4.1.1: resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} @@ -3020,10 +2984,6 @@ packages: resolution: {integrity: sha512-cO1I/zmz4w2dcKHVvpCr7JVRu8/FymG5OEpmvsZYlccYolPBLoVGKUHgNoc4ZGkFeFlWGEDmMyBM+TTqRdW/wg==} dev: true - /csv-parse@5.5.2: - resolution: {integrity: sha512-YRVtvdtUNXZCMyK5zd5Wty1W6dNTpGKdqQd4EQ8tl/c6KW1aMBB1Kg1ppky5FONKmEqGJ/8WjLlTNLPne4ioVA==} - dev: false - /csv-stringify@5.6.5: resolution: {integrity: sha512-PjiQ659aQ+fUTQqSrd1XEDnOr52jh30RBurfzkscaE2tPaFsDH5wOAHJiw8XAHphRknCwMUE9KRayc4K/NbO8A==} dev: true @@ -3038,16 +2998,6 @@ packages: stream-transform: 2.1.3 dev: true - /csvtojson@2.0.10: - resolution: {integrity: sha512-lUWFxGKyhraKCW8Qghz6Z0f2l/PqB1W3AO0HKJzGIQ5JRSlR651ekJDiGJbBT4sRNNv5ddnSGVEnsxP9XRCVpQ==} - engines: {node: '>=4.0.0'} - hasBin: true - dependencies: - bluebird: 3.7.2 - lodash: 4.17.21 - strip-bom: 2.0.0 - dev: false - /currently-unhandled@0.4.1: resolution: {integrity: sha512-/fITjgjGU50vjQ4FH6eUoYu+iUoUKIXws2hL15JJpIR+BbTxaXQsMuuyjtNh2WqsSBS5nsaZHFsFecyw5CCAng==} engines: {node: '>=0.10.0'} @@ -3188,6 +3138,7 @@ packages: /delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} + dev: true /delegates@1.0.0: resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} @@ -4013,10 +3964,6 @@ packages: - supports-color dev: true - /fast-deep-equal@3.1.3: - resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - dev: false - /fast-diff@1.3.0: resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} @@ -4173,6 +4120,7 @@ packages: peerDependenciesMeta: debug: optional: true + dev: true /for-in@1.0.2: resolution: {integrity: sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==} @@ -4203,6 +4151,7 @@ packages: asynckit: 0.4.0 combined-stream: 1.0.8 mime-types: 2.1.35 + dev: true /fragment-cache@0.2.1: resolution: {integrity: sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==} @@ -4985,10 +4934,6 @@ packages: resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} engines: {node: '>=12'} - /is-utf8@0.2.1: - resolution: {integrity: sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==} - dev: false - /is-weakref@1.0.2: resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} dependencies: @@ -5082,10 +5027,6 @@ packages: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} dev: true - /json-schema-traverse@1.0.0: - resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} - dev: false - /jsonfile@4.0.0: resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} optionalDependencies: @@ -5100,6 +5041,7 @@ packages: /jsonpath-plus@4.0.0: resolution: {integrity: sha512-e0Jtg4KAzDJKKwzbLaUtinCn0RZseWBVRTRGihSpvFlM3wTR7ExSp+PTdeTsDrLNJUe7L7JYJe8mblHX5SCT6A==} engines: {node: '>=10.0'} + dev: true /jsonpath@1.1.1: resolution: {integrity: sha512-l6Cg7jRpixfbgoWgkrl77dgEj8RPvND0wMH6TwQmi9Qs4TFfS9u5cUFnbeKTwj5ga5Y3BTGGNI28k117LJ009w==} @@ -6406,6 +6348,7 @@ packages: /proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + dev: true /proxy-middleware@0.15.0: resolution: {integrity: sha512-EGCG8SeoIRVMhsqHQUdDigB2i7qU7fCsWASwn54+nPutYO8n4q6EiwMzyfWlC+dzRFExP+kvcnDFdBDHoZBU7Q==} @@ -6438,6 +6381,7 @@ packages: /punycode@2.3.0: resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} engines: {node: '>=6'} + dev: true /qs@6.11.2: resolution: {integrity: sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==} @@ -6643,11 +6587,6 @@ packages: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} - /require-from-string@2.0.2: - resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} - engines: {node: '>=0.10.0'} - dev: false - /require-main-filename@2.0.0: resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} dev: true @@ -7171,13 +7110,6 @@ packages: dependencies: ansi-regex: 6.0.1 - /strip-bom@2.0.0: - resolution: {integrity: sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g==} - engines: {node: '>=0.10.0'} - dependencies: - is-utf8: 0.2.1 - dev: false - /strip-bom@3.0.0: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} @@ -7731,13 +7663,6 @@ packages: resolution: {integrity: sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw==} dev: true - /undici@5.27.2: - resolution: {integrity: sha512-iS857PdOEy/y3wlM3yRp+6SNQQ6xU0mmZcwRSriqk+et/cwWAtwmIGf6WkoDN2EK/AMdCO/dfXzIwi+rFMrjjQ==} - engines: {node: '>=14.0'} - dependencies: - '@fastify/busboy': 2.0.0 - dev: false - /union-value@1.0.1: resolution: {integrity: sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==} engines: {node: '>=0.10.0'} @@ -7788,12 +7713,6 @@ packages: engines: {node: '>=4'} dev: true - /uri-js@4.4.1: - resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - dependencies: - punycode: 2.3.0 - dev: false - /urix@0.1.0: resolution: {integrity: sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==} deprecated: Please see https://github.com/lydell/urix#deprecated