Repository: denoland/deno
Pull request: fix(ext/node): deep `assert` compatibility

Allows https://github.com/nodejs/node/blob/v24.12.0/test/parallel/test-assert-deep.js to pass. This marks 100% coverage of node compat tests related to the `assert` module.

Changed files:
filename: ext/crypto/00_crypto.js
patch: @@ -90,6 +90,7 @@ const {
 import * as webidl from "ext:deno_webidl/00_webidl.js";
 import { createFilteredInspectProxy } from "ext:deno_web/01_console.js";
 import { DOMException } from "ext:deno_web/01_dom_exception.js";
+import { kKeyObject } from "ext:deno_node/internal/crypto/constants.ts";
 
 const supportedNamedCurves = ["P-256", "P-384", "P-521"];
 const recognisedUsages = [
@@ -365,6 +366,8 @@ class CryptoKey {
   [_usages];
   /** @type {object} */
   [_handle];
+  /** @type {object} */
+  [kKeyObject];
 
   constructor() {
     webidl.illegalConstructor();
@@ -431,6 +434,7 @@ function constructKey(type, extractable, usages, algorithm, handle) {
   key[_usages] = usages;
   key[_algorithm] = algorithm;
   key[_handle] = handle;
+  key[kKeyObject] = WeakMapPrototypeGet(KEY_STORE, handle);
   return key;
 }
 

filename: ext/node/ops/util.rs
patch: @@ -227,7 +227,7 @@ pub fn op_node_get_own_non_index_properties<'s>(
   scope: &mut v8::PinScope<'s, '_>,
   obj: v8::Local<'s, v8::Object>,
   #[smi] filter: u32,
-) -> Result<v8::Local<'s, v8::Array>, JsErrorBox> {
+) -> Result<v8::Local<'s, v8::Value>, JsErrorBox> {
   let mut property_filter = v8::PropertyFilter::ALL_PROPERTIES;
   if filter & 1 << 0 != 0 {
     property_filter = property_filter | v8::PropertyFilter::ONLY_WRITABLE;
@@ -245,19 +245,33 @@ pub fn op_node_get_own_non_index_properties<'s>(
     property_filter = property_filter | v8::PropertyFilter::SKIP_SYMBOLS;
   }
 
-  obj
-    .get_property_names(
-      scope,
-      v8::GetPropertyNamesArgs {
-        index_filter: v8::IndexFilter::SkipIndices,
-        property_filter,
-        key_conversion: v8::KeyConversionMode::NoNumbers,
-        mode: v8::KeyCollectionMode::OwnOnly,
-      },
-    )
-    .ok_or_else(|| {
-      JsErrorBox::type_error("Failed to get own non-index properties")
-    })
+  v8::tc_scope!(let tc_scope, scope);
+
+  let result = obj.get_property_names(
+    tc_scope,
+    v8::GetPropertyNamesArgs {
+      index_filter: v8::IndexFilter::SkipIndices,
+      property_filter,
+      key_conversion: v8::KeyConversionMode::NoNumbers,
+      mode: v8::KeyCollectionMode::OwnOnly,
+    },
+  );
+
+  match result {
+    Some(names) => Ok(names.into()),
+    None => {
+      if tc_scope.has_caught() || tc_scope.has_terminated() {
+        tc_scope.rethrow();
+        // Dummy value, this result will be discarded because an error was thrown.
+        let v = v8::undefined(tc_scope);
+        Ok(v.into())
+      } else {
+        Err(JsErrorBox::type_error(
+          "Failed to get own non-index properties",
+        ))
+      }
+    }
+  }
 }
 
 #[op2]

filename: ext/node/polyfills/internal/util/comparisons.ts
patch: @@ -15,7 +15,6 @@ import {
   isFloat64Array,
   isKeyObject,
   isMap,
-  isNativeError,
   isNumberObject,
   isRegExp,
   isSet,
@@ -33,6 +32,7 @@ import {
 import { primordials } from "ext:core/mod.js";
 import assert from "node:assert";
 import { kKeyObject } from "ext:deno_node/internal/crypto/constants.ts";
+import { isError } from "ext:deno_node/internal/util.mjs";
 import { isURL } from "ext:deno_node/internal/url.ts";
 
 const {
@@ -51,7 +51,6 @@ const {
   Date,
   DatePrototypeGetTime,
   Error,
-  ErrorPrototype,
   Float32Array,
   Float64Array,
   Function,
@@ -69,7 +68,6 @@ const {
   ObjectKeys,
   ObjectPrototypeHasOwnProperty: hasOwn,
   ObjectPrototypePropertyIsEnumerable: hasEnumerable,
-  ObjectPrototypeIsPrototypeOf,
   ObjectPrototypeToString,
   Promise,
   RegExp,
@@ -419,11 +417,11 @@ function objectComparisonStart(
     ) {
       return false;
     }
-  } else if (ObjectPrototypeIsPrototypeOf(ErrorPrototype, val1)) {
+  } else if (isError(val1)) {
     // Do not compare the stack as it might differ even though the error itself
     // is otherwise identical.
     if (
-      !ObjectPrototypeIsPrototypeOf(ErrorPrototype, val2) ||
+      !isError(val2) ||
       !isEnumerableOrIdentical(val1, val2, "message", mode, memos) ||
       !isEnumerableOrIdentical(val1, val2, "name", mode, memos) ||
       !isEnumerableOrIdentical(val1, val2, "cause", mode, memos) ||
@@ -451,8 +449,7 @@ function objectComparisonStart(
     isRegExp(val2) ||
     isAnyArrayBuffer(val2) ||
     isBoxedPrimitive(val2) ||
-    isNativeError(val2) ||
-    ObjectPrototypeIsPrototypeOf(ErrorPrototype, val2)
+    isError(val2)
   ) {
     return false;
   } else if (isURL(val1)) {

filename: ext/web/01_console.js
patch: @@ -34,6 +34,7 @@ import {
   op_preview_entries,
 } from "ext:core/ops";
 import * as ops from "ext:core/ops";
+import { URLPrototype } from "ext:deno_web/00_url.js";
 const {
   AggregateError,
   AggregateErrorPrototype,
@@ -71,6 +72,7 @@ const {
   DatePrototype,
   DatePrototypeGetTime,
   DatePrototypeToISOString,
+  DatePrototypeToString,
   Error,
   ErrorCaptureStackTrace,
   ErrorPrototype,
@@ -847,14 +849,16 @@ function formatRaw(ctx, value, recurseTimes, typedArray, proxyDetails) {
         (proxyDetails === null && isDate(value)) ||
         (proxyDetails !== null && isDate(proxyDetails[0]))
       ) {
-        const date = proxyDetails?.[0] ?? value;
-        if (NumberIsNaN(DatePrototypeGetTime(date))) {
-          return ctx.stylize("Invalid Date", "date");
-        } else {
-          base = DatePrototypeToISOString(date);
-          if (keys.length === 0 && protoProps === undefined) {
-            return ctx.stylize(base, "date");
-          }
+        value = proxyDetails?.[0] ?? value;
+        base = NumberIsNaN(DatePrototypeGetTime(value))
+          ? DatePrototypeToString(value)
+          : DatePrototypeToISOString(value);
+        const prefix = getPrefix(constructor, tag, "Date");
+        if (prefix !== "Date ") {
+          base = `${prefix}${base}`;
+        }
+        if (keys.length === 0 && protoProps === undefined) {
+          return ctx.stylize(base, "date");
         }
       } else if (
         proxyDetails === null &&
@@ -969,6 +973,14 @@ function formatRaw(ctx, value, recurseTimes, typedArray, proxyDetails) {
         if (keys.length === 0 && protoProps === undefined) {
           return base;
         }
+      } else if (
+        ObjectPrototypeIsPrototypeOf(URLPrototype, value) &&
+        !(recurseTimes > ctx.depth && ctx.depth !== null)
+      ) {
+        base = value.href;
+        if (keys.length === 0 && protoProps === undefined) {
+          return base;
+        }
       } else {
         if (keys.length === 0 && protoProps === undefined) {
           // TODO(wafuwafu13): Implement

filename: tests/node_compat/config.jsonc
patch: @@ -67,6 +67,7 @@
     "parallel/test-assert-class-destructuring.js": {},
     "parallel/test-assert-class.js": {},
     "parallel/test-assert-deep-with-error.js": {},
+    "parallel/test-assert-deep.js": {},
     "parallel/test-assert-esm-cjs-message-verify.js": {},
     "parallel/test-assert-fail-deprecation.js": {},
     "parallel/test-assert-fail.js": {},