diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3cc7fc24..145d2545 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,16 +6,20 @@ on: jobs: validate: - runs-on: ubuntu-latest + runs-on: [self-hosted, linux, x64] steps: - name: Checkout code uses: actions/checkout@v4 + - name: Clean stale artifacts + run: rm -rf node_modules ui/node_modules dist + - name: Setup Bun uses: oven-sh/setup-bun@v2 with: bun-version: '1.3.9' + no-cache: true - name: Setup Node.js uses: actions/setup-node@v4 diff --git a/src/commands/api-command.ts b/src/commands/api-command.ts deleted file mode 100644 index 6aa43a58..00000000 --- a/src/commands/api-command.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './api-command/index'; diff --git a/src/commands/api-command/handler.ts b/src/commands/api-command/handler.ts new file mode 100644 index 00000000..2ad906e7 --- /dev/null +++ b/src/commands/api-command/handler.ts @@ -0,0 +1,51 @@ +import { dispatchNamedCommand, type NamedCommandRoute } from '../named-command-router'; + +type ApiCommandHandler = (args: string[]) => Promise; +type ApiCommandHelpHandler = () => Promise; +type ApiCommandUnknownHandler = (command: string) => Promise; + +export interface ApiCommandDependencies { + help: ApiCommandHelpHandler; + unknown: ApiCommandUnknownHandler; + create: ApiCommandHandler; + list: ApiCommandHandler; + discover: ApiCommandHandler; + copy: ApiCommandHandler; + export: ApiCommandHandler; + import: ApiCommandHandler; + remove: ApiCommandHandler; +} + +function createApiCommandRoutes( + dependencies: ApiCommandDependencies +): readonly NamedCommandRoute[] { + return [ + { name: 'create', handle: dependencies.create }, + { name: 'list', handle: dependencies.list }, + { name: 'discover', handle: dependencies.discover }, + { name: 'copy', handle: dependencies.copy }, + { name: 'export', handle: dependencies.export }, + { name: 'import', handle: dependencies.import }, + { name: 'remove', aliases: ['delete', 'rm'], handle: dependencies.remove }, + ]; +} + +/** + * Factory for building an api-command handler with injectable dependencies. + * Extracted from index.ts so tests can import without loading all subcommand modules. + */ +export function createApiCommandHandler( + dependencies: ApiCommandDependencies +): (args: string[]) => Promise { + const routes = createApiCommandRoutes(dependencies); + + return async (args: string[]) => { + await dispatchNamedCommand({ + args, + routes, + onHelp: dependencies.help, + onUnknown: dependencies.unknown, + allowEmptyHelp: true, + }); + }; +} diff --git a/src/commands/api-command/index.ts b/src/commands/api-command/index.ts index 2d9fe526..c014b91a 100644 --- a/src/commands/api-command/index.ts +++ b/src/commands/api-command/index.ts @@ -1,31 +1,16 @@ -import { dispatchNamedCommand, type NamedCommandRoute } from '../named-command-router'; import { handleApiCopyCommand } from './copy-command'; import { handleApiCreateCommand } from './create-command'; import { handleApiDiscoverCommand } from './discover-command'; import { handleApiExportCommand } from './export-command'; +import { createApiCommandHandler, type ApiCommandDependencies } from './handler'; import { showApiCommandHelp, showUnknownApiCommand } from './help'; import { handleApiImportCommand } from './import-command'; import { handleApiListCommand } from './list-command'; import { handleApiRemoveCommand } from './remove-command'; +export { createApiCommandHandler, type ApiCommandDependencies } from './handler'; export { parseApiCommandArgs } from './shared'; -type ApiCommandHandler = (args: string[]) => Promise; -type ApiCommandHelpHandler = () => Promise; -type ApiCommandUnknownHandler = (command: string) => Promise; - -export interface ApiCommandDependencies { - help: ApiCommandHelpHandler; - unknown: ApiCommandUnknownHandler; - create: ApiCommandHandler; - list: ApiCommandHandler; - discover: ApiCommandHandler; - copy: ApiCommandHandler; - export: ApiCommandHandler; - import: ApiCommandHandler; - remove: ApiCommandHandler; -} - const DEFAULT_API_COMMAND_DEPENDENCIES: ApiCommandDependencies = { help: showApiCommandHelp, unknown: showUnknownApiCommand, @@ -38,40 +23,6 @@ const DEFAULT_API_COMMAND_DEPENDENCIES: ApiCommandDependencies = { remove: handleApiRemoveCommand, }; -function createApiCommandRoutes( - dependencies: ApiCommandDependencies -): readonly NamedCommandRoute[] { - return [ - { name: 'create', handle: dependencies.create }, - { name: 'list', handle: dependencies.list }, - { name: 'discover', handle: dependencies.discover }, - { name: 'copy', handle: dependencies.copy }, - { name: 'export', handle: dependencies.export }, - { name: 'import', handle: dependencies.import }, - { name: 'remove', aliases: ['delete', 'rm'], handle: dependencies.remove }, - ]; -} - -export function createApiCommandHandler( - overrides: Partial = {} -): (args: string[]) => Promise { - const dependencies: ApiCommandDependencies = { - ...DEFAULT_API_COMMAND_DEPENDENCIES, - ...overrides, - }; - const routes = createApiCommandRoutes(dependencies); - - return async (args: string[]) => { - await dispatchNamedCommand({ - args, - routes, - onHelp: dependencies.help, - onUnknown: dependencies.unknown, - allowEmptyHelp: true, - }); - }; -} - export async function handleApiCommand(args: string[]): Promise { - await createApiCommandHandler()(args); + await createApiCommandHandler(DEFAULT_API_COMMAND_DEPENDENCIES)(args); } diff --git a/src/commands/index.ts b/src/commands/index.ts index 39e1651b..d6d6f2c2 100644 --- a/src/commands/index.ts +++ b/src/commands/index.ts @@ -2,7 +2,7 @@ * Commands module barrel export */ -export { handleApiCommand } from './api-command'; +export { handleApiCommand } from './api-command/index'; export { handleCleanupCommand } from './cleanup-command'; export { handleCliproxyCommand } from './cliproxy-command'; export { handleConfigCommand } from './config-command'; diff --git a/src/commands/root-command-router.ts b/src/commands/root-command-router.ts index 07d72885..202878d5 100644 --- a/src/commands/root-command-router.ts +++ b/src/commands/root-command-router.ts @@ -118,7 +118,7 @@ const ROOT_COMMAND_ROUTES: readonly NamedCommandRoute[] = [ { name: 'api', handle: async (args) => { - const { handleApiCommand } = await import('./api-command'); + const { handleApiCommand } = await import('./api-command/index'); await handleApiCommand(args); }, }, diff --git a/tests/unit/commands/api-command-router.test.ts b/tests/unit/commands/api-command-router.test.ts index 450a7326..54176bea 100644 --- a/tests/unit/commands/api-command-router.test.ts +++ b/tests/unit/commands/api-command-router.test.ts @@ -1,6 +1,6 @@ import { beforeEach, describe, expect, it } from 'bun:test'; -import { createApiCommandHandler } from '../../../src/commands/api-command'; +import { createApiCommandHandler } from '../../../src/commands/api-command/handler'; let calls: string[] = [];