@reliverse/rempts 1.7.65 → 2.2.7
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 -21
- package/README.md +1534 -1431
- package/cleanup.mjs +33 -0
- package/dist/cancel.d.ts +31 -0
- package/dist/cancel.js +28 -0
- package/dist/ffi.d.ts +1 -0
- package/dist/ffi.js +165 -0
- package/dist/group.d.ts +16 -0
- package/dist/group.js +22 -0
- package/dist/launcher/command.d.ts +8 -0
- package/dist/launcher/command.js +10 -0
- package/dist/launcher/discovery.d.ts +3 -0
- package/dist/launcher/discovery.js +207 -0
- package/dist/launcher/errors.d.ts +15 -0
- package/dist/launcher/errors.js +31 -0
- package/dist/launcher/help.d.ts +3 -0
- package/dist/launcher/help.js +145 -0
- package/dist/launcher/mod.d.ts +12 -0
- package/dist/launcher/mod.js +222 -0
- package/dist/launcher/parser.d.ts +14 -0
- package/dist/launcher/parser.js +255 -0
- package/dist/launcher/registry.d.ts +10 -0
- package/dist/launcher/registry.js +42 -0
- package/dist/launcher/types.d.ts +78 -0
- package/dist/launcher/validator.d.ts +3 -0
- package/dist/launcher/validator.js +39 -0
- package/dist/mod.d.ts +6 -0
- package/dist/mod.js +6 -0
- package/dist/prompt.d.ts +13 -0
- package/dist/prompt.js +53 -0
- package/dist/selection.d.ts +92 -0
- package/dist/selection.js +191 -0
- package/dist/spinner.d.ts +26 -0
- package/dist/spinner.js +141 -0
- package/dist/utils.d.ts +3 -0
- package/dist/utils.js +11 -0
- package/package.json +41 -47
- package/bin/libs/animate/animate-mod.ts.txt +0 -78
- package/bin/libs/anykey/anykey-mod.d.ts +0 -12
- package/bin/libs/anykey/anykey-mod.js +0 -125
- package/bin/libs/cancel/cancel.d.ts +0 -45
- package/bin/libs/cancel/cancel.js +0 -72
- package/bin/libs/confirm/confirm-alias.d.ts +0 -2
- package/bin/libs/confirm/confirm-alias.js +0 -2
- package/bin/libs/confirm/confirm-mod.d.ts +0 -5
- package/bin/libs/confirm/confirm-mod.js +0 -179
- package/bin/libs/date/date.d.ts +0 -2
- package/bin/libs/date/date.js +0 -254
- package/bin/libs/editor/editor-mod.d.ts +0 -25
- package/bin/libs/editor/editor-mod.js +0 -1133
- package/bin/libs/figures/figures-mod.d.ts +0 -461
- package/bin/libs/figures/figures-mod.js +0 -285
- package/bin/libs/group/group-mod.d.ts +0 -33
- package/bin/libs/group/group-mod.js +0 -89
- package/bin/libs/input/input-alias.d.ts +0 -5
- package/bin/libs/input/input-alias.js +0 -4
- package/bin/libs/input/input-mod.d.ts +0 -16
- package/bin/libs/input/input-mod.js +0 -370
- package/bin/libs/intro/intro-alias.d.ts +0 -3
- package/bin/libs/intro/intro-alias.js +0 -3
- package/bin/libs/intro/intro-mod.d.ts +0 -19
- package/bin/libs/intro/intro-mod.js +0 -71
- package/bin/libs/launcher/command-runner.d.ts +0 -31
- package/bin/libs/launcher/command-runner.js +0 -229
- package/bin/libs/launcher/launcher-alias.d.ts +0 -2
- package/bin/libs/launcher/launcher-alias.js +0 -2
- package/bin/libs/launcher/launcher-mod.d.ts +0 -66
- package/bin/libs/launcher/launcher-mod.js +0 -1037
- package/bin/libs/launcher/launcher-types.d.ts +0 -176
- package/bin/libs/launcher/launcher-types.js +0 -0
- package/bin/libs/log/log-alias.d.ts +0 -1
- package/bin/libs/log/log-alias.js +0 -2
- package/bin/libs/msg-fmt/colors.d.ts +0 -30
- package/bin/libs/msg-fmt/colors.js +0 -42
- package/bin/libs/msg-fmt/mapping.d.ts +0 -3
- package/bin/libs/msg-fmt/mapping.js +0 -41
- package/bin/libs/msg-fmt/messages.d.ts +0 -35
- package/bin/libs/msg-fmt/messages.js +0 -305
- package/bin/libs/msg-fmt/terminal.d.ts +0 -15
- package/bin/libs/msg-fmt/terminal.js +0 -60
- package/bin/libs/msg-fmt/variants.d.ts +0 -11
- package/bin/libs/msg-fmt/variants.js +0 -52
- package/bin/libs/multiselect/multiselect-alias.d.ts +0 -2
- package/bin/libs/multiselect/multiselect-alias.js +0 -2
- package/bin/libs/multiselect/multiselect-prompt.d.ts +0 -2
- package/bin/libs/multiselect/multiselect-prompt.js +0 -340
- package/bin/libs/next-steps/next-steps.d.ts +0 -13
- package/bin/libs/next-steps/next-steps.js +0 -24
- package/bin/libs/number/number-mod.d.ts +0 -28
- package/bin/libs/number/number-mod.js +0 -234
- package/bin/libs/outro/outro-alias.d.ts +0 -3
- package/bin/libs/outro/outro-alias.js +0 -3
- package/bin/libs/outro/outro-mod.d.ts +0 -7
- package/bin/libs/outro/outro-mod.js +0 -49
- package/bin/libs/reliarg/reliarg-mod.d.ts +0 -76
- package/bin/libs/reliarg/reliarg-mod.js +0 -276
- package/bin/libs/results/results.d.ts +0 -7
- package/bin/libs/results/results.js +0 -27
- package/bin/libs/select/nummultiselect-prompt.d.ts +0 -6
- package/bin/libs/select/nummultiselect-prompt.js +0 -141
- package/bin/libs/select/numselect-prompt.d.ts +0 -7
- package/bin/libs/select/numselect-prompt.js +0 -111
- package/bin/libs/select/select-alias.d.ts +0 -9
- package/bin/libs/select/select-alias.js +0 -9
- package/bin/libs/select/select-prompt.d.ts +0 -5
- package/bin/libs/select/select-prompt.js +0 -311
- package/bin/libs/select/toggle-prompt.d.ts +0 -5
- package/bin/libs/select/toggle-prompt.js +0 -207
- package/bin/libs/spinner/spinner-impl.d.ts +0 -70
- package/bin/libs/spinner/spinner-impl.js +0 -336
- package/bin/libs/spinner/spinner-mod.d.ts +0 -167
- package/bin/libs/spinner/spinner-mod.js +0 -447
- package/bin/libs/utils/colorize.d.ts +0 -2
- package/bin/libs/utils/colorize.js +0 -122
- package/bin/libs/utils/errors.d.ts +0 -1
- package/bin/libs/utils/errors.js +0 -17
- package/bin/libs/utils/prevent.d.ts +0 -8
- package/bin/libs/utils/prevent.js +0 -62
- package/bin/libs/utils/prompt-end.d.ts +0 -8
- package/bin/libs/utils/prompt-end.js +0 -36
- package/bin/libs/utils/stream-text.d.ts +0 -18
- package/bin/libs/utils/stream-text.js +0 -138
- package/bin/libs/utils/system.d.ts +0 -6
- package/bin/libs/utils/system.js +0 -7
- package/bin/libs/utils/validate.d.ts +0 -21
- package/bin/libs/utils/validate.js +0 -17
- package/bin/libs/visual/visual-mod.ts.txt +0 -19
- package/bin/mod.d.ts +0 -50
- package/bin/mod.js +0 -127
- package/bin/types.d.ts +0 -372
- /package/{bin → dist/launcher}/types.js +0 -0
package/cleanup.mjs
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { suffix } from "bun:ffi";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
|
|
4
|
+
const releasePath = new URL("./release", import.meta.url).pathname;
|
|
5
|
+
|
|
6
|
+
// Check if release directory exists
|
|
7
|
+
if (!fs.existsSync(releasePath)) {
|
|
8
|
+
console.log("Release directory does not exist, nothing to clean up.");
|
|
9
|
+
process.exit(0);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const files = fs.readdirSync(releasePath);
|
|
13
|
+
|
|
14
|
+
const { platform, arch } = process;
|
|
15
|
+
let filename;
|
|
16
|
+
|
|
17
|
+
if (arch === "x64") {
|
|
18
|
+
filename = `rempts-${platform}-amd64.${suffix}`;
|
|
19
|
+
} else {
|
|
20
|
+
filename = `rempts-${platform}-${arch}.${suffix}`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
files.forEach((file) => {
|
|
24
|
+
if (file !== filename) {
|
|
25
|
+
const filePath = `./release/${file}`;
|
|
26
|
+
try {
|
|
27
|
+
fs.unlinkSync(filePath);
|
|
28
|
+
console.log(`Removed: ${file}`);
|
|
29
|
+
} catch (error) {
|
|
30
|
+
console.warn(`Failed to remove ${file}:`, error.message);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
});
|
package/dist/cancel.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom error class for prompt cancellations
|
|
3
|
+
*/
|
|
4
|
+
export declare class PromptCancelledError extends Error {
|
|
5
|
+
constructor(message?: string);
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Throws a PromptCancelledError to signal that user cancelled the prompt
|
|
9
|
+
* @param message - Optional custom cancellation message
|
|
10
|
+
*/
|
|
11
|
+
export declare function cancel(message?: string): never;
|
|
12
|
+
/**
|
|
13
|
+
* Checks if an error is a cancellation error from a prompt
|
|
14
|
+
* @param error - The error to check
|
|
15
|
+
* @returns `true` if the error is a cancellation, `false` otherwise
|
|
16
|
+
*/
|
|
17
|
+
export declare function isCancel(error: unknown): error is PromptCancelledError;
|
|
18
|
+
/**
|
|
19
|
+
* Exits the process with exit code 0 after logging a cancellation message
|
|
20
|
+
* @param message - The message to log before exiting (default: "Operation cancelled")
|
|
21
|
+
*/
|
|
22
|
+
export declare function exitCancelled(message?: string): never;
|
|
23
|
+
/**
|
|
24
|
+
* Sets up a global handler for unhandled PromptCancelledError
|
|
25
|
+
* This ensures that if a developer doesn't explicitly handle cancellations,
|
|
26
|
+
* the app will exit cleanly instead of crashing.
|
|
27
|
+
*
|
|
28
|
+
* Call this once at the start of your application if you want automatic
|
|
29
|
+
* cancellation handling.
|
|
30
|
+
*/
|
|
31
|
+
export declare function setupAutoCancelHandler(): void;
|
package/dist/cancel.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export class PromptCancelledError extends Error {
|
|
2
|
+
constructor(message = "Cancelled") {
|
|
3
|
+
super(message);
|
|
4
|
+
this.name = "PromptCancelledError";
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
export function cancel(message = "Cancelled") {
|
|
8
|
+
throw new PromptCancelledError(message);
|
|
9
|
+
}
|
|
10
|
+
export function isCancel(error) {
|
|
11
|
+
return error instanceof PromptCancelledError;
|
|
12
|
+
}
|
|
13
|
+
export function exitCancelled(message = "Operation cancelled") {
|
|
14
|
+
console.log(message);
|
|
15
|
+
process.exit(0);
|
|
16
|
+
}
|
|
17
|
+
export function setupAutoCancelHandler() {
|
|
18
|
+
process.on("unhandledRejection", (reason) => {
|
|
19
|
+
if (reason instanceof PromptCancelledError) {
|
|
20
|
+
exitCancelled("Operation cancelled");
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
process.on("uncaughtException", (error) => {
|
|
24
|
+
if (error instanceof PromptCancelledError) {
|
|
25
|
+
exitCancelled("Operation cancelled");
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
}
|
package/dist/ffi.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const symbols: any;
|
package/dist/ffi.js
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { dlopen, FFIType, suffix } from "bun:ffi";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
const { platform, arch } = process;
|
|
5
|
+
function getBinaryLocation() {
|
|
6
|
+
let filename;
|
|
7
|
+
if (arch === "x64") {
|
|
8
|
+
filename = `../release/rempts-${platform}-amd64.${suffix}`;
|
|
9
|
+
} else {
|
|
10
|
+
filename = `../release/rempts-${platform}-${arch}.${suffix}`;
|
|
11
|
+
}
|
|
12
|
+
return Bun.fileURLToPath(new URL(filename, import.meta.url));
|
|
13
|
+
}
|
|
14
|
+
let cachedSymbols;
|
|
15
|
+
function loadSymbols() {
|
|
16
|
+
if (cachedSymbols) {
|
|
17
|
+
return cachedSymbols;
|
|
18
|
+
}
|
|
19
|
+
const location = getBinaryLocation();
|
|
20
|
+
if (!existsSync(location)) {
|
|
21
|
+
const releaseDir = path.dirname(location);
|
|
22
|
+
const expectedFile = path.basename(location);
|
|
23
|
+
let errorMessage = `
|
|
24
|
+
\u274C Native binary not found! (Did you have Docker launched?)
|
|
25
|
+
|
|
26
|
+
`;
|
|
27
|
+
errorMessage += `Expected file: ${location}
|
|
28
|
+
|
|
29
|
+
`;
|
|
30
|
+
if (!existsSync(releaseDir)) {
|
|
31
|
+
errorMessage += `The release directory does not exist: ${releaseDir}
|
|
32
|
+
|
|
33
|
+
`;
|
|
34
|
+
} else {
|
|
35
|
+
errorMessage += `The release directory exists but the binary is missing.
|
|
36
|
+
`;
|
|
37
|
+
errorMessage += `Expected: ${expectedFile}
|
|
38
|
+
|
|
39
|
+
`;
|
|
40
|
+
}
|
|
41
|
+
errorMessage += `To fix this, run:
|
|
42
|
+
`;
|
|
43
|
+
errorMessage += ` bun run build
|
|
44
|
+
|
|
45
|
+
`;
|
|
46
|
+
errorMessage += `Or use native build (no Docker):
|
|
47
|
+
`;
|
|
48
|
+
errorMessage += ` bun run build --provider native
|
|
49
|
+
`;
|
|
50
|
+
throw new Error(errorMessage);
|
|
51
|
+
}
|
|
52
|
+
try {
|
|
53
|
+
cachedSymbols = dlopen(location, {
|
|
54
|
+
CreateSelection: {
|
|
55
|
+
args: [
|
|
56
|
+
FFIType.ptr,
|
|
57
|
+
FFIType.ptr,
|
|
58
|
+
FFIType.ptr,
|
|
59
|
+
FFIType.int,
|
|
60
|
+
FFIType.bool,
|
|
61
|
+
FFIType.ptr,
|
|
62
|
+
FFIType.ptr
|
|
63
|
+
],
|
|
64
|
+
returns: FFIType.ptr
|
|
65
|
+
},
|
|
66
|
+
CreatePrompt: {
|
|
67
|
+
args: [
|
|
68
|
+
FFIType.ptr,
|
|
69
|
+
FFIType.ptr,
|
|
70
|
+
FFIType.ptr,
|
|
71
|
+
FFIType.ptr,
|
|
72
|
+
FFIType.ptr,
|
|
73
|
+
FFIType.ptr,
|
|
74
|
+
FFIType.bool,
|
|
75
|
+
FFIType.int
|
|
76
|
+
],
|
|
77
|
+
returns: FFIType.ptr
|
|
78
|
+
},
|
|
79
|
+
CreateMultiselect: {
|
|
80
|
+
args: [
|
|
81
|
+
FFIType.ptr,
|
|
82
|
+
FFIType.ptr,
|
|
83
|
+
FFIType.ptr,
|
|
84
|
+
FFIType.int,
|
|
85
|
+
FFIType.bool,
|
|
86
|
+
FFIType.ptr,
|
|
87
|
+
FFIType.ptr
|
|
88
|
+
],
|
|
89
|
+
returns: FFIType.ptr
|
|
90
|
+
},
|
|
91
|
+
CreateConfirm: {
|
|
92
|
+
args: [FFIType.ptr, FFIType.ptr, FFIType.ptr, FFIType.ptr, FFIType.ptr],
|
|
93
|
+
returns: FFIType.ptr
|
|
94
|
+
},
|
|
95
|
+
CreateGroupMultiselect: {
|
|
96
|
+
args: [
|
|
97
|
+
FFIType.ptr,
|
|
98
|
+
FFIType.ptr,
|
|
99
|
+
FFIType.ptr,
|
|
100
|
+
FFIType.int,
|
|
101
|
+
FFIType.bool,
|
|
102
|
+
FFIType.bool,
|
|
103
|
+
FFIType.ptr,
|
|
104
|
+
FFIType.ptr,
|
|
105
|
+
FFIType.int
|
|
106
|
+
],
|
|
107
|
+
returns: FFIType.ptr
|
|
108
|
+
},
|
|
109
|
+
FreeString: {
|
|
110
|
+
args: [FFIType.ptr],
|
|
111
|
+
returns: FFIType.void
|
|
112
|
+
}
|
|
113
|
+
}).symbols;
|
|
114
|
+
} catch (error) {
|
|
115
|
+
let errorMessage = `
|
|
116
|
+
\u274C Failed to load native binary!
|
|
117
|
+
|
|
118
|
+
`;
|
|
119
|
+
errorMessage += `File: ${location}
|
|
120
|
+
`;
|
|
121
|
+
errorMessage += `Error: ${error.message || error}
|
|
122
|
+
|
|
123
|
+
`;
|
|
124
|
+
if (error.code === "ERR_DLOPEN_FAILED" || error.errno === 126) {
|
|
125
|
+
errorMessage += `This usually means:
|
|
126
|
+
`;
|
|
127
|
+
errorMessage += ` \u2022 The binary is corrupted or incomplete
|
|
128
|
+
`;
|
|
129
|
+
errorMessage += ` \u2022 Missing dependencies (DLLs, shared libraries)
|
|
130
|
+
`;
|
|
131
|
+
errorMessage += ` \u2022 Architecture mismatch
|
|
132
|
+
|
|
133
|
+
`;
|
|
134
|
+
errorMessage += `Try rebuilding:
|
|
135
|
+
`;
|
|
136
|
+
errorMessage += ` bun run build
|
|
137
|
+
`;
|
|
138
|
+
} else {
|
|
139
|
+
errorMessage += `Please rebuild the native binaries:
|
|
140
|
+
`;
|
|
141
|
+
errorMessage += ` bun run build
|
|
142
|
+
`;
|
|
143
|
+
}
|
|
144
|
+
throw new Error(errorMessage);
|
|
145
|
+
}
|
|
146
|
+
return cachedSymbols;
|
|
147
|
+
}
|
|
148
|
+
export const symbols = new Proxy({}, {
|
|
149
|
+
get(_target, prop) {
|
|
150
|
+
const symbols2 = loadSymbols();
|
|
151
|
+
return symbols2[prop];
|
|
152
|
+
},
|
|
153
|
+
has(_target, prop) {
|
|
154
|
+
const symbols2 = loadSymbols();
|
|
155
|
+
return prop in symbols2;
|
|
156
|
+
},
|
|
157
|
+
ownKeys(_target) {
|
|
158
|
+
const symbols2 = loadSymbols();
|
|
159
|
+
return Object.keys(symbols2);
|
|
160
|
+
},
|
|
161
|
+
getOwnPropertyDescriptor(_target, prop) {
|
|
162
|
+
const symbols2 = loadSymbols();
|
|
163
|
+
return Object.getOwnPropertyDescriptor(symbols2, prop);
|
|
164
|
+
}
|
|
165
|
+
});
|
package/dist/group.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { PromptCancelledError } from "./cancel.js";
|
|
2
|
+
type MaybePromise<T> = T | Promise<T>;
|
|
3
|
+
export interface GroupPromptContext<TResult extends Record<string, unknown>, TKey extends keyof TResult> {
|
|
4
|
+
key: TKey;
|
|
5
|
+
results: Readonly<Partial<TResult>>;
|
|
6
|
+
}
|
|
7
|
+
export type GroupPromptHandler<TResult extends Record<string, unknown>, TKey extends keyof TResult> = (context: GroupPromptContext<TResult, TKey>) => MaybePromise<TResult[TKey]>;
|
|
8
|
+
export type GroupPromptHandlers<TResult extends Record<string, unknown>> = {
|
|
9
|
+
[K in keyof TResult]: GroupPromptHandler<TResult, K>;
|
|
10
|
+
};
|
|
11
|
+
export interface GroupPromptOptions {
|
|
12
|
+
onCancel?: (error: PromptCancelledError) => MaybePromise<void>;
|
|
13
|
+
}
|
|
14
|
+
export declare function groupPrompt<TResult extends Record<string, unknown>>(handlers: GroupPromptHandlers<TResult>, options?: GroupPromptOptions): Promise<TResult>;
|
|
15
|
+
export declare const group: typeof groupPrompt;
|
|
16
|
+
export {};
|
package/dist/group.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { PromptCancelledError } from "./cancel.js";
|
|
2
|
+
export async function groupPrompt(handlers, options) {
|
|
3
|
+
const results = {};
|
|
4
|
+
const keys = Object.keys(handlers);
|
|
5
|
+
for (const key of keys) {
|
|
6
|
+
const handler = handlers[key];
|
|
7
|
+
try {
|
|
8
|
+
const value = await handler({
|
|
9
|
+
key,
|
|
10
|
+
results
|
|
11
|
+
});
|
|
12
|
+
results[key] = value;
|
|
13
|
+
} catch (error) {
|
|
14
|
+
if (error instanceof PromptCancelledError && options?.onCancel) {
|
|
15
|
+
await options.onCancel(error);
|
|
16
|
+
}
|
|
17
|
+
throw error;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return results;
|
|
21
|
+
}
|
|
22
|
+
export const group = groupPrompt;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { CmdArgsSchema, CmdDefinition, CmdHandler, CmdMeta } from "./types.js";
|
|
2
|
+
export declare const defineArgs: <const T extends CmdArgsSchema>(args: T) => T;
|
|
3
|
+
export interface DefineCommandOptions<Args extends CmdArgsSchema, Meta extends CmdMeta> {
|
|
4
|
+
meta: Meta;
|
|
5
|
+
args: Args;
|
|
6
|
+
run: CmdHandler<Args>;
|
|
7
|
+
}
|
|
8
|
+
export declare const defineCommand: <const Args extends CmdArgsSchema, const Meta extends CmdMeta>({ meta, args, run, }: DefineCommandOptions<Args, Meta>) => CmdDefinition<Args>;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { CmdDefinition, DiscoveryResult } from "./types.js";
|
|
2
|
+
export declare const discoverCommands: (cmdsDir: string, baseDir?: string, verbose?: boolean) => Promise<DiscoveryResult>;
|
|
3
|
+
export declare const validateCommandStructure: (definition: unknown) => definition is CmdDefinition;
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { statSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import pMap from "@reliverse/mapkit";
|
|
4
|
+
import { Glob } from "bun";
|
|
5
|
+
import { CommandLoadError } from "./errors.js";
|
|
6
|
+
const createLazyMetadataLoader = (filePath, cmdName) => {
|
|
7
|
+
return async () => {
|
|
8
|
+
try {
|
|
9
|
+
const module = await import(filePath);
|
|
10
|
+
const definition = module.default;
|
|
11
|
+
if (!definition || !definition.meta) {
|
|
12
|
+
throw new Error("Invalid command definition");
|
|
13
|
+
}
|
|
14
|
+
return {
|
|
15
|
+
name: definition.meta.name,
|
|
16
|
+
description: definition.meta.description,
|
|
17
|
+
aliases: definition.meta.aliases,
|
|
18
|
+
version: definition.meta.version,
|
|
19
|
+
examples: definition.meta.examples
|
|
20
|
+
};
|
|
21
|
+
} catch (error) {
|
|
22
|
+
throw new CommandLoadError(cmdName, error);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
const resolveCommandsDirectory = (baseDir, cmdsDir, verbose) => {
|
|
27
|
+
if (verbose) {
|
|
28
|
+
console.debug(`\u{1F50D} Resolving commands directory:`);
|
|
29
|
+
console.debug(` baseDir: ${baseDir}`);
|
|
30
|
+
console.debug(` cmdsDir: ${cmdsDir}`);
|
|
31
|
+
}
|
|
32
|
+
if (cmdsDir.startsWith("/") || cmdsDir.includes("\\")) {
|
|
33
|
+
const resolved = join(baseDir, cmdsDir);
|
|
34
|
+
if (verbose) {
|
|
35
|
+
console.debug(` Using absolute path: ${resolved}`);
|
|
36
|
+
}
|
|
37
|
+
return resolved;
|
|
38
|
+
}
|
|
39
|
+
const parentDir = dirname(baseDir);
|
|
40
|
+
const parentSrcPath = join(parentDir, "src", cmdsDir);
|
|
41
|
+
try {
|
|
42
|
+
const srcStats = statSync(parentSrcPath);
|
|
43
|
+
if (srcStats.isDirectory()) {
|
|
44
|
+
if (verbose) {
|
|
45
|
+
console.debug(
|
|
46
|
+
` \u2705 Found: ${parentSrcPath} (parent src - development)`
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
return parentSrcPath;
|
|
50
|
+
}
|
|
51
|
+
} catch {
|
|
52
|
+
if (verbose) {
|
|
53
|
+
console.debug(` \u274C Not found: ${parentSrcPath}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const parentDistPath = join(parentDir, "dist", cmdsDir);
|
|
57
|
+
try {
|
|
58
|
+
const distStats = statSync(parentDistPath);
|
|
59
|
+
if (distStats.isDirectory()) {
|
|
60
|
+
if (verbose) {
|
|
61
|
+
console.debug(
|
|
62
|
+
` \u2705 Found: ${parentDistPath} (parent dist - production)`
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
return parentDistPath;
|
|
66
|
+
}
|
|
67
|
+
} catch {
|
|
68
|
+
if (verbose) {
|
|
69
|
+
console.debug(` \u274C Not found: ${parentDistPath}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const srcPath = join(baseDir, "src", cmdsDir);
|
|
73
|
+
try {
|
|
74
|
+
const srcStats = statSync(srcPath);
|
|
75
|
+
if (srcStats.isDirectory()) {
|
|
76
|
+
if (verbose) {
|
|
77
|
+
console.debug(` \u2705 Found: ${srcPath} (development)`);
|
|
78
|
+
}
|
|
79
|
+
return srcPath;
|
|
80
|
+
}
|
|
81
|
+
} catch {
|
|
82
|
+
if (verbose) {
|
|
83
|
+
console.debug(` \u274C Not found: ${srcPath}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
const distPath = join(baseDir, "dist", cmdsDir);
|
|
87
|
+
try {
|
|
88
|
+
const distStats = statSync(distPath);
|
|
89
|
+
if (distStats.isDirectory()) {
|
|
90
|
+
if (verbose) {
|
|
91
|
+
console.debug(` \u2705 Found: ${distPath} (production)`);
|
|
92
|
+
}
|
|
93
|
+
return distPath;
|
|
94
|
+
}
|
|
95
|
+
} catch {
|
|
96
|
+
if (verbose) {
|
|
97
|
+
console.debug(` \u274C Not found: ${distPath}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const fallbackPath = join(baseDir, cmdsDir);
|
|
101
|
+
try {
|
|
102
|
+
const fallbackStats = statSync(fallbackPath);
|
|
103
|
+
if (fallbackStats.isDirectory()) {
|
|
104
|
+
if (verbose) {
|
|
105
|
+
console.debug(` \u2705 Found: ${fallbackPath} (fallback)`);
|
|
106
|
+
}
|
|
107
|
+
return fallbackPath;
|
|
108
|
+
}
|
|
109
|
+
} catch {
|
|
110
|
+
if (verbose) {
|
|
111
|
+
console.debug(` \u274C Not found: ${fallbackPath}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
throw new Error(
|
|
115
|
+
`Commands directory not found. Checked: ${srcPath}, ${distPath}, ${fallbackPath}`
|
|
116
|
+
);
|
|
117
|
+
};
|
|
118
|
+
export const discoverCommands = async (cmdsDir, baseDir, verbose) => {
|
|
119
|
+
const registry = /* @__PURE__ */ new Map();
|
|
120
|
+
const aliases = /* @__PURE__ */ new Map();
|
|
121
|
+
const metadata = /* @__PURE__ */ new Map();
|
|
122
|
+
const hierarchy = /* @__PURE__ */ new Map();
|
|
123
|
+
const rootCommands = /* @__PURE__ */ new Set();
|
|
124
|
+
const glob = new Glob("**/cmd.{ts,js}");
|
|
125
|
+
const actualBaseDir = baseDir || process.cwd();
|
|
126
|
+
if (verbose) {
|
|
127
|
+
console.debug(`
|
|
128
|
+
\u{1F50D} Discovering commands:`);
|
|
129
|
+
console.debug(` actualBaseDir: ${actualBaseDir}`);
|
|
130
|
+
}
|
|
131
|
+
const resolvedCommandsDir = resolveCommandsDirectory(
|
|
132
|
+
actualBaseDir,
|
|
133
|
+
cmdsDir,
|
|
134
|
+
verbose
|
|
135
|
+
);
|
|
136
|
+
if (verbose) {
|
|
137
|
+
console.debug(` resolvedCommandsDir: ${resolvedCommandsDir}`);
|
|
138
|
+
}
|
|
139
|
+
const files = await Array.fromAsync(glob.scan(resolvedCommandsDir));
|
|
140
|
+
if (verbose) {
|
|
141
|
+
console.debug(` Found ${files.length} command files`);
|
|
142
|
+
}
|
|
143
|
+
const fileData = await pMap(
|
|
144
|
+
files,
|
|
145
|
+
async (file) => {
|
|
146
|
+
const pathParts = file.split(/[/\\]/);
|
|
147
|
+
const cmdName = pathParts[pathParts.length - 2];
|
|
148
|
+
const filePath = `${resolvedCommandsDir}/${file}`;
|
|
149
|
+
const depth = pathParts.length - 1;
|
|
150
|
+
const parent = depth > 1 ? pathParts[0] : void 0;
|
|
151
|
+
const fullPath = pathParts.slice(0, -1).join("/");
|
|
152
|
+
return {
|
|
153
|
+
cmdName,
|
|
154
|
+
filePath,
|
|
155
|
+
depth,
|
|
156
|
+
parent,
|
|
157
|
+
fullPath
|
|
158
|
+
};
|
|
159
|
+
},
|
|
160
|
+
{ concurrency: 10 }
|
|
161
|
+
// Limit concurrency to avoid overwhelming filesystem
|
|
162
|
+
);
|
|
163
|
+
for (const { cmdName, filePath, depth, parent, fullPath } of fileData) {
|
|
164
|
+
const loader = async () => {
|
|
165
|
+
try {
|
|
166
|
+
const module = await import(filePath);
|
|
167
|
+
const definition = module.default;
|
|
168
|
+
if (!definition || typeof definition.handler !== "function") {
|
|
169
|
+
throw new Error("Invalid command definition");
|
|
170
|
+
}
|
|
171
|
+
return definition;
|
|
172
|
+
} catch (error) {
|
|
173
|
+
throw new CommandLoadError(cmdName, error);
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
const lazyMetadataLoader = createLazyMetadataLoader(filePath, cmdName);
|
|
177
|
+
metadata.set(cmdName, lazyMetadataLoader);
|
|
178
|
+
registry.set(cmdName, loader);
|
|
179
|
+
const cmdNode = {
|
|
180
|
+
name: cmdName,
|
|
181
|
+
path: fullPath,
|
|
182
|
+
depth,
|
|
183
|
+
parent,
|
|
184
|
+
children: /* @__PURE__ */ new Map(),
|
|
185
|
+
loader,
|
|
186
|
+
metadata: metadata.get(cmdName)
|
|
187
|
+
};
|
|
188
|
+
hierarchy.set(cmdName, cmdNode);
|
|
189
|
+
if (depth === 1) {
|
|
190
|
+
rootCommands.add(cmdName);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
for (const [cmdName, node] of hierarchy) {
|
|
194
|
+
if (node.parent) {
|
|
195
|
+
const parentNode = hierarchy.get(node.parent);
|
|
196
|
+
if (parentNode) {
|
|
197
|
+
parentNode.children.set(cmdName, node);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return { registry, aliases, metadata, hierarchy, rootCommands };
|
|
202
|
+
};
|
|
203
|
+
export const validateCommandStructure = (definition) => {
|
|
204
|
+
if (typeof definition !== "object" || definition === null) return false;
|
|
205
|
+
const def = definition;
|
|
206
|
+
return typeof def.handler === "function" && typeof def.args === "object" && def.args !== null && typeof def.meta === "object" && def.meta !== null && typeof def.meta.name === "string";
|
|
207
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export declare class LauncherError extends Error {
|
|
2
|
+
code: string;
|
|
3
|
+
constructor(message: string, code: string);
|
|
4
|
+
}
|
|
5
|
+
export declare class CommandNotFoundError extends LauncherError {
|
|
6
|
+
constructor(cmdName: string, availableCmds: string[]);
|
|
7
|
+
}
|
|
8
|
+
export declare class ArgumentValidationError extends LauncherError {
|
|
9
|
+
argName: string;
|
|
10
|
+
reason: string;
|
|
11
|
+
constructor(argName: string, reason: string);
|
|
12
|
+
}
|
|
13
|
+
export declare class CommandLoadError extends LauncherError {
|
|
14
|
+
constructor(cmdName: string, cause: unknown);
|
|
15
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export class LauncherError extends Error {
|
|
2
|
+
constructor(message, code) {
|
|
3
|
+
super(message);
|
|
4
|
+
this.code = code;
|
|
5
|
+
this.name = "LauncherError";
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
export class CommandNotFoundError extends LauncherError {
|
|
9
|
+
constructor(cmdName, availableCmds) {
|
|
10
|
+
super(
|
|
11
|
+
`Command "${cmdName}" not found. Available commands: ${availableCmds.join(", ")}`,
|
|
12
|
+
"CMD_NOT_FOUND"
|
|
13
|
+
);
|
|
14
|
+
this.name = "CommandNotFoundError";
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export class ArgumentValidationError extends LauncherError {
|
|
18
|
+
constructor(argName, reason) {
|
|
19
|
+
super(`Invalid argument "${argName}": ${reason}`, "ARG_VALIDATION_ERROR");
|
|
20
|
+
this.argName = argName;
|
|
21
|
+
this.reason = reason;
|
|
22
|
+
this.name = "ArgumentValidationError";
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export class CommandLoadError extends LauncherError {
|
|
26
|
+
constructor(cmdName, cause) {
|
|
27
|
+
super(`Failed to load command "${cmdName}"`, "CMD_LOAD_ERROR");
|
|
28
|
+
this.name = "CommandLoadError";
|
|
29
|
+
this.cause = cause;
|
|
30
|
+
}
|
|
31
|
+
}
|