@neon/config-runtime 1.6.3 → 1.7.1
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.
- package/dist/_virtual/_rolldown/runtime.js +4 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2 -1
- package/dist/lib/run-hook.d.ts +51 -0
- package/dist/lib/run-hook.d.ts.map +1 -0
- package/dist/lib/run-hook.js +92 -0
- package/dist/lib/run-hook.js.map +1 -0
- package/dist/lib/run-hook.test-d.d.ts +1 -0
- package/dist/lib/run-hook.test-d.js +39 -0
- package/dist/lib/run-hook.test-d.js.map +1 -0
- package/dist/node_modules/.pnpm/@vitest_pretty-format@3.0.9/node_modules/@vitest/pretty-format/dist/index.js +879 -0
- package/dist/node_modules/.pnpm/@vitest_pretty-format@3.0.9/node_modules/@vitest/pretty-format/dist/index.js.map +1 -0
- package/dist/node_modules/.pnpm/@vitest_runner@3.0.9/node_modules/@vitest/runner/dist/chunk-tasks.js +30 -0
- package/dist/node_modules/.pnpm/@vitest_runner@3.0.9/node_modules/@vitest/runner/dist/chunk-tasks.js.map +1 -0
- package/dist/node_modules/.pnpm/@vitest_runner@3.0.9/node_modules/@vitest/runner/dist/index.js +581 -0
- package/dist/node_modules/.pnpm/@vitest_runner@3.0.9/node_modules/@vitest/runner/dist/index.js.map +1 -0
- package/dist/node_modules/.pnpm/@vitest_runner@3.0.9/node_modules/@vitest/runner/dist/utils.js +2 -0
- package/dist/node_modules/.pnpm/@vitest_utils@3.0.9/node_modules/@vitest/utils/dist/chunk-_commonjsHelpers.js +91 -0
- package/dist/node_modules/.pnpm/@vitest_utils@3.0.9/node_modules/@vitest/utils/dist/chunk-_commonjsHelpers.js.map +1 -0
- package/dist/node_modules/.pnpm/@vitest_utils@3.0.9/node_modules/@vitest/utils/dist/helpers.js +43 -0
- package/dist/node_modules/.pnpm/@vitest_utils@3.0.9/node_modules/@vitest/utils/dist/helpers.js.map +1 -0
- package/dist/node_modules/.pnpm/@vitest_utils@3.0.9/node_modules/@vitest/utils/dist/index.js +450 -0
- package/dist/node_modules/.pnpm/@vitest_utils@3.0.9/node_modules/@vitest/utils/dist/index.js.map +1 -0
- package/dist/node_modules/.pnpm/@vitest_utils@3.0.9/node_modules/@vitest/utils/dist/source-map.js +168 -0
- package/dist/node_modules/.pnpm/@vitest_utils@3.0.9/node_modules/@vitest/utils/dist/source-map.js.map +1 -0
- package/dist/node_modules/.pnpm/chai@5.2.0/node_modules/chai/chai.js +3110 -0
- package/dist/node_modules/.pnpm/chai@5.2.0/node_modules/chai/chai.js.map +1 -0
- package/dist/node_modules/.pnpm/expect-type@1.2.1/node_modules/expect-type/dist/branding.js +10 -0
- package/dist/node_modules/.pnpm/expect-type@1.2.1/node_modules/expect-type/dist/branding.js.map +1 -0
- package/dist/node_modules/.pnpm/expect-type@1.2.1/node_modules/expect-type/dist/index.js +109 -0
- package/dist/node_modules/.pnpm/expect-type@1.2.1/node_modules/expect-type/dist/index.js.map +1 -0
- package/dist/node_modules/.pnpm/expect-type@1.2.1/node_modules/expect-type/dist/messages.js +10 -0
- package/dist/node_modules/.pnpm/expect-type@1.2.1/node_modules/expect-type/dist/messages.js.map +1 -0
- package/dist/node_modules/.pnpm/expect-type@1.2.1/node_modules/expect-type/dist/overloads.js +10 -0
- package/dist/node_modules/.pnpm/expect-type@1.2.1/node_modules/expect-type/dist/overloads.js.map +1 -0
- package/dist/node_modules/.pnpm/expect-type@1.2.1/node_modules/expect-type/dist/utils.js +10 -0
- package/dist/node_modules/.pnpm/expect-type@1.2.1/node_modules/expect-type/dist/utils.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/arguments.js +11 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/arguments.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/array.js +16 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/array.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/bigint.js +11 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/bigint.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/class.js +15 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/class.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/date.js +13 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/date.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/error.js +34 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/error.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/function.js +12 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/function.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/helpers.js +122 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/helpers.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/html.js +39 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/html.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/index.js +99 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/index.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/map.js +25 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/map.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/number.js +14 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/number.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/object.js +21 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/object.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/promise.js +6 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/promise.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/regexp.js +12 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/regexp.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/set.js +18 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/set.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/string.js +24 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/string.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/symbol.js +9 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/symbol.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/typedarray.js +30 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/typedarray.js.map +1 -0
- package/dist/node_modules/.pnpm/tinyrainbow@2.0.0/node_modules/tinyrainbow/dist/chunk-BVHSVHOK.js +82 -0
- package/dist/node_modules/.pnpm/tinyrainbow@2.0.0/node_modules/tinyrainbow/dist/chunk-BVHSVHOK.js.map +1 -0
- package/dist/node_modules/.pnpm/tinyrainbow@2.0.0/node_modules/tinyrainbow/dist/node.js +9 -0
- package/dist/node_modules/.pnpm/tinyrainbow@2.0.0/node_modules/tinyrainbow/dist/node.js.map +1 -0
- package/dist/node_modules/.pnpm/vitest@3.0.9_@types_debug@4.1.13_@types_node@20.19.17_jiti@2.7.0_jsdom@26.1.0_lightning_fb7ae83398b95c49208c9194029bba02/node_modules/vitest/dist/index.js +10 -0
- package/dist/node_modules/.pnpm/vitest@3.0.9_@types_debug@4.1.13_@types_node@20.19.17_jiti@2.7.0_jsdom@26.1.0_lightning_fb7ae83398b95c49208c9194029bba02/node_modules/vitest/dist/index.js.map +1 -0
- package/dist/v1.d.ts +3 -2
- package/dist/v1.js +2 -1
- package/package.json +4 -4
package/dist/index.d.ts
CHANGED
|
@@ -6,5 +6,6 @@ import { NativeEvidence, NativeFinding, describeNativeFinding, findUndeclaredNat
|
|
|
6
6
|
import { PullConfigOptions, PulledBranchConfig, PulledPreview, pullConfig } from "./lib/pull-config.js";
|
|
7
7
|
import { PushConfigOptions, PushConfirmContext, pushConfig } from "./lib/push-config.js";
|
|
8
8
|
import { ApplyOptions, ConfigOperationOptions, CreateBranchOptions, CreateBranchResult, apply, createBranch, inspect, plan } from "./lib/operations.js";
|
|
9
|
-
import {
|
|
10
|
-
|
|
9
|
+
import { HookExecutionError, RunHookOptions, runHook, runShellHook } from "./lib/run-hook.js";
|
|
10
|
+
import { AppliedChange, CheckoutAfterContext, CheckoutBeforeContext, CheckoutBeforeResult, CheckoutEvent, CheckoutHooks, Config, ConfigLoadError, ConfigValidationError, ConflictReport, CreateAfterContext, CreateBeforeContext, CreateHooks, DeployAfterContext, DeployBeforeContext, DeployEvent, DeployHooks, ErrorCode, ExperimentalInput, FunctionBundle, FunctionBundlerInput, GitContext, Hook, HookBranch, HookEnv, Hooks, LoadConfigOptions, MissingContextError, NeonApi, PartialBranchCreateError, PlatformError, PushAbortedError, PushConflictError, PushResult, ShellHook, createNeonApiFromOptions, createRealNeonApi, defineConfig, isPartialBranchCreateError, isPlatformError, loadConfigFromFile } from "./v1.js";
|
|
11
|
+
export { AppliedChange, ApplyOptions, ArchiveLimits, CheckoutAfterContext, CheckoutBeforeContext, CheckoutBeforeResult, CheckoutEvent, CheckoutHooks, Config, ConfigLoadError, ConfigOperationOptions, ConfigValidationError, ConflictReport, CreateAfterContext, CreateBeforeContext, CreateBranchOptions, CreateBranchResult, CreateHooks, DEFAULT_ARCHIVE_LIMITS, DeployAfterContext, DeployBeforeContext, DeployEvent, DeployHooks, ErrorCode, ExperimentalInput, FunctionBundle, FunctionBundler, FunctionBundlerInput, GitContext, Hook, HookBranch, HookEnv, HookExecutionError, Hooks, LoadConfigOptions, MissingContextError, NativeEvidence, NativeFinding, NativeTraceDeps, NativeTraceResult, NeonApi, PartialBranchCreateError, PlatformError, PullConfigOptions, PulledBranchConfig, PulledPreview, PushAbortedError, PushConfigOptions, PushConfirmContext, PushConflictError, PushResult, RUNTIME_TARGET, RUNTIME_TARGET_LABEL, RunHookOptions, ShellHook, apply, assertZipWithinLimits, buildFunctionBundle, bundleAsIs, createBranch, createNeonApiFromOptions, createRealNeonApi, defineConfig, describeNativeFinding, enforceLimits, findUndeclaredNativePackages, inspect, isPartialBranchCreateError, isPlatformError, loadConfigFromFile, plan, pullConfig, pushConfig, resolveEsbuildEntry, runHook, runShellHook, traceNativePackages, zipFunctionBundle };
|
package/dist/index.js
CHANGED
|
@@ -6,5 +6,6 @@ import { buildFunctionBundle } from "./lib/function-bundle.js";
|
|
|
6
6
|
import { pullConfig } from "./lib/pull-config.js";
|
|
7
7
|
import { pushConfig } from "./lib/push-config.js";
|
|
8
8
|
import { apply, createBranch, inspect, plan } from "./lib/operations.js";
|
|
9
|
+
import { HookExecutionError, runHook, runShellHook } from "./lib/run-hook.js";
|
|
9
10
|
import { ConfigLoadError, ConfigValidationError, ErrorCode, MissingContextError, PartialBranchCreateError, PlatformError, PushAbortedError, PushConflictError, createNeonApiFromOptions, createRealNeonApi, defineConfig, isPartialBranchCreateError, isPlatformError, loadConfigFromFile } from "./v1.js";
|
|
10
|
-
export { ConfigLoadError, ConfigValidationError, DEFAULT_ARCHIVE_LIMITS, ErrorCode, MissingContextError, PartialBranchCreateError, PlatformError, PushAbortedError, PushConflictError, RUNTIME_TARGET, RUNTIME_TARGET_LABEL, apply, assertZipWithinLimits, buildFunctionBundle, bundleAsIs, createBranch, createNeonApiFromOptions, createRealNeonApi, defineConfig, describeNativeFinding, enforceLimits, findUndeclaredNativePackages, inspect, isPartialBranchCreateError, isPlatformError, loadConfigFromFile, plan, pullConfig, pushConfig, resolveEsbuildEntry, traceNativePackages, zipFunctionBundle };
|
|
11
|
+
export { ConfigLoadError, ConfigValidationError, DEFAULT_ARCHIVE_LIMITS, ErrorCode, HookExecutionError, MissingContextError, PartialBranchCreateError, PlatformError, PushAbortedError, PushConflictError, RUNTIME_TARGET, RUNTIME_TARGET_LABEL, apply, assertZipWithinLimits, buildFunctionBundle, bundleAsIs, createBranch, createNeonApiFromOptions, createRealNeonApi, defineConfig, describeNativeFinding, enforceLimits, findUndeclaredNativePackages, inspect, isPartialBranchCreateError, isPlatformError, loadConfigFromFile, plan, pullConfig, pushConfig, resolveEsbuildEntry, runHook, runShellHook, traceNativePackages, zipFunctionBundle };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Hook, ShellHook } from "@neon/config";
|
|
2
|
+
|
|
3
|
+
//#region src/lib/run-hook.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Thrown when a **shell-command** hook exits non-zero (or fails to spawn). Function hooks
|
|
7
|
+
* surface their own thrown errors directly; this wraps the process-level failure with the
|
|
8
|
+
* offending command and exit code so the CLI can report which hook step failed.
|
|
9
|
+
*/
|
|
10
|
+
declare class HookExecutionError extends Error {
|
|
11
|
+
readonly command: string;
|
|
12
|
+
readonly exitCode: number | null;
|
|
13
|
+
readonly signal: NodeJS.Signals | null;
|
|
14
|
+
constructor(command: string, exitCode: number | null, signal: NodeJS.Signals | null, cause?: unknown);
|
|
15
|
+
}
|
|
16
|
+
/** Options controlling how a hook (specifically its shell-command form) is executed. */
|
|
17
|
+
interface RunHookOptions {
|
|
18
|
+
/** Working directory for shell-command hooks. Defaults to `process.cwd()`. */
|
|
19
|
+
cwd?: string;
|
|
20
|
+
/**
|
|
21
|
+
* Extra environment variables injected into shell-command hooks (e.g. `DATABASE_URL`),
|
|
22
|
+
* merged over `process.env`. Ignored by function hooks (they receive the typed context).
|
|
23
|
+
*/
|
|
24
|
+
env?: Record<string, string | undefined>;
|
|
25
|
+
/** Receives stdout/stderr chunks from shell-command hooks as they stream. */
|
|
26
|
+
onOutput?: (chunk: string) => void;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Run a lifecycle hook and return its result.
|
|
30
|
+
*
|
|
31
|
+
* - **Function hook** — awaited and its value returned (e.g. a `checkout.before` rename).
|
|
32
|
+
* - **Shell-command hook** — each command run sequentially and **non-interactively** (stdin
|
|
33
|
+
* is not attached and `CI=1` is set, so an accidental interactive command fails fast
|
|
34
|
+
* instead of hanging). Returns `undefined` (shell hooks have no return channel). A
|
|
35
|
+
* non-zero exit throws {@link HookExecutionError} and stops the chain.
|
|
36
|
+
* - **`undefined`** — no hook configured; returns `undefined`.
|
|
37
|
+
*
|
|
38
|
+
* The caller decides semantics: for `before` hooks, propagate a throw to abort the operation;
|
|
39
|
+
* for `after` hooks, observe (and typically degrade a failure to a warning, since the branch
|
|
40
|
+
* already changed).
|
|
41
|
+
*/
|
|
42
|
+
declare function runHook<Ctx, Result>(hook: Hook<Ctx, Result> | undefined, ctx: Ctx, options?: RunHookOptions): Promise<Result | undefined>;
|
|
43
|
+
/**
|
|
44
|
+
* Run a {@link ShellHook} (one command or a sequential list) non-interactively. Exposed for
|
|
45
|
+
* callers that already know a hook is shell-shaped, or that want to run ad-hoc command chains
|
|
46
|
+
* with Neon env injected. Throws {@link HookExecutionError} on the first non-zero exit.
|
|
47
|
+
*/
|
|
48
|
+
declare function runShellHook(hook: ShellHook, options?: RunHookOptions): Promise<void>;
|
|
49
|
+
//#endregion
|
|
50
|
+
export { HookExecutionError, RunHookOptions, runHook, runShellHook };
|
|
51
|
+
//# sourceMappingURL=run-hook.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-hook.d.ts","names":[],"sources":["../../src/lib/run-hook.ts"],"mappings":";;;;;;AAQA;AAAgC;AAGd;AAKR,cARG,kBAAA,SAA2B,KAAA,CAQvB;EARuB,SAAA,OAAA,EAAA,MAAA;EAAK,SAAA,QAAA,EAAA,MAAA,GAAA,IAAA;EAwB5B,SAAA,MAAA,EArBC,MAAA,CAAO,OA4BlB,GAAM,IAAA;EAmBS,WAAO,CAAA,OAAA,EAAA,MAAA,EAAA,QAAA,EAAA,MAAA,GAAA,IAAA,EAAA,MAAA,EA1CnB,MAAA,CAAO,OA0CY,GAAA,IAAA,EAAA,KAAA,CAAA,EAAA,OAAA;AAAA;AACjB;AAAK,UA3BA,cAAA,CA2BA;EAAV;EACD,GAAA,CAAA,EAAA,MAAA;EACI;AACC;AAAR;AAAO;EAcY,GAAA,CAAA,EArCf,MAqCe,CAAA,MAAY,EAAA,MAAA,GAAA,SAAA,CAAA;EAAA;EAC3B,QAAA,CAAA,EAAA,CAAA,KAAA,EAAA,MAAA,EAAA,GAAA,IAAA;AACG;AACP;AAAO;;;;;;;;;;;;;iBArBY,2BACf,KAAK,KAAK,0BACX,eACI,iBACP,QAAQ;;;;;;iBAcW,YAAA,OACf,qBACG,iBACP"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
//#region src/lib/run-hook.ts
|
|
3
|
+
/**
|
|
4
|
+
* Thrown when a **shell-command** hook exits non-zero (or fails to spawn). Function hooks
|
|
5
|
+
* surface their own thrown errors directly; this wraps the process-level failure with the
|
|
6
|
+
* offending command and exit code so the CLI can report which hook step failed.
|
|
7
|
+
*/
|
|
8
|
+
var HookExecutionError = class extends Error {
|
|
9
|
+
command;
|
|
10
|
+
exitCode;
|
|
11
|
+
signal;
|
|
12
|
+
constructor(command, exitCode, signal, cause) {
|
|
13
|
+
const how = signal !== null ? `was killed by signal ${signal}` : `exited with code ${exitCode ?? "unknown"}`;
|
|
14
|
+
super(`Hook command failed (${how}): ${command}`, { cause });
|
|
15
|
+
this.name = "HookExecutionError";
|
|
16
|
+
this.command = command;
|
|
17
|
+
this.exitCode = exitCode;
|
|
18
|
+
this.signal = signal;
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Run a lifecycle hook and return its result.
|
|
23
|
+
*
|
|
24
|
+
* - **Function hook** — awaited and its value returned (e.g. a `checkout.before` rename).
|
|
25
|
+
* - **Shell-command hook** — each command run sequentially and **non-interactively** (stdin
|
|
26
|
+
* is not attached and `CI=1` is set, so an accidental interactive command fails fast
|
|
27
|
+
* instead of hanging). Returns `undefined` (shell hooks have no return channel). A
|
|
28
|
+
* non-zero exit throws {@link HookExecutionError} and stops the chain.
|
|
29
|
+
* - **`undefined`** — no hook configured; returns `undefined`.
|
|
30
|
+
*
|
|
31
|
+
* The caller decides semantics: for `before` hooks, propagate a throw to abort the operation;
|
|
32
|
+
* for `after` hooks, observe (and typically degrade a failure to a warning, since the branch
|
|
33
|
+
* already changed).
|
|
34
|
+
*/
|
|
35
|
+
async function runHook(hook, ctx, options = {}) {
|
|
36
|
+
if (hook === void 0) return void 0;
|
|
37
|
+
if (typeof hook === "function") return await hook(ctx);
|
|
38
|
+
await runShellHook(hook, options);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Run a {@link ShellHook} (one command or a sequential list) non-interactively. Exposed for
|
|
42
|
+
* callers that already know a hook is shell-shaped, or that want to run ad-hoc command chains
|
|
43
|
+
* with Neon env injected. Throws {@link HookExecutionError} on the first non-zero exit.
|
|
44
|
+
*/
|
|
45
|
+
async function runShellHook(hook, options = {}) {
|
|
46
|
+
const commands = Array.isArray(hook) ? hook : [hook];
|
|
47
|
+
for (const command of commands) await runOneCommand(command, options);
|
|
48
|
+
}
|
|
49
|
+
function runOneCommand(command, options) {
|
|
50
|
+
return new Promise((resolve, reject) => {
|
|
51
|
+
const child = spawn(command, {
|
|
52
|
+
shell: true,
|
|
53
|
+
cwd: options.cwd ?? process.cwd(),
|
|
54
|
+
stdio: [
|
|
55
|
+
"ignore",
|
|
56
|
+
"pipe",
|
|
57
|
+
"pipe"
|
|
58
|
+
],
|
|
59
|
+
env: buildEnv(options.env)
|
|
60
|
+
});
|
|
61
|
+
child.stdout?.on("data", (chunk) => options.onOutput?.(chunk.toString()));
|
|
62
|
+
child.stderr?.on("data", (chunk) => options.onOutput?.(chunk.toString()));
|
|
63
|
+
child.on("error", (cause) => {
|
|
64
|
+
reject(new HookExecutionError(command, null, null, cause));
|
|
65
|
+
});
|
|
66
|
+
child.on("close", (code, signal) => {
|
|
67
|
+
if (code === 0) {
|
|
68
|
+
resolve();
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
reject(new HookExecutionError(command, code, signal));
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Build the child env: `process.env` + a forced `CI=1` (so tools default to non-interactive)
|
|
77
|
+
* + the caller's overrides, dropping any `undefined` values so they don't shadow real vars.
|
|
78
|
+
*/
|
|
79
|
+
function buildEnv(extra) {
|
|
80
|
+
const env = {
|
|
81
|
+
...process.env,
|
|
82
|
+
CI: "1"
|
|
83
|
+
};
|
|
84
|
+
if (extra) {
|
|
85
|
+
for (const [key, value] of Object.entries(extra)) if (value !== void 0) env[key] = value;
|
|
86
|
+
}
|
|
87
|
+
return env;
|
|
88
|
+
}
|
|
89
|
+
//#endregion
|
|
90
|
+
export { HookExecutionError, runHook, runShellHook };
|
|
91
|
+
|
|
92
|
+
//# sourceMappingURL=run-hook.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-hook.js","names":[],"sources":["../../src/lib/run-hook.ts"],"sourcesContent":["import { spawn } from \"node:child_process\";\nimport type { Hook, ShellHook } from \"@neon/config\";\n\n/**\n * Thrown when a **shell-command** hook exits non-zero (or fails to spawn). Function hooks\n * surface their own thrown errors directly; this wraps the process-level failure with the\n * offending command and exit code so the CLI can report which hook step failed.\n */\nexport class HookExecutionError extends Error {\n\treadonly command: string;\n\treadonly exitCode: number | null;\n\treadonly signal: NodeJS.Signals | null;\n\n\tconstructor(\n\t\tcommand: string,\n\t\texitCode: number | null,\n\t\tsignal: NodeJS.Signals | null,\n\t\tcause?: unknown,\n\t) {\n\t\tconst how =\n\t\t\tsignal !== null\n\t\t\t\t? `was killed by signal ${signal}`\n\t\t\t\t: `exited with code ${exitCode ?? \"unknown\"}`;\n\t\tsuper(`Hook command failed (${how}): ${command}`, { cause });\n\t\tthis.name = \"HookExecutionError\";\n\t\tthis.command = command;\n\t\tthis.exitCode = exitCode;\n\t\tthis.signal = signal;\n\t}\n}\n\n/** Options controlling how a hook (specifically its shell-command form) is executed. */\nexport interface RunHookOptions {\n\t/** Working directory for shell-command hooks. Defaults to `process.cwd()`. */\n\tcwd?: string;\n\t/**\n\t * Extra environment variables injected into shell-command hooks (e.g. `DATABASE_URL`),\n\t * merged over `process.env`. Ignored by function hooks (they receive the typed context).\n\t */\n\tenv?: Record<string, string | undefined>;\n\t/** Receives stdout/stderr chunks from shell-command hooks as they stream. */\n\tonOutput?: (chunk: string) => void;\n}\n\n/**\n * Run a lifecycle hook and return its result.\n *\n * - **Function hook** — awaited and its value returned (e.g. a `checkout.before` rename).\n * - **Shell-command hook** — each command run sequentially and **non-interactively** (stdin\n * is not attached and `CI=1` is set, so an accidental interactive command fails fast\n * instead of hanging). Returns `undefined` (shell hooks have no return channel). A\n * non-zero exit throws {@link HookExecutionError} and stops the chain.\n * - **`undefined`** — no hook configured; returns `undefined`.\n *\n * The caller decides semantics: for `before` hooks, propagate a throw to abort the operation;\n * for `after` hooks, observe (and typically degrade a failure to a warning, since the branch\n * already changed).\n */\nexport async function runHook<Ctx, Result>(\n\thook: Hook<Ctx, Result> | undefined,\n\tctx: Ctx,\n\toptions: RunHookOptions = {},\n): Promise<Result | undefined> {\n\tif (hook === undefined) return undefined;\n\tif (typeof hook === \"function\") {\n\t\treturn await hook(ctx);\n\t}\n\tawait runShellHook(hook, options);\n\treturn undefined;\n}\n\n/**\n * Run a {@link ShellHook} (one command or a sequential list) non-interactively. Exposed for\n * callers that already know a hook is shell-shaped, or that want to run ad-hoc command chains\n * with Neon env injected. Throws {@link HookExecutionError} on the first non-zero exit.\n */\nexport async function runShellHook(\n\thook: ShellHook,\n\toptions: RunHookOptions = {},\n): Promise<void> {\n\tconst commands = Array.isArray(hook) ? hook : [hook];\n\tfor (const command of commands) {\n\t\tawait runOneCommand(command, options);\n\t}\n}\n\nfunction runOneCommand(\n\tcommand: string,\n\toptions: RunHookOptions,\n): Promise<void> {\n\treturn new Promise<void>((resolve, reject) => {\n\t\tconst child = spawn(command, {\n\t\t\tshell: true,\n\t\t\tcwd: options.cwd ?? process.cwd(),\n\t\t\t// stdin is intentionally NOT inherited: hooks must be non-interactive so a stray\n\t\t\t// prompt fails fast in CI instead of hanging forever.\n\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\tenv: buildEnv(options.env),\n\t\t});\n\n\t\tchild.stdout?.on(\"data\", (chunk: Buffer) =>\n\t\t\toptions.onOutput?.(chunk.toString()),\n\t\t);\n\t\tchild.stderr?.on(\"data\", (chunk: Buffer) =>\n\t\t\toptions.onOutput?.(chunk.toString()),\n\t\t);\n\t\tchild.on(\"error\", (cause) => {\n\t\t\treject(new HookExecutionError(command, null, null, cause));\n\t\t});\n\t\tchild.on(\"close\", (code, signal) => {\n\t\t\tif (code === 0) {\n\t\t\t\tresolve();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\treject(new HookExecutionError(command, code, signal));\n\t\t});\n\t});\n}\n\n/**\n * Build the child env: `process.env` + a forced `CI=1` (so tools default to non-interactive)\n * + the caller's overrides, dropping any `undefined` values so they don't shadow real vars.\n */\nfunction buildEnv(\n\textra: Record<string, string | undefined> | undefined,\n): NodeJS.ProcessEnv {\n\tconst env: NodeJS.ProcessEnv = { ...process.env, CI: \"1\" };\n\tif (extra) {\n\t\tfor (const [key, value] of Object.entries(extra)) {\n\t\t\tif (value !== undefined) env[key] = value;\n\t\t}\n\t}\n\treturn env;\n}\n"],"mappings":";;;;;;;AAQA,IAAa,qBAAb,cAAwC,MAAM;CAC7C;CACA;CACA;CAEA,YACC,SACA,UACA,QACA,OACC;EACD,MAAM,MACL,WAAW,OACR,wBAAwB,WACxB,oBAAoB,YAAY;EACpC,MAAM,wBAAwB,IAAI,KAAK,WAAW,EAAE,MAAM,CAAC;EAC3D,KAAK,OAAO;EACZ,KAAK,UAAU;EACf,KAAK,WAAW;EAChB,KAAK,SAAS;CACf;AACD;;;;;;;;;;;;;;;AA6BA,eAAsB,QACrB,MACA,KACA,UAA0B,CAAC,GACG;CAC9B,IAAI,SAAS,KAAA,GAAW,OAAO,KAAA;CAC/B,IAAI,OAAO,SAAS,YACnB,OAAO,MAAM,KAAK,GAAG;CAEtB,MAAM,aAAa,MAAM,OAAO;AAEjC;;;;;;AAOA,eAAsB,aACrB,MACA,UAA0B,CAAC,GACX;CAChB,MAAM,WAAW,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC,IAAI;CACnD,KAAK,MAAM,WAAW,UACrB,MAAM,cAAc,SAAS,OAAO;AAEtC;AAEA,SAAS,cACR,SACA,SACgB;CAChB,OAAO,IAAI,SAAe,SAAS,WAAW;EAC7C,MAAM,QAAQ,MAAM,SAAS;GAC5B,OAAO;GACP,KAAK,QAAQ,OAAO,QAAQ,IAAI;GAGhC,OAAO;IAAC;IAAU;IAAQ;GAAM;GAChC,KAAK,SAAS,QAAQ,GAAG;EAC1B,CAAC;EAED,MAAM,QAAQ,GAAG,SAAS,UACzB,QAAQ,WAAW,MAAM,SAAS,CAAC,CACpC;EACA,MAAM,QAAQ,GAAG,SAAS,UACzB,QAAQ,WAAW,MAAM,SAAS,CAAC,CACpC;EACA,MAAM,GAAG,UAAU,UAAU;GAC5B,OAAO,IAAI,mBAAmB,SAAS,MAAM,MAAM,KAAK,CAAC;EAC1D,CAAC;EACD,MAAM,GAAG,UAAU,MAAM,WAAW;GACnC,IAAI,SAAS,GAAG;IACf,QAAQ;IACR;GACD;GACA,OAAO,IAAI,mBAAmB,SAAS,MAAM,MAAM,CAAC;EACrD,CAAC;CACF,CAAC;AACF;;;;;AAMA,SAAS,SACR,OACoB;CACpB,MAAM,MAAyB;EAAE,GAAG,QAAQ;EAAK,IAAI;CAAI;CACzD,IAAI,OACE;OAAA,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,KAAK,GAC9C,IAAI,UAAU,KAAA,GAAW,IAAI,OAAO;CAAA;CAGtC,OAAO;AACR"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { runHook, runShellHook } from "./run-hook.js";
|
|
2
|
+
import { describe, test } from "../node_modules/.pnpm/@vitest_runner@3.0.9/node_modules/@vitest/runner/dist/index.js";
|
|
3
|
+
import { import_dist } from "../node_modules/.pnpm/vitest@3.0.9_@types_debug@4.1.13_@types_node@20.19.17_jiti@2.7.0_jsdom@26.1.0_lightning_fb7ae83398b95c49208c9194029bba02/node_modules/vitest/dist/index.js";
|
|
4
|
+
//#region src/lib/run-hook.test-d.ts
|
|
5
|
+
describe("runHook return typing", () => {
|
|
6
|
+
test("a function hook resolves to `Result | undefined`, inferred from the function", () => {
|
|
7
|
+
const fn = (ctx) => ({ name: ctx.inputName });
|
|
8
|
+
(0, import_dist.expectTypeOf)(runHook(fn, { inputName: "x" })).toEqualTypeOf();
|
|
9
|
+
});
|
|
10
|
+
test("an async function hook unwraps its awaited result", () => {
|
|
11
|
+
const fn = async () => 1;
|
|
12
|
+
(0, import_dist.expectTypeOf)(runHook(fn, void 0)).toEqualTypeOf();
|
|
13
|
+
});
|
|
14
|
+
test("an absent hook (undefined) still resolves to `Result | undefined`", () => {
|
|
15
|
+
(0, import_dist.expectTypeOf)(runHook(void 0, { inputName: "x" })).toEqualTypeOf();
|
|
16
|
+
});
|
|
17
|
+
test("a shell-command hook has no typed return channel (resolves to unknown)", () => {
|
|
18
|
+
(0, import_dist.expectTypeOf)(runHook("npm run db:migrate", void 0)).toEqualTypeOf();
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
describe("runShellHook typing", () => {
|
|
22
|
+
test("accepts a string or string[] and resolves to void", () => {
|
|
23
|
+
(0, import_dist.expectTypeOf)(runShellHook("echo hi")).toEqualTypeOf();
|
|
24
|
+
(0, import_dist.expectTypeOf)(runShellHook(["a", "b"])).toEqualTypeOf();
|
|
25
|
+
});
|
|
26
|
+
test("a non-shell value is rejected", () => {
|
|
27
|
+
runShellHook(42);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
describe("RunHookOptions shape", () => {
|
|
31
|
+
test("env values allow `string | undefined` (so unset keys can be passed through)", () => {
|
|
32
|
+
(0, import_dist.expectTypeOf)().toEqualTypeOf();
|
|
33
|
+
(0, import_dist.expectTypeOf)().toEqualTypeOf();
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
//#endregion
|
|
37
|
+
export {};
|
|
38
|
+
|
|
39
|
+
//# sourceMappingURL=run-hook.test-d.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-hook.test-d.js","names":[],"sources":["../../src/lib/run-hook.test-d.ts"],"sourcesContent":["import { describe, expectTypeOf, test } from \"vitest\";\nimport { type RunHookOptions, runHook, runShellHook } from \"./run-hook.js\";\n\ndescribe(\"runHook return typing\", () => {\n\ttest(\"a function hook resolves to `Result | undefined`, inferred from the function\", () => {\n\t\tconst fn = (ctx: { inputName: string }): { name: string } => ({\n\t\t\tname: ctx.inputName,\n\t\t});\n\t\texpectTypeOf(runHook(fn, { inputName: \"x\" })).toEqualTypeOf<\n\t\t\tPromise<{ name: string } | undefined>\n\t\t>();\n\t});\n\n\ttest(\"an async function hook unwraps its awaited result\", () => {\n\t\tconst fn = async (): Promise<number> => 1;\n\t\texpectTypeOf(runHook(fn, undefined)).toEqualTypeOf<\n\t\t\tPromise<number | undefined>\n\t\t>();\n\t});\n\n\ttest(\"an absent hook (undefined) still resolves to `Result | undefined`\", () => {\n\t\texpectTypeOf(\n\t\t\trunHook<{ inputName: string }, { name: string }>(undefined, {\n\t\t\t\tinputName: \"x\",\n\t\t\t}),\n\t\t).toEqualTypeOf<Promise<{ name: string } | undefined>>();\n\t});\n\n\ttest(\"a shell-command hook has no typed return channel (resolves to unknown)\", () => {\n\t\texpectTypeOf(runHook(\"npm run db:migrate\", undefined)).toEqualTypeOf<\n\t\t\tPromise<unknown>\n\t\t>();\n\t});\n});\n\ndescribe(\"runShellHook typing\", () => {\n\ttest(\"accepts a string or string[] and resolves to void\", () => {\n\t\texpectTypeOf(runShellHook(\"echo hi\")).toEqualTypeOf<Promise<void>>();\n\t\texpectTypeOf(runShellHook([\"a\", \"b\"])).toEqualTypeOf<Promise<void>>();\n\t});\n\n\ttest(\"a non-shell value is rejected\", () => {\n\t\t// @ts-expect-error a ShellHook is a string or string[] — not a number.\n\t\trunShellHook(42);\n\t});\n});\n\ndescribe(\"RunHookOptions shape\", () => {\n\ttest(\"env values allow `string | undefined` (so unset keys can be passed through)\", () => {\n\t\texpectTypeOf<NonNullable<RunHookOptions[\"env\"]>>().toEqualTypeOf<\n\t\t\tRecord<string, string | undefined>\n\t\t>();\n\t\texpectTypeOf<RunHookOptions[\"cwd\"]>().toEqualTypeOf<\n\t\t\tstring | undefined\n\t\t>();\n\t});\n});\n"],"mappings":";;;;AAGA,SAAS,+BAA+B;CACvC,KAAK,sFAAsF;EAC1F,MAAM,MAAM,SAAkD,EAC7D,MAAM,IAAI,UACX;EACA,CAAA,GAAA,YAAA,aAAA,CAAa,QAAQ,IAAI,EAAE,WAAW,IAAI,CAAC,CAAC,CAAC,CAAC,cAE5C;CACH,CAAC;CAED,KAAK,2DAA2D;EAC/D,MAAM,KAAK,YAA6B;EACxC,CAAA,GAAA,YAAA,aAAA,CAAa,QAAQ,IAAI,KAAA,CAAS,CAAC,CAAC,CAAC,cAEnC;CACH,CAAC;CAED,KAAK,2EAA2E;EAC/E,CAAA,GAAA,YAAA,aAAA,CACC,QAAiD,KAAA,GAAW,EAC3D,WAAW,IACZ,CAAC,CACF,CAAC,CAAC,cAAqD;CACxD,CAAC;CAED,KAAK,gFAAgF;EACpF,CAAA,GAAA,YAAA,aAAA,CAAa,QAAQ,sBAAsB,KAAA,CAAS,CAAC,CAAC,CAAC,cAErD;CACH,CAAC;AACF,CAAC;AAED,SAAS,6BAA6B;CACrC,KAAK,2DAA2D;EAC/D,CAAA,GAAA,YAAA,aAAA,CAAa,aAAa,SAAS,CAAC,CAAC,CAAC,cAA6B;EACnE,CAAA,GAAA,YAAA,aAAA,CAAa,aAAa,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,cAA6B;CACrE,CAAC;CAED,KAAK,uCAAuC;EAE3C,aAAa,EAAE;CAChB,CAAC;AACF,CAAC;AAED,SAAS,8BAA8B;CACtC,KAAK,qFAAqF;EACzF,CAAA,GAAA,YAAA,aAAA,CAAiD,CAAC,CAAC,cAEjD;EACF,CAAA,GAAA,YAAA,aAAA,CAAoC,CAAC,CAAC,cAEpC;CACH,CAAC;AACF,CAAC"}
|