All files / packages/error/src index.ts

95.23% Statements 20/21
100% Branches 13/13
88.88% Functions 8/9
95% Lines 19/20

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120          62x 62x 62x 62x     93x               61x                 39x                         3237x           3237x 3237x     93x                                                 3502x                 2444x     1058x             28x     1030x             7x   7x       10x                
import { type MessageJsonType, toJson } from '@bufbuild/protobuf'
import { type IssueMessage, IssueMessageSchema, StatusIds_StatusCode } from '@ydbjs/api/operation'
 
export class YDBIssue {
	constructor(
		public code: number,
		public message: string,
		public severity: number,
		public issues: YDBIssue[] = []
	) {}
 
	static severity: Record<IssueMessage['severity'], string> = {
		0: 'FATAL',
		1: 'ERROR',
		2: 'WARNING',
		3: 'INFO',
	} as const
 
	static fromIssueMessage(issue: IssueMessage): YDBIssue {
		return new YDBIssue(
			issue.issueCode,
			issue.message,
			issue.severity,
			issue.issues.map(YDBIssue.fromIssueMessage)
		)
	}
 
	toString(): string {
		return `${YDBIssue.severity[this.severity]}(${this.code}): ${this.message}`
	}
 
	[Symbol.toStringTag]() {
		return this.toString()
	}
}
 
export class YDBError extends Error {
	readonly code: StatusIds_StatusCode
	readonly issues: MessageJsonType<typeof IssueMessageSchema>[]
 
	constructor(code: StatusIds_StatusCode, issues: IssueMessage[]) {
		super(
			`${YDBError.codes[code]}` +
				(issues.length
					? `, Issues: ${issues.map(YDBIssue.fromIssueMessage).join('; ')}`
					: '')
		)
		this.code = code
		this.issues = issues.map((issue) => toJson(IssueMessageSchema, issue as IssueMessage))
	}
 
	static codes: Record<StatusIds_StatusCode, string> = {
		[StatusIds_StatusCode.STATUS_CODE_UNSPECIFIED]: 'UNSPECIFIED',
		[StatusIds_StatusCode.SUCCESS]: 'SUCCESS',
		[StatusIds_StatusCode.BAD_REQUEST]: 'BAD_REQUEST',
		[StatusIds_StatusCode.UNAUTHORIZED]: 'UNAUTHORIZED',
		[StatusIds_StatusCode.INTERNAL_ERROR]: 'INTERNAL_ERROR',
		[StatusIds_StatusCode.ABORTED]: 'ABORTED',
		[StatusIds_StatusCode.UNAVAILABLE]: 'UNAVAILABLE',
		[StatusIds_StatusCode.OVERLOADED]: 'OVERLOADED',
		[StatusIds_StatusCode.SCHEME_ERROR]: 'SCHEME_ERROR',
		[StatusIds_StatusCode.GENERIC_ERROR]: 'GENERIC_ERROR',
		[StatusIds_StatusCode.TIMEOUT]: 'TIMEOUT',
		[StatusIds_StatusCode.BAD_SESSION]: 'BAD_SESSION',
		[StatusIds_StatusCode.PRECONDITION_FAILED]: 'PRECONDITION_FAILED',
		[StatusIds_StatusCode.ALREADY_EXISTS]: 'ALREADY_EXISTS',
		[StatusIds_StatusCode.NOT_FOUND]: 'NOT_FOUND',
		[StatusIds_StatusCode.SESSION_EXPIRED]: 'SESSION_EXPIRED',
		[StatusIds_StatusCode.CANCELLED]: 'CANCELLED',
		[StatusIds_StatusCode.UNDETERMINED]: 'UNDETERMINED',
		[StatusIds_StatusCode.UNSUPPORTED]: 'UNSUPPORTED',
		[StatusIds_StatusCode.SESSION_BUSY]: 'SESSION_BUSY',
		[StatusIds_StatusCode.EXTERNAL_ERROR]: 'EXTERNAL_ERROR',
	} as const
 
	get retryable(): boolean | 'conditionally' {
		if (
			[
				StatusIds_StatusCode.ABORTED,
				StatusIds_StatusCode.OVERLOADED,
				StatusIds_StatusCode.UNAVAILABLE,
				StatusIds_StatusCode.BAD_SESSION,
				StatusIds_StatusCode.SESSION_BUSY,
			].includes(this.code)
		) {
			return true
		}
 
		if (
			[
				StatusIds_StatusCode.SESSION_EXPIRED,
				StatusIds_StatusCode.UNDETERMINED,
				StatusIds_StatusCode.TIMEOUT,
			].includes(this.code)
		) {
			return 'conditionally'
		}
 
		return false
	}
}
 
export class CommitError extends Error {
	constructor(
		message: string,
		public override cause?: unknown
	) {
		super(message)
	}
 
	retryable(idempotent: boolean = false): boolean {
		return (
			(this.cause instanceof YDBError && this.cause.retryable === true) ||
			(this.cause instanceof YDBError &&
				this.cause.retryable === 'conditionally' &&
				idempotent)
		)
	}
}