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          60x 60x 60x 60x     79x               59x                 37x                         1925x           1925x 1925x     79x                                                 2176x                 1142x     1034x             24x     1010x             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)
		)
	}
}