@kidd-cli/core 0.2.0 → 0.3.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/dist/{config-Db_sjFU-.js → config-D8e5qxLp.js} +5 -17
- package/dist/config-D8e5qxLp.js.map +1 -0
- package/dist/{create-store-D-fQpCql.js → create-store-OHdkm_Yt.js} +3 -4
- package/dist/{create-store-D-fQpCql.js.map → create-store-OHdkm_Yt.js.map} +1 -1
- package/dist/index.d.ts +31 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +75 -40
- package/dist/index.js.map +1 -1
- package/dist/lib/config.js +3 -4
- package/dist/lib/logger.d.ts +1 -1
- package/dist/lib/logger.js +1 -2
- package/dist/lib/logger.js.map +1 -1
- package/dist/lib/project.d.ts +1 -1
- package/dist/lib/project.d.ts.map +1 -1
- package/dist/lib/project.js +2 -3
- package/dist/lib/store.d.ts +1 -1
- package/dist/lib/store.js +3 -4
- package/dist/{logger-BkQQej8h.d.ts → logger-9j49T5da.d.ts} +1 -1
- package/dist/{logger-BkQQej8h.d.ts.map → logger-9j49T5da.d.ts.map} +1 -1
- package/dist/middleware/auth.d.ts +68 -40
- package/dist/middleware/auth.d.ts.map +1 -1
- package/dist/middleware/auth.js +245 -230
- package/dist/middleware/auth.js.map +1 -1
- package/dist/middleware/http.d.ts +1 -1
- package/dist/middleware/http.js +163 -4
- package/dist/middleware/http.js.map +1 -1
- package/dist/{middleware-BFBKNSPQ.js → middleware-BWnPSRWR.js} +2 -4
- package/dist/{middleware-BFBKNSPQ.js.map → middleware-BWnPSRWR.js.map} +1 -1
- package/dist/{project-DuXgjaa_.js → project-D0g84bZY.js} +4 -8
- package/dist/project-D0g84bZY.js.map +1 -0
- package/dist/{types-BaZ5WqVM.d.ts → types-CTvrsrnD.d.ts} +3 -3
- package/dist/types-CTvrsrnD.d.ts.map +1 -0
- package/dist/{types-C0CYivzY.d.ts → types-D-BxshYM.d.ts} +1 -1
- package/dist/{types-C0CYivzY.d.ts.map → types-D-BxshYM.d.ts.map} +1 -1
- package/package.json +7 -7
- package/dist/config-Db_sjFU-.js.map +0 -1
- package/dist/create-http-client-tZJWlWp1.js +0 -165
- package/dist/create-http-client-tZJWlWp1.js.map +0 -1
- package/dist/project-DuXgjaa_.js.map +0 -1
- package/dist/types-BaZ5WqVM.d.ts.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as findProjectRoot } from "./project-
|
|
1
|
+
import { i as findProjectRoot } from "./project-D0g84bZY.js";
|
|
2
2
|
import { dirname, extname, join } from "node:path";
|
|
3
3
|
import { attempt, attemptAsync, err, match } from "@kidd-cli/utils/fp";
|
|
4
4
|
import { jsonParse, jsonStringify } from "@kidd-cli/utils/json";
|
|
@@ -7,22 +7,12 @@ import { formatZodIssues } from "@kidd-cli/utils/validate";
|
|
|
7
7
|
import { fileExists } from "@kidd-cli/utils/fs";
|
|
8
8
|
import { parse, printParseErrorCode } from "jsonc-parser";
|
|
9
9
|
import { parse as parse$1, stringify } from "yaml";
|
|
10
|
-
|
|
11
|
-
//#region src/utils/constants.ts
|
|
12
|
-
/**
|
|
13
|
-
* Default process exit code for error conditions.
|
|
14
|
-
*/
|
|
15
|
-
const DEFAULT_EXIT_CODE = 1;
|
|
16
|
-
|
|
17
|
-
//#endregion
|
|
18
10
|
//#region src/lib/config/constants.ts
|
|
19
|
-
const EMPTY_LENGTH = 0;
|
|
20
11
|
const CONFIG_EXTENSIONS = [
|
|
21
12
|
".jsonc",
|
|
22
13
|
".json",
|
|
23
14
|
".yaml"
|
|
24
15
|
];
|
|
25
|
-
|
|
26
16
|
//#endregion
|
|
27
17
|
//#region src/lib/config/find.ts
|
|
28
18
|
/**
|
|
@@ -80,7 +70,6 @@ async function findConfigFile(dir, fileNames) {
|
|
|
80
70
|
return null;
|
|
81
71
|
}))).find((result) => result !== null) ?? null;
|
|
82
72
|
}
|
|
83
|
-
|
|
84
73
|
//#endregion
|
|
85
74
|
//#region src/lib/config/parse.ts
|
|
86
75
|
/**
|
|
@@ -154,7 +143,7 @@ function parseJsoncContent(content, filePath) {
|
|
|
154
143
|
allowEmptyContent: false,
|
|
155
144
|
allowTrailingComma: true
|
|
156
145
|
});
|
|
157
|
-
if (errors.length >
|
|
146
|
+
if (errors.length > 0) return err(`Failed to parse JSONC in ${filePath}:\n${errors.map((parseError) => ` - ${printParseErrorCode(parseError.error)} at offset ${parseError.offset}`).join("\n")}`);
|
|
158
147
|
return [null, result];
|
|
159
148
|
}
|
|
160
149
|
/**
|
|
@@ -170,7 +159,6 @@ function parseYamlContent(content, filePath) {
|
|
|
170
159
|
if (error) return err(`Failed to parse YAML in ${filePath}: ${String(error)}`);
|
|
171
160
|
return [null, result];
|
|
172
161
|
}
|
|
173
|
-
|
|
174
162
|
//#endregion
|
|
175
163
|
//#region src/lib/config/create-config.ts
|
|
176
164
|
/**
|
|
@@ -271,7 +259,7 @@ function resolveReadErrorDetail(readError) {
|
|
|
271
259
|
if (readError) return String(readError);
|
|
272
260
|
return "empty file";
|
|
273
261
|
}
|
|
274
|
-
|
|
275
262
|
//#endregion
|
|
276
|
-
export {
|
|
277
|
-
|
|
263
|
+
export { createConfigClient as t };
|
|
264
|
+
|
|
265
|
+
//# sourceMappingURL=config-D8e5qxLp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-D8e5qxLp.js","names":["stringifyYaml","parseJsonc","parseYaml"],"sources":["../src/lib/config/constants.ts","../src/lib/config/find.ts","../src/lib/config/parse.ts","../src/lib/config/create-config.ts"],"sourcesContent":["/**\n * Supported configuration file formats.\n */\nexport type ConfigFormat = 'json' | 'jsonc' | 'yaml'\n\nexport { JSON_INDENT } from '@/utils/constants.js'\nexport const EMPTY_LENGTH = 0\nexport const CONFIG_EXTENSIONS = ['.jsonc', '.json', '.yaml'] as const\n","import { join } from 'node:path'\n\nimport { fileExists } from '@kidd-cli/utils/fs'\n\nimport { findProjectRoot } from '@/lib/project/index.js'\n\nimport { CONFIG_EXTENSIONS } from './constants.js'\n\n/**\n * Generate the list of config file names to search for based on the CLI name.\n *\n * Produces names like `.myapp.jsonc`, `.myapp.json`, `.myapp.yaml` from the\n * supported extension list.\n *\n * @param name - The CLI name used to derive config file names.\n * @returns An array of config file names to search for.\n */\nexport function getConfigFileNames(name: string): string[] {\n return CONFIG_EXTENSIONS.map((ext) => `.${name}${ext}`)\n}\n\n/**\n * Search for a config file across multiple directories.\n *\n * Searches in order: explicit search paths, the current working directory,\n * and the project root (if different from cwd). Returns the path of the\n * first matching file found.\n *\n * @param options - Search options including cwd, file names, and optional search paths.\n * @returns The full path to the config file, or null if not found.\n */\nexport async function findConfig(options: {\n cwd: string\n fileNames: string[]\n searchPaths?: string[]\n}): Promise<string | null> {\n const { fileNames, cwd, searchPaths } = options\n\n if (searchPaths) {\n const searchResults = await Promise.all(\n searchPaths.map((dir) => findConfigFile(dir, fileNames))\n )\n const found = searchResults.find((result): result is string => result !== null)\n if (found) {\n return found\n }\n }\n\n const fromCwd = await findConfigFile(cwd, fileNames)\n if (fromCwd) {\n return fromCwd\n }\n\n const projectRoot = findProjectRoot(cwd)\n if (projectRoot && projectRoot.path !== cwd) {\n const fromRoot = await findConfigFile(projectRoot.path, fileNames)\n if (fromRoot) {\n return fromRoot\n }\n }\n\n return null\n}\n\n// ---------------------------------------------------------------------------\n// Private helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Search a single directory for the first matching config file name.\n *\n * Checks each candidate file name in order and returns the path of the first\n * one that exists on disk.\n *\n * @param dir - The directory to search in.\n * @param fileNames - Candidate config file names to look for.\n * @returns The full path to the first matching config file, or null if none found.\n * @private\n */\nasync function findConfigFile(dir: string, fileNames: readonly string[]): Promise<string | null> {\n const results = await Promise.all(\n fileNames.map(async (fileName) => {\n const filePath = join(dir, fileName)\n const exists = await fileExists(filePath)\n if (exists) {\n return filePath\n }\n return null\n })\n )\n const found = results.find((result): result is string => result !== null)\n return found ?? null\n}\n","import { extname } from 'node:path'\n\nimport { attempt, err, match } from '@kidd-cli/utils/fp'\nimport { jsonParse, jsonStringify } from '@kidd-cli/utils/json'\nimport type { ParseError } from 'jsonc-parser'\nimport { parse as parseJsonc, printParseErrorCode } from 'jsonc-parser'\nimport { parse as parseYaml, stringify as stringifyYaml } from 'yaml'\n\nimport type { ConfigFormat } from './constants.js'\nimport { EMPTY_LENGTH } from './constants.js'\nimport type { ConfigOperationResult } from './types.js'\n\n/**\n * Determine the config format from a file path's extension.\n *\n * @param filePath - The file path to inspect.\n * @returns The detected config format ('json', 'jsonc', or 'yaml').\n */\nexport function getFormat(filePath: string): ConfigFormat {\n const ext = extname(filePath)\n return match(ext)\n .with('.jsonc', () => 'jsonc' as const)\n .with('.yaml', () => 'yaml' as const)\n .otherwise(() => 'json' as const)\n}\n\n/**\n * Options for parsing config file content.\n */\nexport interface ParseContentOptions {\n readonly content: string\n readonly filePath: string\n readonly format: ConfigFormat\n}\n\n/**\n * Parse config file content using the appropriate parser for the given format.\n *\n * @param options - Parse content options.\n * @returns A ConfigOperationResult with the parsed data or an error.\n */\nexport function parseContent(options: ParseContentOptions): ConfigOperationResult<unknown> {\n const { content, filePath, format } = options\n return match(format)\n .with('json', () => parseJson(content, filePath))\n .with('jsonc', () => parseJsoncContent(content, filePath))\n .with('yaml', () => parseYamlContent(content, filePath))\n .exhaustive()\n}\n\n/**\n * Serialize data to a string in the specified config format.\n *\n * @param data - The data to serialize.\n * @param format - The target config format.\n * @returns The serialized string representation.\n */\nexport function serializeContent(data: unknown, format: ConfigFormat): string {\n return match(format)\n .with('json', () => {\n const [, json] = jsonStringify(data, { pretty: true })\n return `${json}\\n`\n })\n .with('jsonc', () => {\n const [, json] = jsonStringify(data, { pretty: true })\n return `${json}\\n`\n })\n .with('yaml', () => stringifyYaml(data))\n .exhaustive()\n}\n\n/**\n * Get the file extension string for a given config format.\n *\n * @param format - The config format.\n * @returns The file extension including the leading dot (e.g. '.json').\n */\nexport function getExtension(format: ConfigFormat): string {\n return match(format)\n .with('json', () => '.json')\n .with('jsonc', () => '.jsonc')\n .with('yaml', () => '.yaml')\n .exhaustive()\n}\n\n// ---------------------------------------------------------------------------\n// Private helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Parse a JSON string and return the result as a ConfigOperationResult.\n *\n * @param content - The raw JSON string to parse.\n * @param filePath - The file path used in error messages.\n * @returns A ConfigOperationResult with the parsed data or a parse error.\n * @private\n */\nfunction parseJson(content: string, filePath: string): ConfigOperationResult<unknown> {\n const [error, result] = jsonParse(content)\n if (error) {\n return err(`Failed to parse JSON in ${filePath}: ${error.message}`)\n }\n return [null, result]\n}\n\n/**\n * Parse a JSONC (JSON with comments) string and return the result as a ConfigOperationResult.\n *\n * @param content - The raw JSONC string to parse.\n * @param filePath - The file path used in error messages.\n * @returns A ConfigOperationResult with the parsed data or a parse error.\n * @private\n */\nfunction parseJsoncContent(content: string, filePath: string): ConfigOperationResult<unknown> {\n // Intentional mutation: jsonc-parser API requires a mutable errors array.\n // There is no immutable alternative — the parser populates it during parsing.\n const errors: ParseError[] = []\n const result = parseJsonc(content, errors, {\n allowEmptyContent: false,\n allowTrailingComma: true,\n })\n if (errors.length > EMPTY_LENGTH) {\n const errorMessages = errors\n .map(\n (parseError) =>\n ` - ${printParseErrorCode(parseError.error)} at offset ${parseError.offset}`\n )\n .join('\\n')\n return err(`Failed to parse JSONC in ${filePath}:\\n${errorMessages}`)\n }\n return [null, result]\n}\n\n/**\n * Parse a YAML string and return the result as a ConfigOperationResult.\n *\n * @param content - The raw YAML string to parse.\n * @param filePath - The file path used in error messages.\n * @returns A ConfigOperationResult with the parsed data or a parse error.\n * @private\n */\nfunction parseYamlContent(content: string, filePath: string): ConfigOperationResult<unknown> {\n const [error, result] = attempt(() => parseYaml(content))\n if (error) {\n return err(`Failed to parse YAML in ${filePath}: ${String(error)}`)\n }\n return [null, result]\n}\n","import { mkdir, readFile, writeFile } from 'node:fs/promises'\nimport { dirname, join } from 'node:path'\n\nimport { attemptAsync, err, match } from '@kidd-cli/utils/fp'\nimport { formatZodIssues } from '@kidd-cli/utils/validate'\nimport type { ZodTypeAny, output } from 'zod'\n\nimport { findConfig, getConfigFileNames } from './find.js'\nimport { getExtension, getFormat, parseContent, serializeContent } from './parse.js'\nimport type {\n Config,\n ConfigOperationResult,\n ConfigOptions,\n ConfigResult,\n ConfigWriteOptions,\n ConfigWriteResult,\n} from './types.js'\n\n/**\n * Create a typed config client that loads, validates, and writes config files.\n *\n * @param options - Config client options including name and Zod schema.\n * @returns A {@link Config} client instance.\n */\nexport function createConfigClient<TSchema extends ZodTypeAny>(\n options: ConfigOptions<TSchema>\n): Config<output<TSchema>> {\n const { name, schema, searchPaths } = options\n const fileNames = getConfigFileNames(name)\n\n /**\n * Find a config file in the given directory.\n *\n * @private\n * @param cwd - Working directory to search from.\n * @returns The path to the config file, or null if not found.\n */\n async function find(cwd?: string): Promise<string | null> {\n return findConfig({\n cwd: cwd ?? process.cwd(),\n fileNames,\n searchPaths,\n })\n }\n\n /**\n * Load and validate a config file.\n *\n * @private\n * @param cwd - Working directory to search from.\n * @returns A ConfigOperationResult with the loaded config, or [null, null] if not found.\n */\n async function load(\n cwd?: string\n ): Promise<ConfigOperationResult<ConfigResult<output<TSchema>>> | readonly [null, null]> {\n const filePath = await find(cwd)\n if (!filePath) {\n return [null, null]\n }\n\n const [readError, content] = await attemptAsync(() => readFile(filePath, 'utf8'))\n if (readError || content === null) {\n const errorDetail = resolveReadErrorDetail(readError)\n return err(`Failed to read config at ${filePath}: ${errorDetail}`)\n }\n\n const format = getFormat(filePath)\n const parsedResult = parseContent({ content, filePath, format })\n\n if (parsedResult[0]) {\n return [parsedResult[0], null]\n }\n\n const result = schema.safeParse(parsedResult[1])\n if (!result.success) {\n const { message } = formatZodIssues(result.error.issues, '\\n')\n return err(`Invalid config in ${filePath}:\\n${message}`)\n }\n\n return [\n null,\n {\n config: result.data,\n filePath,\n format,\n },\n ]\n }\n\n /**\n * Validate and write config data to a file.\n *\n * @private\n * @param data - The config data to write.\n * @param writeOptions - Write options including path and format.\n * @returns A ConfigOperationResult with the write result.\n */\n async function write(\n data: output<TSchema>,\n writeOptions: ConfigWriteOptions = {}\n ): Promise<ConfigOperationResult<ConfigWriteResult>> {\n const result = schema.safeParse(data)\n if (!result.success) {\n const { message } = formatZodIssues(result.error.issues, '\\n')\n return err(`Invalid config data:\\n${message}`)\n }\n\n const resolvedFormat = match(writeOptions)\n .when(\n (opts) => opts.format !== null && opts.format !== undefined,\n (opts) => opts.format ?? ('jsonc' as const)\n )\n .when(\n (opts) => opts.filePath !== null && opts.filePath !== undefined,\n (opts) => getFormat(opts.filePath ?? '')\n )\n .otherwise(() => 'jsonc' as const)\n\n const resolvedFilePath = match(writeOptions.filePath)\n .when(\n (fp) => fp !== null && fp !== undefined,\n (fp) => fp ?? ''\n )\n .otherwise(() => {\n const dir = writeOptions.dir ?? process.cwd()\n const ext = getExtension(resolvedFormat)\n return join(dir, `.${name}${ext}`)\n })\n\n const serialized = serializeContent(result.data, resolvedFormat)\n\n const [mkdirError] = await attemptAsync(() =>\n mkdir(dirname(resolvedFilePath), { recursive: true })\n )\n if (mkdirError) {\n return err(`Failed to create directory for ${resolvedFilePath}: ${String(mkdirError)}`)\n }\n\n const [writeError] = await attemptAsync(() => writeFile(resolvedFilePath, serialized, 'utf8'))\n if (writeError) {\n return err(`Failed to write config to ${resolvedFilePath}: ${String(writeError)}`)\n }\n\n return [null, { filePath: resolvedFilePath, format: resolvedFormat }]\n }\n\n return { find, load, write }\n}\n\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve the error detail string from a read error.\n *\n * @private\n * @param readError - The error from the read operation, or null.\n * @returns A descriptive error string.\n */\nfunction resolveReadErrorDetail(readError: unknown): string {\n if (readError) {\n return String(readError)\n }\n return 'empty file'\n}\n"],"mappings":"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"}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { n as resolveLocalPath, t as resolveGlobalPath } from "./project-
|
|
1
|
+
import { n as resolveLocalPath, t as resolveGlobalPath } from "./project-D0g84bZY.js";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { attempt, err, match, ok } from "@kidd-cli/utils/fp";
|
|
4
4
|
import { jsonParse, jsonStringify } from "@kidd-cli/utils/json";
|
|
5
5
|
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
6
|
-
|
|
7
6
|
//#region src/lib/store/create-store.ts
|
|
8
7
|
/**
|
|
9
8
|
* Create a file-backed {@link FileStore} that resolves JSON files from project-local
|
|
@@ -219,7 +218,7 @@ function createStore(options) {
|
|
|
219
218
|
function resolveSaveDir(options) {
|
|
220
219
|
return match(options.source).with("local", () => options.localDir).with("global", () => options.globalDir).exhaustive();
|
|
221
220
|
}
|
|
222
|
-
|
|
223
221
|
//#endregion
|
|
224
222
|
export { createStore as t };
|
|
225
|
-
|
|
223
|
+
|
|
224
|
+
//# sourceMappingURL=create-store-OHdkm_Yt.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-store-D-fQpCql.js","names":[],"sources":["../src/lib/store/create-store.ts"],"sourcesContent":["import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs'\nimport { join } from 'node:path'\n\nimport { attempt, err, match, ok } from '@kidd-cli/utils/fp'\nimport type { Result } from '@kidd-cli/utils/fp'\nimport { jsonParse, jsonStringify } from '@kidd-cli/utils/json'\n\nimport { resolveGlobalPath, resolveLocalPath } from '@/lib/project/index.js'\nimport type { PathSource } from '@/lib/project/types.js'\n\nimport type { FileStore, LoadOptions, SaveOptions, StoreOptions } from './types.js'\n\n/**\n * Create a file-backed {@link FileStore} that resolves JSON files from project-local\n * or global home directories.\n *\n * @param options - Store configuration.\n * @returns A FileStore instance.\n */\nexport function createStore<TData = unknown>(options: StoreOptions<TData>): FileStore<TData> {\n const { dirName, defaults } = options\n\n /**\n * Resolve the local project directory for the store.\n *\n * @private\n * @param startDir - Optional directory to start searching from.\n * @returns The local directory path, or null if no project root is found.\n */\n function getLocalDir(startDir?: string): string | null {\n return resolveLocalPath({ dirName, startDir })\n }\n\n /**\n * Resolve the global home directory for the store.\n *\n * @private\n * @returns The global directory path.\n */\n function getGlobalDir(): string {\n return resolveGlobalPath({ dirName })\n }\n\n /**\n * Read the raw string content from a file path.\n *\n * @private\n * @param filePath - The file path to read.\n * @returns The file content, or null if the file does not exist or cannot be read.\n */\n function loadFromPath(filePath: string): string | null {\n const [error, content] = attempt(() => readFileSync(filePath, 'utf8'))\n\n if (error) {\n return null\n }\n\n return content\n }\n\n /**\n * Resolve a file from local or global directories based on the source strategy.\n *\n * @private\n * @param resolveOptions - Resolution options.\n * @returns The resolved result, or null if not found.\n */\n function resolveFromSource<T>(resolveOptions: {\n source: PathSource\n localDir: string | null\n globalDir: string\n filename: string\n handler: (filePath: string) => T | null\n }): T | null {\n return match(resolveOptions.source)\n .with('local', (): T | null => {\n if (!resolveOptions.localDir) {\n return null\n }\n return resolveOptions.handler(join(resolveOptions.localDir, resolveOptions.filename))\n })\n .with('global', () =>\n resolveOptions.handler(join(resolveOptions.globalDir, resolveOptions.filename))\n )\n .with('resolve', (): T | null => {\n if (resolveOptions.localDir) {\n const localResult = resolveOptions.handler(\n join(resolveOptions.localDir, resolveOptions.filename)\n )\n if (localResult !== null) {\n return localResult\n }\n }\n return resolveOptions.handler(join(resolveOptions.globalDir, resolveOptions.filename))\n })\n .exhaustive()\n }\n\n /**\n * Load the raw string content of a store file.\n *\n * @private\n * @param filename - The filename to load.\n * @param loadOptions - Options controlling source resolution.\n * @returns The raw file content, or null if not found.\n */\n function loadRaw(filename: string, loadOptions: LoadOptions = {}): string | null {\n const { source: loadSource = 'resolve', startDir } = loadOptions\n const localDir = getLocalDir(startDir)\n const globalDir = getGlobalDir()\n\n return resolveFromSource<string>({\n filename,\n globalDir,\n handler: loadFromPath,\n localDir,\n source: loadSource,\n })\n }\n\n /**\n * Load and parse a store file as JSON, merging with defaults if available.\n *\n * @private\n * @param filename - The filename to load.\n * @param loadOptions - Options controlling source resolution.\n * @returns The parsed data, defaults, or null.\n */\n function load(filename: string, loadOptions: LoadOptions = {}): TData | null {\n const raw = loadRaw(filename, loadOptions)\n\n if (raw === null) {\n return defaults ?? null\n }\n\n const [parseError, parsed] = jsonParse(raw)\n if (parseError) {\n return defaults ?? null\n }\n\n if (defaults) {\n return { ...defaults, ...(parsed as Partial<TData>) }\n }\n return parsed as TData\n }\n\n /**\n * Check if a file exists at the given path and return the path if so.\n *\n * @private\n * @param filePath - The file path to check.\n * @returns The file path if it exists, or null.\n */\n function checkFileExists(filePath: string): string | null {\n if (existsSync(filePath)) {\n return filePath\n }\n return null\n }\n\n /**\n * Resolve the file path for a store file without reading its content.\n *\n * @private\n * @param filename - The filename to resolve.\n * @param loadOptions - Options controlling source resolution.\n * @returns The resolved file path, or null if not found.\n */\n function getFilePath(filename: string, loadOptions: LoadOptions = {}): string | null {\n const { source: fileSource = 'resolve', startDir } = loadOptions\n const localDir = getLocalDir(startDir)\n const globalDir = getGlobalDir()\n\n return resolveFromSource<string>({\n filename,\n globalDir,\n handler: checkFileExists,\n localDir,\n source: fileSource,\n })\n }\n\n /**\n * Serialize data to JSON and write it to a store file.\n *\n * Creates the target directory if it does not exist. Defaults to\n * the global home directory when no source is specified.\n *\n * @private\n * @param filename - The filename to write.\n * @param data - The data to serialize.\n * @param saveOptions - Options controlling the write target.\n * @returns A Result with the written file path on success.\n */\n function save(filename: string, data: unknown, saveOptions: SaveOptions = {}): Result<string> {\n const { source: saveSource = 'global', startDir } = saveOptions\n\n const dir = resolveSaveDir({\n globalDir: getGlobalDir(),\n localDir: getLocalDir(startDir),\n source: saveSource,\n })\n\n if (dir === null) {\n return err(new Error(`Cannot save to \"${saveSource}\" — no local project directory found`))\n }\n\n const [stringifyError, json] = jsonStringify(data, { pretty: true })\n\n if (stringifyError) {\n return err(stringifyError)\n }\n\n const filePath = join(dir, filename)\n\n const [writeError] = attempt(() => {\n mkdirSync(dir, { mode: 0o700, recursive: true })\n writeFileSync(filePath, json, { encoding: 'utf8', mode: 0o600 })\n })\n\n if (writeError) {\n return err(writeError)\n }\n\n return ok(filePath)\n }\n\n /**\n * Remove a file from the store.\n *\n * Returns `ok(filePath)` when the file was deleted or did not exist\n * (idempotent). Returns an error when the target directory cannot be\n * resolved or the unlink fails.\n *\n * @private\n * @param filename - The filename to remove.\n * @param removeOptions - Options controlling the removal target.\n * @returns A Result with the file path on success.\n */\n function remove(filename: string, removeOptions: SaveOptions = {}): Result<string> {\n const { source: removeSource = 'global', startDir } = removeOptions\n\n const dir = resolveSaveDir({\n globalDir: getGlobalDir(),\n localDir: getLocalDir(startDir),\n source: removeSource,\n })\n\n if (dir === null) {\n return err(new Error(`Cannot remove from \"${removeSource}\" — no local project directory found`))\n }\n\n const filePath = join(dir, filename)\n\n if (!existsSync(filePath)) {\n return ok(filePath)\n }\n\n const [removeError] = attempt(() => {\n unlinkSync(filePath)\n })\n\n if (removeError) {\n return err(removeError)\n }\n\n return ok(filePath)\n }\n\n return {\n getFilePath,\n getGlobalDir,\n getLocalDir,\n load,\n loadRaw,\n remove,\n save,\n }\n}\n\n// ---------------------------------------------------------------------------\n// Private helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve the target directory for a save operation.\n *\n * @private\n * @param options - Resolution options.\n * @returns The directory path, or null when `local` is requested but unavailable.\n */\nfunction resolveSaveDir(options: {\n readonly localDir: string | null\n readonly globalDir: string\n readonly source: 'local' | 'global'\n}): string | null {\n return match(options.source)\n .with('local', (): string | null => options.localDir)\n .with('global', () => options.globalDir)\n .exhaustive()\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"create-store-OHdkm_Yt.js","names":[],"sources":["../src/lib/store/create-store.ts"],"sourcesContent":["import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs'\nimport { join } from 'node:path'\n\nimport { attempt, err, match, ok } from '@kidd-cli/utils/fp'\nimport type { Result } from '@kidd-cli/utils/fp'\nimport { jsonParse, jsonStringify } from '@kidd-cli/utils/json'\n\nimport { resolveGlobalPath, resolveLocalPath } from '@/lib/project/index.js'\nimport type { PathSource } from '@/lib/project/types.js'\n\nimport type { FileStore, LoadOptions, SaveOptions, StoreOptions } from './types.js'\n\n/**\n * Create a file-backed {@link FileStore} that resolves JSON files from project-local\n * or global home directories.\n *\n * @param options - Store configuration.\n * @returns A FileStore instance.\n */\nexport function createStore<TData = unknown>(options: StoreOptions<TData>): FileStore<TData> {\n const { dirName, defaults } = options\n\n /**\n * Resolve the local project directory for the store.\n *\n * @private\n * @param startDir - Optional directory to start searching from.\n * @returns The local directory path, or null if no project root is found.\n */\n function getLocalDir(startDir?: string): string | null {\n return resolveLocalPath({ dirName, startDir })\n }\n\n /**\n * Resolve the global home directory for the store.\n *\n * @private\n * @returns The global directory path.\n */\n function getGlobalDir(): string {\n return resolveGlobalPath({ dirName })\n }\n\n /**\n * Read the raw string content from a file path.\n *\n * @private\n * @param filePath - The file path to read.\n * @returns The file content, or null if the file does not exist or cannot be read.\n */\n function loadFromPath(filePath: string): string | null {\n const [error, content] = attempt(() => readFileSync(filePath, 'utf8'))\n\n if (error) {\n return null\n }\n\n return content\n }\n\n /**\n * Resolve a file from local or global directories based on the source strategy.\n *\n * @private\n * @param resolveOptions - Resolution options.\n * @returns The resolved result, or null if not found.\n */\n function resolveFromSource<T>(resolveOptions: {\n source: PathSource\n localDir: string | null\n globalDir: string\n filename: string\n handler: (filePath: string) => T | null\n }): T | null {\n return match(resolveOptions.source)\n .with('local', (): T | null => {\n if (!resolveOptions.localDir) {\n return null\n }\n return resolveOptions.handler(join(resolveOptions.localDir, resolveOptions.filename))\n })\n .with('global', () =>\n resolveOptions.handler(join(resolveOptions.globalDir, resolveOptions.filename))\n )\n .with('resolve', (): T | null => {\n if (resolveOptions.localDir) {\n const localResult = resolveOptions.handler(\n join(resolveOptions.localDir, resolveOptions.filename)\n )\n if (localResult !== null) {\n return localResult\n }\n }\n return resolveOptions.handler(join(resolveOptions.globalDir, resolveOptions.filename))\n })\n .exhaustive()\n }\n\n /**\n * Load the raw string content of a store file.\n *\n * @private\n * @param filename - The filename to load.\n * @param loadOptions - Options controlling source resolution.\n * @returns The raw file content, or null if not found.\n */\n function loadRaw(filename: string, loadOptions: LoadOptions = {}): string | null {\n const { source: loadSource = 'resolve', startDir } = loadOptions\n const localDir = getLocalDir(startDir)\n const globalDir = getGlobalDir()\n\n return resolveFromSource<string>({\n filename,\n globalDir,\n handler: loadFromPath,\n localDir,\n source: loadSource,\n })\n }\n\n /**\n * Load and parse a store file as JSON, merging with defaults if available.\n *\n * @private\n * @param filename - The filename to load.\n * @param loadOptions - Options controlling source resolution.\n * @returns The parsed data, defaults, or null.\n */\n function load(filename: string, loadOptions: LoadOptions = {}): TData | null {\n const raw = loadRaw(filename, loadOptions)\n\n if (raw === null) {\n return defaults ?? null\n }\n\n const [parseError, parsed] = jsonParse(raw)\n if (parseError) {\n return defaults ?? null\n }\n\n if (defaults) {\n return { ...defaults, ...(parsed as Partial<TData>) }\n }\n return parsed as TData\n }\n\n /**\n * Check if a file exists at the given path and return the path if so.\n *\n * @private\n * @param filePath - The file path to check.\n * @returns The file path if it exists, or null.\n */\n function checkFileExists(filePath: string): string | null {\n if (existsSync(filePath)) {\n return filePath\n }\n return null\n }\n\n /**\n * Resolve the file path for a store file without reading its content.\n *\n * @private\n * @param filename - The filename to resolve.\n * @param loadOptions - Options controlling source resolution.\n * @returns The resolved file path, or null if not found.\n */\n function getFilePath(filename: string, loadOptions: LoadOptions = {}): string | null {\n const { source: fileSource = 'resolve', startDir } = loadOptions\n const localDir = getLocalDir(startDir)\n const globalDir = getGlobalDir()\n\n return resolveFromSource<string>({\n filename,\n globalDir,\n handler: checkFileExists,\n localDir,\n source: fileSource,\n })\n }\n\n /**\n * Serialize data to JSON and write it to a store file.\n *\n * Creates the target directory if it does not exist. Defaults to\n * the global home directory when no source is specified.\n *\n * @private\n * @param filename - The filename to write.\n * @param data - The data to serialize.\n * @param saveOptions - Options controlling the write target.\n * @returns A Result with the written file path on success.\n */\n function save(filename: string, data: unknown, saveOptions: SaveOptions = {}): Result<string> {\n const { source: saveSource = 'global', startDir } = saveOptions\n\n const dir = resolveSaveDir({\n globalDir: getGlobalDir(),\n localDir: getLocalDir(startDir),\n source: saveSource,\n })\n\n if (dir === null) {\n return err(new Error(`Cannot save to \"${saveSource}\" — no local project directory found`))\n }\n\n const [stringifyError, json] = jsonStringify(data, { pretty: true })\n\n if (stringifyError) {\n return err(stringifyError)\n }\n\n const filePath = join(dir, filename)\n\n const [writeError] = attempt(() => {\n mkdirSync(dir, { mode: 0o700, recursive: true })\n writeFileSync(filePath, json, { encoding: 'utf8', mode: 0o600 })\n })\n\n if (writeError) {\n return err(writeError)\n }\n\n return ok(filePath)\n }\n\n /**\n * Remove a file from the store.\n *\n * Returns `ok(filePath)` when the file was deleted or did not exist\n * (idempotent). Returns an error when the target directory cannot be\n * resolved or the unlink fails.\n *\n * @private\n * @param filename - The filename to remove.\n * @param removeOptions - Options controlling the removal target.\n * @returns A Result with the file path on success.\n */\n function remove(filename: string, removeOptions: SaveOptions = {}): Result<string> {\n const { source: removeSource = 'global', startDir } = removeOptions\n\n const dir = resolveSaveDir({\n globalDir: getGlobalDir(),\n localDir: getLocalDir(startDir),\n source: removeSource,\n })\n\n if (dir === null) {\n return err(\n new Error(`Cannot remove from \"${removeSource}\" — no local project directory found`)\n )\n }\n\n const filePath = join(dir, filename)\n\n if (!existsSync(filePath)) {\n return ok(filePath)\n }\n\n const [removeError] = attempt(() => {\n unlinkSync(filePath)\n })\n\n if (removeError) {\n return err(removeError)\n }\n\n return ok(filePath)\n }\n\n return {\n getFilePath,\n getGlobalDir,\n getLocalDir,\n load,\n loadRaw,\n remove,\n save,\n }\n}\n\n// ---------------------------------------------------------------------------\n// Private helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve the target directory for a save operation.\n *\n * @private\n * @param options - Resolution options.\n * @returns The directory path, or null when `local` is requested but unavailable.\n */\nfunction resolveSaveDir(options: {\n readonly localDir: string | null\n readonly globalDir: string\n readonly source: 'local' | 'global'\n}): string | null {\n return match(options.source)\n .with('local', (): string | null => options.localDir)\n .with('global', () => options.globalDir)\n .exhaustive()\n}\n"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as CommandDef, c as
|
|
1
|
+
import { a as CommandDef, c as Middleware, d as Context, i as Command, l as MiddlewareEnv, n as AutoloadOptions, o as CommandMap, r as CliOptions, s as InferVariables, t as ArgsDef, u as MiddlewareFn } from "./types-CTvrsrnD.js";
|
|
2
2
|
import { defineConfig } from "@kidd-cli/config";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
|
|
@@ -26,6 +26,35 @@ declare function cli<TSchema extends z.ZodType = z.ZodType>(options: CliOptions<
|
|
|
26
26
|
*/
|
|
27
27
|
declare function command<TArgsDef extends ArgsDef = ArgsDef, TConfig extends Record<string, unknown> = Record<string, unknown>, const TMiddleware extends readonly Middleware<MiddlewareEnv>[] = readonly Middleware<MiddlewareEnv>[]>(def: CommandDef<TArgsDef, TConfig, TMiddleware>): Command;
|
|
28
28
|
//#endregion
|
|
29
|
+
//#region src/compose.d.ts
|
|
30
|
+
/**
|
|
31
|
+
* The composed return environment. Uses a conditional type to normalize
|
|
32
|
+
* `unknown` (produced by empty tuples) into `Record<string, unknown>`,
|
|
33
|
+
* satisfying the `MiddlewareEnv` constraint.
|
|
34
|
+
*/
|
|
35
|
+
interface ComposedEnv<TMiddleware extends readonly Middleware<MiddlewareEnv>[]> {
|
|
36
|
+
readonly Variables: InferVariables<TMiddleware> extends infer V ? unknown extends V ? Record<string, unknown> : V : never;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Compose multiple middleware into a single middleware.
|
|
40
|
+
*
|
|
41
|
+
* Executes each middleware in order, threading `next()` through the chain.
|
|
42
|
+
* The final `next()` call from the last composed middleware continues to
|
|
43
|
+
* the downstream middleware or command handler.
|
|
44
|
+
*
|
|
45
|
+
* The returned middleware's type merges all `Variables` from the input tuple,
|
|
46
|
+
* so downstream handlers see the combined context.
|
|
47
|
+
*
|
|
48
|
+
* @param middlewares - An ordered tuple of middleware to compose.
|
|
49
|
+
* @returns A single Middleware whose Variables is the intersection of all input Variables.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```ts
|
|
53
|
+
* const combined = compose([auth({ strategies: [auth.env()] }), auth.require()])
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
declare function compose<const TMiddleware extends readonly Middleware<MiddlewareEnv>[]>(middlewares: TMiddleware): Middleware<ComposedEnv<TMiddleware>>;
|
|
57
|
+
//#endregion
|
|
29
58
|
//#region src/autoloader.d.ts
|
|
30
59
|
/**
|
|
31
60
|
* Scan a directory for command files and produce a CommandMap.
|
|
@@ -84,5 +113,5 @@ declare function decorateContext<TKey extends string, TValue>(ctx: Context, key:
|
|
|
84
113
|
*/
|
|
85
114
|
declare function middleware<TEnv extends MiddlewareEnv = MiddlewareEnv>(handler: MiddlewareFn<TEnv>): Middleware<TEnv>;
|
|
86
115
|
//#endregion
|
|
87
|
-
export { type Command, type Context, type MiddlewareEnv, autoload, cli, command, decorateContext, defineConfig, middleware };
|
|
116
|
+
export { type Command, type Context, type MiddlewareEnv, autoload, cli, command, compose, decorateContext, defineConfig, middleware };
|
|
88
117
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/cli.ts","../src/command.ts","../src/autoloader.ts","../src/context/decorate.ts","../src/middleware.ts"],"mappings":";;;;;;;;AAyBA;;;;;iBAAsB,GAAA,iBAAoB,CAAA,CAAE,OAAA,GAAU,CAAA,CAAE,OAAA,CAAA,CACtD,OAAA,EAAS,UAAA,CAAW,OAAA,IACnB,OAAA;;;;;;;AAFH;;;;;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/cli.ts","../src/command.ts","../src/compose.ts","../src/autoloader.ts","../src/context/decorate.ts","../src/middleware.ts"],"mappings":";;;;;;;;AAyBA;;;;;iBAAsB,GAAA,iBAAoB,CAAA,CAAE,OAAA,GAAU,CAAA,CAAE,OAAA,CAAA,CACtD,OAAA,EAAS,UAAA,CAAW,OAAA,IACnB,OAAA;;;;;;;AAFH;;;;;;iBCLgB,OAAA,kBACG,OAAA,GAAU,OAAA,kBACX,MAAA,oBAA0B,MAAA,sDACP,UAAA,CAAW,aAAA,eACnC,UAAA,CAAW,aAAA,IAAA,CACtB,GAAA,EAAK,UAAA,CAAW,QAAA,EAAU,OAAA,EAAS,WAAA,IAAe,OAAA;;;;;;;ADApD;UEXU,WAAA,8BAAyC,UAAA,CAAW,aAAA;EAAA,SACnD,SAAA,EAAW,cAAA,CAAe,WAAA,oCACf,CAAA,GACd,MAAA,oBACA,CAAA;AAAA;;;;;;;;;;;;;;;;;;;iBAsBQ,OAAA,oCAA2C,UAAA,CAAW,aAAA,IAAA,CACpE,WAAA,EAAa,WAAA,GACZ,UAAA,CAAW,WAAA,CAAY,WAAA;;;;;;;AFjB1B;;iBGPsB,QAAA,CAAS,OAAA,GAAU,eAAA,GAAkB,OAAA,CAAQ,UAAA;;;;;;;AHOnE;;;;;;;;;;;;;;;;;;;;;;iBIGgB,eAAA,6BAAA,CACd,GAAA,EAAK,OAAA,EACL,GAAA,EAAK,IAAA,EACL,KAAA,EAAO,MAAA,GACN,OAAA;;;;;;;AJPH;;;;;;;;;;;;;iBKJgB,UAAA,cAAwB,aAAA,GAAgB,aAAA,CAAA,CACtD,OAAA,EAAS,YAAA,CAAa,IAAA,IACrB,UAAA,CAAW,IAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { createCliLogger } from "./lib/logger.js";
|
|
2
|
-
import { n as
|
|
3
|
-
import
|
|
4
|
-
import "./
|
|
2
|
+
import { n as decorateContext, t as middleware } from "./middleware-BWnPSRWR.js";
|
|
3
|
+
import "./project-D0g84bZY.js";
|
|
4
|
+
import { t as createConfigClient } from "./config-D8e5qxLp.js";
|
|
5
5
|
import { basename, extname, join, resolve } from "node:path";
|
|
6
6
|
import { loadConfig } from "@kidd-cli/config/loader";
|
|
7
|
-
import { attemptAsync, err, isPlainObject, isString, ok } from "@kidd-cli/utils/fp";
|
|
7
|
+
import { P, attemptAsync, err, isPlainObject, isString, match, ok } from "@kidd-cli/utils/fp";
|
|
8
8
|
import yargs from "yargs";
|
|
9
9
|
import * as clack from "@clack/prompts";
|
|
10
10
|
import { TAG, hasTag, withTag } from "@kidd-cli/utils/tag";
|
|
@@ -13,7 +13,6 @@ import { readdir } from "node:fs/promises";
|
|
|
13
13
|
import { formatZodIssues } from "@kidd-cli/utils/validate";
|
|
14
14
|
import { match as match$1 } from "ts-pattern";
|
|
15
15
|
import { defineConfig } from "@kidd-cli/config";
|
|
16
|
-
|
|
17
16
|
//#region src/context/error.ts
|
|
18
17
|
/**
|
|
19
18
|
* Create a ContextError with an exit code and optional error code.
|
|
@@ -63,7 +62,7 @@ function isContextError(error) {
|
|
|
63
62
|
}
|
|
64
63
|
function resolveExitCode(options) {
|
|
65
64
|
if (options && options.exitCode !== void 0) return options.exitCode;
|
|
66
|
-
return
|
|
65
|
+
return 1;
|
|
67
66
|
}
|
|
68
67
|
function resolveCode(options) {
|
|
69
68
|
if (options && options.code !== void 0) return options.code;
|
|
@@ -75,7 +74,6 @@ function createContextErrorData(message, options) {
|
|
|
75
74
|
message
|
|
76
75
|
}, "ContextError");
|
|
77
76
|
}
|
|
78
|
-
|
|
79
77
|
//#endregion
|
|
80
78
|
//#region src/context/output.ts
|
|
81
79
|
/**
|
|
@@ -192,7 +190,6 @@ function writeTableToStream(stream, rows, keys) {
|
|
|
192
190
|
].join("\n");
|
|
193
191
|
stream.write(`${content}\n`);
|
|
194
192
|
}
|
|
195
|
-
|
|
196
193
|
//#endregion
|
|
197
194
|
//#region src/context/prompts.ts
|
|
198
195
|
/**
|
|
@@ -234,12 +231,11 @@ function unwrapCancelSignal(result) {
|
|
|
234
231
|
clack.cancel("Operation cancelled.");
|
|
235
232
|
throw createContextError("Prompt cancelled by user", {
|
|
236
233
|
code: "PROMPT_CANCELLED",
|
|
237
|
-
exitCode:
|
|
234
|
+
exitCode: 1
|
|
238
235
|
});
|
|
239
236
|
}
|
|
240
237
|
return result;
|
|
241
238
|
}
|
|
242
|
-
|
|
243
239
|
//#endregion
|
|
244
240
|
//#region src/context/store.ts
|
|
245
241
|
/**
|
|
@@ -268,7 +264,6 @@ function createMemoryStore() {
|
|
|
268
264
|
}
|
|
269
265
|
};
|
|
270
266
|
}
|
|
271
|
-
|
|
272
267
|
//#endregion
|
|
273
268
|
//#region src/context/create-context.ts
|
|
274
269
|
/**
|
|
@@ -306,7 +301,6 @@ function createContext(options) {
|
|
|
306
301
|
store: ctxStore
|
|
307
302
|
};
|
|
308
303
|
}
|
|
309
|
-
|
|
310
304
|
//#endregion
|
|
311
305
|
//#region src/autoloader.ts
|
|
312
306
|
const VALID_EXTENSIONS = new Set([
|
|
@@ -426,7 +420,8 @@ async function importCommand(filePath) {
|
|
|
426
420
|
*/
|
|
427
421
|
function isCommandExport(mod) {
|
|
428
422
|
if (typeof mod !== "object" || mod === null) return false;
|
|
429
|
-
|
|
423
|
+
if (!("default" in mod)) return false;
|
|
424
|
+
const def = mod.default;
|
|
430
425
|
if (!isPlainObject(def)) return false;
|
|
431
426
|
return hasTag(def, "Command");
|
|
432
427
|
}
|
|
@@ -464,7 +459,6 @@ function isCommandDir(entry) {
|
|
|
464
459
|
if (!entry.isDirectory()) return false;
|
|
465
460
|
return !entry.name.startsWith("_") && !entry.name.startsWith(".");
|
|
466
461
|
}
|
|
467
|
-
|
|
468
462
|
//#endregion
|
|
469
463
|
//#region src/runtime/args/zod.ts
|
|
470
464
|
/**
|
|
@@ -624,7 +618,6 @@ function getZodTypeOption(schema) {
|
|
|
624
618
|
};
|
|
625
619
|
return base;
|
|
626
620
|
}
|
|
627
|
-
|
|
628
621
|
//#endregion
|
|
629
622
|
//#region src/runtime/args/parser.ts
|
|
630
623
|
/**
|
|
@@ -671,7 +664,6 @@ function validateArgs(argsDef, parsedArgs) {
|
|
|
671
664
|
if (!result.success) return err(/* @__PURE__ */ new Error(`Invalid arguments:\n ${formatZodIssues(result.error.issues).message}`));
|
|
672
665
|
return ok(result.data);
|
|
673
666
|
}
|
|
674
|
-
|
|
675
667
|
//#endregion
|
|
676
668
|
//#region src/runtime/args/register.ts
|
|
677
669
|
/**
|
|
@@ -688,10 +680,7 @@ function registerCommandArgs(builder, args) {
|
|
|
688
680
|
if (isZodSchema(args)) {
|
|
689
681
|
const options = zodSchemaToYargsOptions(args);
|
|
690
682
|
for (const [key, opt] of Object.entries(options)) builder.option(key, opt);
|
|
691
|
-
} else
|
|
692
|
-
const argsDef = args;
|
|
693
|
-
for (const [key, def] of Object.entries(argsDef)) builder.option(key, yargsArgDefToOption(def));
|
|
694
|
-
}
|
|
683
|
+
} else for (const [key, def] of Object.entries(args)) builder.option(key, yargsArgDefToOption(def));
|
|
695
684
|
}
|
|
696
685
|
/**
|
|
697
686
|
* Convert a yargs-native arg definition into a yargs option object.
|
|
@@ -710,7 +699,6 @@ function yargsArgDefToOption(def) {
|
|
|
710
699
|
type: def.type
|
|
711
700
|
};
|
|
712
701
|
}
|
|
713
|
-
|
|
714
702
|
//#endregion
|
|
715
703
|
//#region src/runtime/register.ts
|
|
716
704
|
/**
|
|
@@ -733,7 +721,7 @@ function isCommand(value) {
|
|
|
733
721
|
*/
|
|
734
722
|
function registerCommands(options) {
|
|
735
723
|
const { instance, commands, resolved, parentPath } = options;
|
|
736
|
-
const commandEntries = Object.entries(commands).filter((
|
|
724
|
+
const commandEntries = Object.entries(commands).filter((pair) => isCommand(pair[1]));
|
|
737
725
|
for (const [name, entry] of commandEntries) registerResolvedCommand({
|
|
738
726
|
builder: instance,
|
|
739
727
|
cmd: entry,
|
|
@@ -759,7 +747,7 @@ function registerResolvedCommand(options) {
|
|
|
759
747
|
instance.command(name, description, (builder) => {
|
|
760
748
|
registerCommandArgs(builder, cmd.args);
|
|
761
749
|
if (cmd.commands) {
|
|
762
|
-
const subCommands = Object.entries(cmd.commands).filter((
|
|
750
|
+
const subCommands = Object.entries(cmd.commands).filter((pair) => isCommand(pair[1]));
|
|
763
751
|
for (const [subName, subEntry] of subCommands) registerResolvedCommand({
|
|
764
752
|
builder,
|
|
765
753
|
cmd: subEntry,
|
|
@@ -781,7 +769,6 @@ function registerResolvedCommand(options) {
|
|
|
781
769
|
};
|
|
782
770
|
});
|
|
783
771
|
}
|
|
784
|
-
|
|
785
772
|
//#endregion
|
|
786
773
|
//#region src/runtime/runner.ts
|
|
787
774
|
/**
|
|
@@ -832,7 +819,6 @@ async function runMiddlewareChain(middlewares, ctx, finalHandler) {
|
|
|
832
819
|
}
|
|
833
820
|
await executeChain(0);
|
|
834
821
|
}
|
|
835
|
-
|
|
836
822
|
//#endregion
|
|
837
823
|
//#region src/runtime/runtime.ts
|
|
838
824
|
/**
|
|
@@ -855,7 +841,7 @@ async function createRuntime(options) {
|
|
|
855
841
|
args: validatedArgs,
|
|
856
842
|
config,
|
|
857
843
|
meta: {
|
|
858
|
-
command: command.commandPath,
|
|
844
|
+
command: [...command.commandPath],
|
|
859
845
|
name: options.name,
|
|
860
846
|
version: options.version
|
|
861
847
|
}
|
|
@@ -890,7 +876,6 @@ async function resolveConfig(configOptions, defaultName) {
|
|
|
890
876
|
if (configError || !configResult) return {};
|
|
891
877
|
return configResult.config;
|
|
892
878
|
}
|
|
893
|
-
|
|
894
879
|
//#endregion
|
|
895
880
|
//#region src/cli.ts
|
|
896
881
|
const ARGV_SLICE_START = 2;
|
|
@@ -1002,18 +987,19 @@ function applyCwd(argv) {
|
|
|
1002
987
|
* @param logger - Logger with an error method for output.
|
|
1003
988
|
*/
|
|
1004
989
|
function exitOnError(error, logger) {
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
}
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
}
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
}
|
|
990
|
+
const info = match(error).when(isContextError, (e) => ({
|
|
991
|
+
exitCode: e.exitCode,
|
|
992
|
+
message: e.message
|
|
993
|
+
})).with(P.instanceOf(Error), (e) => ({
|
|
994
|
+
exitCode: 1,
|
|
995
|
+
message: e.message
|
|
996
|
+
})).otherwise((e) => ({
|
|
997
|
+
exitCode: 1,
|
|
998
|
+
message: String(e)
|
|
999
|
+
}));
|
|
1000
|
+
logger.error(info.message);
|
|
1001
|
+
process.exit(info.exitCode);
|
|
1015
1002
|
}
|
|
1016
|
-
|
|
1017
1003
|
//#endregion
|
|
1018
1004
|
//#region src/command.ts
|
|
1019
1005
|
/**
|
|
@@ -1029,7 +1015,56 @@ function exitOnError(error, logger) {
|
|
|
1029
1015
|
function command(def) {
|
|
1030
1016
|
return withTag({ ...def }, "Command");
|
|
1031
1017
|
}
|
|
1032
|
-
|
|
1033
1018
|
//#endregion
|
|
1034
|
-
|
|
1019
|
+
//#region src/compose.ts
|
|
1020
|
+
/**
|
|
1021
|
+
* Middleware combinator that merges multiple middleware into one.
|
|
1022
|
+
*
|
|
1023
|
+
* @module
|
|
1024
|
+
*/
|
|
1025
|
+
/**
|
|
1026
|
+
* Compose multiple middleware into a single middleware.
|
|
1027
|
+
*
|
|
1028
|
+
* Executes each middleware in order, threading `next()` through the chain.
|
|
1029
|
+
* The final `next()` call from the last composed middleware continues to
|
|
1030
|
+
* the downstream middleware or command handler.
|
|
1031
|
+
*
|
|
1032
|
+
* The returned middleware's type merges all `Variables` from the input tuple,
|
|
1033
|
+
* so downstream handlers see the combined context.
|
|
1034
|
+
*
|
|
1035
|
+
* @param middlewares - An ordered tuple of middleware to compose.
|
|
1036
|
+
* @returns A single Middleware whose Variables is the intersection of all input Variables.
|
|
1037
|
+
*
|
|
1038
|
+
* @example
|
|
1039
|
+
* ```ts
|
|
1040
|
+
* const combined = compose([auth({ strategies: [auth.env()] }), auth.require()])
|
|
1041
|
+
* ```
|
|
1042
|
+
*/
|
|
1043
|
+
function compose(middlewares) {
|
|
1044
|
+
return middleware((ctx, next) => executeChain(middlewares, 0, ctx, next));
|
|
1045
|
+
}
|
|
1046
|
+
/**
|
|
1047
|
+
* Recursively execute middleware in order, calling next() after the last one.
|
|
1048
|
+
*
|
|
1049
|
+
* @private
|
|
1050
|
+
* @param middlewares - The middleware array.
|
|
1051
|
+
* @param index - Current position in the array.
|
|
1052
|
+
* @param ctx - The context object.
|
|
1053
|
+
* @param next - The downstream next function.
|
|
1054
|
+
*/
|
|
1055
|
+
async function executeChain(middlewares, index, ctx, next) {
|
|
1056
|
+
if (index >= middlewares.length) {
|
|
1057
|
+
await next();
|
|
1058
|
+
return;
|
|
1059
|
+
}
|
|
1060
|
+
const mw = middlewares[index];
|
|
1061
|
+
if (mw === void 0) {
|
|
1062
|
+
await next();
|
|
1063
|
+
return;
|
|
1064
|
+
}
|
|
1065
|
+
await mw.handler(ctx, () => executeChain(middlewares, index + 1, ctx, next));
|
|
1066
|
+
}
|
|
1067
|
+
//#endregion
|
|
1068
|
+
export { autoload, cli, command, compose, decorateContext, defineConfig, middleware };
|
|
1069
|
+
|
|
1035
1070
|
//# sourceMappingURL=index.js.map
|