FUNC - Tiny typed CLI framework

Error Handling

func separates system errors, runtime errors, and runtime-print errors. Local command catches run before global handlers.

Error Families

Family When Behavior
F_SYSTEM Invalid command definitions, duplicate registrations, missing handlers, or invalid injection setup. Thrown immediately. System errors are not delivered to user error handlers.
F_RUNTIME A selected command handler, catch method, or global error handler throws. Delivered to local catch first, then global error handlers. Without a handler, it is thrown.
F_RUNTIME_PRINT Input errors such as parse failures, unknown options, multiple selected handler flags, or validation failures. Delivered to handlers and printed to stderr unless preventDefaultPrint() is called.

System Errors

System errors describe invalid framework setup: duplicate command names, duplicate option tokens, missing handlers, unsupported array types, invalid decorator targets, or missing providers. They should be fixed during development and are not passed to @Catch or @CatchAll.

Single-command Catch

@Catch() registers a method on a command class. It catches non-system errors from that command before any global handler runs.

import { Catch, Command, Exception, FuncException, Handler } from 'func'

@Command({ name: 'publish' })
export class PublishCommand {
  @Catch()
  onError(@Exception() exception: FuncException) {
    console.error(`publish failed: ${exception.message}`)
    exception.preventDefaultPrint()
  }

  @Handler()
  run() {
    throw new Error('missing package')
  }
}

Global Error Handler

@CatchAll() registers a global error handler class. @CommandError() is an alias for @CatchAll(). Register global handlers in the same command list as the rest of the application.

import { CatchAll, Exception, FuncException } from 'func'

@CatchAll()
export class ErrorHandler {
  constructor(@Exception() exception: FuncException) {
    if (exception.level === 'runtime-print') {
      console.error(`Invalid input: ${exception.message}`)
      exception.preventDefaultPrint()
      return
    }

    console.error(exception.message)
  }
}
import { FuncModule } from 'func'
import { PublishCommand } from './commands/publish'
import { ErrorHandler } from './error-handler'

@FuncModule({
  commands: [PublishCommand, ErrorHandler],
})
export class AppModule {}

Runtime-print Errors

Runtime-print errors have a default stderr print. Call preventDefaultPrint() when your handler already printed the message.

import { F_RUNTIME_PRINT, createRuntimePrintError, errorTypes } from 'func'

throw createRuntimePrintError(
  F_RUNTIME_PRINT.VALIDATION,
  errorTypes.INPUT,
  'Token is required.',
  { option: 'token' },
)