Skip to content
File

Blob: src/node/internal/internal_assert.ts

typescript1108 lines
1// Copyright (c) 2017-2022 Cloudflare, Inc.
2// Licensed under the Apache 2.0 license found in the LICENSE file or at:
3// https://opensource.org/licenses/Apache-2.0
4//
5// Adapted from Deno and Node.js:
6// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
7//
8// Adapted from Node.js. Copyright Joyent, Inc. and other Node contributors.
9//
10// Permission is hereby granted, free of charge, to any person obtaining a
11// copy of this software and associated documentation files (the
12// "Software"), to deal in the Software without restriction, including
13// without limitation the rights to use, copy, modify, merge, publish,
14// distribute, sublicense, and/or sell copies of the Software, and to permit
15// persons to whom the Software is furnished to do so, subject to the
16// following conditions:
17//
18// The above copyright notice and this permission notice shall be included
19// in all copies or substantial portions of the Software.
20//
21// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
24// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
25// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
26// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
27// USE OR OTHER DEALINGS IN THE SOFTWARE.
28/* eslint-disable */
29 
30import {
31 AssertionError,
32 type AssertionErrorConstructorOptions,
33} from 'node-internal:internal_assertionerror';
34 
35import { diffstr, diff, buildMessage } from 'node-internal:internal_diffs';
36 
37import { isDeepStrictEqual } from 'node-internal:internal_comparisons';
38 
39import {
40 ERR_AMBIGUOUS_ARGUMENT,
41 ERR_INVALID_ARG_TYPE,
42 ERR_INVALID_ARG_VALUE,
43 ERR_INVALID_RETURN_VALUE,
44 ERR_MISSING_ARGS,
45} from 'node-internal:internal_errors';
46 
47import { inspect } from 'node-internal:internal_inspect';
48 
49interface ExtendedAssertionErrorConstructorOptions extends AssertionErrorConstructorOptions {
50 generatedMessage?: boolean;
51}
52 
53function createAssertionError(
54 options: ExtendedAssertionErrorConstructorOptions
55): AssertionError {
56 const error = new AssertionError(options);
57 if (options.generatedMessage) {
58 (error as any).generatedMessage = true;
59 }
60 return error;
61}
62 
63// Note: Intentionally not exported, use assert.ok() instead.
64function assert(actual: unknown, message?: string | Error): asserts actual {
65 if (arguments.length === 0) {
66 throw new AssertionError({
67 message: 'No value argument passed to `assert.ok()`',
68 });
69 }
70 if (!actual) {
71 throw new AssertionError({
72 message,
73 actual,
74 expected: true,
75 operator: '==',
76 } as AssertionErrorConstructorOptions);
77 }
78}
79 
80type Assert = (actual: unknown, message?: string | Error) => asserts actual;
81export const ok: Assert = assert;
82 
83export function throws(
84 fn: () => void,
85 error?: RegExp | Function | Error,
86 message?: string
87) {
88 // Check arg types
89 if (typeof fn !== 'function') {
90 throw new ERR_INVALID_ARG_TYPE('fn', 'function', fn);
91 }
92 if (
93 typeof error === 'object' &&
94 error !== null &&
95 Object.getPrototypeOf(error) === Object.prototype &&
96 Object.keys(error).length === 0
97 ) {
98 // error is an empty object
99 throw new ERR_INVALID_ARG_VALUE(
100 'error',
101 error,
102 'may not be an empty object'
103 );
104 }
105 if (typeof message === 'string') {
106 if (
107 !(error instanceof RegExp) &&
108 typeof error !== 'function' &&
109 !(error instanceof Error) &&
110 typeof error !== 'object'
111 ) {
112 throw new ERR_INVALID_ARG_TYPE(
113 'error',
114 ['Function', 'Error', 'RegExp', 'Object'],
115 error
116 );
117 }
118 } else {
119 if (
120 typeof error !== 'undefined' &&
121 typeof error !== 'string' &&
122 !(error instanceof RegExp) &&
123 typeof error !== 'function' &&
124 !(error instanceof Error) &&
125 typeof error !== 'object'
126 ) {
127 throw new ERR_INVALID_ARG_TYPE(
128 'error',
129 ['Function', 'Error', 'RegExp', 'Object'],
130 error
131 );
132 }
133 }
134 
135 // Checks test function
136 try {
137 fn();
138 } catch (e) {
139 if (
140 validateThrownError(e, error, message, {
141 operator: throws,
142 })
143 ) {
144 return;
145 }
146 }
147 if (message) {
148 let msg = `Missing expected exception: ${message}`;
149 if (typeof error === 'function' && error?.name) {
150 msg = `Missing expected exception (${error.name}): ${message}`;
151 }
152 throw new AssertionError({
153 message: msg,
154 operator: 'throws',
155 actual: undefined,
156 expected: error,
157 });
158 } else if (typeof error === 'string') {
159 // Use case of throws(fn, message)
160 throw new AssertionError({
161 message: `Missing expected exception: ${error}`,
162 operator: 'throws',
163 actual: undefined,
164 expected: undefined,
165 });
166 } else if (typeof error === 'function' && error?.prototype !== undefined) {
167 throw new AssertionError({
168 message: `Missing expected exception (${error.name}).`,
169 operator: 'throws',
170 actual: undefined,
171 expected: error,
172 });
173 } else {
174 throw new AssertionError({
175 message: 'Missing expected exception.',
176 operator: 'throws',
177 actual: undefined,
178 expected: error,
179 });
180 }
181}
182 
183export function doesNotThrow(fn: () => void, message?: string): void;
184export function doesNotThrow(
185 fn: () => void,
186 error?: Function,
187 message?: string | Error
188): void;
189export function doesNotThrow(
190 fn: () => void,
191 error?: RegExp,
192 message?: string
193): void;
194export function doesNotThrow(
195 fn: () => void,
196 expected?: Function | RegExp | string,
197 message?: string | Error
198) {
199 // Check arg type
200 if (typeof fn !== 'function') {
201 throw new ERR_INVALID_ARG_TYPE('fn', 'function', fn);
202 } else if (
203 !(expected instanceof RegExp) &&
204 typeof expected !== 'function' &&
205 typeof expected !== 'string' &&
206 typeof expected !== 'undefined'
207 ) {
208 throw new ERR_INVALID_ARG_TYPE('expected', ['Function', 'RegExp'], fn);
209 }
210 
211 // Checks test function
212 try {
213 fn();
214 } catch (e) {
215 gotUnwantedException(e, expected, message, doesNotThrow);
216 }
217}
218 
219export function equal(
220 actual: unknown,
221 expected: unknown,
222 message?: string | Error
223) {
224 return strictEqual(actual, expected, message);
225}
226 
227export function notEqual(
228 actual: unknown,
229 expected: unknown,
230 message?: string | Error
231) {
232 return notStrictEqual(actual, expected, message);
233}
234 
235export function strictEqual(
236 actual: unknown,
237 expected: unknown,
238 message?: string | Error
239) {
240 if (arguments.length < 2) {
241 throw new ERR_MISSING_ARGS('actual', 'expected');
242 }
243 
244 if (Object.is(actual, expected)) {
245 return;
246 }
247 
248 if (message) {
249 message = `${message}`;
250 } else {
251 const actualString = inspect(actual);
252 const expectedString = inspect(expected);
253 
254 if (actualString === expectedString) {
255 const withOffset = actualString
256 .split('\n')
257 .map((l) => ` ${l}`)
258 .join('\n');
259 message = `Values have the same structure but are not reference-equal:\n\n${withOffset}\n`;
260 } else {
261 try {
262 const stringDiff =
263 typeof actual === 'string' && typeof expected === 'string';
264 const diffResult = stringDiff
265 ? diffstr(actual as string, expected as string)
266 : diff(actualString.split('\n'), expectedString.split('\n'));
267 const diffMsg = buildMessage(diffResult, { stringDiff }).join('\n');
268 message = `Values are not strictly equal:\n${diffMsg}`;
269 } catch {
270 message = '\n$[Cannot display] + \n\n';
271 }
272 }
273 }
274 
275 throw new AssertionError({
276 message,
277 actual,
278 expected,
279 operator: 'strictEqual',
280 });
281}
282 
283export function notStrictEqual(
284 actual: unknown,
285 expected: unknown,
286 message?: string | Error
287) {
288 if (arguments.length < 2) {
289 throw new ERR_MISSING_ARGS('actual', 'expected');
290 }
291 
292 if (!Object.is(actual, expected)) {
293 return;
294 }
295 
296 if (message) {
297 message = `${message}`;
298 } else {
299 message = 'Expected actual to be strictly unequal to expected';
300 }
301 
302 throw new AssertionError({
303 message,
304 actual,
305 expected,
306 operator: 'notStrictEqual',
307 });
308}
309 
310export function deepEqual(
311 actual: unknown,
312 expected: unknown,
313 message?: string | Error
314) {
315 return deepStrictEqual(actual, expected, message);
316}
317 
318export function notDeepEqual(
319 actual: unknown,
320 expected: unknown,
321 message?: string | Error
322) {
323 return notDeepStrictEqual(actual, expected, message);
324}
325 
326export function deepStrictEqual(
327 actual: unknown,
328 expected: unknown,
329 message?: string | Error
330) {
331 if (arguments.length < 2) {
332 throw new ERR_MISSING_ARGS('actual', 'expected');
333 }
334 
335 if (isDeepStrictEqual(actual, expected)) {
336 return;
337 }
338 
339 if (message) {
340 message = `${message}`;
341 }
342 
343 throw new AssertionError({
344 message,
345 actual,
346 expected,
347 operator: 'deepStrictEqual',
348 });
349}
350 
351export function notDeepStrictEqual(
352 actual: unknown,
353 expected: unknown,
354 message?: string | Error
355) {
356 if (arguments.length < 2) {
357 throw new ERR_MISSING_ARGS('actual', 'expected');
358 }
359 
360 if (isDeepStrictEqual(actual, expected)) {
361 if (message) {
362 message = `${message}`;
363 } else {
364 message = 'Expected actual to not be deeply strictly equal to expected';
365 }
366 
367 throw new AssertionError({
368 message,
369 actual,
370 expected,
371 operator: 'notDeepStrictEqual',
372 });
373 }
374}
375 
376// Helper function to check if actual contains all properties from expected with deep strict equality
377function isPartialDeepStrictEqual(
378 actual: unknown,
379 expected: unknown,
380 memo?: Map<unknown, Set<unknown>>
381): boolean {
382 // Handle primitive cases first
383 if (expected === null || typeof expected !== 'object') {
384 return Object.is(actual, expected);
385 }
386 
387 if (actual === null || typeof actual !== 'object') {
388 return false;
389 }
390 
391 // Initialize memo for cycle detection on first call
392 memo ??= new Map();
393 
394 // Cycle detection: check if we're already comparing this pair
395 if (memo.has(actual)) {
396 const actualSet = memo.get(actual)!;
397 if (actualSet.has(expected)) {
398 // We're already comparing this pair, assume they match to avoid infinite recursion
399 return true;
400 }
401 actualSet.add(expected);
402 } else {
403 memo.set(actual, new Set([expected]));
404 }
405 
406 try {
407 // Handle Date objects
408 if (expected instanceof Date) {
409 return actual instanceof Date && actual.getTime() === expected.getTime();
410 }
411 
412 // Handle RegExp objects
413 if (expected instanceof RegExp) {
414 return (
415 actual instanceof RegExp &&
416 actual.source === expected.source &&
417 actual.flags === expected.flags &&
418 actual.lastIndex === expected.lastIndex
419 );
420 }
421 
422 // Handle Error objects
423 if (expected instanceof Error) {
424 return (
425 actual instanceof Error &&
426 actual.name === expected.name &&
427 actual.message === expected.message
428 );
429 }
430 
431 // Handle arrays
432 if (Array.isArray(expected)) {
433 if (!Array.isArray(actual)) {
434 return false;
435 }
436 
437 // For arrays, check if all expected elements are present in actual
438 // This allows for sparse checking - actual can have more elements
439 for (let i = 0; i < expected.length; i++) {
440 if (i in expected) {
441 if (
442 !(i in actual) ||
443 !isPartialDeepStrictEqual(actual[i], expected[i], memo)
444 ) {
445 return false;
446 }
447 }
448 }
449 return true;
450 }
451 
452 // Handle Sets
453 if (expected instanceof Set) {
454 if (!(actual instanceof Set)) {
455 return false;
456 }
457 
458 // Check if all expected values are in actual set
459 for (const expectedValue of expected) {
460 let found = false;
461 for (const actualValue of actual) {
462 if (isDeepStrictEqual(expectedValue, actualValue)) {
463 found = true;
464 break;
465 }
466 }
467 if (!found) {
468 return false;
469 }
470 }
471 return true;
472 }
473 
474 // Handle Maps
475 if (expected instanceof Map) {
476 if (!(actual instanceof Map)) {
477 return false;
478 }
479 
480 // Check if all expected key-value pairs are in actual map
481 for (const [expectedKey, expectedValue] of expected) {
482 let found = false;
483 for (const [actualKey, actualValue] of actual) {
484 if (isDeepStrictEqual(expectedKey, actualKey)) {
485 if (!isPartialDeepStrictEqual(actualValue, expectedValue, memo)) {
486 return false;
487 }
488 found = true;
489 break;
490 }
491 }
492 if (!found) {
493 return false;
494 }
495 }
496 return true;
497 }
498 
499 // Check prototypes are the same (required for deepStrictEqual behavior)
500 if (Object.getPrototypeOf(actual) !== Object.getPrototypeOf(expected)) {
501 return false;
502 }
503 
504 // Handle regular objects
505 const expectedKeys = Object.keys(expected);
506 
507 for (const key of expectedKeys) {
508 if (!(key in actual)) {
509 return false;
510 }
511 
512 if (
513 !isPartialDeepStrictEqual(
514 (actual as any)[key],
515 (expected as any)[key],
516 memo
517 )
518 ) {
519 return false;
520 }
521 }
522 
523 // Handle symbol properties
524 const expectedSymbols = Object.getOwnPropertySymbols(expected);
525 for (const symbol of expectedSymbols) {
526 if (expected.propertyIsEnumerable(symbol)) {
527 if (
528 !(symbol in actual) ||
529 !isPartialDeepStrictEqual(
530 (actual as any)[symbol],
531 (expected as any)[symbol],
532 memo
533 )
534 ) {
535 return false;
536 }
537 }
538 }
539 
540 return true;
541 } finally {
542 // Clean up memo to prevent memory leaks
543 if (memo.has(actual)) {
544 const actualSet = memo.get(actual)!;
545 actualSet.delete(expected);
546 if (actualSet.size === 0) {
547 memo.delete(actual);
548 }
549 }
550 }
551}
552 
553export function partialDeepStrictEqual(
554 actual: unknown,
555 expected: unknown,
556 message?: string | Error
557): void {
558 if (arguments.length < 2) {
559 throw new ERR_MISSING_ARGS('actual', 'expected');
560 }
561 
562 if (isPartialDeepStrictEqual(actual, expected)) {
563 return;
564 }
565 
566 if (message) {
567 message = `${message}`;
568 } else {
569 message =
570 'Expected actual to be partially deeply strictly equal to expected';
571 }
572 
573 throw new AssertionError({
574 message,
575 actual,
576 expected,
577 operator: 'partialDeepStrictEqual',
578 });
579}
580 
581export function fail(message?: string | Error): never {
582 if (typeof message === 'string' || message == null) {
583 throw createAssertionError({
584 message: message ?? 'Failed',
585 operator: 'fail',
586 generatedMessage: message == null,
587 });
588 } else {
589 throw message;
590 }
591}
592 
593export function match(
594 actual: string,
595 regexp: RegExp,
596 message?: string | Error
597) {
598 if (arguments.length < 2) {
599 throw new ERR_MISSING_ARGS('actual', 'regexp');
600 }
601 if (!(regexp instanceof RegExp)) {
602 throw new ERR_INVALID_ARG_TYPE('regexp', 'RegExp', regexp);
603 }
604 
605 if (!regexp.test(actual)) {
606 if (!message) {
607 message = `actual: "${actual}" expected to match: "${regexp}"`;
608 } else {
609 message = `${message}`;
610 }
611 throw new AssertionError({
612 message,
613 actual,
614 expected: regexp,
615 operator: 'match',
616 });
617 }
618}
619 
620export function doesNotMatch(
621 string: string,
622 regexp: RegExp,
623 message?: string | Error
624) {
625 if (arguments.length < 2) {
626 throw new ERR_MISSING_ARGS('string', 'regexp');
627 }
628 if (!(regexp instanceof RegExp)) {
629 throw new ERR_INVALID_ARG_TYPE('regexp', 'RegExp', regexp);
630 }
631 if (typeof string !== 'string') {
632 if (message instanceof Error) {
633 throw message;
634 }
635 throw new AssertionError({
636 message:
637 message ||
638 `The "string" argument must be of type string. Received type ${typeof string} (${inspect(
639 string
640 )})`,
641 actual: string,
642 expected: regexp,
643 operator: 'doesNotMatch',
644 });
645 }
646 
647 if (regexp.test(string)) {
648 if (!message) {
649 message = `actual: "${string}" expected to not match: "${regexp}"`;
650 } else {
651 message = `${message}`;
652 }
653 throw new AssertionError({
654 message,
655 actual: string,
656 expected: regexp,
657 operator: 'doesNotMatch',
658 });
659 }
660}
661 
662export function strict(
663 actual: unknown,
664 message?: string | Error
665): asserts actual {
666 if (arguments.length === 0) {
667 throw new AssertionError({
668 message: 'No value argument passed to `assert.ok()`',
669 });
670 }
671 assert(actual, message);
672}
673 
674export function rejects(
675 asyncFn: Promise<any> | (() => Promise<any>),
676 error?: RegExp | Function | Error
677): Promise<void>;
678 
679export function rejects(
680 asyncFn: Promise<any> | (() => Promise<any>),
681 message?: string
682): Promise<void>;
683 
684export function rejects(
685 asyncFn: Promise<any> | (() => Promise<any>),
686 error?: RegExp | Function | Error | string,
687 message?: string
688) {
689 let promise: Promise<void>;
690 if (typeof asyncFn === 'function') {
691 try {
692 promise = asyncFn();
693 } catch (err) {
694 return Promise.reject(err);
695 }
696 
697 if (!isValidThenable(promise)) {
698 return Promise.reject(
699 new ERR_INVALID_RETURN_VALUE(
700 'instance of Promise',
701 'promiseFn',
702 promise
703 )
704 );
705 }
706 } else if (!isValidThenable(asyncFn)) {
707 return Promise.reject(
708 new ERR_INVALID_ARG_TYPE('promiseFn', ['function', 'Promise'], asyncFn)
709 );
710 } else {
711 promise = asyncFn;
712 }
713 
714 function onFulfilled() {
715 let message = 'Missing expected rejection';
716 if (typeof error === 'string') {
717 message += `: ${error}`;
718 } else if (typeof error === 'function' && error.prototype !== undefined) {
719 message += ` (${error.name}).`;
720 } else {
721 message += '.';
722 }
723 return Promise.reject(
724 createAssertionError({
725 message,
726 operator: 'rejects',
727 generatedMessage: true,
728 })
729 );
730 }
731 
732 function rejects_onRejected(e: Error) {
733 if (
734 validateThrownError(e, error, message, {
735 operator: rejects,
736 validationFunctionName: 'validate',
737 })
738 ) {
739 return;
740 }
741 }
742 
743 return promise.then(onFulfilled, rejects_onRejected);
744}
745 
746export function doesNotReject(
747 asyncFn: Promise<any> | (() => Promise<any>),
748 error?: RegExp | Function
749): Promise<void>;
750 
751export function doesNotReject(
752 asyncFn: Promise<any> | (() => Promise<any>),
753 message?: string
754): Promise<void>;
755 
756export function doesNotReject(
757 asyncFn: Promise<any> | (() => Promise<any>),
758 error?: RegExp | Function | string,
759 message?: string
760) {
761 let promise: Promise<any>;
762 if (typeof asyncFn === 'function') {
763 try {
764 const value = asyncFn();
765 if (!isValidThenable(value)) {
766 return Promise.reject(
767 new ERR_INVALID_RETURN_VALUE(
768 'instance of Promise',
769 'promiseFn',
770 value
771 )
772 );
773 }
774 promise = value;
775 } catch (e) {
776 return Promise.reject(e);
777 }
778 } else if (!isValidThenable(asyncFn)) {
779 return Promise.reject(
780 new ERR_INVALID_ARG_TYPE('promiseFn', ['function', 'Promise'], asyncFn)
781 );
782 } else {
783 promise = asyncFn;
784 }
785 
786 return promise.then(
787 () => {},
788 (e) => gotUnwantedException(e, error, message, doesNotReject)
789 );
790}
791 
792function gotUnwantedException(
793 e: any,
794 expected: RegExp | Function | string | null | undefined,
795 message: string | Error | null | undefined,
796 operator: Function
797): never {
798 if (typeof expected === 'string') {
799 // The use case of doesNotThrow(fn, message);
800 throw new AssertionError({
801 message: `Got unwanted exception: ${expected}\nActual message: "${e.message}"`,
802 operator: operator.name,
803 });
804 } else if (
805 typeof expected === 'function' &&
806 expected.prototype !== undefined
807 ) {
808 // The use case of doesNotThrow(fn, Error, message);
809 if (e instanceof expected) {
810 let msg = `Got unwanted exception: ${e.constructor?.name}`;
811 if (message) {
812 msg += ` ${String(message)}`;
813 }
814 throw new AssertionError({
815 message: msg,
816 operator: operator.name,
817 });
818 } else if (expected.prototype instanceof Error) {
819 throw e;
820 } else {
821 const result = expected(e);
822 if (result === true) {
823 let msg = `Got unwanted rejection.\nActual message: "${e.message}"`;
824 if (message) {
825 msg += ` ${String(message)}`;
826 }
827 throw new AssertionError({
828 message: msg,
829 operator: operator.name,
830 });
831 }
832 }
833 throw e;
834 } else {
835 if (message) {
836 throw new AssertionError({
837 message: `Got unwanted exception: ${message}\nActual message: "${
838 e ? e.message : String(e)
839 }"`,
840 operator: operator.name,
841 });
842 }
843 throw new AssertionError({
844 message: `Got unwanted exception.\nActual message: "${
845 e ? e.message : String(e)
846 }"`,
847 operator: operator.name,
848 });
849 }
850}
851 
852export function ifError(err: any) {
853 if (err !== null && err !== undefined) {
854 let message = 'ifError got unwanted exception: ';
855 
856 if (typeof err === 'object' && typeof err.message === 'string') {
857 if (err.message.length === 0 && err.constructor) {
858 message += err.constructor.name;
859 } else {
860 message += err.message;
861 }
862 } else {
863 message += inspect(err);
864 }
865 
866 const newErr = new AssertionError({
867 actual: err,
868 expected: null,
869 operator: 'ifError',
870 message,
871 stackStartFn: ifError,
872 });
873 
874 // Make sure we actually have a stack trace!
875 const origStack = err.stack;
876 
877 if (typeof origStack === 'string') {
878 const tmp2 = origStack.split('\n');
879 tmp2.shift();
880 let tmp1 = newErr!.stack?.split('\n');
881 
882 for (const errFrame of tmp2) {
883 const pos = tmp1?.indexOf(errFrame);
884 
885 if (pos !== -1) {
886 tmp1 = tmp1?.slice(0, pos);
887 
888 break;
889 }
890 }
891 
892 newErr.stack = `${tmp1?.join('\n')}\n${tmp2.join('\n')}`;
893 }
894 
895 throw newErr;
896 }
897}
898 
899interface ValidateThrownErrorOptions {
900 operator: Function;
901 validationFunctionName?: string;
902}
903 
904function validateThrownError(
905 e: any,
906 error: RegExp | Function | Error | string | null | undefined,
907 message: string | undefined | null,
908 options: ValidateThrownErrorOptions
909): boolean {
910 if (typeof error === 'string') {
911 if (message != null) {
912 throw new ERR_INVALID_ARG_TYPE(
913 'error',
914 ['Object', 'Error', 'Function', 'RegExp'],
915 error
916 );
917 } else if (typeof e === 'object' && e !== null) {
918 if (e.message === error) {
919 throw new ERR_AMBIGUOUS_ARGUMENT(
920 'error/message',
921 `The error message "${e.message}" is identical to the message.`
922 );
923 }
924 } else if (e === error) {
925 throw new ERR_AMBIGUOUS_ARGUMENT(
926 'error/message',
927 `The error "${e}" is identical to the message.`
928 );
929 }
930 message = error;
931 error = undefined;
932 }
933 if (
934 error instanceof Function &&
935 error.prototype !== undefined &&
936 (error === Error || error.prototype instanceof Error)
937 ) {
938 // error is a constructor (Error itself or a subclass of Error)
939 if (e instanceof error) {
940 return true;
941 }
942 throw createAssertionError({
943 message: `The error is expected to be an instance of "${error.name}". Received "${e?.constructor?.name}"\n\nError message:\n\n${e?.message}`,
944 actual: e,
945 expected: error,
946 operator: options.operator.name,
947 generatedMessage: true,
948 });
949 }
950 if (error instanceof Function) {
951 const received = error(e);
952 if (received === true) {
953 return true;
954 }
955 throw createAssertionError({
956 message: `The ${
957 options.validationFunctionName
958 ? `"${options.validationFunctionName}" validation`
959 : 'validation'
960 } function is expected to return "true". Received ${inspect(
961 received
962 )}\n\nCaught error:\n\n${e}`,
963 actual: e,
964 expected: error,
965 operator: options.operator.name,
966 generatedMessage: true,
967 });
968 }
969 if (error instanceof RegExp) {
970 if (error.test(String(e))) {
971 return true;
972 }
973 throw createAssertionError({
974 message: `The input did not match the regular expression ${error.toString()}. Input:\n\n'${String(
975 e
976 )}'\n`,
977 actual: e,
978 expected: error,
979 operator: options.operator.name,
980 generatedMessage: true,
981 });
982 }
983 if (typeof error === 'object' && error !== null) {
984 const keys = Object.keys(error);
985 if (error instanceof Error) {
986 keys.push('name', 'message');
987 }
988 for (const k of keys) {
989 if (e == null) {
990 throw createAssertionError({
991 message: message || 'object is expected to thrown, but got null',
992 actual: e,
993 expected: error,
994 operator: options.operator.name,
995 generatedMessage: message == null,
996 });
997 }
998 
999 if (typeof e === 'string') {
1000 throw createAssertionError({
1001 message:
1002 message || `object is expected to thrown, but got string: ${e}`,
1003 actual: e,
1004 expected: error,
1005 operator: options.operator.name,
1006 generatedMessage: message == null,
1007 });
1008 }
1009 if (typeof e === 'number') {
1010 throw createAssertionError({
1011 message:
1012 message || `object is expected to thrown, but got number: ${e}`,
1013 actual: e,
1014 expected: error,
1015 operator: options.operator.name,
1016 generatedMessage: message == null,
1017 });
1018 }
1019 if (!(k in e)) {
1020 throw createAssertionError({
1021 message: message || `A key in the expected object is missing: ${k}`,
1022 actual: e,
1023 expected: error,
1024 operator: options.operator.name,
1025 generatedMessage: message == null,
1026 });
1027 }
1028 const actual = e[k];
1029 
1030 const expected = (error as any)[k];
1031 if (typeof actual === 'string' && expected instanceof RegExp) {
1032 match(actual, expected);
1033 } else {
1034 deepStrictEqual(actual, expected);
1035 }
1036 }
1037 return true;
1038 }
1039 if (typeof error === 'undefined') {
1040 return true;
1041 }
1042 throw createAssertionError({
1043 message: `Invalid expectation: ${error}`,
1044 operator: options.operator.name,
1045 generatedMessage: true,
1046 });
1047}
1048 
1049function isValidThenable(maybeThennable: any): boolean {
1050 if (!maybeThennable) {
1051 return false;
1052 }
1053 
1054 if (maybeThennable instanceof Promise) {
1055 return true;
1056 }
1057 
1058 return typeof maybeThennable.then === 'function';
1059}
1060 
1061export { AssertionError };
1062 
1063Object.assign(strict, {
1064 AssertionError,
1065 deepEqual: deepStrictEqual,
1066 deepStrictEqual,
1067 doesNotMatch,
1068 doesNotReject,
1069 doesNotThrow,
1070 equal: strictEqual,
1071 fail,
1072 ifError,
1073 match,
1074 notDeepEqual: notDeepStrictEqual,
1075 notDeepStrictEqual,
1076 notEqual: notStrictEqual,
1077 notStrictEqual,
1078 ok,
1079 partialDeepStrictEqual,
1080 rejects,
1081 strict,
1082 strictEqual,
1083 throws,
1084});
1085 
1086export default Object.assign(assert, {
1087 AssertionError,
1088 deepEqual,
1089 deepStrictEqual,
1090 doesNotMatch,
1091 doesNotReject,
1092 doesNotThrow,
1093 equal,
1094 fail,
1095 ifError,
1096 match,
1097 notDeepEqual,
1098 notDeepStrictEqual,
1099 notEqual,
1100 notStrictEqual,
1101 ok,
1102 partialDeepStrictEqual,
1103 rejects,
1104 strict,
1105 strictEqual,
1106 throws,
1107});