diff --git a/integration-tests/cli/CHANGELOG.md b/integration-tests/cli/CHANGELOG.md index 919bf2e0e..667714cfd 100644 --- a/integration-tests/cli/CHANGELOG.md +++ b/integration-tests/cli/CHANGELOG.md @@ -1,5 +1,11 @@ # @openfn/integration-tests-cli +## 1.0.3 + +### Patch Changes + +- @openfn/lightning-mock@2.2.1 + ## 1.0.2 ### Patch Changes diff --git a/integration-tests/cli/package.json b/integration-tests/cli/package.json index b61a64bca..b3c4799ce 100644 --- a/integration-tests/cli/package.json +++ b/integration-tests/cli/package.json @@ -1,7 +1,7 @@ { "name": "@openfn/integration-tests-cli", "private": true, - "version": "1.0.2", + "version": "1.0.3", "description": "CLI integration tests", "author": "Open Function Group ", "license": "ISC", diff --git a/integration-tests/cli/test/errors.test.ts b/integration-tests/cli/test/errors.test.ts index 4bef066e5..35003a8cf 100644 --- a/integration-tests/cli/test/errors.test.ts +++ b/integration-tests/cli/test/errors.test.ts @@ -88,7 +88,11 @@ test.serial("can't find config referenced in a workflow", async (t) => { const stdlogs = extractLogs(stdout); - assertLog(t, stdlogs, /File not found for job 1: does-not-exist.js/i); + assertLog( + t, + stdlogs, + /File not found for job configuration 1: does-not-exist.js/i + ); assertLog( t, stdlogs, diff --git a/integration-tests/cli/test/execute-workflow.test.ts b/integration-tests/cli/test/execute-workflow.test.ts index 8c4216a5d..da069b938 100644 --- a/integration-tests/cli/test/execute-workflow.test.ts +++ b/integration-tests/cli/test/execute-workflow.test.ts @@ -221,3 +221,55 @@ test.serial( }); } ); + +test.serial(`openfn ${jobsPath}/globals-exp.json`, async (t) => { + const res = await run(t.title); + t.falsy(res.err); + const out = getJSON(); + t.deepEqual(out, { + alter: 'heartsfx', + data: {}, + final: 'some-big-valueheartsfx', + val: 'some-big-value', + }); +}); + +test.serial(`openfn ${jobsPath}/globals-path.json`, async (t) => { + const res = await run(t.title); + t.falsy(res.err); + const out = getJSON(); + t.deepEqual(out, { + alter: 'heart.path.value', + data: {}, + final: 'path-valueheart.path.value', + val: 'path-value', + }); +}); + +test.serial( + `openfn ${jobsPath}/globals-job.js --globals="export const suffixer = w => w + '-some-suffix'" -a common`, + async (t) => { + const res = await run(t.title); + t.falsy(res.err); + const out = getJSON(); + t.deepEqual(out, { + data: { + result: 'love-some-suffix', + }, + }); + } +); + +test.serial( + `openfn ${jobsPath}/globals-job.js --globals ${jobsPath}/globals-path-file.js -a common`, + async (t) => { + const res = await run(t.title); + t.falsy(res.err); + const out = getJSON(); + t.deepEqual(out, { + data: { + result: 'love-humble-suffix', + }, + }); + } +); diff --git a/integration-tests/cli/test/fixtures/globals-exp.json b/integration-tests/cli/test/fixtures/globals-exp.json new file mode 100644 index 000000000..ba73616df --- /dev/null +++ b/integration-tests/cli/test/fixtures/globals-exp.json @@ -0,0 +1,19 @@ +{ + "workflow": { + "globals": "export const BIG_VAL = 'some-big-value'; export function suffix(name){ return name + 'sfx'}", + "steps": [ + { + "adaptor": "common", + "expression": "fn(state=> ({val: BIG_VAL, alter: suffix('heart')}))", + "next": { + "b": true + } + }, + { + "id": "b", + "adaptor": "common", + "expression": "fn((state) => { state.final = state.val + state.alter; return state; });" + } + ] + } +} \ No newline at end of file diff --git a/integration-tests/cli/test/fixtures/globals-job.js b/integration-tests/cli/test/fixtures/globals-job.js new file mode 100644 index 000000000..387eb37d5 --- /dev/null +++ b/integration-tests/cli/test/fixtures/globals-job.js @@ -0,0 +1,3 @@ +fn(() => { + return { data: { result: suffixer('love') } } +}) \ No newline at end of file diff --git a/integration-tests/cli/test/fixtures/globals-path-file.js b/integration-tests/cli/test/fixtures/globals-path-file.js new file mode 100644 index 000000000..b7689ee97 --- /dev/null +++ b/integration-tests/cli/test/fixtures/globals-path-file.js @@ -0,0 +1,7 @@ +export const BIG_VAL = 'path-value'; + +export function suffix(name) { + return name + '.path.value' +} + +export const suffixer = w => w + '-humble-suffix' \ No newline at end of file diff --git a/integration-tests/cli/test/fixtures/globals-path.json b/integration-tests/cli/test/fixtures/globals-path.json new file mode 100644 index 000000000..73173f835 --- /dev/null +++ b/integration-tests/cli/test/fixtures/globals-path.json @@ -0,0 +1,19 @@ +{ + "workflow": { + "globals": "./globals-path-file.js", + "steps": [ + { + "adaptor": "common", + "expression": "fn(state=> ({val: BIG_VAL, alter: suffix('heart')}))", + "next": { + "b": true + } + }, + { + "id": "b", + "adaptor": "common", + "expression": "fn((state) => { state.final = state.val + state.alter; return state; });" + } + ] + } +} \ No newline at end of file diff --git a/integration-tests/execute/CHANGELOG.md b/integration-tests/execute/CHANGELOG.md index d81cb4178..ff74f9317 100644 --- a/integration-tests/execute/CHANGELOG.md +++ b/integration-tests/execute/CHANGELOG.md @@ -1,5 +1,13 @@ # @openfn/integration-tests-execute +## 1.0.21 + +### Patch Changes + +- Updated dependencies [9d4ece3] + - @openfn/compiler@1.1.0 + - @openfn/runtime@1.7.0 + ## 1.0.20 ### Patch Changes diff --git a/integration-tests/execute/package.json b/integration-tests/execute/package.json index 3da343fcf..0af8c8e80 100644 --- a/integration-tests/execute/package.json +++ b/integration-tests/execute/package.json @@ -1,7 +1,7 @@ { "name": "@openfn/integration-tests-execute", "private": true, - "version": "1.0.20", + "version": "1.0.21", "description": "Job execution tests", "author": "Open Function Group ", "license": "ISC", diff --git a/integration-tests/worker/CHANGELOG.md b/integration-tests/worker/CHANGELOG.md index 9874b9e22..535019ab1 100644 --- a/integration-tests/worker/CHANGELOG.md +++ b/integration-tests/worker/CHANGELOG.md @@ -1,5 +1,14 @@ # @openfn/integration-tests-worker +## 1.0.89 + +### Patch Changes + +- Updated dependencies [9d4ece3] + - @openfn/ws-worker@1.14.0 + - @openfn/engine-multi@1.6.7 + - @openfn/lightning-mock@2.2.1 + ## 1.0.88 ### Patch Changes diff --git a/integration-tests/worker/package.json b/integration-tests/worker/package.json index 1c5218f70..8320be161 100644 --- a/integration-tests/worker/package.json +++ b/integration-tests/worker/package.json @@ -1,7 +1,7 @@ { "name": "@openfn/integration-tests-worker", "private": true, - "version": "1.0.88", + "version": "1.0.89", "description": "Lightning WOrker integration tests", "author": "Open Function Group ", "license": "ISC", diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index f83f8526b..99cfc32fb 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,17 @@ # @openfn/cli +## 1.13.0 + +### Minor Changes + +- 9d4ece3: Add support for global functions in execution plan + +### Patch Changes + +- Updated dependencies [9d4ece3] + - @openfn/compiler@1.1.0 + - @openfn/runtime@1.7.0 + ## 1.12.1 ### Patch Changes diff --git a/packages/cli/README.md b/packages/cli/README.md index 225647154..3fda1316c 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -317,6 +317,7 @@ A workflow has a structure like this: { "workflow": { "name": "my-workflow", // human readable name used in logging + "globals": "./common-funcs.js", // code or path to functions that can be accessed in any step. globally scoped "steps": [ { "name": "a", // human readable name used in logging diff --git a/packages/cli/package.json b/packages/cli/package.json index b63baa81b..f401a62b5 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@openfn/cli", - "version": "1.12.1", + "version": "1.13.0", "description": "CLI devtools for the openfn toolchain.", "engines": { "node": ">=18", diff --git a/packages/cli/src/compile/command.ts b/packages/cli/src/compile/command.ts index 73e7acd13..3ba01fb9f 100644 --- a/packages/cli/src/compile/command.ts +++ b/packages/cli/src/compile/command.ts @@ -17,6 +17,7 @@ export type CompileOptions = Pick< | 'repoDir' | 'path' | 'useAdaptorsMonorepo' + | 'globals' > & { workflow?: Opts['workflow']; repoDir?: string; diff --git a/packages/cli/src/compile/compile.ts b/packages/cli/src/compile/compile.ts index 9a7818047..632416ef5 100644 --- a/packages/cli/src/compile/compile.ts +++ b/packages/cli/src/compile/compile.ts @@ -1,4 +1,8 @@ -import compile, { preloadAdaptorExports, Options } from '@openfn/compiler'; +import compile, { + preloadAdaptorExports, + Options, + getExports, +} from '@openfn/compiler'; import { getModulePath } from '@openfn/runtime'; import type { ExecutionPlan, @@ -72,11 +76,14 @@ const compileWorkflow = async ( opts: CompileOptions, log: Logger ) => { + let globalsIgnoreList: string[] = getExports(plan.workflow.globals); + for (const step of plan.workflow.steps) { const job = step as Job; const jobOpts = { ...opts, adaptors: job.adaptors ?? opts.adaptors, + ignoreImports: globalsIgnoreList, }; if (job.expression) { const { code, map } = await compileJob( diff --git a/packages/cli/src/execute/command.ts b/packages/cli/src/execute/command.ts index 13819ac20..c8f88e605 100644 --- a/packages/cli/src/execute/command.ts +++ b/packages/cli/src/execute/command.ts @@ -33,6 +33,7 @@ export type ExecuteOptions = Required< | 'timeout' | 'useAdaptorsMonorepo' | 'workflowPath' + | 'globals' > > & Pick; diff --git a/packages/cli/src/options.ts b/packages/cli/src/options.ts index 279a7220c..574dba3ef 100644 --- a/packages/cli/src/options.ts +++ b/packages/cli/src/options.ts @@ -18,6 +18,7 @@ export type Opts = { command?: CommandList; baseDir?: string; + globals?: string; adaptor?: boolean | string; adaptors?: string[]; apolloUrl?: string; diff --git a/packages/cli/src/types.ts b/packages/cli/src/types.ts index ba0b3b8cf..2f5cb0427 100644 --- a/packages/cli/src/types.ts +++ b/packages/cli/src/types.ts @@ -18,6 +18,7 @@ export type CLIExecutionPlan = { id?: UUID; name?: string; steps: Array; + globals?: string; }; }; diff --git a/packages/cli/src/util/load-plan.ts b/packages/cli/src/util/load-plan.ts index 6511f8038..fa7fe7811 100644 --- a/packages/cli/src/util/load-plan.ts +++ b/packages/cli/src/util/load-plan.ts @@ -21,6 +21,7 @@ const loadPlan = async ( | 'baseDir' | 'expandAdaptors' | 'path' + | 'globals' > & { workflow?: Opts['workflow']; }, @@ -141,7 +142,10 @@ const maybeAssign = (a: any, b: any, keys: Array) => { }; const loadExpression = async ( - options: Pick, + options: Pick< + Opts, + 'expressionPath' | 'adaptors' | 'monorepoPath' | 'globals' + >, logger: Logger ): Promise => { const expressionPath = options.expressionPath!; @@ -165,6 +169,7 @@ const loadExpression = async ( workflow: { name, steps: [step], + globals: options.globals, }, options: wfOptions, }; @@ -212,11 +217,14 @@ const loadOldWorkflow = async ( }; const fetchFile = async ( - jobId: string, - rootDir: string = '', - filePath: string, + fileInfo: { + name: string; + rootDir?: string; + filePath: string; + }, log: Logger ) => { + const { rootDir = '', filePath, name } = fileInfo; try { // Special handling for ~ feels like a necessary evil const fullPath = filePath.startsWith('~') @@ -228,7 +236,7 @@ const fetchFile = async ( } catch (e) { abort( log, - `File not found for job ${jobId}: ${filePath}`, + `File not found for ${name}: ${filePath}`, undefined, `This workflow references a file which cannot be found at ${filePath}\n\nPaths inside the workflow are relative to the workflow.json` ); @@ -238,6 +246,24 @@ const fetchFile = async ( } }; +const importGlobals = async ( + plan: CLIExecutionPlan, + rootDir: string, + log: Logger +) => { + const fnStr = plan.workflow?.globals; + if (fnStr) { + if (isPath(fnStr)) { + plan.workflow.globals = await fetchFile( + { name: 'globals', rootDir, filePath: fnStr }, + log + ); + } else { + plan.workflow.globals = fnStr; + } + } +}; + // TODO this is currently untested in load-plan // (but covered a bit in execute tests) const importExpressions = async ( @@ -260,26 +286,32 @@ const importExpressions = async ( if (expressionStr && isPath(expressionStr)) { job.expression = await fetchFile( - job.id || `${idx}`, - rootDir, - expressionStr, + { + name: `job ${job.id || idx}`, + rootDir, + filePath: expressionStr, + }, log ); } if (configurationStr && isPath(configurationStr)) { const configString = await fetchFile( - job.id || `${idx}`, - rootDir, - configurationStr, + { + name: `job configuration ${job.id || idx}`, + rootDir, + filePath: configurationStr, + }, log ); job.configuration = JSON.parse(configString!); } if (stateStr && isPath(stateStr)) { const stateString = await fetchFile( - job.id || `${idx}`, - rootDir, - stateStr, + { + name: `job state ${job.id || idx}`, + rootDir, + filePath: stateStr, + }, log ); job.state = JSON.parse(stateString!); @@ -303,7 +335,10 @@ const ensureAdaptors = (plan: CLIExecutionPlan) => { const loadXPlan = async ( plan: CLIExecutionPlan, - options: Pick, + options: Pick< + Opts, + 'monorepoPath' | 'baseDir' | 'expandAdaptors' | 'globals' + >, logger: Logger, defaultName: string = '' ) => { @@ -316,6 +351,11 @@ const loadXPlan = async ( } ensureAdaptors(plan); + // import global functions + // if globals is provided via cli argument. it takes precedence + if (options.globals) plan.workflow.globals = options.globals; + await importGlobals(plan, options.baseDir!, logger); + // Note that baseDir should be set up in the default function await importExpressions(plan, options.baseDir!, logger); // expand shorthand adaptors in the workflow jobs diff --git a/packages/cli/test/execute/execute.test.ts b/packages/cli/test/execute/execute.test.ts index 5cbb22f69..66acbe26b 100644 --- a/packages/cli/test/execute/execute.test.ts +++ b/packages/cli/test/execute/execute.test.ts @@ -450,3 +450,56 @@ test.serial('run a job which does not return state', async (t) => { // Check that no error messages have been logged t.is(logger._history.length, 0); }); + +test.serial('globals: use a global function in an operation', async (t) => { + const workflow = { + workflow: { + globals: "export const prefixer = (w) => 'welcome '+w", + steps: [ + { + id: 'a', + state: { data: { name: 'John' } }, + expression: `${fn}fn(state=> { state.data.new = prefixer(state.data.name); return state; })`, + }, + ], + }, + }; + + mockFs({ + '/workflow.json': JSON.stringify(workflow), + }); + + const options = { + ...defaultOptions, + workflowPath: '/workflow.json', + }; + const result = await handler(options, logger); + t.deepEqual(result.data, { name: 'John', new: 'welcome John' }); +}); + +test.serial('globals: get global functions from a filePath', async (t) => { + const workflow = { + workflow: { + globals: '/my-globals.js', + steps: [ + { + id: 'a', + state: { data: { name: 'John' } }, + expression: `${fn}fn(state=> { state.data.new = suffixer(state.data.name); return state; })`, + }, + ], + }, + }; + + mockFs({ + '/workflow.json': JSON.stringify(workflow), + '/my-globals.js': `export const suffixer = (w) => w + " goodbye!"`, + }); + + const options = { + ...defaultOptions, + workflowPath: '/workflow.json', + }; + const result = await handler(options, logger); + t.deepEqual(result.data, { name: 'John', new: 'John goodbye!' }); +}); diff --git a/packages/compiler/CHANGELOG.md b/packages/compiler/CHANGELOG.md index 5f095fd72..fef06d4cd 100644 --- a/packages/compiler/CHANGELOG.md +++ b/packages/compiler/CHANGELOG.md @@ -1,5 +1,11 @@ # @openfn/compiler +## 1.1.0 + +### Minor Changes + +- 9d4ece3: Add support for global functions in execution plan + ## 1.0.4 ### Patch Changes diff --git a/packages/compiler/package.json b/packages/compiler/package.json index 2d7e3aed0..51a4190d5 100644 --- a/packages/compiler/package.json +++ b/packages/compiler/package.json @@ -1,6 +1,6 @@ { "name": "@openfn/compiler", - "version": "1.0.4", + "version": "1.1.0", "description": "Compiler and language tooling for openfn jobs.", "author": "Open Function Group ", "license": "ISC", diff --git a/packages/compiler/src/getExports.ts b/packages/compiler/src/getExports.ts new file mode 100644 index 000000000..8084fab1e --- /dev/null +++ b/packages/compiler/src/getExports.ts @@ -0,0 +1,51 @@ +import recast from 'recast'; +import * as acorn from 'acorn'; +import { namedTypes as n } from 'ast-types'; + +function getExports(content?: string) { + if (!content) { + return []; + } + + const ast = recast.parse(content, { + parser: { + parse: (source: string) => + acorn.parse(source, { + sourceType: 'module', + ecmaVersion: 'latest', + allowHashBang: true, + locations: true, + }), + }, + }); + const exportIdentifiers: string[] = []; + + recast.types.visit(ast, { + visitExportNamedDeclaration(path) { + const node = path.node; + if (node.declaration) { + if (n.VariableDeclaration.check(node.declaration)) { + node.declaration.declarations.forEach((decl) => { + // @ts-ignore + const id = decl.id; + if (id && n.Identifier.check(id)) exportIdentifiers.push(id.name); + }); + } else if ( + n.FunctionDeclaration.check(node.declaration) || + n.ClassDeclaration.check(node.declaration) + ) { + const id = node.declaration.id; + if (id && n.Identifier.check(id)) exportIdentifiers.push(id.name); + } + } else if (node.specifiers) { + node.specifiers.forEach((spec) => { + exportIdentifiers.push(spec.exported.name); + }); + } + this.traverse(path); + }, + }); + return exportIdentifiers; +} + +export default getExports; diff --git a/packages/compiler/src/index.ts b/packages/compiler/src/index.ts index f9a2b58a4..b21ab7949 100644 --- a/packages/compiler/src/index.ts +++ b/packages/compiler/src/index.ts @@ -1,4 +1,5 @@ import compile from './compile'; +export { default as getExports } from './getExports'; export * from './util'; export type { TransformOptions } from './transform'; diff --git a/packages/compiler/test/compile.test.ts b/packages/compiler/test/compile.test.ts index 0372151a7..fab00696b 100644 --- a/packages/compiler/test/compile.test.ts +++ b/packages/compiler/test/compile.test.ts @@ -244,3 +244,37 @@ export default [each( const { code: result } = compile(source); t.is(result, expected); }); + +// TODO: this is error prone and shouldn't be desired +test('try picking dangling identifiers from adaptor', (t) => { + const options = { + 'add-imports': { + adaptors: [ + { + name: '@openfn/language-common', + }, + ], + }, + }; + const source = 'fn(state=> doSomething(state))'; + const expected = `import { fn, doSomething } from "@openfn/language-common";\nexport default [fn(state=> doSomething(state))];`; + const { code: result } = compile(source, options); + t.is(result, expected); +}); + +test('respect ignore list when exports not provided', (t) => { + const options = { + 'add-imports': { + ignore: ['doSomething'], + adaptors: [ + { + name: '@openfn/language-common', + }, + ], + }, + }; + const source = 'fn(state=> doSomething(state))'; + const expected = `import { fn } from "@openfn/language-common";\nexport default [fn(state=> doSomething(state))];`; + const { code: result } = compile(source, options); + t.is(result, expected); +}); diff --git a/packages/compiler/test/getExports.test.ts b/packages/compiler/test/getExports.test.ts new file mode 100644 index 000000000..1bdb35c75 --- /dev/null +++ b/packages/compiler/test/getExports.test.ts @@ -0,0 +1,71 @@ +import test from 'ava'; +import getExports from '../src/getExports'; + +test('exported variable', (t) => { + const source = 'export const BigVal = 56;'; + const res = getExports(source); + t.deepEqual(res, ['BigVal']); +}); + +test('exported function declaration', (t) => { + const source = 'export function doSomething() {}'; + const res = getExports(source); + t.deepEqual(res, ['doSomething']); +}); + +test('exported class', (t) => { + const source = 'export class SomeClass {}'; + const res = getExports(source); + t.deepEqual(res, ['SomeClass']); +}); + +test('exported multiple variables', (t) => { + const source = 'export const a = 1, b = 2;'; + const res = getExports(source); + t.deepEqual(res, ['a', 'b']); +}); + +test('named exports after declaration', (t) => { + const source = ` + const a = 1, b = 2; + export { a, b }; + `; + const res = getExports(source); + t.deepEqual(res, ['a', 'b']); +}); + +test('renamed exports', (t) => { + const source = ` + const a = 1; + export { a as alpha }; + `; + const res = getExports(source); + t.deepEqual(res, ['alpha']); +}); + +test('mixed export declarations', (t) => { + const source = ` + export const x = 1; + function y() {} + export { y }; + const z = 3; + export { z as zomething }; + `; + const res = getExports(source); + t.deepEqual(res, ['x', 'y', 'zomething']); +}); + +test('re-export from module', (t) => { + const source = `export { lightning, runtime as compiler } from './openfn';`; + const res = getExports(source); + t.deepEqual(res, ['lightning', 'compiler']); +}); + +test('export default should be ignored', (t) => { + const source = ` + export default function main() {} + export const util = true; + `; + const res = getExports(source); + t.deepEqual(res, ['util']); +}); diff --git a/packages/engine-multi/CHANGELOG.md b/packages/engine-multi/CHANGELOG.md index 170dd8027..a2b7c5db0 100644 --- a/packages/engine-multi/CHANGELOG.md +++ b/packages/engine-multi/CHANGELOG.md @@ -1,5 +1,13 @@ # engine-multi +## 1.6.7 + +### Patch Changes + +- Updated dependencies [9d4ece3] + - @openfn/compiler@1.1.0 + - @openfn/runtime@1.7.0 + ## 1.6.6 ### Patch Changes diff --git a/packages/engine-multi/package.json b/packages/engine-multi/package.json index 4eeb77fd6..f36726c56 100644 --- a/packages/engine-multi/package.json +++ b/packages/engine-multi/package.json @@ -1,6 +1,6 @@ { "name": "@openfn/engine-multi", - "version": "1.6.6", + "version": "1.6.7", "description": "Multi-process runtime engine", "main": "dist/index.js", "type": "module", diff --git a/packages/lexicon/core.d.ts b/packages/lexicon/core.d.ts index 37cccdd64..b2aa1d3c1 100644 --- a/packages/lexicon/core.d.ts +++ b/packages/lexicon/core.d.ts @@ -62,6 +62,8 @@ export type Workflow = { // (gets applied to every configuration object) credentials?: Record; + // a path to a file where functions are defined + globals?: string; // properties which map to an openfn project // only used by projects openfn?: { diff --git a/packages/lexicon/lightning.d.ts b/packages/lexicon/lightning.d.ts index ca62e2d7c..86a438207 100644 --- a/packages/lexicon/lightning.d.ts +++ b/packages/lexicon/lightning.d.ts @@ -34,6 +34,8 @@ export type LightningPlan = { edges: LightningEdge[]; options?: LightningPlanOptions; + + globals?: string; }; /** diff --git a/packages/lightning-mock/CHANGELOG.md b/packages/lightning-mock/CHANGELOG.md index 2500f17d6..8962547f3 100644 --- a/packages/lightning-mock/CHANGELOG.md +++ b/packages/lightning-mock/CHANGELOG.md @@ -1,5 +1,13 @@ # @openfn/lightning-mock +## 2.2.1 + +### Patch Changes + +- Updated dependencies [9d4ece3] + - @openfn/runtime@1.7.0 + - @openfn/engine-multi@1.6.7 + ## 2.2.0 ### Minor Changes diff --git a/packages/lightning-mock/package.json b/packages/lightning-mock/package.json index 42b4d6b42..8e850900e 100644 --- a/packages/lightning-mock/package.json +++ b/packages/lightning-mock/package.json @@ -1,6 +1,6 @@ { "name": "@openfn/lightning-mock", - "version": "2.2.0", + "version": "2.2.1", "private": true, "description": "A mock Lightning server", "main": "dist/index.js", diff --git a/packages/runtime/CHANGELOG.md b/packages/runtime/CHANGELOG.md index 6893f55d9..e2791f4d6 100644 --- a/packages/runtime/CHANGELOG.md +++ b/packages/runtime/CHANGELOG.md @@ -1,5 +1,11 @@ # @openfn/runtime +## 1.7.0 + +### Minor Changes + +- 9d4ece3: Add support for global functions in execution plan + ## 1.6.4 ### Patch Changes diff --git a/packages/runtime/package.json b/packages/runtime/package.json index 43a3ea41a..78cddff0e 100644 --- a/packages/runtime/package.json +++ b/packages/runtime/package.json @@ -1,6 +1,6 @@ { "name": "@openfn/runtime", - "version": "1.6.4", + "version": "1.7.0", "description": "Job processing runtime.", "type": "module", "exports": { diff --git a/packages/runtime/src/execute/compile-plan.ts b/packages/runtime/src/execute/compile-plan.ts index 01a18a255..c78cc34c3 100644 --- a/packages/runtime/src/execute/compile-plan.ts +++ b/packages/runtime/src/execute/compile-plan.ts @@ -106,6 +106,10 @@ export default (plan: ExecutionPlan) => { start: options.start ?? workflow.steps[0]?.id!, }, }; + + if (typeof workflow.globals === 'string') + newPlan.workflow.globals = workflow.globals; + if (workflow.credentials) { newPlan.workflow.credentials = workflow.credentials; } diff --git a/packages/runtime/src/execute/expression.ts b/packages/runtime/src/execute/expression.ts index a161fb696..268011882 100644 --- a/packages/runtime/src/execute/expression.ts +++ b/packages/runtime/src/execute/expression.ts @@ -21,7 +21,7 @@ import { assertSecurityKill, AdaptorError, } from '../errors'; -import type { JobModule, ExecutionContext } from '../types'; +import type { JobModule, ExecutionContext, GlobalsModule } from '../types'; import { ModuleInfoMap } from '../modules/linker'; import { clearNullState, @@ -50,8 +50,14 @@ export default ( try { const timeout = plan.options?.timeout ?? ctx.opts.defaultRunTimeoutMs; + // prepare global functions to be injected into execution context + const globals = { + ...opts.globals, + ...(await prepareGlobals(plan.workflow?.globals || '', opts)), + }; + // Setup an execution context - const context = buildContext(input, opts); + const context = buildContext(input, { ...opts, globals }); const { operations, execute } = await prepareJob( expression, @@ -59,6 +65,7 @@ export default ( opts, moduleOverrides ); + // Create the main reducer function const reducer = (execute || defaultExecute)( ...operations.map((op, idx) => @@ -238,3 +245,21 @@ const prepareJob = async ( return { operations: expression as Operation[] }; } }; + +const prepareGlobals = async ( + source: string, + opts: Options = {} +): Promise => { + if (typeof source === 'string' && !!source.trim()) { + const context = buildContext({}, opts); + return await loadModule(source || '', { + context, + }).catch((e) => { + // mostly syntax errors + // repackage errors and throw + e.message = `[globals] ${e.message}`; + throw e; + }); + } + return {}; +}; diff --git a/packages/runtime/src/types.ts b/packages/runtime/src/types.ts index f8f45c832..471e8b7b1 100644 --- a/packages/runtime/src/types.ts +++ b/packages/runtime/src/types.ts @@ -41,6 +41,7 @@ export type Lazy = string | T; export type CompiledExecutionPlan = { workflow: { + globals?: string; steps: Record; credentials?: Record; }; @@ -55,6 +56,8 @@ export type JobModule = { // TODO lifecycle hooks }; +export type GlobalsModule = Record; + type NotifyHandler = ( event: NotifyEvents, payload: NotifyEventsLookup[typeof event] diff --git a/packages/runtime/test/execute/compile-plan.test.ts b/packages/runtime/test/execute/compile-plan.test.ts index 922b593ea..091e99bb3 100644 --- a/packages/runtime/test/execute/compile-plan.test.ts +++ b/packages/runtime/test/execute/compile-plan.test.ts @@ -433,6 +433,43 @@ test('throw for a syntax error on a job edge', (t) => { } }); +test('globals should be undefined if no globals are passed', (t) => { + const plan: ExecutionPlan = { + workflow: { + steps: [{ expression: 'x' }, { expression: 'y' }], + }, + options: {}, + }; + const compiled = compilePlan(plan); + t.is(compiled.workflow.globals, undefined); +}); + +test('globals should be undefined if non-string is passed', (t) => { + const plan: ExecutionPlan = { + workflow: { + globals: { some: 'val' } as any, + steps: [{ expression: 'x' }, { expression: 'y' }], + }, + options: {}, + }; + const compiled = compilePlan(plan); + t.is(compiled.workflow.globals, undefined); +}); + +test('global functions should perform no transformation inline code', (t) => { + const GLOBAL_FUNCTIONS = + 'export const cleaner = (name) => name.replace("a", "b");'; + const plan: ExecutionPlan = { + workflow: { + globals: GLOBAL_FUNCTIONS, + steps: [{ expression: 'x' }, { expression: 'y' }], + }, + options: {}, + }; + const compiled = compilePlan(plan); + t.is(compiled.workflow.globals, GLOBAL_FUNCTIONS); +}); + test('throw for multiple errors', (t) => { const plan = { workflow: { diff --git a/packages/runtime/test/execute/expression.test.ts b/packages/runtime/test/execute/expression.test.ts index 85b2105b1..046ba87b8 100644 --- a/packages/runtime/test/execute/expression.test.ts +++ b/packages/runtime/test/execute/expression.test.ts @@ -51,6 +51,23 @@ test.serial('run a live no-op job with one operation', async (t) => { t.deepEqual(state, result); }); +test.serial('use global function defined in functions', async (t) => { + const job = `export default [state=> {state.data.pr = prefixer(state.data.name); return state;}]`; + + const state = createState(); + // @ts-ignore + state.data.name = 'john'; + const context = createContext(); + context.plan.workflow = { + steps: [] as any, + globals: "export const prefixer = (w) => 'welcome '+ w", + }; + + const result = await execute(context, job, state); + // @ts-ignore + t.is(result.data.pr, 'welcome ' + state.data.name); +}); + test.serial('run a stringified no-op job with one operation', async (t) => { const job = 'export default [(s) => s]'; const state = createState(); diff --git a/packages/runtime/test/execute/plan.test.ts b/packages/runtime/test/execute/plan.test.ts index d8be4a1d3..7f658562d 100644 --- a/packages/runtime/test/execute/plan.test.ts +++ b/packages/runtime/test/execute/plan.test.ts @@ -10,9 +10,11 @@ let mockLogger = createMockLogger(undefined, { level: 'debug' }); const createPlan = ( steps: Job[], - options: Partial = {} + options: Partial = {}, + globals?: string ): ExecutionPlan => ({ workflow: { + globals, steps, }, options, @@ -1204,3 +1206,64 @@ test('Plans log step names for each job start and end', async (t) => { const end = logger._find('success', /do-the-thing completed in/i); t.regex(end!.message as string, /do-the-thing completed in \d+ms/); }); + +test.serial( + 'global functions should be scoped per step or job code', + async (t) => { + const globals = ` + let x = 10; + export const setX = (value) => { x = value } + export const getX = () => x + `; + const plan = createPlan( + [ + { + id: 'a', + name: 'do-a', + expression: + 'export default [s => {setX(20); return {data: {x1: getX()}}}]', + next: { + b: true, + }, + }, + { + id: 'b', + name: 'do-b', + expression: + 'export default [s => {return {data: {...s.data, x2: getX()}}}]', + }, + ], + {}, + globals + ); + + const result: any = await executePlan(plan, {}, {}, mockLogger); + t.deepEqual(result.data, { x1: 20, x2: 10 }); + } +); + +test.serial( + 'global function scope should be shared between operations', + async (t) => { + const globals = ` + let x = 10; + export const setX = (value) => { x = value } + export const getX = () => x + `; + const plan = createPlan( + [ + { + id: 'a', + name: 'do-a', + expression: + 'export default [s => {setX(20); return {data: {x1: getX()}}}, s=> ({data: {...s.data, x2: getX()}})]', + }, + ], + {}, + globals + ); + + const result: any = await executePlan(plan, {}, {}, mockLogger); + t.deepEqual(result.data, { x1: 20, x2: 20 }); + } +); diff --git a/packages/ws-worker/CHANGELOG.md b/packages/ws-worker/CHANGELOG.md index df923fb30..5adda4947 100644 --- a/packages/ws-worker/CHANGELOG.md +++ b/packages/ws-worker/CHANGELOG.md @@ -1,5 +1,17 @@ # ws-worker +## 1.14.0 + +### Minor Changes + +- 9d4ece3: Add support for global functions in execution plan + +### Patch Changes + +- Updated dependencies [9d4ece3] + - @openfn/runtime@1.7.0 + - @openfn/engine-multi@1.6.7 + ## 1.13.6 ### Patch Changes diff --git a/packages/ws-worker/package.json b/packages/ws-worker/package.json index 4f8e6b450..4043ad0f5 100644 --- a/packages/ws-worker/package.json +++ b/packages/ws-worker/package.json @@ -1,6 +1,6 @@ { "name": "@openfn/ws-worker", - "version": "1.13.6", + "version": "1.14.0", "description": "A Websocket Worker to connect Lightning to a Runtime Engine", "main": "dist/index.js", "type": "module", diff --git a/packages/ws-worker/src/util/convert-lightning-plan.ts b/packages/ws-worker/src/util/convert-lightning-plan.ts index 8c0d61a58..7c93aeb6c 100644 --- a/packages/ws-worker/src/util/convert-lightning-plan.ts +++ b/packages/ws-worker/src/util/convert-lightning-plan.ts @@ -225,6 +225,10 @@ export default ( steps: Object.values(nodes), }; + // only pass globals if it's a string + if (run.globals && typeof run.globals === 'string') + plan.workflow.globals = run.globals; + if (run.name) { plan.workflow.name = run.name; } diff --git a/packages/ws-worker/test/api/execute.test.ts b/packages/ws-worker/test/api/execute.test.ts index a95a74126..4def76f78 100644 --- a/packages/ws-worker/test/api/execute.test.ts +++ b/packages/ws-worker/test/api/execute.test.ts @@ -525,3 +525,39 @@ test('execute should call all events on the socket', async (t) => { }); }); }); + +test('execute should acknowledge globals', async (t) => { + const logger = createMockLogger(); + + const channel = mockChannel({ + ...mockEventHandlers, + [GET_DATACLIP]: (id) => { + t.truthy(id); + return toArrayBuffer({}); + }, + }); + const engine = await createMockRTE(); + + const plan = { + id: 'a', + workflow: { + globals: "export const DEFAULT_NAME = 'han solo'", + steps: [ + { + expression: 'fn(() => ({ name: DEFAULT_NAME }))', + }, + ], + }, + options: {}, + } as ExecutionPlan; + + const options = {}; + const input = 'abc'; + + return new Promise((done) => { + execute(channel, engine, logger, plan, input, options, (results) => { + t.is(results.state.name, 'han solo'); + done(); + }); + }); +}); diff --git a/packages/ws-worker/test/util/convert-lightning-plan.test.ts b/packages/ws-worker/test/util/convert-lightning-plan.test.ts index e193a7273..3118bd327 100644 --- a/packages/ws-worker/test/util/convert-lightning-plan.test.ts +++ b/packages/ws-worker/test/util/convert-lightning-plan.test.ts @@ -726,3 +726,27 @@ test('Use local paths', (t) => { }, }); }); + +test('pass globals from lightning run to plan', (t) => { + const GLOBALS_CONTENT = "export const prefixer = (v) => 'prefix-' + v"; + const run: Partial = { + id: 'w', + jobs: [createNode({ id: 'a' })], + globals: GLOBALS_CONTENT, + }; + + const { plan } = convertPlan(run as LightningPlan); + t.deepEqual(plan.workflow.globals, GLOBALS_CONTENT); +}); + +test("ignore globals when it isn't a string", (t) => { + const GLOBALS_CONTENT = { some: 'value' }; // not a string + const run: Partial = { + id: 'w', + jobs: [createNode({ id: 'a' })], + globals: GLOBALS_CONTENT as any, + }; + + const { plan } = convertPlan(run as LightningPlan); + t.deepEqual(plan.workflow.globals, undefined); +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3840c6330..987926644 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -464,6 +464,18 @@ importers: specifier: ^5.1.6 version: 5.7.2 + packages/engine-multi/tmp/a/b/c: + dependencies: + ava: + specifier: '6' + version: 6.4.0 + + packages/engine-multi/tmp/repo: + dependencies: + ava: + specifier: '6' + version: 6.4.0 + packages/lexicon: dependencies: source-map: @@ -2088,6 +2100,13 @@ packages: wrap-ansi: 8.1.0 wrap-ansi-cjs: /wrap-ansi@7.0.0 + /@isaacs/fs-minipass@4.0.1: + resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} + engines: {node: '>=18.0.0'} + dependencies: + minipass: 7.1.2 + dev: false + /@jridgewell/gen-mapping@0.3.8: resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} engines: {node: '>=6.0.0'} @@ -2176,6 +2195,23 @@ packages: read-yaml-file: 1.1.0 dev: true + /@mapbox/node-pre-gyp@2.0.0: + resolution: {integrity: sha512-llMXd39jtP0HpQLVI37Bf1m2ADlEb35GYSh1SDSLsBhR+5iCxiNGlT31yqbNtVHygHAtMy6dWFERpU2JgufhPg==} + engines: {node: '>=18'} + hasBin: true + dependencies: + consola: 3.4.2 + detect-libc: 2.0.4 + https-proxy-agent: 7.0.6 + node-fetch: 2.7.0 + nopt: 8.1.0 + semver: 7.6.3 + tar: 7.4.3 + transitivePeerDependencies: + - encoding + - supports-color + dev: false + /@napi-rs/wasm-runtime@0.2.9: resolution: {integrity: sha512-OKRBiajrrxB9ATokgEQoG87Z25c67pCpYcCwmXYX8PBftC9pBfN18gnm/fh1wurSLEKIAt+QRFLFCQISrb66Jg==} requiresBuild: true @@ -2779,7 +2815,6 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} requiresBuild: true - dev: true optional: true /@prisma/instrumentation@6.4.1(@opentelemetry/api@1.9.0): @@ -2793,6 +2828,20 @@ packages: - supports-color dev: false + /@rollup/pluginutils@5.1.4: + resolution: {integrity: sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + dependencies: + '@types/estree': 1.0.6 + estree-walker: 2.0.2 + picomatch: 4.0.2 + dev: false + /@rollup/rollup-android-arm-eabi@4.28.1: resolution: {integrity: sha512-2aZp8AES04KI2dy3Ss6/MDjXbwBzj+i0GqKtWXgw2/Ma6E4jJvujryO6gJAghIRVz7Vwr9Gtl/8na3nDUKpraQ==} cpu: [arm] @@ -3012,6 +3061,11 @@ packages: '@sentry/core': 9.5.0 dev: false + /@sindresorhus/merge-streams@2.3.0: + resolution: {integrity: sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==} + engines: {node: '>=18'} + dev: false + /@slack/logger@3.0.0: resolution: {integrity: sha512-DTuBFbqu4gGfajREEMrkq5jBhcnskinhr4+AnfJEk48zhVeEv3XnUKGIX98B74kxhYsIMfApGGySTn7V3b5yBA==} engines: {node: '>= 12.13.0', npm: '>= 6.12.0'} @@ -3268,7 +3322,6 @@ packages: /@types/estree@1.0.6: resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} - dev: true /@types/express-serve-static-core@5.0.2: resolution: {integrity: sha512-vluaspfvWEtE4vcSDlKRNer52DvOGrB2xv6diXy6UKyKW0lqZiWHGNApSyxOv+8DE5Z27IzVvE7hNkxg7EXIcg==} @@ -3548,6 +3601,34 @@ packages: - supports-color dev: false + /@vercel/nft@0.29.4: + resolution: {integrity: sha512-6lLqMNX3TuycBPABycx7A9F1bHQR7kiQln6abjFbPrf5C/05qHM9M5E4PeTE59c7z8g6vHnx1Ioihb2AQl7BTA==} + engines: {node: '>=18'} + hasBin: true + dependencies: + '@mapbox/node-pre-gyp': 2.0.0 + '@rollup/pluginutils': 5.1.4 + acorn: 8.15.0 + acorn-import-attributes: 1.9.5(acorn@8.15.0) + async-sema: 3.1.1 + bindings: 1.5.0 + estree-walker: 2.0.2 + glob: 10.4.5 + graceful-fs: 4.2.11 + node-gyp-build: 4.8.4 + picomatch: 4.0.2 + resolve-from: 5.0.0 + transitivePeerDependencies: + - encoding + - rollup + - supports-color + dev: false + + /abbrev@3.0.1: + resolution: {integrity: sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==} + engines: {node: ^18.17.0 || >=20.5.0} + dev: false + /accepts@1.3.8: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} engines: {node: '>= 0.6'} @@ -3563,6 +3644,14 @@ packages: acorn: 8.14.0 dev: false + /acorn-import-attributes@1.9.5(acorn@8.15.0): + resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} + peerDependencies: + acorn: ^8 + dependencies: + acorn: 8.15.0 + dev: false + /acorn-walk@8.3.4: resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==} engines: {node: '>=0.4.0'} @@ -3574,10 +3663,15 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + /acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: false + /agent-base@7.1.3: resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==} engines: {node: '>= 14'} - dev: true /aggregate-error@3.1.0: resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} @@ -3700,6 +3794,10 @@ packages: dependencies: tslib: 2.8.1 + /async-sema@3.1.1: + resolution: {integrity: sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg==} + dev: false + /asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} dev: true @@ -3819,6 +3917,62 @@ packages: transitivePeerDependencies: - supports-color + /ava@6.4.0: + resolution: {integrity: sha512-aeFapuBZtaGwVMlFFf074SZJ0bPcdmAdJdsvhHMp+XaOnC2DgeMzopb7yyYAhulNGRJQfUK/SIBYo2PoX7+gtw==} + engines: {node: ^18.18 || ^20.8 || ^22 || ^23 || >=24} + hasBin: true + peerDependencies: + '@ava/typescript': '*' + peerDependenciesMeta: + '@ava/typescript': + optional: true + dependencies: + '@vercel/nft': 0.29.4 + acorn: 8.15.0 + acorn-walk: 8.3.4 + ansi-styles: 6.2.1 + arrgv: 1.0.2 + arrify: 3.0.0 + callsites: 4.2.0 + cbor: 10.0.3 + chalk: 5.4.1 + chunkd: 2.0.1 + ci-info: 4.2.0 + ci-parallel-vars: 1.0.1 + cli-truncate: 4.0.0 + code-excerpt: 4.0.0 + common-path-prefix: 3.0.0 + concordance: 5.0.4 + currently-unhandled: 0.4.1 + debug: 4.4.1 + emittery: 1.1.0 + figures: 6.1.0 + globby: 14.1.0 + ignore-by-default: 2.1.0 + indent-string: 5.0.0 + is-plain-object: 5.0.0 + is-promise: 4.0.0 + matcher: 5.0.0 + memoize: 10.1.0 + ms: 2.1.3 + p-map: 7.0.3 + package-config: 5.0.0 + picomatch: 4.0.2 + plur: 5.1.0 + pretty-ms: 9.2.0 + resolve-cwd: 3.0.0 + stack-utils: 2.0.6 + strip-ansi: 7.1.0 + supertap: 3.0.1 + temp-dir: 3.0.0 + write-file-atomic: 6.0.0 + yargs: 17.7.2 + transitivePeerDependencies: + - encoding + - rollup + - supports-color + dev: false + /awilix@10.0.2: resolution: {integrity: sha512-hFatb7eZFdtiWjjmGRSm/K/uxZpmcBlM+YoeMB3VpOPXk3xa6+7zctg3LRbUzoimom5bwGrePF0jXReO6b4zNQ==} engines: {node: '>=14.0.0'} @@ -3865,6 +4019,12 @@ packages: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} + /bindings@1.5.0: + resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} + dependencies: + file-uri-to-path: 1.0.0 + dev: false + /bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} dependencies: @@ -4027,6 +4187,13 @@ packages: engines: {node: '>= 6'} dev: true + /cbor@10.0.3: + resolution: {integrity: sha512-72Jnj81xMsqepqdcSdf2+fflz/UDsThOHy5hj2MW5F5xzHL8Oa0KQ6I6V9CwVUPxg5pf+W9xp6W2KilaRXWWtw==} + engines: {node: '>=18'} + dependencies: + nofilter: 3.1.0 + dev: false + /cbor@8.1.0: resolution: {integrity: sha512-DwGjNW9omn6EwP70aXsn7FQJx5kO12tX0bZkaTjzdVFM6/7nhA4t0EENocKGx6D2Bch9PE2KzCUf5SceBdeijg==} engines: {node: '>=12.19'} @@ -4053,6 +4220,11 @@ packages: resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + /chalk@5.4.1: + resolution: {integrity: sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + dev: false + /chardet@0.7.0: resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} @@ -4103,6 +4275,11 @@ packages: engines: {node: '>=10'} dev: true + /chownr@3.0.0: + resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} + engines: {node: '>=18'} + dev: false + /chunkd@2.0.1: resolution: {integrity: sha512-7d58XsFmOq0j6el67Ug9mHf9ELUXsQXYJBkyxhH/k+6Ke0qXRnv0kbemx+Twc6fRJ07C49lcbdgm9FL1Ei/6SQ==} @@ -4110,6 +4287,11 @@ packages: resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} engines: {node: '>=8'} + /ci-info@4.2.0: + resolution: {integrity: sha512-cYY9mypksY8NRqgDB1XD1RiJL338v/551niynFTGkZOO2LHuB2OmOYxDIe/ttN9AHwrqdum1360G3ald0W9kCg==} + engines: {node: '>=8'} + dev: false + /ci-parallel-vars@1.0.1: resolution: {integrity: sha512-uvzpYrpmidaoxvIQHM+rKSrigjOe9feHYbw4uOI2gdfe1C3xIlxO+kVXq83WQWNniTf8bAxVpy+cQeFQsMERKg==} @@ -4150,6 +4332,14 @@ packages: slice-ansi: 5.0.0 string-width: 5.1.2 + /cli-truncate@4.0.0: + resolution: {integrity: sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==} + engines: {node: '>=18'} + dependencies: + slice-ansi: 5.0.0 + string-width: 7.2.0 + dev: false + /cli-width@4.1.0: resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} engines: {node: '>= 12'} @@ -4248,6 +4438,11 @@ packages: semver: 7.6.3 well-known-symbols: 2.0.0 + /consola@3.4.2: + resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} + engines: {node: ^14.18.0 || >=16.10.0} + dev: false + /content-disposition@0.5.4: resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} engines: {node: '>= 0.6'} @@ -4412,6 +4607,18 @@ packages: dependencies: ms: 2.1.3 + /debug@4.4.1: + resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.3 + dev: false + /decode-uri-component@0.4.1: resolution: {integrity: sha512-+8VxcR21HhTy8nOt6jf20w0c9CADrw1O8d+VZ/YzzCt4bJ3uBjw+D1q2osAB8RnpwwaeYBxy0HyKQxD5JBMuuQ==} engines: {node: '>=14.16'} @@ -4472,6 +4679,11 @@ packages: engines: {node: '>=8'} dev: true + /detect-libc@2.0.4: + resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==} + engines: {node: '>=8'} + dev: false + /didyoumean@1.2.2: resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} dev: true @@ -4552,6 +4764,15 @@ packages: resolution: {integrity: sha512-tJdCJitoy2lrC2ldJcqN4vkqJ00lT+tOWNT1hBJjO/3FDMJa5TTIiYGCKGkn/WfCyOzUMObeohbVTj00fhiLiA==} engines: {node: '>=14.16'} + /emittery@1.1.0: + resolution: {integrity: sha512-rsX7ktqARv/6UQDgMaLfIqUWAEzzbCQiVh7V9rhDXp6c37yoJcks12NVD+XPkgl4AEavmNhVfrhGoqYwIsMYYA==} + engines: {node: '>=14.16'} + dev: false + + /emoji-regex@10.4.0: + resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==} + dev: false + /emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -5026,6 +5247,10 @@ packages: engines: {node: '>=4.0'} dev: true + /estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + dev: false + /esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} @@ -5129,6 +5354,17 @@ packages: merge2: 1.4.1 micromatch: 4.0.8 + /fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + dev: false + /fast-json-patch@3.1.1: resolution: {integrity: sha512-vf6IHUX2SBcA+5/+4883dsIjpBTqmfBjmYiWK1savxQmFk4JfBMLa7ynTYOs1Rolp/T1betJxHiGD3g1Mn8lUQ==} dev: false @@ -5163,6 +5399,17 @@ packages: escape-string-regexp: 5.0.0 is-unicode-supported: 1.3.0 + /figures@6.1.0: + resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} + engines: {node: '>=18'} + dependencies: + is-unicode-supported: 2.1.0 + dev: false + + /file-uri-to-path@1.0.0: + resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} + dev: false + /fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} @@ -5189,6 +5436,11 @@ packages: - supports-color dev: true + /find-up-simple@1.0.1: + resolution: {integrity: sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==} + engines: {node: '>=18'} + dev: false + /find-up@4.1.0: resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} engines: {node: '>=8'} @@ -5302,6 +5554,11 @@ packages: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} + /get-east-asian-width@1.3.0: + resolution: {integrity: sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==} + engines: {node: '>=18'} + dev: false + /get-intrinsic@1.2.6: resolution: {integrity: sha512-qxsEs+9A+u85HhllWJJFicJfPDhRmjzoYdl64aMWW9yRIJmSyxdn8IEkuIM530/7T+lv0TIHd8L6Q/ra0tEoeA==} engines: {node: '>= 0.4'} @@ -5351,7 +5608,6 @@ packages: minipass: 7.1.2 package-json-from-dist: 1.0.1 path-scurry: 1.11.1 - dev: true /glob@11.0.2: resolution: {integrity: sha512-YT7U7Vye+t5fZ/QMkBFrTJ7ZQxInIUjwyAjVj84CYXqgBdv30MFUPGnBR6sQaVq6Is15wYJUsnzTuWaGRBhBAQ==} @@ -5399,13 +5655,24 @@ packages: merge2: 1.4.1 slash: 4.0.0 + /globby@14.1.0: + resolution: {integrity: sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==} + engines: {node: '>=18'} + dependencies: + '@sindresorhus/merge-streams': 2.3.0 + fast-glob: 3.3.3 + ignore: 7.0.5 + path-type: 6.0.0 + slash: 5.1.0 + unicorn-magic: 0.3.0 + dev: false + /gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} /graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - dev: true /gunzip-maybe@1.4.2: resolution: {integrity: sha512-4haO1M4mLO91PW57BMsDFf75UmwoRX0GkdD+Faw+Lr+r/OZrOCS0pIBwOL1xCKQqnQzbNFGgK2V2CpBUPeFNTw==} @@ -5518,7 +5785,6 @@ packages: debug: 4.4.0 transitivePeerDependencies: - supports-color - dev: true /human-id@1.0.2: resolution: {integrity: sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw==} @@ -5569,6 +5835,11 @@ packages: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} + /ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + dev: false + /import-fresh@3.3.0: resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} engines: {node: '>=6'} @@ -5749,6 +6020,11 @@ packages: resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} engines: {node: '>=12'} + /is-unicode-supported@2.1.0: + resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} + engines: {node: '>=18'} + dev: false + /is-utf8@0.2.1: resolution: {integrity: sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==} @@ -5774,7 +6050,6 @@ packages: '@isaacs/cliui': 8.0.2 optionalDependencies: '@pkgjs/parseargs': 0.11.0 - dev: true /jackspeak@4.1.0: resolution: {integrity: sha512-9DDdhb5j6cpeitCbvLO7n7J4IxnbM6hoF6O1g4HQ5TfhvvKN8ywDM7668ZhMHRqVmxqhps/F6syWK2KcPxYlkw==} @@ -6046,7 +6321,6 @@ packages: /lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - dev: true /lru-cache@11.1.0: resolution: {integrity: sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==} @@ -6110,6 +6384,13 @@ packages: map-age-cleaner: 0.1.3 mimic-fn: 4.0.0 + /memoize@10.1.0: + resolution: {integrity: sha512-MMbFhJzh4Jlg/poq1si90XRlTZRDHVqdlz2mPyGJ6kqMpyHUyVpDd5gpFAvVehW64+RA1eKE9Yt8aSLY7w2Kgg==} + engines: {node: '>=18'} + dependencies: + mimic-function: 5.0.1 + dev: false + /merge-descriptors@1.0.3: resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} dev: true @@ -6158,6 +6439,11 @@ packages: resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} engines: {node: '>=12'} + /mimic-function@5.0.1: + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + engines: {node: '>=18'} + dev: false + /mini-svg-data-uri@1.4.4: resolution: {integrity: sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==} hasBin: true @@ -6180,7 +6466,6 @@ packages: engines: {node: '>=16 || 14 >=14.17'} dependencies: brace-expansion: 2.0.1 - dev: true /minipass-collect@2.0.1: resolution: {integrity: sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==} @@ -6245,12 +6530,25 @@ packages: yallist: 4.0.0 dev: true + /minizlib@3.0.2: + resolution: {integrity: sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==} + engines: {node: '>= 18'} + dependencies: + minipass: 7.1.2 + dev: false + /mkdirp@1.0.4: resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} engines: {node: '>=10'} hasBin: true dev: true + /mkdirp@3.0.1: + resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} + engines: {node: '>=10'} + hasBin: true + dev: false + /mock-fs@5.4.1: resolution: {integrity: sha512-sz/Q8K1gXXXHR+qr0GZg2ysxCRr323kuN10O7CtQjraJsFDJ4SJ+0I5MzALz7aRp9lHk8Cc/YdsT95h9Ka1aFw==} engines: {node: '>=12.0.0'} @@ -6324,6 +6622,11 @@ packages: whatwg-url: 5.0.0 dev: false + /node-gyp-build@4.8.4: + resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} + hasBin: true + dev: false + /nodemon@3.0.1: resolution: {integrity: sha512-g9AZ7HmkhQkqXkRc20w+ZfQ73cHLbE8hnPbtaFbFtCumZsjyMhKk9LajQ07U5Ux28lvFjZ5X7HvWR1xzU8jHVw==} engines: {node: '>=10'} @@ -6345,6 +6648,14 @@ packages: resolution: {integrity: sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g==} engines: {node: '>=12.19'} + /nopt@8.1.0: + resolution: {integrity: sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==} + engines: {node: ^18.17.0 || >=20.5.0} + hasBin: true + dependencies: + abbrev: 3.0.1 + dev: false + /normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} @@ -6545,6 +6856,11 @@ packages: dependencies: aggregate-error: 4.0.1 + /p-map@7.0.3: + resolution: {integrity: sha512-VkndIv2fIB99swvQoA65bm+fsmt6UNdGeIB0oxBs+WhAhdh08QA04JXpI7rbB9r08/nkbysKoya9rtDERYOYMA==} + engines: {node: '>=18'} + dev: false + /p-queue@6.6.2: resolution: {integrity: sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==} engines: {node: '>=8'} @@ -6577,6 +6893,14 @@ packages: engines: {node: '>=6'} dev: true + /package-config@5.0.0: + resolution: {integrity: sha512-GYTTew2slBcYdvRHqjhwaaydVMvn/qrGC323+nKclYioNSLTDUM/lGgtGTgyHVtYcozb+XkE8CNhwcraOmZ9Mg==} + engines: {node: '>=18'} + dependencies: + find-up-simple: 1.0.1 + load-json-file: 7.0.1 + dev: false + /package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} @@ -6609,6 +6933,11 @@ packages: resolution: {integrity: sha512-Tpb8Z7r7XbbtBTrM9UhpkzzaMrqA2VXMT3YChzYltwV3P3pM6t8wl7TvpMnSTosz1aQAdVib7kdoys7vYOPerw==} engines: {node: '>=12'} + /parse-ms@4.0.0: + resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==} + engines: {node: '>=18'} + dev: false + /parse5-htmlparser2-tree-adapter@7.1.0: resolution: {integrity: sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==} dependencies: @@ -6663,7 +6992,6 @@ packages: dependencies: lru-cache: 10.4.3 minipass: 7.1.2 - dev: true /path-scurry@2.0.0: resolution: {integrity: sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==} @@ -6691,6 +7019,11 @@ packages: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} + /path-type@6.0.0: + resolution: {integrity: sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==} + engines: {node: '>=18'} + dev: false + /peek-stream@1.1.3: resolution: {integrity: sha512-FhJ+YbOSBb9/rIl2ZeE/QHEsWn7PqNYt8ARAY3kIgNGOk13g9FGyIY6JIl/xB/3TFRVoTv5as0l11weORrTekA==} dependencies: @@ -6731,6 +7064,11 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} + /picomatch@4.0.2: + resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + engines: {node: '>=12'} + dev: false + /pify@2.3.0: resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} engines: {node: '>=0.10.0'} @@ -6902,6 +7240,13 @@ packages: dependencies: parse-ms: 3.0.0 + /pretty-ms@9.2.0: + resolution: {integrity: sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg==} + engines: {node: '>=18'} + dependencies: + parse-ms: 4.0.0 + dev: false + /proc-log@4.2.0: resolution: {integrity: sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -7348,6 +7693,11 @@ packages: resolution: {integrity: sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==} engines: {node: '>=12'} + /slash@5.1.0: + resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} + engines: {node: '>=14.16'} + dev: false + /slice-ansi@5.0.0: resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==} engines: {node: '>=12'} @@ -7494,6 +7844,15 @@ packages: emoji-regex: 9.2.2 strip-ansi: 7.1.0 + /string-width@7.2.0: + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + engines: {node: '>=18'} + dependencies: + emoji-regex: 10.4.0 + get-east-asian-width: 1.3.0 + strip-ansi: 7.1.0 + dev: false + /string_decoder@1.1.1: resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} dependencies: @@ -7624,6 +7983,18 @@ packages: yallist: 4.0.0 dev: true + /tar@7.4.3: + resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==} + engines: {node: '>=18'} + dependencies: + '@isaacs/fs-minipass': 4.0.1 + chownr: 3.0.0 + minipass: 7.1.2 + minizlib: 3.0.2 + mkdirp: 3.0.1 + yallist: 5.0.0 + dev: false + /temp-dir@3.0.0: resolution: {integrity: sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==} engines: {node: '>=14.16'} @@ -7947,6 +8318,11 @@ packages: engines: {node: '>=20.18.1'} dev: false + /unicorn-magic@0.3.0: + resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} + engines: {node: '>=18'} + dev: false + /unique-filename@3.0.0: resolution: {integrity: sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -8081,6 +8457,14 @@ packages: imurmurhash: 0.1.4 signal-exit: 4.1.0 + /write-file-atomic@6.0.0: + resolution: {integrity: sha512-GmqrO8WJ1NuzJ2DrziEI2o57jKAVIQNf8a18W3nCYU3H7PNWqCCVTeH6/NQE93CIllIgQS98rrmVkYgTX9fFJQ==} + engines: {node: ^18.17.0 || >=20.5.0} + dependencies: + imurmurhash: 0.1.4 + signal-exit: 4.1.0 + dev: false + /ws@8.18.0: resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} engines: {node: '>=10.0.0'} @@ -8105,6 +8489,11 @@ packages: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} dev: true + /yallist@5.0.0: + resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} + engines: {node: '>=18'} + dev: false + /yaml@1.10.2: resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} engines: {node: '>= 6'}