paratix 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/builtins.ts","../src/recipe.ts","../src/server.ts"],"sourcesContent":["import { mergeEnvironmentFromMeta } from \"./meta.js\"\nimport { failed } from \"./moduleFailure.js\"\nimport {\n type Environment,\n type Module,\n type ModuleMetaEntry,\n type ModuleResult,\n NEEDS_APPLY,\n type SshConnection,\n} from \"./types.js\"\n\n/**\n * Fail the run if a condition on the current env is not satisfied.\n *\n * The check phase evaluates the condition; if it returns `false`, apply\n * marks the module as failed, which aborts the parent recipe.\n *\n * @param condition - A predicate receiving the current env at run time.\n * @param message - Human-readable description shown in the run output.\n * @returns A Module that asserts the condition.\n *\n * @example\n * assert(env => !!env[\"APP_SECRET\"], \"APP_SECRET must be set\")\n */\nexport function assert(condition: (environment: Environment) => boolean, message: string): Module {\n return {\n _dryRunBlocker: true,\n // eslint-disable-next-line @typescript-eslint/require-await -- Interface requires async\n async apply(_ssh: null | SshConnection, environment: Environment): Promise<ModuleResult> {\n if (condition(environment)) {\n return { status: \"ok\" }\n }\n return failed(`[assert] ${message}`)\n },\n // eslint-disable-next-line @typescript-eslint/require-await -- Interface requires async\n async check(\n _ssh: null | SshConnection,\n environment: Environment\n ): Promise<\"needs-apply\" | \"ok\"> {\n return condition(environment) ? \"ok\" : NEEDS_APPLY\n },\n name: `assert: ${message}`,\n }\n}\n\n/**\n * Print a static debug message to the console during the apply phase.\n * Always runs (never skipped by the check phase).\n *\n * @param message - The message to print, prefixed with `[debug]`.\n * @returns A Module that prints a debug message.\n */\nexport function debug(message: string): Module {\n return {\n // eslint-disable-next-line @typescript-eslint/require-await -- Interface requires async\n async apply(): Promise<ModuleResult> {\n console.log(` [debug] ${message}`)\n return { status: \"ok\" }\n },\n // eslint-disable-next-line @typescript-eslint/require-await -- Interface requires async\n async check(): Promise<\"needs-apply\" | \"ok\"> {\n return NEEDS_APPLY\n },\n name: `debug: ${message}`,\n }\n}\n\n/**\n * Unconditionally abort the run with a failed status and an error message.\n * Useful as a sentinel at the end of a conditional branch.\n *\n * @param message - The message to print, prefixed with `[fail]`.\n * @returns A Module that fails the run.\n */\nexport function fail(message: string): Module {\n return {\n _dryRunBlocker: true,\n // eslint-disable-next-line @typescript-eslint/require-await -- Interface requires async\n async apply(): Promise<ModuleResult> {\n return failed(`[fail] ${message}`)\n },\n // eslint-disable-next-line @typescript-eslint/require-await -- Interface requires async\n async check(): Promise<\"needs-apply\" | \"ok\"> {\n return NEEDS_APPLY\n },\n name: `fail: ${message}`,\n }\n}\n\n/**\n * Pause execution and wait for the operator to press Enter.\n * Useful for interactive confirmation during a run.\n *\n * @param message - Prompt shown to the operator. Defaults to `\"Press enter to continue...\"`.\n * @returns A Module that pauses execution.\n */\nexport function pause(message?: string): Module {\n return {\n async apply(): Promise<ModuleResult> {\n const promptText = message ?? \"Press enter to continue...\"\n process.stdout.write(` [pause] ${promptText} `)\n\n await new Promise<void>((resolve) => {\n process.stdin.once(\"data\", () => {\n process.stdin.pause()\n resolve()\n })\n })\n\n return { status: \"ok\" }\n },\n // eslint-disable-next-line @typescript-eslint/require-await -- Interface requires async\n async check(): Promise<\"needs-apply\" | \"ok\"> {\n return NEEDS_APPLY\n },\n name: message == null ? \"pause\" : `pause: ${message}`,\n }\n}\n\nfunction isFirstRunEnabled(environment: Environment): boolean {\n return environment.PARATIX_FIRST_RUN === \"true\" || environment.FIRST_RUN === true\n}\n\n/**\n * Built-ins related to the explicit first-run bootstrap stage.\n */\nexport const firstRun = {\n /**\n * Stop the current run successfully when Paratix was invoked with `--first-run`.\n * Useful as an explicit stage boundary in scaffolded playbooks.\n *\n * @param message - Optional note shown in the module name.\n * @returns A local module that stops the run only during first-run execution.\n */\n stop(message?: string): Module {\n const moduleName = message == null ? \"firstRun.stop\" : `firstRun.stop: ${message}`\n\n return {\n _dryRunBlocker: true,\n // eslint-disable-next-line @typescript-eslint/require-await -- Interface requires async\n async apply(_ssh: null | SshConnection, environment: Environment): Promise<ModuleResult> {\n if (!isFirstRunEnabled(environment)) {\n return { status: \"ok\" }\n }\n\n return {\n _dryRunDetail: \"(first-run stop)\",\n _stopRun: true,\n status: \"ok\",\n }\n },\n // eslint-disable-next-line @typescript-eslint/require-await -- Interface requires async\n async check(\n _ssh: null | SshConnection,\n environment: Environment\n ): Promise<\"needs-apply\" | \"ok\"> {\n return isFirstRunEnabled(environment) ? NEEDS_APPLY : \"ok\"\n },\n local: true,\n name: moduleName,\n }\n },\n}\n\n/**\n * Built-ins for explicit signal checkpoints.\n */\nexport const signals = {\n /**\n * Flush all currently pending signals for the active scope.\n * Signals remain scope-local:\n * - in a recipe, this flushes that recipe's signals\n * - at top level, this flushes `server(...).signals`\n *\n * @param message - Optional note shown in the module name.\n * @returns A local control module that requests an immediate signal flush.\n */\n flush(message?: string): Module {\n const moduleName = message == null ? \"signals.flush\" : `signals.flush: ${message}`\n\n return {\n _dryRunBlocker: true,\n // eslint-disable-next-line @typescript-eslint/require-await -- Interface requires async\n async apply(): Promise<ModuleResult> {\n return {\n _dryRunDetail: \"(dry-run, pending signals not executed)\",\n _flushSignals: true,\n status: \"ok\",\n }\n },\n // eslint-disable-next-line @typescript-eslint/require-await -- Interface requires async\n async check(): Promise<\"needs-apply\" | \"ok\"> {\n return NEEDS_APPLY\n },\n local: true,\n name: moduleName,\n }\n },\n}\n\nasync function applyConditionalModules(parameters: {\n dryRun?: boolean\n environment: Environment\n modules: Module[]\n ssh: null | SshConnection\n}): Promise<ModuleResult> {\n const { dryRun = false, modules, ssh } = parameters\n let state = createConditionalApplyState(parameters.environment)\n\n for (const currentModule of modules) {\n // eslint-disable-next-line no-await-in-loop\n const checkResult = await currentModule.check(ssh, state.environment)\n if (checkResult === \"ok\") continue\n\n if (!shouldExecuteConditionalApply(currentModule, dryRun)) {\n state = markConditionalApplyChanged(state)\n continue\n }\n\n // eslint-disable-next-line no-await-in-loop -- conditional modules must preserve ordered env propagation\n const result = await executeConditionalApply({\n dryRun,\n environment: state.environment,\n module: currentModule,\n ssh,\n })\n if (result.status === \"failed\") return result\n // eslint-disable-next-line no-await-in-loop -- downstream env must see each module's meta in order\n state = await mergeConditionalApplyState(state, result)\n if (state.stopRun === true) break\n }\n\n return {\n _flushSignals: state.flushSignals,\n _stopRun: state.stopRun,\n meta: state.meta.length === 0 ? undefined : state.meta,\n status: state.status,\n }\n}\n\nfunction shouldExecuteConditionalApply(module: Module, dryRun: boolean): boolean {\n if (!dryRun) return true\n return (\n module._applyDryRun != null ||\n module._dryRunBlocker === true ||\n module._dryRunMetaProducer === true\n )\n}\n\ntype ConditionalApplyState = {\n environment: Environment\n flushSignals?: true\n meta: ModuleMetaEntry[]\n status: \"changed\" | \"ok\" | \"skipped\"\n stopRun?: true\n}\n\nfunction createConditionalApplyState(environment: Environment): ConditionalApplyState {\n return { environment: { ...environment }, meta: [], status: \"ok\" }\n}\n\nfunction markConditionalApplyChanged(state: ConditionalApplyState): ConditionalApplyState {\n return { ...state, status: \"changed\" }\n}\n\nasync function executeConditionalApply(parameters: {\n dryRun: boolean\n environment: Environment\n module: Module\n ssh: null | SshConnection\n}): Promise<ModuleResult> {\n const { dryRun, environment, module, ssh } = parameters\n if (dryRun && module._applyDryRun != null) {\n return module._applyDryRun(ssh, environment)\n }\n return module.apply(ssh, environment)\n}\n\nfunction whenNeedsDryRunApply(modules: Module[]): boolean {\n return modules.some((module) => shouldExecuteConditionalDryRun(module))\n}\n\nfunction shouldExecuteConditionalDryRun(module: Module): boolean {\n return (\n module._applyDryRun != null ||\n module._dryRunBlocker === true ||\n module._dryRunMetaProducer === true\n )\n}\n\nasync function mergeConditionalApplyState(\n state: ConditionalApplyState,\n result: ModuleResult\n): Promise<ConditionalApplyState> {\n const environment = await mergeEnvironmentFromMeta(state.environment, result.meta)\n return {\n environment,\n flushSignals: result._flushSignals === true ? true : state.flushSignals,\n meta: result.meta == null ? state.meta : [...state.meta, ...result.meta],\n status: result.status === \"changed\" ? \"changed\" : state.status,\n stopRun: result._stopRun === true ? true : state.stopRun,\n }\n}\n\nfunction createWhenDryRunApply(\n condition: (environment: Environment) => boolean,\n modules: Module[],\n needsDryRunApply: boolean\n): ((ssh: null | SshConnection, environment: Environment) => Promise<ModuleResult>) | undefined {\n if (!needsDryRunApply) return undefined\n return async (ssh: null | SshConnection, environment: Environment) => {\n if (!condition(environment)) {\n return { status: \"skipped\" as const }\n }\n return applyConditionalModules({ dryRun: true, environment, modules, ssh })\n }\n}\n\n/**\n * Run one or more modules only when a runtime condition is met.\n * When the condition is `false`, the whole group is skipped without running\n * any child checks or applies.\n *\n * @param condition - A predicate evaluated against the current env at run time.\n * @param modules - One or more modules to run when the condition is `true`.\n * @returns A Module that conditionally runs the inner modules.\n *\n * @example\n * when(env => env[\"DEPLOY_ENV\"] === \"production\", service.enabled(\"fail2ban\"))\n */\nexport function when(\n condition: (environment: Environment) => boolean,\n ...modules: Module[]\n): Module {\n const needsDryRunApply = whenNeedsDryRunApply(modules)\n const applyDryRun = createWhenDryRunApply(condition, modules, needsDryRunApply)\n return {\n ...(modules.some((module) => module._dryRunBlocker === true)\n ? { _dryRunBlocker: true as const }\n : {}),\n ...(modules.some((module) => module._dryRunMetaProducer === true)\n ? { _dryRunMetaProducer: true as const }\n : {}),\n ...(applyDryRun == null ? {} : { _applyDryRun: applyDryRun }),\n async apply(ssh: null | SshConnection, environment: Environment): Promise<ModuleResult> {\n if (!condition(environment)) {\n return { status: \"skipped\" }\n }\n return applyConditionalModules({ environment, modules, ssh })\n },\n async check(\n ssh: null | SshConnection,\n environment: Environment\n ): Promise<\"needs-apply\" | \"ok\"> {\n if (!condition(environment)) {\n return \"ok\"\n }\n // Defensive copy so inner modules can mutate the env without affecting\n // the caller's object (see Bug #13 regression tests).\n const currentEnvironment = { ...environment }\n for (const currentModule of modules) {\n // eslint-disable-next-line no-await-in-loop\n const result = await currentModule.check(ssh, currentEnvironment)\n if (result === NEEDS_APPLY) {\n return NEEDS_APPLY\n }\n }\n return \"ok\"\n },\n name: `when: conditional (${modules.length} modules)`,\n }\n}\n","/* eslint-disable max-lines -- recipe orchestration intentionally stays co-located */\nimport { isEnvironmentMetaEntry, mergeEnvironmentFromMeta } from \"./meta.js\"\nimport {\n printCommandFailure,\n printModuleResult,\n printRecipeHeader,\n startModuleSpinner,\n withRecipeOutputScope,\n} from \"./output.js\"\nimport { runSignalModules, type SignalHooks } from \"./signalOrchestration.js\"\nimport { CommandError } from \"./sshHelpers.js\"\nimport {\n type Environment,\n type Module,\n type ModuleMetaEntry,\n type ModuleResult,\n type ModuleStatus,\n NEEDS_APPLY,\n type OrchestrationStep,\n type SshConnection,\n} from \"./types.js\"\n\n/**\n * Internal representation of a recipe module.\n * The `_isRecipe` flag lets the runner distinguish recipes from leaf modules.\n * @internal\n */\nexport type RecipeModule = {\n _isRecipe: true\n _modules: Module[]\n _signals?: Module[]\n apply: (\n ssh: null | SshConnection,\n environment: Environment,\n options?: {\n onChildStep?: (step: OrchestrationStep) => Promise<void>\n onSignalStep?: (step: OrchestrationStep) => Promise<void>\n shutdownSignal?: () => NodeJS.Signals | null\n signalHooks?: SignalHooks\n verbose?: boolean\n }\n ) => Promise<ModuleResult>\n} & Module\n\ntype RecipeState = {\n env: Environment\n meta?: ModuleMetaEntry[]\n signalsPending: boolean\n status: Exclude<ModuleStatus, \"skipped\">\n stopRun?: true\n}\n\ntype ExecuteModulesParameters = {\n environment: Environment\n onChildStep?: (step: OrchestrationStep) => Promise<void>\n onSignalStep?: (step: OrchestrationStep) => Promise<void>\n shutdownSignal?: () => NodeJS.Signals | null\n signalHooks?: SignalHooks\n signals?: Module[]\n verbose?: boolean\n}\n\ntype RecipeLoopStepResult =\n | { kind: \"break\"; state: RecipeState }\n | { kind: \"continue\"; state: RecipeState }\n\nconst INTERRUPTED_BEFORE_APPLY = Symbol(\"recipe-interrupted-before-apply\")\n\nfunction applyRecipeStepToState(\n state: RecipeState,\n step: OrchestrationStep,\n preserveControlPlaneMeta: boolean\n): RecipeState {\n let stepMeta: ModuleMetaEntry[] | undefined\n if (step.meta == null) {\n stepMeta = undefined\n } else if (preserveControlPlaneMeta) {\n stepMeta = step.meta\n } else {\n stepMeta = step.meta.filter(isEnvironmentMetaEntry)\n }\n const nextMeta = stepMeta == null ? (state.meta ?? []) : [...(state.meta ?? []), ...stepMeta]\n let nextStatus = state.status\n if (step.status === \"failed\") nextStatus = \"failed\"\n else if (step.status === \"changed\") nextStatus = \"changed\"\n\n return {\n env: step.env,\n meta: nextMeta,\n signalsPending: step.status === \"changed\" ? true : state.signalsPending,\n status: nextStatus,\n stopRun: step._stopRun === true ? true : state.stopRun,\n }\n}\n\n/**\n * Recipes run their own check→apply loop, separate from the runner's\n * `runModuleLoop` in runner.ts. This is intentional: recipes execute as a\n * single nested module inside the runner, so SSH-lifecycle concerns\n * (port-change reconnects, reboot handling), shutdown-signal guards,\n * dry-run mode and stats tracking are the runner's responsibility and\n * must not be duplicated here.\n *\n * @param parameters - Parameters for executing one child module.\n * @param parameters.targetModule - The module to check and conditionally apply.\n * @param parameters.ssh - Active SSH connection, or `null` for local modules.\n * @param parameters.currentEnvironment - Environment values available to the module.\n * @param parameters.shutdownSignal - Optional shutdown getter used to suppress new apply steps.\n * @param parameters.verbose - Whether verbose command diagnostics should be printed.\n * @returns The updated environment and status, or `null` if the module was already ok.\n */\nasync function executeOneModule(parameters: {\n currentEnvironment: Environment\n shutdownSignal?: () => NodeJS.Signals | null\n ssh: null | SshConnection\n targetModule: Module\n verbose?: boolean\n}): Promise<null | OrchestrationStep | typeof INTERRUPTED_BEFORE_APPLY> {\n const { currentEnvironment, ssh, targetModule } = parameters\n const verbose = parameters.verbose ?? false\n const connection = targetModule.local === true ? null : ssh\n const checkResult = await checkRecipeChild(targetModule, connection, currentEnvironment)\n\n if (checkResult === \"ok\") {\n printModuleResult(targetModule.name, \"ok\")\n return null\n }\n\n if ((parameters.shutdownSignal?.() ?? null) != null) {\n return INTERRUPTED_BEFORE_APPLY\n }\n\n const result = await targetModule.apply(connection, currentEnvironment)\n printModuleResult(targetModule.name, result.status)\n if (result.status === \"failed\" && result.error != null) {\n printCommandFailure(result.error, verbose)\n }\n\n const environment = await mergeEnvironmentFromMeta(currentEnvironment, result.meta)\n return {\n _flushSignals: result._flushSignals,\n _stopRun: result._stopRun,\n env: environment,\n meta: result.meta,\n status: result.status,\n }\n}\n\nasync function checkRecipeChild(\n targetModule: Module,\n connection: null | SshConnection,\n currentEnvironment: Environment\n): Promise<\"needs-apply\" | \"ok\"> {\n startModuleSpinner(targetModule.name)\n return targetModule.check(connection, currentEnvironment)\n}\n\nasync function applyExecutedRecipeStep(parameters: {\n onChildStep?: (step: OrchestrationStep) => Promise<void>\n preserveControlPlaneMeta: boolean\n state: RecipeState\n step: null | OrchestrationStep | typeof INTERRUPTED_BEFORE_APPLY\n}): Promise<null | RecipeState> {\n if (parameters.step == null) return parameters.state\n if (parameters.step === INTERRUPTED_BEFORE_APPLY) return null\n\n if (parameters.onChildStep != null) {\n await parameters.onChildStep(parameters.step)\n }\n\n return applyRecipeStepToState(\n parameters.state,\n parameters.step,\n parameters.preserveControlPlaneMeta\n )\n}\n\nfunction failedRecipeState(environment: Environment): RecipeState {\n return {\n env: environment,\n meta: undefined,\n signalsPending: false,\n status: \"failed\",\n }\n}\n\nfunction annotateRecipeChildError(moduleName: string, error: unknown): Error {\n const prefix = `[${moduleName}] `\n if (error instanceof CommandError) {\n return new CommandError(`${prefix}${error.message}`, error.fullStdout, error.fullStderr)\n }\n if (error instanceof Error) {\n return new Error(`${prefix}${error.message}`)\n }\n return new Error(`${prefix}${String(error)}`)\n}\n\ntype RecipeChildExecution =\n | { kind: \"failed\"; state: RecipeState }\n | {\n kind: \"step\"\n step: null | OrchestrationStep | typeof INTERRUPTED_BEFORE_APPLY\n }\n\nasync function executeRecipeChildStep(parameters: {\n currentEnvironment: Environment\n shutdownSignal: () => NodeJS.Signals | null\n ssh: null | SshConnection\n targetModule: Module\n verbose: boolean\n}): Promise<RecipeChildExecution> {\n try {\n return { kind: \"step\", step: await executeOneModule(parameters) }\n } catch (error) {\n if (parameters.shutdownSignal() != null) {\n return { kind: \"step\", step: INTERRUPTED_BEFORE_APPLY }\n }\n printModuleResult(parameters.targetModule.name, \"failed\")\n printCommandFailure(error, parameters.verbose)\n return { kind: \"failed\", state: failedRecipeState(parameters.currentEnvironment) }\n }\n}\n\nasync function processRecipeStep(parameters: {\n onChildStep?: (step: OrchestrationStep) => Promise<void>\n onSignalStep?: (step: OrchestrationStep) => Promise<void>\n preserveControlPlaneMeta: boolean\n shutdownSignal: () => NodeJS.Signals | null\n signalHooks?: SignalHooks\n signals?: Module[]\n ssh: null | SshConnection\n state: RecipeState\n step: RecipeChildExecution\n verbose: boolean\n}): Promise<RecipeLoopStepResult> {\n if (parameters.step.kind === \"failed\") {\n return { kind: \"break\", state: parameters.step.state }\n }\n\n const nextState = await applyExecutedRecipeStep({\n onChildStep: parameters.onChildStep,\n preserveControlPlaneMeta: parameters.preserveControlPlaneMeta,\n state: parameters.state,\n step: parameters.step.step,\n })\n if (nextState == null) {\n return { kind: \"break\", state: parameters.state }\n }\n\n let state = nextState\n if (shouldFlushRecipeSignals(parameters.step.step, state, parameters.signals)) {\n const signalStatus = await flushPendingRecipeSignals({\n environment: state.env,\n onSignalStep: parameters.onSignalStep,\n shutdownSignal: parameters.shutdownSignal,\n signalHooks: parameters.signalHooks,\n signals: parameters.signals,\n ssh: parameters.ssh,\n verbose: parameters.verbose,\n })\n state = applyRecipeSignalStatus(state, signalStatus)\n }\n\n if (state.status === \"failed\" || state.stopRun === true) {\n return { kind: \"break\", state }\n }\n\n return { kind: \"continue\", state }\n}\n\nasync function executeModules(\n modules: Module[],\n ssh: null | SshConnection,\n parameters: ExecuteModulesParameters\n): Promise<RecipeState> {\n const onChildStep = parameters.onChildStep\n const preserveControlPlaneMeta = onChildStep == null\n const shutdownSignal = parameters.shutdownSignal ?? (() => null)\n const verbose = parameters.verbose ?? false\n let state: RecipeState = {\n env: { ...parameters.environment },\n meta: undefined,\n signalsPending: false,\n status: \"ok\",\n }\n\n for (const currentModule of modules) {\n if (shutdownSignal() != null) break\n // eslint-disable-next-line no-await-in-loop\n const step = await executeRecipeChildStep({\n currentEnvironment: state.env,\n shutdownSignal,\n ssh,\n targetModule: currentModule,\n verbose,\n })\n // eslint-disable-next-line no-await-in-loop\n const processedStep = await processRecipeStep({\n onChildStep,\n onSignalStep: parameters.onSignalStep,\n preserveControlPlaneMeta,\n shutdownSignal,\n signalHooks: parameters.signalHooks,\n signals: parameters.signals,\n ssh,\n state,\n step,\n verbose,\n })\n state = processedStep.state\n if (processedStep.kind === \"break\") return state\n }\n\n return state\n}\n\nasync function triggerSignals(parameters: {\n environment: Environment\n onSignalStep?: (step: OrchestrationStep) => Promise<void>\n shutdownSignal?: () => NodeJS.Signals | null\n signalHooks?: SignalHooks\n signals: Module[]\n ssh: null | SshConnection\n verbose?: boolean\n}): Promise<\"changed\" | \"failed\"> {\n return runSignalModules({\n environment: parameters.environment,\n hooks: parameters.signalHooks,\n onSignalStep: parameters.onSignalStep,\n shutdownSignal: parameters.shutdownSignal,\n signals: parameters.signals,\n ssh: parameters.ssh,\n verbose: parameters.verbose,\n })\n}\n\nfunction shouldFlushRecipeSignals(\n step: null | OrchestrationStep | typeof INTERRUPTED_BEFORE_APPLY,\n state: RecipeState,\n signals?: Module[]\n): signals is Module[] {\n return (\n step != null &&\n step !== INTERRUPTED_BEFORE_APPLY &&\n step._flushSignals === true &&\n state.signalsPending &&\n signals != null\n )\n}\n\nfunction applyRecipeSignalStatus(\n state: RecipeState,\n signalStatus: \"changed\" | \"failed\"\n): RecipeState {\n return {\n ...state,\n signalsPending: false,\n status: signalStatus === \"failed\" ? \"failed\" : state.status,\n }\n}\n\nasync function flushPendingRecipeSignals(parameters: {\n environment: Environment\n onSignalStep?: (step: OrchestrationStep) => Promise<void>\n shutdownSignal?: () => NodeJS.Signals | null\n signalHooks?: SignalHooks\n signals: Module[]\n ssh: null | SshConnection\n verbose?: boolean\n}): Promise<\"changed\" | \"failed\"> {\n return triggerSignals(parameters)\n}\n\nasync function applyRecipe(parameters: {\n environment: Environment\n modules: Module[]\n name: string\n options?: {\n onChildStep?: (step: OrchestrationStep) => Promise<void>\n onSignalStep?: (step: OrchestrationStep) => Promise<void>\n shutdownSignal?: () => NodeJS.Signals | null\n signalHooks?: SignalHooks\n verbose?: boolean\n }\n signals?: Module[]\n ssh: null | SshConnection\n}): Promise<ModuleResult> {\n return withRecipeOutputScope(async () => {\n const shutdownSignal = parameters.options?.shutdownSignal\n const verbose = parameters.options?.verbose ?? false\n printRecipeHeader(parameters.name)\n const state = await executeModules(parameters.modules, parameters.ssh, {\n environment: parameters.environment,\n onChildStep: parameters.options?.onChildStep,\n onSignalStep: parameters.options?.onSignalStep,\n shutdownSignal,\n signalHooks: parameters.options?.signalHooks,\n signals: parameters.signals,\n verbose,\n })\n\n if (shouldRunRecipeSignalsAtEnd(state, parameters.signals)) {\n state.status = await triggerSignals({\n environment: state.env,\n onSignalStep: parameters.options?.onSignalStep,\n shutdownSignal,\n signalHooks: parameters.options?.signalHooks,\n signals: parameters.signals,\n ssh: parameters.ssh,\n verbose,\n })\n }\n\n return {\n _stopRun: state.stopRun,\n meta: state.meta,\n status: state.status,\n }\n })\n}\n\nfunction shouldRunRecipeSignalsAtEnd(state: RecipeState, signals?: Module[]): signals is Module[] {\n return state.signalsPending && state.status === \"changed\" && signals != null\n}\n\n/**\n * Group a list of modules into a named, self-contained recipe.\n *\n * The recipe runs each child module in order, short-circuits on the first\n * failure, and propagates `meta` env values from one module to all subsequent\n * ones. If any child reports `\"changed\"`, the optional `signals` are triggered\n * at the end of the run.\n *\n * @param name - Display name shown in the run output header.\n * @param modules - Ordered list of modules to execute.\n * @param options - Optional recipe configuration.\n * @param options.signals - Modules to fire when at least one child changed state.\n * @returns A RecipeModule that groups the child modules.\n *\n * @example\n * export const nginxRecipe = recipe(\"nginx\", [\n * apt.installed(\"nginx\"),\n * file.template(\"/etc/nginx/nginx.conf\", \"./files/nginx.conf.tmpl\"),\n * service.enabled(\"nginx\"),\n * ], {\n * signals: [service.reload(\"nginx\")],\n * });\n */\nexport function recipe(\n name: string,\n modules: Module[],\n options?: { signals?: Module[] }\n): RecipeModule {\n return {\n _isRecipe: true,\n _modules: modules,\n _signals: options?.signals,\n async apply(\n ssh: null | SshConnection,\n environment: Environment,\n parameters?: {\n onChildStep?: (step: OrchestrationStep) => Promise<void>\n onSignalStep?: (step: OrchestrationStep) => Promise<void>\n shutdownSignal?: () => NodeJS.Signals | null\n signalHooks?: SignalHooks\n verbose?: boolean\n }\n ): Promise<ModuleResult> {\n return applyRecipe({\n environment,\n modules,\n name,\n options: parameters,\n signals: options?.signals,\n ssh,\n })\n },\n\n async check(\n ssh: null | SshConnection,\n environment: Environment\n ): Promise<\"needs-apply\" | \"ok\"> {\n // Each child receives the original environment — no meta propagation,\n // because check() never calls apply() and therefore produces no meta.\n for (const childModule of modules) {\n const connection = childModule.local === true ? null : ssh\n let result: \"needs-apply\" | \"ok\"\n try {\n // eslint-disable-next-line no-await-in-loop\n result = await childModule.check(connection, environment)\n } catch (error) {\n throw annotateRecipeChildError(childModule.name, error)\n }\n if (result === NEEDS_APPLY) return NEEDS_APPLY\n }\n return \"ok\"\n },\n\n name,\n }\n}\n","import type { ServerDefinition } from \"./types.js\"\n\nimport { validateSshConfig } from \"./serverDefinitionValidation.js\"\n\n/**\n * Define a server and validate its configuration at construction time.\n *\n * This is a thin identity function whose only purpose is to provide\n * type-safe validation with descriptive error messages before the runner\n * ever attempts an SSH connection.\n *\n * @param config - The server definition to validate and return.\n * @returns The validated server definition.\n * @throws {Error} When any required field is missing or empty.\n *\n * @example\n * export default server({\n * name: \"web-01\",\n * host: \"10.0.0.1\",\n * ssh: { user: \"root\", ports: [22], privateKey: \"~/.ssh/id_ed25519\" }, // \"~\" is expanded\n * run: [apt.installed(\"nginx\")],\n * });\n */\nexport function server(config: ServerDefinition): ServerDefinition {\n if (config.host.length === 0) {\n throw new Error(\"ServerDefinition: host is required\")\n }\n if (config.name.length === 0) {\n throw new Error(\"ServerDefinition: name is required\")\n }\n validateSshConfig(config.ssh)\n if (config.run.length === 0) {\n throw new Error(\"ServerDefinition: run must contain at least one module\")\n }\n\n return config\n}\n"],"mappings":"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","names":["ssh","ssh","signals"]}