From 1c13ce8ebbad26004f875b3baed10fa0132c69e5 Mon Sep 17 00:00:00 2001 From: Darlan Alves <745095+darlanalves@users.noreply.github.com> Date: Thu, 2 Apr 2026 10:15:04 +0530 Subject: [PATCH 1/4] feat(on): add generic workflow runner interface --- packages/on/src/types.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/on/src/types.ts b/packages/on/src/types.ts index 94dce56..ced5592 100644 --- a/packages/on/src/types.ts +++ b/packages/on/src/types.ts @@ -22,6 +22,14 @@ export interface StepOutput { stderr: string; } +export interface WorkflowRunner< + TStep extends StepDefinition = StepDefinition, + TContext extends WorkflowContext = WorkflowContext, + TOutput extends StepOutput = StepOutput, +> { + run(step: TStep, context: TContext): Promise; +} + export interface EventOutput { id: string; parentId?: string; From 5b8b30bb276eef277ac97f44d3e4850ac4a95f1e Mon Sep 17 00:00:00 2001 From: Darlan Alves <745095+darlanalves@users.noreply.github.com> Date: Thu, 2 Apr 2026 10:32:19 +0530 Subject: [PATCH 2/4] feat(runners): split shell and docker runners into packages --- packages/on-runner-docker/package.json | 20 ++++ packages/on-runner-docker/project.json | 12 ++ packages/on-runner-docker/src/index.ts | 116 ++++++++++++++++++++ packages/on-runner-docker/tsconfig.json | 5 + packages/on-runner-docker/tsconfig.lib.json | 8 ++ packages/on-runner-docker/vite.config.ts | 3 + packages/on-runner-docker/vitest.config.ts | 3 + packages/on-runner-shell/package.json | 20 ++++ packages/on-runner-shell/project.json | 12 ++ packages/on-runner-shell/src/index.ts | 56 ++++++++++ packages/on-runner-shell/tsconfig.json | 5 + packages/on-runner-shell/tsconfig.lib.json | 8 ++ packages/on-runner-shell/vite.config.ts | 3 + packages/on-runner-shell/vitest.config.ts | 3 + packages/on/package.json | 6 + packages/on/src/docker.ts | 76 ++----------- packages/on/src/workflow.ts | 49 ++------- pnpm-lock.yaml | 23 ++-- tsconfig.base.json | 9 +- 19 files changed, 324 insertions(+), 113 deletions(-) create mode 100644 packages/on-runner-docker/package.json create mode 100644 packages/on-runner-docker/project.json create mode 100644 packages/on-runner-docker/src/index.ts create mode 100644 packages/on-runner-docker/tsconfig.json create mode 100644 packages/on-runner-docker/tsconfig.lib.json create mode 100644 packages/on-runner-docker/vite.config.ts create mode 100644 packages/on-runner-docker/vitest.config.ts create mode 100644 packages/on-runner-shell/package.json create mode 100644 packages/on-runner-shell/project.json create mode 100644 packages/on-runner-shell/src/index.ts create mode 100644 packages/on-runner-shell/tsconfig.json create mode 100644 packages/on-runner-shell/tsconfig.lib.json create mode 100644 packages/on-runner-shell/vite.config.ts create mode 100644 packages/on-runner-shell/vitest.config.ts diff --git a/packages/on-runner-docker/package.json b/packages/on-runner-docker/package.json new file mode 100644 index 0000000..5fe5dd6 --- /dev/null +++ b/packages/on-runner-docker/package.json @@ -0,0 +1,20 @@ +{ + "name": "@cloud-cli/on-runner-docker", + "version": "0.1.0", + "description": "Docker runner for @cloud-cli/on workflows.", + "type": "module", + "main": "./dist/on-runner-docker.js", + "exports": { + ".": "./dist/on-runner-docker.js" + }, + "files": [ + "dist" + ], + "engines": { + "node": ">=20" + }, + "license": "MIT", + "repository": { + "url": "https://github.com/cloud-cli/on" + } +} diff --git a/packages/on-runner-docker/project.json b/packages/on-runner-docker/project.json new file mode 100644 index 0000000..e9fa2b7 --- /dev/null +++ b/packages/on-runner-docker/project.json @@ -0,0 +1,12 @@ +{ + "name": "on-runner-docker", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "packages/on-runner-docker/src", + "projectType": "library", + "targets": { + "build": {}, + "test": {}, + "lint": {} + }, + "tags": [] +} diff --git a/packages/on-runner-docker/src/index.ts b/packages/on-runner-docker/src/index.ts new file mode 100644 index 0000000..e1e40c5 --- /dev/null +++ b/packages/on-runner-docker/src/index.ts @@ -0,0 +1,116 @@ +import { spawn } from "node:child_process"; +import type { + NormalizedStepDefinition, + StepOutput, + WorkflowContext, + WorkflowRunner, +} from "@cloud-cli/on/types"; + +export const defaultWorkspace = "/workspace"; +export const defaultImage = "dhi.io/alpine-base:3.23-alpine3.23-dev"; + +function interpolate(template: string, context: WorkflowContext): string { + const keys = Object.keys(context); + const f = Function( + "context", + "const { " + keys.join(", ") + " } = context;return `" + template + "`;", + ); + + return String(f(context) || ""); +} + +export function prepareDockerStep( + step: NormalizedStepDefinition, + context: WorkflowContext, +) { + const defaults = context.workflow.defaults || {}; + + step.image ||= defaults.image || defaultImage; + step.volumes ||= defaults.volumes || {}; + step.args ||= defaults.args || []; + + return step; +} + +export function prepareDockerArgs( + args: Record[], + context: WorkflowContext, +): string[] { + return (args || []).flatMap((arg) => + Object.entries(arg).flatMap(([key, value]) => [ + `--${key}`, + interpolate(String(value), context), + ]), + ); +} + +export function prepareDockerVolumes( + volumes: Record, + context: WorkflowContext, +): string[] { + volumes["."] ||= defaultWorkspace; + return Object.entries(volumes).flatMap(([hostPath, containerPath]) => [ + "-v", + `${hostPath === "." ? context.workingDir : hostPath}:${containerPath}`, + ]); +} + +function prepareDockerShell( + step: NormalizedStepDefinition, + context: WorkflowContext, +) { + const prepared = prepareDockerStep(step, context); + const { args, image, volumes } = prepared; + const mappedVolumes = prepareDockerVolumes(volumes, context); + const mappedArgs = prepareDockerArgs(args, context); + const workingDir = volumes["."]; + const dockerArgs = [ + "run", + "-i", + "--rm", + ...mappedVolumes, + ...mappedArgs, + "-w", + workingDir, + "--entrypoint", + "sh", + image, + ] as string[]; + + return spawn("docker", dockerArgs, { + env: context.env, + }); +} + +export const dockerRunner: WorkflowRunner< + NormalizedStepDefinition, + WorkflowContext, + StepOutput +> = { + run(step, context) { + const stdout: Buffer[] = []; + const stderr: Buffer[] = []; + const cmd = interpolate(step.run, context); + + const shell = prepareDockerShell(step, context); + + shell.stdout?.on("data", (data) => stdout.push(data)); + shell.stderr?.on("data", (data) => stderr.push(data)); + + return new Promise((resolve, reject) => { + shell.once("error", reject); + shell.once("exit", (code) => { + resolve({ + code: code ?? 0, + cmd, + stdout: Buffer.concat(stdout).toString("utf-8"), + stderr: Buffer.concat(stderr).toString("utf-8"), + }); + }); + + shell.stdin?.write(cmd); + shell.stdin?.write("\nexit $?;\n"); + shell.stdin?.end(); + }); + }, +}; diff --git a/packages/on-runner-docker/tsconfig.json b/packages/on-runner-docker/tsconfig.json new file mode 100644 index 0000000..d6f9607 --- /dev/null +++ b/packages/on-runner-docker/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["src/**/*.ts"], + "exclude": ["dist/**/*", "node_modules", "src/**/*.spec.ts"] +} diff --git a/packages/on-runner-docker/tsconfig.lib.json b/packages/on-runner-docker/tsconfig.lib.json new file mode 100644 index 0000000..975d824 --- /dev/null +++ b/packages/on-runner-docker/tsconfig.lib.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./dist/" + }, + "include": ["src/**/*.ts"], + "exclude": ["src/**/*.spec.ts"] +} diff --git a/packages/on-runner-docker/vite.config.ts b/packages/on-runner-docker/vite.config.ts new file mode 100644 index 0000000..8e26c9f --- /dev/null +++ b/packages/on-runner-docker/vite.config.ts @@ -0,0 +1,3 @@ +import { asLib } from "../../vite.config.base.js"; + +export default asLib(import.meta.dirname); diff --git a/packages/on-runner-docker/vitest.config.ts b/packages/on-runner-docker/vitest.config.ts new file mode 100644 index 0000000..1ec1094 --- /dev/null +++ b/packages/on-runner-docker/vitest.config.ts @@ -0,0 +1,3 @@ +import config from "../../vitest.config.base.js"; + +export default config(import.meta.dirname); diff --git a/packages/on-runner-shell/package.json b/packages/on-runner-shell/package.json new file mode 100644 index 0000000..aba4331 --- /dev/null +++ b/packages/on-runner-shell/package.json @@ -0,0 +1,20 @@ +{ + "name": "@cloud-cli/on-runner-shell", + "version": "0.1.0", + "description": "Shell runner for @cloud-cli/on workflows.", + "type": "module", + "main": "./dist/on-runner-shell.js", + "exports": { + ".": "./dist/on-runner-shell.js" + }, + "files": [ + "dist" + ], + "engines": { + "node": ">=20" + }, + "license": "MIT", + "repository": { + "url": "https://github.com/cloud-cli/on" + } +} diff --git a/packages/on-runner-shell/project.json b/packages/on-runner-shell/project.json new file mode 100644 index 0000000..397aa92 --- /dev/null +++ b/packages/on-runner-shell/project.json @@ -0,0 +1,12 @@ +{ + "name": "on-runner-shell", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "packages/on-runner-shell/src", + "projectType": "library", + "targets": { + "build": {}, + "test": {}, + "lint": {} + }, + "tags": [] +} diff --git a/packages/on-runner-shell/src/index.ts b/packages/on-runner-shell/src/index.ts new file mode 100644 index 0000000..f0c6947 --- /dev/null +++ b/packages/on-runner-shell/src/index.ts @@ -0,0 +1,56 @@ +import { spawn } from "node:child_process"; +import type { + NormalizedStepDefinition, + StepOutput, + WorkflowContext, + WorkflowRunner, +} from "@cloud-cli/on/types"; + +const SHELL = process.env.SHELL || "sh"; + +function interpolate(template: string, context: WorkflowContext): string { + const keys = Object.keys(context); + const f = Function( + "context", + "const { " + keys.join(", ") + " } = context;return `" + template + "`;", + ); + + return String(f(context) || ""); +} + +export const shellRunner: WorkflowRunner< + NormalizedStepDefinition, + WorkflowContext, + StepOutput +> = { + run(step, context) { + const stdout: Buffer[] = []; + const stderr: Buffer[] = []; + const cmd = interpolate(step.run, context); + + const shell = spawn(SHELL, { + shell: true, + env: context.env, + cwd: step.workingDir || context.workingDir, + }); + + shell.stdout?.on("data", (data) => stdout.push(data)); + shell.stderr?.on("data", (data) => stderr.push(data)); + + return new Promise((resolve, reject) => { + shell.once("error", reject); + shell.once("exit", (code) => { + resolve({ + code: code ?? 0, + cmd, + stdout: Buffer.concat(stdout).toString("utf-8"), + stderr: Buffer.concat(stderr).toString("utf-8"), + }); + }); + + shell.stdin?.write(cmd); + shell.stdin?.write("\nexit $?;\n"); + shell.stdin?.end(); + }); + }, +}; diff --git a/packages/on-runner-shell/tsconfig.json b/packages/on-runner-shell/tsconfig.json new file mode 100644 index 0000000..d6f9607 --- /dev/null +++ b/packages/on-runner-shell/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["src/**/*.ts"], + "exclude": ["dist/**/*", "node_modules", "src/**/*.spec.ts"] +} diff --git a/packages/on-runner-shell/tsconfig.lib.json b/packages/on-runner-shell/tsconfig.lib.json new file mode 100644 index 0000000..975d824 --- /dev/null +++ b/packages/on-runner-shell/tsconfig.lib.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./dist/" + }, + "include": ["src/**/*.ts"], + "exclude": ["src/**/*.spec.ts"] +} diff --git a/packages/on-runner-shell/vite.config.ts b/packages/on-runner-shell/vite.config.ts new file mode 100644 index 0000000..8e26c9f --- /dev/null +++ b/packages/on-runner-shell/vite.config.ts @@ -0,0 +1,3 @@ +import { asLib } from "../../vite.config.base.js"; + +export default asLib(import.meta.dirname); diff --git a/packages/on-runner-shell/vitest.config.ts b/packages/on-runner-shell/vitest.config.ts new file mode 100644 index 0000000..1ec1094 --- /dev/null +++ b/packages/on-runner-shell/vitest.config.ts @@ -0,0 +1,3 @@ +import config from "../../vitest.config.base.js"; + +export default config(import.meta.dirname); diff --git a/packages/on/package.json b/packages/on/package.json index 9b56afe..84ce876 100644 --- a/packages/on/package.json +++ b/packages/on/package.json @@ -7,6 +7,10 @@ "on": "./dist/on.js" }, "main": "./dist/on.js", + "exports": { + ".": "./dist/on.js", + "./types": "./dist/types.js" + }, "files": [ "dist" ], @@ -22,6 +26,8 @@ "url": "https://github.com/cloud-cli/on" }, "dependencies": { + "@cloud-cli/on-runner-docker": "workspace:*", + "@cloud-cli/on-runner-shell": "workspace:*", "ansi_up": "6.0.6", "yaml": "^2.8.2" } diff --git a/packages/on/src/docker.ts b/packages/on/src/docker.ts index 828123e..6f8d8ac 100644 --- a/packages/on/src/docker.ts +++ b/packages/on/src/docker.ts @@ -1,68 +1,8 @@ -import { spawn } from "node:child_process"; -import type { WorkflowContext, NormalizedStepDefinition } from "./types.js"; -import { interpolate } from "./utils.js"; - -export const defaultWorkspace = "/workspace"; -export const defaultImage = "dhi.io/alpine-base:3.23-alpine3.23-dev"; - -export function prepareDockerStep( - step: NormalizedStepDefinition, - context: WorkflowContext, -) { - const defaults = context.workflow.defaults || {}; - - step.image ||= defaults.image || defaultImage; - step.volumes ||= defaults.volumes || {}; - step.args ||= defaults.args || []; - - return step; -} - -export function prepareDockerArgs( - args: Record[], - context: WorkflowContext, -): string[] { - return (args || []).flatMap((arg) => - Object.entries(arg).flatMap(([key, value]) => [ - `--${key}`, - interpolate(String(value), context), - ]), - ); -} -export function prepareDockerVolumes( - volumes: Record, - context: WorkflowContext, -): string[] { - volumes["."] ||= defaultWorkspace; - return Object.entries(volumes).flatMap(([hostPath, containerPath]) => [ - "-v", - `${hostPath === "." ? context.workingDir : hostPath}:${containerPath}`, - ]); -} - -export function prepareShell( - step: NormalizedStepDefinition, - context: WorkflowContext, -) { - const prepared = prepareDockerStep(step, context); - const { args, image, volumes } = prepared; - const mappedVolumes = prepareDockerVolumes(volumes, context); - const mappedArgs = prepareDockerArgs(args, context); - const workingDir = volumes["."]; - const dockerArgs = [ - "run", - "-i", - "--rm", - ...mappedVolumes, - ...mappedArgs, - "-w", - workingDir, - "--entrypoint", - "sh", - image, - ] as string[]; - - return spawn("docker", dockerArgs, { - env: context.env, - }); -} +export { + defaultImage, + defaultWorkspace, + dockerRunner, + prepareDockerArgs, + prepareDockerStep, + prepareDockerVolumes, +} from "@cloud-cli/on-runner-docker"; diff --git a/packages/on/src/workflow.ts b/packages/on/src/workflow.ts index ed62c90..6681489 100644 --- a/packages/on/src/workflow.ts +++ b/packages/on/src/workflow.ts @@ -1,8 +1,9 @@ -import { spawn } from "node:child_process"; import { existsSync } from "node:fs"; import { mkdtemp, rm, readFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path/posix"; +import { dockerRunner } from "@cloud-cli/on-runner-docker"; +import { shellRunner } from "@cloud-cli/on-runner-shell"; import { loadSecrets } from "./secrets.js"; import type { StepDefinition, @@ -13,13 +14,18 @@ import type { StepOutput, EventOutput, NormalizedStepDefinition, + WorkflowRunner, } from "./types.js"; import { interpolate, withMappings, asObject, toStringProxy } from "./utils.js"; import { randomUUID } from "node:crypto"; import { createReport } from "./reports.js"; -import { prepareShell } from "./docker.js"; - -const SHELL = process.env.SHELL || "sh"; +const runners: Record< + WorkflowContext["runner"], + WorkflowRunner +> = { + docker: dockerRunner, + shell: shellRunner, +}; function prepareEnv(context: WorkflowContext) { const { workflow, secrets } = context; @@ -78,39 +84,8 @@ async function runStep( step: NormalizedStepDefinition, context: WorkflowContext, ): Promise { - const stdout: Buffer[] = []; - const stderr: Buffer[] = []; - const cmd = interpolate(step.run, context); - - const shell = - context.runner === "docker" - ? prepareShell(step, context) - : spawn(SHELL, { - shell: true, - env: context.env, - cwd: step.workingDir || context.workingDir, - }); - - shell.stdout?.on("data", (data) => stdout.push(data)); - shell.stderr?.on("data", (data) => stderr.push(data)); - - return new Promise((resolve, reject) => { - shell.once("error", reject); - shell.once("exit", (code) => { - const stepOutput = { - code: code ?? 0, - cmd: step.run, - stdout: Buffer.concat(stdout).toString("utf-8"), - stderr: Buffer.concat(stderr).toString("utf-8"), - }; - - resolve(stepOutput); - }); - - shell.stdin?.write(cmd); - shell.stdin?.write("\nexit $?;\n"); - shell.stdin?.end(); - }); + const runner = runners[context.runner]; + return runner.run(step, context); } function normalizeSteps( diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 24e2ea4..6bb7a22 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -59,6 +59,12 @@ importers: packages/on: dependencies: + '@cloud-cli/on-runner-docker': + specifier: workspace:* + version: link:../on-runner-docker + '@cloud-cli/on-runner-shell': + specifier: workspace:* + version: link:../on-runner-shell ansi_up: specifier: 6.0.6 version: 6.0.6 @@ -66,14 +72,17 @@ importers: specifier: ^2.8.2 version: 2.8.2 - packages/on/dist: + packages/on-runner-docker: dependencies: - ansi_up: - specifier: 6.0.6 - version: 6.0.6 - yaml: - specifier: ^2.8.2 - version: 2.8.2 + '@cloud-cli/on': + specifier: workspace:* + version: link:../on + + packages/on-runner-shell: + dependencies: + '@cloud-cli/on': + specifier: workspace:* + version: link:../on packages: diff --git a/tsconfig.base.json b/tsconfig.base.json index 8f54fdb..0d54b58 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -3,6 +3,7 @@ "target": "ES2022", "module": "NodeNext", "moduleResolution": "NodeNext", + "baseUrl": ".", "strict": true, "declaration": true, "declarationMap": true, @@ -12,6 +13,12 @@ "skipLibCheck": true, "types": [ "node" - ] + ], + "paths": { + "@cloud-cli/on": ["./packages/on/src/index.ts"], + "@cloud-cli/on/types": ["./packages/on/src/types.ts"], + "@cloud-cli/on-runner-docker": ["./packages/on-runner-docker/src/index.ts"], + "@cloud-cli/on-runner-shell": ["./packages/on-runner-shell/src/index.ts"] + } } } From 3fa6a003869e719f17f4d27c4fdc7fff74c5b9e0 Mon Sep 17 00:00:00 2001 From: Darlan Alves <745095+darlanalves@users.noreply.github.com> Date: Thu, 2 Apr 2026 05:08:26 +0000 Subject: [PATCH 3/4] chore: update lockfile --- package.json | 2 +- pnpm-lock.yaml | 375 +++++++++++++++++++------------------------------ 2 files changed, 145 insertions(+), 232 deletions(-) diff --git a/package.json b/package.json index 258e00d..4fe97ec 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "@types/node": "^22.13.10", "@typescript-eslint/eslint-plugin": "8.57.1", "@typescript-eslint/parser": "8.57.1", - "eslint": "~8.57.0", + "eslint": "^10.1.0", "nx": "^22.5.4", "tsx": "^4.19.3", "typescript": "^5.8.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6bb7a22..b678bdb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13,10 +13,10 @@ importers: version: 2.30.0(@types/node@22.19.13) '@eslint/js': specifier: 10.0.1 - version: 10.0.1(eslint@8.57.1) + version: 10.0.1(eslint@10.1.0) '@nx/eslint': specifier: 22.5.4 - version: 22.5.4(@babel/traverse@7.29.0)(@zkochan/js-yaml@0.0.7)(eslint@8.57.1)(nx@22.5.4) + version: 22.5.4(@babel/traverse@7.29.0)(@zkochan/js-yaml@0.0.7)(eslint@10.1.0)(nx@22.5.4) '@nx/js': specifier: ^22.5.4 version: 22.5.4(@babel/traverse@7.29.0)(nx@22.5.4) @@ -31,13 +31,13 @@ importers: version: 22.19.13 '@typescript-eslint/eslint-plugin': specifier: 8.57.1 - version: 8.57.1(@typescript-eslint/parser@8.57.1(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3) + version: 8.57.1(@typescript-eslint/parser@8.57.1(eslint@10.1.0)(typescript@5.9.3))(eslint@10.1.0)(typescript@5.9.3) '@typescript-eslint/parser': specifier: 8.57.1 - version: 8.57.1(eslint@8.57.1)(typescript@5.9.3) + version: 8.57.1(eslint@10.1.0)(typescript@5.9.3) eslint: - specifier: ~8.57.0 - version: 8.57.1 + specifier: ^10.1.0 + version: 10.1.0 nx: specifier: ^22.5.4 version: 22.5.4 @@ -49,7 +49,7 @@ importers: version: 5.9.3 typescript-eslint: specifier: 8.57.1 - version: 8.57.1(eslint@8.57.1)(typescript@5.9.3) + version: 8.57.1(eslint@10.1.0)(typescript@5.9.3) vite: specifier: ^7.3.1 version: 7.3.1(@types/node@22.19.13)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2) @@ -72,17 +72,18 @@ importers: specifier: ^2.8.2 version: 2.8.2 - packages/on-runner-docker: - dependencies: - '@cloud-cli/on': - specifier: workspace:* - version: link:../on + packages/on-runner-docker: {} - packages/on-runner-shell: + packages/on-runner-shell: {} + + packages/on/dist: dependencies: - '@cloud-cli/on': - specifier: workspace:* - version: link:../on + ansi_up: + specifier: 6.0.6 + version: 6.0.6 + yaml: + specifier: ^2.8.2 + version: 2.8.2 packages: @@ -853,9 +854,17 @@ packages: resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint/eslintrc@2.1.4': - resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@eslint/config-array@0.23.3': + resolution: {integrity: sha512-j+eEWmB6YYLwcNOdlwQ6L2OsptI/LO6lNBuLIqe5R7RetD658HLoF+Mn7LzYmAWWNNzdC6cqP+L6r8ujeYXWLw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@eslint/config-helpers@0.5.3': + resolution: {integrity: sha512-lzGN0onllOZCGroKJmRwY6QcEHxbjBw1gwB8SgRSqK8YbbtEXMvKynsXc3553ckIEBxsbMBU7oOZXKIPGZNeZw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@eslint/core@1.1.1': + resolution: {integrity: sha512-QUPblTtE51/7/Zhfv8BDwO0qkkzQL7P/aWWbqcf4xWLEYn1oKjdO0gglQBB4GAsu7u6wjijbCmzsUTy6mnk6oQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} '@eslint/js@10.0.1': resolution: {integrity: sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==} @@ -866,22 +875,29 @@ packages: eslint: optional: true - '@eslint/js@8.57.1': - resolution: {integrity: sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@eslint/object-schema@3.0.3': + resolution: {integrity: sha512-iM869Pugn9Nsxbh/YHRqYiqd23AmIbxJOcpUMOuWCVNdoQJ5ZtwL6h3t0bcZzJUlC3Dq9jCFCESBZnX0GTv7iQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@eslint/plugin-kit@0.6.1': + resolution: {integrity: sha512-iH1B076HoAshH1mLpHMgwdGeTs0CYwL0SPMkGuSebZrwBp16v415e9NZXg2jtrqPVQjf6IANe2Vtlr5KswtcZQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@humanfs/core@0.19.1': + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + engines: {node: '>=18.18.0'} - '@humanwhocodes/config-array@0.13.0': - resolution: {integrity: sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==} - engines: {node: '>=10.10.0'} - deprecated: Use @eslint/config-array instead + '@humanfs/node@0.16.7': + resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} + engines: {node: '>=18.18.0'} '@humanwhocodes/module-importer@1.0.1': resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} engines: {node: '>=12.22'} - '@humanwhocodes/object-schema@2.0.3': - resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} - deprecated: Use @eslint/object-schema instead + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} '@inquirer/external-editor@1.0.3': resolution: {integrity: sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==} @@ -1198,9 +1214,15 @@ packages: '@types/esquery@1.5.4': resolution: {integrity: sha512-yYO4Q8H+KJHKW1rEeSzHxcZi90durqYgWVfnh5K6ZADVBjBv2e1NEveYX5yT2bffgN7RqzH3k9930m+i2yBoMA==} + '@types/esrecurse@4.3.1': + resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==} + '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + '@types/node@12.20.55': resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} @@ -1269,9 +1291,6 @@ packages: resolution: {integrity: sha512-YWnmJkXbofiz9KbnbbwuA2rpGkFPLbAIetcCNO6mJ8gdhdZ/v7WDXsoGFAJuM6ikUFKTlSQnjWnVO4ux+UzS6A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@ungap/structured-clone@1.3.0': - resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} - '@vitest/expect@4.1.0': resolution: {integrity: sha512-EIxG7k4wlWweuCLG9Y5InKFwpMEOyrMb6ZJ1ihYu02LVj/bzUwn2VMU+13PinsjRW75XnITeFrQBMH5+dLvCDA==} @@ -1434,9 +1453,6 @@ packages: bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} - brace-expansion@1.1.12: - resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} - brace-expansion@2.0.2: resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} @@ -1516,9 +1532,6 @@ packages: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} - concat-map@0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} @@ -1573,10 +1586,6 @@ packages: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} - doctrine@3.0.0: - resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} - engines: {node: '>=6.0.0'} - dotenv-expand@11.0.7: resolution: {integrity: sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==} engines: {node: '>=12'} @@ -1650,9 +1659,9 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} - eslint-scope@7.2.2: - resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + eslint-scope@9.1.2: + resolution: {integrity: sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} eslint-visitor-keys@3.4.3: resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} @@ -1662,15 +1671,19 @@ packages: resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} - eslint@8.57.1: - resolution: {integrity: sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. + eslint@10.1.0: + resolution: {integrity: sha512-S9jlY/ELKEUwwQnqWDO+f+m6sercqOPSqXM5Go94l7DOmxHVDgmSFGWEzeE/gwgTAr0W103BWt0QLe/7mabIvA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true - espree@9.6.1: - resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + espree@11.2.0: + resolution: {integrity: sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} esprima@4.0.1: resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} @@ -1735,9 +1748,9 @@ packages: resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} engines: {node: '>=8'} - file-entry-cache@6.0.1: - resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} - engines: {node: ^10.12.0 || >=12.0.0} + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} filelist@1.0.6: resolution: {integrity: sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==} @@ -1754,9 +1767,9 @@ packages: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} - flat-cache@3.2.0: - resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} - engines: {node: ^10.12.0 || >=12.0.0} + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} flat@5.0.2: resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} @@ -1792,9 +1805,6 @@ packages: resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} engines: {node: '>=6 <7 || >=8'} - fs.realpath@1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -1830,14 +1840,6 @@ packages: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} - glob@7.2.3: - resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me - - globals@13.24.0: - resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} - engines: {node: '>=8'} - globby@11.1.0: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} engines: {node: '>=10'} @@ -1849,9 +1851,6 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - graphemer@1.4.0: - resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} @@ -1895,10 +1894,6 @@ packages: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} - inflight@1.0.6: - resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} - deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. - inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} @@ -1934,10 +1929,6 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} - is-path-inside@3.0.3: - resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} - engines: {node: '>=8'} - is-subdir@1.2.0: resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} engines: {node: '>=4'} @@ -2107,9 +2098,6 @@ packages: lodash.debounce@4.0.8: resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} - lodash.merge@4.6.2: - resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - lodash.startcase@4.4.0: resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} @@ -2151,9 +2139,6 @@ packages: resolution: {integrity: sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==} engines: {node: 18 || 20 || >=22} - minimatch@3.1.5: - resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} - minimatch@5.1.9: resolution: {integrity: sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==} engines: {node: '>=10'} @@ -2266,10 +2251,6 @@ packages: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} - path-is-absolute@1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} - path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -2398,11 +2379,6 @@ packages: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rimraf@3.0.2: - resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} - deprecated: Rimraf versions prior to v4 are no longer supported - hasBin: true - rollup@4.59.0: resolution: {integrity: sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -2486,10 +2462,6 @@ packages: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} - strip-json-comments@3.1.1: - resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} - engines: {node: '>=8'} - supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -2506,9 +2478,6 @@ packages: resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} engines: {node: '>=8'} - text-table@0.2.0: - resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} - tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} @@ -2558,10 +2527,6 @@ packages: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} - type-fest@0.20.2: - resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} - engines: {node: '>=10'} - typescript-eslint@8.57.1: resolution: {integrity: sha512-fLvZWf+cAGw3tqMCYzGIU6yR8K+Y9NT2z23RwOjlNFF2HwSB3KhdEFI5lSBv8tNmFkkBShSjsCjzx1vahZfISA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -3684,44 +3649,50 @@ snapshots: '@esbuild/win32-x64@0.27.3': optional: true - '@eslint-community/eslint-utils@4.9.1(eslint@8.57.1)': + '@eslint-community/eslint-utils@4.9.1(eslint@10.1.0)': dependencies: - eslint: 8.57.1 + eslint: 10.1.0 eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.2': {} - '@eslint/eslintrc@2.1.4': + '@eslint/config-array@0.23.3': dependencies: - ajv: 6.14.0 + '@eslint/object-schema': 3.0.3 debug: 4.4.3 - espree: 9.6.1 - globals: 13.24.0 - ignore: 5.3.2 - import-fresh: 3.3.1 - js-yaml: 4.1.1 - minimatch: 3.1.5 - strip-json-comments: 3.1.1 + minimatch: 10.2.4 transitivePeerDependencies: - supports-color - '@eslint/js@10.0.1(eslint@8.57.1)': + '@eslint/config-helpers@0.5.3': + dependencies: + '@eslint/core': 1.1.1 + + '@eslint/core@1.1.1': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/js@10.0.1(eslint@10.1.0)': optionalDependencies: - eslint: 8.57.1 + eslint: 10.1.0 - '@eslint/js@8.57.1': {} + '@eslint/object-schema@3.0.3': {} - '@humanwhocodes/config-array@0.13.0': + '@eslint/plugin-kit@0.6.1': dependencies: - '@humanwhocodes/object-schema': 2.0.3 - debug: 4.4.3 - minimatch: 3.1.5 - transitivePeerDependencies: - - supports-color + '@eslint/core': 1.1.1 + levn: 0.4.1 + + '@humanfs/core@0.19.1': {} + + '@humanfs/node@0.16.7': + dependencies: + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.4.3 '@humanwhocodes/module-importer@1.0.1': {} - '@humanwhocodes/object-schema@2.0.3': {} + '@humanwhocodes/retry@0.4.3': {} '@inquirer/external-editor@1.0.3(@types/node@22.19.13)': dependencies: @@ -3802,11 +3773,11 @@ snapshots: tslib: 2.8.1 yargs-parser: 21.1.1 - '@nx/eslint@22.5.4(@babel/traverse@7.29.0)(@zkochan/js-yaml@0.0.7)(eslint@8.57.1)(nx@22.5.4)': + '@nx/eslint@22.5.4(@babel/traverse@7.29.0)(@zkochan/js-yaml@0.0.7)(eslint@10.1.0)(nx@22.5.4)': dependencies: '@nx/devkit': 22.5.4(nx@22.5.4) '@nx/js': 22.5.4(@babel/traverse@7.29.0)(nx@22.5.4) - eslint: 8.57.1 + eslint: 10.1.0 semver: 7.7.4 tslib: 2.8.1 typescript: 5.9.3 @@ -4047,8 +4018,12 @@ snapshots: dependencies: '@types/estree': 1.0.8 + '@types/esrecurse@4.3.1': {} + '@types/estree@1.0.8': {} + '@types/json-schema@7.0.15': {} + '@types/node@12.20.55': {} '@types/node@22.19.13': @@ -4057,15 +4032,15 @@ snapshots: '@types/parse-json@4.0.2': {} - '@typescript-eslint/eslint-plugin@8.57.1(@typescript-eslint/parser@8.57.1(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3)': + '@typescript-eslint/eslint-plugin@8.57.1(@typescript-eslint/parser@8.57.1(eslint@10.1.0)(typescript@5.9.3))(eslint@10.1.0)(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.57.1(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/parser': 8.57.1(eslint@10.1.0)(typescript@5.9.3) '@typescript-eslint/scope-manager': 8.57.1 - '@typescript-eslint/type-utils': 8.57.1(eslint@8.57.1)(typescript@5.9.3) - '@typescript-eslint/utils': 8.57.1(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/type-utils': 8.57.1(eslint@10.1.0)(typescript@5.9.3) + '@typescript-eslint/utils': 8.57.1(eslint@10.1.0)(typescript@5.9.3) '@typescript-eslint/visitor-keys': 8.57.1 - eslint: 8.57.1 + eslint: 10.1.0 ignore: 7.0.5 natural-compare: 1.4.0 ts-api-utils: 2.4.0(typescript@5.9.3) @@ -4073,14 +4048,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.57.1(eslint@8.57.1)(typescript@5.9.3)': + '@typescript-eslint/parser@8.57.1(eslint@10.1.0)(typescript@5.9.3)': dependencies: '@typescript-eslint/scope-manager': 8.57.1 '@typescript-eslint/types': 8.57.1 '@typescript-eslint/typescript-estree': 8.57.1(typescript@5.9.3) '@typescript-eslint/visitor-keys': 8.57.1 debug: 4.4.3 - eslint: 8.57.1 + eslint: 10.1.0 typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -4103,13 +4078,13 @@ snapshots: dependencies: typescript: 5.9.3 - '@typescript-eslint/type-utils@8.57.1(eslint@8.57.1)(typescript@5.9.3)': + '@typescript-eslint/type-utils@8.57.1(eslint@10.1.0)(typescript@5.9.3)': dependencies: '@typescript-eslint/types': 8.57.1 '@typescript-eslint/typescript-estree': 8.57.1(typescript@5.9.3) - '@typescript-eslint/utils': 8.57.1(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/utils': 8.57.1(eslint@10.1.0)(typescript@5.9.3) debug: 4.4.3 - eslint: 8.57.1 + eslint: 10.1.0 ts-api-utils: 2.4.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: @@ -4132,13 +4107,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.57.1(eslint@8.57.1)(typescript@5.9.3)': + '@typescript-eslint/utils@8.57.1(eslint@10.1.0)(typescript@5.9.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@8.57.1) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.1.0) '@typescript-eslint/scope-manager': 8.57.1 '@typescript-eslint/types': 8.57.1 '@typescript-eslint/typescript-estree': 8.57.1(typescript@5.9.3) - eslint: 8.57.1 + eslint: 10.1.0 typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -4148,8 +4123,6 @@ snapshots: '@typescript-eslint/types': 8.57.1 eslint-visitor-keys: 5.0.1 - '@ungap/structured-clone@1.3.0': {} - '@vitest/expect@4.1.0': dependencies: '@standard-schema/spec': 1.1.0 @@ -4330,11 +4303,6 @@ snapshots: inherits: 2.0.4 readable-stream: 3.6.2 - brace-expansion@1.1.12: - dependencies: - balanced-match: 1.0.2 - concat-map: 0.0.1 - brace-expansion@2.0.2: dependencies: balanced-match: 1.0.2 @@ -4411,8 +4379,6 @@ snapshots: dependencies: delayed-stream: 1.0.0 - concat-map@0.0.1: {} - convert-source-map@2.0.0: {} core-js-compat@3.48.0: @@ -4463,10 +4429,6 @@ snapshots: dependencies: path-type: 4.0.0 - doctrine@3.0.0: - dependencies: - esutils: 2.0.3 - dotenv-expand@11.0.7: dependencies: dotenv: 16.4.7 @@ -4556,8 +4518,10 @@ snapshots: escape-string-regexp@4.0.0: {} - eslint-scope@7.2.2: + eslint-scope@9.1.2: dependencies: + '@types/esrecurse': 4.3.1 + '@types/estree': 1.0.8 esrecurse: 4.3.0 estraverse: 5.3.0 @@ -4565,54 +4529,46 @@ snapshots: eslint-visitor-keys@5.0.1: {} - eslint@8.57.1: + eslint@10.1.0: dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@8.57.1) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.1.0) '@eslint-community/regexpp': 4.12.2 - '@eslint/eslintrc': 2.1.4 - '@eslint/js': 8.57.1 - '@humanwhocodes/config-array': 0.13.0 + '@eslint/config-array': 0.23.3 + '@eslint/config-helpers': 0.5.3 + '@eslint/core': 1.1.1 + '@eslint/plugin-kit': 0.6.1 + '@humanfs/node': 0.16.7 '@humanwhocodes/module-importer': 1.0.1 - '@nodelib/fs.walk': 1.2.8 - '@ungap/structured-clone': 1.3.0 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 ajv: 6.14.0 - chalk: 4.1.2 cross-spawn: 7.0.6 debug: 4.4.3 - doctrine: 3.0.0 escape-string-regexp: 4.0.0 - eslint-scope: 7.2.2 - eslint-visitor-keys: 3.4.3 - espree: 9.6.1 + eslint-scope: 9.1.2 + eslint-visitor-keys: 5.0.1 + espree: 11.2.0 esquery: 1.7.0 esutils: 2.0.3 fast-deep-equal: 3.1.3 - file-entry-cache: 6.0.1 + file-entry-cache: 8.0.0 find-up: 5.0.0 glob-parent: 6.0.2 - globals: 13.24.0 - graphemer: 1.4.0 ignore: 5.3.2 imurmurhash: 0.1.4 is-glob: 4.0.3 - is-path-inside: 3.0.3 - js-yaml: 4.1.1 json-stable-stringify-without-jsonify: 1.0.1 - levn: 0.4.1 - lodash.merge: 4.6.2 - minimatch: 3.1.5 + minimatch: 10.2.4 natural-compare: 1.4.0 optionator: 0.9.4 - strip-ansi: 6.0.1 - text-table: 0.2.0 transitivePeerDependencies: - supports-color - espree@9.6.1: + espree@11.2.0: dependencies: acorn: 8.16.0 acorn-jsx: 5.3.2(acorn@8.16.0) - eslint-visitor-keys: 3.4.3 + eslint-visitor-keys: 5.0.1 esprima@4.0.1: {} @@ -4664,9 +4620,9 @@ snapshots: dependencies: escape-string-regexp: 1.0.5 - file-entry-cache@6.0.1: + file-entry-cache@8.0.0: dependencies: - flat-cache: 3.2.0 + flat-cache: 4.0.1 filelist@1.0.6: dependencies: @@ -4686,11 +4642,10 @@ snapshots: locate-path: 6.0.0 path-exists: 4.0.0 - flat-cache@3.2.0: + flat-cache@4.0.1: dependencies: flatted: 3.4.1 keyv: 4.5.4 - rimraf: 3.0.2 flat@5.0.2: {} @@ -4724,8 +4679,6 @@ snapshots: jsonfile: 4.0.0 universalify: 0.1.2 - fs.realpath@1.0.0: {} - fsevents@2.3.3: optional: true @@ -4765,19 +4718,6 @@ snapshots: dependencies: is-glob: 4.0.3 - glob@7.2.3: - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.5 - once: 1.4.0 - path-is-absolute: 1.0.1 - - globals@13.24.0: - dependencies: - type-fest: 0.20.2 - globby@11.1.0: dependencies: array-union: 2.1.0 @@ -4791,8 +4731,6 @@ snapshots: graceful-fs@4.2.11: {} - graphemer@1.4.0: {} - has-flag@4.0.0: {} has-symbols@1.1.0: {} @@ -4824,11 +4762,6 @@ snapshots: imurmurhash@0.1.4: {} - inflight@1.0.6: - dependencies: - once: 1.4.0 - wrappy: 1.0.2 - inherits@2.0.4: {} is-arrayish@0.2.1: {} @@ -4851,8 +4784,6 @@ snapshots: is-number@7.0.0: {} - is-path-inside@3.0.3: {} - is-subdir@1.2.0: dependencies: better-path-resolve: 1.0.0 @@ -4984,8 +4915,6 @@ snapshots: lodash.debounce@4.0.8: {} - lodash.merge@4.6.2: {} - lodash.startcase@4.4.0: {} log-symbols@4.1.0: @@ -5022,10 +4951,6 @@ snapshots: dependencies: brace-expansion: 5.0.4 - minimatch@3.1.5: - dependencies: - brace-expansion: 1.1.12 - minimatch@5.1.9: dependencies: brace-expansion: 2.0.2 @@ -5179,8 +5104,6 @@ snapshots: path-exists@4.0.0: {} - path-is-absolute@1.0.1: {} - path-key@3.1.1: {} path-parse@1.0.7: {} @@ -5284,10 +5207,6 @@ snapshots: reusify@1.1.0: {} - rimraf@3.0.2: - dependencies: - glob: 7.2.3 - rollup@4.59.0: dependencies: '@types/estree': 1.0.8 @@ -5381,8 +5300,6 @@ snapshots: strip-bom@3.0.0: {} - strip-json-comments@3.1.1: {} - supports-color@7.2.0: dependencies: has-flag: 4.0.0 @@ -5399,8 +5316,6 @@ snapshots: term-size@2.2.1: {} - text-table@0.2.0: {} - tinybench@2.9.0: {} tinyexec@1.0.2: {} @@ -5443,15 +5358,13 @@ snapshots: dependencies: prelude-ls: 1.2.1 - type-fest@0.20.2: {} - - typescript-eslint@8.57.1(eslint@8.57.1)(typescript@5.9.3): + typescript-eslint@8.57.1(eslint@10.1.0)(typescript@5.9.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.57.1(@typescript-eslint/parser@8.57.1(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3) - '@typescript-eslint/parser': 8.57.1(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/eslint-plugin': 8.57.1(@typescript-eslint/parser@8.57.1(eslint@10.1.0)(typescript@5.9.3))(eslint@10.1.0)(typescript@5.9.3) + '@typescript-eslint/parser': 8.57.1(eslint@10.1.0)(typescript@5.9.3) '@typescript-eslint/typescript-estree': 8.57.1(typescript@5.9.3) - '@typescript-eslint/utils': 8.57.1(eslint@8.57.1)(typescript@5.9.3) - eslint: 8.57.1 + '@typescript-eslint/utils': 8.57.1(eslint@10.1.0)(typescript@5.9.3) + eslint: 10.1.0 typescript: 5.9.3 transitivePeerDependencies: - supports-color From 31364c09dcb9479e8cc385241c6d01394aad2282 Mon Sep 17 00:00:00 2001 From: Darlan Alves <745095+darlanalves@users.noreply.github.com> Date: Thu, 2 Apr 2026 09:24:22 +0000 Subject: [PATCH 4/4] chore: split devkit and code for runners --- packages/devkit/CHANGELOG.md | 7 ++ packages/devkit/README.md | 1 + packages/devkit/package.json | 29 +++++ packages/devkit/project.json | 12 ++ packages/devkit/src/index.spec.ts | 9 ++ packages/devkit/src/index.ts | 44 ++++++++ packages/devkit/tsconfig.json | 8 ++ packages/devkit/tsconfig.lib.json | 8 ++ packages/devkit/vite.config.ts | 2 + packages/devkit/vitest.config.ts | 2 + packages/on-runner-docker/src/index.ts | 82 ++++---------- packages/on-runner-shell/src/index.ts | 61 ++--------- packages/on/package.json | 3 +- packages/on/src/cli.spec.ts | 146 ++++++++++++------------- packages/on/src/reports.ts | 3 +- packages/on/src/step.ts | 39 +++++++ packages/on/src/types.ts | 44 +++----- packages/on/src/workflow.ts | 53 +++------ pnpm-lock.yaml | 13 ++- tsconfig.base.json | 13 +-- 20 files changed, 309 insertions(+), 270 deletions(-) create mode 100644 packages/devkit/CHANGELOG.md create mode 100644 packages/devkit/README.md create mode 100644 packages/devkit/package.json create mode 100644 packages/devkit/project.json create mode 100644 packages/devkit/src/index.spec.ts create mode 100644 packages/devkit/src/index.ts create mode 100644 packages/devkit/tsconfig.json create mode 100644 packages/devkit/tsconfig.lib.json create mode 100644 packages/devkit/vite.config.ts create mode 100644 packages/devkit/vitest.config.ts create mode 100644 packages/on/src/step.ts diff --git a/packages/devkit/CHANGELOG.md b/packages/devkit/CHANGELOG.md new file mode 100644 index 0000000..128bea9 --- /dev/null +++ b/packages/devkit/CHANGELOG.md @@ -0,0 +1,7 @@ +# @cloud-cli/devkit + +## 0.1.0 + +### Patch Changes + +Introduce toobelt package diff --git a/packages/devkit/README.md b/packages/devkit/README.md new file mode 100644 index 0000000..1894a4a --- /dev/null +++ b/packages/devkit/README.md @@ -0,0 +1 @@ +# Devkit diff --git a/packages/devkit/package.json b/packages/devkit/package.json new file mode 100644 index 0000000..e472185 --- /dev/null +++ b/packages/devkit/package.json @@ -0,0 +1,29 @@ +{ + "name": "@cloud-cli/on-devkit", + "version": "0.1.0", + "description": "Toolbelt for workflows", + "type": "module", + "exports": { + ".": { + "types": "./src/index.ts", + "default": "./dist/on-devkit.js" + }, + "./types": "./src/index.ts" + }, + "files": [ + "dist", + "src" + ], + "engines": { + "node": ">=20" + }, + "publishConfig": { + "access": "public", + "provenance": true + }, + "license": "MIT", + "repository": { + "url": "https://github.com/cloud-cli/on" + }, + "dependencies": {} +} diff --git a/packages/devkit/project.json b/packages/devkit/project.json new file mode 100644 index 0000000..20a9c91 --- /dev/null +++ b/packages/devkit/project.json @@ -0,0 +1,12 @@ +{ + "name": "devkit", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "packages/on/src", + "projectType": "library", + "targets": { + "build": {}, + "test": {}, + "lint": {} + }, + "tags": [] +} diff --git a/packages/devkit/src/index.spec.ts b/packages/devkit/src/index.spec.ts new file mode 100644 index 0000000..61c78d5 --- /dev/null +++ b/packages/devkit/src/index.spec.ts @@ -0,0 +1,9 @@ +import { test, expect } from "vitest"; +import { interpolate } from "./index.js"; + +test("should interpolate a JS snippet with a context object", async () => { + const context = { foo: true, bar: "world" }; + const template = "Hello ${bar}! The value of foo is ${foo}."; + const result = interpolate(template, context); + expect(result).toBe("Hello world! The value of foo is true."); +}); diff --git a/packages/devkit/src/index.ts b/packages/devkit/src/index.ts new file mode 100644 index 0000000..f561f4c --- /dev/null +++ b/packages/devkit/src/index.ts @@ -0,0 +1,44 @@ +import { ChildProcess } from "child_process"; + +export function interpolate(template: string, context: any): string { + const keys = Object.keys(context); + const f = Function( + "__", + "const { " + keys.join(", ") + " } = __;return `" + template + "`;", + ); + + return String(f(context) || ""); +} + +export interface StepOutput { + code: number; + cmd: string; + stdout: string; + stderr: string; +} + +export function getStepOutput( + cmd: string, + childProcess: ChildProcess, +): Promise { + const stdout: Buffer[] = []; + const stderr: Buffer[] = []; + + childProcess.stdout!.on("data", (data: Buffer) => stdout.push(data)); + childProcess.stderr!.on("data", (data: Buffer) => stderr.push(data)); + + return new Promise((resolve, reject) => { + childProcess.once("error", reject); + childProcess.once("exit", (code: number | null) => { + resolve({ + code: code ?? 0, + cmd, + stdout: Buffer.concat(stdout).toString("utf-8"), + stderr: Buffer.concat(stderr).toString("utf-8"), + }); + }); + + childProcess.stdin?.write(cmd); + childProcess.stdin?.end(); + }); +} diff --git a/packages/devkit/tsconfig.json b/packages/devkit/tsconfig.json new file mode 100644 index 0000000..ec3a9bb --- /dev/null +++ b/packages/devkit/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "rootDir": "." + }, + "include": ["src/**/*.ts"], + "exclude": ["dist/**/*", "node_modules", "src/**/*.spec.ts"] +} diff --git a/packages/devkit/tsconfig.lib.json b/packages/devkit/tsconfig.lib.json new file mode 100644 index 0000000..975d824 --- /dev/null +++ b/packages/devkit/tsconfig.lib.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./dist/" + }, + "include": ["src/**/*.ts"], + "exclude": ["src/**/*.spec.ts"] +} diff --git a/packages/devkit/vite.config.ts b/packages/devkit/vite.config.ts new file mode 100644 index 0000000..fa5e45d --- /dev/null +++ b/packages/devkit/vite.config.ts @@ -0,0 +1,2 @@ +import { asLib } from "../../vite.config.base.js"; +export default asLib(import.meta.dirname, ["src/index.ts"]); diff --git a/packages/devkit/vitest.config.ts b/packages/devkit/vitest.config.ts new file mode 100644 index 0000000..c0ace0a --- /dev/null +++ b/packages/devkit/vitest.config.ts @@ -0,0 +1,2 @@ +import config from "../../vitest.config.base.js"; +export default config(import.meta.dirname); diff --git a/packages/on-runner-docker/src/index.ts b/packages/on-runner-docker/src/index.ts index e1e40c5..730ae71 100644 --- a/packages/on-runner-docker/src/index.ts +++ b/packages/on-runner-docker/src/index.ts @@ -1,35 +1,29 @@ import { spawn } from "node:child_process"; -import type { - NormalizedStepDefinition, - StepOutput, - WorkflowContext, - WorkflowRunner, -} from "@cloud-cli/on/types"; +import { interpolate, getStepOutput } from "@cloud-cli/on-devkit"; +import type { StepDefinition, WorkflowContext } from "@cloud-cli/on"; export const defaultWorkspace = "/workspace"; -export const defaultImage = "dhi.io/alpine-base:3.23-alpine3.23-dev"; -function interpolate(template: string, context: WorkflowContext): string { - const keys = Object.keys(context); - const f = Function( - "context", - "const { " + keys.join(", ") + " } = context;return `" + template + "`;", - ); +export const defaults = { + image: "dhi.io/alpine-base:3.23-alpine3.23-dev", + volumes: { + ".": defaultWorkspace, + }, + args: [], +}; - return String(f(context) || ""); +export interface DockerStep extends StepDefinition { + image?: string; + volumes?: Record; + args?: Array>; } export function prepareDockerStep( - step: NormalizedStepDefinition, + step: StepDefinition, context: WorkflowContext, ) { - const defaults = context.workflow.defaults || {}; - - step.image ||= defaults.image || defaultImage; - step.volumes ||= defaults.volumes || {}; - step.args ||= defaults.args || []; - - return step; + const workflowDefaults = context.workflow.defaults || {}; + return { ...defaults, ...workflowDefaults, ...step } as Required; } export function prepareDockerArgs( @@ -55,10 +49,7 @@ export function prepareDockerVolumes( ]); } -function prepareDockerShell( - step: NormalizedStepDefinition, - context: WorkflowContext, -) { +function prepareDockerShell(step: StepDefinition, context: WorkflowContext) { const prepared = prepareDockerStep(step, context); const { args, image, volumes } = prepared; const mappedVolumes = prepareDockerVolumes(volumes, context); @@ -82,35 +73,10 @@ function prepareDockerShell( }); } -export const dockerRunner: WorkflowRunner< - NormalizedStepDefinition, - WorkflowContext, - StepOutput -> = { - run(step, context) { - const stdout: Buffer[] = []; - const stderr: Buffer[] = []; - const cmd = interpolate(step.run, context); - - const shell = prepareDockerShell(step, context); - - shell.stdout?.on("data", (data) => stdout.push(data)); - shell.stderr?.on("data", (data) => stderr.push(data)); - - return new Promise((resolve, reject) => { - shell.once("error", reject); - shell.once("exit", (code) => { - resolve({ - code: code ?? 0, - cmd, - stdout: Buffer.concat(stdout).toString("utf-8"), - stderr: Buffer.concat(stderr).toString("utf-8"), - }); - }); - - shell.stdin?.write(cmd); - shell.stdin?.write("\nexit $?;\n"); - shell.stdin?.end(); - }); - }, -}; +export function run( + step: S, + context: W, +) { + const shell = prepareDockerShell(step, context); + return getStepOutput(cmd, shell); +} diff --git a/packages/on-runner-shell/src/index.ts b/packages/on-runner-shell/src/index.ts index f0c6947..1f9c1c7 100644 --- a/packages/on-runner-shell/src/index.ts +++ b/packages/on-runner-shell/src/index.ts @@ -1,56 +1,17 @@ import { spawn } from "node:child_process"; -import type { - NormalizedStepDefinition, - StepOutput, - WorkflowContext, - WorkflowRunner, -} from "@cloud-cli/on/types"; +import { getStepOutput } from "@cloud-cli/on-devkit"; +import type { StepDefinition, WorkflowContext } from "@cloud-cli/on/types"; const SHELL = process.env.SHELL || "sh"; -function interpolate(template: string, context: WorkflowContext): string { - const keys = Object.keys(context); - const f = Function( - "context", - "const { " + keys.join(", ") + " } = context;return `" + template + "`;", - ); +export function run(step: StepDefinition, context: WorkflowContext) { + const shell = spawn(SHELL, { + shell: true, + env: context.env, + cwd: step.workingDir || context.workingDir || process.cwd(), + }); - return String(f(context) || ""); -} - -export const shellRunner: WorkflowRunner< - NormalizedStepDefinition, - WorkflowContext, - StepOutput -> = { - run(step, context) { - const stdout: Buffer[] = []; - const stderr: Buffer[] = []; - const cmd = interpolate(step.run, context); - - const shell = spawn(SHELL, { - shell: true, - env: context.env, - cwd: step.workingDir || context.workingDir, - }); + const cmd = step.run + "\nexit $?;\n"; - shell.stdout?.on("data", (data) => stdout.push(data)); - shell.stderr?.on("data", (data) => stderr.push(data)); - - return new Promise((resolve, reject) => { - shell.once("error", reject); - shell.once("exit", (code) => { - resolve({ - code: code ?? 0, - cmd, - stdout: Buffer.concat(stdout).toString("utf-8"), - stderr: Buffer.concat(stderr).toString("utf-8"), - }); - }); - - shell.stdin?.write(cmd); - shell.stdin?.write("\nexit $?;\n"); - shell.stdin?.end(); - }); - }, -}; + return getStepOutput(cmd, shell); +} diff --git a/packages/on/package.json b/packages/on/package.json index 84ce876..306cef3 100644 --- a/packages/on/package.json +++ b/packages/on/package.json @@ -26,8 +26,7 @@ "url": "https://github.com/cloud-cli/on" }, "dependencies": { - "@cloud-cli/on-runner-docker": "workspace:*", - "@cloud-cli/on-runner-shell": "workspace:*", + "@cloud-cli/on-devkit": "workspace:*", "ansi_up": "6.0.6", "yaml": "^2.8.2" } diff --git a/packages/on/src/cli.spec.ts b/packages/on/src/cli.spec.ts index 2fe590b..eaaa400 100644 --- a/packages/on/src/cli.spec.ts +++ b/packages/on/src/cli.spec.ts @@ -87,91 +87,88 @@ test("prints help", () => { expect(result.stdout).toMatch(/daemonized webhook runner/); }); -test( - "executes workflow with mappings, secrets, env interpolation, defaults and dispatch", - { timeout: 30000 }, - async () => { - const tempDir = await getTempDir(); - const secretsPath = path.join(tempDir, ".env"); - const resultPath = path.join(tempDir, "result.txt"); - const dispatchedMarkerPath = path.join(tempDir, "dispatched.txt"); - const configPath = path.join(tempDir, "config.json"); - const triggerPath = path.join(tempDir, "trigger.json"); - - await writeFile(secretsPath, "A_SECRET=top-secret\n"); - - const config = { - on: { - published: { - runner: "docker", - secrets: [secretsPath], - mappings: { - url: "inputs.package.package_version.package_url", - }, - env: { - A_SECRET: "${secrets.A_SECRET}", - A_VALUE: "${inputs.image}", - }, - defaults: { - image: "node:latest", - volumes: { ".": "/home", [tempDir]: "/tmp" }, - args: [{ name: "published" }], - }, - steps: [ - "pwd", - "echo ${inputs}", - "echo '{\"followup\":{}}' > /tmp/trigger.json", - "echo ${env.A_SECRET} >> /tmp/result.txt", - "echo ${inputs.url} >> /tmp/result.txt", - "echo ${workflow.defaults.image} >> /tmp/result.txt", - ], - triggers: [triggerPath], +test("executes a full workflow example", { timeout: 30000 }, async () => { + const tempDir = await getTempDir(); + const secretsPath = path.join(tempDir, ".env"); + const resultPath = path.join(tempDir, "result.txt"); + const dispatchedMarkerPath = path.join(tempDir, "dispatched.txt"); + const configPath = path.join(tempDir, "config.json"); + const triggerPath = path.join(tempDir, "trigger.json"); + + await writeFile(secretsPath, "A_SECRET=top-secret\n"); + + const config = { + on: { + published: { + runner: "docker", + secrets: [secretsPath], + mappings: { + url: "inputs.package.package_version.package_url", + }, + env: { + A_SECRET: "${secrets.A_SECRET}", + A_VALUE: "${inputs.image}", }, - followup: { - steps: [ - { - run: "echo OK > /tmp/dispatched.txt", - volumes: { [tempDir]: "/tmp" }, - image: "node:latest", - }, - ], + defaults: { + image: "node:latest", + volumes: { ".": "/home", [tempDir]: "/tmp" }, + args: [{ name: "published" }], }, + steps: [ + "pwd", + "echo ${inputs}", + "echo '{\"followup\":{}}' > /tmp/trigger.json", + "echo ${env.A_SECRET} >> /tmp/result.txt", + "echo ${inputs.url} >> /tmp/result.txt", + "echo ${workflow.defaults.image} >> /tmp/result.txt", + ], + triggers: [triggerPath], }, - }; + followup: { + runner: "docker", + steps: [ + { + run: "echo OK > /tmp/dispatched.txt", + volumes: { [tempDir]: "/tmp" }, + image: "node:latest", + }, + ], + }, + }, + }; - await writeFile(configPath, JSON.stringify(config, null, 2)); + await writeFile(configPath, JSON.stringify(config, null, 2)); - const event = { - published: { - package: { - package_version: { - package_url: "registry/image:v1", - }, + const event = { + published: { + package: { + package_version: { + package_url: "registry/image:v1", }, }, - }; + }, + }; - const { sendEvent, stop } = await startDaemon({ configPath }); - const response = await sendEvent(event); + const { sendEvent, stop } = await startDaemon({ configPath }); + const response = await sendEvent(event); - await stop(); + await stop(); - expect(response.status).toBe(202); + expect(response.status).toBe(202); - expect(existsSync(resultPath)).toBe(true); - const resultContents = (await readFile(resultPath, "utf8")) as string; - expect(resultContents).toContain("top-secret"); - expect(resultContents).toContain("registry/image:v1"); - expect(resultContents).toContain("node:latest"); + expect(existsSync(resultPath)).toBe(true); + const resultContents = (await readFile(resultPath, "utf8")) as string; + expect(resultContents).toContain("top-secret"); + expect(resultContents).toContain("registry/image:v1"); + expect(resultContents).toContain("node:latest"); - const dispatchedMarker = ( - (await readFile(dispatchedMarkerPath, "utf8")) as string - ).trim(); - expect(dispatchedMarker).toBe("OK"); + const dispatchedMarker = ( + (await readFile(dispatchedMarkerPath, "utf8")) as string + ).trim(); + expect(dispatchedMarker).toBe("OK"); - await cleanUp(tempDir); - }, -); + await cleanUp(tempDir); +}); test("stop workflow if one step fails", async () => { const tempDir = await getTempDir(); @@ -257,9 +254,7 @@ test("skip workflow based on conditions", async () => { on: { test: { runner: "shell", - if: [ - "${inputs.value} === 123 ", - ], + if: ["${inputs.value} === 123 "], steps: [ { run: "echo works > result.txt", @@ -284,7 +279,6 @@ test("skip workflow based on conditions", async () => { await cleanUp(tempDir); }); - test("returns 202 for payloads that do not trigger any workflow", async () => { const { sendEvent, stop } = await startDaemon({ configPath: undefined }); const response = await sendEvent({ wrong: true }); diff --git a/packages/on/src/reports.ts b/packages/on/src/reports.ts index c675f07..2772a06 100644 --- a/packages/on/src/reports.ts +++ b/packages/on/src/reports.ts @@ -1,7 +1,8 @@ import { writeFile, readFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; -import { StepOutput, WorkflowContext } from "./types.js"; +import type { StepOutput } from "@cloud-cli/on-devkit"; +import type { WorkflowContext } from "@cloud-cli/on"; import { AnsiUp } from "ansi_up"; import { mkdirSync } from "node:fs"; diff --git a/packages/on/src/step.ts b/packages/on/src/step.ts new file mode 100644 index 0000000..b11ad50 --- /dev/null +++ b/packages/on/src/step.ts @@ -0,0 +1,39 @@ +import { interpolate } from "@cloud-cli/on-devkit"; +import type { + WorkflowContext, + StepDefinition, + WorkflowRunner, +} from "./types.js"; + +export async function runStep(step: StepDefinition, context: WorkflowContext) { + const cmd = interpolate(step.run, context); + const preparedStep = { ...step, run: cmd }; + const runner = await loadRunner(context.runner); + + return await runner.run(preparedStep, context); +} +export function normalizeSteps( + steps: Array, +): StepDefinition[] { + return steps.map((step) => { + if (typeof step === "string") { + step = { run: step }; + } + + if (typeof step.run !== "string") { + throw new Error("Each workflow step must have a 'run' command string."); + } + + return step; + }); +} + +export function loadRunner(runner: string): Promise { + try { + return import("@cloud-cli/on-runner-" + runner); + } catch (error) { + throw new Error( + `Failed to load runner "${runner}". Make sure the package "@cloud-cli/on-runner-${runner}" is installed.`, + ); + } +} diff --git a/packages/on/src/types.ts b/packages/on/src/types.ts index ced5592..d2e1462 100644 --- a/packages/on/src/types.ts +++ b/packages/on/src/types.ts @@ -1,3 +1,5 @@ +import type { StepOutput } from "@cloud-cli/on-devkit"; + export interface ServerOptions { port: number; host: string; @@ -5,29 +7,17 @@ export interface ServerOptions { daemon: boolean; } -export interface StepConfig { - image?: string; - volumes?: Record; - args?: Array>; -} - -export interface StepDefinition extends StepConfig { +export interface StepDefinition { + workingDir?: string; + runner?: string; run: string; } -export interface StepOutput { - code: number; - cmd: string; - stdout: string; - stderr: string; -} - -export interface WorkflowRunner< - TStep extends StepDefinition = StepDefinition, - TContext extends WorkflowContext = WorkflowContext, - TOutput extends StepOutput = StepOutput, -> { - run(step: TStep, context: TContext): Promise; +export interface WorkflowRunner { + run( + cmd: StepDefinition, + context: WorkflowContext, + ): Promise | void; } export interface EventOutput { @@ -37,27 +27,19 @@ export interface EventOutput { context: WorkflowContext | null; } -export interface NormalizedStepDefinition extends StepConfig { - run: string; - image: string; - args: Array>; - volumes: Record; - workingDir?: string; -} - export interface WorkflowDefinition { - runner?: "docker" | "shell"; + runner?: string; secrets?: string[]; mappings?: Record; env?: Record; - defaults?: Partial; + defaults?: Partial; steps?: StepDefinition[] | string[]; triggers?: string[]; if?: string[]; } export interface WorkflowContext { - runner: "docker" | "shell"; + runner: string; inputs: Record; outputs: Array; secrets: Record; diff --git a/packages/on/src/workflow.ts b/packages/on/src/workflow.ts index 6681489..c224157 100644 --- a/packages/on/src/workflow.ts +++ b/packages/on/src/workflow.ts @@ -2,30 +2,18 @@ import { existsSync } from "node:fs"; import { mkdtemp, rm, readFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path/posix"; -import { dockerRunner } from "@cloud-cli/on-runner-docker"; -import { shellRunner } from "@cloud-cli/on-runner-shell"; import { loadSecrets } from "./secrets.js"; import type { - StepDefinition, WorkflowContext, WorkflowEvent, OnConfig, WorkflowDefinition, - StepOutput, EventOutput, - NormalizedStepDefinition, - WorkflowRunner, } from "./types.js"; import { interpolate, withMappings, asObject, toStringProxy } from "./utils.js"; import { randomUUID } from "node:crypto"; import { createReport } from "./reports.js"; -const runners: Record< - WorkflowContext["runner"], - WorkflowRunner -> = { - docker: dockerRunner, - shell: shellRunner, -}; +import { normalizeSteps, runStep } from "./step.js"; function prepareEnv(context: WorkflowContext) { const { workflow, secrets } = context; @@ -71,6 +59,16 @@ function validateEvent(eventPayload: WorkflowEvent, config: OnConfig) { return null; } + if ( + !workflow.runner && + workflow.steps.some((step) => typeof step === "string" || !step.runner) + ) { + console.warn( + `Workflow for event ${event.source}:${event.event} has no steps defined, skipping.`, + ); + return null; + } + const payload = event[key as string]; return { @@ -80,31 +78,6 @@ function validateEvent(eventPayload: WorkflowEvent, config: OnConfig) { }; } -async function runStep( - step: NormalizedStepDefinition, - context: WorkflowContext, -): Promise { - const runner = runners[context.runner]; - return runner.run(step, context); -} - -function normalizeSteps( - steps: Array, - context: WorkflowContext, -): NormalizedStepDefinition[] { - return steps.map((step) => { - if (typeof step === "string") { - step = { run: step }; - } - - if (typeof step.run !== "string") { - throw new Error("Each workflow step must have a 'run' command string."); - } - - return step as NormalizedStepDefinition; - }); -} - export async function processEvent( incomingEvent: WorkflowEvent, config: OnConfig, @@ -132,7 +105,7 @@ export async function processEvent( env: {}, outputs: [], workingDir, - runner: workflow.runner || "docker", + runner: workflow.runner, }); if (workflow.if) { @@ -152,7 +125,7 @@ export async function processEvent( try { prepareEnv(context); - const steps = normalizeSteps(workflow.steps || [], context); + const steps = normalizeSteps(workflow.steps || []); for (const step of steps) { const output = await runStep(step, context); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b678bdb..5b3b987 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -57,14 +57,15 @@ importers: specifier: 4.1.0 version: 4.1.0(@types/node@22.19.13)(vite@7.3.1(@types/node@22.19.13)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.2)) + packages/devkit: {} + + packages/devkit/dist: {} + packages/on: dependencies: - '@cloud-cli/on-runner-docker': + '@cloud-cli/on-devkit': specifier: workspace:* - version: link:../on-runner-docker - '@cloud-cli/on-runner-shell': - specifier: workspace:* - version: link:../on-runner-shell + version: link:../devkit/dist ansi_up: specifier: 6.0.6 version: 6.0.6 @@ -76,6 +77,8 @@ importers: packages/on-runner-shell: {} + packages/on-runner-shell/dist: {} + packages/on/dist: dependencies: ansi_up: diff --git a/tsconfig.base.json b/tsconfig.base.json index 0d54b58..4e5f789 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -1,9 +1,8 @@ { "compilerOptions": { - "target": "ES2022", + "target": "esnext", "module": "NodeNext", "moduleResolution": "NodeNext", - "baseUrl": ".", "strict": true, "declaration": true, "declarationMap": true, @@ -11,13 +10,13 @@ "esModuleInterop": true, "resolveJsonModule": true, "skipLibCheck": true, - "types": [ - "node" - ], + "types": ["node"], "paths": { "@cloud-cli/on": ["./packages/on/src/index.ts"], - "@cloud-cli/on/types": ["./packages/on/src/types.ts"], - "@cloud-cli/on-runner-docker": ["./packages/on-runner-docker/src/index.ts"], + "@cloud-cli/on-devkit": ["./packages/on-devkit/src/index.ts"], + "@cloud-cli/on-runner-docker": [ + "./packages/on-runner-docker/src/index.ts" + ], "@cloud-cli/on-runner-shell": ["./packages/on-runner-shell/src/index.ts"] } }