-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpredicates.ts
More file actions
80 lines (75 loc) · 2.89 KB
/
predicates.ts
File metadata and controls
80 lines (75 loc) · 2.89 KB
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
/**
* @fileoverview Error type-guard predicates — `isError` (with the
* `isErrorBuiltin` / `isErrorShim` building blocks) and the libuv
* errno-code narrower `isErrnoException`. Both are cross-realm-safe
* (they use `[[ErrorData]]` slot semantics rather than
* `instanceof Error`).
*/
import { ObjectPrototypeToString } from '../primordials/object'
import { StringPrototypeCharCodeAt } from '../primordials/string'
/**
* Reference to the native ES2025 `Error.isError` when the running
* engine ships it, otherwise `undefined`. Exposed separately so tests
* and callers can detect the fast-path without re-probing.
*/
export const isErrorBuiltin: ((value: unknown) => value is Error) | undefined =
(Error as unknown as { isError?: (v: unknown) => v is Error }).isError
/**
* Narrow a caught value to a Node.js `ErrnoException` — an Error with a
* `.code` string set by libuv/syscall failures (e.g. `'ENOENT'`,
* `'EACCES'`, `'EBUSY'`, `'EPERM'`). Cross-realm safe (builds on
* {@link isError}), and checks that `code` is a string so a merely
* branded Error without a real errno code returns `false`.
*
* @example
* try {
* await fsPromises.readFile(path)
* } catch (e) {
* if (isErrnoException(e) && e.code === 'ENOENT') {
* // … retry, or return default …
* } else {
* throw e
* }
* }
*/
export function isErrnoException(
value: unknown,
): value is NodeJS.ErrnoException {
if (!isError(value)) {
return false
}
const code = (value as { code?: unknown }).code
if (typeof code !== 'string' || code.length === 0) {
return false
}
// libuv and Node.js errno codes always start with an uppercase
// letter — libuv's `UV_E*` (ENOENT, EACCES, EBUSY, EPERM, EEXIST,
// etc. — see include/uv/errno.h) and Node's `ERR_*` family
// (https://nodejs.org/api/errors.html#nodejs-error-codes). Reject
// Errors whose `.code` is lowercase (usually a package-specific tag)
// rather than maintaining an exact allow-list that would drift on
// every Node release.
const first = StringPrototypeCharCodeAt(code, 0)
return first >= 65 /* 'A' */ && first <= 90 /* 'Z' */
}
/**
* `Error.isError` fallback shim — the in-language approximation used
* when the native ES2025 method isn't available.
*
* Exported separately so test suites on engines that ship the native
* method can still exercise the shim branch directly. Consumers should
* prefer {@link isError}, which picks the native method when present.
*/
export function isErrorShim(value: unknown): value is Error {
if (value === null || typeof value !== 'object') {
return false
}
return ObjectPrototypeToString(value) === '[object Error]'
}
/**
* Prefer the native ES2025 `Error.isError` when available (exact
* `[[ErrorData]]` slot check, cross-realm-safe); fall back to
* {@link isErrorShim} otherwise.
*/
export const isError: (value: unknown) => value is Error =
isErrorBuiltin ?? isErrorShim