padrone 1.8.2 → 2.0.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.
- package/CHANGELOG.md +20 -1
- package/README.md +2 -2
- package/dist/codegen/code-builder.d.mts +10 -0
- package/dist/codegen/code-builder.d.mts.map +1 -0
- package/dist/codegen/code-builder.mjs +185 -0
- package/dist/codegen/code-builder.mjs.map +1 -0
- package/dist/codegen/discovery.d.mts +44 -0
- package/dist/codegen/discovery.d.mts.map +1 -0
- package/dist/codegen/discovery.mjs +179 -0
- package/dist/codegen/discovery.mjs.map +1 -0
- package/dist/codegen/file-emitter.d.mts +10 -0
- package/dist/codegen/file-emitter.d.mts.map +1 -0
- package/dist/codegen/file-emitter.mjs +58 -0
- package/dist/codegen/file-emitter.mjs.map +1 -0
- package/dist/codegen/generators/barrel-file.d.mts +10 -0
- package/dist/codegen/generators/barrel-file.d.mts.map +1 -0
- package/dist/codegen/generators/barrel-file.mjs +16 -0
- package/dist/codegen/generators/barrel-file.mjs.map +1 -0
- package/dist/codegen/generators/command-file.d.mts +25 -0
- package/dist/codegen/generators/command-file.d.mts.map +1 -0
- package/dist/codegen/generators/command-file.mjs +130 -0
- package/dist/codegen/generators/command-file.mjs.map +1 -0
- package/dist/codegen/generators/command-tree.d.mts +17 -0
- package/dist/codegen/generators/command-tree.d.mts.map +1 -0
- package/dist/codegen/generators/command-tree.mjs +72 -0
- package/dist/codegen/generators/command-tree.mjs.map +1 -0
- package/dist/codegen/index.d.mts +15 -330
- package/dist/codegen/index.mjs +13 -1507
- package/dist/codegen/parsers/bash.d.mts +19 -0
- package/dist/codegen/parsers/bash.d.mts.map +1 -0
- package/dist/codegen/parsers/bash.mjs +112 -0
- package/dist/codegen/parsers/bash.mjs.map +1 -0
- package/dist/codegen/parsers/fish.d.mts +13 -0
- package/dist/codegen/parsers/fish.d.mts.map +1 -0
- package/dist/codegen/parsers/fish.mjs +108 -0
- package/dist/codegen/parsers/fish.mjs.map +1 -0
- package/dist/codegen/parsers/help.d.mts +15 -0
- package/dist/codegen/parsers/help.d.mts.map +1 -0
- package/dist/codegen/parsers/help.mjs +271 -0
- package/dist/codegen/parsers/help.mjs.map +1 -0
- package/dist/codegen/parsers/merge.d.mts +14 -0
- package/dist/codegen/parsers/merge.d.mts.map +1 -0
- package/dist/codegen/parsers/merge.mjs +76 -0
- package/dist/codegen/parsers/merge.mjs.map +1 -0
- package/dist/codegen/parsers/zsh.d.mts +16 -0
- package/dist/codegen/parsers/zsh.d.mts.map +1 -0
- package/dist/codegen/parsers/zsh.mjs +161 -0
- package/dist/codegen/parsers/zsh.mjs.map +1 -0
- package/dist/codegen/schema-to-code.d.mts +24 -0
- package/dist/codegen/schema-to-code.d.mts.map +1 -0
- package/dist/codegen/schema-to-code.mjs +147 -0
- package/dist/codegen/schema-to-code.mjs.map +1 -0
- package/dist/codegen/template.d.mts +18 -0
- package/dist/codegen/template.d.mts.map +1 -0
- package/dist/codegen/template.mjs +37 -0
- package/dist/codegen/template.mjs.map +1 -0
- package/dist/codegen/types.d.mts +144 -0
- package/dist/codegen/types.d.mts.map +1 -0
- package/dist/completion.d.mts +2 -0
- package/dist/completion.mjs +2 -0
- package/dist/core/args.d.mts +3 -0
- package/dist/{args-DrCXxXeP.mjs → core/args.mjs} +49 -113
- package/dist/core/args.mjs.map +1 -0
- package/dist/core/commands.d.mts +13 -0
- package/dist/core/commands.d.mts.map +1 -0
- package/dist/{commands-DLR0rFgq.mjs → core/commands.mjs} +4 -232
- package/dist/core/commands.mjs.map +1 -0
- package/dist/core/create.d.mts +63 -0
- package/dist/core/create.d.mts.map +1 -0
- package/dist/core/create.mjs +230 -0
- package/dist/core/create.mjs.map +1 -0
- package/dist/core/default-runtime.mjs +198 -0
- package/dist/core/default-runtime.mjs.map +1 -0
- package/dist/core/errors.d.mts +107 -0
- package/dist/core/errors.d.mts.map +1 -0
- package/dist/{errors-DA4KzK1M.mjs → core/errors.mjs} +2 -2
- package/dist/core/errors.mjs.map +1 -0
- package/dist/core/exec.mjs +191 -0
- package/dist/core/exec.mjs.map +1 -0
- package/dist/core/interceptors.d.mts +33 -0
- package/dist/core/interceptors.d.mts.map +1 -0
- package/dist/core/interceptors.mjs +253 -0
- package/dist/core/interceptors.mjs.map +1 -0
- package/dist/core/parse.mjs +181 -0
- package/dist/core/parse.mjs.map +1 -0
- package/dist/core/program-methods.mjs +256 -0
- package/dist/core/program-methods.mjs.map +1 -0
- package/dist/core/results.d.mts +13 -0
- package/dist/core/results.d.mts.map +1 -0
- package/dist/core/results.mjs +150 -0
- package/dist/core/results.mjs.map +1 -0
- package/dist/core/runtime.d.mts +213 -0
- package/dist/core/runtime.d.mts.map +1 -0
- package/dist/core/runtime.mjs +10 -0
- package/dist/core/runtime.mjs.map +1 -0
- package/dist/core/validate.mjs +220 -0
- package/dist/core/validate.mjs.map +1 -0
- package/dist/docs/index.d.mts +1 -1
- package/dist/docs/index.d.mts.map +1 -1
- package/dist/docs/index.mjs +2 -2
- package/dist/docs/index.mjs.map +1 -1
- package/dist/extension/auto-output.d.mts +53 -0
- package/dist/extension/auto-output.d.mts.map +1 -0
- package/dist/extension/auto-output.mjs +116 -0
- package/dist/extension/auto-output.mjs.map +1 -0
- package/dist/extension/color.d.mts +19 -0
- package/dist/extension/color.d.mts.map +1 -0
- package/dist/extension/color.mjs +37 -0
- package/dist/extension/color.mjs.map +1 -0
- package/dist/extension/completion.d.mts +25 -0
- package/dist/extension/completion.d.mts.map +1 -0
- package/dist/extension/completion.mjs +40 -0
- package/dist/extension/completion.mjs.map +1 -0
- package/dist/extension/config.d.mts +55 -0
- package/dist/extension/config.d.mts.map +1 -0
- package/dist/extension/config.mjs +162 -0
- package/dist/extension/config.mjs.map +1 -0
- package/dist/extension/env.d.mts +42 -0
- package/dist/extension/env.d.mts.map +1 -0
- package/dist/extension/env.mjs +44 -0
- package/dist/extension/env.mjs.map +1 -0
- package/dist/extension/help.d.mts +29 -0
- package/dist/extension/help.d.mts.map +1 -0
- package/dist/extension/help.mjs +161 -0
- package/dist/extension/help.mjs.map +1 -0
- package/dist/extension/index.d.mts +18 -0
- package/dist/extension/ink.d.mts +31 -0
- package/dist/extension/ink.d.mts.map +1 -0
- package/dist/extension/ink.mjs +69 -0
- package/dist/extension/ink.mjs.map +1 -0
- package/dist/extension/interactive.d.mts +16 -0
- package/dist/extension/interactive.d.mts.map +1 -0
- package/dist/extension/interactive.mjs +89 -0
- package/dist/extension/interactive.mjs.map +1 -0
- package/dist/extension/logger.d.mts +67 -0
- package/dist/extension/logger.d.mts.map +1 -0
- package/dist/extension/logger.mjs +188 -0
- package/dist/extension/logger.mjs.map +1 -0
- package/dist/extension/man.d.mts +25 -0
- package/dist/extension/man.d.mts.map +1 -0
- package/dist/extension/man.mjs +41 -0
- package/dist/extension/man.mjs.map +1 -0
- package/dist/extension/mcp.d.mts +28 -0
- package/dist/extension/mcp.d.mts.map +1 -0
- package/dist/extension/mcp.mjs +44 -0
- package/dist/extension/mcp.mjs.map +1 -0
- package/dist/extension/progress-renderer.d.mts +13 -0
- package/dist/extension/progress-renderer.d.mts.map +1 -0
- package/dist/extension/progress-renderer.mjs +351 -0
- package/dist/extension/progress-renderer.mjs.map +1 -0
- package/dist/extension/progress.d.mts +76 -0
- package/dist/extension/progress.d.mts.map +1 -0
- package/dist/extension/progress.mjs +225 -0
- package/dist/extension/progress.mjs.map +1 -0
- package/dist/extension/repl.d.mts +26 -0
- package/dist/extension/repl.d.mts.map +1 -0
- package/dist/extension/repl.mjs +67 -0
- package/dist/extension/repl.mjs.map +1 -0
- package/dist/extension/serve.d.mts +27 -0
- package/dist/extension/serve.d.mts.map +1 -0
- package/dist/extension/serve.mjs +41 -0
- package/dist/extension/serve.mjs.map +1 -0
- package/dist/extension/signal.d.mts +19 -0
- package/dist/extension/signal.d.mts.map +1 -0
- package/dist/extension/signal.mjs +81 -0
- package/dist/extension/signal.mjs.map +1 -0
- package/dist/extension/stdin.d.mts +19 -0
- package/dist/extension/stdin.d.mts.map +1 -0
- package/dist/extension/stdin.mjs +52 -0
- package/dist/extension/stdin.mjs.map +1 -0
- package/dist/extension/suggestions.d.mts +6 -0
- package/dist/extension/suggestions.d.mts.map +1 -0
- package/dist/extension/suggestions.mjs +113 -0
- package/dist/extension/suggestions.mjs.map +1 -0
- package/dist/extension/timing.d.mts +27 -0
- package/dist/extension/timing.d.mts.map +1 -0
- package/dist/extension/timing.mjs +71 -0
- package/dist/extension/timing.mjs.map +1 -0
- package/dist/extension/tracing.d.mts +85 -0
- package/dist/extension/tracing.d.mts.map +1 -0
- package/dist/extension/tracing.mjs +107 -0
- package/dist/extension/tracing.mjs.map +1 -0
- package/dist/extension/update-check.d.mts +20 -0
- package/dist/extension/update-check.d.mts.map +1 -0
- package/dist/extension/update-check.mjs +62 -0
- package/dist/extension/update-check.mjs.map +1 -0
- package/dist/extension/utils.mjs +43 -0
- package/dist/extension/utils.mjs.map +1 -0
- package/dist/extension/version.d.mts +20 -0
- package/dist/extension/version.d.mts.map +1 -0
- package/dist/extension/version.mjs +51 -0
- package/dist/extension/version.mjs.map +1 -0
- package/dist/{completion-UnBKfGuk.mjs → feature/completion.mjs} +4 -3
- package/dist/feature/completion.mjs.map +1 -0
- package/dist/feature/interactive.mjs +145 -0
- package/dist/feature/interactive.mjs.map +1 -0
- package/dist/feature/mcp.d.mts +18 -0
- package/dist/feature/mcp.d.mts.map +1 -0
- package/dist/{mcp-D6PdtjIs.mjs → feature/mcp.mjs} +6 -6
- package/dist/feature/mcp.mjs.map +1 -0
- package/dist/feature/repl-loop.mjs +217 -0
- package/dist/feature/repl-loop.mjs.map +1 -0
- package/dist/feature/serve.d.mts +18 -0
- package/dist/feature/serve.d.mts.map +1 -0
- package/dist/{serve-PaCLsNoD.mjs → feature/serve.mjs} +7 -7
- package/dist/feature/serve.mjs.map +1 -0
- package/dist/feature/test.d.mts +109 -0
- package/dist/feature/test.d.mts.map +1 -0
- package/dist/feature/test.mjs +116 -0
- package/dist/feature/test.mjs.map +1 -0
- package/dist/feature/update-check.d.mts +35 -0
- package/dist/feature/update-check.d.mts.map +1 -0
- package/dist/{update-check-CZ2VqjnV.mjs → feature/update-check.mjs} +2 -2
- package/dist/feature/update-check.mjs.map +1 -0
- package/dist/feature/wrap.d.mts +58 -0
- package/dist/feature/wrap.d.mts.map +1 -0
- package/dist/feature/wrap.mjs +77 -0
- package/dist/feature/wrap.mjs.map +1 -0
- package/dist/index.d.mts +38 -901
- package/dist/index.mjs +25 -4337
- package/dist/ink.d.mts +2 -0
- package/dist/ink.mjs +2 -0
- package/dist/man.d.mts +2 -0
- package/dist/man.mjs +2 -0
- package/dist/mcp.d.mts +3 -0
- package/dist/mcp.mjs +2 -0
- package/dist/output/colorizer.d.mts +28 -0
- package/dist/output/colorizer.d.mts.map +1 -0
- package/dist/output/colorizer.mjs +94 -0
- package/dist/output/colorizer.mjs.map +1 -0
- package/dist/{formatter-CY3KrOEd.d.mts → output/formatter.d.mts} +2 -28
- package/dist/output/formatter.d.mts.map +1 -0
- package/dist/output/formatter.mjs +360 -0
- package/dist/output/formatter.mjs.map +1 -0
- package/dist/output/help.d.mts +18 -0
- package/dist/output/help.d.mts.map +1 -0
- package/dist/output/help.mjs +267 -0
- package/dist/output/help.mjs.map +1 -0
- package/dist/output/output-indicator.d.mts +27 -0
- package/dist/output/output-indicator.d.mts.map +1 -0
- package/dist/output/output-indicator.mjs +54 -0
- package/dist/output/output-indicator.mjs.map +1 -0
- package/dist/output/primitives.d.mts +33 -0
- package/dist/output/primitives.d.mts.map +1 -0
- package/dist/output/primitives.mjs +206 -0
- package/dist/output/primitives.mjs.map +1 -0
- package/dist/output/styling.d.mts +38 -0
- package/dist/output/styling.d.mts.map +1 -0
- package/dist/output/styling.mjs +146 -0
- package/dist/output/styling.mjs.map +1 -0
- package/dist/schema/zod.d.mts +6 -0
- package/dist/schema/zod.d.mts.map +1 -0
- package/dist/schema/zod.mjs +50 -0
- package/dist/schema/zod.mjs.map +1 -0
- package/dist/serve.d.mts +2 -0
- package/dist/serve.mjs +2 -0
- package/dist/test.d.mts +2 -109
- package/dist/test.mjs +1 -115
- package/dist/tracing.d.mts +2 -0
- package/dist/tracing.mjs +2 -0
- package/dist/types/args-meta.d.mts +129 -0
- package/dist/types/args-meta.d.mts.map +1 -0
- package/dist/types/builder.d.mts +212 -0
- package/dist/types/builder.d.mts.map +1 -0
- package/dist/types/command.d.mts +112 -0
- package/dist/types/command.d.mts.map +1 -0
- package/dist/types/index.d.mts +8 -0
- package/dist/types/interceptor.d.mts +232 -0
- package/dist/types/interceptor.d.mts.map +1 -0
- package/dist/types/preferences.d.mts +79 -0
- package/dist/types/preferences.d.mts.map +1 -0
- package/dist/types/result.d.mts +61 -0
- package/dist/types/result.d.mts.map +1 -0
- package/dist/types/schema.d.mts +24 -0
- package/dist/types/schema.d.mts.map +1 -0
- package/dist/util/dotenv.mjs +174 -0
- package/dist/util/dotenv.mjs.map +1 -0
- package/dist/util/shell-utils.mjs +80 -0
- package/dist/util/shell-utils.mjs.map +1 -0
- package/dist/util/stream.d.mts +33 -0
- package/dist/util/stream.d.mts.map +1 -0
- package/dist/{stream-DC4H8YTx.mjs → util/stream.mjs} +2 -2
- package/dist/util/stream.mjs.map +1 -0
- package/dist/util/type-helpers.d.mts +74 -0
- package/dist/util/type-helpers.d.mts.map +1 -0
- package/dist/util/type-utils.d.mts +186 -0
- package/dist/util/type-utils.d.mts.map +1 -0
- package/dist/util/utils.mjs +43 -0
- package/dist/util/utils.mjs.map +1 -0
- package/dist/zod.d.mts +1 -32
- package/dist/zod.mjs +1 -49
- package/package.json +65 -17
- package/src/completion.ts +2 -0
- package/src/core/args.ts +63 -37
- package/src/core/runtime.ts +11 -2
- package/src/extension/completion.ts +3 -0
- package/src/extension/index.ts +0 -12
- package/src/extension/ink.ts +2 -1
- package/src/extension/man.ts +3 -0
- package/src/extension/mcp.ts +3 -0
- package/src/extension/progress-renderer.ts +32 -8
- package/src/extension/progress.ts +19 -9
- package/src/extension/serve.ts +3 -0
- package/src/extension/tracing.ts +2 -0
- package/src/index.ts +1 -20
- package/src/ink.ts +2 -0
- package/src/man.ts +2 -0
- package/src/mcp.ts +3 -0
- package/src/serve.ts +2 -0
- package/src/tracing.ts +9 -0
- package/src/types/builder.ts +2 -2
- package/src/zod.ts +1 -1
- package/dist/args-DrCXxXeP.mjs.map +0 -1
- package/dist/codegen/index.d.mts.map +0 -1
- package/dist/codegen/index.mjs.map +0 -1
- package/dist/commands-DLR0rFgq.mjs.map +0 -1
- package/dist/completion-UnBKfGuk.mjs.map +0 -1
- package/dist/errors-DA4KzK1M.mjs.map +0 -1
- package/dist/formatter-CY3KrOEd.d.mts.map +0 -1
- package/dist/help-B-ZMYyn-.mjs +0 -892
- package/dist/help-B-ZMYyn-.mjs.map +0 -1
- package/dist/index-D-Dpz7l_.d.mts +0 -1427
- package/dist/index-D-Dpz7l_.d.mts.map +0 -1
- package/dist/index.d.mts.map +0 -1
- package/dist/index.mjs.map +0 -1
- package/dist/mcp-D6PdtjIs.mjs.map +0 -1
- package/dist/serve-PaCLsNoD.mjs.map +0 -1
- package/dist/stream-DC4H8YTx.mjs.map +0 -1
- package/dist/test.d.mts.map +0 -1
- package/dist/test.mjs.map +0 -1
- package/dist/update-check-CZ2VqjnV.mjs.map +0 -1
- package/dist/zod.d.mts.map +0 -1
- package/dist/zod.mjs.map +0 -1
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { PadroneSignal } from "./runtime.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/core/errors.d.ts
|
|
4
|
+
type PadroneErrorOptions = {
|
|
5
|
+
/** Process exit code. Defaults to 1. */exitCode?: number; /** Actionable suggestions shown to the user (e.g. "Use --env production"). */
|
|
6
|
+
suggestions?: string[]; /** The command path that produced the error (e.g. "deploy staging"). */
|
|
7
|
+
command?: string; /** The phase where the error occurred. */
|
|
8
|
+
phase?: 'parse' | 'validate' | 'execute' | 'config'; /** Original cause for error chaining. */
|
|
9
|
+
cause?: unknown;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Base error class for all Padrone errors.
|
|
13
|
+
* Carries structured metadata for user-friendly formatting and programmatic handling.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* throw new PadroneError('Something went wrong', {
|
|
18
|
+
* exitCode: 1,
|
|
19
|
+
* suggestions: ['Try --help for usage information'],
|
|
20
|
+
* });
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
declare class PadroneError extends Error {
|
|
24
|
+
readonly exitCode: number;
|
|
25
|
+
readonly suggestions: string[];
|
|
26
|
+
readonly command?: string;
|
|
27
|
+
readonly phase?: 'parse' | 'validate' | 'execute' | 'config';
|
|
28
|
+
constructor(message: string, options?: PadroneErrorOptions);
|
|
29
|
+
/**
|
|
30
|
+
* Returns a serializable representation of the error,
|
|
31
|
+
* suitable for non-terminal runtimes (web UIs, APIs, etc.).
|
|
32
|
+
*/
|
|
33
|
+
toJSON(): {
|
|
34
|
+
name: string;
|
|
35
|
+
message: string;
|
|
36
|
+
exitCode: number;
|
|
37
|
+
suggestions: string[];
|
|
38
|
+
command?: string;
|
|
39
|
+
phase?: string;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Thrown when command routing fails — unknown command, unexpected arguments, etc.
|
|
44
|
+
*/
|
|
45
|
+
declare class RoutingError extends PadroneError {
|
|
46
|
+
constructor(message: string, options?: PadroneErrorOptions);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Thrown when argument or schema validation fails.
|
|
50
|
+
* Carries the structured issues from the schema validator.
|
|
51
|
+
*/
|
|
52
|
+
declare class ValidationError extends PadroneError {
|
|
53
|
+
readonly issues: readonly {
|
|
54
|
+
path?: PropertyKey[];
|
|
55
|
+
message: string;
|
|
56
|
+
}[];
|
|
57
|
+
constructor(message: string, issues: readonly {
|
|
58
|
+
path?: PropertyKey[];
|
|
59
|
+
message: string;
|
|
60
|
+
}[], options?: PadroneErrorOptions);
|
|
61
|
+
toJSON(): {
|
|
62
|
+
issues: {
|
|
63
|
+
path: string[] | undefined;
|
|
64
|
+
message: string;
|
|
65
|
+
}[];
|
|
66
|
+
name: string;
|
|
67
|
+
message: string;
|
|
68
|
+
exitCode: number;
|
|
69
|
+
suggestions: string[];
|
|
70
|
+
command?: string;
|
|
71
|
+
phase?: string;
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Thrown when config file loading or validation fails.
|
|
76
|
+
*/
|
|
77
|
+
declare class ConfigError extends PadroneError {
|
|
78
|
+
constructor(message: string, options?: PadroneErrorOptions);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Thrown from user action handlers to surface structured errors with exit codes and suggestions.
|
|
82
|
+
* This is the primary error class users should throw from their command actions.
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```ts
|
|
86
|
+
* throw new ActionError('Missing environment', {
|
|
87
|
+
* exitCode: 1,
|
|
88
|
+
* suggestions: ['Use --env production or --env staging'],
|
|
89
|
+
* });
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
declare class ActionError extends PadroneError {
|
|
93
|
+
constructor(message: string, options?: PadroneErrorOptions);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Thrown when command execution is interrupted by a process signal (SIGINT, SIGTERM, SIGHUP).
|
|
97
|
+
* Carries the signal name and the conventional exit code (128 + signal number).
|
|
98
|
+
*/
|
|
99
|
+
declare class SignalError extends PadroneError {
|
|
100
|
+
readonly signal: PadroneSignal;
|
|
101
|
+
constructor(signal: PadroneSignal, options?: {
|
|
102
|
+
cause?: unknown;
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
//#endregion
|
|
106
|
+
export { ActionError, ConfigError, PadroneError, PadroneErrorOptions, RoutingError, SignalError, ValidationError };
|
|
107
|
+
//# sourceMappingURL=errors.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.mts","names":[],"sources":["../../src/core/errors.ts"],"mappings":";;;KAWY,mBAAA;EAQV,wCANA,QAAA,WAQK;EANL,WAAA,aAqBW;EAnBX,OAAA;EAEA,KAAA,gDAiBgC;EAfhC,KAAA;AAAA;;;;;;;;;;;;;cAeW,YAAA,SAAqB,KAAK;EAAA,SAC5B,QAAA;EAAA,SACA,WAAA;EAAA,SACA,OAAA;EAAA,SACA,KAAA;cAEG,OAAA,UAAiB,OAAA,GAAU,mBAAA;EAmCK;;;;EAtB5C,MAAA;IACE,IAAA;IACA,OAAA;IACA,QAAA;IACA,WAAA;IACA,OAAA;IACA,KAAA;EAAA;AAAA;;;;cAgBS,YAAA,SAAqB,YAAY;cAChC,OAAA,UAAiB,OAAA,GAAU,mBAAA;AAAA;;;;;cAU5B,eAAA,SAAwB,YAAA;EAAA,SAC1B,MAAA;IAAmB,IAAA,GAAO,WAAA;IAAe,OAAA;EAAA;cAEtC,OAAA,UAAiB,MAAA;IAAmB,IAAA,GAAO,WAAA;IAAe,OAAA;EAAA,KAAqB,OAAA,GAAU,mBAAA;EAM5F,MAAA;;;;;;;;;;;;;;;;cAWE,WAAA,SAAoB,YAAY;cAC/B,OAAA,UAAiB,OAAA,GAAU,mBAAA;AAAA;;AAAmB;AAkB5D;;;;;;;;;;cAAa,WAAA,SAAoB,YAAY;cAC/B,OAAA,UAAiB,OAAA,GAAU,mBAAA;AAAA;;;;;cAU5B,WAAA,SAAoB,YAAA;EAAA,SACtB,MAAA,EAAQ,aAAA;cAEL,MAAA,EAAQ,aAAA,EAAe,OAAA;IAAY,KAAA;EAAA;AAAA"}
|
|
@@ -132,6 +132,6 @@ function signalExitCode(signal) {
|
|
|
132
132
|
}[signal] ?? 1;
|
|
133
133
|
}
|
|
134
134
|
//#endregion
|
|
135
|
-
export {
|
|
135
|
+
export { ActionError, ConfigError, PadroneError, RoutingError, SignalError, ValidationError, signalExitCode };
|
|
136
136
|
|
|
137
|
-
//# sourceMappingURL=errors
|
|
137
|
+
//# sourceMappingURL=errors.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.mjs","names":[],"sources":["../../src/core/errors.ts"],"sourcesContent":["/**\n * Structured error hierarchy for Padrone CLI framework.\n *\n * All Padrone errors extend `PadroneError`, which carries an exit code,\n * optional suggestions, and context about which command/phase produced the error.\n * This allows callers to distinguish user errors (bad input) from bugs (unexpected throws)\n * and to present formatted, actionable error messages.\n */\n\nimport type { PadroneSignal } from './runtime.ts';\n\nexport type PadroneErrorOptions = {\n /** Process exit code. Defaults to 1. */\n exitCode?: number;\n /** Actionable suggestions shown to the user (e.g. \"Use --env production\"). */\n suggestions?: string[];\n /** The command path that produced the error (e.g. \"deploy staging\"). */\n command?: string;\n /** The phase where the error occurred. */\n phase?: 'parse' | 'validate' | 'execute' | 'config';\n /** Original cause for error chaining. */\n cause?: unknown;\n};\n\n/**\n * Base error class for all Padrone errors.\n * Carries structured metadata for user-friendly formatting and programmatic handling.\n *\n * @example\n * ```ts\n * throw new PadroneError('Something went wrong', {\n * exitCode: 1,\n * suggestions: ['Try --help for usage information'],\n * });\n * ```\n */\nexport class PadroneError extends Error {\n readonly exitCode: number;\n readonly suggestions: string[];\n readonly command?: string;\n readonly phase?: 'parse' | 'validate' | 'execute' | 'config';\n\n constructor(message: string, options?: PadroneErrorOptions) {\n super(message, options?.cause ? { cause: options.cause } : undefined);\n this.name = 'PadroneError';\n this.exitCode = options?.exitCode ?? 1;\n this.suggestions = options?.suggestions ?? [];\n this.command = options?.command;\n this.phase = options?.phase;\n }\n\n /**\n * Returns a serializable representation of the error,\n * suitable for non-terminal runtimes (web UIs, APIs, etc.).\n */\n toJSON(): {\n name: string;\n message: string;\n exitCode: number;\n suggestions: string[];\n command?: string;\n phase?: string;\n } {\n return {\n name: this.name,\n message: this.message,\n exitCode: this.exitCode,\n suggestions: this.suggestions,\n command: this.command,\n phase: this.phase,\n };\n }\n}\n\n/**\n * Thrown when command routing fails — unknown command, unexpected arguments, etc.\n */\nexport class RoutingError extends PadroneError {\n constructor(message: string, options?: PadroneErrorOptions) {\n super(message, { phase: 'parse', ...options });\n this.name = 'RoutingError';\n }\n}\n\n/**\n * Thrown when argument or schema validation fails.\n * Carries the structured issues from the schema validator.\n */\nexport class ValidationError extends PadroneError {\n readonly issues: readonly { path?: PropertyKey[]; message: string }[];\n\n constructor(message: string, issues: readonly { path?: PropertyKey[]; message: string }[], options?: PadroneErrorOptions) {\n super(message, { phase: 'validate', ...options });\n this.name = 'ValidationError';\n this.issues = issues;\n }\n\n override toJSON() {\n return {\n ...super.toJSON(),\n issues: this.issues.map((i) => ({ path: i.path?.map(String), message: i.message })),\n };\n }\n}\n\n/**\n * Thrown when config file loading or validation fails.\n */\nexport class ConfigError extends PadroneError {\n constructor(message: string, options?: PadroneErrorOptions) {\n super(message, { phase: 'config', ...options });\n this.name = 'ConfigError';\n }\n}\n\n/**\n * Thrown from user action handlers to surface structured errors with exit codes and suggestions.\n * This is the primary error class users should throw from their command actions.\n *\n * @example\n * ```ts\n * throw new ActionError('Missing environment', {\n * exitCode: 1,\n * suggestions: ['Use --env production or --env staging'],\n * });\n * ```\n */\nexport class ActionError extends PadroneError {\n constructor(message: string, options?: PadroneErrorOptions) {\n super(message, { phase: 'execute', ...options });\n this.name = 'ActionError';\n }\n}\n\n/**\n * Thrown when command execution is interrupted by a process signal (SIGINT, SIGTERM, SIGHUP).\n * Carries the signal name and the conventional exit code (128 + signal number).\n */\nexport class SignalError extends PadroneError {\n readonly signal: PadroneSignal;\n\n constructor(signal: PadroneSignal, options?: { cause?: unknown }) {\n super(`Process interrupted by ${signal}`, { exitCode: signalExitCode(signal), cause: options?.cause });\n this.name = 'SignalError';\n this.signal = signal;\n }\n}\n\n/** Maps a signal name to its conventional exit code (128 + signal number). */\nexport function signalExitCode(signal: PadroneSignal): number {\n const codes: Record<string, number> = { SIGINT: 130, SIGTERM: 143, SIGHUP: 129 };\n return codes[signal] ?? 1;\n}\n"],"mappings":";;;;;;;;;;;;;AAoCA,IAAa,eAAb,cAAkC,MAAM;CACtC;CACA;CACA;CACA;CAEA,YAAY,SAAiB,SAA+B;EAC1D,MAAM,SAAS,SAAS,QAAQ,EAAE,OAAO,QAAQ,MAAM,IAAI,KAAA,CAAS;EACpE,KAAK,OAAO;EACZ,KAAK,WAAW,SAAS,YAAY;EACrC,KAAK,cAAc,SAAS,eAAe,CAAC;EAC5C,KAAK,UAAU,SAAS;EACxB,KAAK,QAAQ,SAAS;CACxB;;;;;CAMA,SAOE;EACA,OAAO;GACL,MAAM,KAAK;GACX,SAAS,KAAK;GACd,UAAU,KAAK;GACf,aAAa,KAAK;GAClB,SAAS,KAAK;GACd,OAAO,KAAK;EACd;CACF;AACF;;;;AAKA,IAAa,eAAb,cAAkC,aAAa;CAC7C,YAAY,SAAiB,SAA+B;EAC1D,MAAM,SAAS;GAAE,OAAO;GAAS,GAAG;EAAQ,CAAC;EAC7C,KAAK,OAAO;CACd;AACF;;;;;AAMA,IAAa,kBAAb,cAAqC,aAAa;CAChD;CAEA,YAAY,SAAiB,QAA8D,SAA+B;EACxH,MAAM,SAAS;GAAE,OAAO;GAAY,GAAG;EAAQ,CAAC;EAChD,KAAK,OAAO;EACZ,KAAK,SAAS;CAChB;CAEA,SAAkB;EAChB,OAAO;GACL,GAAG,MAAM,OAAO;GAChB,QAAQ,KAAK,OAAO,KAAK,OAAO;IAAE,MAAM,EAAE,MAAM,IAAI,MAAM;IAAG,SAAS,EAAE;GAAQ,EAAE;EACpF;CACF;AACF;;;;AAKA,IAAa,cAAb,cAAiC,aAAa;CAC5C,YAAY,SAAiB,SAA+B;EAC1D,MAAM,SAAS;GAAE,OAAO;GAAU,GAAG;EAAQ,CAAC;EAC9C,KAAK,OAAO;CACd;AACF;;;;;;;;;;;;;AAcA,IAAa,cAAb,cAAiC,aAAa;CAC5C,YAAY,SAAiB,SAA+B;EAC1D,MAAM,SAAS;GAAE,OAAO;GAAW,GAAG;EAAQ,CAAC;EAC/C,KAAK,OAAO;CACd;AACF;;;;;AAMA,IAAa,cAAb,cAAiC,aAAa;CAC5C;CAEA,YAAY,QAAuB,SAA+B;EAChE,MAAM,0BAA0B,UAAU;GAAE,UAAU,eAAe,MAAM;GAAG,OAAO,SAAS;EAAM,CAAC;EACrG,KAAK,OAAO;EACZ,KAAK,SAAS;CAChB;AACF;;AAGA,SAAgB,eAAe,QAA+B;CAE5D,OAAO;EADiC,QAAQ;EAAK,SAAS;EAAK,QAAQ;CAChE,EAAE,WAAW;AAC1B"}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { getCommandRuntime } from "./commands.mjs";
|
|
2
|
+
import { errorResult, noop, thenMaybe, warnIfUnexpectedAsync, withDrain } from "./results.mjs";
|
|
3
|
+
import { resolveRegisteredInterceptors, runInterceptorChain, wrapWithCommandLifecycle, wrapWithLifecycle } from "./interceptors.mjs";
|
|
4
|
+
import { RoutingError, SignalError, ValidationError } from "./errors.mjs";
|
|
5
|
+
import { buildCommandArgs, formatIssueMessages, validateCommandArgs } from "./validate.mjs";
|
|
6
|
+
//#region src/core/exec.ts
|
|
7
|
+
/**
|
|
8
|
+
* Collects registered interceptors from the command's parent chain (root → ... → target).
|
|
9
|
+
* Root/program interceptors come first (outermost), target command's interceptors last (innermost).
|
|
10
|
+
*/
|
|
11
|
+
function collectInterceptors(cmd, rootCommand) {
|
|
12
|
+
const chain = [];
|
|
13
|
+
let current = cmd;
|
|
14
|
+
while (current) {
|
|
15
|
+
const isTarget = current === cmd;
|
|
16
|
+
if (!current.parent) {
|
|
17
|
+
if (rootCommand.interceptors?.length) {
|
|
18
|
+
const isRootTarget = cmd === rootCommand || !cmd.parent;
|
|
19
|
+
chain.unshift(isRootTarget ? rootCommand.interceptors : rootCommand.interceptors.filter((i) => i.meta.inherit !== false));
|
|
20
|
+
}
|
|
21
|
+
} else if (current.interceptors?.length) chain.unshift(isTarget ? current.interceptors : current.interceptors.filter((i) => i.meta.inherit !== false));
|
|
22
|
+
current = current.parent;
|
|
23
|
+
}
|
|
24
|
+
return chain.flat();
|
|
25
|
+
}
|
|
26
|
+
/** Wraps an error into a result, preserving any signal info from the pipeline. */
|
|
27
|
+
function errorResultWithSignal(err) {
|
|
28
|
+
const result = errorResult(err);
|
|
29
|
+
if (err instanceof SignalError) {
|
|
30
|
+
result.signal = err.signal;
|
|
31
|
+
result.exitCode = err.exitCode;
|
|
32
|
+
}
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
35
|
+
/** Resolve context by walking the command parent chain and applying transforms from root to target. */
|
|
36
|
+
function resolveContext(command, initialContext) {
|
|
37
|
+
const chain = [];
|
|
38
|
+
let current = command;
|
|
39
|
+
while (current) {
|
|
40
|
+
chain.unshift(current);
|
|
41
|
+
current = current.parent;
|
|
42
|
+
}
|
|
43
|
+
let resolved = initialContext;
|
|
44
|
+
for (const cmd of chain) if (cmd.contextTransform) resolved = cmd.contextTransform(resolved);
|
|
45
|
+
return resolved;
|
|
46
|
+
}
|
|
47
|
+
/** Validate parse result — reject unmatched terms when the command doesn't accept positional args. */
|
|
48
|
+
function validateParseResult(parseResult, rootCommand) {
|
|
49
|
+
const { command, rawArgs, args, unmatchedTerms } = parseResult;
|
|
50
|
+
if (unmatchedTerms.length > 0) {
|
|
51
|
+
if (!(command.meta?.positional && command.meta.positional.length > 0)) {
|
|
52
|
+
const isRootCommand = command === rootCommand;
|
|
53
|
+
const commandDisplayName = command.name || command.aliases?.[0] || command.path || "(default)";
|
|
54
|
+
throw new RoutingError(isRootCommand ? `Unknown command: ${unmatchedTerms[0]}` : `Unexpected arguments for '${commandDisplayName}': ${unmatchedTerms.join(" ")}`, { command: command.path || command.name });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
command,
|
|
59
|
+
rawArgs,
|
|
60
|
+
positionalArgs: args
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/** Handle validation issues based on error mode: throw (hard) or return result with issues (soft). */
|
|
64
|
+
function handleValidationIssues(argsResult, command, errorMode) {
|
|
65
|
+
if (errorMode === "hard") throw new ValidationError(`Validation error:\n${formatIssueMessages(argsResult.issues)}`, argsResult.issues, { command: command.path || command.name });
|
|
66
|
+
return withDrain({
|
|
67
|
+
command,
|
|
68
|
+
args: void 0,
|
|
69
|
+
argsResult,
|
|
70
|
+
result: void 0
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Core execution logic shared by eval() and cli().
|
|
75
|
+
* errorMode controls validation error behavior:
|
|
76
|
+
* - 'soft': return result with issues (eval behavior)
|
|
77
|
+
* - 'hard': print error + help and throw (cli-without-input behavior)
|
|
78
|
+
*/
|
|
79
|
+
function execCommand(resolvedInput, ctx, evalOptions, errorMode = "soft", caller = "eval") {
|
|
80
|
+
const { rootCommand, parseCommandFn, collectInterceptorsFn } = ctx;
|
|
81
|
+
const baseRuntime = getCommandRuntime(rootCommand);
|
|
82
|
+
const runtime = evalOptions?.runtime ? Object.assign({}, baseRuntime, Object.fromEntries(Object.entries(evalOptions.runtime).filter(([, v]) => v !== void 0))) : baseRuntime;
|
|
83
|
+
const inertSignal = new AbortController().signal;
|
|
84
|
+
const pipelineState = { phase: "start" };
|
|
85
|
+
const initialContext = evalOptions?.context;
|
|
86
|
+
const factoryCache = /* @__PURE__ */ new Map();
|
|
87
|
+
const rootInterceptors = resolveRegisteredInterceptors(rootCommand.interceptors ?? [], factoryCache);
|
|
88
|
+
const rootInterceptorSet = new Set(rootInterceptors);
|
|
89
|
+
const runPipeline = (signal, pipelineContext) => {
|
|
90
|
+
const parseCtx = {
|
|
91
|
+
input: resolvedInput,
|
|
92
|
+
command: rootCommand,
|
|
93
|
+
signal,
|
|
94
|
+
context: pipelineContext,
|
|
95
|
+
runtime,
|
|
96
|
+
program: ctx.builder,
|
|
97
|
+
caller
|
|
98
|
+
};
|
|
99
|
+
const coreParse = (parseCtx) => validateParseResult(parseCommandFn(parseCtx.input), rootCommand);
|
|
100
|
+
const parsedOrPromise = runInterceptorChain("parse", rootInterceptors, parseCtx, coreParse);
|
|
101
|
+
const continueAfterParse = (parsed) => {
|
|
102
|
+
const { command } = parsed;
|
|
103
|
+
pipelineState.phase = "parse";
|
|
104
|
+
pipelineState.rawArgs = parsed.rawArgs;
|
|
105
|
+
pipelineState.positionalArgs = parsed.positionalArgs;
|
|
106
|
+
const commandInterceptors = resolveRegisteredInterceptors(collectInterceptorsFn(command), factoryCache);
|
|
107
|
+
const commandOnlyInterceptors = commandInterceptors.filter((i) => !rootInterceptorSet.has(i));
|
|
108
|
+
const context = resolveContext(command, pipelineContext);
|
|
109
|
+
const routedOrPromise = runInterceptorChain("route", commandInterceptors, {
|
|
110
|
+
...parseCtx,
|
|
111
|
+
command,
|
|
112
|
+
rawArgs: parsed.rawArgs,
|
|
113
|
+
positionalArgs: parsed.positionalArgs,
|
|
114
|
+
context
|
|
115
|
+
}, () => {});
|
|
116
|
+
const continueAfterRoute = () => {
|
|
117
|
+
pipelineState.phase = "route";
|
|
118
|
+
const runValidateAndExecute = () => {
|
|
119
|
+
const validateCtx = {
|
|
120
|
+
...parseCtx,
|
|
121
|
+
command,
|
|
122
|
+
rawArgs: parsed.rawArgs,
|
|
123
|
+
positionalArgs: parsed.positionalArgs,
|
|
124
|
+
context,
|
|
125
|
+
evalInteractive: evalOptions?.interactive
|
|
126
|
+
};
|
|
127
|
+
const coreValidate = (validateCtx) => {
|
|
128
|
+
const { args: preprocessedArgs, issues } = buildCommandArgs(validateCtx.command, validateCtx.rawArgs, validateCtx.positionalArgs);
|
|
129
|
+
if (issues) return {
|
|
130
|
+
args: void 0,
|
|
131
|
+
argsResult: { issues }
|
|
132
|
+
};
|
|
133
|
+
return thenMaybe(validateCommandArgs(validateCtx.command, preprocessedArgs), (v) => v);
|
|
134
|
+
};
|
|
135
|
+
const validatedOrPromise = runInterceptorChain("validate", commandInterceptors, validateCtx, coreValidate);
|
|
136
|
+
const continueAfterValidate = (v) => {
|
|
137
|
+
pipelineState.phase = "validate";
|
|
138
|
+
pipelineState.args = v.args;
|
|
139
|
+
if (v.argsResult?.issues) return handleValidationIssues(v.argsResult, command, errorMode);
|
|
140
|
+
const executeCtx = {
|
|
141
|
+
...validateCtx,
|
|
142
|
+
args: v.args
|
|
143
|
+
};
|
|
144
|
+
const coreExecute = (executeCtx) => {
|
|
145
|
+
const handler = command.action ?? noop;
|
|
146
|
+
const actionCtx = {
|
|
147
|
+
runtime: executeCtx.runtime,
|
|
148
|
+
command: executeCtx.command,
|
|
149
|
+
program: ctx.builder,
|
|
150
|
+
signal: executeCtx.signal,
|
|
151
|
+
context: executeCtx.context,
|
|
152
|
+
caller
|
|
153
|
+
};
|
|
154
|
+
return { result: handler(executeCtx.args, actionCtx) };
|
|
155
|
+
};
|
|
156
|
+
pipelineState.phase = "execute";
|
|
157
|
+
return thenMaybe(runInterceptorChain("execute", commandInterceptors, executeCtx, coreExecute), (e) => {
|
|
158
|
+
const finalize = (result) => withDrain({
|
|
159
|
+
command,
|
|
160
|
+
args: v.args,
|
|
161
|
+
argsResult: v.argsResult,
|
|
162
|
+
result
|
|
163
|
+
});
|
|
164
|
+
if (e.result instanceof Promise) return e.result.then(finalize);
|
|
165
|
+
return finalize(e.result);
|
|
166
|
+
});
|
|
167
|
+
};
|
|
168
|
+
return thenMaybe(warnIfUnexpectedAsync(validatedOrPromise, command), continueAfterValidate);
|
|
169
|
+
};
|
|
170
|
+
return wrapWithCommandLifecycle(commandOnlyInterceptors, command, resolvedInput, runValidateAndExecute, (result) => withDrain({
|
|
171
|
+
command,
|
|
172
|
+
args: void 0,
|
|
173
|
+
argsResult: void 0,
|
|
174
|
+
result
|
|
175
|
+
}), signal, context, runtime, ctx.builder, caller, pipelineState);
|
|
176
|
+
};
|
|
177
|
+
return thenMaybe(routedOrPromise, continueAfterRoute);
|
|
178
|
+
};
|
|
179
|
+
return thenMaybe(parsedOrPromise, continueAfterParse);
|
|
180
|
+
};
|
|
181
|
+
return wrapWithLifecycle(rootInterceptors, rootCommand, resolvedInput, runPipeline, (result) => withDrain({
|
|
182
|
+
command: rootCommand,
|
|
183
|
+
args: void 0,
|
|
184
|
+
argsResult: void 0,
|
|
185
|
+
result
|
|
186
|
+
}), inertSignal, initialContext, runtime, ctx.builder, caller, pipelineState);
|
|
187
|
+
}
|
|
188
|
+
//#endregion
|
|
189
|
+
export { collectInterceptors, errorResultWithSignal, execCommand };
|
|
190
|
+
|
|
191
|
+
//# sourceMappingURL=exec.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exec.mjs","names":[],"sources":["../../src/core/exec.ts"],"sourcesContent":["import type { StandardSchemaV1 } from '@standard-schema/spec';\nimport type {\n AnyPadroneCommand,\n AnyPadroneProgram,\n InterceptorExecuteContext,\n InterceptorExecuteResult,\n InterceptorParseContext,\n InterceptorParseResult,\n InterceptorPipelinePhase,\n InterceptorRouteContext,\n InterceptorValidateContext,\n InterceptorValidateResult,\n PadroneActionContext,\n PadroneEvalPreferences,\n RegisteredInterceptor,\n ResolvedInterceptor,\n} from '../types/index.ts';\nimport { getCommandRuntime } from './commands.ts';\nimport { RoutingError, SignalError, ValidationError } from './errors.ts';\nimport { resolveRegisteredInterceptors, runInterceptorChain, wrapWithCommandLifecycle, wrapWithLifecycle } from './interceptors.ts';\nimport { errorResult, noop, thenMaybe, warnIfUnexpectedAsync, withDrain } from './results.ts';\nimport { buildCommandArgs, formatIssueMessages, validateCommandArgs } from './validate.ts';\n\nexport type ExecContext = {\n rootCommand: AnyPadroneCommand;\n builder: AnyPadroneProgram;\n parseCommandFn: (input: string | undefined) => {\n command: AnyPadroneCommand;\n rawArgs: Record<string, unknown>;\n args: string[];\n unmatchedTerms: string[];\n };\n collectInterceptorsFn: (cmd: AnyPadroneCommand) => RegisteredInterceptor[];\n};\n\n/**\n * Collects registered interceptors from the command's parent chain (root → ... → target).\n * Root/program interceptors come first (outermost), target command's interceptors last (innermost).\n */\nexport function collectInterceptors(cmd: AnyPadroneCommand, rootCommand: AnyPadroneCommand): RegisteredInterceptor[] {\n const chain: RegisteredInterceptor[][] = [];\n let current: AnyPadroneCommand | undefined = cmd;\n while (current) {\n const isTarget = current === cmd;\n if (!current.parent) {\n if (rootCommand.interceptors?.length) {\n const isRootTarget = cmd === rootCommand || !cmd.parent;\n chain.unshift(isRootTarget ? rootCommand.interceptors : rootCommand.interceptors.filter((i) => i.meta.inherit !== false));\n }\n } else {\n if (current.interceptors?.length) {\n chain.unshift(isTarget ? current.interceptors : current.interceptors.filter((i) => i.meta.inherit !== false));\n }\n }\n current = current.parent;\n }\n return chain.flat();\n}\n\n/** Wraps an error into a result, preserving any signal info from the pipeline. */\nexport function errorResultWithSignal(err: unknown) {\n const result = errorResult(err);\n if (err instanceof SignalError) {\n (result as any).signal = err.signal;\n (result as any).exitCode = err.exitCode;\n }\n return result;\n}\n\n/** Resolve context by walking the command parent chain and applying transforms from root to target. */\nfunction resolveContext(command: AnyPadroneCommand, initialContext: unknown): unknown {\n const chain: AnyPadroneCommand[] = [];\n let current: AnyPadroneCommand | undefined = command;\n while (current) {\n chain.unshift(current);\n current = current.parent;\n }\n let resolved = initialContext;\n for (const cmd of chain) {\n if (cmd.contextTransform) resolved = cmd.contextTransform(resolved);\n }\n return resolved;\n}\n\n/** Validate parse result — reject unmatched terms when the command doesn't accept positional args. */\nfunction validateParseResult(\n parseResult: { command: AnyPadroneCommand; rawArgs: Record<string, unknown>; args: string[]; unmatchedTerms: string[] },\n rootCommand: AnyPadroneCommand,\n): InterceptorParseResult {\n const { command, rawArgs, args, unmatchedTerms } = parseResult;\n\n if (unmatchedTerms.length > 0) {\n const hasPositionalConfig = command.meta?.positional && command.meta.positional.length > 0;\n if (!hasPositionalConfig) {\n const isRootCommand = command === rootCommand;\n const commandDisplayName = command.name || command.aliases?.[0] || command.path || '(default)';\n const errorMsg = isRootCommand\n ? `Unknown command: ${unmatchedTerms[0]}`\n : `Unexpected arguments for '${commandDisplayName}': ${unmatchedTerms.join(' ')}`;\n\n throw new RoutingError(errorMsg, { command: command.path || command.name });\n }\n }\n\n return { command, rawArgs, positionalArgs: args };\n}\n\n/** Handle validation issues based on error mode: throw (hard) or return result with issues (soft). */\nfunction handleValidationIssues(argsResult: StandardSchemaV1.FailureResult, command: AnyPadroneCommand, errorMode: 'soft' | 'hard') {\n if (errorMode === 'hard') {\n const issueMessages = formatIssueMessages(argsResult.issues);\n throw new ValidationError(`Validation error:\\n${issueMessages}`, argsResult.issues as any, {\n command: command.path || command.name,\n });\n }\n\n return withDrain({\n command: command as any,\n args: undefined,\n argsResult,\n result: undefined,\n });\n}\n\n/**\n * Core execution logic shared by eval() and cli().\n * errorMode controls validation error behavior:\n * - 'soft': return result with issues (eval behavior)\n * - 'hard': print error + help and throw (cli-without-input behavior)\n */\nexport function execCommand(\n resolvedInput: string | undefined,\n ctx: ExecContext,\n evalOptions?: PadroneEvalPreferences,\n errorMode: 'soft' | 'hard' = 'soft',\n caller: PadroneActionContext['caller'] = 'eval',\n) {\n const { rootCommand, parseCommandFn, collectInterceptorsFn } = ctx;\n const baseRuntime = getCommandRuntime(rootCommand);\n const runtime = evalOptions?.runtime\n ? Object.assign({}, baseRuntime, Object.fromEntries(Object.entries(evalOptions.runtime).filter(([, v]) => v !== undefined)))\n : baseRuntime;\n\n // Inert signal — the signal extension overrides this via next({ signal }) in the start phase.\n const inertSignal = new AbortController().signal;\n\n // Pipeline state accumulated as phases complete — propagated to error/shutdown contexts.\n const pipelineState: { phase: InterceptorPipelinePhase; rawArgs?: Record<string, unknown>; positionalArgs?: string[]; args?: unknown } = {\n phase: 'start',\n };\n\n const initialContext = evalOptions?.context;\n\n // Factory resolution cache — ensures each factory is called at most once per execution,\n // so root interceptor closures are shared when they appear in both root and command chains.\n const factoryCache = new Map<RegisteredInterceptor, ResolvedInterceptor>();\n const rootRegistered = rootCommand.interceptors ?? [];\n const rootInterceptors = resolveRegisteredInterceptors(rootRegistered, factoryCache);\n const rootInterceptorSet = new Set(rootInterceptors);\n\n const runPipeline = (signal: AbortSignal, pipelineContext: unknown) => {\n // ── Phase 1: Parse ──────────────────────────────────────────────────\n const parseCtx: InterceptorParseContext = {\n input: resolvedInput,\n command: rootCommand,\n signal,\n context: pipelineContext as object,\n runtime,\n program: ctx.builder,\n caller,\n };\n\n const coreParse = (parseCtx: InterceptorParseContext): InterceptorParseResult =>\n validateParseResult(parseCommandFn(parseCtx.input), rootCommand);\n\n const parsedOrPromise = runInterceptorChain('parse', rootInterceptors, parseCtx, coreParse);\n\n // ── Phases 2 & 3 chained after parse ────────────────────────────────\n const continueAfterParse = (parsed: InterceptorParseResult) => {\n const { command } = parsed;\n pipelineState.phase = 'parse';\n pipelineState.rawArgs = parsed.rawArgs;\n pipelineState.positionalArgs = parsed.positionalArgs;\n const commandInterceptors = resolveRegisteredInterceptors(collectInterceptorsFn(command), factoryCache);\n const commandOnlyInterceptors = commandInterceptors.filter((i) => !rootInterceptorSet.has(i));\n const context = resolveContext(command, pipelineContext);\n\n // ── Phase 2: Route ──────────────────────────────────────────────\n const routeCtx: InterceptorRouteContext = {\n ...parseCtx,\n command,\n rawArgs: parsed.rawArgs,\n positionalArgs: parsed.positionalArgs,\n context: context as object,\n };\n\n const routedOrPromise = runInterceptorChain('route', commandInterceptors, routeCtx, () => {});\n\n const continueAfterRoute = () => {\n pipelineState.phase = 'route';\n const runValidateAndExecute = () => {\n // ── Phase 3: Validate ───────────────────────────────────────────\n const validateCtx: InterceptorValidateContext = {\n ...parseCtx,\n command,\n rawArgs: parsed.rawArgs,\n positionalArgs: parsed.positionalArgs,\n context: context as object,\n evalInteractive: evalOptions?.interactive,\n };\n\n const coreValidate = (\n validateCtx: InterceptorValidateContext,\n ): InterceptorValidateResult | Promise<InterceptorValidateResult> => {\n const { args: preprocessedArgs, issues } = buildCommandArgs(\n validateCtx.command,\n validateCtx.rawArgs,\n validateCtx.positionalArgs,\n );\n if (issues) return { args: undefined, argsResult: { issues } as any };\n const validated = validateCommandArgs(validateCtx.command, preprocessedArgs);\n return thenMaybe(validated, (v) => v as InterceptorValidateResult);\n };\n\n const validatedOrPromise = runInterceptorChain('validate', commandInterceptors, validateCtx, coreValidate);\n\n // ── Phase 3: Execute (or handle validation errors) ──────────────\n const continueAfterValidate = (v: InterceptorValidateResult) => {\n pipelineState.phase = 'validate';\n pipelineState.args = v.args;\n if (v.argsResult?.issues) return handleValidationIssues(v.argsResult as StandardSchemaV1.FailureResult, command, errorMode);\n\n const executeCtx: InterceptorExecuteContext = {\n ...validateCtx,\n args: v.args,\n };\n\n const coreExecute = (executeCtx: InterceptorExecuteContext): InterceptorExecuteResult => {\n const handler = command.action ?? noop;\n const effectiveRuntime = executeCtx.runtime;\n const actionCtx: PadroneActionContext = {\n runtime: effectiveRuntime,\n command: executeCtx.command,\n program: ctx.builder as any,\n signal: executeCtx.signal,\n context: executeCtx.context,\n caller,\n };\n const result = handler(executeCtx.args as any, actionCtx);\n return { result };\n };\n\n pipelineState.phase = 'execute';\n const executedOrPromise = runInterceptorChain('execute', commandInterceptors, executeCtx, coreExecute);\n\n return thenMaybe(executedOrPromise, (e) => {\n const finalize = (result: unknown) =>\n withDrain({\n command: command as any,\n args: v.args,\n argsResult: v.argsResult,\n result,\n });\n\n if (e.result instanceof Promise) return e.result.then(finalize);\n return finalize(e.result);\n });\n };\n\n return thenMaybe(warnIfUnexpectedAsync(validatedOrPromise, command), continueAfterValidate) as any;\n };\n\n return wrapWithCommandLifecycle(\n commandOnlyInterceptors,\n command,\n resolvedInput,\n runValidateAndExecute,\n (result) => withDrain({ command: command as any, args: undefined, argsResult: undefined, result }),\n signal,\n context,\n runtime,\n ctx.builder,\n caller,\n pipelineState,\n );\n };\n\n return thenMaybe(routedOrPromise, continueAfterRoute) as any;\n };\n\n return thenMaybe(parsedOrPromise, continueAfterParse) as any;\n };\n\n return wrapWithLifecycle(\n rootInterceptors,\n rootCommand,\n resolvedInput,\n runPipeline,\n (result) => withDrain({ command: rootCommand, args: undefined, argsResult: undefined, result }),\n inertSignal,\n initialContext,\n runtime,\n ctx.builder,\n caller,\n pipelineState,\n ) as any;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { InterceptorDefBuilder, InterceptorFactory, InterceptorMeta, PadroneInterceptorFn } from "../types/interceptor.mjs";
|
|
2
|
+
//#region src/core/interceptors.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Creates a self-contained interceptor value by attaching static metadata to the factory function.
|
|
5
|
+
* The returned value can be passed directly to `.intercept()` or exported from a package.
|
|
6
|
+
*
|
|
7
|
+
* Two-arg form — define metadata and factory in one call:
|
|
8
|
+
* ```ts
|
|
9
|
+
* export const myInterceptor = defineInterceptor(
|
|
10
|
+
* { name: 'my-interceptor', order: 10 },
|
|
11
|
+
* () => ({
|
|
12
|
+
* execute(ctx, next) { return next(); },
|
|
13
|
+
* }),
|
|
14
|
+
* );
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* Single-arg form — chain `.requires<T>()` for typed context, then `.factory()`:
|
|
18
|
+
* ```ts
|
|
19
|
+
* export const myInterceptor = defineInterceptor({ name: 'with-db' })
|
|
20
|
+
* .requires<{ db: DB }>()
|
|
21
|
+
* .factory(() => ({
|
|
22
|
+
* execute(ctx, next) {
|
|
23
|
+
* ctx.context.db; // typed!
|
|
24
|
+
* return next();
|
|
25
|
+
* },
|
|
26
|
+
* }));
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
declare function defineInterceptor<TArgs = unknown, TResult = unknown>(meta: InterceptorMeta, factory: InterceptorFactory<TArgs, TResult>): PadroneInterceptorFn<TArgs, TResult>;
|
|
30
|
+
declare function defineInterceptor(meta: InterceptorMeta): InterceptorDefBuilder;
|
|
31
|
+
//#endregion
|
|
32
|
+
export { defineInterceptor };
|
|
33
|
+
//# sourceMappingURL=interceptors.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interceptors.d.mts","names":[],"sources":["../../src/core/interceptors.ts"],"mappings":";;;;AA2DA;;;;;;;;;;;;;;;;;;;;;;;;iBAAgB,iBAAA,qCACd,IAAA,EAAM,eAAA,EACN,OAAA,EAAS,kBAAA,CAAmB,KAAA,EAAO,OAAA,IAClC,oBAAA,CAAqB,KAAA,EAAO,OAAA;AAAA,iBACf,iBAAA,CAAkB,IAAA,EAAM,eAAA,GAAkB,qBAAqB"}
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import { thenMaybe } from "./results.mjs";
|
|
2
|
+
//#region src/core/interceptors.ts
|
|
3
|
+
function buildInterceptorFn(meta, factory) {
|
|
4
|
+
Object.defineProperty(factory, "name", {
|
|
5
|
+
value: meta.name,
|
|
6
|
+
configurable: true
|
|
7
|
+
});
|
|
8
|
+
if (meta.id !== void 0) factory.id = meta.id;
|
|
9
|
+
if (meta.order !== void 0) factory.order = meta.order;
|
|
10
|
+
if (meta.disabled !== void 0) factory.disabled = meta.disabled;
|
|
11
|
+
if (meta.inherit !== void 0) factory.inherit = meta.inherit;
|
|
12
|
+
factory.provides = () => factory;
|
|
13
|
+
factory.requires = () => factory;
|
|
14
|
+
return factory;
|
|
15
|
+
}
|
|
16
|
+
function defineInterceptor(meta, factory) {
|
|
17
|
+
if (factory) return buildInterceptorFn(meta, factory);
|
|
18
|
+
const builder = {
|
|
19
|
+
requires: () => builder,
|
|
20
|
+
factory: (f) => buildInterceptorFn(meta, f)
|
|
21
|
+
};
|
|
22
|
+
return builder;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Normalizes an interceptor input (single-value form or two-arg form) into the internal
|
|
26
|
+
* `RegisteredInterceptor` storage format.
|
|
27
|
+
*/
|
|
28
|
+
function toRegisteredInterceptor(metaOrFn, factory) {
|
|
29
|
+
if (typeof metaOrFn === "function") return {
|
|
30
|
+
meta: {
|
|
31
|
+
name: metaOrFn.name,
|
|
32
|
+
id: metaOrFn.id,
|
|
33
|
+
order: metaOrFn.order,
|
|
34
|
+
disabled: metaOrFn.disabled,
|
|
35
|
+
inherit: metaOrFn.inherit
|
|
36
|
+
},
|
|
37
|
+
factory: metaOrFn
|
|
38
|
+
};
|
|
39
|
+
return {
|
|
40
|
+
meta: metaOrFn,
|
|
41
|
+
factory
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Resolves registered interceptors by calling their factories and merging the resulting
|
|
46
|
+
* phase handlers with the static metadata. Uses a cache to ensure each factory is called
|
|
47
|
+
* at most once per execution (so root interceptor closures are shared across all phases).
|
|
48
|
+
*/
|
|
49
|
+
function resolveRegisteredInterceptors(registered, cache) {
|
|
50
|
+
return registered.map((reg) => {
|
|
51
|
+
let resolved = cache.get(reg);
|
|
52
|
+
if (!resolved) {
|
|
53
|
+
resolved = {
|
|
54
|
+
...reg.meta,
|
|
55
|
+
...reg.factory()
|
|
56
|
+
};
|
|
57
|
+
cache.set(reg, resolved);
|
|
58
|
+
}
|
|
59
|
+
return resolved;
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Deduplicates interceptors by `id`. When multiple interceptors share the same `id`,
|
|
64
|
+
* only the last one in the array is kept. Interceptors without an `id` are always kept.
|
|
65
|
+
*/
|
|
66
|
+
function deduplicateInterceptors(interceptors) {
|
|
67
|
+
if (!interceptors.some((p) => p.id)) return interceptors;
|
|
68
|
+
const lastIndex = /* @__PURE__ */ new Map();
|
|
69
|
+
for (let i = 0; i < interceptors.length; i++) {
|
|
70
|
+
const id = interceptors[i].id;
|
|
71
|
+
if (id) lastIndex.set(id, i);
|
|
72
|
+
}
|
|
73
|
+
return interceptors.filter((p, i) => !p.id || lastIndex.get(p.id) === i);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Runs an interceptor chain for a given phase using the onion/middleware pattern.
|
|
77
|
+
* Interceptors are sorted by `order` (ascending, stable), then composed so that
|
|
78
|
+
* the first interceptor in sorted order is the outermost wrapper.
|
|
79
|
+
* If no interceptors handle this phase, `core` is called directly.
|
|
80
|
+
*
|
|
81
|
+
* Each interceptor's `next()` accepts optional partial overrides that are merged
|
|
82
|
+
* into the context before passing to the next interceptor or core function.
|
|
83
|
+
*/
|
|
84
|
+
function runInterceptorChain(phase, interceptors, ctx, core) {
|
|
85
|
+
const phaseInterceptors = deduplicateInterceptors(interceptors).filter((p) => p[phase] && !p.disabled);
|
|
86
|
+
if (phaseInterceptors.length === 0) return core(ctx);
|
|
87
|
+
phaseInterceptors.sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
|
|
88
|
+
let next = core;
|
|
89
|
+
for (let i = phaseInterceptors.length - 1; i >= 0; i--) {
|
|
90
|
+
const handler = phaseInterceptors[i][phase];
|
|
91
|
+
const prevNext = next;
|
|
92
|
+
next = (currentCtx) => handler(currentCtx, (overrides) => {
|
|
93
|
+
if (!overrides) return prevNext(currentCtx);
|
|
94
|
+
if (overrides.context != null && typeof overrides.context === "object") overrides = {
|
|
95
|
+
...overrides,
|
|
96
|
+
context: Object.assign({}, currentCtx.context, overrides.context)
|
|
97
|
+
};
|
|
98
|
+
return prevNext(Object.assign({}, currentCtx, overrides));
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
return next(ctx);
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Wraps a pipeline with start → error → shutdown lifecycle hooks.
|
|
105
|
+
* - `start` interceptors wrap the pipeline (onion pattern, root interceptors only).
|
|
106
|
+
* - On error: `error` interceptors run (can transform/suppress the error).
|
|
107
|
+
* - Always: `shutdown` interceptors run (success or failure).
|
|
108
|
+
*/
|
|
109
|
+
function wrapWithLifecycle(interceptors, command, input, pipeline, wrapErrorResult, signal, context, runtime, program, caller = "eval", pipelineState) {
|
|
110
|
+
const defaultSignal = typeof AbortSignal !== "undefined" ? AbortSignal.abort() : void 0;
|
|
111
|
+
const hasStart = interceptors.some((p) => p.start);
|
|
112
|
+
const hasError = interceptors.some((p) => p.error);
|
|
113
|
+
const hasShutdown = interceptors.some((p) => p.shutdown);
|
|
114
|
+
const effectivePipelineState = pipelineState ?? { phase: "start" };
|
|
115
|
+
if (!hasStart && !hasError && !hasShutdown) return pipeline(signal ?? defaultSignal, context);
|
|
116
|
+
let effectiveSignal = signal ?? defaultSignal;
|
|
117
|
+
let effectiveContext = context;
|
|
118
|
+
const runShutdown = (error, result) => {
|
|
119
|
+
if (!hasShutdown) return;
|
|
120
|
+
return runInterceptorChain("shutdown", interceptors, {
|
|
121
|
+
command,
|
|
122
|
+
input,
|
|
123
|
+
error,
|
|
124
|
+
result,
|
|
125
|
+
signal: effectiveSignal,
|
|
126
|
+
context: effectiveContext,
|
|
127
|
+
runtime,
|
|
128
|
+
program,
|
|
129
|
+
caller,
|
|
130
|
+
...effectivePipelineState
|
|
131
|
+
}, () => {});
|
|
132
|
+
};
|
|
133
|
+
const runError = (error) => {
|
|
134
|
+
if (!hasError) {
|
|
135
|
+
const s = runShutdown(error);
|
|
136
|
+
if (s instanceof Promise) return s.then(() => {
|
|
137
|
+
throw error;
|
|
138
|
+
});
|
|
139
|
+
throw error;
|
|
140
|
+
}
|
|
141
|
+
return thenMaybe(runInterceptorChain("error", interceptors, {
|
|
142
|
+
command,
|
|
143
|
+
input,
|
|
144
|
+
error,
|
|
145
|
+
signal: effectiveSignal,
|
|
146
|
+
context: effectiveContext,
|
|
147
|
+
runtime,
|
|
148
|
+
program,
|
|
149
|
+
caller,
|
|
150
|
+
...effectivePipelineState
|
|
151
|
+
}, () => ({ error })), (er) => {
|
|
152
|
+
if (er.error !== void 0) return thenMaybe(runShutdown(er.error), () => {
|
|
153
|
+
throw er.error;
|
|
154
|
+
});
|
|
155
|
+
const wrapped = wrapErrorResult ? wrapErrorResult(er.result) : er.result;
|
|
156
|
+
return thenMaybe(runShutdown(void 0, wrapped), () => wrapped);
|
|
157
|
+
});
|
|
158
|
+
};
|
|
159
|
+
const handleSuccess = (result) => {
|
|
160
|
+
const s = runShutdown(void 0, result);
|
|
161
|
+
if (s instanceof Promise) return s.then(() => result);
|
|
162
|
+
return result;
|
|
163
|
+
};
|
|
164
|
+
const startCtx = {
|
|
165
|
+
command,
|
|
166
|
+
signal: effectiveSignal,
|
|
167
|
+
context: effectiveContext,
|
|
168
|
+
runtime,
|
|
169
|
+
program,
|
|
170
|
+
input,
|
|
171
|
+
caller
|
|
172
|
+
};
|
|
173
|
+
let result;
|
|
174
|
+
try {
|
|
175
|
+
result = hasStart ? runInterceptorChain("start", interceptors, startCtx, (ctx) => {
|
|
176
|
+
effectiveSignal = ctx.signal;
|
|
177
|
+
effectiveContext = ctx.context;
|
|
178
|
+
return pipeline(ctx.signal, ctx.context);
|
|
179
|
+
}) : pipeline(effectiveSignal, effectiveContext);
|
|
180
|
+
} catch (e) {
|
|
181
|
+
return runError(e);
|
|
182
|
+
}
|
|
183
|
+
if (result instanceof Promise) return result.then(handleSuccess, runError);
|
|
184
|
+
return handleSuccess(result);
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Wraps a command-level pipeline (validate + execute) with error → shutdown lifecycle hooks.
|
|
188
|
+
* Unlike `wrapWithLifecycle`, this has no `start` phase and uses the resolved command context.
|
|
189
|
+
* Only interceptors exclusive to the command chain (not in root) should be passed here.
|
|
190
|
+
*/
|
|
191
|
+
function wrapWithCommandLifecycle(interceptors, command, input, pipeline, wrapErrorResult, signal, context, runtime, program, caller, pipelineState) {
|
|
192
|
+
const hasError = interceptors.some((p) => p.error);
|
|
193
|
+
const hasShutdown = interceptors.some((p) => p.shutdown);
|
|
194
|
+
if (!hasError && !hasShutdown) return pipeline();
|
|
195
|
+
const runShutdown = (error, result) => {
|
|
196
|
+
if (!hasShutdown) return;
|
|
197
|
+
return runInterceptorChain("shutdown", interceptors, {
|
|
198
|
+
command,
|
|
199
|
+
input,
|
|
200
|
+
error,
|
|
201
|
+
result,
|
|
202
|
+
signal,
|
|
203
|
+
context,
|
|
204
|
+
runtime,
|
|
205
|
+
program,
|
|
206
|
+
caller,
|
|
207
|
+
...pipelineState
|
|
208
|
+
}, () => {});
|
|
209
|
+
};
|
|
210
|
+
const runError = (error) => {
|
|
211
|
+
if (!hasError) {
|
|
212
|
+
const s = runShutdown(error);
|
|
213
|
+
if (s instanceof Promise) return s.then(() => {
|
|
214
|
+
throw error;
|
|
215
|
+
});
|
|
216
|
+
throw error;
|
|
217
|
+
}
|
|
218
|
+
return thenMaybe(runInterceptorChain("error", interceptors, {
|
|
219
|
+
command,
|
|
220
|
+
input,
|
|
221
|
+
error,
|
|
222
|
+
signal,
|
|
223
|
+
context,
|
|
224
|
+
runtime,
|
|
225
|
+
program,
|
|
226
|
+
caller,
|
|
227
|
+
...pipelineState
|
|
228
|
+
}, () => ({ error })), (er) => {
|
|
229
|
+
if (er.error !== void 0) return thenMaybe(runShutdown(er.error), () => {
|
|
230
|
+
throw er.error;
|
|
231
|
+
});
|
|
232
|
+
const wrapped = wrapErrorResult ? wrapErrorResult(er.result) : er.result;
|
|
233
|
+
return thenMaybe(runShutdown(void 0, wrapped), () => wrapped);
|
|
234
|
+
});
|
|
235
|
+
};
|
|
236
|
+
const handleSuccess = (result) => {
|
|
237
|
+
const s = runShutdown(void 0, result);
|
|
238
|
+
if (s instanceof Promise) return s.then(() => result);
|
|
239
|
+
return result;
|
|
240
|
+
};
|
|
241
|
+
let result;
|
|
242
|
+
try {
|
|
243
|
+
result = pipeline();
|
|
244
|
+
} catch (e) {
|
|
245
|
+
return runError(e);
|
|
246
|
+
}
|
|
247
|
+
if (result instanceof Promise) return result.then(handleSuccess, runError);
|
|
248
|
+
return handleSuccess(result);
|
|
249
|
+
}
|
|
250
|
+
//#endregion
|
|
251
|
+
export { defineInterceptor, resolveRegisteredInterceptors, runInterceptorChain, toRegisteredInterceptor, wrapWithCommandLifecycle, wrapWithLifecycle };
|
|
252
|
+
|
|
253
|
+
//# sourceMappingURL=interceptors.mjs.map
|