@nexical/cli-core 0.1.2 → 0.1.4

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 CHANGED
@@ -108,7 +108,7 @@ export default class GreetCommand extends BaseCommand {
108
108
 
109
109
  // Implement the run method
110
110
  async run(options: any) {
111
- this.log('Hello from my-cli!');
111
+ this.info('Hello from my-cli!');
112
112
  }
113
113
  }
114
114
  ```
@@ -155,7 +155,7 @@ export default class GreetCommand extends BaseCommand {
155
155
  const finalMessage = shout ? message.toUpperCase() : message;
156
156
 
157
157
  for (let i = 0; i < count; i++) {
158
- this.log(finalMessage);
158
+ this.info(finalMessage);
159
159
  }
160
160
  }
161
161
  }
@@ -186,7 +186,15 @@ The core is built around three main components:
186
186
  * `init()`: Async initialization hook (pre-run).
187
187
  * `run()`: The main execution logic.
188
188
  * `this.projectRoot`: Automatically resolved path to the project root (if running in a project context).
189
- * Logging helpers (`this.success`, `this.warn`, `this.error`).
189
+ * Output helpers:
190
+ * `this.success(msg)`: Logs success message (✔) in green.
191
+ * `this.warn(msg)`: Logs warning message (⚠) in yellow.
192
+ * `this.error(msg, code?)`: Logs error (✖) in red and exits.
193
+ * `this.notice(msg)`: Logs a notice (📢) in blue.
194
+ * `this.input(msg)`: Logs an input prompt display (?) in cyan.
195
+ * `this.info(msg)`: Standard informational message.
196
+ * Input helpers:
197
+ * `this.prompt(msg)`: Prompts the user for input and returns the result.
190
198
 
191
199
  ---
192
200
 
@@ -17,7 +17,7 @@ import pc from "picocolors";
17
17
  // package.json
18
18
  var package_default = {
19
19
  name: "@nexical/cli-core",
20
- version: "0.1.2",
20
+ version: "0.1.4",
21
21
  type: "module",
22
22
  main: "dist/index.js",
23
23
  types: "dist/index.d.ts",
@@ -252,4 +252,4 @@ var CLI = class {
252
252
  export {
253
253
  CLI
254
254
  };
255
- //# sourceMappingURL=chunk-Z4RQIDDY.js.map
255
+ //# sourceMappingURL=chunk-GVJZJZX3.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/CLI.ts","../package.json"],"sourcesContent":["import { cac } from 'cac';\nimport { CommandLoader } from './CommandLoader.js';\nimport path from 'node:path';\nimport fs from 'node:fs';\nimport { fileURLToPath } from 'node:url';\nimport pc from 'picocolors';\nimport pkg from '../package.json';\nimport { logger, setDebugMode } from './utils/logger.js';\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\nexport interface CLIConfig {\n commandName?: string;\n searchDirectories?: string[];\n}\n\nexport class CLI {\n public name: string;\n private cli: ReturnType<typeof cac>;\n private loader: CommandLoader;\n private HelpCommandClass: any;\n private config: CLIConfig;\n\n constructor(config: CLIConfig = {}) {\n this.config = config;\n this.name = this.config.commandName || 'app';\n this.cli = cac(this.name);\n this.loader = new CommandLoader(this);\n }\n\n private loadedCommands: any[] = [];\n\n getCommands() {\n return this.loadedCommands;\n }\n\n getRawCLI() {\n return this.cli;\n }\n\n async start() {\n // In built version, we are in dist/index.js (from cli/index.ts) -> core bundled? or just imported.\n // The core logic is now in src/cli/core/src/CLI.ts or dist/core/src/CLI.js\n\n // Check for debug flag early\n if (process.argv.includes('--debug')) {\n setDebugMode(true);\n logger.debug('Debug mode enabled via --debug flag');\n }\n\n let commandsDirs: string[] = [];\n\n if (this.config.searchDirectories && this.config.searchDirectories.length > 0) {\n commandsDirs = this.config.searchDirectories;\n } else {\n // We assume the standard structure:\n // cli/\n // index.js\n // commands/\n // core/\n // src/\n // CLI.ts\n\n // When running from source (ts-node src/cli/index.ts), specific commands are in src/cli/commands.\n // core is in src/cli/core/src.\n // Relative path from CLI.ts to commands: ../../../commands\n\n const possibleDirs = [\n path.resolve(__dirname, './src/commands'),\n path.resolve(process.cwd(), 'commands') // Fallback relative to cwd?\n ];\n\n for (const dir of possibleDirs) {\n if (fs.existsSync(dir)) {\n commandsDirs.push(dir);\n break; // Keep existing behavior: verify if we want multiple or just first found\n }\n }\n }\n\n // Fallback or error\n if (commandsDirs.length === 0) {\n logger.debug(\"No commands directory found.\");\n }\n\n for (const dir of commandsDirs) {\n // Loader accumulates commands\n await this.loader.load(dir);\n }\n this.loadedCommands = this.loader.getCommands();\n\n // Group commands by root command name\n const commandGroups: Record<string, any[]> = {};\n // Locate HelpCommand for fallback usage\n const helpCmd = this.loadedCommands.find(c => c.command === 'help');\n if (helpCmd) {\n this.HelpCommandClass = helpCmd.class;\n }\n\n for (const cmd of this.loadedCommands) {\n const root = cmd.command.split(' ')[0];\n if (!commandGroups[root]) commandGroups[root] = [];\n commandGroups[root].push(cmd);\n }\n\n for (const [root, cmds] of Object.entries(commandGroups)) {\n // Case 1: Single command, no subcommands (e.g. 'init')\n if (cmds.length === 1 && cmds[0].command === root) {\n const cmd = cmds[0];\n const CommandClass = cmd.class;\n // Original logic for single command\n let commandName = cmd.command;\n const argsDef = CommandClass.args || {};\n if (argsDef.args) {\n argsDef.args.forEach((arg: any) => {\n const isVariadic = arg.name.endsWith('...');\n const cleanName = isVariadic ? arg.name.slice(0, -3) : arg.name;\n // Always use optional brackets [] for CAC to allow --help to pass validation\n // We will enforce 'required' manually in the action handler\n commandName += isVariadic ? ` [...${cleanName}]` : ` [${cleanName}]`;\n });\n }\n const cacCommand = this.cli.command(commandName, CommandClass.description || '');\n\n // Register options\n if (argsDef.options) {\n argsDef.options.forEach((opt: any) => {\n cacCommand.option(opt.name, opt.description, { default: opt.default });\n });\n }\n this.registerGlobalOptions(cacCommand);\n\n cacCommand.action(async (...args: any[]) => {\n const options = args.pop();\n\n if (options.help) {\n await this.runHelp([cmd.command]);\n return;\n }\n\n const positionalArgs = args;\n\n if (argsDef.args) {\n argsDef.args.forEach((arg: any, index: number) => {\n const isVariadic = arg.name.endsWith('...');\n const name = isVariadic ? arg.name.slice(0, -3) : arg.name;\n const val = positionalArgs[index];\n\n if (val !== undefined) {\n options[name] = val;\n } else if (arg.required) {\n console.error(pc.red(`Missing required argument: ${name}`));\n this.runHelp([cmd.command]).then(() => process.exit(1));\n return;\n }\n });\n }\n await this.runCommand(CommandClass, options, [cmd.command]);\n });\n } else {\n // Case 2: Command with subcommands (e.g. 'module add')\n // Register 'module <subcommand>' catch-all\n const commandName = `${root} [subcommand] [...args]`;\n const cacCommand = this.cli.command(commandName, `Manage ${root} commands`);\n\n cacCommand.allowUnknownOptions(); // Pass options to subcommand\n this.registerGlobalOptions(cacCommand);\n\n cacCommand.action(async (subcommand: string, ...args: any[]) => {\n const options = args.pop(); // last is options\n\n if (!subcommand || options.help) {\n // If --help is passed to 'module --help', subcommand might be caught as 'module' if args parsing is weird? \n // ACTUALLY: cac parses 'module add --help' as subcommand=\"add\".\n // 'module --help' might trigger the command itself? No, 'module <subcommand>' expects a subcommand.\n // If I run 'module --help', it might fail validation or parse 'help' as subcommand if unlucky, \n // but likely it just prints help if we didn't override.\n\n await this.runHelp([root, subcommand].filter(Boolean));\n return;\n }\n\n\n // Find matching command\n // Match against \"root subcommand\"\n const fullCommandName = `${root} ${subcommand}`;\n const cmd = cmds.find(c => c.command === fullCommandName);\n\n if (!cmd) {\n console.error(pc.red(`Unknown subcommand '${subcommand}' for '${root}'`));\n process.exit(1);\n }\n\n const CommandClass = cmd.class;\n // Map remaining args? \n // The args array contains positional args AFTER subcommand.\n // But we didn't define them in CAC, so they are just strings.\n // We need to map them manually to the Target Command's args definition.\n // argsDef.args usually starts after the command.\n // For 'module add <url>', <url> is the first arg after 'add'.\n // So 'args' here corresponds to <url>.\n\n const argsDef = CommandClass.args || {};\n // If using [...args], the variadic args are collected into the first argument array\n // args here is what remains after popping options.\n const positionalArgs = (args.length > 0 && Array.isArray(args[0])) ? args[0] : args;\n\n const childOptions = { ...options }; // Copy options\n\n const cmdParts = [root, subcommand];\n\n if (argsDef.args) {\n argsDef.args.forEach((arg: any, index: number) => {\n const isVariadic = arg.name.endsWith('...');\n const name = isVariadic ? arg.name.slice(0, -3) : arg.name;\n const val = positionalArgs[index];\n\n if (val !== undefined) {\n if (isVariadic) {\n childOptions[name] = positionalArgs.slice(index);\n } else {\n childOptions[name] = val;\n }\n } else if (arg.required) {\n console.error(pc.red(`Missing required argument: ${name}`));\n this.runHelp(cmdParts).then(() => process.exit(1));\n return;\n }\n });\n }\n\n await this.runCommand(CommandClass, childOptions, cmdParts);\n });\n }\n }\n // Disable default help\n // this.cli.help(); \n\n // Manually register global help to ensure it's allowed\n this.cli.option('--help, -h', 'Display help');\n\n this.cli.version(pkg.version);\n\n // Global help interception for root command\n // If we run `app --help`, we need to catch it.\n // CAC doesn't expose a clean global action without a command content.\n // However, if we parse and no command matches, it usually errors or shows help.\n // If we have default logic, we can put it here?\n // Let's rely on standard parsing but maybe inspect raw args first?\n\n if (process.argv.includes('--help') || process.argv.includes('-h')) {\n // Inspect non-option args to see if there's a command?\n const args = process.argv.slice(2).filter(a => !a.startsWith('-'));\n if (args.length === 0) {\n await this.runHelp([]);\n process.exit(0);\n }\n }\n\n try {\n this.cli.parse();\n } catch (e: any) {\n console.error(pc.red(e.message));\n\n // Try to provide helpful context\n console.log('');\n // Simple heuristic: find first non-flag arg as command\n const args = process.argv.slice(2);\n const potentialCommand = args.find(a => !a.startsWith('-'));\n // If it matches a loaded command root, show help for it\n // Otherwise show global help\n\n // We need to match 'module add' etc?\n // Just pass the potential command parts to runHelp. \n // runHelp handles filtering itself? No, runHelp takes commandParts to pass to HelpCommand.\n // HelpCommand expects `command` array.\n\n const helpArgs = potentialCommand ? [potentialCommand] : [];\n await this.runHelp(helpArgs);\n\n process.exit(1);\n }\n }\n\n private registerGlobalOptions(cacCommand: any) {\n cacCommand.option('--root-dir <path>', 'Override project root');\n cacCommand.option('--debug', 'Enable debug mode');\n cacCommand.option('--help, -h', 'Display help message');\n }\n\n private async runHelp(commandParts: string[]) {\n if (this.HelpCommandClass) {\n const helpInstance = new this.HelpCommandClass(this);\n await helpInstance.run({ command: commandParts });\n } else {\n // Fallback if HelpCommand not loaded (shouldn't happen)\n this.cli.outputHelp();\n }\n }\n\n private async runCommand(CommandClass: any, options: any, commandParts: string[] = []) {\n try {\n const instance = new CommandClass(this, options);\n await instance.init();\n await instance.run(options);\n } catch (e: any) {\n console.error(pc.red(e.message));\n if (options.debug) console.error(e.stack);\n\n console.log(''); // spacer\n await this.runHelp(commandParts);\n\n process.exit(1);\n }\n }\n}\n","{\n \"name\": \"@nexical/cli-core\",\n \"version\": \"0.1.2\",\n \"type\": \"module\",\n \"main\": \"dist/index.js\",\n \"types\": \"dist/index.d.ts\",\n \"exports\": {\n \".\": \"./dist/index.js\"\n },\n \"scripts\": {\n \"build\": \"tsup\",\n \"dev\": \"tsup --watch\",\n \"cli\": \"node dist/cli.js\",\n \"test\": \"npm run test:unit && npm run test:integration && npm run test:e2e\",\n \"test:unit\": \"vitest run --config vitest.config.ts --coverage\",\n \"test:integration\": \"vitest run --config vitest.integration.config.ts\",\n \"test:e2e\": \"npm run build && vitest run --config vitest.e2e.config.ts\",\n \"test:watch\": \"vitest\"\n },\n \"dependencies\": {\n \"cac\": \"^6.7.14\",\n \"consola\": \"^3.4.2\",\n \"lilconfig\": \"^3.1.3\",\n \"picocolors\": \"^1.1.1\",\n \"yaml\": \"^2.8.2\",\n \"zod\": \"^3.22.4\"\n },\n \"devDependencies\": {\n \"@types/fs-extra\": \"^11.0.4\",\n \"@types/node\": \"^20.10.0\",\n \"@vitest/coverage-v8\": \"^4.0.15\",\n \"execa\": \"^9.6.1\",\n \"fs-extra\": \"^11.3.2\",\n \"tsup\": \"^8.0.1\",\n \"typescript\": \"^5.3.3\",\n \"vitest\": \"^4.0.15\"\n }\n}"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/CLI.ts","../package.json"],"sourcesContent":["import { cac } from 'cac';\nimport { CommandLoader } from './CommandLoader.js';\nimport path from 'node:path';\nimport fs from 'node:fs';\nimport { fileURLToPath } from 'node:url';\nimport pc from 'picocolors';\nimport pkg from '../package.json';\nimport { logger, setDebugMode } from './utils/logger.js';\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\nexport interface CLIConfig {\n commandName?: string;\n searchDirectories?: string[];\n}\n\nexport class CLI {\n public name: string;\n private cli: ReturnType<typeof cac>;\n private loader: CommandLoader;\n private HelpCommandClass: any;\n private config: CLIConfig;\n\n constructor(config: CLIConfig = {}) {\n this.config = config;\n this.name = this.config.commandName || 'app';\n this.cli = cac(this.name);\n this.loader = new CommandLoader(this);\n }\n\n private loadedCommands: any[] = [];\n\n getCommands() {\n return this.loadedCommands;\n }\n\n getRawCLI() {\n return this.cli;\n }\n\n async start() {\n // In built version, we are in dist/index.js (from cli/index.ts) -> core bundled? or just imported.\n // The core logic is now in src/cli/core/src/CLI.ts or dist/core/src/CLI.js\n\n // Check for debug flag early\n if (process.argv.includes('--debug')) {\n setDebugMode(true);\n logger.debug('Debug mode enabled via --debug flag');\n }\n\n let commandsDirs: string[] = [];\n\n if (this.config.searchDirectories && this.config.searchDirectories.length > 0) {\n commandsDirs = this.config.searchDirectories;\n } else {\n // We assume the standard structure:\n // cli/\n // index.js\n // commands/\n // core/\n // src/\n // CLI.ts\n\n // When running from source (ts-node src/cli/index.ts), specific commands are in src/cli/commands.\n // core is in src/cli/core/src.\n // Relative path from CLI.ts to commands: ../../../commands\n\n const possibleDirs = [\n path.resolve(__dirname, './src/commands'),\n path.resolve(process.cwd(), 'commands') // Fallback relative to cwd?\n ];\n\n for (const dir of possibleDirs) {\n if (fs.existsSync(dir)) {\n commandsDirs.push(dir);\n break; // Keep existing behavior: verify if we want multiple or just first found\n }\n }\n }\n\n // Fallback or error\n if (commandsDirs.length === 0) {\n logger.debug(\"No commands directory found.\");\n }\n\n for (const dir of commandsDirs) {\n // Loader accumulates commands\n await this.loader.load(dir);\n }\n this.loadedCommands = this.loader.getCommands();\n\n // Group commands by root command name\n const commandGroups: Record<string, any[]> = {};\n // Locate HelpCommand for fallback usage\n const helpCmd = this.loadedCommands.find(c => c.command === 'help');\n if (helpCmd) {\n this.HelpCommandClass = helpCmd.class;\n }\n\n for (const cmd of this.loadedCommands) {\n const root = cmd.command.split(' ')[0];\n if (!commandGroups[root]) commandGroups[root] = [];\n commandGroups[root].push(cmd);\n }\n\n for (const [root, cmds] of Object.entries(commandGroups)) {\n // Case 1: Single command, no subcommands (e.g. 'init')\n if (cmds.length === 1 && cmds[0].command === root) {\n const cmd = cmds[0];\n const CommandClass = cmd.class;\n // Original logic for single command\n let commandName = cmd.command;\n const argsDef = CommandClass.args || {};\n if (argsDef.args) {\n argsDef.args.forEach((arg: any) => {\n const isVariadic = arg.name.endsWith('...');\n const cleanName = isVariadic ? arg.name.slice(0, -3) : arg.name;\n // Always use optional brackets [] for CAC to allow --help to pass validation\n // We will enforce 'required' manually in the action handler\n commandName += isVariadic ? ` [...${cleanName}]` : ` [${cleanName}]`;\n });\n }\n const cacCommand = this.cli.command(commandName, CommandClass.description || '');\n\n // Register options\n if (argsDef.options) {\n argsDef.options.forEach((opt: any) => {\n cacCommand.option(opt.name, opt.description, { default: opt.default });\n });\n }\n this.registerGlobalOptions(cacCommand);\n\n cacCommand.action(async (...args: any[]) => {\n const options = args.pop();\n\n if (options.help) {\n await this.runHelp([cmd.command]);\n return;\n }\n\n const positionalArgs = args;\n\n if (argsDef.args) {\n argsDef.args.forEach((arg: any, index: number) => {\n const isVariadic = arg.name.endsWith('...');\n const name = isVariadic ? arg.name.slice(0, -3) : arg.name;\n const val = positionalArgs[index];\n\n if (val !== undefined) {\n options[name] = val;\n } else if (arg.required) {\n console.error(pc.red(`Missing required argument: ${name}`));\n this.runHelp([cmd.command]).then(() => process.exit(1));\n return;\n }\n });\n }\n await this.runCommand(CommandClass, options, [cmd.command]);\n });\n } else {\n // Case 2: Command with subcommands (e.g. 'module add')\n // Register 'module <subcommand>' catch-all\n const commandName = `${root} [subcommand] [...args]`;\n const cacCommand = this.cli.command(commandName, `Manage ${root} commands`);\n\n cacCommand.allowUnknownOptions(); // Pass options to subcommand\n this.registerGlobalOptions(cacCommand);\n\n cacCommand.action(async (subcommand: string, ...args: any[]) => {\n const options = args.pop(); // last is options\n\n if (!subcommand || options.help) {\n // If --help is passed to 'module --help', subcommand might be caught as 'module' if args parsing is weird? \n // ACTUALLY: cac parses 'module add --help' as subcommand=\"add\".\n // 'module --help' might trigger the command itself? No, 'module <subcommand>' expects a subcommand.\n // If I run 'module --help', it might fail validation or parse 'help' as subcommand if unlucky, \n // but likely it just prints help if we didn't override.\n\n await this.runHelp([root, subcommand].filter(Boolean));\n return;\n }\n\n\n // Find matching command\n // Match against \"root subcommand\"\n const fullCommandName = `${root} ${subcommand}`;\n const cmd = cmds.find(c => c.command === fullCommandName);\n\n if (!cmd) {\n console.error(pc.red(`Unknown subcommand '${subcommand}' for '${root}'`));\n process.exit(1);\n }\n\n const CommandClass = cmd.class;\n // Map remaining args? \n // The args array contains positional args AFTER subcommand.\n // But we didn't define them in CAC, so they are just strings.\n // We need to map them manually to the Target Command's args definition.\n // argsDef.args usually starts after the command.\n // For 'module add <url>', <url> is the first arg after 'add'.\n // So 'args' here corresponds to <url>.\n\n const argsDef = CommandClass.args || {};\n // If using [...args], the variadic args are collected into the first argument array\n // args here is what remains after popping options.\n const positionalArgs = (args.length > 0 && Array.isArray(args[0])) ? args[0] : args;\n\n const childOptions = { ...options }; // Copy options\n\n const cmdParts = [root, subcommand];\n\n if (argsDef.args) {\n argsDef.args.forEach((arg: any, index: number) => {\n const isVariadic = arg.name.endsWith('...');\n const name = isVariadic ? arg.name.slice(0, -3) : arg.name;\n const val = positionalArgs[index];\n\n if (val !== undefined) {\n if (isVariadic) {\n childOptions[name] = positionalArgs.slice(index);\n } else {\n childOptions[name] = val;\n }\n } else if (arg.required) {\n console.error(pc.red(`Missing required argument: ${name}`));\n this.runHelp(cmdParts).then(() => process.exit(1));\n return;\n }\n });\n }\n\n await this.runCommand(CommandClass, childOptions, cmdParts);\n });\n }\n }\n // Disable default help\n // this.cli.help(); \n\n // Manually register global help to ensure it's allowed\n this.cli.option('--help, -h', 'Display help');\n\n this.cli.version(pkg.version);\n\n // Global help interception for root command\n // If we run `app --help`, we need to catch it.\n // CAC doesn't expose a clean global action without a command content.\n // However, if we parse and no command matches, it usually errors or shows help.\n // If we have default logic, we can put it here?\n // Let's rely on standard parsing but maybe inspect raw args first?\n\n if (process.argv.includes('--help') || process.argv.includes('-h')) {\n // Inspect non-option args to see if there's a command?\n const args = process.argv.slice(2).filter(a => !a.startsWith('-'));\n if (args.length === 0) {\n await this.runHelp([]);\n process.exit(0);\n }\n }\n\n try {\n this.cli.parse();\n } catch (e: any) {\n console.error(pc.red(e.message));\n\n // Try to provide helpful context\n console.log('');\n // Simple heuristic: find first non-flag arg as command\n const args = process.argv.slice(2);\n const potentialCommand = args.find(a => !a.startsWith('-'));\n // If it matches a loaded command root, show help for it\n // Otherwise show global help\n\n // We need to match 'module add' etc?\n // Just pass the potential command parts to runHelp. \n // runHelp handles filtering itself? No, runHelp takes commandParts to pass to HelpCommand.\n // HelpCommand expects `command` array.\n\n const helpArgs = potentialCommand ? [potentialCommand] : [];\n await this.runHelp(helpArgs);\n\n process.exit(1);\n }\n }\n\n private registerGlobalOptions(cacCommand: any) {\n cacCommand.option('--root-dir <path>', 'Override project root');\n cacCommand.option('--debug', 'Enable debug mode');\n cacCommand.option('--help, -h', 'Display help message');\n }\n\n private async runHelp(commandParts: string[]) {\n if (this.HelpCommandClass) {\n const helpInstance = new this.HelpCommandClass(this);\n await helpInstance.run({ command: commandParts });\n } else {\n // Fallback if HelpCommand not loaded (shouldn't happen)\n this.cli.outputHelp();\n }\n }\n\n private async runCommand(CommandClass: any, options: any, commandParts: string[] = []) {\n try {\n const instance = new CommandClass(this, options);\n await instance.init();\n await instance.run(options);\n } catch (e: any) {\n console.error(pc.red(e.message));\n if (options.debug) console.error(e.stack);\n\n console.log(''); // spacer\n await this.runHelp(commandParts);\n\n process.exit(1);\n }\n }\n}\n","{\n \"name\": \"@nexical/cli-core\",\n \"version\": \"0.1.4\",\n \"type\": \"module\",\n \"main\": \"dist/index.js\",\n \"types\": \"dist/index.d.ts\",\n \"exports\": {\n \".\": \"./dist/index.js\"\n },\n \"scripts\": {\n \"build\": \"tsup\",\n \"dev\": \"tsup --watch\",\n \"cli\": \"node dist/cli.js\",\n \"test\": \"npm run test:unit && npm run test:integration && npm run test:e2e\",\n \"test:unit\": \"vitest run --config vitest.config.ts --coverage\",\n \"test:integration\": \"vitest run --config vitest.integration.config.ts\",\n \"test:e2e\": \"npm run build && vitest run --config vitest.e2e.config.ts\",\n \"test:watch\": \"vitest\"\n },\n \"dependencies\": {\n \"cac\": \"^6.7.14\",\n \"consola\": \"^3.4.2\",\n \"lilconfig\": \"^3.1.3\",\n \"picocolors\": \"^1.1.1\",\n \"yaml\": \"^2.8.2\",\n \"zod\": \"^3.22.4\"\n },\n \"devDependencies\": {\n \"@types/fs-extra\": \"^11.0.4\",\n \"@types/node\": \"^20.10.0\",\n \"@vitest/coverage-v8\": \"^4.0.15\",\n \"execa\": \"^9.6.1\",\n \"fs-extra\": \"^11.3.2\",\n \"tsup\": \"^8.0.1\",\n \"typescript\": \"^5.3.3\",\n \"vitest\": \"^4.0.15\"\n }\n}"],"mappings":"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","names":[]}
@@ -9,6 +9,8 @@ import {
9
9
 
10
10
  // src/BaseCommand.ts
11
11
  import process from "process";
12
+ import pc from "picocolors";
13
+ import { consola } from "consola";
12
14
  var BaseCommand = class {
13
15
  static usage = "";
14
16
  static description = "";
@@ -40,28 +42,38 @@ var BaseCommand = class {
40
42
  }
41
43
  // Helpers
42
44
  success(msg) {
43
- logger.success(msg);
45
+ console.log(pc.green("\u2714 " + msg));
46
+ }
47
+ notice(msg) {
48
+ console.log(pc.blue("\u{1F4E2} " + msg));
49
+ }
50
+ input(msg) {
51
+ console.log(pc.cyan("? " + msg));
44
52
  }
45
53
  info(msg) {
46
- logger.info(msg);
54
+ console.log(msg);
47
55
  }
48
56
  warn(msg) {
49
- logger.warn(msg);
57
+ console.log(pc.yellow("\u26A0 " + msg));
50
58
  }
51
59
  error(msg, code = 1) {
52
60
  if (msg instanceof Error) {
53
- logger.error(msg.message);
61
+ console.log(pc.red("\u2716 " + msg.message));
54
62
  if (this.globalOptions.debug) {
55
63
  logger.error(msg.stack);
56
64
  }
57
65
  } else {
58
- logger.error(msg);
66
+ console.log(pc.red("\u2716 " + msg));
59
67
  }
60
68
  process.exit(code);
61
69
  }
70
+ async prompt(msg) {
71
+ const response = await consola.prompt(msg, { type: "text" });
72
+ return response;
73
+ }
62
74
  };
63
75
 
64
76
  export {
65
77
  BaseCommand
66
78
  };
67
- //# sourceMappingURL=chunk-CKAS75E6.js.map
79
+ //# sourceMappingURL=chunk-ZKP2UD2M.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/BaseCommand.ts"],"sourcesContent":["import { logger } from './utils/logger.js';\nimport { CommandDefinition, CommandInterface } from './CommandInterface.js';\nimport { findProjectRoot, loadConfig } from './utils/config.js';\nimport process from 'node:process';\nimport pc from 'picocolors';\nimport { consola } from 'consola';\n\nexport abstract class BaseCommand implements CommandInterface {\n static usage = '';\n static description = '';\n static args: CommandDefinition = {};\n\n // Configurable flags\n static requiresProject = false;\n\n protected projectRoot: string | null = null;\n protected config: any = {};\n protected globalOptions: any = {};\n protected cli: any = null;\n\n\n constructor(cli: any, globalOptions: any = {}) {\n this.globalOptions = globalOptions;\n this.cli = cli;\n }\n\n async init() {\n // 1. Root detection strategy\n if (this.globalOptions.rootDir) {\n this.projectRoot = this.globalOptions.rootDir;\n } else {\n this.projectRoot = await findProjectRoot(this.cli.name, process.cwd());\n }\n\n const requiresProject = (this.constructor as any).requiresProject;\n\n if (requiresProject && !this.projectRoot) {\n this.error('This command requires to be run within an app project (app.yml not found).', 1);\n return; // TS doesn't know error exits\n }\n\n if (this.projectRoot) {\n this.config = await loadConfig(this.cli.name, this.projectRoot);\n // logger.debug(`Loaded config from ${this.projectRoot}`);\n }\n }\n\n abstract run(options: any): Promise<void>;\n\n // Helpers\n success(msg: string) {\n console.log(pc.green('✔ ' + msg));\n }\n\n notice(msg: string) {\n console.log(pc.blue('📢 ' + msg));\n }\n\n input(msg: string) {\n console.log(pc.cyan('? ' + msg));\n }\n\n info(msg: string) {\n console.log(msg);\n }\n\n warn(msg: string) {\n console.log(pc.yellow('⚠ ' + msg));\n }\n\n error(msg: string | Error, code = 1) {\n if (msg instanceof Error) {\n console.log(pc.red('✖ ' + msg.message));\n if (this.globalOptions.debug) {\n logger.error(msg.stack);\n }\n } else {\n console.log(pc.red('✖ ' + msg));\n }\n process.exit(code);\n }\n\n async prompt(msg: string): Promise<string> {\n const response = await consola.prompt(msg, { type: 'text' });\n return response as string;\n }\n}\n"],"mappings":";;;;;;;;;;AAGA,OAAO,aAAa;AACpB,OAAO,QAAQ;AACf,SAAS,eAAe;AAEjB,IAAe,cAAf,MAAuD;AAAA,EAC1D,OAAO,QAAQ;AAAA,EACf,OAAO,cAAc;AAAA,EACrB,OAAO,OAA0B,CAAC;AAAA;AAAA,EAGlC,OAAO,kBAAkB;AAAA,EAEf,cAA6B;AAAA,EAC7B,SAAc,CAAC;AAAA,EACf,gBAAqB,CAAC;AAAA,EACtB,MAAW;AAAA,EAGrB,YAAY,KAAU,gBAAqB,CAAC,GAAG;AAC3C,SAAK,gBAAgB;AACrB,SAAK,MAAM;AAAA,EACf;AAAA,EAEA,MAAM,OAAO;AAET,QAAI,KAAK,cAAc,SAAS;AAC5B,WAAK,cAAc,KAAK,cAAc;AAAA,IAC1C,OAAO;AACH,WAAK,cAAc,MAAM,gBAAgB,KAAK,IAAI,MAAM,QAAQ,IAAI,CAAC;AAAA,IACzE;AAEA,UAAM,kBAAmB,KAAK,YAAoB;AAElD,QAAI,mBAAmB,CAAC,KAAK,aAAa;AACtC,WAAK,MAAM,8EAA8E,CAAC;AAC1F;AAAA,IACJ;AAEA,QAAI,KAAK,aAAa;AAClB,WAAK,SAAS,MAAM,WAAW,KAAK,IAAI,MAAM,KAAK,WAAW;AAAA,IAElE;AAAA,EACJ;AAAA;AAAA,EAKA,QAAQ,KAAa;AACjB,YAAQ,IAAI,GAAG,MAAM,YAAO,GAAG,CAAC;AAAA,EACpC;AAAA,EAEA,OAAO,KAAa;AAChB,YAAQ,IAAI,GAAG,KAAK,eAAQ,GAAG,CAAC;AAAA,EACpC;AAAA,EAEA,MAAM,KAAa;AACf,YAAQ,IAAI,GAAG,KAAK,OAAO,GAAG,CAAC;AAAA,EACnC;AAAA,EAEA,KAAK,KAAa;AACd,YAAQ,IAAI,GAAG;AAAA,EACnB;AAAA,EAEA,KAAK,KAAa;AACd,YAAQ,IAAI,GAAG,OAAO,YAAO,GAAG,CAAC;AAAA,EACrC;AAAA,EAEA,MAAM,KAAqB,OAAO,GAAG;AACjC,QAAI,eAAe,OAAO;AACtB,cAAQ,IAAI,GAAG,IAAI,YAAO,IAAI,OAAO,CAAC;AACtC,UAAI,KAAK,cAAc,OAAO;AAC1B,eAAO,MAAM,IAAI,KAAK;AAAA,MAC1B;AAAA,IACJ,OAAO;AACH,cAAQ,IAAI,GAAG,IAAI,YAAO,GAAG,CAAC;AAAA,IAClC;AACA,YAAQ,KAAK,IAAI;AAAA,EACrB;AAAA,EAEA,MAAM,OAAO,KAA8B;AACvC,UAAM,WAAW,MAAM,QAAQ,OAAO,KAAK,EAAE,MAAM,OAAO,CAAC;AAC3D,WAAO;AAAA,EACX;AACJ;","names":[]}
package/dist/cli.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import { createRequire } from "module"; const require = createRequire(import.meta.url);
3
3
  import {
4
4
  CLI
5
- } from "./chunk-Z4RQIDDY.js";
5
+ } from "./chunk-GVJZJZX3.js";
6
6
  import "./chunk-LLXEFSKN.js";
7
7
  import {
8
8
  logger
package/dist/index.js CHANGED
@@ -4,12 +4,12 @@ import {
4
4
  } from "./chunk-SNTKHUTX.js";
5
5
  import {
6
6
  CLI
7
- } from "./chunk-Z4RQIDDY.js";
7
+ } from "./chunk-GVJZJZX3.js";
8
8
  import "./chunk-C4IL52NB.js";
9
9
  import "./chunk-LLXEFSKN.js";
10
10
  import {
11
11
  BaseCommand
12
- } from "./chunk-CKAS75E6.js";
12
+ } from "./chunk-ZKP2UD2M.js";
13
13
  import "./chunk-IIOHHU2E.js";
14
14
  import {
15
15
  logger,
@@ -13,9 +13,12 @@ declare abstract class BaseCommand implements CommandInterface {
13
13
  init(): Promise<void>;
14
14
  abstract run(options: any): Promise<void>;
15
15
  success(msg: string): void;
16
+ notice(msg: string): void;
17
+ input(msg: string): void;
16
18
  info(msg: string): void;
17
19
  warn(msg: string): void;
18
20
  error(msg: string | Error, code?: number): void;
21
+ prompt(msg: string): Promise<string>;
19
22
  }
20
23
 
21
24
  export { BaseCommand };
@@ -1,7 +1,7 @@
1
1
  import { createRequire } from "module"; const require = createRequire(import.meta.url);
2
2
  import {
3
3
  BaseCommand
4
- } from "../chunk-CKAS75E6.js";
4
+ } from "../chunk-ZKP2UD2M.js";
5
5
  import "../chunk-IIOHHU2E.js";
6
6
  import "../chunk-2HJDWSDA.js";
7
7
  export {
package/dist/src/CLI.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { createRequire } from "module"; const require = createRequire(import.meta.url);
2
2
  import {
3
3
  CLI
4
- } from "../chunk-Z4RQIDDY.js";
4
+ } from "../chunk-GVJZJZX3.js";
5
5
  import "../chunk-LLXEFSKN.js";
6
6
  import "../chunk-2HJDWSDA.js";
7
7
  export {
@@ -1,7 +1,7 @@
1
1
  import { createRequire } from "module"; const require = createRequire(import.meta.url);
2
2
  import {
3
3
  BaseCommand
4
- } from "../../chunk-CKAS75E6.js";
4
+ } from "../../chunk-ZKP2UD2M.js";
5
5
  import "../../chunk-IIOHHU2E.js";
6
6
  import "../../chunk-2HJDWSDA.js";
7
7
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nexical/cli-core",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -2,6 +2,8 @@ import { logger } from './utils/logger.js';
2
2
  import { CommandDefinition, CommandInterface } from './CommandInterface.js';
3
3
  import { findProjectRoot, loadConfig } from './utils/config.js';
4
4
  import process from 'node:process';
5
+ import pc from 'picocolors';
6
+ import { consola } from 'consola';
5
7
 
6
8
  export abstract class BaseCommand implements CommandInterface {
7
9
  static usage = '';
@@ -47,27 +49,39 @@ export abstract class BaseCommand implements CommandInterface {
47
49
 
48
50
  // Helpers
49
51
  success(msg: string) {
50
- logger.success(msg);
52
+ console.log(pc.green('✔ ' + msg));
51
53
  }
52
54
 
55
+ notice(msg: string) {
56
+ console.log(pc.blue('📢 ' + msg));
57
+ }
58
+
59
+ input(msg: string) {
60
+ console.log(pc.cyan('? ' + msg));
61
+ }
53
62
 
54
63
  info(msg: string) {
55
- logger.info(msg);
64
+ console.log(msg);
56
65
  }
57
66
 
58
67
  warn(msg: string) {
59
- logger.warn(msg);
68
+ console.log(pc.yellow('⚠ ' + msg));
60
69
  }
61
70
 
62
71
  error(msg: string | Error, code = 1) {
63
72
  if (msg instanceof Error) {
64
- logger.error(msg.message);
73
+ console.log(pc.red('✖ ' + msg.message));
65
74
  if (this.globalOptions.debug) {
66
75
  logger.error(msg.stack);
67
76
  }
68
77
  } else {
69
- logger.error(msg);
78
+ console.log(pc.red('✖ ' + msg));
70
79
  }
71
80
  process.exit(code);
72
81
  }
82
+
83
+ async prompt(msg: string): Promise<string> {
84
+ const response = await consola.prompt(msg, { type: 'text' });
85
+ return response as string;
86
+ }
73
87
  }
@@ -340,6 +340,6 @@ describe('HelpCommand', () => {
340
340
  expect(e.message).toBe('EXIT');
341
341
  }
342
342
 
343
- expect(logger.error).toHaveBeenCalledWith(expect.stringContaining('Unknown command: unknown'));
343
+ expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringContaining('Unknown command: unknown'));
344
344
  });
345
345
  });
@@ -4,9 +4,22 @@ import { BaseCommand } from '../../../src/BaseCommand.js';
4
4
  import * as ConfigUtils from '../../../src/utils/config.js';
5
5
  import { logger } from '../../../src/utils/logger.js';
6
6
  import process from 'node:process';
7
+ import pc from 'picocolors';
8
+ import { consola } from 'consola';
7
9
 
8
10
  vi.mock('../../../src/utils/config.js');
9
11
  vi.mock('../../../src/utils/logger.js');
12
+ vi.mock('consola', async (importOriginal) => {
13
+ const actual: any = await importOriginal();
14
+ return {
15
+ ...actual,
16
+ consola: {
17
+ ...actual.consola,
18
+ create: actual.consola?.create || actual.create || (() => ({})),
19
+ prompt: vi.fn(),
20
+ }
21
+ };
22
+ });
10
23
 
11
24
  class TestCommand extends BaseCommand {
12
25
  async run() { }
@@ -19,14 +32,17 @@ class ProjectRequiredCommand extends BaseCommand {
19
32
 
20
33
  describe('BaseCommand', () => {
21
34
  let processExitSpy: any;
35
+ let consoleLogSpy: any;
22
36
 
23
37
  beforeEach(() => {
24
38
  vi.clearAllMocks();
25
39
  processExitSpy = vi.spyOn(process, 'exit').mockImplementation((() => { }) as any);
40
+ consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
26
41
  });
27
42
 
28
43
  afterEach(() => {
29
44
  processExitSpy.mockRestore();
45
+ consoleLogSpy.mockRestore();
30
46
  });
31
47
 
32
48
  it('should initialize with default options', () => {
@@ -69,7 +85,7 @@ describe('BaseCommand', () => {
69
85
  const cmd = new ProjectRequiredCommand(cli, {});
70
86
  await cmd.init();
71
87
 
72
- expect(logger.error).toHaveBeenCalledWith(expect.stringContaining('requires to be run within an app project'));
88
+ expect(consoleLogSpy).toHaveBeenCalledWith(pc.red('✖ This command requires to be run within an app project (app.yml not found).'));
73
89
  expect(process.exit).toHaveBeenCalledWith(1);
74
90
  });
75
91
 
@@ -77,28 +93,42 @@ describe('BaseCommand', () => {
77
93
  const cli = new CLI({ commandName: 'app' });
78
94
  const cmd = new TestCommand(cli);
79
95
  cmd.success('test');
80
- expect(logger.success).toHaveBeenCalledWith('test');
96
+ expect(consoleLogSpy).toHaveBeenCalledWith(pc.green('test'));
97
+ });
98
+
99
+ it('should log notice', () => {
100
+ const cli = new CLI({ commandName: 'app' });
101
+ const cmd = new TestCommand(cli);
102
+ cmd.notice('test');
103
+ expect(consoleLogSpy).toHaveBeenCalledWith(pc.blue('📢 test'));
104
+ });
105
+
106
+ it('should log input', () => {
107
+ const cli = new CLI({ commandName: 'app' });
108
+ const cmd = new TestCommand(cli);
109
+ cmd.input('test');
110
+ expect(consoleLogSpy).toHaveBeenCalledWith(pc.cyan('? test'));
81
111
  });
82
112
 
83
113
  it('should log info', () => {
84
114
  const cli = new CLI({ commandName: 'app' });
85
115
  const cmd = new TestCommand(cli);
86
116
  cmd.info('test');
87
- expect(logger.info).toHaveBeenCalledWith('test');
117
+ expect(consoleLogSpy).toHaveBeenCalledWith('test');
88
118
  });
89
119
 
90
120
  it('should log warn', () => {
91
121
  const cli = new CLI({ commandName: 'app' });
92
122
  const cmd = new TestCommand(cli);
93
123
  cmd.warn('test');
94
- expect(logger.warn).toHaveBeenCalledWith('test');
124
+ expect(consoleLogSpy).toHaveBeenCalledWith(pc.yellow('test'));
95
125
  });
96
126
 
97
127
  it('should log error string and exit', () => {
98
128
  const cli = new CLI({ commandName: 'app' });
99
129
  const cmd = new TestCommand(cli);
100
130
  cmd.error('fail', 1);
101
- expect(logger.error).toHaveBeenCalledWith('fail');
131
+ expect(consoleLogSpy).toHaveBeenCalledWith(pc.red('fail'));
102
132
  expect(process.exit).toHaveBeenCalledWith(1);
103
133
  });
104
134
 
@@ -107,7 +137,7 @@ describe('BaseCommand', () => {
107
137
  const cmd = new TestCommand(cli);
108
138
  const err = new Error('fail');
109
139
  cmd.error(err, 2);
110
- expect(logger.error).toHaveBeenCalledWith('fail');
140
+ expect(consoleLogSpy).toHaveBeenCalledWith(pc.red('fail'));
111
141
  expect(process.exit).toHaveBeenCalledWith(2);
112
142
  });
113
143
 
@@ -116,9 +146,10 @@ describe('BaseCommand', () => {
116
146
  const cmd = new TestCommand(cli, { debug: true });
117
147
  const err = new Error('fail');
118
148
  cmd.error(err);
119
- expect(logger.error).toHaveBeenCalledWith('fail');
120
- expect(logger.error).toHaveBeenCalledTimes(2); // One for message, one for stack
149
+ expect(consoleLogSpy).toHaveBeenCalledWith(pc.red('fail'));
150
+ expect(logger.error).toHaveBeenCalledWith(err.stack); // Stack is still logged via logger in debug
121
151
  });
152
+
122
153
  it('should skip config loading if project root is not found', async () => {
123
154
  (ConfigUtils.findProjectRoot as any).mockResolvedValue(null);
124
155
  const cli = new CLI({ commandName: 'app' });
@@ -128,4 +159,15 @@ describe('BaseCommand', () => {
128
159
  expect(ConfigUtils.loadConfig).not.toHaveBeenCalled();
129
160
  expect((cmd as any).config).toEqual({});
130
161
  });
162
+
163
+ it('should prompt user and return input', async () => {
164
+ const cli = new CLI({ commandName: 'app' });
165
+ const cmd = new TestCommand(cli);
166
+ (consola.prompt as any).mockResolvedValue('user input');
167
+
168
+ const res = await cmd.prompt('Enter value');
169
+
170
+ expect(consola.prompt).toHaveBeenCalledWith('Enter value', { type: 'text' });
171
+ expect(res).toBe('user input');
172
+ });
131
173
  });
@@ -197,4 +197,18 @@ describe('CommandLoader', () => {
197
197
  const commands = await loader.load(rootDir);
198
198
  expect(commands).toHaveLength(0);
199
199
  });
200
+ it('should return loaded commands via getCommands', async () => {
201
+ const rootDir = '/commands';
202
+ (fs.existsSync as any).mockReturnValue(true);
203
+ (fs.readdirSync as any).mockReturnValue(['test.ts']);
204
+ (fs.statSync as any).mockReturnValue({ isDirectory: () => false });
205
+
206
+ class TestCmd extends BaseCommand { async run() { } }
207
+ mockImporter.mockResolvedValue({ default: TestCmd });
208
+
209
+ await loader.load(rootDir);
210
+ const cmds = loader.getCommands();
211
+ expect(cmds).toHaveLength(1);
212
+ expect(cmds[0].command).toBe('test');
213
+ });
200
214
  });
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/BaseCommand.ts"],"sourcesContent":["import { logger } from './utils/logger.js';\nimport { CommandDefinition, CommandInterface } from './CommandInterface.js';\nimport { findProjectRoot, loadConfig } from './utils/config.js';\nimport process from 'node:process';\n\nexport abstract class BaseCommand implements CommandInterface {\n static usage = '';\n static description = '';\n static args: CommandDefinition = {};\n\n // Configurable flags\n static requiresProject = false;\n\n protected projectRoot: string | null = null;\n protected config: any = {};\n protected globalOptions: any = {};\n protected cli: any = null;\n\n\n constructor(cli: any, globalOptions: any = {}) {\n this.globalOptions = globalOptions;\n this.cli = cli;\n }\n\n async init() {\n // 1. Root detection strategy\n if (this.globalOptions.rootDir) {\n this.projectRoot = this.globalOptions.rootDir;\n } else {\n this.projectRoot = await findProjectRoot(this.cli.name, process.cwd());\n }\n\n const requiresProject = (this.constructor as any).requiresProject;\n\n if (requiresProject && !this.projectRoot) {\n this.error('This command requires to be run within an app project (app.yml not found).', 1);\n return; // TS doesn't know error exits\n }\n\n if (this.projectRoot) {\n this.config = await loadConfig(this.cli.name, this.projectRoot);\n // logger.debug(`Loaded config from ${this.projectRoot}`);\n }\n }\n\n abstract run(options: any): Promise<void>;\n\n // Helpers\n success(msg: string) {\n logger.success(msg);\n }\n\n\n info(msg: string) {\n logger.info(msg);\n }\n\n warn(msg: string) {\n logger.warn(msg);\n }\n\n error(msg: string | Error, code = 1) {\n if (msg instanceof Error) {\n logger.error(msg.message);\n if (this.globalOptions.debug) {\n logger.error(msg.stack);\n }\n } else {\n logger.error(msg);\n }\n process.exit(code);\n }\n}\n"],"mappings":";;;;;;;;;;AAGA,OAAO,aAAa;AAEb,IAAe,cAAf,MAAuD;AAAA,EAC1D,OAAO,QAAQ;AAAA,EACf,OAAO,cAAc;AAAA,EACrB,OAAO,OAA0B,CAAC;AAAA;AAAA,EAGlC,OAAO,kBAAkB;AAAA,EAEf,cAA6B;AAAA,EAC7B,SAAc,CAAC;AAAA,EACf,gBAAqB,CAAC;AAAA,EACtB,MAAW;AAAA,EAGrB,YAAY,KAAU,gBAAqB,CAAC,GAAG;AAC3C,SAAK,gBAAgB;AACrB,SAAK,MAAM;AAAA,EACf;AAAA,EAEA,MAAM,OAAO;AAET,QAAI,KAAK,cAAc,SAAS;AAC5B,WAAK,cAAc,KAAK,cAAc;AAAA,IAC1C,OAAO;AACH,WAAK,cAAc,MAAM,gBAAgB,KAAK,IAAI,MAAM,QAAQ,IAAI,CAAC;AAAA,IACzE;AAEA,UAAM,kBAAmB,KAAK,YAAoB;AAElD,QAAI,mBAAmB,CAAC,KAAK,aAAa;AACtC,WAAK,MAAM,8EAA8E,CAAC;AAC1F;AAAA,IACJ;AAEA,QAAI,KAAK,aAAa;AAClB,WAAK,SAAS,MAAM,WAAW,KAAK,IAAI,MAAM,KAAK,WAAW;AAAA,IAElE;AAAA,EACJ;AAAA;AAAA,EAKA,QAAQ,KAAa;AACjB,WAAO,QAAQ,GAAG;AAAA,EACtB;AAAA,EAGA,KAAK,KAAa;AACd,WAAO,KAAK,GAAG;AAAA,EACnB;AAAA,EAEA,KAAK,KAAa;AACd,WAAO,KAAK,GAAG;AAAA,EACnB;AAAA,EAEA,MAAM,KAAqB,OAAO,GAAG;AACjC,QAAI,eAAe,OAAO;AACtB,aAAO,MAAM,IAAI,OAAO;AACxB,UAAI,KAAK,cAAc,OAAO;AAC1B,eAAO,MAAM,IAAI,KAAK;AAAA,MAC1B;AAAA,IACJ,OAAO;AACH,aAAO,MAAM,GAAG;AAAA,IACpB;AACA,YAAQ,KAAK,IAAI;AAAA,EACrB;AACJ;","names":[]}