| 1 | /** |
| 2 | * Utility for safely manipulating `Error.stackTraceLimit` in environments |
| 3 | * where intrinsics may be frozen (e.g., SES / hardened JavaScript or |
| 4 | * deterministic sandboxes such as Temporal). When the property is non-writable, |
| 5 | * mutating it throws, so all manipulation here degrades to a best-effort, |
| 6 | * silent no-op. |
| 7 | * |
| 8 | * Mirrors the guard Node uses internally: |
| 9 | * https://github.com/nodejs/node/blob/e77694631f1642c302f664703197b5aabc65b482/lib/internal/errors.js#L246 |
| 10 | * |
| 11 | * The error is constructed inline at each call site (rather than inside a |
| 12 | * closure here) so the captured stack trace keeps pointing at the real caller |
| 13 | * instead of this module. |
| 14 | * |
| 15 | * @internal |
| 16 | */ |
| 17 | import type { ErrorWithStackTraceLimit } from "./tracer.ts" |
| 18 | |
| 19 | /** |
| 20 | * Check if `Error.stackTraceLimit` is writable. |
| 21 | * Returns `false` if the property is frozen, non-writable, or `Error` is non-extensible. |
| 22 | * |
| 23 | * @internal |
| 24 | */ |
| 25 | export const isStackTraceLimitWritable = (): boolean => { |
| 26 | const desc = Object.getOwnPropertyDescriptor(Error, "stackTraceLimit") |
| 27 | if (desc === undefined) { |
| 28 | return Object.isExtensible(Error) |
| 29 | } |
| 30 | |
| 31 | return Object.hasOwn(desc, "writable") |
| 32 | ? desc.writable === true |
| 33 | : desc.set !== undefined |
| 34 | } |
| 35 | |
| 36 | // Cache the check result since it won't change during runtime |
| 37 | const canWriteStackTraceLimit = isStackTraceLimitWritable() |
| 38 | |
| 39 | /** |
| 40 | * Get the current `Error.stackTraceLimit` value. |
| 41 | * Returns `undefined` if the property doesn't exist. |
| 42 | * |
| 43 | * @internal |
| 44 | */ |
| 45 | export const getStackTraceLimit = (): number | undefined => (Error as ErrorWithStackTraceLimit).stackTraceLimit |
| 46 | |
| 47 | /** |
| 48 | * Safely set `Error.stackTraceLimit` if possible, otherwise no-op. |
| 49 | * |
| 50 | * Accepts `undefined` so a value read via {@link getStackTraceLimit} can be |
| 51 | * restored faithfully. |
| 52 | * |
| 53 | * @internal |
| 54 | */ |
| 55 | export const setStackTraceLimit = (value: number | undefined): void => { |
| 56 | if (canWriteStackTraceLimit) { |
| 57 | ;(Error as ErrorWithStackTraceLimit).stackTraceLimit = value |
| 58 | } |
| 59 | } |