erii 2.0.6 → 3.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,193 @@
1
+ import validator from "validator";
2
+ //#region src/types.d.ts
3
+ /** Raw values the CLI parser may return; generic declarations do not convert them. */
4
+ type ArgumentValue = string | number | boolean | ArgumentValue[] | {
5
+ [key: string]: ArgumentValue;
6
+ };
7
+ /** Raw options stored internally, before narrowing to a command schema. */
8
+ type CommandOptions = Record<string, ArgumentValue | undefined>;
9
+ interface ParsedArguments extends CommandOptions {
10
+ _: Array<string | number>;
11
+ }
12
+ type LifecycleHandler = () => unknown;
13
+ /** Custom validators receive raw values and return false when validation fails. */
14
+ type ArgumentValidator = (value: ArgumentValue | undefined, logger: (message: string) => void) => boolean;
15
+ /** Only validator.js methods callable with a single string and returning a boolean are allowed. */
16
+ type ValidatorName = { [K in keyof typeof validator]: (typeof validator)[K] extends ((value: string) => boolean) ? K : never; }[keyof typeof validator];
17
+ interface MetaInfo {
18
+ version?: string;
19
+ name?: string;
20
+ }
21
+ /** Argument metadata and runtime validation rules for a command or option. */
22
+ interface Argument {
23
+ name: string;
24
+ description: string;
25
+ validate?: ValidatorName | ArgumentValidator;
26
+ }
27
+ type SchemaCommands<Schema> = Schema extends {
28
+ commands: infer Commands;
29
+ } ? Commands : never;
30
+ type LocalOptions<Command> = Command extends {
31
+ options?: infer Options;
32
+ } ? NonNullable<Options> : {};
33
+ type CommonOptions<Schema> = Schema extends {
34
+ commonOptions?: infer Options;
35
+ } ? NonNullable<Options> : {};
36
+ type SchemaOptionValues<Options> = { [Name in keyof Options]: ArgumentValue | undefined; };
37
+ type SchemaCommand<Command> = {
38
+ argument?: ArgumentValue;
39
+ options?: SchemaOptionValues<LocalOptions<Command>>;
40
+ aliases?: string;
41
+ };
42
+ type DefaultSchema = {
43
+ commands: Record<string, {
44
+ options?: CommandOptions;
45
+ }>;
46
+ commonOptions?: CommandOptions;
47
+ };
48
+ /**
49
+ * Compile-time schema constraints, with no role in runtime parsing or validation.
50
+ * Checks the supplied schema's own keys, supporting named interfaces without widening valid names.
51
+ */
52
+ interface EriiSchema<Schema = DefaultSchema> {
53
+ commonOptions?: SchemaOptionValues<CommonOptions<Schema>>;
54
+ commands: { [Name in keyof SchemaCommands<Schema>]: SchemaCommand<SchemaCommands<Schema>[Name]>; };
55
+ }
56
+ type CommandName<Schema extends EriiSchema<Schema>> = Extract<keyof Schema["commands"], string>;
57
+ type CommandAliases<Command> = "aliases" extends keyof Command ? Extract<Command["aliases"], string> : never;
58
+ /** All valid command lookup names, including aliases declared in the schema. */
59
+ type CommandLookup<Schema extends EriiSchema<Schema>> = { [Name in CommandName<Schema>]: Name | CommandAliases<Schema["commands"][Name]>; }[CommandName<Schema>];
60
+ /** Resolves a primary name or alias to its command key in the schema. */
61
+ type ResolveCommand<Schema extends EriiSchema<Schema>, Lookup extends CommandLookup<Schema>> = { [Name in CommandName<Schema>]: Lookup extends Name | CommandAliases<Schema["commands"][Name]> ? Name : never; }[CommandName<Schema>];
62
+ /** Detects argument declarations by key presence, preserving the optional value type of argument?: T. */
63
+ type CommandArgument<Command> = "argument" extends keyof Command ? Command["argument"] : ArgumentValue;
64
+ type LowerLetter = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z";
65
+ /** Generates candidate camelCase names for kebab-case keys; underscores are not converted. */
66
+ type CamelKey<Key extends string> = Key extends `${infer Head}-${infer Letter}${infer Tail}` ? Letter extends LowerLetter ? `${Head}${Uppercase<Letter>}${CamelKey<Tail>}` : `${Head}-${CamelKey<`${Letter}${Tail}`>}` : Key;
67
+ type KebabLetter<Letter extends string> = Letter extends Uppercase<LowerLetter> ? `-${Lowercase<Letter>}` : Letter;
68
+ type KebabTail<Key extends string> = Key extends `${infer Head}${infer Tail}` ? `${KebabLetter<Head>}${KebabTail<Tail>}` : Key;
69
+ /** Matches runtime toKebabCase: lowercases the first letter and inserts hyphens before subsequent uppercase ASCII letters. */
70
+ type KebabKey<Key extends string> = Key extends `${infer Head}${infer Tail}` ? `${Lowercase<Head>}${KebabTail<Tail>}` : Key;
71
+ /** Only candidates that the proxy can resolve back to the original key are valid aliases. */
72
+ type CamelAliases<Options> = { [Name in Extract<keyof Options, string>]: KebabKey<CamelKey<Name>> extends Name ? CamelKey<Name> : never; }[Extract<keyof Options, string>];
73
+ type OptionValue<Options, Name> = Name extends keyof Options ? Options[Name] : never;
74
+ type FallbackOptionValue<Options, Name> = Name extends string ? OptionValue<Options, KebabKey<Name>> : never;
75
+ /**
76
+ * Matches proxy reads: checks the original key first, then falls back to the kebab-case key when absent.
77
+ * Name collisions may yield either value; any option may be omitted.
78
+ */
79
+ type ProxiedOptions<Options> = { readonly [Name in keyof Options | CamelAliases<Options>]?: OptionValue<Options, Name> | FallbackOptionValue<Options, Name>; };
80
+ /** Common option names take precedence and are excluded from command-specific declarations. */
81
+ type ScopedOptions<Schema extends EriiSchema<Schema>, Name extends CommandName<Schema>> = Omit<LocalOptions<Schema["commands"][Name]>, keyof CommonOptions<Schema>>;
82
+ /** Options available to a handler: command-specific options, common options, and camelCase aliases. */
83
+ type TypedCommandOptions<Schema extends EriiSchema<Schema>, Name extends CommandName<Schema>> = ProxiedOptions<ScopedOptions<Schema, Name> & CommonOptions<Schema>>;
84
+ /** Reads the current command's argument when no name is supplied, or the argument of the named command or alias. */
85
+ interface TypedCommandCtx<Schema extends EriiSchema<Schema>, Name extends CommandName<Schema>> {
86
+ showVersion: () => void;
87
+ showHelp: () => void;
88
+ getArgument(): CommandArgument<Schema["commands"][Name]> | undefined;
89
+ getArgument<Lookup extends CommandLookup<Schema>>(name: Lookup): LookupArgument<Schema, Lookup> | undefined;
90
+ }
91
+ /** Provides context and option types matching the specified command for independently defined handlers. */
92
+ type TypedCommandHandler<Schema extends EriiSchema<Schema>, Name extends CommandName<Schema>> = (ctx: TypedCommandCtx<Schema, Name>, options: TypedCommandOptions<Schema, Name>) => unknown;
93
+ /** Argument metadata is required, optional, or forbidden according to the schema declaration. */
94
+ type ArgumentDefinition<Command> = "argument" extends keyof Command ? {} extends Pick<Command, "argument"> ? {
95
+ argument?: Argument;
96
+ } : {
97
+ argument: Argument;
98
+ } : {
99
+ argument?: never;
100
+ };
101
+ /** bind configuration: the first name must be the primary command name; subsequent names must be its aliases. */
102
+ type TypedCommand<Schema extends EriiSchema<Schema>, Name extends CommandName<Schema>> = {
103
+ name: Name | readonly [Name, ...CommandAliases<Schema["commands"][Name]>[]];
104
+ description?: string;
105
+ } & ArgumentDefinition<Schema["commands"][Name]>;
106
+ type OptionDefinition<Name extends string> = {
107
+ name: Name | readonly [Name, ...string[]];
108
+ description?: string;
109
+ argument?: Argument;
110
+ };
111
+ /** Generates a separate configuration for each option, preserving the association between its name and command scope. */
112
+ type OptionDefinitions<Options, Scope> = { [Name in Extract<keyof Options, string>]: OptionDefinition<Name> & Scope; }[Extract<keyof Options, string>];
113
+ type ScopedOptionDefinitions<Schema extends EriiSchema<Schema>> = { [Name in CommandName<Schema>]: OptionDefinitions<ScopedOptions<Schema, Name>, {
114
+ command: Name | CommandAliases<Schema["commands"][Name]>;
115
+ }>; }[CommandName<Schema>];
116
+ /** addOption configuration: common options omit command; scoped options must specify their owning command or alias. */
117
+ type TypedOption<Schema extends EriiSchema<Schema>> = OptionDefinitions<CommonOptions<Schema>, {
118
+ command?: never;
119
+ }> | ScopedOptionDefinitions<Schema>;
120
+ /** Internal lookup result for Erii.getArgument; the method signature separately accounts for missing values. */
121
+ type LookupArgument<Schema extends EriiSchema<Schema>, Lookup extends CommandLookup<Schema>> = CommandArgument<Schema["commands"][ResolveCommand<Schema, Lookup>]>;
122
+ //#endregion
123
+ //#region src/erii.d.ts
124
+ export declare class Erii<S extends EriiSchema<S> = never> {
125
+ rawArguments: string[];
126
+ parsedArguments: ParsedArguments;
127
+ private version;
128
+ private name;
129
+ private commands;
130
+ private commonOptions;
131
+ validator: typeof validator;
132
+ alwaysHandler?: LifecycleHandler;
133
+ defaultHandler?: LifecycleHandler;
134
+ constructor(..._schema: [S] extends [never] ? [schema: never] : []);
135
+ /**
136
+ * Binds a command handler.
137
+ * @param config
138
+ * @param handler
139
+ */
140
+ bind<K extends CommandName<S>>(config: TypedCommand<S, K>, handler: TypedCommandHandler<S, NoInfer<K>>): void;
141
+ private bindCommand;
142
+ /**
143
+ * Registers a handler that runs on every start.
144
+ * @param handler
145
+ */
146
+ always(handler: LifecycleHandler): void;
147
+ default(handler: LifecycleHandler): void;
148
+ /**
149
+ * Registers an option.
150
+ * @param config
151
+ */
152
+ addOption(config: TypedOption<S>): void;
153
+ private registerOption;
154
+ private commandCtx;
155
+ /**
156
+ * Sets the CLI metadata.
157
+ * @param metaInfo
158
+ */
159
+ setMetaInfo({ version, name }?: MetaInfo): void;
160
+ /**
161
+ * Displays help information.
162
+ */
163
+ showHelp(command?: CommandLookup<S>): void;
164
+ /**
165
+ * Displays the version number.
166
+ */
167
+ showVersion(): void;
168
+ /**
169
+ * Starts command dispatch.
170
+ */
171
+ start(): void;
172
+ /**
173
+ * Executes the command handler.
174
+ * @param command
175
+ */
176
+ private exec;
177
+ validateArgument(argumentValue: ArgumentValue | undefined, argument?: Argument): boolean;
178
+ /**
179
+ * Gets the argument for a command.
180
+ * @param commandName
181
+ * @param followRedirect Whether to follow alias redirects.
182
+ */
183
+ getArgument<N extends CommandLookup<S>>(commandName: N, followRedirect?: boolean): LookupArgument<S, N> | undefined;
184
+ private readArgument;
185
+ private findArgument;
186
+ /**
187
+ * Starts command dispatch.
188
+ * Erii is awake!
189
+ */
190
+ okite(): void;
191
+ }
192
+ //#endregion
193
+ export { type Argument, type ArgumentValidator, type ArgumentValue, Erii as default, type EriiSchema, type LifecycleHandler, type MetaInfo, type TypedCommand, type TypedCommandCtx, type TypedCommandHandler, type TypedCommandOptions, type TypedOption, type ValidatorName };
package/dist/index.mjs ADDED
@@ -0,0 +1,234 @@
1
+ import yargs from "yargs-parser";
2
+ import chalk from "chalk";
3
+ import validator from "validator";
4
+ import CLI from "clui";
5
+ import clc from "cli-color";
6
+ //#region src/help.ts
7
+ /** Render the help body; the caller prints the version first. */
8
+ function renderHelp(commands, commonOptions) {
9
+ console.log("\nHelp:");
10
+ const Line = CLI.Line;
11
+ new Line().padding(5).column("Commands", 30, [clc.cyan]).column("Description", 30, [clc.cyan]).column("Alias", 20, [clc.cyan]).output();
12
+ new Line().output();
13
+ for (const [key, command] of Object.entries(commands)) {
14
+ if (!command || command.redirect) continue;
15
+ const commandText = "--" + key + (command.argument ? ` <${command.argument.name}>` : "");
16
+ const aliasText = (command.alias ?? []).map((alias) => alias.length === 1 ? `-${alias}` : `--${alias}`).join(" / ");
17
+ new Line().padding(5).column(commandText, 30).column(command.description ?? "", 30).column(aliasText, 20).output();
18
+ if (command.argument) new Line().padding(9).column(`<${command.argument.name}>`, 26).column(command.argument.description, 30).output();
19
+ for (const option of command.options ?? []) renderOption(option, 9, 26);
20
+ }
21
+ new Line().output();
22
+ if (commonOptions.length > 0) {
23
+ console.log("Options:\n");
24
+ new Line().padding(5).column("Options", 30, [clc.cyan]).column("Description", 30, [clc.cyan]).output();
25
+ for (const option of commonOptions) renderOption(option, 5, 30);
26
+ }
27
+ }
28
+ /** Scoped and common options share formatting, with different indentation. */
29
+ function renderOption(option, padding, width) {
30
+ const text = `--${(Array.isArray(option.name) ? option.name : [option.name]).join(", ")}` + (option.argument ? ` <${option.argument.name}>` : "");
31
+ new CLI.Line().padding(padding).column(text, width).column(option.description || "", 30).output();
32
+ if (option.argument) new CLI.Line().padding(padding + 4).column(`<${option.argument.name}>`, width - 4).column(option.argument.description || "", 30).output();
33
+ }
34
+ //#endregion
35
+ //#region src/utils/convert.ts
36
+ function toKebabCase(str) {
37
+ return (str[0]?.toLowerCase() ?? "") + str.slice(1).replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`);
38
+ }
39
+ function createCamelProxifiedObject(before) {
40
+ return new Proxy(before, {
41
+ get(target, key, receiver) {
42
+ const value = Reflect.get(target, key, receiver);
43
+ return typeof key === "string" ? value ?? Reflect.get(target, toKebabCase(key), receiver) : value;
44
+ },
45
+ set(target, key, value, receiver) {
46
+ if (typeof key === "string" && !key.includes("-") && Reflect.get(target, key, receiver) == null && Reflect.get(target, toKebabCase(key), receiver) != null) return Reflect.set(target, toKebabCase(key), value, receiver);
47
+ return Reflect.set(target, key, value, receiver);
48
+ }
49
+ });
50
+ }
51
+ //#endregion
52
+ //#region src/erii.ts
53
+ var Erii = class {
54
+ rawArguments;
55
+ parsedArguments;
56
+ version = "1.0.0";
57
+ name = "Erii";
58
+ commands = {};
59
+ commonOptions = [];
60
+ validator;
61
+ alwaysHandler;
62
+ defaultHandler;
63
+ constructor(..._schema) {
64
+ this.rawArguments = process.argv.slice(2);
65
+ this.parsedArguments = yargs(process.argv.slice(2), { configuration: { "boolean-negation": false } });
66
+ this.validator = validator;
67
+ for (const key of Object.keys(this.parsedArguments)) if (key !== "_" && !this.rawArguments.includes("--" + key) && !this.rawArguments.includes("-" + key)) delete this.parsedArguments[key];
68
+ }
69
+ bind(config, handler) {
70
+ this.bindCommand(config, handler);
71
+ }
72
+ bindCommand(config, handler) {
73
+ if (config.name === void 0) return console.error(chalk.red("Invalid command binding, ignored."));
74
+ const { name, description, argument } = config;
75
+ const [mainCommand, ...aliases] = Array.isArray(name) ? name : [name];
76
+ if (!mainCommand) {
77
+ console.error(chalk.red("Invalid command binding, ignored."));
78
+ return;
79
+ }
80
+ this.commands[mainCommand] = {
81
+ name: mainCommand,
82
+ description,
83
+ argument,
84
+ alias: aliases,
85
+ options: [],
86
+ handler
87
+ };
88
+ if (Array.isArray(name)) for (const alias of aliases) this.commands[alias] = {
89
+ name: alias,
90
+ redirect: mainCommand
91
+ };
92
+ }
93
+ /**
94
+ * Registers a handler that runs on every start.
95
+ * @param handler
96
+ */
97
+ always(handler) {
98
+ this.alwaysHandler = handler;
99
+ }
100
+ default(handler) {
101
+ this.defaultHandler = handler;
102
+ }
103
+ addOption(config) {
104
+ this.registerOption(config);
105
+ }
106
+ registerOption(config) {
107
+ config.name = Array.isArray(config.name) ? config.name : [config.name];
108
+ if (!config.command) this.commonOptions.push(config);
109
+ else {
110
+ if (!(config.command in this.commands)) return console.error(chalk.red(`Command for option [${config.name.join(", ")}] not found, ignored.`));
111
+ const command = this.commands[config.command];
112
+ const target = command?.redirect ? this.commands[command.redirect] : command;
113
+ if (target) (target.options ??= []).push(config);
114
+ }
115
+ }
116
+ commandCtx(command) {
117
+ return {
118
+ showVersion: () => {
119
+ this.showVersion();
120
+ },
121
+ showHelp: () => {
122
+ this.showHelp();
123
+ },
124
+ getArgument: (commandName = command) => {
125
+ return this.readArgument(commandName);
126
+ }
127
+ };
128
+ }
129
+ /**
130
+ * Sets the CLI metadata.
131
+ * @param metaInfo
132
+ */
133
+ setMetaInfo({ version = "", name = "" } = {}) {
134
+ this.version = version;
135
+ this.name = name;
136
+ }
137
+ /**
138
+ * Displays help information.
139
+ */
140
+ showHelp(command) {
141
+ this.showVersion();
142
+ renderHelp(this.commands, this.commonOptions);
143
+ }
144
+ /**
145
+ * Displays the version number.
146
+ */
147
+ showVersion() {
148
+ console.log(`${this.name} / ${this.version}`);
149
+ }
150
+ /**
151
+ * Starts command dispatch.
152
+ */
153
+ start() {
154
+ if (this.alwaysHandler) this.alwaysHandler();
155
+ if (this.defaultHandler) {
156
+ if (this.parsedArguments["_"].length === 0 && Object.keys(this.parsedArguments).length === 1) this.defaultHandler();
157
+ }
158
+ for (const key of Object.keys(this.parsedArguments)) if (key in this.commands) this.exec(key);
159
+ for (const key of this.parsedArguments["_"]) if (key in this.commands) this.exec(String(key));
160
+ }
161
+ /**
162
+ * Executes the command handler.
163
+ * @param command
164
+ */
165
+ exec(command) {
166
+ const boundCommand = this.commands[command];
167
+ if (!boundCommand) return;
168
+ if (boundCommand.redirect) {
169
+ this.exec(boundCommand.redirect);
170
+ return;
171
+ }
172
+ const options = {};
173
+ for (const option of [...boundCommand.options ?? [], ...this.commonOptions]) {
174
+ const names = Array.isArray(option.name) ? option.name : [option.name];
175
+ const primaryName = names[0];
176
+ if (!primaryName) continue;
177
+ for (const name of names) {
178
+ if (!(name in this.parsedArguments)) continue;
179
+ if (this.validateArgument(this.parsedArguments[name], option.argument)) options[primaryName] = this.parsedArguments[name];
180
+ else {
181
+ console.error(chalk.red(`Argument validation failed for option '${name}'.`));
182
+ if (typeof option.argument?.validate === "string") console.error(chalk.red(`<${option.argument.name}> should be a/an ${option.argument.validate.slice(2)}.`));
183
+ }
184
+ }
185
+ }
186
+ const argumentValue = this.findArgument(command);
187
+ if (this.validateArgument(argumentValue, boundCommand.argument)) boundCommand.handler?.(this.commandCtx(command), createCamelProxifiedObject(options));
188
+ else {
189
+ console.error(chalk.red(`Argument validation failed for command ${command}`));
190
+ if (typeof boundCommand.argument?.validate === "string") console.error(chalk.red(`<${boundCommand.argument.name}> should be a/an ${boundCommand.argument.validate.slice(2)}.`));
191
+ }
192
+ }
193
+ validateArgument(argumentValue, argument) {
194
+ if (!argument || !argument.validate) return true;
195
+ if (typeof argument.validate === "string") {
196
+ if (argument.validate in this.validator) {
197
+ if (argumentValue === void 0) return false;
198
+ return this.validator[argument.validate](String(argumentValue));
199
+ } else {
200
+ console.error(chalk.red(`Unknown validate method for ${argument.name}.`));
201
+ return true;
202
+ }
203
+ } else return argument.validate(argumentValue, (message) => {
204
+ console.log(chalk.red(message));
205
+ });
206
+ }
207
+ getArgument(commandName, followRedirect = true) {
208
+ return this.readArgument(commandName, followRedirect);
209
+ }
210
+ readArgument(commandName, followRedirect = true) {
211
+ const value = this.findArgument(commandName, followRedirect);
212
+ if (value === void 0) console.error(chalk.red(`Command ${commandName} not found.`));
213
+ return value;
214
+ }
215
+ findArgument(commandName, followRedirect = true) {
216
+ const command = this.commands[commandName];
217
+ if (!command) return void 0;
218
+ if (command.redirect && followRedirect) return this.findArgument(command.redirect);
219
+ if (commandName !== "_" && Object.hasOwn(this.parsedArguments, commandName)) return this.parsedArguments[commandName];
220
+ for (const alias of command.alias ?? []) if (Object.hasOwn(this.parsedArguments, alias)) return this.parsedArguments[alias];
221
+ }
222
+ /**
223
+ * Starts command dispatch.
224
+ * Erii is awake!
225
+ */
226
+ okite() {
227
+ return this.start();
228
+ }
229
+ };
230
+ //#endregion
231
+ //#region src/index.ts
232
+ var src_default = Erii;
233
+ //#endregion
234
+ export { Erii, src_default as default };
package/package.json CHANGED
@@ -1,30 +1,58 @@
1
- {
2
- "name": "erii",
3
- "version": "2.0.6",
4
- "description": "",
5
- "main": "./dist/index.js",
6
- "types": "./dist/index.d.ts",
7
- "scripts": {
8
- "build": "tsc"
9
- },
10
- "repository": {
11
- "type": "git",
12
- "url": "git+https://github.com/Last-Order/erii.git"
13
- },
14
- "author": "",
15
- "license": "MIT",
16
- "bugs": {
17
- "url": "https://github.com/Last-Order/erii/issues"
18
- },
19
- "homepage": "https://github.com/Last-Order/erii#readme",
20
- "dependencies": {
21
- "chalk": "^2.4.1",
22
- "clui": "^0.3.6",
23
- "validator": "^13.7.0",
24
- "yargs-parser": "^21.0.0"
25
- },
26
- "devDependencies": {
27
- "@types/node": "^10.17.48",
28
- "typescript": "^4.1.2"
29
- }
30
- }
1
+ {
2
+ "name": "erii",
3
+ "version": "3.0.0-beta.2",
4
+ "description": "",
5
+ "main": "./dist/index.mjs",
6
+ "types": "./dist/index.d.mts",
7
+ "scripts": {
8
+ "format": "prettier --write .",
9
+ "format:check": "prettier --check .",
10
+ "build": "tsdown",
11
+ "typecheck": "tsc --noEmit",
12
+ "test": "npm run build && npm run typecheck && node --test test/*.test.mjs",
13
+ "test:coverage": "npm run build && npm run typecheck && node --test --experimental-test-coverage --test-coverage-include=dist/index.mjs --test-coverage-include=src/utils/convert.ts --test-coverage-lines=100 --test-coverage-functions=100 --test-coverage-branches=95 test/*.test.mjs",
14
+ "prepack": "npm run build"
15
+ },
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/Last-Order/erii.git"
19
+ },
20
+ "author": "",
21
+ "license": "MIT",
22
+ "bugs": {
23
+ "url": "https://github.com/Last-Order/erii/issues"
24
+ },
25
+ "homepage": "https://github.com/Last-Order/erii#readme",
26
+ "dependencies": {
27
+ "@types/validator": "^13.15.10",
28
+ "chalk": "^6.0.0",
29
+ "cli-color": "^2.0.0",
30
+ "clui": "^0.3.6",
31
+ "validator": "^13.15.35",
32
+ "yargs-parser": "^21.0.0"
33
+ },
34
+ "devDependencies": {
35
+ "@types/cli-color": "^2.0.6",
36
+ "@types/clui": "^0.3.5",
37
+ "@types/node": "^24.13.5",
38
+ "@types/yargs-parser": "^21.0.3",
39
+ "prettier": "3.9.7",
40
+ "tsdown": "^0.23.0",
41
+ "typescript": "^7.0.2"
42
+ },
43
+ "type": "module",
44
+ "exports": {
45
+ ".": {
46
+ "types": "./dist/index.d.mts",
47
+ "import": "./dist/index.mjs"
48
+ }
49
+ },
50
+ "files": [
51
+ "dist",
52
+ "readme.md",
53
+ "logo.png"
54
+ ],
55
+ "engines": {
56
+ "node": ">=22.18.0"
57
+ }
58
+ }