dify/cli/src/errors/base.ts
Xiyuan Chen ba59d9a4ac
feat: unified ErrorBody contract for /openapi/v1 and difyctl (#37285)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-06-11 10:26:27 +00:00

160 lines
4.4 KiB
TypeScript

import type { ErrorBody } from '@dify/contracts/api/openapi/types.gen'
import type { ErrorCodeValue, ExitCodeValue } from './codes'
import type { ErrorEnvelope, PrintableError } from './format'
import { ErrorCode, exitFor } from './codes'
export type BaseErrorOptions = {
readonly code: ErrorCodeValue
readonly message: string
readonly hint?: string
readonly cause?: unknown
}
export class BaseError extends Error implements PrintableError {
readonly code: ErrorCodeValue
readonly hint?: string
constructor(opts: BaseErrorOptions) {
super(opts.message, opts.cause === undefined ? undefined : { cause: opts.cause })
this.name = 'BaseError'
this.code = opts.code
this.hint = opts.hint
Object.setPrototypeOf(this, new.target.prototype)
}
exit(): ExitCodeValue {
return exitFor(this.code)
}
override toString(): string {
return this.hint
? `${this.code}: ${this.message} (hint: ${this.hint})`
: `${this.code}: ${this.message}`
}
toEnvelope(): ErrorEnvelope {
const payload: ErrorEnvelope['error'] = {
code: this.code,
message: this.message,
}
if (this.hint !== undefined)
payload.hint = this.hint
return { error: payload }
}
withHint<T extends BaseError>(this: T, hint: string): T {
const Ctor = this.constructor as new (opts: BaseErrorOptions) => T
return new Ctor({ ...this.snapshot(), hint })
}
wrap<T extends BaseError>(this: T, cause: unknown): T {
const Ctor = this.constructor as new (opts: BaseErrorOptions) => T
return new Ctor({ ...this.snapshot(), cause })
}
protected snapshot(): BaseErrorOptions {
return {
code: this.code,
message: this.message,
hint: this.hint,
cause: this.cause,
}
}
}
export function newError(code: ErrorCodeValue, message: string): BaseError {
return new BaseError({ code, message })
}
export function isBaseError(value: unknown): value is BaseError {
return value instanceof BaseError
}
export function isHttpClientError(value: unknown): value is HttpClientError {
return value instanceof HttpClientError
}
export function unknownError(message: string, cause?: unknown): BaseError {
return new BaseError({ code: ErrorCode.Unknown, message, cause })
}
type HttpClientErrorOptions = BaseErrorOptions & {
readonly httpStatus?: number
readonly method?: string
readonly url?: string
readonly rawResponse?: string
readonly serverError?: ErrorBody
}
export class HttpClientError extends BaseError {
readonly httpStatus?: number
readonly method?: string
readonly url?: string
readonly rawResponse?: string
readonly serverError?: ErrorBody
constructor(opts: HttpClientErrorOptions) {
super(opts)
this.httpStatus = opts.httpStatus
this.method = opts.method
this.url = opts.url
this.rawResponse = opts.rawResponse
this.serverError = opts.serverError
}
override toEnvelope(): ErrorEnvelope {
const envelope = super.toEnvelope()
if (this.httpStatus !== undefined)
envelope.error.http_status = this.httpStatus
if (this.method !== undefined)
envelope.error.method = this.method
if (this.url !== undefined)
envelope.error.url = this.url
if (this.rawResponse !== undefined)
envelope.error.raw_response = this.rawResponse
if (this.serverError !== undefined)
envelope.error.server = this.serverError
return envelope
}
protected override snapshot(): HttpClientErrorOptions {
return {
...super.snapshot(),
httpStatus: this.httpStatus,
method: this.method,
url: this.url,
rawResponse: this.rawResponse,
serverError: this.serverError,
}
}
public static from(error: BaseError): HttpClientError {
return new HttpClientError({
code: error.code,
message: error.message,
hint: error.hint,
cause: error.cause,
})
}
withHttpStatus(httpStatus: number): HttpClientError {
return new HttpClientError({ ...this.snapshot(), httpStatus })
}
withRequest(method: string, url: string): HttpClientError {
return new HttpClientError({ ...this.snapshot(), method, url })
}
withRawResponse(rawResponse: string): HttpClientError {
if (!rawResponse) {
return this
}
return new HttpClientError({ ...this.snapshot(), rawResponse })
}
withServerError(serverError: ErrorBody): HttpClientError {
return new HttpClientError({ ...this.snapshot(), serverError })
}
}