@lovrabet/cli-framework 0.1.1-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,53 @@
1
+ /**
2
+ * @fileoverview Core domain types for the CLI framework.
3
+ *
4
+ * Defines the primitive risk levels, output formats, and the central
5
+ * {@link CommandDefinition} interface that every declarative command must implement.
6
+ */
7
+ /**
8
+ * Returns a numeric ordering value for a given risk level.
9
+ * Higher numbers indicate greater risk. Used by {@link assertRiskWithinLevel}
10
+ * to compare the command's risk against the configured risk ceiling.
11
+ *
12
+ * @param risk - A `Risk` value, an arbitrary string, or `undefined`.
13
+ * @returns `0` for `read`, `1` for `write`, `2` for `high-risk-write`, or `0` as fallback.
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * riskLevelOrder("high-risk-write") > riskLevelOrder("read") // true
18
+ * ```
19
+ */
20
+ export function riskLevelOrder(risk) {
21
+ const order = { read: 0, write: 1, "high-risk-write": 2 };
22
+ return order[risk ?? "read"] ?? 0;
23
+ }
24
+ /**
25
+ * Type guard that checks whether a value is a valid {@link OutputFormat}.
26
+ *
27
+ * @param v - Any value to test.
28
+ * @returns `true` if `v` is `"json"`, `"pretty"`, or `"compress"`.
29
+ */
30
+ export function isValidFormat(v) {
31
+ return v === "json" || v === "pretty" || v === "compress";
32
+ }
33
+ /**
34
+ * Converts legacy or alternative format aliases to the canonical {@link OutputFormat}.
35
+ * The legacy alias `"table"` maps to `"pretty"`.
36
+ *
37
+ * @param v - Any value to normalize.
38
+ * @returns The canonical `OutputFormat`, or `undefined` if `v` cannot be mapped.
39
+ *
40
+ * @example
41
+ * ```ts
42
+ * normalizeLegacyOutputFormat("table") // "pretty"
43
+ * normalizeLegacyOutputFormat("compress") // "compress"
44
+ * normalizeLegacyOutputFormat(123) // undefined
45
+ * ```
46
+ */
47
+ export function normalizeLegacyOutputFormat(v) {
48
+ if (v === "table")
49
+ return "pretty";
50
+ if (isValidFormat(v))
51
+ return v;
52
+ return undefined;
53
+ }
package/lib/index.d.ts ADDED
@@ -0,0 +1,209 @@
1
+ /**
2
+ * @fileoverview Public API surface of the `@yuntoo/cli-framework` package.
3
+ *
4
+ * Re-exports all public types and values from the framework sub-modules.
5
+ * Import from this file rather than deep-importing internal modules to
6
+ * ensure compatibility across minor version bumps.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * import { runCommandWithAdapter, createCliErrors, type CommandDefinition } from "@yuntoo/cli-framework";
11
+ * ```
12
+ */
13
+ export type {
14
+ /** @see ./framework/types.ts */
15
+ ArgDef, FlagDef, CommandDefinition, CommandResult, DryRunResult, RuntimeContextBase, RuntimeContext, OutputFormat, OutputEnvelope, Risk, } from "./framework/types.js";
16
+ export {
17
+ /** @see ./framework/types.ts */
18
+ riskLevelOrder, normalizeLegacyOutputFormat, isValidFormat, } from "./framework/types.js";
19
+ export { CliError, createCliErrors } from "./errors.js";
20
+ export type {
21
+ /**
22
+ * @see ./errors.ts
23
+ * @description Shape of the factory returned by `createCliErrors`.
24
+ */
25
+ CliErrorsShape,
26
+ /**
27
+ * @see ./errors.ts
28
+ * @description Configuration for `createCliErrors`.
29
+ */
30
+ CliErrorFactoryOptions, } from "./errors.js";
31
+ export { buildAllFlags } from "./framework/build-all-flags.js";
32
+ export type {
33
+ /**
34
+ * @see ./framework/build-all-flags.ts
35
+ * @description Options for `buildAllFlags`.
36
+ */
37
+ BuildAllFlagsOptions, } from "./framework/build-all-flags.js";
38
+ export { createFlagHelpers } from "./framework/flags.js";
39
+ export type {
40
+ /**
41
+ * @see ./framework/flags.ts
42
+ * @description Parsed flag map returned by `parseFlags`.
43
+ */
44
+ ParsedFlags, } from "./framework/flags.js";
45
+ export { createHelpGenerators } from "./framework/help.js";
46
+ export type {
47
+ /**
48
+ * @see ./framework/help.ts
49
+ * @description A global flag shown in the top-level help.
50
+ */
51
+ GlobalHelpFlag,
52
+ /**
53
+ * @see ./framework/help.ts
54
+ * @description A command entry in a service's help listing.
55
+ */
56
+ HelpCommandListing,
57
+ /**
58
+ * @see ./framework/help.ts
59
+ * @description A service entry in the help registry.
60
+ */
61
+ HelpServiceEntry,
62
+ /**
63
+ * @see ./framework/help.ts
64
+ * @description Options for `createHelpGenerators`.
65
+ */
66
+ HelpGeneratorOptions, } from "./framework/help.js";
67
+ export { buildSchemaPayload } from "./framework/schema-export.js";
68
+ export type {
69
+ /**
70
+ * @see ./framework/schema-export.ts
71
+ * @description A serialized regex pattern (RegExp replaced with plain fields).
72
+ */
73
+ SerializedFlagPattern,
74
+ /**
75
+ * @see ./framework/schema-export.ts
76
+ * @description A serialized flag definition for JSON export.
77
+ */
78
+ SerializedFlag,
79
+ /**
80
+ * @see ./framework/schema-export.ts
81
+ * @description A serialized command entry for the schema payload.
82
+ */
83
+ SchemaCommandEntry,
84
+ /**
85
+ * @see ./framework/schema-export.ts
86
+ * @description A serialized service entry for the schema payload.
87
+ */
88
+ SchemaServiceEntry,
89
+ /**
90
+ * @see ./framework/schema-export.ts
91
+ * @description The root schema payload object.
92
+ */
93
+ SchemaPayload,
94
+ /**
95
+ * @see ./framework/schema-export.ts
96
+ * @description A stripped-down command listing used during schema build.
97
+ */
98
+ SchemaCommandListing,
99
+ /**
100
+ * @see ./framework/schema-export.ts
101
+ * @description A stripped-down service listing used during schema build.
102
+ */
103
+ SchemaServiceListing,
104
+ /**
105
+ * @see ./framework/schema-export.ts
106
+ * @description Options for `buildSchemaPayload`.
107
+ */
108
+ BuildSchemaOptions, } from "./framework/schema-export.js";
109
+ export { runCommandWithAdapter } from "./framework/runner.js";
110
+ export type {
111
+ /**
112
+ * @see ./framework/runner.ts
113
+ * @description Raw environment values from the CLI entry point.
114
+ */
115
+ RunnerEnvBase,
116
+ /**
117
+ * @see ./framework/runner.ts
118
+ * @description Values produced by the adapter's `prepare` hook.
119
+ */
120
+ PreparedRuntimeValues,
121
+ /**
122
+ * @see ./framework/runner.ts
123
+ * @description Risk policy governing risk ceiling enforcement.
124
+ */
125
+ RiskPolicy,
126
+ /**
127
+ * @see ./framework/runner.ts
128
+ * @description Options passed to `adapter.confirmHighRisk`.
129
+ */
130
+ ConfirmHighRiskOptions,
131
+ /**
132
+ * @see ./framework/runner.ts
133
+ * @description Context object passed to adapter hooks before execution.
134
+ */
135
+ ExecuteHookOptions,
136
+ /**
137
+ * @see ./framework/runner.ts
138
+ * @description Context object passed to the `finalize` hook.
139
+ */
140
+ FinalizeHookOptions,
141
+ /**
142
+ * @see ./framework/runner.ts
143
+ * @description Adapter interface bridging the framework runner to a concrete CLI.
144
+ */
145
+ RunnerAdapter, } from "./framework/runner.js";
146
+ export {
147
+ /** @see ./framework/runner-shared.ts */
148
+ resolveJqFilter,
149
+ /** @see ./framework/runner-shared.ts */
150
+ resolveFormat,
151
+ /** @see ./framework/runner-shared.ts */
152
+ buildRuntimeContext,
153
+ /** @see ./framework/runner-shared.ts */
154
+ resolveOutputConfig,
155
+ /** @see ./framework/runner-shared.ts */
156
+ assertRiskWithinLevel,
157
+ /** @see ./framework/runner-shared.ts */
158
+ runDryRunIfNeeded,
159
+ /** @see ./framework/runner-shared.ts */
160
+ requireConfirmationPrompt, } from "./framework/runner-shared.js";
161
+ export type {
162
+ /**
163
+ * @see ./framework/runner-shared.ts
164
+ * @description Options for `resolveOutputConfig`.
165
+ */
166
+ ResolveOutputOptions,
167
+ /**
168
+ * @see ./framework/runner-shared.ts
169
+ * @description Output configuration resolved by `resolveOutputConfig`.
170
+ */
171
+ ResolvedOutputConfig,
172
+ /**
173
+ * @see ./framework/runner-shared.ts
174
+ * @description Options for `buildRuntimeContext`.
175
+ */
176
+ BuildRuntimeContextOptions,
177
+ /**
178
+ * @see ./framework/runner-shared.ts
179
+ * @description Options for `assertRiskWithinLevel`.
180
+ */
181
+ AssertRiskWithinLevelOptions,
182
+ /**
183
+ * @see ./framework/runner-shared.ts
184
+ * @description Options for `runDryRunIfNeeded`.
185
+ */
186
+ RunDryRunOptions,
187
+ /**
188
+ * @see ./framework/runner-shared.ts
189
+ * @description Options for `requireConfirmationPrompt`.
190
+ */
191
+ ConfirmationPromptOptions, } from "./framework/runner-shared.js";
192
+ export {
193
+ /** @see ./framework/response.ts */
194
+ extractList,
195
+ /** @see ./framework/response.ts */
196
+ extractPaging, } from "./framework/response.js";
197
+ export type {
198
+ /**
199
+ * @see ./framework/response.ts
200
+ * @description Pagination metadata in list responses.
201
+ */
202
+ Paging,
203
+ /**
204
+ * @see ./framework/response.ts
205
+ * @description List response wrapper with records and optional paging.
206
+ */
207
+ ListResponse, } from "./framework/response.js";
208
+ export { formatOutput } from "./framework/output.js";
209
+ export { createJqFilter } from "./utils/apply-jq-filter.js";
package/lib/index.js ADDED
@@ -0,0 +1,52 @@
1
+ /**
2
+ * @fileoverview Public API surface of the `@yuntoo/cli-framework` package.
3
+ *
4
+ * Re-exports all public types and values from the framework sub-modules.
5
+ * Import from this file rather than deep-importing internal modules to
6
+ * ensure compatibility across minor version bumps.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * import { runCommandWithAdapter, createCliErrors, type CommandDefinition } from "@yuntoo/cli-framework";
11
+ * ```
12
+ */
13
+ export {
14
+ /** @see ./framework/types.ts */
15
+ riskLevelOrder, normalizeLegacyOutputFormat, isValidFormat, } from "./framework/types.js";
16
+ // ─── Errors ───────────────────────────────────────────────────────────────────
17
+ export { CliError, createCliErrors } from "./errors.js";
18
+ // ─── Flag Pipeline ─────────────────────────────────────────────────────────────
19
+ export { buildAllFlags } from "./framework/build-all-flags.js";
20
+ export { createFlagHelpers } from "./framework/flags.js";
21
+ // ─── Help Generation ───────────────────────────────────────────────────────────
22
+ export { createHelpGenerators } from "./framework/help.js";
23
+ // ─── Schema Export ─────────────────────────────────────────────────────────────
24
+ export { buildSchemaPayload } from "./framework/schema-export.js";
25
+ // ─── Runner ────────────────────────────────────────────────────────────────────
26
+ export { runCommandWithAdapter } from "./framework/runner.js";
27
+ // ─── Runner Shared Utilities ────────────────────────────────────────────────────
28
+ export {
29
+ /** @see ./framework/runner-shared.ts */
30
+ resolveJqFilter,
31
+ /** @see ./framework/runner-shared.ts */
32
+ resolveFormat,
33
+ /** @see ./framework/runner-shared.ts */
34
+ buildRuntimeContext,
35
+ /** @see ./framework/runner-shared.ts */
36
+ resolveOutputConfig,
37
+ /** @see ./framework/runner-shared.ts */
38
+ assertRiskWithinLevel,
39
+ /** @see ./framework/runner-shared.ts */
40
+ runDryRunIfNeeded,
41
+ /** @see ./framework/runner-shared.ts */
42
+ requireConfirmationPrompt, } from "./framework/runner-shared.js";
43
+ // ─── Response Utilities ────────────────────────────────────────────────────────
44
+ export {
45
+ /** @see ./framework/response.ts */
46
+ extractList,
47
+ /** @see ./framework/response.ts */
48
+ extractPaging, } from "./framework/response.js";
49
+ // ─── Output Formatting ─────────────────────────────────────────────────────────
50
+ export { formatOutput } from "./framework/output.js";
51
+ // ─── JQ Filter ────────────────────────────────────────────────────────────────
52
+ export { createJqFilter } from "./utils/apply-jq-filter.js";
@@ -0,0 +1,33 @@
1
+ /**
2
+ * @fileoverview jq post-filter integration for JSON output.
3
+ *
4
+ * Wraps the `jq` command-line tool (https://jqlang.org) to apply an
5
+ * expression filter to a JSON stream. Used by the output formatter when
6
+ * the user supplies `--jq <expression>` together with `--format json`
7
+ * or `--format compress`.
8
+ *
9
+ * The jq binary is resolved in this order:
10
+ * 1. Bundled `jq` shipped with the `node-jq` npm package (platform-specific).
11
+ * 2. A `jq` executable found on `PATH`.
12
+ *
13
+ * @module apply-jq-filter
14
+ */
15
+ import type { CliErrorsShape } from "../errors.js";
16
+ /**
17
+ * Creates a jq filter function bound to a specific error factory.
18
+ *
19
+ * The returned function accepts a JSON string and a jq expression, spawns
20
+ * the jq binary, and returns the filtered output string. On failure it
21
+ * throws a typed {@link CliError} with code `"validation_error"`.
22
+ *
23
+ * @param cliErrors - Error factory providing `validation` for error reporting.
24
+ * @returns An `applyJqFilter(jsonInput, expr)` function.
25
+ *
26
+ * @example
27
+ * ```ts
28
+ * const applyJqFilter = createJqFilter(cliErrors);
29
+ * const filtered = applyJqFilter('{"items":[{"id":1}]}\n', ".[].id");
30
+ * // filtered === "1\n"
31
+ * ```
32
+ */
33
+ export declare function createJqFilter(cliErrors: Pick<CliErrorsShape, "validation">): (jsonInput: string, expr: string) => string;
@@ -0,0 +1,99 @@
1
+ /**
2
+ * @fileoverview jq post-filter integration for JSON output.
3
+ *
4
+ * Wraps the `jq` command-line tool (https://jqlang.org) to apply an
5
+ * expression filter to a JSON stream. Used by the output formatter when
6
+ * the user supplies `--jq <expression>` together with `--format json`
7
+ * or `--format compress`.
8
+ *
9
+ * The jq binary is resolved in this order:
10
+ * 1. Bundled `jq` shipped with the `node-jq` npm package (platform-specific).
11
+ * 2. A `jq` executable found on `PATH`.
12
+ *
13
+ * @module apply-jq-filter
14
+ */
15
+ import { execFileSync } from "node:child_process";
16
+ import { existsSync } from "node:fs";
17
+ import { createRequire } from "node:module";
18
+ import path from "node:path";
19
+ /** Maximum stdout buffer for the jq process (50 MB). */
20
+ const MAX_BUFFER = 50 * 1024 * 1024;
21
+ /**
22
+ * Creates a jq filter function bound to a specific error factory.
23
+ *
24
+ * The returned function accepts a JSON string and a jq expression, spawns
25
+ * the jq binary, and returns the filtered output string. On failure it
26
+ * throws a typed {@link CliError} with code `"validation_error"`.
27
+ *
28
+ * @param cliErrors - Error factory providing `validation` for error reporting.
29
+ * @returns An `applyJqFilter(jsonInput, expr)` function.
30
+ *
31
+ * @example
32
+ * ```ts
33
+ * const applyJqFilter = createJqFilter(cliErrors);
34
+ * const filtered = applyJqFilter('{"items":[{"id":1}]}\n', ".[].id");
35
+ * // filtered === "1\n"
36
+ * ```
37
+ */
38
+ export function createJqFilter(cliErrors) {
39
+ /**
40
+ * Applies a jq expression filter to a JSON input string.
41
+ *
42
+ * @param jsonInput - Complete JSON line(s) to filter.
43
+ * @param expr - jq expression (e.g. `.[].name`).
44
+ * @returns The jq-filtered output string.
45
+ * @throws {@link CliError} with code `"validation_error"` when:
46
+ * - The jq binary cannot be found.
47
+ * - The expression is invalid.
48
+ * - The input is not valid JSON.
49
+ */
50
+ return function applyJqFilter(jsonInput, expr) {
51
+ const jqCmd = resolveJqBinary();
52
+ try {
53
+ return execFileSync(jqCmd, [expr], {
54
+ input: jsonInput,
55
+ encoding: "utf8",
56
+ maxBuffer: MAX_BUFFER,
57
+ stdio: ["pipe", "pipe", "pipe"],
58
+ });
59
+ }
60
+ catch (err) {
61
+ const e = err;
62
+ // jq binary not found
63
+ if (e.code === "ENOENT") {
64
+ throw cliErrors.validation("--jq needs a jq binary. Bundled path (from node-jq) was not found and `jq` is not on PATH. " +
65
+ "Reinstall dependencies (allow install scripts), set JQ_PATH to a jq executable, " +
66
+ "or install jq: https://jqlang.org/", "e.g. `brew install jq` — or `npm install` with scripts enabled so node-jq can download jq");
67
+ }
68
+ const stderr = e.stderr?.toString?.().trim() ?? "";
69
+ const fallback = (e.message ?? String(err)).trim();
70
+ const body = stderr || fallback;
71
+ const msg = /^\s*jq:/i.test(body) ? body : `jq: ${body}`;
72
+ throw cliErrors.validation(msg);
73
+ }
74
+ };
75
+ }
76
+ /**
77
+ * Resolves the path to the jq executable.
78
+ *
79
+ * Resolution order:
80
+ * 1. Bundled `jq` from the `node-jq` npm package (platform-specific binary).
81
+ * 2. `jq` / `jq.exe` found on `PATH` via the `which` equivalent.
82
+ *
83
+ * @returns Absolute path to the jq executable.
84
+ */
85
+ function resolveJqBinary() {
86
+ try {
87
+ const require = createRequire(import.meta.url);
88
+ const pkgDir = path.dirname(require.resolve("node-jq/package.json"));
89
+ const binDir = path.join(pkgDir, "bin");
90
+ const name = process.platform === "win32" ? "jq.exe" : "jq";
91
+ const bundled = path.join(binDir, name);
92
+ if (existsSync(bundled))
93
+ return bundled;
94
+ }
95
+ catch {
96
+ // node-jq not installed or resolution failed — fall through to PATH
97
+ }
98
+ return "jq";
99
+ }
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@lovrabet/cli-framework",
3
+ "version": "0.1.1-beta.0",
4
+ "type": "module",
5
+ "main": "lib/index.js",
6
+ "types": "lib/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./lib/index.d.ts",
10
+ "default": "./lib/index.js"
11
+ }
12
+ },
13
+ "files": [
14
+ "lib"
15
+ ],
16
+ "publishConfig": {
17
+ "access": "public"
18
+ },
19
+ "scripts": {
20
+ "build": "rm -rf lib && tsc && echo 'cli-framework build done'",
21
+ "beta-release": "sh scripts/update-beta-version.sh && bun run build && git push && git push origin --tag && bun publish --tag beta",
22
+ "release": "sh scripts/update-latest-version.sh && bun run build && git push && git push origin --tag && bun publish",
23
+ "typecheck": "tsc --noEmit",
24
+ "test": "vitest run"
25
+ },
26
+ "dependencies": {
27
+ "chalk": "^5.6.2",
28
+ "node-jq": "^6.3.1"
29
+ },
30
+ "devDependencies": {
31
+ "@types/node": "^24.5.2",
32
+ "typescript": "latest",
33
+ "vitest": "^4.1.2"
34
+ }
35
+ }