mirror of
https://github.com/langgenius/dify.git
synced 2026-10-04 13:47:55 +08:00
204 lines
5.7 KiB
TypeScript
204 lines
5.7 KiB
TypeScript
import type { ErrorCodeValue, ExitCodeValue } from './codes'
|
|
import { BINARY } from '@/version/info'
|
|
import { ErrorCode, exitFor } from './codes'
|
|
|
|
export type ServerErrorDetail = {
|
|
readonly type: string
|
|
readonly loc?: (string | number)[]
|
|
readonly msg: string
|
|
}
|
|
|
|
export type ServerErrorBody = {
|
|
readonly code?: string
|
|
readonly message: string
|
|
readonly status?: number
|
|
readonly hint?: string
|
|
readonly details?: ServerErrorDetail[]
|
|
}
|
|
|
|
export type ErrorEnvelope = {
|
|
error: {
|
|
code: string
|
|
message: string
|
|
hint?: string
|
|
details?: ServerErrorDetail[]
|
|
schema?: unknown
|
|
http_status?: number
|
|
method?: string
|
|
url?: string
|
|
raw_response?: string
|
|
server?: ServerErrorBody
|
|
}
|
|
}
|
|
|
|
export type BaseErrorOptions = {
|
|
readonly code: ErrorCodeValue
|
|
readonly message: string
|
|
readonly hint?: string
|
|
readonly cause?: unknown
|
|
readonly details?: ServerErrorDetail[]
|
|
readonly schema?: unknown
|
|
}
|
|
|
|
export class BaseError extends Error {
|
|
readonly code: ErrorCodeValue
|
|
readonly hint?: string
|
|
readonly details?: ServerErrorDetail[]
|
|
readonly schema?: unknown
|
|
|
|
constructor(opts: BaseErrorOptions) {
|
|
super(opts.message, opts.cause === undefined ? undefined : { cause: opts.cause })
|
|
this.name = 'BaseError'
|
|
this.code = opts.code
|
|
this.hint = opts.hint
|
|
this.details = opts.details
|
|
this.schema = opts.schema
|
|
|
|
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
|
|
if (this.details !== undefined) payload.details = this.details
|
|
if (this.schema !== undefined) payload.schema = this.schema
|
|
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,
|
|
details: this.details,
|
|
schema: this.schema,
|
|
}
|
|
}
|
|
}
|
|
|
|
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 })
|
|
}
|
|
|
|
export const LOGIN_HINT = `run ${BINARY} login`
|
|
|
|
export function notLoggedIn(): BaseError {
|
|
return new BaseError({ code: ErrorCode.NotLoggedIn, message: 'not logged in', hint: LOGIN_HINT })
|
|
}
|
|
|
|
type HttpClientErrorOptions = BaseErrorOptions & {
|
|
readonly httpStatus?: number
|
|
readonly method?: string
|
|
readonly url?: string
|
|
readonly rawResponse?: string
|
|
readonly serverError?: ServerErrorBody
|
|
}
|
|
|
|
export class HttpClientError extends BaseError {
|
|
readonly httpStatus?: number
|
|
readonly method?: string
|
|
readonly url?: string
|
|
readonly rawResponse?: string
|
|
readonly serverError?: ServerErrorBody
|
|
|
|
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
|
|
// One details[] surface for both client- and server-authored rejections: the
|
|
// server's own details fill in only when this error didn't already have its own.
|
|
if (envelope.error.details === undefined && this.serverError?.details !== undefined)
|
|
envelope.error.details = this.serverError.details
|
|
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,
|
|
details: error.details,
|
|
schema: error.schema,
|
|
})
|
|
}
|
|
|
|
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: ServerErrorBody): HttpClientError {
|
|
return new HttpClientError({ ...this.snapshot(), serverError })
|
|
}
|
|
}
|