@kidd-cli/core 0.1.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/LICENSE +21 -0
- package/README.md +214 -0
- package/dist/config-BvGapuFJ.js +282 -0
- package/dist/config-BvGapuFJ.js.map +1 -0
- package/dist/create-store-BQUX0tAn.js +197 -0
- package/dist/create-store-BQUX0tAn.js.map +1 -0
- package/dist/index.d.ts +73 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1034 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/config.d.ts +64 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +4 -0
- package/dist/lib/logger.d.ts +2 -0
- package/dist/lib/logger.js +55 -0
- package/dist/lib/logger.js.map +1 -0
- package/dist/lib/output.d.ts +62 -0
- package/dist/lib/output.d.ts.map +1 -0
- package/dist/lib/output.js +276 -0
- package/dist/lib/output.js.map +1 -0
- package/dist/lib/project.d.ts +59 -0
- package/dist/lib/project.d.ts.map +1 -0
- package/dist/lib/project.js +3 -0
- package/dist/lib/prompts.d.ts +24 -0
- package/dist/lib/prompts.d.ts.map +1 -0
- package/dist/lib/prompts.js +3 -0
- package/dist/lib/store.d.ts +56 -0
- package/dist/lib/store.d.ts.map +1 -0
- package/dist/lib/store.js +4 -0
- package/dist/logger-BkQQej8h.d.ts +76 -0
- package/dist/logger-BkQQej8h.d.ts.map +1 -0
- package/dist/middleware/auth.d.ts +22 -0
- package/dist/middleware/auth.d.ts.map +1 -0
- package/dist/middleware/auth.js +759 -0
- package/dist/middleware/auth.js.map +1 -0
- package/dist/middleware/http.d.ts +87 -0
- package/dist/middleware/http.d.ts.map +1 -0
- package/dist/middleware/http.js +255 -0
- package/dist/middleware/http.js.map +1 -0
- package/dist/middleware-D3psyhYo.js +54 -0
- package/dist/middleware-D3psyhYo.js.map +1 -0
- package/dist/project-NPtYX2ZX.js +181 -0
- package/dist/project-NPtYX2ZX.js.map +1 -0
- package/dist/prompts-lLfUSgd6.js +63 -0
- package/dist/prompts-lLfUSgd6.js.map +1 -0
- package/dist/types-CqKJhsYk.d.ts +135 -0
- package/dist/types-CqKJhsYk.d.ts.map +1 -0
- package/dist/types-Cz9h927W.d.ts +23 -0
- package/dist/types-Cz9h927W.d.ts.map +1 -0
- package/dist/types-DFtYg5uZ.d.ts +26 -0
- package/dist/types-DFtYg5uZ.d.ts.map +1 -0
- package/dist/types-kjpRau0U.d.ts +382 -0
- package/dist/types-kjpRau0U.d.ts.map +1 -0
- package/package.json +94 -0
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { dirname, join, resolve } from "node:path";
|
|
2
|
+
import { attempt } from "@kidd-cli/utils/fp";
|
|
3
|
+
import { match as match$1 } from "ts-pattern";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
6
|
+
|
|
7
|
+
//#region src/lib/project/root.ts
|
|
8
|
+
const MIN_MODULES_PARTS = 2;
|
|
9
|
+
/**
|
|
10
|
+
* Walk up the directory tree to find the nearest git project root.
|
|
11
|
+
*
|
|
12
|
+
* @param startDir - Directory to start searching from (defaults to cwd).
|
|
13
|
+
* @returns The project root info, or null if no git root is found.
|
|
14
|
+
*/
|
|
15
|
+
function findProjectRoot(startDir = process.cwd()) {
|
|
16
|
+
/**
|
|
17
|
+
* Recursively walk up the directory tree searching for a `.git` marker.
|
|
18
|
+
*
|
|
19
|
+
* @private
|
|
20
|
+
*/
|
|
21
|
+
const findRootRecursive = (currentDir, visited) => {
|
|
22
|
+
if (visited.has(currentDir)) return null;
|
|
23
|
+
const nextVisited = new Set([...visited, currentDir]);
|
|
24
|
+
const gitPath = join(currentDir, ".git");
|
|
25
|
+
try {
|
|
26
|
+
const result = checkGitPath(gitPath, currentDir);
|
|
27
|
+
if (result) return result;
|
|
28
|
+
} catch {}
|
|
29
|
+
const parent = dirname(currentDir);
|
|
30
|
+
if (parent === currentDir) return null;
|
|
31
|
+
return findRootRecursive(parent, nextVisited);
|
|
32
|
+
};
|
|
33
|
+
return findRootRecursive(resolve(startDir), /* @__PURE__ */ new Set());
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Check whether the current directory is inside a git submodule.
|
|
37
|
+
*
|
|
38
|
+
* @param startDir - Directory to start searching from.
|
|
39
|
+
* @returns True if the directory is inside a submodule.
|
|
40
|
+
*/
|
|
41
|
+
function isInSubmodule(startDir) {
|
|
42
|
+
const projectRoot = findProjectRoot(startDir);
|
|
43
|
+
if (!projectRoot) return false;
|
|
44
|
+
return projectRoot.isSubmodule;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Resolve the parent repository root when inside a git submodule.
|
|
48
|
+
*
|
|
49
|
+
* @param startDir - Directory to start searching from.
|
|
50
|
+
* @returns The parent repository root path, or null.
|
|
51
|
+
*/
|
|
52
|
+
function getParentRepoRoot(startDir) {
|
|
53
|
+
const projectRoot = findProjectRoot(startDir);
|
|
54
|
+
if (!projectRoot || !projectRoot.isSubmodule) return null;
|
|
55
|
+
const gitFileContent = resolveGitDirFromFile(join(projectRoot.path, ".git"));
|
|
56
|
+
if (gitFileContent === null) return null;
|
|
57
|
+
return resolveParentGitDir(projectRoot, gitFileContent);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Resolve a `.git` file reference to determine if this is a submodule.
|
|
61
|
+
*
|
|
62
|
+
* @private
|
|
63
|
+
*/
|
|
64
|
+
function resolveGitFileSubmodule(gitPath, currentDir) {
|
|
65
|
+
const [readError, gitFileContent] = attempt(() => readFileSync(gitPath, "utf8").trim());
|
|
66
|
+
if (readError || gitFileContent === null) return {
|
|
67
|
+
isSubmodule: false,
|
|
68
|
+
path: currentDir
|
|
69
|
+
};
|
|
70
|
+
const gitDirMatch = gitFileContent.match(/^gitdir:\s*(.+)$/);
|
|
71
|
+
if (gitDirMatch && gitDirMatch[1]) {
|
|
72
|
+
const gitDir = resolve(currentDir, gitDirMatch[1]);
|
|
73
|
+
return {
|
|
74
|
+
isSubmodule: /[/\\]\.git[/\\]modules[/\\]/.test(gitDir),
|
|
75
|
+
path: currentDir
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Check whether a `.git` path is a directory or file and resolve accordingly.
|
|
82
|
+
*
|
|
83
|
+
* @private
|
|
84
|
+
*/
|
|
85
|
+
function checkGitPath(gitPath, currentDir) {
|
|
86
|
+
if (!existsSync(gitPath)) return null;
|
|
87
|
+
const stats = statSync(gitPath);
|
|
88
|
+
if (stats.isDirectory()) return {
|
|
89
|
+
isSubmodule: false,
|
|
90
|
+
path: currentDir
|
|
91
|
+
};
|
|
92
|
+
if (stats.isFile()) return resolveGitFileSubmodule(gitPath, currentDir);
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Read a `.git` file and return its raw content.
|
|
97
|
+
*
|
|
98
|
+
* @private
|
|
99
|
+
*/
|
|
100
|
+
function resolveGitDirFromFile(gitFilePath) {
|
|
101
|
+
const [readError, gitFileContent] = attempt(() => readFileSync(gitFilePath, "utf8").trim());
|
|
102
|
+
if (readError || gitFileContent === null) return null;
|
|
103
|
+
return gitFileContent;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Extract the parent repository root from a resolved git modules path.
|
|
107
|
+
*
|
|
108
|
+
* @private
|
|
109
|
+
*/
|
|
110
|
+
function resolveParentFromGitDir(resolvedGitDir) {
|
|
111
|
+
const gitDirParts = resolvedGitDir.split("/modules/");
|
|
112
|
+
if (gitDirParts.length >= MIN_MODULES_PARTS) {
|
|
113
|
+
const [parentGitDir] = gitDirParts;
|
|
114
|
+
if (parentGitDir && parentGitDir.endsWith(".git")) return dirname(parentGitDir);
|
|
115
|
+
}
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Resolve the parent repository root from a submodule's gitdir reference.
|
|
120
|
+
*
|
|
121
|
+
* @private
|
|
122
|
+
*/
|
|
123
|
+
function resolveParentGitDir(projectRoot, gitFileContent) {
|
|
124
|
+
const gitDirMatch = gitFileContent.match(/^gitdir:\s*(.+)$/);
|
|
125
|
+
if (!gitDirMatch) return null;
|
|
126
|
+
const gitDir = gitDirMatch[1] ?? "";
|
|
127
|
+
const resolvedGitDir = resolve(projectRoot.path, gitDir);
|
|
128
|
+
if (process.platform === "win32") return null;
|
|
129
|
+
return resolveParentFromGitDir(resolvedGitDir);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
//#endregion
|
|
133
|
+
//#region src/lib/project/paths.ts
|
|
134
|
+
/**
|
|
135
|
+
* Resolve a directory path relative to the project root.
|
|
136
|
+
*
|
|
137
|
+
* @param options - Options containing the directory name and optional start directory.
|
|
138
|
+
* @returns The resolved local path, or null if no project root is found.
|
|
139
|
+
*/
|
|
140
|
+
function resolveLocalPath(options) {
|
|
141
|
+
const projectRoot = findProjectRoot(options.startDir);
|
|
142
|
+
if (!projectRoot) return null;
|
|
143
|
+
return join(projectRoot.path, options.dirName);
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Resolve a directory path relative to the user's home directory.
|
|
147
|
+
*
|
|
148
|
+
* @param options - Options containing the directory name.
|
|
149
|
+
* @returns The resolved global path.
|
|
150
|
+
*/
|
|
151
|
+
function resolveGlobalPath(options) {
|
|
152
|
+
return join(homedir(), options.dirName);
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Resolve a directory path using the specified source strategy.
|
|
156
|
+
*
|
|
157
|
+
* When source is 'local', resolves relative to the project root.
|
|
158
|
+
* When source is 'global', resolves relative to the home directory.
|
|
159
|
+
* When source is 'resolve' (default), tries local first, falling back to global.
|
|
160
|
+
*
|
|
161
|
+
* @param options - Resolution options with dirName, source, and startDir.
|
|
162
|
+
* @returns The resolved path, or null if local resolution fails with source='local'.
|
|
163
|
+
*/
|
|
164
|
+
function resolvePath(options) {
|
|
165
|
+
const { dirName, source = "resolve", startDir } = options;
|
|
166
|
+
return match$1(source).with("local", () => resolveLocalPath({
|
|
167
|
+
dirName,
|
|
168
|
+
startDir
|
|
169
|
+
})).with("global", () => resolveGlobalPath({ dirName })).with("resolve", () => {
|
|
170
|
+
const localPath = resolveLocalPath({
|
|
171
|
+
dirName,
|
|
172
|
+
startDir
|
|
173
|
+
});
|
|
174
|
+
if (localPath) return localPath;
|
|
175
|
+
return resolveGlobalPath({ dirName });
|
|
176
|
+
}).exhaustive();
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
//#endregion
|
|
180
|
+
export { getParentRepoRoot as a, findProjectRoot as i, resolveLocalPath as n, isInSubmodule as o, resolvePath as r, resolveGlobalPath as t };
|
|
181
|
+
//# sourceMappingURL=project-NPtYX2ZX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-NPtYX2ZX.js","names":["match"],"sources":["../src/lib/project/root.ts","../src/lib/project/paths.ts"],"sourcesContent":["import { existsSync, readFileSync, statSync } from 'node:fs'\nimport { dirname, join, resolve } from 'node:path'\n\nimport { attempt } from '@kidd-cli/utils/fp'\n\nimport type { ProjectRoot } from './types.js'\n\nconst MIN_MODULES_PARTS = 2\n\n/**\n * Walk up the directory tree to find the nearest git project root.\n *\n * @param startDir - Directory to start searching from (defaults to cwd).\n * @returns The project root info, or null if no git root is found.\n */\nexport function findProjectRoot(startDir: string = process.cwd()): ProjectRoot | null {\n /**\n * Recursively walk up the directory tree searching for a `.git` marker.\n *\n * @private\n */\n const findRootRecursive = (currentDir: string, visited: Set<string>): ProjectRoot | null => {\n if (visited.has(currentDir)) {\n return null\n }\n const nextVisited = new Set([...visited, currentDir])\n\n const gitPath = join(currentDir, '.git')\n try {\n const result = checkGitPath(gitPath, currentDir)\n if (result) {\n return result\n }\n } catch {\n // Race condition: file may have been deleted between existsSync and statSync\n }\n\n const parent = dirname(currentDir)\n if (parent === currentDir) {\n return null\n }\n return findRootRecursive(parent, nextVisited)\n }\n\n return findRootRecursive(resolve(startDir), new Set())\n}\n\n/**\n * Check whether the current directory is inside a git submodule.\n *\n * @param startDir - Directory to start searching from.\n * @returns True if the directory is inside a submodule.\n */\nexport function isInSubmodule(startDir?: string): boolean {\n const projectRoot = findProjectRoot(startDir)\n if (!projectRoot) {\n return false\n }\n return projectRoot.isSubmodule\n}\n\n/**\n * Resolve the parent repository root when inside a git submodule.\n *\n * @param startDir - Directory to start searching from.\n * @returns The parent repository root path, or null.\n */\nexport function getParentRepoRoot(startDir?: string): string | null {\n const projectRoot = findProjectRoot(startDir)\n if (!projectRoot || !projectRoot.isSubmodule) {\n return null\n }\n\n const gitFilePath = join(projectRoot.path, '.git')\n const gitFileContent = resolveGitDirFromFile(gitFilePath)\n if (gitFileContent === null) {\n return null\n }\n\n return resolveParentGitDir(projectRoot, gitFileContent)\n}\n\n// ---------------------------------------------------------------------------\n// Private\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve a `.git` file reference to determine if this is a submodule.\n *\n * @private\n */\nfunction resolveGitFileSubmodule(gitPath: string, currentDir: string): ProjectRoot | null {\n const [readError, gitFileContent] = attempt(() => readFileSync(gitPath, 'utf8').trim())\n if (readError || gitFileContent === null) {\n return { isSubmodule: false, path: currentDir }\n }\n const gitDirMatch = gitFileContent.match(/^gitdir:\\s*(.+)$/)\n if (gitDirMatch && gitDirMatch[1]) {\n const gitDir = resolve(currentDir, gitDirMatch[1])\n const isSubmodule = /[/\\\\]\\.git[/\\\\]modules[/\\\\]/.test(gitDir)\n return { isSubmodule, path: currentDir }\n }\n return null\n}\n\n/**\n * Check whether a `.git` path is a directory or file and resolve accordingly.\n *\n * @private\n */\nfunction checkGitPath(gitPath: string, currentDir: string): ProjectRoot | null {\n if (!existsSync(gitPath)) {\n return null\n }\n\n const stats = statSync(gitPath)\n if (stats.isDirectory()) {\n return { isSubmodule: false, path: currentDir }\n }\n\n if (stats.isFile()) {\n return resolveGitFileSubmodule(gitPath, currentDir)\n }\n\n return null\n}\n\n/**\n * Read a `.git` file and return its raw content.\n *\n * @private\n */\nfunction resolveGitDirFromFile(gitFilePath: string): string | null {\n const [readError, gitFileContent] = attempt(() => readFileSync(gitFilePath, 'utf8').trim())\n if (readError || gitFileContent === null) {\n return null\n }\n return gitFileContent\n}\n\n/**\n * Extract the parent repository root from a resolved git modules path.\n *\n * @private\n */\nfunction resolveParentFromGitDir(resolvedGitDir: string): string | null {\n const gitDirParts = resolvedGitDir.split('/modules/')\n if (gitDirParts.length >= MIN_MODULES_PARTS) {\n const [parentGitDir] = gitDirParts\n if (parentGitDir && parentGitDir.endsWith('.git')) {\n return dirname(parentGitDir)\n }\n }\n return null\n}\n\n/**\n * Resolve the parent repository root from a submodule's gitdir reference.\n *\n * @private\n */\nfunction resolveParentGitDir(projectRoot: ProjectRoot, gitFileContent: string): string | null {\n const gitDirMatch = gitFileContent.match(/^gitdir:\\s*(.+)$/)\n if (!gitDirMatch) {\n return null\n }\n\n const gitDir = gitDirMatch[1] ?? ''\n const resolvedGitDir = resolve(projectRoot.path, gitDir)\n\n if (process.platform === 'win32') {\n return null\n }\n\n return resolveParentFromGitDir(resolvedGitDir)\n}\n","import { homedir } from 'node:os'\nimport { join } from 'node:path'\n\nimport { match } from 'ts-pattern'\n\nimport { findProjectRoot } from './root.js'\nimport type { ResolvePathOptions } from './types.js'\n\n/**\n * Resolve a directory path relative to the project root.\n *\n * @param options - Options containing the directory name and optional start directory.\n * @returns The resolved local path, or null if no project root is found.\n */\nexport function resolveLocalPath(options: {\n readonly dirName: string\n readonly startDir?: string\n}): string | null {\n const projectRoot = findProjectRoot(options.startDir)\n if (!projectRoot) {\n return null\n }\n return join(projectRoot.path, options.dirName)\n}\n\n/**\n * Resolve a directory path relative to the user's home directory.\n *\n * @param options - Options containing the directory name.\n * @returns The resolved global path.\n */\nexport function resolveGlobalPath(options: { readonly dirName: string }): string {\n return join(homedir(), options.dirName)\n}\n\n/**\n * Resolve a directory path using the specified source strategy.\n *\n * When source is 'local', resolves relative to the project root.\n * When source is 'global', resolves relative to the home directory.\n * When source is 'resolve' (default), tries local first, falling back to global.\n *\n * @param options - Resolution options with dirName, source, and startDir.\n * @returns The resolved path, or null if local resolution fails with source='local'.\n */\nexport function resolvePath(options: ResolvePathOptions): string | null {\n const { dirName, source = 'resolve', startDir } = options\n return match(source)\n .with('local', (): string | null => resolveLocalPath({ dirName, startDir }))\n .with('global', (): string => resolveGlobalPath({ dirName }))\n .with('resolve', (): string => {\n const localPath = resolveLocalPath({ dirName, startDir })\n if (localPath) {\n return localPath\n }\n return resolveGlobalPath({ dirName })\n })\n .exhaustive()\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import * as clack from "@clack/prompts";
|
|
2
|
+
|
|
3
|
+
//#region src/lib/prompts/create-prompts.ts
|
|
4
|
+
/**
|
|
5
|
+
* Create a new {@link Spinner} instance backed by @clack/prompts.
|
|
6
|
+
*/
|
|
7
|
+
function createSpinner() {
|
|
8
|
+
const spinnerInstance = clack.spinner();
|
|
9
|
+
return {
|
|
10
|
+
message(msg) {
|
|
11
|
+
spinnerInstance.message(msg);
|
|
12
|
+
},
|
|
13
|
+
start(message) {
|
|
14
|
+
spinnerInstance.start(message);
|
|
15
|
+
},
|
|
16
|
+
stop(message) {
|
|
17
|
+
spinnerInstance.stop(message);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Create a new {@link PromptUtils} instance backed by @clack/prompts.
|
|
23
|
+
*/
|
|
24
|
+
function createPromptUtils() {
|
|
25
|
+
return {
|
|
26
|
+
cancel(message) {
|
|
27
|
+
clack.cancel(message);
|
|
28
|
+
},
|
|
29
|
+
confirm(opts) {
|
|
30
|
+
return clack.confirm(opts);
|
|
31
|
+
},
|
|
32
|
+
isCancel(value) {
|
|
33
|
+
return clack.isCancel(value);
|
|
34
|
+
},
|
|
35
|
+
multiselect(opts) {
|
|
36
|
+
return clack.multiselect(opts);
|
|
37
|
+
},
|
|
38
|
+
password(opts) {
|
|
39
|
+
return clack.password(opts);
|
|
40
|
+
},
|
|
41
|
+
select(opts) {
|
|
42
|
+
return clack.select(opts);
|
|
43
|
+
},
|
|
44
|
+
text(opts) {
|
|
45
|
+
return clack.text(opts);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
//#endregion
|
|
51
|
+
//#region src/lib/prompts/defaults.ts
|
|
52
|
+
/**
|
|
53
|
+
* Default spinner instance.
|
|
54
|
+
*/
|
|
55
|
+
const spinner = createSpinner();
|
|
56
|
+
/**
|
|
57
|
+
* Default prompt utilities instance.
|
|
58
|
+
*/
|
|
59
|
+
const prompts = createPromptUtils();
|
|
60
|
+
|
|
61
|
+
//#endregion
|
|
62
|
+
export { createSpinner as i, spinner as n, createPromptUtils as r, prompts as t };
|
|
63
|
+
//# sourceMappingURL=prompts-lLfUSgd6.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompts-lLfUSgd6.js","names":[],"sources":["../src/lib/prompts/create-prompts.ts","../src/lib/prompts/defaults.ts"],"sourcesContent":["import * as clack from '@clack/prompts'\n\nimport type { PromptUtils, Spinner } from './types.js'\n\n/**\n * Create a new {@link Spinner} instance backed by @clack/prompts.\n */\nexport function createSpinner(): Spinner {\n const spinnerInstance = clack.spinner()\n\n return {\n message(msg: string): void {\n spinnerInstance.message(msg)\n },\n\n start(message?: string): void {\n spinnerInstance.start(message)\n },\n\n stop(message?: string): void {\n spinnerInstance.stop(message)\n },\n }\n}\n\n/**\n * Create a new {@link PromptUtils} instance backed by @clack/prompts.\n */\nexport function createPromptUtils(): PromptUtils {\n return {\n cancel(message?: string): void {\n clack.cancel(message)\n },\n\n confirm(\n opts: Omit<Parameters<typeof clack.confirm>[0], 'output'>\n ): ReturnType<typeof clack.confirm> {\n return clack.confirm(opts)\n },\n\n isCancel(value: unknown): value is symbol {\n return clack.isCancel(value)\n },\n\n multiselect<TValue>(\n opts: Omit<Parameters<typeof clack.multiselect<TValue>>[0], 'output'>\n ): ReturnType<typeof clack.multiselect<TValue>> {\n return clack.multiselect(opts)\n },\n\n password(\n opts: Omit<Parameters<typeof clack.password>[0], 'output'>\n ): ReturnType<typeof clack.password> {\n return clack.password(opts)\n },\n\n select<TValue>(\n opts: Omit<Parameters<typeof clack.select<TValue>>[0], 'output'>\n ): ReturnType<typeof clack.select<TValue>> {\n return clack.select(opts)\n },\n\n text(opts: Omit<Parameters<typeof clack.text>[0], 'output'>): ReturnType<typeof clack.text> {\n return clack.text(opts)\n },\n }\n}\n","import { createPromptUtils, createSpinner } from './create-prompts.js'\nimport type { PromptUtils, Spinner } from './types.js'\n\n/**\n * Default spinner instance.\n */\nexport const spinner: Spinner = createSpinner()\n\n/**\n * Default prompt utilities instance.\n */\nexport const prompts: PromptUtils = createPromptUtils()\n"],"mappings":";;;;;;AAOA,SAAgB,gBAAyB;CACvC,MAAM,kBAAkB,MAAM,SAAS;AAEvC,QAAO;EACL,QAAQ,KAAmB;AACzB,mBAAgB,QAAQ,IAAI;;EAG9B,MAAM,SAAwB;AAC5B,mBAAgB,MAAM,QAAQ;;EAGhC,KAAK,SAAwB;AAC3B,mBAAgB,KAAK,QAAQ;;EAEhC;;;;;AAMH,SAAgB,oBAAiC;AAC/C,QAAO;EACL,OAAO,SAAwB;AAC7B,SAAM,OAAO,QAAQ;;EAGvB,QACE,MACkC;AAClC,UAAO,MAAM,QAAQ,KAAK;;EAG5B,SAAS,OAAiC;AACxC,UAAO,MAAM,SAAS,MAAM;;EAG9B,YACE,MAC8C;AAC9C,UAAO,MAAM,YAAY,KAAK;;EAGhC,SACE,MACmC;AACnC,UAAO,MAAM,SAAS,KAAK;;EAG7B,OACE,MACyC;AACzC,UAAO,MAAM,OAAO,KAAK;;EAG3B,KAAK,MAAuF;AAC1F,UAAO,MAAM,KAAK,KAAK;;EAE1B;;;;;;;;AC3DH,MAAa,UAAmB,eAAe;;;;AAK/C,MAAa,UAAuB,mBAAmB"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { AsyncResult } from "@kidd-cli/utils/fp";
|
|
2
|
+
|
|
3
|
+
//#region src/middleware/auth/types.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Bearer token credential — sends `Authorization: Bearer <token>`.
|
|
6
|
+
*/
|
|
7
|
+
interface BearerCredential {
|
|
8
|
+
readonly type: "bearer";
|
|
9
|
+
readonly token: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Basic auth credential — sends `Authorization: Basic base64(user:pass)`.
|
|
13
|
+
*/
|
|
14
|
+
interface BasicCredential {
|
|
15
|
+
readonly type: "basic";
|
|
16
|
+
readonly username: string;
|
|
17
|
+
readonly password: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* API key credential — sends the key in a custom header.
|
|
21
|
+
*/
|
|
22
|
+
interface ApiKeyCredential {
|
|
23
|
+
readonly type: "api-key";
|
|
24
|
+
readonly headerName: string;
|
|
25
|
+
readonly key: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Custom credential — sends arbitrary headers.
|
|
29
|
+
*/
|
|
30
|
+
interface CustomCredential {
|
|
31
|
+
readonly type: "custom";
|
|
32
|
+
readonly headers: Readonly<Record<string, string>>;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Discriminated union of all supported auth credential formats.
|
|
36
|
+
* The `type` field acts as the discriminator.
|
|
37
|
+
*/
|
|
38
|
+
type AuthCredential = BearerCredential | BasicCredential | ApiKeyCredential | CustomCredential;
|
|
39
|
+
/**
|
|
40
|
+
* Resolve credentials from environment variables.
|
|
41
|
+
*/
|
|
42
|
+
interface EnvSourceConfig {
|
|
43
|
+
readonly source: "env";
|
|
44
|
+
readonly tokenVar?: string;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Resolve credentials from a `.env` file parsed with dotenv.
|
|
48
|
+
*/
|
|
49
|
+
interface DotenvSourceConfig {
|
|
50
|
+
readonly source: "dotenv";
|
|
51
|
+
readonly tokenVar?: string;
|
|
52
|
+
readonly path?: string;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Resolve credentials from a JSON file on disk.
|
|
56
|
+
*/
|
|
57
|
+
interface FileSourceConfig {
|
|
58
|
+
readonly source: "file";
|
|
59
|
+
readonly filename?: string;
|
|
60
|
+
readonly dirName?: string;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Resolve credentials via an OAuth browser flow.
|
|
64
|
+
*/
|
|
65
|
+
interface OAuthSourceConfig {
|
|
66
|
+
readonly source: "oauth";
|
|
67
|
+
readonly authUrl: string;
|
|
68
|
+
readonly port?: number;
|
|
69
|
+
readonly callbackPath?: string;
|
|
70
|
+
readonly timeout?: number;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Resolve credentials by prompting the user interactively.
|
|
74
|
+
*/
|
|
75
|
+
interface PromptSourceConfig {
|
|
76
|
+
readonly source: "prompt";
|
|
77
|
+
readonly message?: string;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Resolve credentials via a user-supplied function.
|
|
81
|
+
*/
|
|
82
|
+
interface CustomSourceConfig {
|
|
83
|
+
readonly source: "custom";
|
|
84
|
+
readonly resolver: () => Promise<AuthCredential | null> | AuthCredential | null;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Discriminated union of all supported credential source configurations.
|
|
88
|
+
* The `source` field acts as the discriminator.
|
|
89
|
+
*/
|
|
90
|
+
type ResolverConfig = EnvSourceConfig | DotenvSourceConfig | FileSourceConfig | OAuthSourceConfig | PromptSourceConfig | CustomSourceConfig;
|
|
91
|
+
/**
|
|
92
|
+
* Error returned by {@link AuthContext.authenticate} when interactive auth fails.
|
|
93
|
+
*/
|
|
94
|
+
interface LoginError {
|
|
95
|
+
readonly type: "no_credential" | "save_failed";
|
|
96
|
+
readonly message: string;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Auth context decorated onto `ctx.auth` by the auth middleware.
|
|
100
|
+
*
|
|
101
|
+
* No credential data is stored directly on the context. Instead, callers
|
|
102
|
+
* use `credential()` to read saved credentials on demand and
|
|
103
|
+
* `authenticated()` to check whether a credential exists without exposing it.
|
|
104
|
+
*
|
|
105
|
+
* `authenticate()` runs the configured interactive resolvers (OAuth, prompt,
|
|
106
|
+
* etc.), persists the resulting credential to disk, and returns a
|
|
107
|
+
* {@link AsyncResult}.
|
|
108
|
+
*/
|
|
109
|
+
interface AuthContext {
|
|
110
|
+
readonly credential: () => AuthCredential | null;
|
|
111
|
+
readonly authenticated: () => boolean;
|
|
112
|
+
readonly authenticate: () => AsyncResult<AuthCredential, LoginError>;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Options accepted by the `auth()` middleware factory.
|
|
116
|
+
*
|
|
117
|
+
* @property resolvers - Ordered list of credential sources to try via `authenticate()`.
|
|
118
|
+
*/
|
|
119
|
+
interface AuthOptions {
|
|
120
|
+
readonly resolvers: readonly ResolverConfig[];
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Augments the base {@link Context} with an optional `auth` property.
|
|
124
|
+
*
|
|
125
|
+
* When a consumer imports `kidd/auth`, this declaration merges `auth`
|
|
126
|
+
* onto `Context` so that `ctx.auth` is typed without manual casting.
|
|
127
|
+
*/
|
|
128
|
+
declare module "@kidd-cli/core" {
|
|
129
|
+
interface Context {
|
|
130
|
+
readonly auth: AuthContext;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
//#endregion
|
|
134
|
+
export { ResolverConfig as a, LoginError as i, AuthCredential as n, AuthOptions as r, AuthContext as t };
|
|
135
|
+
//# sourceMappingURL=types-CqKJhsYk.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types-CqKJhsYk.d.ts","names":[],"sources":["../src/middleware/auth/types.ts"],"mappings":";;;;;;UAmBiB,gBAAA;EAAA,SACN,IAAA;EAAA,SACA,KAAA;AAAA;AAeX;;;AAAA,UATiB,eAAA;EAAA,SACN,IAAA;EAAA,SACA,QAAA;EAAA,SACA,QAAA;AAAA;;AAeX;;UATiB,gBAAA;EAAA,SACN,IAAA;EAAA,SACA,UAAA;EAAA,SACA,GAAA;AAAA;;;;UAMM,gBAAA;EAAA,SACN,IAAA;EAAA,SACA,OAAA,EAAS,QAAA,CAAS,MAAA;AAAA;;;;;KAOjB,cAAA,GACR,gBAAA,GACA,eAAA,GACA,gBAAA,GACA,gBAAA;;;;UASa,eAAA;EAAA,SACN,MAAA;EAAA,SACA,QAAA;AAAA;;;;UAMM,kBAAA;EAAA,SACN,MAAA;EAAA,SACA,QAAA;EAAA,SACA,IAAA;AAAA;;;;UAMM,gBAAA;EAAA,SACN,MAAA;EAAA,SACA,QAAA;EAAA,SACA,OAAA;AAAA;;;;UAMM,iBAAA;EAAA,SACN,MAAA;EAAA,SACA,OAAA;EAAA,SACA,IAAA;EAAA,SACA,YAAA;EAAA,SACA,OAAA;AAAA;;;;UAMM,kBAAA;EAAA,SACN,MAAA;EAAA,SACA,OAAA;AAAA;AAFX;;;AAAA,UAQiB,kBAAA;EAAA,SACN,MAAA;EAAA,SACA,QAAA,QAAgB,OAAA,CAAQ,cAAA,WAAyB,cAAA;AAAA;;;;;KAOhD,cAAA,GACR,eAAA,GACA,kBAAA,GACA,gBAAA,GACA,iBAAA,GACA,kBAAA,GACA,kBAAA;;;;UASa,UAAA;EAAA,SACN,IAAA;EAAA,SACA,OAAA;AAAA;;AAjBX;;;;;;;;;;UAmCiB,WAAA;EAAA,SACN,UAAA,QAAkB,cAAA;EAAA,SAClB,aAAA;EAAA,SACA,YAAA,QAAoB,WAAA,CAAY,cAAA,EAAgB,UAAA;AAAA;;;;;AAvB3D;UAmCiB,WAAA;EAAA,SACN,SAAA,WAAoB,cAAA;AAAA;;AAhB/B;;;;;;YA8BY,OAAA;IAAA,SACC,IAAA,EAAM,WAAA;EAAA;AAAA"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
//#region src/lib/project/types.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Resolved project root with submodule detection.
|
|
4
|
+
*/
|
|
5
|
+
interface ProjectRoot {
|
|
6
|
+
readonly isSubmodule: boolean;
|
|
7
|
+
readonly path: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Source strategy for path resolution.
|
|
11
|
+
*/
|
|
12
|
+
type PathSource = "local" | "global" | "resolve";
|
|
13
|
+
/**
|
|
14
|
+
* Options for resolving a directory path from local or global sources.
|
|
15
|
+
*/
|
|
16
|
+
interface ResolvePathOptions {
|
|
17
|
+
readonly dirName: string;
|
|
18
|
+
readonly source?: PathSource;
|
|
19
|
+
readonly startDir?: string;
|
|
20
|
+
}
|
|
21
|
+
//#endregion
|
|
22
|
+
export { ProjectRoot as n, ResolvePathOptions as r, PathSource as t };
|
|
23
|
+
//# sourceMappingURL=types-Cz9h927W.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types-Cz9h927W.d.ts","names":[],"sources":["../src/lib/project/types.ts"],"mappings":";;AAGA;;UAAiB,WAAA;EAAA,SACN,WAAA;EAAA,SACA,IAAA;AAAA;;;;KAMC,UAAA;AAKZ;;;AAAA,UAAiB,kBAAA;EAAA,SACN,OAAA;EAAA,SACA,MAAA,GAAS,UAAA;EAAA,SACT,QAAA;AAAA"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import * as clack from "@clack/prompts";
|
|
2
|
+
|
|
3
|
+
//#region src/lib/prompts/types.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Terminal spinner for indicating long-running operations.
|
|
6
|
+
*/
|
|
7
|
+
interface Spinner {
|
|
8
|
+
start(message?: string): void;
|
|
9
|
+
stop(message?: string): void;
|
|
10
|
+
message(message: string): void;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Interactive prompt methods backed by @clack/prompts.
|
|
14
|
+
*/
|
|
15
|
+
interface PromptUtils {
|
|
16
|
+
select<TValue>(opts: Omit<Parameters<typeof clack.select<TValue>>[0], "output">): ReturnType<typeof clack.select<TValue>>;
|
|
17
|
+
confirm(opts: Omit<Parameters<typeof clack.confirm>[0], "output">): ReturnType<typeof clack.confirm>;
|
|
18
|
+
text(opts: Omit<Parameters<typeof clack.text>[0], "output">): ReturnType<typeof clack.text>;
|
|
19
|
+
multiselect<TValue>(opts: Omit<Parameters<typeof clack.multiselect<TValue>>[0], "output">): ReturnType<typeof clack.multiselect<TValue>>;
|
|
20
|
+
password(opts: Omit<Parameters<typeof clack.password>[0], "output">): ReturnType<typeof clack.password>;
|
|
21
|
+
isCancel(value: unknown): value is symbol;
|
|
22
|
+
cancel(message?: string): void;
|
|
23
|
+
}
|
|
24
|
+
//#endregion
|
|
25
|
+
export { Spinner as n, PromptUtils as t };
|
|
26
|
+
//# sourceMappingURL=types-DFtYg5uZ.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types-DFtYg5uZ.d.ts","names":[],"sources":["../src/lib/prompts/types.ts"],"mappings":";;;;;AAKA;UAAiB,OAAA;EACf,KAAA,CAAM,OAAA;EACN,IAAA,CAAK,OAAA;EACL,OAAA,CAAQ,OAAA;AAAA;;;;UAMO,WAAA;EACf,MAAA,SACE,IAAA,EAAM,IAAA,CAAK,UAAA,QAAkB,KAAA,CAAM,MAAA,CAAO,MAAA,mBACzC,UAAA,QAAkB,KAAA,CAAM,MAAA,CAAO,MAAA;EAClC,OAAA,CACE,IAAA,EAAM,IAAA,CAAK,UAAA,QAAkB,KAAA,CAAM,OAAA,kBAClC,UAAA,QAAkB,KAAA,CAAM,OAAA;EAC3B,IAAA,CAAK,IAAA,EAAM,IAAA,CAAK,UAAA,QAAkB,KAAA,CAAM,IAAA,kBAAsB,UAAA,QAAkB,KAAA,CAAM,IAAA;EACtF,WAAA,SACE,IAAA,EAAM,IAAA,CAAK,UAAA,QAAkB,KAAA,CAAM,WAAA,CAAY,MAAA,mBAC9C,UAAA,QAAkB,KAAA,CAAM,WAAA,CAAY,MAAA;EACvC,QAAA,CACE,IAAA,EAAM,IAAA,CAAK,UAAA,QAAkB,KAAA,CAAM,QAAA,kBAClC,UAAA,QAAkB,KAAA,CAAM,QAAA;EAC3B,QAAA,CAAS,KAAA,YAAiB,KAAA;EAC1B,MAAA,CAAO,OAAA;AAAA"}
|