From 2daa88c25f182c718c5e921fbff3331bb7e94b80 Mon Sep 17 00:00:00 2001 From: Farhan Yahaya Date: Wed, 5 Mar 2025 03:00:24 +0000 Subject: [PATCH 01/28] feat: runtime support for re-usable global functions --- packages/lexicon/core.d.ts | 3 ++ packages/runtime/src/execute/compile-plan.ts | 1 + packages/runtime/src/execute/expression.ts | 34 ++++++++++++++++++-- packages/runtime/src/types.ts | 3 ++ 4 files changed, 39 insertions(+), 2 deletions(-) diff --git a/packages/lexicon/core.d.ts b/packages/lexicon/core.d.ts index 5ba75c6f7..217357f4d 100644 --- a/packages/lexicon/core.d.ts +++ b/packages/lexicon/core.d.ts @@ -34,6 +34,9 @@ export type Workflow = { // global credentials // (gets applied to every configuration object) credentials?: Record; + + // a path to a file where functions are defined! + functions?: string; }; /** diff --git a/packages/runtime/src/execute/compile-plan.ts b/packages/runtime/src/execute/compile-plan.ts index 01a18a255..dee86945b 100644 --- a/packages/runtime/src/execute/compile-plan.ts +++ b/packages/runtime/src/execute/compile-plan.ts @@ -100,6 +100,7 @@ export default (plan: ExecutionPlan) => { const newPlan: CompiledExecutionPlan = { workflow: { steps: {}, + functions: workflow.functions, }, options: { ...options, diff --git a/packages/runtime/src/execute/expression.ts b/packages/runtime/src/execute/expression.ts index a161fb696..b6bc9998b 100644 --- a/packages/runtime/src/execute/expression.ts +++ b/packages/runtime/src/execute/expression.ts @@ -21,13 +21,18 @@ import { assertSecurityKill, AdaptorError, } from '../errors'; -import type { JobModule, ExecutionContext } from '../types'; +import type { + JobModule, + ExecutionContext, + GlobalFunctionsModule, +} from '../types'; import { ModuleInfoMap } from '../modules/linker'; import { clearNullState, isNullState, createNullState, } from '../util/null-state'; +import vm from '../modules/experimental-vm'; export type ExecutionErrorWrapper = { state: any; @@ -50,8 +55,14 @@ export default ( try { const timeout = plan.options?.timeout ?? ctx.opts.defaultRunTimeoutMs; + // prepare global functions to be injected into execution context + let funcs = {}; + if (plan.workflow?.functions) + funcs = await prepareGlobalFunctions(plan.workflow.functions); + const globals = { ...opts.globals, ...funcs }; + // Setup an execution context - const context = buildContext(input, opts); + const context = buildContext(input, { ...opts, globals }); const { operations, execute } = await prepareJob( expression, @@ -238,3 +249,22 @@ const prepareJob = async ( return { operations: expression as Operation[] }; } }; + +const prepareGlobalFunctions = async ( + source: string, + opts: Options = {} +): Promise => { + if (typeof source === 'string' && !!source.trim()) { + const context = vm.createContext({ console: opts.logger }); + const funcs = await loadModule(source || '', { + context, + }).catch((e) => { + // mostly syntax errors + // repackage errors and throw + e.message = `(global functions) ${e.message}`; + throw e; + }); + return funcs; + } + return {}; +}; diff --git a/packages/runtime/src/types.ts b/packages/runtime/src/types.ts index f8f45c832..59c24f1c4 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: { + functions?: string; steps: Record; credentials?: Record; }; @@ -55,6 +56,8 @@ export type JobModule = { // TODO lifecycle hooks }; +export type GlobalFunctionsModule = Record; + type NotifyHandler = ( event: NotifyEvents, payload: NotifyEventsLookup[typeof event] From a39327f6a73483addb4493e99dc3e31d724999b3 Mon Sep 17 00:00:00 2001 From: Farhan Yahaya Date: Wed, 5 Mar 2025 09:32:30 +0000 Subject: [PATCH 02/28] tests: using global functions --- packages/runtime/src/execute/expression.ts | 3 +++ .../runtime/test/execute/compile-plan.test.ts | 25 +++++++++++++++++++ .../runtime/test/execute/expression.test.ts | 17 +++++++++++++ 3 files changed, 45 insertions(+) diff --git a/packages/runtime/src/execute/expression.ts b/packages/runtime/src/execute/expression.ts index b6bc9998b..43471bc7a 100644 --- a/packages/runtime/src/execute/expression.ts +++ b/packages/runtime/src/execute/expression.ts @@ -64,12 +64,15 @@ export default ( // Setup an execution context const context = buildContext(input, { ...opts, globals }); + // FIXME: when expression isn't a string, additional stuff isn't loaded + // eg. global functions might not be loaded! const { operations, execute } = await prepareJob( expression, context, opts, moduleOverrides ); + // Create the main reducer function const reducer = (execute || defaultExecute)( ...operations.map((op, idx) => diff --git a/packages/runtime/test/execute/compile-plan.test.ts b/packages/runtime/test/execute/compile-plan.test.ts index 922b593ea..ea1c10e04 100644 --- a/packages/runtime/test/execute/compile-plan.test.ts +++ b/packages/runtime/test/execute/compile-plan.test.ts @@ -433,6 +433,31 @@ test('throw for a syntax error on a job edge', (t) => { } }); +test('global functions. when undefined', (t) => { + const plan: ExecutionPlan = { + workflow: { + steps: [{ expression: 'x' }, { expression: 'y' }], + }, + options: {}, + }; + const compiled = compilePlan(plan); + t.is(compiled.workflow.functions, undefined); +}); + +test('global functions. perform no transformation', (t) => { + const GLOBAL_FUNCTIONS = + 'export const cleaner = (name) => name.replace("a", "b");'; + const plan: ExecutionPlan = { + workflow: { + functions: GLOBAL_FUNCTIONS, + steps: [{ expression: 'x' }, { expression: 'y' }], + }, + options: {}, + }; + const compiled = compilePlan(plan); + t.is(compiled.workflow.functions, 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..8af979c28 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, + functions: "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(); From 8b564f65580e7ca2b84eaa16c6eba75f00d9d64b Mon Sep 17 00:00:00 2001 From: Farhan Yahaya Date: Wed, 5 Mar 2025 09:50:25 +0000 Subject: [PATCH 03/28] feat: cli loading of global functions --- packages/cli/src/types.ts | 1 + packages/cli/src/util/load-plan.ts | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/packages/cli/src/types.ts b/packages/cli/src/types.ts index ba0b3b8cf..8f67764ee 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; + functions?: string; }; }; diff --git a/packages/cli/src/util/load-plan.ts b/packages/cli/src/util/load-plan.ts index ed12eb8f4..e85f83313 100644 --- a/packages/cli/src/util/load-plan.ts +++ b/packages/cli/src/util/load-plan.ts @@ -182,6 +182,23 @@ const fetchFile = async ( } }; +const importFunctions = async ( + plan: CLIExecutionPlan, + rootDir: string, + log: Logger +) => { + const fnStr = plan.workflow?.functions; + if (fnStr && isPath(fnStr)) { + // FIXME: fetchFile function isn't generic enough + plan.workflow.functions = await fetchFile( + 'global functions', + rootDir, + fnStr, + log + ); + } +}; + // TODO this is currently untested in load-plan // (but covered a bit in execute tests) const importExpressions = async ( @@ -260,6 +277,9 @@ const loadXPlan = async ( } ensureAdaptors(plan); + // import global functions + await importFunctions(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 From 65cb9b8a4a0af0d78e967737bda04f91f0a66f04 Mon Sep 17 00:00:00 2001 From: Farhan Yahaya Date: Wed, 5 Mar 2025 16:51:05 +0000 Subject: [PATCH 04/28] tests: cli tests for globals --- packages/cli/test/execute/execute.test.ts | 53 +++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/packages/cli/test/execute/execute.test.ts b/packages/cli/test/execute/execute.test.ts index 5cbb22f69..afc7b8acb 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: { + functions: "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: { + functions: '/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!' }); +}); From e1953cac4e5cfa0b9a48099f660166d2c2995405 Mon Sep 17 00:00:00 2001 From: Farhan Yahaya Date: Wed, 5 Mar 2025 17:40:02 +0000 Subject: [PATCH 05/28] tests: scope global functions per step or job code --- packages/runtime/test/execute/plan.test.ts | 40 +++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/packages/runtime/test/execute/plan.test.ts b/packages/runtime/test/execute/plan.test.ts index d8be4a1d3..6db0b2e89 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 = {}, + functions?: string ): ExecutionPlan => ({ workflow: { + functions, steps, }, options, @@ -1204,3 +1206,39 @@ 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 functions = ` + export const addToBase = ((a) => (b) => { a = a + b; return a })(0); + export const INC = 5; + `; + const plan = createPlan( + [ + { + id: 'a', + name: 'do-a', + expression: + 'export default [s => {addToBase(INC); return s;}, s => {state.data.a = addToBase(INC); return state;}]', + next: { + b: true, + }, + }, + { + id: 'b', + name: 'do-b', + expression: + 'export default [s => {addToBase(INC); return s;}, s => {state.data.b = addToBase(INC); return state;}]', + }, + ], + {}, + functions + ); + + const result: any = await executePlan(plan, {}, {}, mockLogger); + + t.notThrows(() => JSON.stringify(result)); + t.deepEqual(result.data, { a: 10, b: 10 }); + } +); From ec66bc5dc872447d9551e9f9ac70a55fc71c52ca Mon Sep 17 00:00:00 2001 From: Farhan Yahaya Date: Wed, 5 Mar 2025 17:49:21 +0000 Subject: [PATCH 06/28] refactor: rename functions to globals --- packages/cli/src/types.ts | 2 +- packages/cli/src/util/load-plan.ts | 8 ++++---- packages/cli/test/execute/execute.test.ts | 4 ++-- packages/lexicon/core.d.ts | 2 +- packages/runtime/src/execute/compile-plan.ts | 2 +- packages/runtime/src/execute/expression.ts | 16 ++++++---------- packages/runtime/src/types.ts | 4 ++-- .../runtime/test/execute/compile-plan.test.ts | 6 +++--- packages/runtime/test/execute/expression.test.ts | 2 +- packages/runtime/test/execute/plan.test.ts | 4 ++-- 10 files changed, 23 insertions(+), 27 deletions(-) diff --git a/packages/cli/src/types.ts b/packages/cli/src/types.ts index 8f67764ee..2f5cb0427 100644 --- a/packages/cli/src/types.ts +++ b/packages/cli/src/types.ts @@ -18,7 +18,7 @@ export type CLIExecutionPlan = { id?: UUID; name?: string; steps: Array; - functions?: string; + globals?: string; }; }; diff --git a/packages/cli/src/util/load-plan.ts b/packages/cli/src/util/load-plan.ts index e85f83313..75a537654 100644 --- a/packages/cli/src/util/load-plan.ts +++ b/packages/cli/src/util/load-plan.ts @@ -182,15 +182,15 @@ const fetchFile = async ( } }; -const importFunctions = async ( +const importGlobals = async ( plan: CLIExecutionPlan, rootDir: string, log: Logger ) => { - const fnStr = plan.workflow?.functions; + const fnStr = plan.workflow?.globals; if (fnStr && isPath(fnStr)) { // FIXME: fetchFile function isn't generic enough - plan.workflow.functions = await fetchFile( + plan.workflow.globals = await fetchFile( 'global functions', rootDir, fnStr, @@ -278,7 +278,7 @@ const loadXPlan = async ( ensureAdaptors(plan); // import global functions - await importFunctions(plan, options.baseDir!, logger); + await importGlobals(plan, options.baseDir!, logger); // Note that baseDir should be set up in the default function await importExpressions(plan, options.baseDir!, logger); diff --git a/packages/cli/test/execute/execute.test.ts b/packages/cli/test/execute/execute.test.ts index afc7b8acb..66acbe26b 100644 --- a/packages/cli/test/execute/execute.test.ts +++ b/packages/cli/test/execute/execute.test.ts @@ -454,7 +454,7 @@ test.serial('run a job which does not return state', async (t) => { test.serial('globals: use a global function in an operation', async (t) => { const workflow = { workflow: { - functions: "export const prefixer = (w) => 'welcome '+w", + globals: "export const prefixer = (w) => 'welcome '+w", steps: [ { id: 'a', @@ -480,7 +480,7 @@ test.serial('globals: use a global function in an operation', async (t) => { test.serial('globals: get global functions from a filePath', async (t) => { const workflow = { workflow: { - functions: '/my-globals.js', + globals: '/my-globals.js', steps: [ { id: 'a', diff --git a/packages/lexicon/core.d.ts b/packages/lexicon/core.d.ts index 217357f4d..24e986820 100644 --- a/packages/lexicon/core.d.ts +++ b/packages/lexicon/core.d.ts @@ -36,7 +36,7 @@ export type Workflow = { credentials?: Record; // a path to a file where functions are defined! - functions?: string; + globals?: string; }; /** diff --git a/packages/runtime/src/execute/compile-plan.ts b/packages/runtime/src/execute/compile-plan.ts index dee86945b..2052f52da 100644 --- a/packages/runtime/src/execute/compile-plan.ts +++ b/packages/runtime/src/execute/compile-plan.ts @@ -100,7 +100,7 @@ export default (plan: ExecutionPlan) => { const newPlan: CompiledExecutionPlan = { workflow: { steps: {}, - functions: workflow.functions, + globals: workflow.globals, }, options: { ...options, diff --git a/packages/runtime/src/execute/expression.ts b/packages/runtime/src/execute/expression.ts index 43471bc7a..58261eba0 100644 --- a/packages/runtime/src/execute/expression.ts +++ b/packages/runtime/src/execute/expression.ts @@ -21,11 +21,7 @@ import { assertSecurityKill, AdaptorError, } from '../errors'; -import type { - JobModule, - ExecutionContext, - GlobalFunctionsModule, -} from '../types'; +import type { JobModule, ExecutionContext, GlobalsModule } from '../types'; import { ModuleInfoMap } from '../modules/linker'; import { clearNullState, @@ -57,8 +53,8 @@ export default ( // prepare global functions to be injected into execution context let funcs = {}; - if (plan.workflow?.functions) - funcs = await prepareGlobalFunctions(plan.workflow.functions); + if (plan.workflow?.globals) + funcs = await prepareGlobals(plan.workflow.globals); const globals = { ...opts.globals, ...funcs }; // Setup an execution context @@ -253,10 +249,10 @@ const prepareJob = async ( } }; -const prepareGlobalFunctions = async ( +const prepareGlobals = async ( source: string, opts: Options = {} -): Promise => { +): Promise => { if (typeof source === 'string' && !!source.trim()) { const context = vm.createContext({ console: opts.logger }); const funcs = await loadModule(source || '', { @@ -264,7 +260,7 @@ const prepareGlobalFunctions = async ( }).catch((e) => { // mostly syntax errors // repackage errors and throw - e.message = `(global functions) ${e.message}`; + e.message = `(inside globals) ${e.message}`; throw e; }); return funcs; diff --git a/packages/runtime/src/types.ts b/packages/runtime/src/types.ts index 59c24f1c4..471e8b7b1 100644 --- a/packages/runtime/src/types.ts +++ b/packages/runtime/src/types.ts @@ -41,7 +41,7 @@ export type Lazy = string | T; export type CompiledExecutionPlan = { workflow: { - functions?: string; + globals?: string; steps: Record; credentials?: Record; }; @@ -56,7 +56,7 @@ export type JobModule = { // TODO lifecycle hooks }; -export type GlobalFunctionsModule = Record; +export type GlobalsModule = Record; type NotifyHandler = ( event: NotifyEvents, diff --git a/packages/runtime/test/execute/compile-plan.test.ts b/packages/runtime/test/execute/compile-plan.test.ts index ea1c10e04..02517f22f 100644 --- a/packages/runtime/test/execute/compile-plan.test.ts +++ b/packages/runtime/test/execute/compile-plan.test.ts @@ -441,7 +441,7 @@ test('global functions. when undefined', (t) => { options: {}, }; const compiled = compilePlan(plan); - t.is(compiled.workflow.functions, undefined); + t.is(compiled.workflow.globals, undefined); }); test('global functions. perform no transformation', (t) => { @@ -449,13 +449,13 @@ test('global functions. perform no transformation', (t) => { 'export const cleaner = (name) => name.replace("a", "b");'; const plan: ExecutionPlan = { workflow: { - functions: GLOBAL_FUNCTIONS, + globals: GLOBAL_FUNCTIONS, steps: [{ expression: 'x' }, { expression: 'y' }], }, options: {}, }; const compiled = compilePlan(plan); - t.is(compiled.workflow.functions, GLOBAL_FUNCTIONS); + t.is(compiled.workflow.globals, GLOBAL_FUNCTIONS); }); test('throw for multiple errors', (t) => { diff --git a/packages/runtime/test/execute/expression.test.ts b/packages/runtime/test/execute/expression.test.ts index 8af979c28..046ba87b8 100644 --- a/packages/runtime/test/execute/expression.test.ts +++ b/packages/runtime/test/execute/expression.test.ts @@ -60,7 +60,7 @@ test.serial('use global function defined in functions', async (t) => { const context = createContext(); context.plan.workflow = { steps: [] as any, - functions: "export const prefixer = (w) => 'welcome '+ w", + globals: "export const prefixer = (w) => 'welcome '+ w", }; const result = await execute(context, job, state); diff --git a/packages/runtime/test/execute/plan.test.ts b/packages/runtime/test/execute/plan.test.ts index 6db0b2e89..5cc96b586 100644 --- a/packages/runtime/test/execute/plan.test.ts +++ b/packages/runtime/test/execute/plan.test.ts @@ -11,10 +11,10 @@ let mockLogger = createMockLogger(undefined, { level: 'debug' }); const createPlan = ( steps: Job[], options: Partial = {}, - functions?: string + globals?: string ): ExecutionPlan => ({ workflow: { - functions, + globals, steps, }, options, From dfe358968a9256d8fccf38473417c00e5c34534c Mon Sep 17 00:00:00 2001 From: Farhan Yahaya Date: Wed, 5 Mar 2025 18:05:41 +0000 Subject: [PATCH 07/28] refactor: update fetchfile function signature --- packages/cli/src/util/load-plan.ts | 43 +++++++++++++++++++----------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/packages/cli/src/util/load-plan.ts b/packages/cli/src/util/load-plan.ts index 75a537654..e27c09923 100644 --- a/packages/cli/src/util/load-plan.ts +++ b/packages/cli/src/util/load-plan.ts @@ -156,11 +156,15 @@ const loadOldWorkflow = async ( }; const fetchFile = async ( - jobId: string, - rootDir: string = '', - filePath: string, + fileInfo: { + id: string; + fileType: string; + rootDir?: string; + filePath: string; + }, log: Logger ) => { + const { id, rootDir = '', filePath, fileType = 'resource' } = fileInfo; try { // Special handling for ~ feels like a necessary evil const fullPath = filePath.startsWith('~') @@ -172,7 +176,7 @@ const fetchFile = async ( } catch (e) { abort( log, - `File not found for job ${jobId}: ${filePath}`, + `File not found for ${fileType} ${id}: ${filePath}`, undefined, `This workflow references a file which cannot be found at ${filePath}\n\nPaths inside the workflow are relative to the workflow.json` ); @@ -191,9 +195,7 @@ const importGlobals = async ( if (fnStr && isPath(fnStr)) { // FIXME: fetchFile function isn't generic enough plan.workflow.globals = await fetchFile( - 'global functions', - rootDir, - fnStr, + { id: '', fileType: 'globals', rootDir, filePath: fnStr }, log ); } @@ -221,26 +223,35 @@ const importExpressions = async ( if (expressionStr && isPath(expressionStr)) { job.expression = await fetchFile( - job.id || `${idx}`, - rootDir, - expressionStr, + { + id: job.id || `${idx}`, + rootDir, + filePath: expressionStr, + fileType: 'job', + }, log ); } if (configurationStr && isPath(configurationStr)) { const configString = await fetchFile( - job.id || `${idx}`, - rootDir, - configurationStr, + { + id: job.id || `${idx}`, + rootDir, + filePath: configurationStr, + fileType: 'job configuration', + }, log ); job.configuration = JSON.parse(configString!); } if (stateStr && isPath(stateStr)) { const stateString = await fetchFile( - job.id || `${idx}`, - rootDir, - stateStr, + { + id: job.id || `${idx}`, + rootDir, + filePath: stateStr, + fileType: 'job state', + }, log ); job.state = JSON.parse(stateString!); From 54dd347fa2f663bf590ebe231edef35bb24b9650 Mon Sep 17 00:00:00 2001 From: Farhan Yahaya Date: Thu, 6 Mar 2025 07:41:02 +0000 Subject: [PATCH 08/28] refactor: resolve comments --- packages/cli/src/util/load-plan.ts | 1 - packages/lexicon/core.d.ts | 2 +- packages/runtime/src/execute/expression.ts | 15 ++++++++------- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/cli/src/util/load-plan.ts b/packages/cli/src/util/load-plan.ts index e27c09923..a161a7eef 100644 --- a/packages/cli/src/util/load-plan.ts +++ b/packages/cli/src/util/load-plan.ts @@ -193,7 +193,6 @@ const importGlobals = async ( ) => { const fnStr = plan.workflow?.globals; if (fnStr && isPath(fnStr)) { - // FIXME: fetchFile function isn't generic enough plan.workflow.globals = await fetchFile( { id: '', fileType: 'globals', rootDir, filePath: fnStr }, log diff --git a/packages/lexicon/core.d.ts b/packages/lexicon/core.d.ts index 24e986820..a3d1e2f32 100644 --- a/packages/lexicon/core.d.ts +++ b/packages/lexicon/core.d.ts @@ -35,7 +35,7 @@ export type Workflow = { // (gets applied to every configuration object) credentials?: Record; - // a path to a file where functions are defined! + // a path to a file where functions are defined globals?: string; }; diff --git a/packages/runtime/src/execute/expression.ts b/packages/runtime/src/execute/expression.ts index 58261eba0..a0bc3783b 100644 --- a/packages/runtime/src/execute/expression.ts +++ b/packages/runtime/src/execute/expression.ts @@ -52,10 +52,12 @@ export default ( const timeout = plan.options?.timeout ?? ctx.opts.defaultRunTimeoutMs; // prepare global functions to be injected into execution context - let funcs = {}; - if (plan.workflow?.globals) - funcs = await prepareGlobals(plan.workflow.globals); - const globals = { ...opts.globals, ...funcs }; + const globals = { + ...opts.globals, + ...(plan.workflow?.globals + ? await prepareGlobals(plan.workflow.globals) + : {}), + }; // Setup an execution context const context = buildContext(input, { ...opts, globals }); @@ -255,15 +257,14 @@ const prepareGlobals = async ( ): Promise => { if (typeof source === 'string' && !!source.trim()) { const context = vm.createContext({ console: opts.logger }); - const funcs = await loadModule(source || '', { + return await loadModule(source || '', { context, }).catch((e) => { // mostly syntax errors // repackage errors and throw - e.message = `(inside globals) ${e.message}`; + e.message = `[globals] ${e.message}`; throw e; }); - return funcs; } return {}; }; From f20a6020584cf42892ddd70256e2ea91627d7141 Mon Sep 17 00:00:00 2001 From: Farhan Yahaya Date: Thu, 6 Mar 2025 07:41:15 +0000 Subject: [PATCH 09/28] tests: fix test --- integration-tests/cli/test/errors.test.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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, From c929feb05b8e18931b0d71ca76f1ed7328572a1b Mon Sep 17 00:00:00 2001 From: Farhan Yahaya Date: Fri, 7 Mar 2025 08:38:23 +0000 Subject: [PATCH 10/28] refactor: update fetchFile signature --- packages/cli/src/util/load-plan.ts | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/packages/cli/src/util/load-plan.ts b/packages/cli/src/util/load-plan.ts index a161a7eef..18f21bb4b 100644 --- a/packages/cli/src/util/load-plan.ts +++ b/packages/cli/src/util/load-plan.ts @@ -157,14 +157,13 @@ const loadOldWorkflow = async ( const fetchFile = async ( fileInfo: { - id: string; - fileType: string; + name: string; rootDir?: string; filePath: string; }, log: Logger ) => { - const { id, rootDir = '', filePath, fileType = 'resource' } = fileInfo; + const { rootDir = '', filePath, name } = fileInfo; try { // Special handling for ~ feels like a necessary evil const fullPath = filePath.startsWith('~') @@ -176,7 +175,7 @@ const fetchFile = async ( } catch (e) { abort( log, - `File not found for ${fileType} ${id}: ${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` ); @@ -194,7 +193,7 @@ const importGlobals = async ( const fnStr = plan.workflow?.globals; if (fnStr && isPath(fnStr)) { plan.workflow.globals = await fetchFile( - { id: '', fileType: 'globals', rootDir, filePath: fnStr }, + { name: 'globals', rootDir, filePath: fnStr }, log ); } @@ -223,10 +222,9 @@ const importExpressions = async ( if (expressionStr && isPath(expressionStr)) { job.expression = await fetchFile( { - id: job.id || `${idx}`, + name: `job ${job.id || idx}`, rootDir, filePath: expressionStr, - fileType: 'job', }, log ); @@ -234,10 +232,9 @@ const importExpressions = async ( if (configurationStr && isPath(configurationStr)) { const configString = await fetchFile( { - id: job.id || `${idx}`, + name: `job configuration ${job.id || idx}`, rootDir, filePath: configurationStr, - fileType: 'job configuration', }, log ); @@ -246,10 +243,9 @@ const importExpressions = async ( if (stateStr && isPath(stateStr)) { const stateString = await fetchFile( { - id: job.id || `${idx}`, + name: `job state ${job.id || idx}`, rootDir, filePath: stateStr, - fileType: 'job state', }, log ); From bf0d9eebffc631bbdb45079baf297c1a653c30f4 Mon Sep 17 00:00:00 2001 From: Farhan Yahaya Date: Mon, 17 Mar 2025 13:23:22 +0000 Subject: [PATCH 11/28] docs: update workflow template with globals --- packages/cli/README.md | 1 + 1 file changed, 1 insertion(+) 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 From 45c584eb033bf376cfba8dfe38d978665ac65d9a Mon Sep 17 00:00:00 2001 From: Farhan Yahaya Date: Mon, 17 Mar 2025 14:31:02 +0000 Subject: [PATCH 12/28] feat: support globals in ws-worker --- packages/lexicon/lightning.d.ts | 2 ++ .../src/util/convert-lightning-plan.ts | 4 ++++ .../test/util/convert-lightning-plan.test.ts | 24 +++++++++++++++++++ 3 files changed, 30 insertions(+) diff --git a/packages/lexicon/lightning.d.ts b/packages/lexicon/lightning.d.ts index a66e8d5ad..91684b862 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/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/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); +}); From 5d4c006fbf1c9a91416c8a920b9311ae3883f990 Mon Sep 17 00:00:00 2001 From: Farhan Yahaya Date: Mon, 17 Mar 2025 14:52:17 +0000 Subject: [PATCH 13/28] tests: add execute test in ws-worker for globals --- packages/ws-worker/test/api/execute.test.ts | 36 +++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/packages/ws-worker/test/api/execute.test.ts b/packages/ws-worker/test/api/execute.test.ts index 926804630..471305717 100644 --- a/packages/ws-worker/test/api/execute.test.ts +++ b/packages/ws-worker/test/api/execute.test.ts @@ -478,3 +478,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(); + }); + }); +}); From 3af2cd386096a85f53c4f505908e3823a7411f2b Mon Sep 17 00:00:00 2001 From: Farhan Yahaya Date: Mon, 17 Mar 2025 15:00:48 +0000 Subject: [PATCH 14/28] chore: add missing arg to prepareGlobals --- packages/runtime/src/execute/expression.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/runtime/src/execute/expression.ts b/packages/runtime/src/execute/expression.ts index a0bc3783b..cfb827713 100644 --- a/packages/runtime/src/execute/expression.ts +++ b/packages/runtime/src/execute/expression.ts @@ -55,7 +55,7 @@ export default ( const globals = { ...opts.globals, ...(plan.workflow?.globals - ? await prepareGlobals(plan.workflow.globals) + ? await prepareGlobals(plan.workflow.globals, opts) : {}), }; From ea67a70ecedc1613083986b90506cf7dce290e83 Mon Sep 17 00:00:00 2001 From: Farhan Yahaya Date: Mon, 17 Mar 2025 15:07:04 +0000 Subject: [PATCH 15/28] refactor: use buildContext for context building --- packages/runtime/src/execute/expression.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/runtime/src/execute/expression.ts b/packages/runtime/src/execute/expression.ts index cfb827713..2f8a2460e 100644 --- a/packages/runtime/src/execute/expression.ts +++ b/packages/runtime/src/execute/expression.ts @@ -28,7 +28,6 @@ import { isNullState, createNullState, } from '../util/null-state'; -import vm from '../modules/experimental-vm'; export type ExecutionErrorWrapper = { state: any; @@ -256,7 +255,7 @@ const prepareGlobals = async ( opts: Options = {} ): Promise => { if (typeof source === 'string' && !!source.trim()) { - const context = vm.createContext({ console: opts.logger }); + const context = buildContext({}, opts); return await loadModule(source || '', { context, }).catch((e) => { From 6a6bce5646e1813106ec3263833eff1f99cae635 Mon Sep 17 00:00:00 2001 From: Farhan Yahaya Date: Mon, 17 Mar 2025 15:25:57 +0000 Subject: [PATCH 16/28] tests: update globals undefined tests --- packages/runtime/src/execute/compile-plan.ts | 5 ++++- .../runtime/test/execute/compile-plan.test.ts | 16 ++++++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/packages/runtime/src/execute/compile-plan.ts b/packages/runtime/src/execute/compile-plan.ts index 2052f52da..c78cc34c3 100644 --- a/packages/runtime/src/execute/compile-plan.ts +++ b/packages/runtime/src/execute/compile-plan.ts @@ -100,13 +100,16 @@ export default (plan: ExecutionPlan) => { const newPlan: CompiledExecutionPlan = { workflow: { steps: {}, - globals: workflow.globals, }, options: { ...options, 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/test/execute/compile-plan.test.ts b/packages/runtime/test/execute/compile-plan.test.ts index 02517f22f..091e99bb3 100644 --- a/packages/runtime/test/execute/compile-plan.test.ts +++ b/packages/runtime/test/execute/compile-plan.test.ts @@ -433,7 +433,7 @@ test('throw for a syntax error on a job edge', (t) => { } }); -test('global functions. when undefined', (t) => { +test('globals should be undefined if no globals are passed', (t) => { const plan: ExecutionPlan = { workflow: { steps: [{ expression: 'x' }, { expression: 'y' }], @@ -444,7 +444,19 @@ test('global functions. when undefined', (t) => { t.is(compiled.workflow.globals, undefined); }); -test('global functions. perform no transformation', (t) => { +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 = { From cc41a5b2daabcf52716839714cb5f28c7858c03d Mon Sep 17 00:00:00 2001 From: Farhan Yahaya Date: Mon, 17 Mar 2025 15:52:50 +0000 Subject: [PATCH 17/28] tests: global functions scoping tests --- packages/runtime/test/execute/plan.test.ts | 41 +++++++++++++++++----- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/packages/runtime/test/execute/plan.test.ts b/packages/runtime/test/execute/plan.test.ts index 5cc96b586..7f658562d 100644 --- a/packages/runtime/test/execute/plan.test.ts +++ b/packages/runtime/test/execute/plan.test.ts @@ -1210,9 +1210,10 @@ test('Plans log step names for each job start and end', async (t) => { test.serial( 'global functions should be scoped per step or job code', async (t) => { - const functions = ` - export const addToBase = ((a) => (b) => { a = a + b; return a })(0); - export const INC = 5; + const globals = ` + let x = 10; + export const setX = (value) => { x = value } + export const getX = () => x `; const plan = createPlan( [ @@ -1220,7 +1221,7 @@ test.serial( id: 'a', name: 'do-a', expression: - 'export default [s => {addToBase(INC); return s;}, s => {state.data.a = addToBase(INC); return state;}]', + 'export default [s => {setX(20); return {data: {x1: getX()}}}]', next: { b: true, }, @@ -1229,16 +1230,40 @@ test.serial( id: 'b', name: 'do-b', expression: - 'export default [s => {addToBase(INC); return s;}, s => {state.data.b = addToBase(INC); return state;}]', + 'export default [s => {return {data: {...s.data, x2: getX()}}}]', }, ], {}, - functions + globals ); const result: any = await executePlan(plan, {}, {}, mockLogger); + t.deepEqual(result.data, { x1: 20, x2: 10 }); + } +); - t.notThrows(() => JSON.stringify(result)); - t.deepEqual(result.data, { a: 10, b: 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 }); } ); From f7b2f6dba9d126903492f70ddfa3c1c52e718122 Mon Sep 17 00:00:00 2001 From: Farhan Yahaya Date: Mon, 17 Mar 2025 16:50:30 +0000 Subject: [PATCH 18/28] refactor: cleanup --- packages/runtime/src/execute/expression.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/runtime/src/execute/expression.ts b/packages/runtime/src/execute/expression.ts index 2f8a2460e..268011882 100644 --- a/packages/runtime/src/execute/expression.ts +++ b/packages/runtime/src/execute/expression.ts @@ -53,16 +53,12 @@ export default ( // prepare global functions to be injected into execution context const globals = { ...opts.globals, - ...(plan.workflow?.globals - ? await prepareGlobals(plan.workflow.globals, opts) - : {}), + ...(await prepareGlobals(plan.workflow?.globals || '', opts)), }; // Setup an execution context const context = buildContext(input, { ...opts, globals }); - // FIXME: when expression isn't a string, additional stuff isn't loaded - // eg. global functions might not be loaded! const { operations, execute } = await prepareJob( expression, context, From ae78f131e6cd8fa1f9ecf40f5148b95ac119c981 Mon Sep 17 00:00:00 2001 From: Farhan Yahaya Date: Wed, 4 Jun 2025 07:19:58 +0000 Subject: [PATCH 19/28] feat: get named exports & pass to ignoreList of job compilation --- packages/cli/src/compile/compile.ts | 11 +++- packages/compiler/src/getExports.ts | 47 +++++++++++++++ packages/compiler/src/index.ts | 1 + packages/compiler/test/getExports.test.ts | 72 +++++++++++++++++++++++ 4 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 packages/compiler/src/getExports.ts create mode 100644 packages/compiler/test/getExports.test.ts diff --git a/packages/cli/src/compile/compile.ts b/packages/cli/src/compile/compile.ts index 9a7818047..b6719a662 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,16 @@ const compileWorkflow = async ( opts: CompileOptions, log: Logger ) => { + let globalsIgnoreList: string[] = []; + if (plan.workflow.globals) + globalsIgnoreList = 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/compiler/src/getExports.ts b/packages/compiler/src/getExports.ts new file mode 100644 index 000000000..34c1cd985 --- /dev/null +++ b/packages/compiler/src/getExports.ts @@ -0,0 +1,47 @@ +import recast from 'recast'; +import * as acorn from 'acorn'; +import { namedTypes as n } from 'ast-types'; + +function getExports(content: string) { + 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/getExports.test.ts b/packages/compiler/test/getExports.test.ts new file mode 100644 index 000000000..3d69c9f14 --- /dev/null +++ b/packages/compiler/test/getExports.test.ts @@ -0,0 +1,72 @@ +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']); +}); + +// TODO: do we want to pick up default exports? +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']); +}); From 0806a38f51663b8f1f00d5a3cabbc49710dc8597 Mon Sep 17 00:00:00 2001 From: Farhan Yahaya Date: Wed, 4 Jun 2025 08:29:11 +0000 Subject: [PATCH 20/28] test: adaptor imports should respect ignore list --- packages/compiler/test/compile.test.ts | 34 ++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) 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); +}); From 0407d33b45c6e52f293aa848133ebc2bf9875750 Mon Sep 17 00:00:00 2001 From: Farhan Yahaya Date: Wed, 4 Jun 2025 08:46:17 +0000 Subject: [PATCH 21/28] test: global functions expression --- .../cli/test/execute-workflow.test.ts | 11 +++++++++++ .../cli/test/fixtures/globals-exp.json | 19 +++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 integration-tests/cli/test/fixtures/globals-exp.json diff --git a/integration-tests/cli/test/execute-workflow.test.ts b/integration-tests/cli/test/execute-workflow.test.ts index 8c4216a5d..384ef435e 100644 --- a/integration-tests/cli/test/execute-workflow.test.ts +++ b/integration-tests/cli/test/execute-workflow.test.ts @@ -221,3 +221,14 @@ test.serial( }); } ); + +test.serial(`openfn ${jobsPath}/globals-exp.json`, async (t) => { + await run(t.title); + const out = getJSON(); + t.deepEqual(out, { + alter: 'heartsfx', + data: {}, + final: 'some-big-valueheartsfx', + val: 'some-big-value', + }); +}); 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 From b92f460012201da4399ab8c04c8e7f683fe91848 Mon Sep 17 00:00:00 2001 From: Farhan Yahaya Date: Wed, 4 Jun 2025 08:55:25 +0000 Subject: [PATCH 22/28] test: globals with relative file path --- .../cli/test/execute-workflow.test.ts | 11 +++++++++++ .../cli/test/fixtures/globals-path-file.js | 5 +++++ .../cli/test/fixtures/globals-path.json | 19 +++++++++++++++++++ 3 files changed, 35 insertions(+) create mode 100644 integration-tests/cli/test/fixtures/globals-path-file.js create mode 100644 integration-tests/cli/test/fixtures/globals-path.json diff --git a/integration-tests/cli/test/execute-workflow.test.ts b/integration-tests/cli/test/execute-workflow.test.ts index 384ef435e..33830cbd7 100644 --- a/integration-tests/cli/test/execute-workflow.test.ts +++ b/integration-tests/cli/test/execute-workflow.test.ts @@ -232,3 +232,14 @@ test.serial(`openfn ${jobsPath}/globals-exp.json`, async (t) => { val: 'some-big-value', }); }); + +test.serial(`openfn ${jobsPath}/globals-path.json`, async (t) => { + await run(t.title); + const out = getJSON(); + t.deepEqual(out, { + alter: 'heartsfx', + data: {}, + final: 'some-big-valueheartsfx', + val: 'some-big-value', + }); +}); 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..63777291f --- /dev/null +++ b/integration-tests/cli/test/fixtures/globals-path-file.js @@ -0,0 +1,5 @@ +export const BIG_VAL = 'some-big-value'; + +export function suffix(name) { + return name + 'sfx' +} \ 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 From 8b818adc78e999fa7f07223e567fa4bcff062044 Mon Sep 17 00:00:00 2001 From: Farhan Yahaya Date: Wed, 4 Jun 2025 14:21:45 +0000 Subject: [PATCH 23/28] feat: add --globals argument to cli eg. openfn --globals ./path-to-globals.js --- packages/cli/src/compile/command.ts | 1 + packages/cli/src/execute/command.ts | 1 + packages/cli/src/options.ts | 1 + packages/cli/src/util/load-plan.ts | 14 ++++++++++++-- 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/compile/command.ts b/packages/cli/src/compile/command.ts index 5b9957b45..c605da741 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' > & { repoDir?: string; }; diff --git a/packages/cli/src/execute/command.ts b/packages/cli/src/execute/command.ts index f4e84cc1a..fe122056e 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 9b2955b67..874d8356c 100644 --- a/packages/cli/src/options.ts +++ b/packages/cli/src/options.ts @@ -17,6 +17,7 @@ export type Opts = { baseDir?: string; path?: string; + globals?: string; adaptor?: boolean | string; adaptors?: string[]; apolloUrl?: string; diff --git a/packages/cli/src/util/load-plan.ts b/packages/cli/src/util/load-plan.ts index 18f21bb4b..a74aa85fd 100644 --- a/packages/cli/src/util/load-plan.ts +++ b/packages/cli/src/util/load-plan.ts @@ -19,6 +19,7 @@ const loadPlan = async ( | 'adaptors' | 'baseDir' | 'expandAdaptors' + | 'globals' >, logger: Logger ): Promise => { @@ -85,7 +86,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!; @@ -109,6 +113,7 @@ const loadExpression = async ( workflow: { name, steps: [step], + globals: options.globals, }, options: wfOptions, }; @@ -270,7 +275,10 @@ const ensureAdaptors = (plan: CLIExecutionPlan) => { const loadXPlan = async ( plan: CLIExecutionPlan, - options: Pick, + options: Pick< + Opts, + 'monorepoPath' | 'baseDir' | 'expandAdaptors' | 'globals' + >, logger: Logger, defaultName: string = '' ) => { @@ -284,6 +292,8 @@ 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 From fc16f7f493bc29d9d005ca6e8db08fbd859f28ea Mon Sep 17 00:00:00 2001 From: Farhan Yahaya Date: Fri, 6 Jun 2025 21:50:25 +0000 Subject: [PATCH 24/28] tests: globals via cli argument --- .../cli/test/execute-workflow.test.ts | 40 ++++++++++++++++--- .../cli/test/fixtures/globals-job.js | 3 ++ .../cli/test/fixtures/globals-path-file.js | 8 ++-- packages/cli/src/util/load-plan.ts | 5 ++- 4 files changed, 46 insertions(+), 10 deletions(-) create mode 100644 integration-tests/cli/test/fixtures/globals-job.js diff --git a/integration-tests/cli/test/execute-workflow.test.ts b/integration-tests/cli/test/execute-workflow.test.ts index 33830cbd7..da069b938 100644 --- a/integration-tests/cli/test/execute-workflow.test.ts +++ b/integration-tests/cli/test/execute-workflow.test.ts @@ -223,7 +223,8 @@ test.serial( ); test.serial(`openfn ${jobsPath}/globals-exp.json`, async (t) => { - await run(t.title); + const res = await run(t.title); + t.falsy(res.err); const out = getJSON(); t.deepEqual(out, { alter: 'heartsfx', @@ -234,12 +235,41 @@ test.serial(`openfn ${jobsPath}/globals-exp.json`, async (t) => { }); test.serial(`openfn ${jobsPath}/globals-path.json`, async (t) => { - await run(t.title); + const res = await run(t.title); + t.falsy(res.err); const out = getJSON(); t.deepEqual(out, { - alter: 'heartsfx', + alter: 'heart.path.value', data: {}, - final: 'some-big-valueheartsfx', - val: 'some-big-value', + 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-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 index 63777291f..b7689ee97 100644 --- a/integration-tests/cli/test/fixtures/globals-path-file.js +++ b/integration-tests/cli/test/fixtures/globals-path-file.js @@ -1,5 +1,7 @@ -export const BIG_VAL = 'some-big-value'; +export const BIG_VAL = 'path-value'; export function suffix(name) { - return name + 'sfx' -} \ No newline at end of file + return name + '.path.value' +} + +export const suffixer = w => w + '-humble-suffix' \ No newline at end of file diff --git a/packages/cli/src/util/load-plan.ts b/packages/cli/src/util/load-plan.ts index aaa01ced6..60991558c 100644 --- a/packages/cli/src/util/load-plan.ts +++ b/packages/cli/src/util/load-plan.ts @@ -252,12 +252,13 @@ const importGlobals = async ( log: Logger ) => { const fnStr = plan.workflow?.globals; - if (fnStr && isPath(fnStr)) { + if (!fnStr) return; + 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 From 5ded024291c8e31e706f1b691a3e6f4a3219c3bc Mon Sep 17 00:00:00 2001 From: Joe Clark Date: Sun, 15 Jun 2025 15:10:32 +0100 Subject: [PATCH 25/28] little tweaks --- packages/cli/src/compile/compile.ts | 4 +- packages/cli/src/util/load-plan.ts | 17 +- packages/compiler/src/getExports.ts | 6 +- pnpm-lock.yaml | 409 +++++++++++++++++++++++++++- 4 files changed, 415 insertions(+), 21 deletions(-) diff --git a/packages/cli/src/compile/compile.ts b/packages/cli/src/compile/compile.ts index b6719a662..632416ef5 100644 --- a/packages/cli/src/compile/compile.ts +++ b/packages/cli/src/compile/compile.ts @@ -76,9 +76,7 @@ const compileWorkflow = async ( opts: CompileOptions, log: Logger ) => { - let globalsIgnoreList: string[] = []; - if (plan.workflow.globals) - globalsIgnoreList = getExports(plan.workflow.globals); + let globalsIgnoreList: string[] = getExports(plan.workflow.globals); for (const step of plan.workflow.steps) { const job = step as Job; diff --git a/packages/cli/src/util/load-plan.ts b/packages/cli/src/util/load-plan.ts index 60991558c..fa7fe7811 100644 --- a/packages/cli/src/util/load-plan.ts +++ b/packages/cli/src/util/load-plan.ts @@ -252,13 +252,16 @@ const importGlobals = async ( log: Logger ) => { const fnStr = plan.workflow?.globals; - if (!fnStr) return; - if (isPath(fnStr)) - plan.workflow.globals = await fetchFile( - { name: 'globals', rootDir, filePath: fnStr }, - log - ); - else plan.workflow.globals = fnStr; + 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 diff --git a/packages/compiler/src/getExports.ts b/packages/compiler/src/getExports.ts index 34c1cd985..8084fab1e 100644 --- a/packages/compiler/src/getExports.ts +++ b/packages/compiler/src/getExports.ts @@ -2,7 +2,11 @@ import recast from 'recast'; import * as acorn from 'acorn'; import { namedTypes as n } from 'ast-types'; -function getExports(content: string) { +function getExports(content?: string) { + if (!content) { + return []; + } + const ast = recast.parse(content, { parser: { parse: (source: string) => 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'} From 871fdd14394fb4b6ce070ac6f274885b1b7f514e Mon Sep 17 00:00:00 2001 From: Joe Clark Date: Sun, 15 Jun 2025 15:12:13 +0100 Subject: [PATCH 26/28] remove comment --- packages/compiler/test/getExports.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/compiler/test/getExports.test.ts b/packages/compiler/test/getExports.test.ts index 3d69c9f14..1bdb35c75 100644 --- a/packages/compiler/test/getExports.test.ts +++ b/packages/compiler/test/getExports.test.ts @@ -61,7 +61,6 @@ test('re-export from module', (t) => { t.deepEqual(res, ['lightning', 'compiler']); }); -// TODO: do we want to pick up default exports? test('export default should be ignored', (t) => { const source = ` export default function main() {} From 9d4ece32a6514615ca948d7bbcde737cf7dc9494 Mon Sep 17 00:00:00 2001 From: Joe Clark Date: Sun, 15 Jun 2025 15:16:13 +0100 Subject: [PATCH 27/28] changesets --- .changeset/forty-shirts-fry.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .changeset/forty-shirts-fry.md diff --git a/.changeset/forty-shirts-fry.md b/.changeset/forty-shirts-fry.md new file mode 100644 index 000000000..556747ce5 --- /dev/null +++ b/.changeset/forty-shirts-fry.md @@ -0,0 +1,8 @@ +--- +'@openfn/ws-worker': minor +'@openfn/compiler': minor +'@openfn/runtime': minor +'@openfn/cli': minor +--- + +Add support for global functions in execution plan From c628121ee3910d98f794c7da1d08038ae9aca093 Mon Sep 17 00:00:00 2001 From: Joe Clark Date: Sun, 15 Jun 2025 15:23:59 +0100 Subject: [PATCH 28/28] versions --- .changeset/forty-shirts-fry.md | 8 -------- integration-tests/cli/CHANGELOG.md | 6 ++++++ integration-tests/cli/package.json | 2 +- integration-tests/execute/CHANGELOG.md | 8 ++++++++ integration-tests/execute/package.json | 2 +- integration-tests/worker/CHANGELOG.md | 9 +++++++++ integration-tests/worker/package.json | 2 +- packages/cli/CHANGELOG.md | 12 ++++++++++++ packages/cli/package.json | 2 +- packages/compiler/CHANGELOG.md | 6 ++++++ packages/compiler/package.json | 2 +- packages/engine-multi/CHANGELOG.md | 8 ++++++++ packages/engine-multi/package.json | 2 +- packages/lightning-mock/CHANGELOG.md | 8 ++++++++ packages/lightning-mock/package.json | 2 +- packages/runtime/CHANGELOG.md | 6 ++++++ packages/runtime/package.json | 2 +- packages/ws-worker/CHANGELOG.md | 12 ++++++++++++ packages/ws-worker/package.json | 2 +- 19 files changed, 84 insertions(+), 17 deletions(-) delete mode 100644 .changeset/forty-shirts-fry.md diff --git a/.changeset/forty-shirts-fry.md b/.changeset/forty-shirts-fry.md deleted file mode 100644 index 556747ce5..000000000 --- a/.changeset/forty-shirts-fry.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@openfn/ws-worker': minor -'@openfn/compiler': minor -'@openfn/runtime': minor -'@openfn/cli': minor ---- - -Add support for global functions in execution plan 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/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/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/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/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/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/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",