summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBartek IwaƄczuk <biwanczuk@gmail.com>2024-06-14 23:07:02 +0100
committerGitHub <noreply@github.com>2024-06-15 00:07:02 +0200
commit78b12a43b307c3405cd63cf4612517210330b487 (patch)
treeef9a6a258f13f7d4151e8152ed9702572126c165
parent184a85eaecbe7055b5b3969391f49c4723ac44fb (diff)
fix(ext/node): better support for `node:diagnostics_channel` module (#24088)
Closes https://github.com/denoland/deno/issues/24060
-rw-r--r--ext/node/lib.rs2
-rw-r--r--ext/node/polyfills/diagnostics_channel.js430
-rw-r--r--ext/node/polyfills/diagnostics_channel.ts93
-rw-r--r--ext/node/polyfills/internal/errors.ts6
-rw-r--r--ext/node/polyfills/internal/util.mjs36
-rw-r--r--tests/node_compat/config.jsonc7
-rw-r--r--tests/node_compat/runner/TODO.md7
-rw-r--r--tests/node_compat/test/parallel/test-diagnostics-channel-net.js32
-rw-r--r--tests/node_compat/test/parallel/test-diagnostics-channel-sync-unsubscribe.js21
-rw-r--r--tests/node_compat/test/parallel/test-diagnostics-channel-tracing-channel-args-types.js46
-rw-r--r--tests/node_compat/test/parallel/test-diagnostics-channel-tracing-channel-callback-run-stores.js36
-rw-r--r--tests/node_compat/test/parallel/test-diagnostics-channel-tracing-channel-promise-run-stores.js38
-rw-r--r--tests/node_compat/test/parallel/test-diagnostics-channel-tracing-channel-sync-error.js46
-rw-r--r--tests/node_compat/test/parallel/test-diagnostics-channel-tracing-channel-sync.js53
-rw-r--r--tools/core_import_map.json2
15 files changed, 749 insertions, 106 deletions
diff --git a/ext/node/lib.rs b/ext/node/lib.rs
index 1fc5198a0..d05434b88 100644
--- a/ext/node/lib.rs
+++ b/ext/node/lib.rs
@@ -583,7 +583,7 @@ deno_core::extension!(deno_node,
"node:constants" = "constants.ts",
"node:crypto" = "crypto.ts",
"node:dgram" = "dgram.ts",
- "node:diagnostics_channel" = "diagnostics_channel.ts",
+ "node:diagnostics_channel" = "diagnostics_channel.js",
"node:dns" = "dns.ts",
"node:dns/promises" = "dns/promises.ts",
"node:domain" = "domain.ts",
diff --git a/ext/node/polyfills/diagnostics_channel.js b/ext/node/polyfills/diagnostics_channel.js
new file mode 100644
index 000000000..807c33e47
--- /dev/null
+++ b/ext/node/polyfills/diagnostics_channel.js
@@ -0,0 +1,430 @@
+// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+
+// TODO(petamoriken): enable prefer-primordials for node polyfills
+// deno-lint-ignore-file prefer-primordials ban-untagged-todo
+
+import { ERR_INVALID_ARG_TYPE } from "ext:deno_node/internal/errors.ts";
+import { validateFunction } from "ext:deno_node/internal/validators.mjs";
+import { nextTick } from "node:process";
+import { primordials } from "ext:core/mod.js";
+
+const {
+ ArrayPrototypeAt,
+ ArrayPrototypeIndexOf,
+ ArrayPrototypePush,
+ ArrayPrototypeSplice,
+ ObjectDefineProperty,
+ ObjectGetPrototypeOf,
+ ObjectSetPrototypeOf,
+ Promise,
+ PromisePrototypeThen,
+ PromiseReject,
+ PromiseResolve,
+ ReflectApply,
+ SafeFinalizationRegistry,
+ SafeMap,
+ SymbolHasInstance,
+} = primordials;
+import { WeakReference } from "ext:deno_node/internal/util.mjs";
+
+// Can't delete when weakref count reaches 0 as it could increment again.
+// Only GC can be used as a valid time to clean up the channels map.
+class WeakRefMap extends SafeMap {
+ #finalizers = new SafeFinalizationRegistry((key) => {
+ this.delete(key);
+ });
+
+ set(key, value) {
+ this.#finalizers.register(value, key);
+ return super.set(key, new WeakReference(value));
+ }
+
+ get(key) {
+ return super.get(key)?.get();
+ }
+
+ incRef(key) {
+ return super.get(key)?.incRef();
+ }
+
+ decRef(key) {
+ return super.get(key)?.decRef();
+ }
+}
+
+function markActive(channel) {
+ ObjectSetPrototypeOf(channel, ActiveChannel.prototype);
+ channel._subscribers = [];
+ channel._stores = new SafeMap();
+}
+
+function maybeMarkInactive(channel) {
+ // When there are no more active subscribers or bound, restore to fast prototype.
+ if (!channel._subscribers.length && !channel._stores.size) {
+ ObjectSetPrototypeOf(channel, Channel.prototype);
+ channel._subscribers = undefined;
+ channel._stores = undefined;
+ }
+}
+
+function defaultTransform(data) {
+ return data;
+}
+
+function wrapStoreRun(store, data, next, transform = defaultTransform) {
+ return () => {
+ let context;
+ try {
+ context = transform(data);
+ } catch (err) {
+ nextTick(() => {
+ // TODO(bartlomieju): in Node.js this is using `triggerUncaughtException` API, need
+ // to clarify if we need that or if just throwing the error is enough here.
+ throw err;
+ // triggerUncaughtException(err, false);
+ });
+ return next();
+ }
+
+ return store.run(context, next);
+ };
+}
+
+class ActiveChannel {
+ subscribe(subscription) {
+ validateFunction(subscription, "subscription");
+ ArrayPrototypePush(this._subscribers, subscription);
+ channels.incRef(this.name);
+ }
+
+ unsubscribe(subscription) {
+ const index = ArrayPrototypeIndexOf(this._subscribers, subscription);
+ if (index === -1) return false;
+
+ ArrayPrototypeSplice(this._subscribers, index, 1);
+
+ channels.decRef(this.name);
+ maybeMarkInactive(this);
+
+ return true;
+ }
+
+ bindStore(store, transform) {
+ const replacing = this._stores.has(store);
+ if (!replacing) channels.incRef(this.name);
+ this._stores.set(store, transform);
+ }
+
+ unbindStore(store) {
+ if (!this._stores.has(store)) {
+ return false;
+ }
+
+ this._stores.delete(store);
+
+ channels.decRef(this.name);
+ maybeMarkInactive(this);
+
+ return true;
+ }
+
+ get hasSubscribers() {
+ return true;
+ }
+
+ publish(data) {
+ for (let i = 0; i < (this._subscribers?.length || 0); i++) {
+ try {
+ const onMessage = this._subscribers[i];
+ onMessage(data, this.name);
+ } catch (err) {
+ nextTick(() => {
+ // TODO(bartlomieju): in Node.js this is using `triggerUncaughtException` API, need
+ // to clarify if we need that or if just throwing the error is enough here.
+ throw err;
+ // triggerUncaughtException(err, false);
+ });
+ }
+ }
+ }
+
+ runStores(data, fn, thisArg, ...args) {
+ let run = () => {
+ this.publish(data);
+ return ReflectApply(fn, thisArg, args);
+ };
+
+ for (const entry of this._stores.entries()) {
+ const store = entry[0];
+ const transform = entry[1];
+ run = wrapStoreRun(store, data, run, transform);
+ }
+
+ return run();
+ }
+}
+
+class Channel {
+ constructor(name) {
+ this._subscribers = undefined;
+ this._stores = undefined;
+ this.name = name;
+
+ channels.set(name, this);
+ }
+
+ static [SymbolHasInstance](instance) {
+ const prototype = ObjectGetPrototypeOf(instance);
+ return prototype === Channel.prototype ||
+ prototype === ActiveChannel.prototype;
+ }
+
+ subscribe(subscription) {
+ markActive(this);
+ this.subscribe(subscription);
+ }
+
+ unsubscribe() {
+ return false;
+ }
+
+ bindStore(store, transform) {
+ markActive(this);
+ this.bindStore(store, transform);
+ }
+
+ unbindStore() {
+ return false;
+ }
+
+ get hasSubscribers() {
+ return false;
+ }
+
+ publish() {}
+
+ runStores(_data, fn, thisArg, ...args) {
+ return ReflectApply(fn, thisArg, args);
+ }
+}
+
+const channels = new WeakRefMap();
+
+export function channel(name) {
+ const channel = channels.get(name);
+ if (channel) return channel;
+
+ if (typeof name !== "string" && typeof name !== "symbol") {
+ throw new ERR_INVALID_ARG_TYPE("channel", ["string", "symbol"], name);
+ }
+
+ return new Channel(name);
+}
+
+export function subscribe(name, subscription) {
+ return channel(name).subscribe(subscription);
+}
+
+export function unsubscribe(name, subscription) {
+ return channel(name).unsubscribe(subscription);
+}
+
+export function hasSubscribers(name) {
+ const channel = channels.get(name);
+ if (!channel) return false;
+
+ return channel.hasSubscribers;
+}
+
+const traceEvents = [
+ "start",
+ "end",
+ "asyncStart",
+ "asyncEnd",
+ "error",
+];
+
+function assertChannel(value, name) {
+ if (!(value instanceof Channel)) {
+ throw new ERR_INVALID_ARG_TYPE(name, ["Channel"], value);
+ }
+}
+
+function tracingChannelFrom(nameOrChannels, name) {
+ if (typeof nameOrChannels === "string") {
+ return channel(`tracing:${nameOrChannels}:${name}`);
+ }
+
+ if (typeof nameOrChannels === "object" && nameOrChannels !== null) {
+ const channel = nameOrChannels[name];
+ assertChannel(channel, `nameOrChannels.${name}`);
+ return channel;
+ }
+
+ throw new ERR_INVALID_ARG_TYPE("nameOrChannels", [
+ "string",
+ "object",
+ "Channel",
+ ], nameOrChannels);
+}
+
+class TracingChannel {
+ constructor(nameOrChannels) {
+ for (const eventName of traceEvents) {
+ ObjectDefineProperty(this, eventName, {
+ __proto__: null,
+ value: tracingChannelFrom(nameOrChannels, eventName),
+ });
+ }
+ }
+
+ get hasSubscribers() {
+ return this.start.hasSubscribers ||
+ this.end.hasSubscribers ||
+ this.asyncStart.hasSubscribers ||
+ this.asyncEnd.hasSubscribers ||
+ this.error.hasSubscribers;
+ }
+
+ subscribe(handlers) {
+ for (const name of traceEvents) {
+ if (!handlers[name]) continue;
+
+ this[name]?.subscribe(handlers[name]);
+ }
+ }
+
+ unsubscribe(handlers) {
+ let done = true;
+
+ for (const name of traceEvents) {
+ if (!handlers[name]) continue;
+
+ if (!this[name]?.unsubscribe(handlers[name])) {
+ done = false;
+ }
+ }
+
+ return done;
+ }
+
+ traceSync(fn, context = {}, thisArg, ...args) {
+ if (!this.hasSubscribers) {
+ return ReflectApply(fn, thisArg, args);
+ }
+
+ const { start, end, error } = this;
+
+ return start.runStores(context, () => {
+ try {
+ const result = ReflectApply(fn, thisArg, args);
+ context.result = result;
+ return result;
+ } catch (err) {
+ context.error = err;
+ error.publish(context);
+ throw err;
+ } finally {
+ end.publish(context);
+ }
+ });
+ }
+
+ tracePromise(fn, context = {}, thisArg, ...args) {
+ if (!this.hasSubscribers) {
+ return ReflectApply(fn, thisArg, args);
+ }
+
+ const { start, end, asyncStart, asyncEnd, error } = this;
+
+ function reject(err) {
+ context.error = err;
+ error.publish(context);
+ asyncStart.publish(context);
+ // TODO: Is there a way to have asyncEnd _after_ the continuation?
+ asyncEnd.publish(context);
+ return PromiseReject(err);
+ }
+
+ function resolve(result) {
+ context.result = result;
+ asyncStart.publish(context);
+ // TODO: Is there a way to have asyncEnd _after_ the continuation?
+ asyncEnd.publish(context);
+ return result;
+ }
+
+ return start.runStores(context, () => {
+ try {
+ let promise = ReflectApply(fn, thisArg, args);
+ // Convert thenables to native promises
+ if (!(promise instanceof Promise)) {
+ promise = PromiseResolve(promise);
+ }
+ return PromisePrototypeThen(promise, resolve, reject);
+ } catch (err) {
+ context.error = err;
+ error.publish(context);
+ throw err;
+ } finally {
+ end.publish(context);
+ }
+ });
+ }
+
+ traceCallback(fn, position = -1, context = {}, thisArg, ...args) {
+ if (!this.hasSubscribers) {
+ return ReflectApply(fn, thisArg, args);
+ }
+
+ const { start, end, asyncStart, asyncEnd, error } = this;
+
+ function wrappedCallback(err, res) {
+ if (err) {
+ context.error = err;
+ error.publish(context);
+ } else {
+ context.result = res;
+ }
+
+ // Using runStores here enables manual context failure recovery
+ asyncStart.runStores(context, () => {
+ try {
+ return ReflectApply(callback, this, arguments);
+ } finally {
+ asyncEnd.publish(context);
+ }
+ });
+ }
+
+ const callback = ArrayPrototypeAt(args, position);
+ validateFunction(callback, "callback");
+ ArrayPrototypeSplice(args, position, 1, wrappedCallback);
+
+ return start.runStores(context, () => {
+ try {
+ return ReflectApply(fn, thisArg, args);
+ } catch (err) {
+ context.error = err;
+ error.publish(context);
+ throw err;
+ } finally {
+ end.publish(context);
+ }
+ });
+ }
+}
+
+export function tracingChannel(nameOrChannels) {
+ return new TracingChannel(nameOrChannels);
+}
+
+export default {
+ channel,
+ hasSubscribers,
+ subscribe,
+ tracingChannel,
+ unsubscribe,
+ Channel,
+};
diff --git a/ext/node/polyfills/diagnostics_channel.ts b/ext/node/polyfills/diagnostics_channel.ts
deleted file mode 100644
index 4f54c3431..000000000
--- a/ext/node/polyfills/diagnostics_channel.ts
+++ /dev/null
@@ -1,93 +0,0 @@
-// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
-
-// TODO(petamoriken): enable prefer-primordials for node polyfills
-// deno-lint-ignore-file prefer-primordials
-
-import { ERR_INVALID_ARG_TYPE } from "ext:deno_node/internal/errors.ts";
-import { validateFunction } from "ext:deno_node/internal/validators.mjs";
-import { nextTick } from "node:process";
-
-type Subscriber = (message: unknown, name?: string) => void;
-
-export class Channel {
- _subscribers: Subscriber[];
- name: string;
- constructor(name: string) {
- this._subscribers = [];
- this.name = name;
- }
-
- publish(message: unknown) {
- for (const subscriber of this._subscribers) {
- try {
- subscriber(message, this.name);
- } catch (err) {
- nextTick(() => {
- throw err;
- });
- }
- }
- }
-
- subscribe(subscription: Subscriber) {
- validateFunction(subscription, "subscription");
-
- this._subscribers.push(subscription);
- }
-
- unsubscribe(subscription: Subscriber) {
- if (!this._subscribers.includes(subscription)) {
- return false;
- }
-
- this._subscribers.splice(this._subscribers.indexOf(subscription), 1);
-
- return true;
- }
-
- get hasSubscribers() {
- return this._subscribers.length > 0;
- }
-}
-
-const channels: Record<string, Channel> = {};
-
-export function channel(name: string) {
- if (typeof name !== "string" && typeof name !== "symbol") {
- throw new ERR_INVALID_ARG_TYPE("channel", ["string", "symbol"], name);
- }
-
- if (!Object.hasOwn(channels, name)) {
- channels[name] = new Channel(name);
- }
-
- return channels[name];
-}
-
-export function hasSubscribers(name: string) {
- if (!Object.hasOwn(channels, name)) {
- return false;
- }
-
- return channels[name].hasSubscribers;
-}
-
-export function subscribe(name: string, subscription: Subscriber) {
- const c = channel(name);
-
- return c.subscribe(subscription);
-}
-
-export function unsubscribe(name: string, subscription: Subscriber) {
- const c = channel(name);
-
- return c.unsubscribe(subscription);
-}
-
-export default {
- channel,
- hasSubscribers,
- subscribe,
- unsubscribe,
- Channel,
-};
diff --git a/ext/node/polyfills/internal/errors.ts b/ext/node/polyfills/internal/errors.ts
index a16656087..cb4119411 100644
--- a/ext/node/polyfills/internal/errors.ts
+++ b/ext/node/polyfills/internal/errors.ts
@@ -349,9 +349,8 @@ export class NodeErrorAbstraction extends Error {
super(message);
this.code = code;
this.name = name;
- //This number changes depending on the name of this class
- //20 characters as of now
- this.stack = this.stack && `${name} [${this.code}]${this.stack.slice(20)}`;
+ this.stack = this.stack &&
+ `${name} [${this.code}]${this.stack.slice(this.name.length)}`;
}
override toString() {
@@ -614,7 +613,6 @@ export class ERR_INVALID_ARG_TYPE_RANGE extends NodeRangeError {
export class ERR_INVALID_ARG_TYPE extends NodeTypeError {
constructor(name: string, expected: string | string[], actual: unknown) {
const msg = createInvalidArgType(name, expected);
-
super("ERR_INVALID_ARG_TYPE", `${msg}.${invalidArgTypeHelper(actual)}`);
}
diff --git a/ext/node/polyfills/internal/util.mjs b/ext/node/polyfills/internal/util.mjs
index 596599859..e6b32d17d 100644
--- a/ext/node/polyfills/internal/util.mjs
+++ b/ext/node/polyfills/internal/util.mjs
@@ -15,6 +15,10 @@ import {
} from "ext:deno_node/internal/primordials.mjs";
import { ERR_UNKNOWN_SIGNAL } from "ext:deno_node/internal/errors.ts";
import { os } from "ext:deno_node/internal_binding/constants.ts";
+import { primordials } from "ext:core/mod.js";
+const {
+ SafeWeakRef,
+} = primordials;
export const customInspectSymbol = Symbol.for("nodejs.util.inspect.custom");
export const kEnumerableProperty = Object.create(null);
@@ -135,6 +139,38 @@ export function convertToValidSignal(signal) {
throw new ERR_UNKNOWN_SIGNAL(signal);
}
+export class WeakReference {
+ #weak = null;
+ #strong = null;
+ #refCount = 0;
+ constructor(object) {
+ this.#weak = new SafeWeakRef(object);
+ }
+
+ incRef() {
+ this.#refCount++;
+ if (this.#refCount === 1) {
+ const derefed = this.#weak.deref();
+ if (derefed !== undefined) {
+ this.#strong = derefed;
+ }
+ }
+ return this.#refCount;
+ }
+
+ decRef() {
+ this.#refCount--;
+ if (this.#refCount === 0) {
+ this.#strong = null;
+ }
+ return this.#refCount;
+ }
+
+ get() {
+ return this.#weak.deref();
+ }
+}
+
promisify.custom = kCustomPromisifiedSymbol;
export default {
diff --git a/tests/node_compat/config.jsonc b/tests/node_compat/config.jsonc
index bc5580c1d..27cf6afb8 100644
--- a/tests/node_compat/config.jsonc
+++ b/tests/node_compat/config.jsonc
@@ -261,9 +261,16 @@
"test-dgram-close-during-bind.js",
"test-dgram-close-signal.js",
"test-diagnostics-channel-has-subscribers.js",
+ "test-diagnostics-channel-net.js",
"test-diagnostics-channel-object-channel-pub-sub.js",
"test-diagnostics-channel-pub-sub.js",
"test-diagnostics-channel-symbol-named.js",
+ "test-diagnostics-channel-sync-unsubscribe.js",
+ "test-diagnostics-channel-tracing-channel-args-types.js",
+ "test-diagnostics-channel-tracing-channel-callback-run-stores.js",
+ "test-diagnostics-channel-tracing-channel-promise-run-stores.js",
+ "test-diagnostics-channel-tracing-channel-sync-error.js",
+ "test-diagnostics-channel-tracing-channel-sync.js",
"test-diagnostics-channel-udp.js",
"test-dns-lookup.js",
"test-dns-memory-error.js",
diff --git a/tests/node_compat/runner/TODO.md b/tests/node_compat/runner/TODO.md
index 0d5bcc626..e24c82b75 100644
--- a/tests/node_compat/runner/TODO.md
+++ b/tests/node_compat/runner/TODO.md
@@ -628,18 +628,11 @@ NOTE: This file should not be manually edited. Please edit `tests/node_compat/co
- [parallel/test-diagnostics-channel-http-server-start.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-diagnostics-channel-http-server-start.js)
- [parallel/test-diagnostics-channel-http.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-diagnostics-channel-http.js)
- [parallel/test-diagnostics-channel-memory-leak.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-diagnostics-channel-memory-leak.js)
-- [parallel/test-diagnostics-channel-net.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-diagnostics-channel-net.js)
- [parallel/test-diagnostics-channel-process.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-diagnostics-channel-process.js)
- [parallel/test-diagnostics-channel-safe-subscriber-errors.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-diagnostics-channel-safe-subscriber-errors.js)
-- [parallel/test-diagnostics-channel-sync-unsubscribe.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-diagnostics-channel-sync-unsubscribe.js)
-- [parallel/test-diagnostics-channel-tracing-channel-args-types.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-diagnostics-channel-tracing-channel-args-types.js)
- [parallel/test-diagnostics-channel-tracing-channel-async-error.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-diagnostics-channel-tracing-channel-async-error.js)
- [parallel/test-diagnostics-channel-tracing-channel-async.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-diagnostics-channel-tracing-channel-async.js)
-- [parallel/test-diagnostics-channel-tracing-channel-callback-run-stores.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-diagnostics-channel-tracing-channel-callback-run-stores.js)
-- [parallel/test-diagnostics-channel-tracing-channel-promise-run-stores.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-diagnostics-channel-tracing-channel-promise-run-stores.js)
- [parallel/test-diagnostics-channel-tracing-channel-run-stores.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-diagnostics-channel-tracing-channel-run-stores.js)
-- [parallel/test-diagnostics-channel-tracing-channel-sync-error.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-diagnostics-channel-tracing-channel-sync-error.js)
-- [parallel/test-diagnostics-channel-tracing-channel-sync.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-diagnostics-channel-tracing-channel-sync.js)
- [parallel/test-diagnostics-channel-worker-threads.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-diagnostics-channel-worker-threads.js)
- [parallel/test-directory-import.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-directory-import.js)
- [parallel/test-disable-proto-delete.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-disable-proto-delete.js)
diff --git a/tests/node_compat/test/parallel/test-diagnostics-channel-net.js b/tests/node_compat/test/parallel/test-diagnostics-channel-net.js
new file mode 100644
index 000000000..504c3e5dc
--- /dev/null
+++ b/tests/node_compat/test/parallel/test-diagnostics-channel-net.js
@@ -0,0 +1,32 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 18.12.1
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
+
+'use strict';
+const common = require('../common');
+const assert = require('assert');
+const net = require('net');
+const dc = require('diagnostics_channel');
+
+const isNetSocket = (socket) => socket instanceof net.Socket;
+
+dc.subscribe('net.client.socket', common.mustCall(({ socket }) => {
+ assert.strictEqual(isNetSocket(socket), true);
+}));
+
+dc.subscribe('net.server.socket', common.mustCall(({ socket }) => {
+ assert.strictEqual(isNetSocket(socket), true);
+}));
+
+const server = net.createServer(common.mustCall((socket) => {
+ socket.destroy();
+ server.close();
+}));
+
+server.listen(() => {
+ const { port } = server.address();
+ net.connect(port);
+});
diff --git a/tests/node_compat/test/parallel/test-diagnostics-channel-sync-unsubscribe.js b/tests/node_compat/test/parallel/test-diagnostics-channel-sync-unsubscribe.js
new file mode 100644
index 000000000..767382476
--- /dev/null
+++ b/tests/node_compat/test/parallel/test-diagnostics-channel-sync-unsubscribe.js
@@ -0,0 +1,21 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 18.12.1
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
+
+'use strict';
+
+const common = require('../common');
+const dc = require('node:diagnostics_channel');
+
+const channel_name = 'test:channel';
+const published_data = 'some message';
+
+const onMessageHandler = common.mustCall(() => dc.unsubscribe(channel_name, onMessageHandler));
+
+dc.subscribe(channel_name, onMessageHandler);
+
+// This must not throw.
+dc.channel(channel_name).publish(published_data);
diff --git a/tests/node_compat/test/parallel/test-diagnostics-channel-tracing-channel-args-types.js b/tests/node_compat/test/parallel/test-diagnostics-channel-tracing-channel-args-types.js
new file mode 100644
index 000000000..885c9d76b
--- /dev/null
+++ b/tests/node_compat/test/parallel/test-diagnostics-channel-tracing-channel-args-types.js
@@ -0,0 +1,46 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 18.12.1
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
+
+'use strict';
+
+require('../common');
+const dc = require('diagnostics_channel');
+const assert = require('assert');
+
+let channel;
+
+// tracingChannel creating with name
+channel = dc.tracingChannel('test');
+assert.strictEqual(channel.start.name, 'tracing:test:start');
+
+// tracingChannel creating with channels
+channel = dc.tracingChannel({
+ start: dc.channel('tracing:test:start'),
+ end: dc.channel('tracing:test:end'),
+ asyncStart: dc.channel('tracing:test:asyncStart'),
+ asyncEnd: dc.channel('tracing:test:asyncEnd'),
+ error: dc.channel('tracing:test:error'),
+});
+
+// tracingChannel creating without nameOrChannels must throw TypeError
+assert.throws(() => (channel = dc.tracingChannel(0)), {
+ code: 'ERR_INVALID_ARG_TYPE',
+ name: 'TypeError',
+ message:
+ /The "nameOrChannels" argument must be of type string or an instance of Channel or Object/,
+});
+
+// tracingChannel creating without instance of Channel must throw error
+assert.throws(() => (channel = dc.tracingChannel({ start: '' })), {
+ code: 'ERR_INVALID_ARG_TYPE',
+ message: /The "nameOrChannels\.start" property must be an instance of Channel/,
+});
+
+// tracingChannel creating with empty nameOrChannels must throw error
+assert.throws(() => (channel = dc.tracingChannel({})), {
+ message: /Cannot convert undefined or null to object/,
+});
diff --git a/tests/node_compat/test/parallel/test-diagnostics-channel-tracing-channel-callback-run-stores.js b/tests/node_compat/test/parallel/test-diagnostics-channel-tracing-channel-callback-run-stores.js
new file mode 100644
index 000000000..1160e6464
--- /dev/null
+++ b/tests/node_compat/test/parallel/test-diagnostics-channel-tracing-channel-callback-run-stores.js
@@ -0,0 +1,36 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 18.12.1
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
+
+'use strict';
+
+const common = require('../common');
+const { AsyncLocalStorage } = require('async_hooks');
+const dc = require('diagnostics_channel');
+const assert = require('assert');
+
+const channel = dc.tracingChannel('test');
+const store = new AsyncLocalStorage();
+
+const firstContext = { foo: 'bar' };
+const secondContext = { baz: 'buz' };
+
+channel.start.bindStore(store, common.mustCall(() => {
+ return firstContext;
+}));
+
+channel.asyncStart.bindStore(store, common.mustCall(() => {
+ return secondContext;
+}));
+
+assert.strictEqual(store.getStore(), undefined);
+channel.traceCallback(common.mustCall((cb) => {
+ assert.deepStrictEqual(store.getStore(), firstContext);
+ setImmediate(cb);
+}), 0, {}, null, common.mustCall(() => {
+ assert.deepStrictEqual(store.getStore(), secondContext);
+}));
+assert.strictEqual(store.getStore(), undefined);
diff --git a/tests/node_compat/test/parallel/test-diagnostics-channel-tracing-channel-promise-run-stores.js b/tests/node_compat/test/parallel/test-diagnostics-channel-tracing-channel-promise-run-stores.js
new file mode 100644
index 000000000..3f015e192
--- /dev/null
+++ b/tests/node_compat/test/parallel/test-diagnostics-channel-tracing-channel-promise-run-stores.js
@@ -0,0 +1,38 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 18.12.1
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
+
+'use strict';
+
+const common = require('../common');
+const { setTimeout } = require('node:timers/promises');
+const { AsyncLocalStorage } = require('async_hooks');
+const dc = require('diagnostics_channel');
+const assert = require('assert');
+
+const channel = dc.tracingChannel('test');
+const store = new AsyncLocalStorage();
+
+const firstContext = { foo: 'bar' };
+const secondContext = { baz: 'buz' };
+
+channel.start.bindStore(store, common.mustCall(() => {
+ return firstContext;
+}));
+
+channel.asyncStart.bindStore(store, common.mustNotCall(() => {
+ return secondContext;
+}));
+
+assert.strictEqual(store.getStore(), undefined);
+channel.tracePromise(common.mustCall(async () => {
+ assert.deepStrictEqual(store.getStore(), firstContext);
+ await setTimeout(1);
+ // Should _not_ switch to second context as promises don't have an "after"
+ // point at which to do a runStores.
+ assert.deepStrictEqual(store.getStore(), firstContext);
+}));
+assert.strictEqual(store.getStore(), undefined);
diff --git a/tests/node_compat/test/parallel/test-diagnostics-channel-tracing-channel-sync-error.js b/tests/node_compat/test/parallel/test-diagnostics-channel-tracing-channel-sync-error.js
new file mode 100644
index 000000000..09fc10329
--- /dev/null
+++ b/tests/node_compat/test/parallel/test-diagnostics-channel-tracing-channel-sync-error.js
@@ -0,0 +1,46 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 18.12.1
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
+
+'use strict';
+
+const common = require('../common');
+const dc = require('diagnostics_channel');
+const assert = require('assert');
+
+const channel = dc.tracingChannel('test');
+
+const expectedError = new Error('test');
+const input = { foo: 'bar' };
+const thisArg = { baz: 'buz' };
+
+function check(found) {
+ assert.deepStrictEqual(found, input);
+}
+
+const handlers = {
+ start: common.mustCall(check),
+ end: common.mustCall(check),
+ asyncStart: common.mustNotCall(),
+ asyncEnd: common.mustNotCall(),
+ error: common.mustCall((found) => {
+ check(found);
+ assert.deepStrictEqual(found.error, expectedError);
+ })
+};
+
+channel.subscribe(handlers);
+try {
+ channel.traceSync(function(err) {
+ assert.deepStrictEqual(this, thisArg);
+ assert.strictEqual(err, expectedError);
+ throw err;
+ }, input, thisArg, expectedError);
+
+ throw new Error('It should not reach this error');
+} catch (error) {
+ assert.deepStrictEqual(error, expectedError);
+}
diff --git a/tests/node_compat/test/parallel/test-diagnostics-channel-tracing-channel-sync.js b/tests/node_compat/test/parallel/test-diagnostics-channel-tracing-channel-sync.js
new file mode 100644
index 000000000..036bcce3b
--- /dev/null
+++ b/tests/node_compat/test/parallel/test-diagnostics-channel-tracing-channel-sync.js
@@ -0,0 +1,53 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 18.12.1
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
+
+'use strict';
+
+const common = require('../common');
+const dc = require('diagnostics_channel');
+const assert = require('assert');
+
+const channel = dc.tracingChannel('test');
+
+const expectedResult = { foo: 'bar' };
+const input = { foo: 'bar' };
+const thisArg = { baz: 'buz' };
+const arg = { baz: 'buz' };
+
+function check(found) {
+ assert.strictEqual(found, input);
+}
+
+const handlers = {
+ start: common.mustCall(check),
+ end: common.mustCall((found) => {
+ check(found);
+ assert.strictEqual(found.result, expectedResult);
+ }),
+ asyncStart: common.mustNotCall(),
+ asyncEnd: common.mustNotCall(),
+ error: common.mustNotCall()
+};
+
+assert.strictEqual(channel.start.hasSubscribers, false);
+channel.subscribe(handlers);
+assert.strictEqual(channel.start.hasSubscribers, true);
+const result1 = channel.traceSync(function(arg1) {
+ assert.strictEqual(arg1, arg);
+ assert.strictEqual(this, thisArg);
+ return expectedResult;
+}, input, thisArg, arg);
+assert.strictEqual(result1, expectedResult);
+
+channel.unsubscribe(handlers);
+assert.strictEqual(channel.start.hasSubscribers, false);
+const result2 = channel.traceSync(function(arg1) {
+ assert.strictEqual(arg1, arg);
+ assert.strictEqual(this, thisArg);
+ return expectedResult;
+}, input, thisArg, arg);
+assert.strictEqual(result2, expectedResult);
diff --git a/tools/core_import_map.json b/tools/core_import_map.json
index 421769e52..698d232a5 100644
--- a/tools/core_import_map.json
+++ b/tools/core_import_map.json
@@ -66,7 +66,7 @@
"node:constants": "../ext/node/polyfills/constants.ts",
"node:crypto": "../ext/node/polyfills/crypto.ts",
"node:dgram": "../ext/node/polyfills/dgram.ts",
- "node:diagnostics_channel": "../ext/node/polyfills/diagnostics_channel.ts",
+ "node:diagnostics_channel": "../ext/node/polyfills/diagnostics_channel.js",
"node:dns": "../ext/node/polyfills/dns.ts",
"node:dns/promises": "../ext/node/polyfills/dns/promises.ts",
"node:domain": "../ext/node/polyfills/domain.ts",