fix(ext/node): better support for node:diagnostics_channel module (#24088)

Closes https://github.com/denoland/deno/issues/24060
This commit is contained in:
Bartek Iwańczuk 2024-06-14 23:07:02 +01:00 committed by GitHub
parent 184a85eaec
commit 78b12a43b3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 749 additions and 106 deletions

View File

@ -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",

View File

@ -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,
};

View File

@ -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,
};

View File

@ -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)}`);
}

View File

@ -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 {

View File

@ -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",

View File

@ -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)

View File

@ -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);
});

View File

@ -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);

View File

@ -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/,
});

View File

@ -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);

View File

@ -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);

View File

@ -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);
}

View File

@ -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);

View File

@ -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",