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 @@
|
|
|
1
|
+
{"version":3,"file":"stdin.mjs","names":[],"sources":["../../src/extension/stdin.ts"],"sourcesContent":["import { applyValues, isArrayField, isAsyncStreamField } from '../core/args.ts';\nimport { resolveStdin, resolveStdinAlways } from '../core/default-runtime.ts';\nimport { defineInterceptor } from '../core/interceptors.ts';\nimport type { AnyPadroneBuilder, CommandTypesBase, InterceptorValidateContext } from '../types/index.ts';\nimport { createStdinStream } from '../util/stream.ts';\n\n// ── Interceptor ─────────────────────────────────────────────────────────\n\nconst stdinInterceptor = defineInterceptor({ id: 'padrone:stdin', name: 'padrone:stdin', order: -1001 }, () => ({\n validate(ctx: InterceptorValidateContext, next) {\n const stdinField = ctx.command.meta?.stdin;\n if (!stdinField) return next();\n\n // Skip if the field was already provided via CLI flags\n if (stdinField in ctx.rawArgs && ctx.rawArgs[stdinField] !== undefined) return next();\n\n const streamInfo = isAsyncStreamField(ctx.command.argsSchema, stdinField);\n if (streamInfo) {\n const stdinForStream = resolveStdinAlways(ctx.runtime as any);\n const mergedRawArgs = applyValues(ctx.rawArgs, { [stdinField]: createStdinStream(stdinForStream, streamInfo.itemSchema) });\n return next({ rawArgs: mergedRawArgs });\n }\n\n const stdin = resolveStdin(ctx.runtime as any);\n if (!stdin) return next();\n\n if (isArrayField(ctx.command.argsSchema, stdinField)) {\n return (async () => {\n const lines: string[] = [];\n for await (const line of stdin.lines()) {\n lines.push(line);\n }\n const mergedRawArgs = applyValues(ctx.rawArgs, { [stdinField]: lines });\n return next({ rawArgs: mergedRawArgs });\n })();\n }\n\n return stdin.text().then((text) => {\n if (!text) return next();\n const mergedRawArgs = applyValues(ctx.rawArgs, { [stdinField]: text });\n return next({ rawArgs: mergedRawArgs });\n });\n },\n}));\n\n// ── Extension ────────────────────────────────────────────────────────────\n\n/**\n * Extension that reads stdin data into the argument field specified by `meta.stdin`.\n * Included by default via `createPadrone()`.\n *\n * Read mode is inferred from the schema type:\n * - `string` field → reads all stdin as a single string\n * - `string[]` field → reads stdin line-by-line into an array\n * - `AsyncIterable` field → returns a stream for line-by-line async consumption\n *\n * Stdin is only read when piped (not a TTY) and the field wasn't already provided via CLI flags.\n */\nexport function padroneStdin(options?: { disabled?: boolean }): <T extends CommandTypesBase>(builder: T) => T {\n const interceptor = options?.disabled ? defineInterceptor({ ...stdinInterceptor, disabled: true }, () => ({})) : stdinInterceptor;\n return ((builder: AnyPadroneBuilder) => builder.intercept(interceptor)) as any;\n}\n"],"mappings":";;;;;AAQA,MAAM,mBAAmB,kBAAkB;CAAE,IAAI;CAAiB,MAAM;CAAiB,OAAO;AAAM,UAAU,EAC9G,SAAS,KAAiC,MAAM;CAC9C,MAAM,aAAa,IAAI,QAAQ,MAAM;CACrC,IAAI,CAAC,YAAY,OAAO,KAAK;CAG7B,IAAI,cAAc,IAAI,WAAW,IAAI,QAAQ,gBAAgB,KAAA,GAAW,OAAO,KAAK;CAEpF,MAAM,aAAa,mBAAmB,IAAI,QAAQ,YAAY,UAAU;CACxE,IAAI,YAAY;EACd,MAAM,iBAAiB,mBAAmB,IAAI,OAAc;EAE5D,OAAO,KAAK,EAAE,SADQ,YAAY,IAAI,SAAS,GAAG,aAAa,kBAAkB,gBAAgB,WAAW,UAAU,EAAE,CACrF,EAAE,CAAC;CACxC;CAEA,MAAM,QAAQ,aAAa,IAAI,OAAc;CAC7C,IAAI,CAAC,OAAO,OAAO,KAAK;CAExB,IAAI,aAAa,IAAI,QAAQ,YAAY,UAAU,GACjD,QAAQ,YAAY;EAClB,MAAM,QAAkB,CAAC;EACzB,WAAW,MAAM,QAAQ,MAAM,MAAM,GACnC,MAAM,KAAK,IAAI;EAGjB,OAAO,KAAK,EAAE,SADQ,YAAY,IAAI,SAAS,GAAG,aAAa,MAAM,CAClC,EAAE,CAAC;CACxC,EAAA,CAAG;CAGL,OAAO,MAAM,KAAK,CAAC,CAAC,MAAM,SAAS;EACjC,IAAI,CAAC,MAAM,OAAO,KAAK;EAEvB,OAAO,KAAK,EAAE,SADQ,YAAY,IAAI,SAAS,GAAG,aAAa,KAAK,CACjC,EAAE,CAAC;CACxC,CAAC;AACH,EACF,EAAE;;;;;;;;;;;;AAeF,SAAgB,aAAa,SAAiF;CAC5G,MAAM,cAAc,SAAS,WAAW,kBAAkB;EAAE,GAAG;EAAkB,UAAU;CAAK,UAAU,CAAC,EAAE,IAAI;CACjH,SAAS,YAA+B,QAAQ,UAAU,WAAW;AACvE"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { CommandTypesBase } from "../types/command.mjs";
|
|
2
|
+
//#region src/extension/suggestions.d.ts
|
|
3
|
+
declare function padroneSuggestions(): <T extends CommandTypesBase>(builder: T) => T;
|
|
4
|
+
//#endregion
|
|
5
|
+
export { padroneSuggestions };
|
|
6
|
+
//# sourceMappingURL=suggestions.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"suggestions.d.mts","names":[],"sources":["../../src/extension/suggestions.ts"],"mappings":";;iBA+GgB,kBAAA,eAAiC,gBAAA,EAAkB,OAAA,EAAS,CAAA,KAAM,CAAA"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { resolveCommand, suggestSimilar } from "../core/commands.mjs";
|
|
2
|
+
import { thenMaybe } from "../core/results.mjs";
|
|
3
|
+
import { defineInterceptor } from "../core/interceptors.mjs";
|
|
4
|
+
import { RoutingError } from "../core/errors.mjs";
|
|
5
|
+
import { getKnownOptionNames } from "../core/validate.mjs";
|
|
6
|
+
//#region src/extension/suggestions.ts
|
|
7
|
+
function formatSuggestions(names, prefix = "") {
|
|
8
|
+
if (names.length === 0) return "";
|
|
9
|
+
const quoted = names.map((n) => `"${prefix}${n}"`);
|
|
10
|
+
if (quoted.length === 1) return `Did you mean ${quoted[0]}?`;
|
|
11
|
+
return `Did you mean ${quoted.slice(0, -1).join(", ")} or ${quoted.at(-1)}?`;
|
|
12
|
+
}
|
|
13
|
+
function findSourceCommand(commandPath, root) {
|
|
14
|
+
if (!commandPath || commandPath === root.name || commandPath === root.path) return root;
|
|
15
|
+
const parts = commandPath.split(" ");
|
|
16
|
+
let current = root;
|
|
17
|
+
for (const part of parts) {
|
|
18
|
+
const found = current.commands?.find((c) => {
|
|
19
|
+
resolveCommand(c);
|
|
20
|
+
return c.name === part || c.aliases?.includes(part);
|
|
21
|
+
});
|
|
22
|
+
if (found) current = found;
|
|
23
|
+
else break;
|
|
24
|
+
}
|
|
25
|
+
return current;
|
|
26
|
+
}
|
|
27
|
+
function enrichRoutingError(err, rootCommand) {
|
|
28
|
+
if (!(err instanceof RoutingError)) return err;
|
|
29
|
+
const unknownMatch = err.message.match(/^Unknown command: (\S+)/);
|
|
30
|
+
const unexpectedMatch = err.message.match(/^Unexpected arguments for '[^']+': (\S+)/);
|
|
31
|
+
const term = unknownMatch?.[1] ?? unexpectedMatch?.[1];
|
|
32
|
+
if (!term) return err;
|
|
33
|
+
const sourceCmd = findSourceCommand(err.command, rootCommand);
|
|
34
|
+
const candidateNames = [];
|
|
35
|
+
if (sourceCmd.commands) for (const cmd of sourceCmd.commands) {
|
|
36
|
+
resolveCommand(cmd);
|
|
37
|
+
if (!cmd.hidden) {
|
|
38
|
+
candidateNames.push(cmd.name);
|
|
39
|
+
if (cmd.aliases) candidateNames.push(...cmd.aliases);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
const suggestionText = formatSuggestions(suggestSimilar(term, candidateNames));
|
|
43
|
+
if (!suggestionText) return err;
|
|
44
|
+
const suggestions = [suggestionText];
|
|
45
|
+
return new RoutingError(`${err.message}\n\n ${suggestionText}`, {
|
|
46
|
+
suggestions,
|
|
47
|
+
command: err.command
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
function enrichIssuesWithSuggestions(issues, knownOptions) {
|
|
51
|
+
return issues.map((i) => {
|
|
52
|
+
const unknownMatch = i.message?.match(/^Unknown option: "([^"]+)"$/);
|
|
53
|
+
if (unknownMatch) {
|
|
54
|
+
const similar = suggestSimilar(unknownMatch[1], knownOptions());
|
|
55
|
+
if (similar.length) {
|
|
56
|
+
const hint = formatSuggestions(similar, "--");
|
|
57
|
+
return {
|
|
58
|
+
...i,
|
|
59
|
+
message: `${i.message} ${hint}`
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
return i;
|
|
63
|
+
}
|
|
64
|
+
const keys = i.keys ?? i.message?.match(/[Uu]nrecognized key(?:s)?[^"]*"([^"]+)"/)?.slice(1);
|
|
65
|
+
if (!keys?.length) return i;
|
|
66
|
+
const hints = keys.flatMap((k) => {
|
|
67
|
+
const similar = suggestSimilar(k, knownOptions());
|
|
68
|
+
return similar.length ? [formatSuggestions(similar, "--")] : [];
|
|
69
|
+
});
|
|
70
|
+
if (!hints.length) return i;
|
|
71
|
+
return {
|
|
72
|
+
...i,
|
|
73
|
+
message: `${i.message} ${hints.join(" ")}`
|
|
74
|
+
};
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
const suggestionsInterceptor = defineInterceptor({
|
|
78
|
+
id: "padrone:suggestions",
|
|
79
|
+
name: "padrone:suggestions",
|
|
80
|
+
order: -500
|
|
81
|
+
}, () => ({
|
|
82
|
+
parse(ctx, next) {
|
|
83
|
+
try {
|
|
84
|
+
const result = next();
|
|
85
|
+
if (result instanceof Promise) return result.catch((err) => {
|
|
86
|
+
throw enrichRoutingError(err, ctx.command);
|
|
87
|
+
});
|
|
88
|
+
return result;
|
|
89
|
+
} catch (err) {
|
|
90
|
+
throw enrichRoutingError(err, ctx.command);
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
validate(ctx, next) {
|
|
94
|
+
return thenMaybe(next(), (v) => {
|
|
95
|
+
if (!v.argsResult?.issues?.length) return v;
|
|
96
|
+
const enriched = enrichIssuesWithSuggestions(v.argsResult.issues, () => getKnownOptionNames(ctx.command));
|
|
97
|
+
return {
|
|
98
|
+
...v,
|
|
99
|
+
argsResult: {
|
|
100
|
+
...v.argsResult,
|
|
101
|
+
issues: enriched
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}));
|
|
107
|
+
function padroneSuggestions() {
|
|
108
|
+
return ((builder) => builder.intercept(suggestionsInterceptor));
|
|
109
|
+
}
|
|
110
|
+
//#endregion
|
|
111
|
+
export { padroneSuggestions };
|
|
112
|
+
|
|
113
|
+
//# sourceMappingURL=suggestions.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"suggestions.mjs","names":[],"sources":["../../src/extension/suggestions.ts"],"sourcesContent":["import { resolveCommand, suggestSimilar } from '../core/commands.ts';\nimport { RoutingError } from '../core/errors.ts';\nimport { defineInterceptor } from '../core/interceptors.ts';\nimport { thenMaybe } from '../core/results.ts';\nimport { getKnownOptionNames } from '../core/validate.ts';\nimport type { AnyPadroneBuilder, AnyPadroneCommand, CommandTypesBase } from '../types/index.ts';\n\nfunction formatSuggestions(names: string[], prefix = ''): string {\n if (names.length === 0) return '';\n const quoted = names.map((n) => `\"${prefix}${n}\"`);\n if (quoted.length === 1) return `Did you mean ${quoted[0]}?`;\n return `Did you mean ${quoted.slice(0, -1).join(', ')} or ${quoted.at(-1)}?`;\n}\n\nfunction findSourceCommand(commandPath: string | undefined, root: AnyPadroneCommand): AnyPadroneCommand {\n if (!commandPath || commandPath === root.name || commandPath === root.path) return root;\n const parts = commandPath.split(' ');\n let current = root;\n for (const part of parts) {\n const found = current.commands?.find((c) => {\n resolveCommand(c);\n return c.name === part || c.aliases?.includes(part);\n });\n if (found) current = found;\n else break;\n }\n return current;\n}\n\nfunction enrichRoutingError(err: unknown, rootCommand: AnyPadroneCommand): unknown {\n if (!(err instanceof RoutingError)) return err;\n\n const unknownMatch = err.message.match(/^Unknown command: (\\S+)/);\n const unexpectedMatch = err.message.match(/^Unexpected arguments for '[^']+': (\\S+)/);\n const term = unknownMatch?.[1] ?? unexpectedMatch?.[1];\n if (!term) return err;\n\n const sourceCmd = findSourceCommand(err.command, rootCommand);\n\n const candidateNames: string[] = [];\n if (sourceCmd.commands) {\n for (const cmd of sourceCmd.commands) {\n resolveCommand(cmd);\n if (!cmd.hidden) {\n candidateNames.push(cmd.name);\n if (cmd.aliases) candidateNames.push(...cmd.aliases);\n }\n }\n }\n\n const similar = suggestSimilar(term, candidateNames);\n const suggestionText = formatSuggestions(similar);\n if (!suggestionText) return err;\n\n const suggestions = [suggestionText];\n const enrichedMsg = `${err.message}\\n\\n ${suggestionText}`;\n return new RoutingError(enrichedMsg, { suggestions, command: err.command });\n}\n\nfunction enrichIssuesWithSuggestions(\n issues: readonly { path?: readonly unknown[]; message: string }[],\n knownOptions: () => string[],\n): typeof issues {\n return issues.map((i: any) => {\n // Handle direct unknown option detection (from checkUnknownArgs)\n const unknownMatch = i.message?.match(/^Unknown option: \"([^\"]+)\"$/);\n if (unknownMatch) {\n const similar = suggestSimilar(unknownMatch[1], knownOptions());\n if (similar.length) {\n const hint = formatSuggestions(similar, '--');\n return { ...i, message: `${i.message} ${hint}` };\n }\n return i;\n }\n\n // Handle Zod strict schema errors (Unrecognized key(s) in object: \"foo\")\n const keys: string[] | undefined = i.keys ?? i.message?.match(/[Uu]nrecognized key(?:s)?[^\"]*\"([^\"]+)\"/)?.slice(1);\n if (!keys?.length) return i;\n const hints = keys.flatMap((k: string) => {\n const similar = suggestSimilar(k, knownOptions());\n return similar.length ? [formatSuggestions(similar, '--')] : [];\n });\n if (!hints.length) return i;\n return { ...i, message: `${i.message} ${hints.join(' ')}` };\n });\n}\n\nconst suggestionsInterceptor = defineInterceptor({ id: 'padrone:suggestions', name: 'padrone:suggestions', order: -500 }, () => ({\n parse(ctx, next) {\n try {\n const result = next();\n if (result instanceof Promise) {\n return result.catch((err: unknown) => {\n throw enrichRoutingError(err, ctx.command);\n });\n }\n return result;\n } catch (err) {\n throw enrichRoutingError(err, ctx.command);\n }\n },\n validate(ctx, next) {\n const result = next();\n return thenMaybe(result, (v) => {\n if (!v.argsResult?.issues?.length) return v;\n const enriched = enrichIssuesWithSuggestions(v.argsResult.issues, () => getKnownOptionNames(ctx.command));\n return { ...v, argsResult: { ...v.argsResult, issues: enriched } } as typeof v;\n });\n },\n}));\n\nexport function padroneSuggestions(): <T extends CommandTypesBase>(builder: T) => T {\n return ((builder: AnyPadroneBuilder) => builder.intercept(suggestionsInterceptor)) as any;\n}\n"],"mappings":";;;;;;AAOA,SAAS,kBAAkB,OAAiB,SAAS,IAAY;CAC/D,IAAI,MAAM,WAAW,GAAG,OAAO;CAC/B,MAAM,SAAS,MAAM,KAAK,MAAM,IAAI,SAAS,EAAE,EAAE;CACjD,IAAI,OAAO,WAAW,GAAG,OAAO,gBAAgB,OAAO,GAAG;CAC1D,OAAO,gBAAgB,OAAO,MAAM,GAAG,EAAE,CAAC,CAAC,KAAK,IAAI,EAAE,MAAM,OAAO,GAAG,EAAE,EAAE;AAC5E;AAEA,SAAS,kBAAkB,aAAiC,MAA4C;CACtG,IAAI,CAAC,eAAe,gBAAgB,KAAK,QAAQ,gBAAgB,KAAK,MAAM,OAAO;CACnF,MAAM,QAAQ,YAAY,MAAM,GAAG;CACnC,IAAI,UAAU;CACd,KAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,QAAQ,QAAQ,UAAU,MAAM,MAAM;GAC1C,eAAe,CAAC;GAChB,OAAO,EAAE,SAAS,QAAQ,EAAE,SAAS,SAAS,IAAI;EACpD,CAAC;EACD,IAAI,OAAO,UAAU;OAChB;CACP;CACA,OAAO;AACT;AAEA,SAAS,mBAAmB,KAAc,aAAyC;CACjF,IAAI,EAAE,eAAe,eAAe,OAAO;CAE3C,MAAM,eAAe,IAAI,QAAQ,MAAM,yBAAyB;CAChE,MAAM,kBAAkB,IAAI,QAAQ,MAAM,0CAA0C;CACpF,MAAM,OAAO,eAAe,MAAM,kBAAkB;CACpD,IAAI,CAAC,MAAM,OAAO;CAElB,MAAM,YAAY,kBAAkB,IAAI,SAAS,WAAW;CAE5D,MAAM,iBAA2B,CAAC;CAClC,IAAI,UAAU,UACZ,KAAK,MAAM,OAAO,UAAU,UAAU;EACpC,eAAe,GAAG;EAClB,IAAI,CAAC,IAAI,QAAQ;GACf,eAAe,KAAK,IAAI,IAAI;GAC5B,IAAI,IAAI,SAAS,eAAe,KAAK,GAAG,IAAI,OAAO;EACrD;CACF;CAIF,MAAM,iBAAiB,kBADP,eAAe,MAAM,cACU,CAAC;CAChD,IAAI,CAAC,gBAAgB,OAAO;CAE5B,MAAM,cAAc,CAAC,cAAc;CAEnC,OAAO,IAAI,aAAa,GADD,IAAI,QAAQ,QAAQ,kBACN;EAAE;EAAa,SAAS,IAAI;CAAQ,CAAC;AAC5E;AAEA,SAAS,4BACP,QACA,cACe;CACf,OAAO,OAAO,KAAK,MAAW;EAE5B,MAAM,eAAe,EAAE,SAAS,MAAM,6BAA6B;EACnE,IAAI,cAAc;GAChB,MAAM,UAAU,eAAe,aAAa,IAAI,aAAa,CAAC;GAC9D,IAAI,QAAQ,QAAQ;IAClB,MAAM,OAAO,kBAAkB,SAAS,IAAI;IAC5C,OAAO;KAAE,GAAG;KAAG,SAAS,GAAG,EAAE,QAAQ,GAAG;IAAO;GACjD;GACA,OAAO;EACT;EAGA,MAAM,OAA6B,EAAE,QAAQ,EAAE,SAAS,MAAM,yCAAyC,CAAC,EAAE,MAAM,CAAC;EACjH,IAAI,CAAC,MAAM,QAAQ,OAAO;EAC1B,MAAM,QAAQ,KAAK,SAAS,MAAc;GACxC,MAAM,UAAU,eAAe,GAAG,aAAa,CAAC;GAChD,OAAO,QAAQ,SAAS,CAAC,kBAAkB,SAAS,IAAI,CAAC,IAAI,CAAC;EAChE,CAAC;EACD,IAAI,CAAC,MAAM,QAAQ,OAAO;EAC1B,OAAO;GAAE,GAAG;GAAG,SAAS,GAAG,EAAE,QAAQ,GAAG,MAAM,KAAK,GAAG;EAAI;CAC5D,CAAC;AACH;AAEA,MAAM,yBAAyB,kBAAkB;CAAE,IAAI;CAAuB,MAAM;CAAuB,OAAO;AAAK,UAAU;CAC/H,MAAM,KAAK,MAAM;EACf,IAAI;GACF,MAAM,SAAS,KAAK;GACpB,IAAI,kBAAkB,SACpB,OAAO,OAAO,OAAO,QAAiB;IACpC,MAAM,mBAAmB,KAAK,IAAI,OAAO;GAC3C,CAAC;GAEH,OAAO;EACT,SAAS,KAAK;GACZ,MAAM,mBAAmB,KAAK,IAAI,OAAO;EAC3C;CACF;CACA,SAAS,KAAK,MAAM;EAElB,OAAO,UADQ,KACO,IAAI,MAAM;GAC9B,IAAI,CAAC,EAAE,YAAY,QAAQ,QAAQ,OAAO;GAC1C,MAAM,WAAW,4BAA4B,EAAE,WAAW,cAAc,oBAAoB,IAAI,OAAO,CAAC;GACxG,OAAO;IAAE,GAAG;IAAG,YAAY;KAAE,GAAG,EAAE;KAAY,QAAQ;IAAS;GAAE;EACnE,CAAC;CACH;AACF,EAAE;AAEF,SAAgB,qBAAoE;CAClF,SAAS,YAA+B,QAAQ,UAAU,sBAAsB;AAClF"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { CommandTypesBase } from "../types/command.mjs";
|
|
2
|
+
//#region src/extension/timing.d.ts
|
|
3
|
+
interface PadroneTimingOptions {
|
|
4
|
+
/** Enable timing by default without requiring `--time` flag. Default: `false`. */
|
|
5
|
+
enabled?: boolean;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Extension that tracks command execution time.
|
|
9
|
+
*
|
|
10
|
+
* - `--time` / `--timing` → enables timing output
|
|
11
|
+
* - `--no-time` / `--no-timing` → disables timing output
|
|
12
|
+
*
|
|
13
|
+
* Pass `{ enabled: true }` to enable timing by default (can be disabled via `--no-time`).
|
|
14
|
+
*
|
|
15
|
+
* Usage:
|
|
16
|
+
* ```ts
|
|
17
|
+
* // Opt-in via flag
|
|
18
|
+
* createPadrone('my-cli').extend(padroneTiming())
|
|
19
|
+
*
|
|
20
|
+
* // Always on, opt-out via --no-time
|
|
21
|
+
* createPadrone('my-cli').extend(padroneTiming({ enabled: true }))
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
declare function padroneTiming(options?: PadroneTimingOptions): <T extends CommandTypesBase>(builder: T) => T;
|
|
25
|
+
//#endregion
|
|
26
|
+
export { PadroneTimingOptions, padroneTiming };
|
|
27
|
+
//# sourceMappingURL=timing.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timing.d.mts","names":[],"sources":["../../src/extension/timing.ts"],"mappings":";;UAwDiB,oBAAA;;EAEf,OAAO;AAAA;;;;AAAA;AAoBT;;;;;;;;;;;;;iBAAgB,aAAA,CAAc,OAAA,GAAU,oBAAA,cAAkC,gBAAA,EAAkB,OAAA,EAAS,CAAA,KAAM,CAAA"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { thenMaybe } from "../core/results.mjs";
|
|
2
|
+
import { defineInterceptor } from "../core/interceptors.mjs";
|
|
3
|
+
//#region src/extension/timing.ts
|
|
4
|
+
function formatDuration(ms) {
|
|
5
|
+
if (ms < 1e3) return `${ms.toFixed(0)}ms`;
|
|
6
|
+
if (ms < 6e4) return `${(ms / 1e3).toFixed(2)}s`;
|
|
7
|
+
return `${Math.floor(ms / 6e4)}m ${(ms % 6e4 / 1e3).toFixed(2)}s`;
|
|
8
|
+
}
|
|
9
|
+
const timingMeta = {
|
|
10
|
+
id: "padrone:timing",
|
|
11
|
+
name: "padrone:timing",
|
|
12
|
+
order: -1002
|
|
13
|
+
};
|
|
14
|
+
function createTimingInterceptor(enabledByDefault) {
|
|
15
|
+
return defineInterceptor(timingMeta, () => {
|
|
16
|
+
let enabled = enabledByDefault;
|
|
17
|
+
let startTime = 0;
|
|
18
|
+
return {
|
|
19
|
+
parse(_ctx, next) {
|
|
20
|
+
return thenMaybe(next(), (res) => {
|
|
21
|
+
if ("timing" in res.rawArgs) {
|
|
22
|
+
enabled = res.rawArgs.timing !== false;
|
|
23
|
+
delete res.rawArgs.timing;
|
|
24
|
+
}
|
|
25
|
+
if ("time" in res.rawArgs) {
|
|
26
|
+
enabled = res.rawArgs.time !== false;
|
|
27
|
+
delete res.rawArgs.time;
|
|
28
|
+
}
|
|
29
|
+
return res;
|
|
30
|
+
});
|
|
31
|
+
},
|
|
32
|
+
start(_ctx, next) {
|
|
33
|
+
startTime = performance.now();
|
|
34
|
+
return next();
|
|
35
|
+
},
|
|
36
|
+
shutdown(ctx, next) {
|
|
37
|
+
return thenMaybe(next(), (res) => {
|
|
38
|
+
if (enabled) {
|
|
39
|
+
const elapsed = performance.now() - startTime;
|
|
40
|
+
ctx.runtime.error(`\nDone in ${formatDuration(elapsed)}`);
|
|
41
|
+
}
|
|
42
|
+
return res;
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Extension that tracks command execution time.
|
|
50
|
+
*
|
|
51
|
+
* - `--time` / `--timing` → enables timing output
|
|
52
|
+
* - `--no-time` / `--no-timing` → disables timing output
|
|
53
|
+
*
|
|
54
|
+
* Pass `{ enabled: true }` to enable timing by default (can be disabled via `--no-time`).
|
|
55
|
+
*
|
|
56
|
+
* Usage:
|
|
57
|
+
* ```ts
|
|
58
|
+
* // Opt-in via flag
|
|
59
|
+
* createPadrone('my-cli').extend(padroneTiming())
|
|
60
|
+
*
|
|
61
|
+
* // Always on, opt-out via --no-time
|
|
62
|
+
* createPadrone('my-cli').extend(padroneTiming({ enabled: true }))
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
function padroneTiming(options) {
|
|
66
|
+
return ((builder) => builder.intercept(createTimingInterceptor(options?.enabled ?? false)));
|
|
67
|
+
}
|
|
68
|
+
//#endregion
|
|
69
|
+
export { padroneTiming };
|
|
70
|
+
|
|
71
|
+
//# sourceMappingURL=timing.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timing.mjs","names":[],"sources":["../../src/extension/timing.ts"],"sourcesContent":["import { defineInterceptor } from '../core/interceptors.ts';\nimport { thenMaybe } from '../core/results.ts';\nimport type { AnyPadroneBuilder, CommandTypesBase } from '../types/index.ts';\n\n// ── Helpers ─────────────────────────────────────────────────────────────\n\nfunction formatDuration(ms: number): string {\n if (ms < 1000) return `${ms.toFixed(0)}ms`;\n if (ms < 60_000) return `${(ms / 1000).toFixed(2)}s`;\n const mins = Math.floor(ms / 60_000);\n const secs = ((ms % 60_000) / 1000).toFixed(2);\n return `${mins}m ${secs}s`;\n}\n\n// ── Interceptor ─────────────────────────────────────────────────────────\n\nconst timingMeta = { id: 'padrone:timing', name: 'padrone:timing', order: -1002 } as const;\n\nfunction createTimingInterceptor(enabledByDefault: boolean) {\n return defineInterceptor(timingMeta, () => {\n let enabled = enabledByDefault;\n let startTime = 0;\n\n return {\n parse(_ctx, next) {\n return thenMaybe(next(), (res) => {\n if ('timing' in res.rawArgs) {\n enabled = res.rawArgs.timing !== false;\n delete res.rawArgs.timing;\n }\n if ('time' in res.rawArgs) {\n enabled = res.rawArgs.time !== false;\n delete res.rawArgs.time;\n }\n return res;\n });\n },\n start(_ctx, next) {\n startTime = performance.now();\n return next();\n },\n shutdown(ctx, next) {\n return thenMaybe(next(), (res) => {\n if (enabled) {\n const elapsed = performance.now() - startTime;\n ctx.runtime.error(`\\nDone in ${formatDuration(elapsed)}`);\n }\n return res;\n });\n },\n };\n });\n}\n\n// ── Extension ───────────────────────────────────────────────────────────\n\nexport interface PadroneTimingOptions {\n /** Enable timing by default without requiring `--time` flag. Default: `false`. */\n enabled?: boolean;\n}\n\n/**\n * Extension that tracks command execution time.\n *\n * - `--time` / `--timing` → enables timing output\n * - `--no-time` / `--no-timing` → disables timing output\n *\n * Pass `{ enabled: true }` to enable timing by default (can be disabled via `--no-time`).\n *\n * Usage:\n * ```ts\n * // Opt-in via flag\n * createPadrone('my-cli').extend(padroneTiming())\n *\n * // Always on, opt-out via --no-time\n * createPadrone('my-cli').extend(padroneTiming({ enabled: true }))\n * ```\n */\nexport function padroneTiming(options?: PadroneTimingOptions): <T extends CommandTypesBase>(builder: T) => T {\n return ((builder: AnyPadroneBuilder) => builder.intercept(createTimingInterceptor(options?.enabled ?? false))) as any;\n}\n"],"mappings":";;;AAMA,SAAS,eAAe,IAAoB;CAC1C,IAAI,KAAK,KAAM,OAAO,GAAG,GAAG,QAAQ,CAAC,EAAE;CACvC,IAAI,KAAK,KAAQ,OAAO,IAAI,KAAK,IAAA,CAAM,QAAQ,CAAC,EAAE;CAGlD,OAAO,GAFM,KAAK,MAAM,KAAK,GAEhB,EAAE,KADA,KAAK,MAAU,IAAA,CAAM,QAAQ,CACtB,EAAE;AAC1B;AAIA,MAAM,aAAa;CAAE,IAAI;CAAkB,MAAM;CAAkB,OAAO;AAAM;AAEhF,SAAS,wBAAwB,kBAA2B;CAC1D,OAAO,kBAAkB,kBAAkB;EACzC,IAAI,UAAU;EACd,IAAI,YAAY;EAEhB,OAAO;GACL,MAAM,MAAM,MAAM;IAChB,OAAO,UAAU,KAAK,IAAI,QAAQ;KAChC,IAAI,YAAY,IAAI,SAAS;MAC3B,UAAU,IAAI,QAAQ,WAAW;MACjC,OAAO,IAAI,QAAQ;KACrB;KACA,IAAI,UAAU,IAAI,SAAS;MACzB,UAAU,IAAI,QAAQ,SAAS;MAC/B,OAAO,IAAI,QAAQ;KACrB;KACA,OAAO;IACT,CAAC;GACH;GACA,MAAM,MAAM,MAAM;IAChB,YAAY,YAAY,IAAI;IAC5B,OAAO,KAAK;GACd;GACA,SAAS,KAAK,MAAM;IAClB,OAAO,UAAU,KAAK,IAAI,QAAQ;KAChC,IAAI,SAAS;MACX,MAAM,UAAU,YAAY,IAAI,IAAI;MACpC,IAAI,QAAQ,MAAM,aAAa,eAAe,OAAO,GAAG;KAC1D;KACA,OAAO;IACT,CAAC;GACH;EACF;CACF,CAAC;AACH;;;;;;;;;;;;;;;;;;AA0BA,SAAgB,cAAc,SAA+E;CAC3G,SAAS,YAA+B,QAAQ,UAAU,wBAAwB,SAAS,WAAW,KAAK,CAAC;AAC9G"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { WithInterceptor } from "../util/type-utils.mjs";
|
|
2
|
+
import { CommandTypesBase } from "../types/command.mjs";
|
|
3
|
+
//#region src/extension/tracing.d.ts
|
|
4
|
+
/** Minimal subset of OTEL `SpanStatusCode`. */
|
|
5
|
+
type SpanStatusCode = 0 | 1 | 2;
|
|
6
|
+
/** Minimal subset of OTEL `SpanStatus`. */
|
|
7
|
+
type SpanStatus = {
|
|
8
|
+
code: SpanStatusCode;
|
|
9
|
+
message?: string;
|
|
10
|
+
};
|
|
11
|
+
/** Minimal subset of OTEL `Span`. */
|
|
12
|
+
interface OtelSpan {
|
|
13
|
+
setAttribute(key: string, value: string | number | boolean): this;
|
|
14
|
+
addEvent(name: string, attributes?: Record<string, string | number | boolean>): this;
|
|
15
|
+
setStatus(status: SpanStatus): this;
|
|
16
|
+
recordException(error: unknown): this;
|
|
17
|
+
end(): void;
|
|
18
|
+
spanContext(): {
|
|
19
|
+
traceId: string;
|
|
20
|
+
spanId: string;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/** Minimal subset of OTEL `Tracer`. */
|
|
24
|
+
interface OtelTracer {
|
|
25
|
+
startSpan(name: string): OtelSpan;
|
|
26
|
+
}
|
|
27
|
+
/** Minimal subset of OTEL `TracerProvider`. */
|
|
28
|
+
interface OtelTracerProvider {
|
|
29
|
+
getTracer(name: string, version?: string): OtelTracer;
|
|
30
|
+
}
|
|
31
|
+
/** Tracing handle injected into the command context. */
|
|
32
|
+
type PadroneTracer = {
|
|
33
|
+
/** The underlying OTEL tracer. */tracer: OtelTracer; /** Root span covering the full command execution. */
|
|
34
|
+
rootSpan: OtelSpan; /** Run `fn` inside a child span that is automatically ended when `fn` returns (or rejects). */
|
|
35
|
+
span: <T>(name: string, fn: (span: OtelSpan) => T) => T;
|
|
36
|
+
};
|
|
37
|
+
/** Configuration for the tracing extension. */
|
|
38
|
+
type PadroneTracingConfig = {
|
|
39
|
+
/** OTEL `TracerProvider`. Required — there is no global fallback. */provider: OtelTracerProvider; /** Service / tracer name. Defaults to the CLI program name. */
|
|
40
|
+
serviceName?: string;
|
|
41
|
+
};
|
|
42
|
+
/** Builder/program type after applying `padroneTracing()`. Adds `{ tracing: PadroneTracer }` to the command context. */
|
|
43
|
+
type WithTracing<T> = WithInterceptor<T, {
|
|
44
|
+
tracing: PadroneTracer;
|
|
45
|
+
}>;
|
|
46
|
+
/**
|
|
47
|
+
* Extension that adds OpenTelemetry tracing to command execution.
|
|
48
|
+
*
|
|
49
|
+
* Creates a root span for each command invocation and provides a `PadroneTracer`
|
|
50
|
+
* on the command context for creating child spans in action handlers.
|
|
51
|
+
*
|
|
52
|
+
* When used with `padroneLogger()`, the logger automatically emits span events
|
|
53
|
+
* for each log call — no extra configuration needed. The logger detects the
|
|
54
|
+
* tracing context and bridges log output to span events.
|
|
55
|
+
*
|
|
56
|
+
* Uses minimal OTEL-compatible interfaces — pass any `TracerProvider` that
|
|
57
|
+
* implements `getTracer()`. Works with `@opentelemetry/api` or compatible
|
|
58
|
+
* libraries.
|
|
59
|
+
*
|
|
60
|
+
* Provides `{ tracing: PadroneTracer }` on the command context.
|
|
61
|
+
* Access it in action handlers as `ctx.context.tracing`.
|
|
62
|
+
*
|
|
63
|
+
* Usage:
|
|
64
|
+
* ```ts
|
|
65
|
+
* import { trace } from '@opentelemetry/api';
|
|
66
|
+
* import { createPadrone, padroneLogger } from 'padrone';
|
|
67
|
+
* import { padroneTracing } from 'padrone/tracing';
|
|
68
|
+
*
|
|
69
|
+
* createPadrone('my-cli')
|
|
70
|
+
* .extend(padroneTracing({ provider: trace.getTracerProvider() }))
|
|
71
|
+
* .extend(padroneLogger())
|
|
72
|
+
* .command('deploy', (c) =>
|
|
73
|
+
* c.action((_args, ctx) => {
|
|
74
|
+
* ctx.context.logger.info('deploying'); // also emits a span event
|
|
75
|
+
* ctx.context.tracing.span('build', (span) => {
|
|
76
|
+
* span.setAttribute('target', 'production');
|
|
77
|
+
* });
|
|
78
|
+
* })
|
|
79
|
+
* )
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
declare function padroneTracing<T extends CommandTypesBase>(config: PadroneTracingConfig): (builder: T) => WithTracing<T>;
|
|
83
|
+
//#endregion
|
|
84
|
+
export { OtelSpan, OtelTracer, OtelTracerProvider, PadroneTracer, PadroneTracingConfig, WithTracing, padroneTracing };
|
|
85
|
+
//# sourceMappingURL=tracing.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tracing.d.mts","names":[],"sources":["../../src/extension/tracing.ts"],"mappings":";;;;KAWK,cAAA;;KAGA,UAAA;EAAe,IAAA,EAAM,cAAc;EAAE,OAAA;AAAA;AAHvB;AAAA,UAMF,QAAA;EACf,YAAA,CAAa,GAAA,UAAa,KAAA;EAC1B,QAAA,CAAS,IAAA,UAAc,UAAA,GAAa,MAAA;EACpC,SAAA,CAAU,MAAA,EAAQ,UAAU;EAC5B,eAAA,CAAgB,KAAA;EAChB,GAAA;EACA,WAAA;IAAiB,OAAA;IAAiB,MAAA;EAAA;AAAA;;UAInB,UAAA;EACf,SAAA,CAAU,IAAA,WAAe,QAAQ;AAAA;;UAIlB,kBAAA;EACf,SAAA,CAAU,IAAA,UAAc,OAAA,YAAmB,UAAU;AAAA;;KAI3C,aAAA;EAjBQ,kCAmBlB,MAAA,EAAQ,UAAA,EAlBR;EAoBA,QAAA,EAAU,QAAA,EAnBV;EAqBA,IAAA,MAAU,IAAA,UAAc,EAAA,GAAK,IAAA,EAAM,QAAA,KAAa,CAAA,KAAM,CAAA;AAAA;;KAI5C,oBAAA;EAxB8B,qEA0BxC,QAAA,EAAU,kBAAkB,EAtBH;EAwBzB,WAAA;AAAA;;KAIU,WAAA,MAAiB,eAAA,CAAgB,CAAA;EAAK,OAAA,EAAS,aAAA;AAAA;AA3BxB;AAInC;;;;;;;;;AACuD;AAIvD;;;;;;;;;;;;;;;;;;;;;;;AAMyD;AAIzD;AAnBmC,iBA8InB,cAAA,WAAyB,gBAAA,EAAkB,MAAA,EAAQ,oBAAA,IAAwB,OAAA,EAAS,CAAA,KAAM,WAAA,CAAY,CAAA"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { thenMaybe } from "../core/results.mjs";
|
|
2
|
+
import { defineInterceptor } from "../core/interceptors.mjs";
|
|
3
|
+
//#region src/extension/tracing.ts
|
|
4
|
+
const OTEL_ERROR = 2;
|
|
5
|
+
function tracingInterceptor(config) {
|
|
6
|
+
return defineInterceptor({
|
|
7
|
+
id: "padrone:tracing",
|
|
8
|
+
name: "padrone:tracing",
|
|
9
|
+
order: -1
|
|
10
|
+
}, () => {
|
|
11
|
+
let rootSpan;
|
|
12
|
+
let tracer;
|
|
13
|
+
return {
|
|
14
|
+
start(ctx, next) {
|
|
15
|
+
tracer = config.provider.getTracer(config.serviceName ?? ctx.command.name);
|
|
16
|
+
return next();
|
|
17
|
+
},
|
|
18
|
+
execute(ctx, next) {
|
|
19
|
+
rootSpan = tracer.startSpan(`cli ${ctx.command.name}`);
|
|
20
|
+
return next({ context: { tracing: {
|
|
21
|
+
tracer,
|
|
22
|
+
rootSpan,
|
|
23
|
+
span(name, fn) {
|
|
24
|
+
const child = tracer.startSpan(name);
|
|
25
|
+
try {
|
|
26
|
+
const result = fn(child);
|
|
27
|
+
if (result != null && typeof result.then === "function") return result.then((v) => {
|
|
28
|
+
child.end();
|
|
29
|
+
return v;
|
|
30
|
+
}, (err) => {
|
|
31
|
+
child.recordException(err);
|
|
32
|
+
child.setStatus({ code: OTEL_ERROR });
|
|
33
|
+
child.end();
|
|
34
|
+
throw err;
|
|
35
|
+
});
|
|
36
|
+
child.end();
|
|
37
|
+
return result;
|
|
38
|
+
} catch (err) {
|
|
39
|
+
child.recordException(err);
|
|
40
|
+
child.setStatus({ code: OTEL_ERROR });
|
|
41
|
+
child.end();
|
|
42
|
+
throw err;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
} } });
|
|
46
|
+
},
|
|
47
|
+
error(ctx, next) {
|
|
48
|
+
rootSpan?.recordException(ctx.error);
|
|
49
|
+
rootSpan?.setStatus({ code: OTEL_ERROR });
|
|
50
|
+
return next();
|
|
51
|
+
},
|
|
52
|
+
shutdown(_ctx, next) {
|
|
53
|
+
return thenMaybe(next(), (res) => {
|
|
54
|
+
rootSpan?.end();
|
|
55
|
+
return res;
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Extension that adds OpenTelemetry tracing to command execution.
|
|
63
|
+
*
|
|
64
|
+
* Creates a root span for each command invocation and provides a `PadroneTracer`
|
|
65
|
+
* on the command context for creating child spans in action handlers.
|
|
66
|
+
*
|
|
67
|
+
* When used with `padroneLogger()`, the logger automatically emits span events
|
|
68
|
+
* for each log call — no extra configuration needed. The logger detects the
|
|
69
|
+
* tracing context and bridges log output to span events.
|
|
70
|
+
*
|
|
71
|
+
* Uses minimal OTEL-compatible interfaces — pass any `TracerProvider` that
|
|
72
|
+
* implements `getTracer()`. Works with `@opentelemetry/api` or compatible
|
|
73
|
+
* libraries.
|
|
74
|
+
*
|
|
75
|
+
* Provides `{ tracing: PadroneTracer }` on the command context.
|
|
76
|
+
* Access it in action handlers as `ctx.context.tracing`.
|
|
77
|
+
*
|
|
78
|
+
* Usage:
|
|
79
|
+
* ```ts
|
|
80
|
+
* import { trace } from '@opentelemetry/api';
|
|
81
|
+
* import { createPadrone, padroneLogger } from 'padrone';
|
|
82
|
+
* import { padroneTracing } from 'padrone/tracing';
|
|
83
|
+
*
|
|
84
|
+
* createPadrone('my-cli')
|
|
85
|
+
* .extend(padroneTracing({ provider: trace.getTracerProvider() }))
|
|
86
|
+
* .extend(padroneLogger())
|
|
87
|
+
* .command('deploy', (c) =>
|
|
88
|
+
* c.action((_args, ctx) => {
|
|
89
|
+
* ctx.context.logger.info('deploying'); // also emits a span event
|
|
90
|
+
* ctx.context.tracing.span('build', (span) => {
|
|
91
|
+
* span.setAttribute('target', 'production');
|
|
92
|
+
* });
|
|
93
|
+
* })
|
|
94
|
+
* )
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
function padroneTracing(config) {
|
|
98
|
+
const resolved = {
|
|
99
|
+
provider: config.provider,
|
|
100
|
+
serviceName: config.serviceName
|
|
101
|
+
};
|
|
102
|
+
return ((builder) => builder.intercept(tracingInterceptor(resolved)));
|
|
103
|
+
}
|
|
104
|
+
//#endregion
|
|
105
|
+
export { padroneTracing };
|
|
106
|
+
|
|
107
|
+
//# sourceMappingURL=tracing.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tracing.mjs","names":[],"sources":["../../src/extension/tracing.ts"],"sourcesContent":["import { defineInterceptor } from '#src/core/interceptors.ts';\nimport { thenMaybe } from '#src/core/results.ts';\nimport type { AnyPadroneBuilder, CommandTypesBase } from '#src/types/index.ts';\nimport type { WithInterceptor } from '#src/util/type-utils.ts';\n\n// ---------------------------------------------------------------------------\n// Types — minimal OTEL-compatible interfaces so we don't hard-depend on\n// `@opentelemetry/api`. Users pass their real Tracer / Span instances.\n// ---------------------------------------------------------------------------\n\n/** Minimal subset of OTEL `SpanStatusCode`. */\ntype SpanStatusCode = 0 | 1 | 2;\n\n/** Minimal subset of OTEL `SpanStatus`. */\ntype SpanStatus = { code: SpanStatusCode; message?: string };\n\n/** Minimal subset of OTEL `Span`. */\nexport interface OtelSpan {\n setAttribute(key: string, value: string | number | boolean): this;\n addEvent(name: string, attributes?: Record<string, string | number | boolean>): this;\n setStatus(status: SpanStatus): this;\n recordException(error: unknown): this;\n end(): void;\n spanContext(): { traceId: string; spanId: string };\n}\n\n/** Minimal subset of OTEL `Tracer`. */\nexport interface OtelTracer {\n startSpan(name: string): OtelSpan;\n}\n\n/** Minimal subset of OTEL `TracerProvider`. */\nexport interface OtelTracerProvider {\n getTracer(name: string, version?: string): OtelTracer;\n}\n\n/** Tracing handle injected into the command context. */\nexport type PadroneTracer = {\n /** The underlying OTEL tracer. */\n tracer: OtelTracer;\n /** Root span covering the full command execution. */\n rootSpan: OtelSpan;\n /** Run `fn` inside a child span that is automatically ended when `fn` returns (or rejects). */\n span: <T>(name: string, fn: (span: OtelSpan) => T) => T;\n};\n\n/** Configuration for the tracing extension. */\nexport type PadroneTracingConfig = {\n /** OTEL `TracerProvider`. Required — there is no global fallback. */\n provider: OtelTracerProvider;\n /** Service / tracer name. Defaults to the CLI program name. */\n serviceName?: string;\n};\n\n/** Builder/program type after applying `padroneTracing()`. Adds `{ tracing: PadroneTracer }` to the command context. */\nexport type WithTracing<T> = WithInterceptor<T, { tracing: PadroneTracer }>;\n\n// ---------------------------------------------------------------------------\n// Interceptor\n// ---------------------------------------------------------------------------\n\nconst OTEL_ERROR: SpanStatusCode = 2;\n\ntype ResolvedTracingConfig = { provider: OtelTracerProvider; serviceName: string | undefined };\n\nfunction tracingInterceptor(config: ResolvedTracingConfig) {\n return defineInterceptor({ id: 'padrone:tracing', name: 'padrone:tracing', order: -1 }, () => {\n let rootSpan: OtelSpan;\n let tracer: OtelTracer;\n\n return {\n start(ctx, next) {\n tracer = config.provider.getTracer(config.serviceName ?? ctx.command.name);\n return next();\n },\n\n execute(ctx, next) {\n rootSpan = tracer.startSpan(`cli ${ctx.command.name}`);\n\n const padroneTracer: PadroneTracer = {\n tracer,\n rootSpan,\n span(name, fn) {\n const child = tracer.startSpan(name);\n try {\n const result = fn(child);\n if (result != null && typeof (result as any).then === 'function') {\n return (result as any).then(\n (v: any) => {\n child.end();\n return v;\n },\n (err: unknown) => {\n child.recordException(err);\n child.setStatus({ code: OTEL_ERROR });\n child.end();\n throw err;\n },\n );\n }\n child.end();\n return result;\n } catch (err) {\n child.recordException(err);\n child.setStatus({ code: OTEL_ERROR });\n child.end();\n throw err;\n }\n },\n };\n\n return next({ context: { tracing: padroneTracer } });\n },\n\n error(ctx, next) {\n rootSpan?.recordException(ctx.error);\n rootSpan?.setStatus({ code: OTEL_ERROR });\n return next();\n },\n\n shutdown(_ctx, next) {\n return thenMaybe(next(), (res) => {\n rootSpan?.end();\n return res;\n });\n },\n };\n });\n}\n\n// ---------------------------------------------------------------------------\n// Extension\n// ---------------------------------------------------------------------------\n\n/**\n * Extension that adds OpenTelemetry tracing to command execution.\n *\n * Creates a root span for each command invocation and provides a `PadroneTracer`\n * on the command context for creating child spans in action handlers.\n *\n * When used with `padroneLogger()`, the logger automatically emits span events\n * for each log call — no extra configuration needed. The logger detects the\n * tracing context and bridges log output to span events.\n *\n * Uses minimal OTEL-compatible interfaces — pass any `TracerProvider` that\n * implements `getTracer()`. Works with `@opentelemetry/api` or compatible\n * libraries.\n *\n * Provides `{ tracing: PadroneTracer }` on the command context.\n * Access it in action handlers as `ctx.context.tracing`.\n *\n * Usage:\n * ```ts\n * import { trace } from '@opentelemetry/api';\n * import { createPadrone, padroneLogger } from 'padrone';\n * import { padroneTracing } from 'padrone/tracing';\n *\n * createPadrone('my-cli')\n * .extend(padroneTracing({ provider: trace.getTracerProvider() }))\n * .extend(padroneLogger())\n * .command('deploy', (c) =>\n * c.action((_args, ctx) => {\n * ctx.context.logger.info('deploying'); // also emits a span event\n * ctx.context.tracing.span('build', (span) => {\n * span.setAttribute('target', 'production');\n * });\n * })\n * )\n * ```\n */\nexport function padroneTracing<T extends CommandTypesBase>(config: PadroneTracingConfig): (builder: T) => WithTracing<T> {\n const resolved: ResolvedTracingConfig = {\n provider: config.provider,\n serviceName: config.serviceName,\n };\n return ((builder: AnyPadroneBuilder) => builder.intercept(tracingInterceptor(resolved))) as any;\n}\n"],"mappings":";;;AA6DA,MAAM,aAA6B;AAInC,SAAS,mBAAmB,QAA+B;CACzD,OAAO,kBAAkB;EAAE,IAAI;EAAmB,MAAM;EAAmB,OAAO;CAAG,SAAS;EAC5F,IAAI;EACJ,IAAI;EAEJ,OAAO;GACL,MAAM,KAAK,MAAM;IACf,SAAS,OAAO,SAAS,UAAU,OAAO,eAAe,IAAI,QAAQ,IAAI;IACzE,OAAO,KAAK;GACd;GAEA,QAAQ,KAAK,MAAM;IACjB,WAAW,OAAO,UAAU,OAAO,IAAI,QAAQ,MAAM;IAkCrD,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS;KA/BhC;KACA;KACA,KAAK,MAAM,IAAI;MACb,MAAM,QAAQ,OAAO,UAAU,IAAI;MACnC,IAAI;OACF,MAAM,SAAS,GAAG,KAAK;OACvB,IAAI,UAAU,QAAQ,OAAQ,OAAe,SAAS,YACpD,OAAQ,OAAe,MACpB,MAAW;QACV,MAAM,IAAI;QACV,OAAO;OACT,IACC,QAAiB;QAChB,MAAM,gBAAgB,GAAG;QACzB,MAAM,UAAU,EAAE,MAAM,WAAW,CAAC;QACpC,MAAM,IAAI;QACV,MAAM;OACR,CACF;OAEF,MAAM,IAAI;OACV,OAAO;MACT,SAAS,KAAK;OACZ,MAAM,gBAAgB,GAAG;OACzB,MAAM,UAAU,EAAE,MAAM,WAAW,CAAC;OACpC,MAAM,IAAI;OACV,MAAM;MACR;KACF;IAG4C,EAAE,EAAE,CAAC;GACrD;GAEA,MAAM,KAAK,MAAM;IACf,UAAU,gBAAgB,IAAI,KAAK;IACnC,UAAU,UAAU,EAAE,MAAM,WAAW,CAAC;IACxC,OAAO,KAAK;GACd;GAEA,SAAS,MAAM,MAAM;IACnB,OAAO,UAAU,KAAK,IAAI,QAAQ;KAChC,UAAU,IAAI;KACd,OAAO;IACT,CAAC;GACH;EACF;CACF,CAAC;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0CA,SAAgB,eAA2C,QAA8D;CACvH,MAAM,WAAkC;EACtC,UAAU,OAAO;EACjB,aAAa,OAAO;CACtB;CACA,SAAS,YAA+B,QAAQ,UAAU,mBAAmB,QAAQ,CAAC;AACxF"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { CommandTypesBase } from "../types/command.mjs";
|
|
2
|
+
import { UpdateCheckConfig } from "../feature/update-check.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/extension/update-check.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Extension that adds background update checking:
|
|
7
|
+
* - Checks for newer versions on npm (or custom registry) in the background
|
|
8
|
+
* - Shows an update notification after command execution
|
|
9
|
+
* - Respects `--no-update-check` flag to suppress
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* ```ts
|
|
13
|
+
* createPadrone('my-cli')
|
|
14
|
+
* .extend(padroneUpdateCheck({ packageName: 'my-cli' }))
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
declare function padroneUpdateCheck(config?: UpdateCheckConfig): <T extends CommandTypesBase>(builder: T) => T;
|
|
18
|
+
//#endregion
|
|
19
|
+
export { padroneUpdateCheck };
|
|
20
|
+
//# sourceMappingURL=update-check.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update-check.d.mts","names":[],"sources":["../../src/extension/update-check.ts"],"mappings":";;;;;;;AA0EA;;;;;;;;;iBAAgB,kBAAA,CAAmB,MAAA,GAAQ,iBAAA,cAAoC,gBAAA,EAAkB,OAAA,EAAS,CAAA,KAAM,CAAA"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { thenMaybe } from "../core/results.mjs";
|
|
2
|
+
import { defineInterceptor } from "../core/interceptors.mjs";
|
|
3
|
+
import { getVersion } from "../util/utils.mjs";
|
|
4
|
+
//#region src/extension/update-check.ts
|
|
5
|
+
function createUpdateCheckInterceptor(config) {
|
|
6
|
+
return defineInterceptor({
|
|
7
|
+
id: "padrone:update-check",
|
|
8
|
+
name: "padrone:update-check",
|
|
9
|
+
order: 1e3
|
|
10
|
+
}, () => {
|
|
11
|
+
let checkPromise;
|
|
12
|
+
let suppressed = false;
|
|
13
|
+
return {
|
|
14
|
+
start(ctx, next) {
|
|
15
|
+
const rootCommand = ctx.command;
|
|
16
|
+
const runtime = ctx.runtime;
|
|
17
|
+
checkPromise = Promise.resolve(getVersion(rootCommand.version)).then((currentVersion) => import("../feature/update-check.mjs").then(({ createUpdateChecker }) => createUpdateChecker(rootCommand.name, currentVersion, config, runtime)));
|
|
18
|
+
return next();
|
|
19
|
+
},
|
|
20
|
+
parse(_ctx, next) {
|
|
21
|
+
return thenMaybe(next(), (res) => {
|
|
22
|
+
if ("update-check" in res.rawArgs) {
|
|
23
|
+
if (res.rawArgs["update-check"] === false) suppressed = true;
|
|
24
|
+
delete res.rawArgs["update-check"];
|
|
25
|
+
}
|
|
26
|
+
return res;
|
|
27
|
+
});
|
|
28
|
+
},
|
|
29
|
+
shutdown(_ctx, next) {
|
|
30
|
+
const result = next();
|
|
31
|
+
if (suppressed || !checkPromise) return result;
|
|
32
|
+
let resolved = null;
|
|
33
|
+
checkPromise.then((fn) => {
|
|
34
|
+
resolved = fn;
|
|
35
|
+
}, () => {
|
|
36
|
+
resolved = void 0;
|
|
37
|
+
});
|
|
38
|
+
if (resolved !== null) resolved?.();
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Extension that adds background update checking:
|
|
46
|
+
* - Checks for newer versions on npm (or custom registry) in the background
|
|
47
|
+
* - Shows an update notification after command execution
|
|
48
|
+
* - Respects `--no-update-check` flag to suppress
|
|
49
|
+
*
|
|
50
|
+
* Usage:
|
|
51
|
+
* ```ts
|
|
52
|
+
* createPadrone('my-cli')
|
|
53
|
+
* .extend(padroneUpdateCheck({ packageName: 'my-cli' }))
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
function padroneUpdateCheck(config = {}) {
|
|
57
|
+
return ((builder) => builder.intercept(createUpdateCheckInterceptor(config)));
|
|
58
|
+
}
|
|
59
|
+
//#endregion
|
|
60
|
+
export { padroneUpdateCheck };
|
|
61
|
+
|
|
62
|
+
//# sourceMappingURL=update-check.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update-check.mjs","names":[],"sources":["../../src/extension/update-check.ts"],"sourcesContent":["import { thenMaybe } from '#src/core/results.ts';\nimport { defineInterceptor } from '../core/interceptors.ts';\nimport type { UpdateCheckConfig } from '../feature/update-check.ts';\nimport type { AnyPadroneBuilder, CommandTypesBase } from '../types/index.ts';\nimport { getVersion } from '../util/utils.ts';\n\n// ── Interceptor ─────────────────────────────────────────────────────────\n\nfunction createUpdateCheckInterceptor(config: UpdateCheckConfig) {\n return defineInterceptor({ id: 'padrone:update-check', name: 'padrone:update-check', order: 1000 }, () => {\n let checkPromise: Promise<(() => void) | undefined> | undefined;\n let suppressed = false;\n\n return {\n start(ctx, next) {\n const rootCommand = ctx.command;\n const runtime = ctx.runtime;\n\n checkPromise = Promise.resolve(getVersion(rootCommand.version)).then((currentVersion) =>\n import('../feature/update-check.ts').then(({ createUpdateChecker }) =>\n createUpdateChecker(rootCommand.name, currentVersion, config, runtime),\n ),\n );\n\n return next();\n },\n parse(_ctx, next) {\n return thenMaybe(next(), (res) => {\n if ('update-check' in res.rawArgs) {\n if (res.rawArgs['update-check'] === false) suppressed = true;\n delete res.rawArgs['update-check'];\n }\n return res;\n });\n },\n shutdown(_ctx, next) {\n const result = next();\n if (suppressed || !checkPromise) return result;\n\n // Try to show notification synchronously if the check already resolved\n let resolved: (() => void) | undefined | null = null;\n checkPromise.then(\n (fn) => {\n resolved = fn;\n },\n () => {\n resolved = undefined;\n },\n );\n\n if (resolved !== null) {\n (resolved as (() => void) | undefined)?.();\n }\n\n return result;\n },\n };\n });\n}\n\n// ── Extension ────────────────────────────────────────────────────────────\n\n/**\n * Extension that adds background update checking:\n * - Checks for newer versions on npm (or custom registry) in the background\n * - Shows an update notification after command execution\n * - Respects `--no-update-check` flag to suppress\n *\n * Usage:\n * ```ts\n * createPadrone('my-cli')\n * .extend(padroneUpdateCheck({ packageName: 'my-cli' }))\n * ```\n */\nexport function padroneUpdateCheck(config: UpdateCheckConfig = {}): <T extends CommandTypesBase>(builder: T) => T {\n return ((builder: AnyPadroneBuilder) => builder.intercept(createUpdateCheckInterceptor(config))) as any;\n}\n"],"mappings":";;;;AAQA,SAAS,6BAA6B,QAA2B;CAC/D,OAAO,kBAAkB;EAAE,IAAI;EAAwB,MAAM;EAAwB,OAAO;CAAK,SAAS;EACxG,IAAI;EACJ,IAAI,aAAa;EAEjB,OAAO;GACL,MAAM,KAAK,MAAM;IACf,MAAM,cAAc,IAAI;IACxB,MAAM,UAAU,IAAI;IAEpB,eAAe,QAAQ,QAAQ,WAAW,YAAY,OAAO,CAAC,CAAC,CAAC,MAAM,mBACpE,OAAO,8BAA6B,CAAC,MAAM,EAAE,0BAC3C,oBAAoB,YAAY,MAAM,gBAAgB,QAAQ,OAAO,CACvE,CACF;IAEA,OAAO,KAAK;GACd;GACA,MAAM,MAAM,MAAM;IAChB,OAAO,UAAU,KAAK,IAAI,QAAQ;KAChC,IAAI,kBAAkB,IAAI,SAAS;MACjC,IAAI,IAAI,QAAQ,oBAAoB,OAAO,aAAa;MACxD,OAAO,IAAI,QAAQ;KACrB;KACA,OAAO;IACT,CAAC;GACH;GACA,SAAS,MAAM,MAAM;IACnB,MAAM,SAAS,KAAK;IACpB,IAAI,cAAc,CAAC,cAAc,OAAO;IAGxC,IAAI,WAA4C;IAChD,aAAa,MACV,OAAO;KACN,WAAW;IACb,SACM;KACJ,WAAW,KAAA;IACb,CACF;IAEA,IAAI,aAAa,MACf,WAAyC;IAG3C,OAAO;GACT;EACF;CACF,CAAC;AACH;;;;;;;;;;;;;AAgBA,SAAgB,mBAAmB,SAA4B,CAAC,GAAkD;CAChH,SAAS,YAA+B,QAAQ,UAAU,6BAA6B,MAAM,CAAC;AAChG"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
//#region src/extension/utils.ts
|
|
2
|
+
/** Minimal Standard Schema that passes through known fields, ignoring unknown ones. */
|
|
3
|
+
function passthroughSchema(fields) {
|
|
4
|
+
return { "~standard": {
|
|
5
|
+
version: 1,
|
|
6
|
+
vendor: "padrone",
|
|
7
|
+
jsonSchema: {
|
|
8
|
+
input: () => ({}),
|
|
9
|
+
output: () => ({})
|
|
10
|
+
},
|
|
11
|
+
validate: (value) => {
|
|
12
|
+
const input = value && typeof value === "object" ? value : {};
|
|
13
|
+
const result = {};
|
|
14
|
+
for (const [name, type] of Object.entries(fields)) {
|
|
15
|
+
const v = input[name];
|
|
16
|
+
if (v === void 0) continue;
|
|
17
|
+
if (type === "string[]") {
|
|
18
|
+
if (Array.isArray(v)) result[name] = v.map(String);
|
|
19
|
+
else if (typeof v === "string") result[name] = [v];
|
|
20
|
+
} else if (type === "string") {
|
|
21
|
+
if (typeof v === "string") result[name] = v;
|
|
22
|
+
else if (Array.isArray(v) && v.length > 0) result[name] = String(v[0]);
|
|
23
|
+
} else if (type === "boolean") result[name] = v === true || v === "true";
|
|
24
|
+
}
|
|
25
|
+
return { value: result };
|
|
26
|
+
}
|
|
27
|
+
} };
|
|
28
|
+
}
|
|
29
|
+
/** Find a command by space-separated name in the command tree. */
|
|
30
|
+
function findCommandInTree(name, rootCommand) {
|
|
31
|
+
const parts = name.split(" ").filter(Boolean);
|
|
32
|
+
let current = rootCommand;
|
|
33
|
+
for (const part of parts) {
|
|
34
|
+
const found = current.commands?.find((c) => c.name === part || c.aliases?.includes(part));
|
|
35
|
+
if (!found) return void 0;
|
|
36
|
+
current = found;
|
|
37
|
+
}
|
|
38
|
+
return current;
|
|
39
|
+
}
|
|
40
|
+
//#endregion
|
|
41
|
+
export { findCommandInTree, passthroughSchema };
|
|
42
|
+
|
|
43
|
+
//# sourceMappingURL=utils.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.mjs","names":[],"sources":["../../src/extension/utils.ts"],"sourcesContent":["import type { AnyPadroneCommand, PadroneSchema } from '../types/index.ts';\n\ntype SchemaShape = Record<string, 'string' | 'string[]' | 'boolean'>;\n\ntype InferPassthroughSchema<T extends SchemaShape> = {\n [K in keyof T]: T[K] extends 'string' ? string : T[K] extends 'string[]' ? string[] : T[K] extends 'boolean' ? boolean : never;\n};\n\n/** Minimal Standard Schema that passes through known fields, ignoring unknown ones. */\nexport function passthroughSchema<TShape extends SchemaShape>(fields: TShape): PadroneSchema<InferPassthroughSchema<TShape>> {\n return {\n '~standard': {\n version: 1 as const,\n vendor: 'padrone' as const,\n jsonSchema: {\n input: () => ({}),\n output: () => ({}),\n },\n validate: (value) => {\n const input = value && typeof value === 'object' ? (value as Record<string, unknown>) : {};\n const result: Record<string, unknown> = {};\n for (const [name, type] of Object.entries(fields)) {\n const v = input[name];\n if (v === undefined) continue;\n if (type === 'string[]') {\n if (Array.isArray(v)) result[name] = v.map(String);\n else if (typeof v === 'string') result[name] = [v];\n } else if (type === 'string') {\n if (typeof v === 'string') result[name] = v;\n else if (Array.isArray(v) && v.length > 0) result[name] = String(v[0]);\n } else if (type === 'boolean') {\n result[name] = v === true || v === 'true';\n }\n }\n return { value: result as InferPassthroughSchema<TShape> };\n },\n },\n };\n}\n\n/** Find a command by space-separated name in the command tree. */\nexport function findCommandInTree(name: string, rootCommand: AnyPadroneCommand): AnyPadroneCommand | undefined {\n const parts = name.split(' ').filter(Boolean);\n let current = rootCommand;\n for (const part of parts) {\n const found = current.commands?.find((c) => c.name === part || c.aliases?.includes(part));\n if (!found) return undefined;\n current = found;\n }\n return current;\n}\n"],"mappings":";;AASA,SAAgB,kBAA8C,QAA+D;CAC3H,OAAO,EACL,aAAa;EACX,SAAS;EACT,QAAQ;EACR,YAAY;GACV,cAAc,CAAC;GACf,eAAe,CAAC;EAClB;EACA,WAAW,UAAU;GACnB,MAAM,QAAQ,SAAS,OAAO,UAAU,WAAY,QAAoC,CAAC;GACzF,MAAM,SAAkC,CAAC;GACzC,KAAK,MAAM,CAAC,MAAM,SAAS,OAAO,QAAQ,MAAM,GAAG;IACjD,MAAM,IAAI,MAAM;IAChB,IAAI,MAAM,KAAA,GAAW;IACrB,IAAI,SAAS;SACP,MAAM,QAAQ,CAAC,GAAG,OAAO,QAAQ,EAAE,IAAI,MAAM;UAC5C,IAAI,OAAO,MAAM,UAAU,OAAO,QAAQ,CAAC,CAAC;IAAA,OAC5C,IAAI,SAAS;SACd,OAAO,MAAM,UAAU,OAAO,QAAQ;UACrC,IAAI,MAAM,QAAQ,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,QAAQ,OAAO,EAAE,EAAE;IAAA,OAChE,IAAI,SAAS,WAClB,OAAO,QAAQ,MAAM,QAAQ,MAAM;GAEvC;GACA,OAAO,EAAE,OAAO,OAAyC;EAC3D;CACF,EACF;AACF;;AAGA,SAAgB,kBAAkB,MAAc,aAA+D;CAC7G,MAAM,QAAQ,KAAK,MAAM,GAAG,CAAC,CAAC,OAAO,OAAO;CAC5C,IAAI,UAAU;CACd,KAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,QAAQ,QAAQ,UAAU,MAAM,MAAM,EAAE,SAAS,QAAQ,EAAE,SAAS,SAAS,IAAI,CAAC;EACxF,IAAI,CAAC,OAAO,OAAO,KAAA;EACnB,UAAU;CACZ;CACA,OAAO;AACT"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { PadroneSchema } from "../types/schema.mjs";
|
|
2
|
+
import { WithCommand } from "../util/type-utils.mjs";
|
|
3
|
+
import { CommandTypesBase, PadroneCommand } from "../types/command.mjs";
|
|
4
|
+
//#region src/extension/version.d.ts
|
|
5
|
+
type VersionCommand = PadroneCommand<'version', '', PadroneSchema<void>, string, [], [], false>;
|
|
6
|
+
type WithVersion<T> = WithCommand<T, 'version', VersionCommand>;
|
|
7
|
+
/**
|
|
8
|
+
* Extension that adds version support:
|
|
9
|
+
* - `version` command
|
|
10
|
+
* - `--version` / `-v` / `-V` flags (root command only)
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* ```ts
|
|
14
|
+
* createPadrone('my-cli').extend(padroneVersion())
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
declare function padroneVersion(): <T extends CommandTypesBase>(builder: T) => WithVersion<T>;
|
|
18
|
+
//#endregion
|
|
19
|
+
export { VersionCommand, WithVersion, padroneVersion };
|
|
20
|
+
//# sourceMappingURL=version.d.mts.map
|