@reliverse/rempts-utils 2.3.1
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/README.md +162 -0
- package/dist/mod.d.ts +8 -0
- package/dist/mod.js +46 -0
- package/dist/prompt.d.ts +5 -0
- package/dist/prompt.js +172 -0
- package/dist/spinner.d.ts +2 -0
- package/dist/spinner.js +73 -0
- package/dist/types.d.ts +82 -0
- package/dist/types.js +0 -0
- package/dist/validation.d.ts +13 -0
- package/dist/validation.js +24 -0
- package/package.json +50 -0
package/README.md
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# rempts-utils
|
|
2
|
+
|
|
3
|
+
Utility functions for building CLI applications with Rempts.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun add rempts-utils
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- 🎨 **Colors** - Terminal colors and styling
|
|
14
|
+
- 💬 **Prompts** - Interactive prompts and confirmations
|
|
15
|
+
- ⏳ **Spinners** - Loading indicators
|
|
16
|
+
- 📋 **Formatting** - Tables, lists, and text formatting
|
|
17
|
+
- 🔍 **Validation** - Input validation helpers
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
### Colors
|
|
22
|
+
|
|
23
|
+
Style your terminal output:
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import { colors } from '@reliverse/rempts-utils'
|
|
27
|
+
|
|
28
|
+
console.log(relico.green('✓ Success!'))
|
|
29
|
+
console.log(relico.red('✗ Error!'))
|
|
30
|
+
console.log(relico.blue('ℹ Info'))
|
|
31
|
+
console.log(relico.yellow('⚠ Warning'))
|
|
32
|
+
console.log(relico.bold('Bold text'))
|
|
33
|
+
console.log(relico.dim('Dimmed text'))
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Prompts
|
|
37
|
+
|
|
38
|
+
Interactive user input:
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
import { prompt, confirm, select } from '@reliverse/rempts-utils'
|
|
42
|
+
|
|
43
|
+
// Text input
|
|
44
|
+
const name = await prompt({
|
|
45
|
+
message: 'What is your name?',
|
|
46
|
+
default: 'Anonymous',
|
|
47
|
+
validate: (value) => value.length > 0
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
// Confirmation
|
|
51
|
+
const proceed = await confirm({
|
|
52
|
+
message: 'Do you want to continue?',
|
|
53
|
+
default: true
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
// Selection
|
|
57
|
+
const choice = await select({
|
|
58
|
+
message: 'Choose your favorite framework',
|
|
59
|
+
options: [
|
|
60
|
+
{ label: 'Bun', value: 'bun' },
|
|
61
|
+
{ label: 'Node.js', value: 'node' },
|
|
62
|
+
{ label: 'Deno', value: 'deno' }
|
|
63
|
+
]
|
|
64
|
+
})
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Spinners
|
|
68
|
+
|
|
69
|
+
Show progress for long-running tasks:
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
import { spinner } from '@reliverse/rempts-utils'
|
|
73
|
+
|
|
74
|
+
const spin = spinner()
|
|
75
|
+
spin.start('Loading...')
|
|
76
|
+
|
|
77
|
+
// Do some work
|
|
78
|
+
await someAsyncTask()
|
|
79
|
+
|
|
80
|
+
spin.success('Done!')
|
|
81
|
+
// or
|
|
82
|
+
spin.error('Failed!')
|
|
83
|
+
// or
|
|
84
|
+
spin.stop()
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Tables
|
|
88
|
+
|
|
89
|
+
Display structured data:
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
import { table } from '@reliverse/rempts-utils'
|
|
93
|
+
|
|
94
|
+
const data = [
|
|
95
|
+
{ name: 'John', age: 30, city: 'New York' },
|
|
96
|
+
{ name: 'Jane', age: 25, city: 'London' },
|
|
97
|
+
{ name: 'Bob', age: 35, city: 'Paris' }
|
|
98
|
+
]
|
|
99
|
+
|
|
100
|
+
console.log(table(data))
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Lists
|
|
104
|
+
|
|
105
|
+
Format lists with bullets or numbers:
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
import { list } from '@reliverse/rempts-utils'
|
|
109
|
+
|
|
110
|
+
// Bullet list
|
|
111
|
+
console.log(list([
|
|
112
|
+
'First item',
|
|
113
|
+
'Second item',
|
|
114
|
+
'Third item'
|
|
115
|
+
]))
|
|
116
|
+
|
|
117
|
+
// Numbered list
|
|
118
|
+
console.log(list([
|
|
119
|
+
'First step',
|
|
120
|
+
'Second step',
|
|
121
|
+
'Third step'
|
|
122
|
+
], { ordered: true }))
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## API Reference
|
|
126
|
+
|
|
127
|
+
### Colors
|
|
128
|
+
|
|
129
|
+
- `relico.red(text)` - Red text
|
|
130
|
+
- `relico.green(text)` - Green text
|
|
131
|
+
- `relico.blue(text)` - Blue text
|
|
132
|
+
- `relico.yellow(text)` - Yellow text
|
|
133
|
+
- `relico.cyan(text)` - Cyan text
|
|
134
|
+
- `relico.magenta(text)` - Magenta text
|
|
135
|
+
- `relico.bold(text)` - Bold text
|
|
136
|
+
- `relico.dim(text)` - Dimmed text
|
|
137
|
+
- `relico.underline(text)` - Underlined text
|
|
138
|
+
|
|
139
|
+
### Prompts
|
|
140
|
+
|
|
141
|
+
- `prompt(options)` - Text input prompt
|
|
142
|
+
- `confirm(options)` - Yes/no confirmation
|
|
143
|
+
- `select(options)` - Single selection from list
|
|
144
|
+
- `multiselect(options)` - Multiple selection from list
|
|
145
|
+
|
|
146
|
+
### Spinners
|
|
147
|
+
|
|
148
|
+
- `spinner(options)` - Create a new spinner instance
|
|
149
|
+
- `spinner.start(message)` - Start spinning with message
|
|
150
|
+
- `spinner.success(message)` - Stop with success message
|
|
151
|
+
- `spinner.error(message)` - Stop with error message
|
|
152
|
+
- `spinner.stop()` - Stop spinning
|
|
153
|
+
|
|
154
|
+
### Formatting
|
|
155
|
+
|
|
156
|
+
- `table(data, options)` - Format data as table
|
|
157
|
+
- `list(items, options)` - Format items as list
|
|
158
|
+
- `tree(data, options)` - Format hierarchical data as tree
|
|
159
|
+
|
|
160
|
+
## License
|
|
161
|
+
|
|
162
|
+
MIT © blefnk
|
package/dist/mod.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { RemptsUtils } from "./types.js";
|
|
2
|
+
export * from "./types.js";
|
|
3
|
+
export declare const utils: RemptsUtils;
|
|
4
|
+
export { createSpinner as spinner } from "./spinner.js";
|
|
5
|
+
export declare const prompt: RemptsUtils["prompt"];
|
|
6
|
+
export { getDotPath, SchemaError } from "@standard-schema/utils";
|
|
7
|
+
export { confirm, password, select } from "./prompt.js";
|
|
8
|
+
export { validate, validateFields } from "./validation.js";
|
package/dist/mod.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { confirm, password, prompt as promptFn, select } from "./prompt.js";
|
|
2
|
+
import { createSpinner } from "./spinner.js";
|
|
3
|
+
export * from "./types.js";
|
|
4
|
+
export const utils = {
|
|
5
|
+
prompt: Object.assign(promptFn, {
|
|
6
|
+
confirm,
|
|
7
|
+
select,
|
|
8
|
+
password,
|
|
9
|
+
text: (message, options) => promptFn(message, options),
|
|
10
|
+
multiselect: async (message, options) => {
|
|
11
|
+
const { options: choices } = options;
|
|
12
|
+
const selected = [];
|
|
13
|
+
console.log(message);
|
|
14
|
+
for (const choice of choices) {
|
|
15
|
+
const ok = await confirm(`Select ${choice.label}?`, { default: false });
|
|
16
|
+
if (ok) {
|
|
17
|
+
selected.push(choice.value);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return selected;
|
|
21
|
+
}
|
|
22
|
+
}),
|
|
23
|
+
spinner: createSpinner
|
|
24
|
+
};
|
|
25
|
+
export { createSpinner as spinner } from "./spinner.js";
|
|
26
|
+
export const prompt = Object.assign(promptFn, {
|
|
27
|
+
confirm,
|
|
28
|
+
select,
|
|
29
|
+
password,
|
|
30
|
+
text: (message, options) => promptFn(message, options),
|
|
31
|
+
multiselect: async (message, options) => {
|
|
32
|
+
const { options: choices } = options;
|
|
33
|
+
const selected = [];
|
|
34
|
+
console.log(message);
|
|
35
|
+
for (const choice of choices) {
|
|
36
|
+
const ok = await confirm(`Select ${choice.label}?`, { default: false });
|
|
37
|
+
if (ok) {
|
|
38
|
+
selected.push(choice.value);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return selected;
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
export { getDotPath, SchemaError } from "@standard-schema/utils";
|
|
45
|
+
export { confirm, password, select } from "./prompt.js";
|
|
46
|
+
export { validate, validateFields } from "./validation.js";
|
package/dist/prompt.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ConfirmOptions, PromptOptions, SelectOptions } from "./types.js";
|
|
2
|
+
export declare function prompt<T = string>(message: string, options?: PromptOptions): Promise<T>;
|
|
3
|
+
export declare function confirm(message: string, options?: ConfirmOptions): Promise<boolean>;
|
|
4
|
+
export declare function select<T = string>(message: string, options: SelectOptions<T>): Promise<T>;
|
|
5
|
+
export declare function password<T = string>(message: string, options?: PromptOptions): Promise<T>;
|
package/dist/prompt.js
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { relico } from "@reliverse/relico";
|
|
2
|
+
const ESC = "\x1B";
|
|
3
|
+
const CSI = `${ESC}[`;
|
|
4
|
+
const CLEAR_LINE = `${CSI}2K`;
|
|
5
|
+
const CURSOR_START = `${CSI}G`;
|
|
6
|
+
const CURSOR_HIDE = `${CSI}?25l`;
|
|
7
|
+
const CURSOR_SHOW = `${CSI}?25h`;
|
|
8
|
+
async function readline(prompt2) {
|
|
9
|
+
process.stdout.write(prompt2);
|
|
10
|
+
for await (const line of console) {
|
|
11
|
+
return line;
|
|
12
|
+
}
|
|
13
|
+
return "";
|
|
14
|
+
}
|
|
15
|
+
export async function prompt(message, options = {}) {
|
|
16
|
+
const defaultHint = options.default ? ` (${options.default})` : "";
|
|
17
|
+
const promptText = `${message}${defaultHint} `;
|
|
18
|
+
while (true) {
|
|
19
|
+
const input = await readline(promptText);
|
|
20
|
+
const value = input.trim() || options.default || "";
|
|
21
|
+
if (options.schema) {
|
|
22
|
+
const result = await options.schema["~standard"].validate(value);
|
|
23
|
+
if (result.issues) {
|
|
24
|
+
console.error(relico.red("Invalid input:"));
|
|
25
|
+
for (const issue of result.issues) {
|
|
26
|
+
console.error(relico.dim(` \u2022 ${issue.message}`));
|
|
27
|
+
}
|
|
28
|
+
console.error();
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
return result.value;
|
|
32
|
+
}
|
|
33
|
+
if (options.validate) {
|
|
34
|
+
const result = options.validate(value);
|
|
35
|
+
if (result === true) {
|
|
36
|
+
return value;
|
|
37
|
+
}
|
|
38
|
+
if (typeof result === "string") {
|
|
39
|
+
console.error(`\u2717 ${result}`);
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
console.error("\u2717 Invalid input");
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
return value;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
export async function confirm(message, options = {}) {
|
|
49
|
+
const defaultHint = options.default === true ? "Y/n" : options.default === false ? "y/N" : "y/n";
|
|
50
|
+
const promptText = `${message} (${defaultHint}) `;
|
|
51
|
+
while (true) {
|
|
52
|
+
const input = await readline(promptText);
|
|
53
|
+
const value = input.trim().toLowerCase();
|
|
54
|
+
if (!value && options.default !== void 0) {
|
|
55
|
+
return options.default;
|
|
56
|
+
}
|
|
57
|
+
if (value === "y" || value === "yes") {
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
if (value === "n" || value === "no") {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
console.error("\u2717 Please answer with y/yes or n/no");
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
export async function select(message, options) {
|
|
67
|
+
const { options: choices, default: defaultValue } = options;
|
|
68
|
+
let selectedIndex = defaultValue ? choices.findIndex((opt) => opt.value === defaultValue) : 0;
|
|
69
|
+
if (selectedIndex === -1) {
|
|
70
|
+
selectedIndex = 0;
|
|
71
|
+
}
|
|
72
|
+
console.log(message);
|
|
73
|
+
process.stdout.write(CURSOR_HIDE);
|
|
74
|
+
drawOptions(choices, selectedIndex);
|
|
75
|
+
return new Promise((resolve) => {
|
|
76
|
+
process.stdin.setRawMode(true);
|
|
77
|
+
process.stdin.resume();
|
|
78
|
+
const cleanup = () => {
|
|
79
|
+
process.stdin.setRawMode(false);
|
|
80
|
+
process.stdin.pause();
|
|
81
|
+
process.stdout.write(CURSOR_SHOW);
|
|
82
|
+
};
|
|
83
|
+
process.stdin.on("data", (data) => {
|
|
84
|
+
const key = data.toString();
|
|
85
|
+
if (key === "\x1B[A") {
|
|
86
|
+
selectedIndex = Math.max(0, selectedIndex - 1);
|
|
87
|
+
drawOptions(choices, selectedIndex);
|
|
88
|
+
} else if (key === "\x1B[B") {
|
|
89
|
+
selectedIndex = Math.min(choices.length - 1, selectedIndex + 1);
|
|
90
|
+
drawOptions(choices, selectedIndex);
|
|
91
|
+
} else if (key === "\r" || key === "\n") {
|
|
92
|
+
cleanup();
|
|
93
|
+
for (let i = 0; i < choices.length; i++) {
|
|
94
|
+
process.stdout.write(`${CSI}1A${CLEAR_LINE}`);
|
|
95
|
+
}
|
|
96
|
+
const selected = choices[selectedIndex];
|
|
97
|
+
if (selected) {
|
|
98
|
+
console.log(`\u2713 ${selected.label}`);
|
|
99
|
+
resolve(selected.value);
|
|
100
|
+
}
|
|
101
|
+
} else if (key === "" || key === "\x1B") {
|
|
102
|
+
cleanup();
|
|
103
|
+
process.exit(0);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
function drawOptions(options, selectedIndex) {
|
|
109
|
+
for (let i = 0; i < options.length; i++) {
|
|
110
|
+
process.stdout.write(`${CSI}1A`);
|
|
111
|
+
}
|
|
112
|
+
options.forEach((option, index) => {
|
|
113
|
+
process.stdout.write(CLEAR_LINE + CURSOR_START);
|
|
114
|
+
const prefix = index === selectedIndex ? "\u276F " : " ";
|
|
115
|
+
const hint = option.hint ? ` (${option.hint})` : "";
|
|
116
|
+
console.log(`${prefix}${option.label}${hint}`);
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
export async function password(message, options = {}) {
|
|
120
|
+
process.stdout.write(`${message} `);
|
|
121
|
+
return new Promise((resolve) => {
|
|
122
|
+
let input = "";
|
|
123
|
+
process.stdin.setRawMode(true);
|
|
124
|
+
process.stdin.resume();
|
|
125
|
+
const cleanup = () => {
|
|
126
|
+
process.stdin.setRawMode(false);
|
|
127
|
+
process.stdin.pause();
|
|
128
|
+
console.log();
|
|
129
|
+
};
|
|
130
|
+
process.stdin.on("data", async (data) => {
|
|
131
|
+
const key = data.toString();
|
|
132
|
+
if (key === "\r" || key === "\n") {
|
|
133
|
+
cleanup();
|
|
134
|
+
if (options.schema) {
|
|
135
|
+
const result = await options.schema["~standard"].validate(input);
|
|
136
|
+
if (result.issues) {
|
|
137
|
+
console.error(relico.red("Invalid input:"));
|
|
138
|
+
for (const issue of result.issues) {
|
|
139
|
+
console.error(relico.dim(` \u2022 ${issue.message}`));
|
|
140
|
+
}
|
|
141
|
+
console.error();
|
|
142
|
+
password(message, options).then(resolve);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
resolve(result.value);
|
|
146
|
+
} else if (options.validate) {
|
|
147
|
+
const result = options.validate(input);
|
|
148
|
+
if (result === true) {
|
|
149
|
+
resolve(input);
|
|
150
|
+
} else {
|
|
151
|
+
const errorMsg = typeof result === "string" ? result : "Invalid input";
|
|
152
|
+
console.error(`\u2717 ${errorMsg}`);
|
|
153
|
+
password(message, options).then(resolve);
|
|
154
|
+
}
|
|
155
|
+
} else {
|
|
156
|
+
resolve(input);
|
|
157
|
+
}
|
|
158
|
+
} else if (key === "") {
|
|
159
|
+
cleanup();
|
|
160
|
+
process.exit(0);
|
|
161
|
+
} else if (key === "\x7F" || key === "\b") {
|
|
162
|
+
if (input.length > 0) {
|
|
163
|
+
input = input.slice(0, -1);
|
|
164
|
+
process.stdout.write("\b \b");
|
|
165
|
+
}
|
|
166
|
+
} else if (key.length === 1 && key >= " ") {
|
|
167
|
+
input += key;
|
|
168
|
+
process.stdout.write("*");
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
}
|
package/dist/spinner.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
const SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
2
|
+
const CLEAR_LINE = "\x1B[2K";
|
|
3
|
+
const CURSOR_START = "\x1B[G";
|
|
4
|
+
export function createSpinner(options) {
|
|
5
|
+
const config = typeof options === "string" ? { text: options } : options || {};
|
|
6
|
+
let isSpinning = false;
|
|
7
|
+
let frameIndex = 0;
|
|
8
|
+
let intervalId = null;
|
|
9
|
+
let currentText = config.text || "";
|
|
10
|
+
const render = (symbol, text) => {
|
|
11
|
+
process.stdout.write(`${CLEAR_LINE}${CURSOR_START}${symbol} ${text}`);
|
|
12
|
+
};
|
|
13
|
+
const spinner = {
|
|
14
|
+
start(text) {
|
|
15
|
+
if (isSpinning) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
isSpinning = true;
|
|
19
|
+
if (text !== void 0) {
|
|
20
|
+
currentText = text;
|
|
21
|
+
}
|
|
22
|
+
process.stdout.write("\x1B[?25l");
|
|
23
|
+
intervalId = setInterval(() => {
|
|
24
|
+
const frame = SPINNER_FRAMES[frameIndex];
|
|
25
|
+
render(frame, currentText);
|
|
26
|
+
frameIndex = (frameIndex + 1) % SPINNER_FRAMES.length;
|
|
27
|
+
}, 80);
|
|
28
|
+
},
|
|
29
|
+
stop(text) {
|
|
30
|
+
if (!isSpinning) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
isSpinning = false;
|
|
34
|
+
if (intervalId) {
|
|
35
|
+
clearInterval(intervalId);
|
|
36
|
+
intervalId = null;
|
|
37
|
+
}
|
|
38
|
+
process.stdout.write(CLEAR_LINE + CURSOR_START);
|
|
39
|
+
process.stdout.write("\x1B[?25h");
|
|
40
|
+
if (text) {
|
|
41
|
+
console.log(text);
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
succeed(text) {
|
|
45
|
+
this.stop();
|
|
46
|
+
console.log(`\u2705 ${text || currentText}`);
|
|
47
|
+
},
|
|
48
|
+
fail(text) {
|
|
49
|
+
this.stop();
|
|
50
|
+
console.log(`\u274C ${text || currentText}`);
|
|
51
|
+
},
|
|
52
|
+
warn(text) {
|
|
53
|
+
this.stop();
|
|
54
|
+
console.log(`\u26A0\uFE0F ${text || currentText}`);
|
|
55
|
+
},
|
|
56
|
+
info(text) {
|
|
57
|
+
this.stop();
|
|
58
|
+
console.log(`\u2139\uFE0F ${text || currentText}`);
|
|
59
|
+
},
|
|
60
|
+
update(text) {
|
|
61
|
+
currentText = text;
|
|
62
|
+
if (isSpinning) {
|
|
63
|
+
render(SPINNER_FRAMES[frameIndex], currentText);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
process.on("exit", () => spinner.stop());
|
|
68
|
+
process.on("SIGINT", () => {
|
|
69
|
+
spinner.stop();
|
|
70
|
+
process.exit(0);
|
|
71
|
+
});
|
|
72
|
+
return spinner;
|
|
73
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { StandardSchemaV1 } from "@standard-schema/spec";
|
|
2
|
+
export interface PromptOptions {
|
|
3
|
+
default?: string;
|
|
4
|
+
validate?: (input: string) => boolean | string;
|
|
5
|
+
schema?: StandardSchemaV1;
|
|
6
|
+
placeholder?: string;
|
|
7
|
+
multiline?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface ConfirmOptions {
|
|
10
|
+
default?: boolean;
|
|
11
|
+
}
|
|
12
|
+
export interface SelectOption<T = string> {
|
|
13
|
+
label: string;
|
|
14
|
+
value: T;
|
|
15
|
+
hint?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface SelectOptions<T = string> {
|
|
18
|
+
options: SelectOption<T>[];
|
|
19
|
+
default?: T;
|
|
20
|
+
hint?: string;
|
|
21
|
+
}
|
|
22
|
+
export interface MultiSelectOptions<T = string> extends SelectOptions<T> {
|
|
23
|
+
min?: number;
|
|
24
|
+
max?: number;
|
|
25
|
+
}
|
|
26
|
+
export interface SpinnerOptions {
|
|
27
|
+
text?: string;
|
|
28
|
+
color?: string;
|
|
29
|
+
}
|
|
30
|
+
export interface Spinner {
|
|
31
|
+
start(text?: string): void;
|
|
32
|
+
stop(text?: string): void;
|
|
33
|
+
succeed(text?: string): void;
|
|
34
|
+
fail(text?: string): void;
|
|
35
|
+
warn(text?: string): void;
|
|
36
|
+
info(text?: string): void;
|
|
37
|
+
update(text: string): void;
|
|
38
|
+
}
|
|
39
|
+
export type ColorFunction = (text: string) => string;
|
|
40
|
+
export interface Colors {
|
|
41
|
+
black: ColorFunction;
|
|
42
|
+
red: ColorFunction;
|
|
43
|
+
green: ColorFunction;
|
|
44
|
+
yellow: ColorFunction;
|
|
45
|
+
blue: ColorFunction;
|
|
46
|
+
magenta: ColorFunction;
|
|
47
|
+
cyan: ColorFunction;
|
|
48
|
+
white: ColorFunction;
|
|
49
|
+
gray: ColorFunction;
|
|
50
|
+
brightRed: ColorFunction;
|
|
51
|
+
brightGreen: ColorFunction;
|
|
52
|
+
brightYellow: ColorFunction;
|
|
53
|
+
brightBlue: ColorFunction;
|
|
54
|
+
brightMagenta: ColorFunction;
|
|
55
|
+
brightCyan: ColorFunction;
|
|
56
|
+
brightWhite: ColorFunction;
|
|
57
|
+
bgRed: ColorFunction;
|
|
58
|
+
bgGreen: ColorFunction;
|
|
59
|
+
bgYellow: ColorFunction;
|
|
60
|
+
bgBlue: ColorFunction;
|
|
61
|
+
bgMagenta: ColorFunction;
|
|
62
|
+
bgCyan: ColorFunction;
|
|
63
|
+
bgWhite: ColorFunction;
|
|
64
|
+
bold: ColorFunction;
|
|
65
|
+
dim: ColorFunction;
|
|
66
|
+
italic: ColorFunction;
|
|
67
|
+
underline: ColorFunction;
|
|
68
|
+
strikethrough: ColorFunction;
|
|
69
|
+
reset: ColorFunction;
|
|
70
|
+
strip: (text: string) => string;
|
|
71
|
+
}
|
|
72
|
+
export interface RemptsUtils {
|
|
73
|
+
prompt: {
|
|
74
|
+
<T = string>(message: string, options?: PromptOptions): Promise<T>;
|
|
75
|
+
confirm(message: string, options?: ConfirmOptions): Promise<boolean>;
|
|
76
|
+
select<T = string>(message: string, options: SelectOptions<T>): Promise<T>;
|
|
77
|
+
password<T = string>(message: string, options?: PromptOptions): Promise<T>;
|
|
78
|
+
text(message: string, options?: PromptOptions): Promise<string>;
|
|
79
|
+
multiselect<T = string>(message: string, options: MultiSelectOptions<T>): Promise<T[]>;
|
|
80
|
+
};
|
|
81
|
+
spinner: (options?: SpinnerOptions | string) => Spinner;
|
|
82
|
+
}
|
package/dist/types.js
ADDED
|
File without changes
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { StandardSchemaV1 } from "@standard-schema/spec";
|
|
2
|
+
/**
|
|
3
|
+
* Validate a value against a schema and throw SchemaError on failure
|
|
4
|
+
*/
|
|
5
|
+
export declare function validate<TSchema extends StandardSchemaV1>(schema: TSchema, value: unknown): Promise<StandardSchemaV1.InferOutput<TSchema>>;
|
|
6
|
+
/**
|
|
7
|
+
* Validate multiple fields and return aggregated errors
|
|
8
|
+
*/
|
|
9
|
+
export declare function validateFields<T extends Record<string, StandardSchemaV1>>(schemas: T, values: Record<string, unknown>): Promise<{
|
|
10
|
+
[K in keyof T]: StandardSchemaV1.InferOutput<T[K]>;
|
|
11
|
+
} | {
|
|
12
|
+
errors: Record<string, string[]>;
|
|
13
|
+
}>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { SchemaError } from "@standard-schema/utils";
|
|
2
|
+
export async function validate(schema, value) {
|
|
3
|
+
const result = await schema["~standard"].validate(value);
|
|
4
|
+
if (result.issues) {
|
|
5
|
+
throw new SchemaError(result.issues);
|
|
6
|
+
}
|
|
7
|
+
return result.value;
|
|
8
|
+
}
|
|
9
|
+
export async function validateFields(schemas, values) {
|
|
10
|
+
const results = {};
|
|
11
|
+
const errors = {};
|
|
12
|
+
for (const [field, schema] of Object.entries(schemas)) {
|
|
13
|
+
const result = await schema["~standard"].validate(values[field]);
|
|
14
|
+
if (result.issues) {
|
|
15
|
+
errors[field] = result.issues.map((issue) => issue.message);
|
|
16
|
+
} else {
|
|
17
|
+
results[field] = result.value;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
if (Object.keys(errors).length > 0) {
|
|
21
|
+
return { errors };
|
|
22
|
+
}
|
|
23
|
+
return results;
|
|
24
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@reliverse/rempts-utils",
|
|
3
|
+
"version": "2.3.1",
|
|
4
|
+
"description": "Built-in utilities for Rempts CLI framework - prompts, spinners, and colors",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"cli",
|
|
7
|
+
"prompts",
|
|
8
|
+
"rempts",
|
|
9
|
+
"spinner",
|
|
10
|
+
"terminal",
|
|
11
|
+
"utilities"
|
|
12
|
+
],
|
|
13
|
+
"homepage": "https://github.com/reliverse/dler#readme",
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/reliverse/dler/issues"
|
|
16
|
+
},
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"author": "blefnk",
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "https://github.com/reliverse/dler.git",
|
|
22
|
+
"directory": "packages/utils"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist",
|
|
26
|
+
"package.json",
|
|
27
|
+
"README.md"
|
|
28
|
+
],
|
|
29
|
+
"type": "module",
|
|
30
|
+
"module": "./src/mod.ts",
|
|
31
|
+
"types": "./src/mod.ts",
|
|
32
|
+
"exports": {
|
|
33
|
+
".": {
|
|
34
|
+
"types": "./dist/mod.d.ts",
|
|
35
|
+
"import": "./dist/mod.js"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"publishConfig": {
|
|
39
|
+
"access": "public"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@reliverse/relico": "2.3.1",
|
|
43
|
+
"@standard-schema/spec": "^1.1.0",
|
|
44
|
+
"@standard-schema/utils": "^0.3.0",
|
|
45
|
+
"arktype": "^2.1.29"
|
|
46
|
+
},
|
|
47
|
+
"engines": {
|
|
48
|
+
"bun": ">=1.3.5"
|
|
49
|
+
}
|
|
50
|
+
}
|