/
githubmirror
/
deno
Обзор
Документация
Войти
/
githubmirror
/
deno
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
ext/node/polyfills/assert.ts
1 016 строк
23 KB
em
refactor(ext/node): use primordials across the node:* polyfill layer (#34721)
03 июн 2026, 17:24
Не верифицирован
03 июн 2026, 17:24
9935b92
Код
Авторство
О чём код?
// Copyright 2018-2026 the Deno authors. MIT license. // deno-lint-ignore-file no-process-global (function () { const { core, primordials } = __bootstrap; const { AssertionError } = core.loadExtScript( "ext:deno_node/internal/assert/assertion_error.js", ); const { innerOk } = core.loadExtScript( "ext:deno_node/internal/assert/utils.ts", ); const { inspect } = core.loadExtScript("ext:deno_node/util.ts"); const { ERR_AMBIGUOUS_ARGUMENT, ERR_CONSTRUCT_CALL_REQUIRED, ERR_INVALID_ARG_TYPE, ERR_INVALID_ARG_VALUE, ERR_INVALID_RETURN_VALUE, ERR_MISSING_ARGS, } = core.loadExtScript("ext:deno_node/internal/errors.ts"); const { isDeepEqual, isDeepStrictEqual, isPartialStrictEqual, } = core.loadExtScript("ext:deno_node/internal/util/comparisons.ts"); const { CallTracker } = core.loadExtScript( "ext:deno_node/internal/assert/calltracker.js", ); const { deprecate } = core.loadExtScript("ext:deno_node/util.ts"); const { isPromise, isRegExp } = core.loadExtScript( "ext:deno_node/internal_binding/types.ts", ); const { validateFunction, validateOneOf, } = core.loadExtScript("ext:deno_node/internal/validators.mjs"); const { isError } = core.loadExtScript("ext:deno_node/internal/util.mjs"); const { ArrayPrototypeForEach, ArrayPrototypeIndexOf, ArrayPrototypeJoin, ArrayPrototypePush, ArrayPrototypeSlice, Error, ErrorPrototype, NumberIsNaN, ObjectAssign, ObjectDefineProperty, ObjectIs, ObjectKeys, ObjectPrototypeIsPrototypeOf, ReflectApply, ReflectHas, RegExpPrototypeExec, SafeArrayIterator, StringPrototypeIndexOf, StringPrototypeSlice, StringPrototypeSplit, String, Symbol, } = primordials; const kOptions = Symbol("options"); const NO_EXCEPTION_SENTINEL = {}; function Assert(options) { if (!new.target) { throw new ERR_CONSTRUCT_CALL_REQUIRED("Assert"); } options = ObjectAssign({ __proto__: null, strict: true, skipPrototype: false, }, options); const allowedDiffs = ["simple", "full"]; if (options.diff !== undefined) { validateOneOf(options.diff, "options.diff", allowedDiffs); } this.AssertionError = AssertionError; ObjectDefineProperty(this, kOptions, { __proto__: null, value: options, enumerable: false, configurable: false, writable: false, }); if (options.strict) { this.equal = this.strictEqual; this.deepEqual = this.deepStrictEqual; this.notEqual = this.notStrictEqual; this.notDeepEqual = this.notDeepStrictEqual; } } Assert.prototype.fail = fail; // Duplicate of the `ok` function below so we don't inherit // the extra assigned properties from `assert` function later on. Assert.prototype.ok = function ok(...args) { innerOk(ok, args.length, ...new SafeArrayIterator(args)); }; Assert.prototype.equal = equal; Assert.prototype.notEqual = notEqual; Assert.prototype.deepEqual = deepEqual; Assert.prototype.notDeepEqual = notDeepEqual; Assert.prototype.deepStrictEqual = deepStrictEqual; Assert.prototype.notDeepStrictEqual = notDeepStrictEqual; Assert.prototype.strictEqual = strictEqual; Assert.prototype.notStrictEqual = notStrictEqual; Assert.prototype.partialDeepStrictEqual = partialDeepStrictEqual; Assert.prototype.throws = throws; Assert.prototype.rejects = rejects; Assert.prototype.doesNotThrow = doesNotThrow; Assert.prototype.doesNotReject = doesNotReject; Assert.prototype.ifError = ifError; Assert.prototype.match = match; Assert.prototype.doesNotMatch = doesNotMatch; function innerFail(obj) { if (ObjectPrototypeIsPrototypeOf(ErrorPrototype, obj.message)) { throw obj.message; } throw new AssertionError({ actual: obj.actual, expected: obj.expected, message: obj.message, operator: obj.operator, stackStartFn: obj.stackStartFn, diff: obj.diff, }); } function assert(...args) { innerOk(ok, args.length, ...new SafeArrayIterator(args)); } const ok = assert; class Comparison { constructor(obj, keys, actual) { for (const key of new SafeArrayIterator(keys)) { if (ReflectHas(obj, key)) { if ( actual !== undefined && typeof actual[key] === "string" && isRegExp(obj[key]) && RegExpPrototypeExec(obj[key], actual[key]) !== null ) { this[key] = actual[key]; } else { this[key] = obj[key]; } } } } } function compareExceptionKey( actual, expected, key, message, keys, fn, ) { if ( !ReflectHas(actual, key) || !isDeepStrictEqual(actual[key], expected[key]) ) { if (!message) { // Create placeholder objects to create a nice output. const a = new Comparison(actual, keys); const b = new Comparison(expected, keys, actual); const err = new AssertionError({ actual: a, expected: b, operator: "deepStrictEqual", stackStartFn: fn, diff: this?.[kOptions]?.diff, }); err.actual = actual; err.expected = expected; err.operator = fn.name; throw err; } innerFail({ actual, expected, message, operator: fn.name, stackStartFn: fn, diff: this?.[kOptions]?.diff, }); } } function expectedException( actual, expected, message, fn, ) { let generatedMessage = false; let throwError = false; if (typeof expected !== "function") { // Handle regular expressions. if (isRegExp(expected)) { const str = String(actual); if (RegExpPrototypeExec(expected, str) !== null) { return; } if (!message) { generatedMessage = true; message = "The input did not match the regular expression " + `${inspect(expected)}. Input:\n\n${inspect(str)}\n`; } throwError = true; // Handle primitives properly. } else if (typeof actual !== "object" || actual === null) { const err = new AssertionError({ actual, expected, message, operator: "deepStrictEqual", stackStartFn: fn, diff: this?.[kOptions]?.diff, }); err.operator = fn.name; throw err; } else { // Handle validation objects. const keys = ObjectKeys(expected); // Special handle errors to make sure the name and the message are // compared as well. if (ObjectPrototypeIsPrototypeOf(ErrorPrototype, expected)) { ArrayPrototypePush(keys, "name", "message"); } else if (keys.length === 0) { throw new ERR_INVALID_ARG_VALUE( "error", expected, "may not be an empty object", ); } for (const key of new SafeArrayIterator(keys)) { if ( typeof actual[key] === "string" && isRegExp(expected[key]) && RegExpPrototypeExec(expected[key], actual[key]) !== null ) { continue; } compareExceptionKey(actual, expected, key, message, keys, fn); } return; } // Guard instanceof against arrow functions as they don't have a prototype. // Check for matching Error classes. } else if ( expected.prototype !== undefined && ObjectPrototypeIsPrototypeOf(expected.prototype, actual) ) { return; } else if (ObjectPrototypeIsPrototypeOf(Error, expected)) { if (!message) { generatedMessage = true; message = "The error is expected to be an instance of " + `"${expected.name}". Received `; if (isError(actual)) { const name = (actual.constructor?.name) || actual.name; if (expected.name === name) { message += "an error with identical name but a different prototype."; } else { message += `"${name}"`; } if (actual.message) { message += `\n\nError message:\n\n${actual.message}`; } } else { message += `"${inspect(actual, { depth: -1 })}"`; } } throwError = true; } else { // Check validation functions return value. const res = ReflectApply(expected, {}, [actual]); if (res !== true) { if (!message) { generatedMessage = true; const name = expected.name ? `"${expected.name}" ` : ""; message = `The ${name}validation function is expected to return` + ` "true". Received ${inspect(res)}`; if (isError(actual)) { message += `\n\nCaught error:\n\n${actual}`; } } throwError = true; } } if (throwError) { const err = new AssertionError({ actual, expected, message, operator: fn.name, stackStartFn: fn, diff: this?.[kOptions]?.diff, }); err.generatedMessage = generatedMessage; throw err; } } function getActual(fn) { validateFunction(fn, "fn"); try { fn(); } catch (e) { return e; } return NO_EXCEPTION_SENTINEL; } function checkIsPromise(obj) { // Accept native ES6 promises and promises that are implemented in a similar // way. Do not accept thenables that use a function as `obj` and that have no // `catch` handler. return isPromise(obj) || (obj !== null && typeof obj === "object" && typeof obj.then === "function" && typeof obj.catch === "function"); } async function waitForActual( promiseFn, ) { let resultPromise; if (typeof promiseFn === "function") { // Return a rejected promise if `promiseFn` throws synchronously. resultPromise = promiseFn(); // Fail in case no promise is returned. if (!checkIsPromise(resultPromise)) { throw new ERR_INVALID_RETURN_VALUE( "instance of Promise", "promiseFn", resultPromise, ); } } else if (checkIsPromise(promiseFn)) { resultPromise = promiseFn; } else { throw new ERR_INVALID_ARG_TYPE( "promiseFn", ["Function", "Promise"], promiseFn, ); } try { await resultPromise; } catch (e) { return e; } return NO_EXCEPTION_SENTINEL; } function expectsError( stackStartFn, actual, error, message, ) { if (typeof error === "string") { if (arguments.length === 4) { throw new ERR_INVALID_ARG_TYPE("error", [ "Object", "Error", "Function", "RegExp", ], error); } if (typeof actual === "object" && actual !== null) { if (actual.message === error) { throw new ERR_AMBIGUOUS_ARGUMENT( "error/message", `The error message "${actual.message}" is identical to the message.`, ); } } else if (actual === error) { throw new ERR_AMBIGUOUS_ARGUMENT( "error/message", `The error "${actual}" is identical to the message.`, ); } message = error; error = undefined; } else if ( error != null && typeof error !== "object" && typeof error !== "function" ) { throw new ERR_INVALID_ARG_TYPE("error", [ "Object", "Error", "Function", "RegExp", ], error); } if (actual === NO_EXCEPTION_SENTINEL) { let details = ""; if (error?.name) { details += ` (${error.name})`; } details += message ? `: ${message}` : "."; const fnType = stackStartFn === rejects ? "rejection" : "exception"; innerFail({ actual: undefined, expected: error, operator: stackStartFn.name, message: `Missing expected ${fnType}${details}`, stackStartFn, diff: this?.[kOptions]?.diff, }); } if (!error) { return; } expectedException(actual, error, message, stackStartFn); } function hasMatchingError(actual, expected) { if (typeof expected !== "function") { if (isRegExp(expected)) { const str = String(actual); return RegExpPrototypeExec(expected, str) !== null; } throw new ERR_INVALID_ARG_TYPE( "expected", ["Function", "RegExp"], expected, ); } // Guard instanceof against arrow functions as they don't have a prototype. if ( expected.prototype !== undefined && ObjectPrototypeIsPrototypeOf(expected.prototype, actual) ) { return true; } if (ObjectPrototypeIsPrototypeOf(Error, expected)) { return false; } return ReflectApply(expected, {}, [actual]) === true; } function expectsNoError( stackStartFn, actual, error, message, ) { if (actual === NO_EXCEPTION_SENTINEL) { return; } if (typeof error === "string") { message = error; error = undefined; } if (!error || hasMatchingError(actual, error)) { const details = message ? `: ${message}` : "."; const fnType = stackStartFn === doesNotReject ? "rejection" : "exception"; innerFail({ actual, expected: error, operator: stackStartFn.name, message: `Got unwanted ${fnType}${details}\n` + `Actual message: "${actual?.message}"`, stackStartFn, diff: this?.[kOptions]?.diff, }); } throw actual; } function throws( fn, ...args ) { expectsError(throws, getActual(fn), ...new SafeArrayIterator(args)); } function doesNotThrow( fn, ...args ) { expectsNoError(doesNotThrow, getActual(fn), ...new SafeArrayIterator(args)); } function equal( actual, expected, message, ) { if (arguments.length < 2) { throw new ERR_MISSING_ARGS("actual", "expected"); } if ( actual != expected && (!NumberIsNaN(actual) || !NumberIsNaN(expected)) ) { innerFail({ actual, expected, message, operator: "==", stackStartFn: equal, diff: this?.[kOptions]?.diff, }); } } function notEqual( actual, expected, message, ) { if (arguments.length < 2) { throw new ERR_MISSING_ARGS("actual", "expected"); } if (actual == expected || (NumberIsNaN(actual) && NumberIsNaN(expected))) { innerFail({ actual, expected, message, operator: "!=", stackStartFn: notEqual, diff: this?.[kOptions]?.diff, }); } } function strictEqual( actual, expected, message, ) { if (arguments.length < 2) { throw new ERR_MISSING_ARGS("actual", "expected"); } if (!ObjectIs(actual, expected)) { innerFail({ actual, expected, message, operator: "strictEqual", stackStartFn: strictEqual, diff: this?.[kOptions]?.diff, }); } } function notStrictEqual( actual, expected, message, ) { if (arguments.length < 2) { throw new ERR_MISSING_ARGS("actual", "expected"); } if (ObjectIs(actual, expected)) { innerFail({ actual, expected, message, operator: "notStrictEqual", stackStartFn: notStrictEqual, diff: this?.[kOptions]?.diff, }); } } function partialDeepStrictEqual( actual, expected, message, ) { if (arguments.length < 2) { throw new ERR_MISSING_ARGS("actual", "expected"); } if (!isPartialStrictEqual(actual, expected)) { innerFail({ actual, expected, message, operator: "partialDeepStrictEqual", stackStartFn: partialDeepStrictEqual, diff: this?.[kOptions]?.diff, }); } } function deepEqual( actual, expected, message, ) { if (arguments.length < 2) { throw new ERR_MISSING_ARGS("actual", "expected"); } if (!isDeepEqual(actual, expected)) { innerFail({ actual, expected, message, operator: "deepEqual", stackStartFn: deepEqual, diff: this?.[kOptions]?.diff, }); } } function notDeepEqual( actual, expected, message, ) { if (arguments.length < 2) { throw new ERR_MISSING_ARGS("actual", "expected"); } if (isDeepEqual(actual, expected)) { innerFail({ actual, expected, message, operator: "notDeepEqual", stackStartFn: notDeepEqual, diff: this?.[kOptions]?.diff, }); } } function deepStrictEqual( actual, expected, message, ) { if (arguments.length < 2) { throw new ERR_MISSING_ARGS("actual", "expected"); } if (!isDeepStrictEqual(actual, expected, this?.[kOptions]?.skipPrototype)) { innerFail({ message, actual, expected, operator: "deepStrictEqual", stackStartFn: deepStrictEqual, diff: this?.[kOptions]?.diff, }); } } function notDeepStrictEqual( actual, expected, message, ) { if (arguments.length < 2) { throw new ERR_MISSING_ARGS("actual", "expected"); } if (isDeepStrictEqual(actual, expected, this?.[kOptions]?.skipPrototype)) { innerFail({ actual, expected, message, operator: "notDeepStrictEqual", stackStartFn: notDeepStrictEqual, diff: this?.[kOptions]?.diff, }); } } let warned = false; function fail( actual, expected, message, operator, stackStartFn, ) { const argsLen = arguments.length; let internalMessage = false; if (actual == null && argsLen <= 1) { internalMessage = true; message = "Failed"; } else if (argsLen === 1) { message = actual; actual = undefined; } else { if (warned === false) { warned = true; process.emitWarning( "assert.fail() with more than one argument is deprecated. " + "Please use assert.strictEqual() instead or only pass a message.", "DeprecationWarning", "DEP0094", ); } if (argsLen === 2) { operator = "!="; } } if (ObjectPrototypeIsPrototypeOf(ErrorPrototype, message)) throw message; // IMPORTANT: When adding new references to `this`, ensure they use optional chaining // (this?.[kOptions]?.diff) to handle cases where the method is destructured from an // Assert instance and loses its context. Destructured methods will fall back // to default behavior when `this` is undefined. const errArgs = { actual, expected, operator: operator === undefined ? "fail" : operator, stackStartFn: stackStartFn || fail, message, diff: this?.[kOptions]?.diff, }; const err = new AssertionError(errArgs); if (internalMessage) { err.generatedMessage = true; } throw err; } function internalMatch( string, regexp, message, fn, ) { if (!isRegExp(regexp)) { throw new ERR_INVALID_ARG_TYPE( "regexp", "RegExp", regexp, ); } const matchFn = fn === match; if ( typeof string !== "string" || RegExpPrototypeExec(regexp, string) !== null !== matchFn ) { if (ObjectPrototypeIsPrototypeOf(ErrorPrototype, message)) { throw message; } const generatedMessage = !message; // 'The input was expected to not match the regular expression ' + message ||= typeof string !== "string" ? 'The "string" argument must be of type string. Received type ' + `${typeof string} (${inspect(string)})` : (matchFn ? "The input did not match the regular expression " : "The input was expected to not match the regular expression ") + `${inspect(regexp)}. Input:\n\n${inspect(string)}\n`; const err = new AssertionError({ actual: string, expected: regexp, message, operator: fn.name, stackStartFn: fn, diff: this?.[kOptions]?.diff, }); err.generatedMessage = generatedMessage; throw err; } } function match(string, regexp, message) { internalMatch(string, regexp, message, match); } function doesNotMatch( string, regexp, message, ) { internalMatch(string, regexp, message, doesNotMatch); } function strict(...args) { innerOk(strict, args.length, ...new SafeArrayIterator(args)); } async function rejects( asyncFn, ...args ) { expectsError( rejects, await waitForActual(asyncFn), ...new SafeArrayIterator(args), ); } async function doesNotReject( asyncFn, ...args ) { expectsNoError( doesNotReject, await waitForActual(asyncFn), ...new SafeArrayIterator(args), ); } /** * Throws `value` if the value is not `null` or `undefined`. * * @param err */ function ifError(err) { if (err !== null && err !== undefined) { let message = "ifError got unwanted exception: "; if (typeof err === "object" && typeof err.message === "string") { if (err.message.length === 0 && err.constructor) { message += err.constructor.name; } else { message += err.message; } } else { message += inspect(err); } const newErr = new AssertionError({ actual: err, expected: null, operator: "ifError", message, stackStartFn: ifError, diff: this?.[kOptions]?.diff, }); // Make sure we actually have a stack trace! const origStack = err.stack; if (typeof origStack === "string") { // This will remove any duplicated frames from the error frames taken // from within `ifError` and add the original error frames to the newly // created ones. const origStackStart = StringPrototypeIndexOf(origStack, "\n at"); if (origStackStart !== -1) { const originalFrames = StringPrototypeSplit( StringPrototypeSlice(origStack, origStackStart + 1), "\n", ); // Filter all frames existing in err.stack. let newFrames = StringPrototypeSplit(newErr.stack, "\n"); for (const errFrame of new SafeArrayIterator(originalFrames)) { // Find the first occurrence of the frame. const pos = ArrayPrototypeIndexOf(newFrames, errFrame); if (pos !== -1) { // Only keep new frames. newFrames = ArrayPrototypeSlice(newFrames, 0, pos); break; } } const stackStart = ArrayPrototypeJoin(newFrames, "\n"); const stackEnd = ArrayPrototypeJoin(originalFrames, "\n"); newErr.stack = `${stackStart}\n${stackEnd}`; } } throw newErr; } } const CallTracker_ = deprecate( CallTracker, "assert.CallTracker is deprecated.", "DEP0173", ); function setOwnProperty(obj, key, value) { return ObjectDefineProperty(obj, key, { __proto__: null, configurable: true, enumerable: true, value, writable: true, }); } ArrayPrototypeForEach([ "fail", "equal", "notEqual", "deepEqual", "notDeepEqual", "deepStrictEqual", "notDeepStrictEqual", "strictEqual", "notStrictEqual", "partialDeepStrictEqual", "match", "doesNotMatch", "throws", "rejects", "doesNotThrow", "doesNotReject", "ifError", ], (name) => { setOwnProperty(assert, name, Assert.prototype[name]); }); ObjectAssign(strict, { Assert, AssertionError, CallTracker: CallTracker_, deepEqual: deepStrictEqual, deepStrictEqual, doesNotMatch, doesNotReject, doesNotThrow, equal: strictEqual, fail, ifError, match, notDeepEqual: notDeepStrictEqual, notDeepStrictEqual, notEqual: notStrictEqual, notStrictEqual, ok, partialDeepStrictEqual, rejects, strict, strictEqual, throws, }); const default_ = ObjectAssign(assert, { Assert, AssertionError, CallTracker: CallTracker_, deepEqual, deepStrictEqual, doesNotMatch, doesNotReject, doesNotThrow, equal, fail, ifError, match, notDeepEqual, notDeepStrictEqual, notEqual, notStrictEqual, ok, partialDeepStrictEqual, rejects, strict, strictEqual, throws, }); return { default: default_, Assert, AssertionError, CallTracker: CallTracker_, deepEqual, deepStrictEqual, doesNotMatch, doesNotReject, doesNotThrow, equal, fail, ifError, match, notDeepEqual, notDeepStrictEqual, notEqual, notStrictEqual, ok, partialDeepStrictEqual, rejects, strict, strictEqual, throws, }; })();