From 69fec358ca3cfd51a1bf424d4e0b5d646892b173 Mon Sep 17 00:00:00 2001 From: Thomas Hunter II Date: Tue, 7 May 2024 13:17:34 -0700 Subject: [PATCH] add support for TracingChannel#hasSubscribers --- .github/workflows/pull-request.yml | 10 +++- README.md | 2 + checks.js | 9 +++ dc-polyfill.js | 4 ++ patch-tracing-channel-has-subscribers.js | 39 +++++++++++++ ...el-tracing-channel-has-subscribers.spec.js | 55 +++++++++++++++++++ 6 files changed, 116 insertions(+), 3 deletions(-) create mode 100644 patch-tracing-channel-has-subscribers.js create mode 100644 test/test-diagnostics-channel-tracing-channel-has-subscribers.spec.js diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index dd28012..6d6fc4d 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -79,9 +79,13 @@ jobs: - 20.0.0 # first 20 - 20.5.1 # sync unsubscribe bug - 20.6.0 # sync unsubscribe bug fixed - - 20.x # nothing special, full support dc, latest 20 - - 21.0.0 # nothing special, full support dc, first 21 - - 21.x # nothing special, full support dc, latest 21 + - 20.x # nothing special, latest 20 + - 20.12.x # last version without TC early exit and TC#hasSubscribers() + - 20.13.0 # introduces TC early exit and TC#hasSubscribers() + - 21.0.0 # nothing special, first 21 + - 21.x # nothing special, latest 21 + - 22.0.0 # introduces TC early exit and TC#hasSubscribers() + - 22.x # nothing special, full support DC steps: - uses: actions/checkout@v2 - uses: actions/setup-node@v1 diff --git a/README.md b/README.md index b7a414b..90bea4e 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,8 @@ Since this package recreates a Node.js API, read the [Node.js `diagnostics_chann | Oldest Supported Node.js Version | 12.17.0 | | Target Node.js DC API Version | 20.6.0 | +> Note that `dc-polyfill` currently has the `TracingChannel#hasSubscribers` getter backported from Node.js v22 however it doesn't yet support the tracing channel early exit feature. Once that's been added we'll delete this clause and update the above table. + Whenever the currently running version of Node.js ships with `diagnostics_channel` (i.e. v16+, v15.14+, v14.17+), **dc-polyfill** will make sure to use the global registry of channels provided by the core module. For older versions of Node.js **dc-polyfill** instead uses its own global collection of channels. This global collection remains in the same location and is shared across all instances of **dc-polyfill**. This avoids the issue wherein multiple versions of an npm library installed in a module dependency hierarchy would otherwise provide different singleton instances. Ideally, this package will forever remain backwards compatible, there will never be a v2.x release, and there will never be an additional global channel collection. diff --git a/checks.js b/checks.js index 247ce37..3c30daf 100644 --- a/checks.js +++ b/checks.js @@ -56,3 +56,12 @@ function hasSyncUnsubscribeBug() { return MAJOR === 20 && MINOR <= 5; } module.exports.hasSyncUnsubscribeBug = hasSyncUnsubscribeBug; + +// if there is a TracingChannel#hasSubscribers() getter +// @see https://github.com/nodejs/node/pull/51915 +// TODO: note that we still need to add the TC early exit from this same version +function hasTracingChannelHasSubscribers() { + return MAJOR >= 22 + || (MAJOR == 20 && MINOR >= 13); +}; +module.exports.hasTracingChannelHasSubscribers = hasTracingChannelHasSubscribers; diff --git a/dc-polyfill.js b/dc-polyfill.js index 0ce1ebf..6c3f130 100644 --- a/dc-polyfill.js +++ b/dc-polyfill.js @@ -30,5 +30,9 @@ if (checks.hasSyncUnsubscribeBug()) { dc = require('./patch-sync-unsubscribe-bug.js')(dc); } +if (!checks.hasTracingChannelHasSubscribers()) { + dc = require('./patch-tracing-channel-has-subscribers.js')(dc); +} + module.exports = dc; diff --git a/patch-tracing-channel-has-subscribers.js b/patch-tracing-channel-has-subscribers.js new file mode 100644 index 0000000..d2fadae --- /dev/null +++ b/patch-tracing-channel-has-subscribers.js @@ -0,0 +1,39 @@ +const { + ObjectDefineProperty, + ObjectGetPrototypeOf, +} = require('./primordials.js'); + +const { ERR_INVALID_ARG_TYPE } = require('./errors.js'); + +const traceEvents = [ + 'start', + 'end', + 'asyncStart', + 'asyncEnd', + 'error', +]; + +module.exports = function (unpatched) { + const { channel } = unpatched; + + const dc = { ...unpatched }; + + { + const fauxTrCh = dc.tracingChannel('dc-polyfill-faux'); + + const TracingChannel = ObjectGetPrototypeOf(fauxTrCh); + + ObjectDefineProperty(TracingChannel, 'hasSubscribers', { + get: function () { + return this.start.hasSubscribers + || this.end.hasSubscribers + || this.asyncStart.hasSubscribers + || this.asyncEnd.hasSubscribers + || this.error.hasSubscribers; + }, + configurable: true + }); + } + + return dc; +}; diff --git a/test/test-diagnostics-channel-tracing-channel-has-subscribers.spec.js b/test/test-diagnostics-channel-tracing-channel-has-subscribers.spec.js new file mode 100644 index 0000000..e36dfc4 --- /dev/null +++ b/test/test-diagnostics-channel-tracing-channel-has-subscribers.spec.js @@ -0,0 +1,55 @@ +'use strict'; + +const test = require('tape'); +const common = require('./common.js'); +const dc = require('../dc-polyfill.js'); + +test('test-diagnostics-channel-tracing-has-subscribers', (t) => { + t.plan(10); + + const handler = common.mustNotCall(); + + { + const handlers = { + start: common.mustNotCall() + }; + + const channel = dc.tracingChannel('test'); + + t.strictEqual(channel.hasSubscribers, false); + + channel.subscribe(handlers); + t.strictEqual(channel.hasSubscribers, true); + + channel.unsubscribe(handlers); + t.strictEqual(channel.hasSubscribers, false); + + channel.start.subscribe(handler); + t.strictEqual(channel.hasSubscribers, true); + + channel.start.unsubscribe(handler); + t.strictEqual(channel.hasSubscribers, false); + } + + { + const handlers = { + asyncEnd: common.mustNotCall() + }; + + const channel = dc.tracingChannel('test'); + + t.strictEqual(channel.hasSubscribers, false); + + channel.subscribe(handlers); + t.strictEqual(channel.hasSubscribers, true); + + channel.unsubscribe(handlers); + t.strictEqual(channel.hasSubscribers, false); + + channel.asyncEnd.subscribe(handler); + t.strictEqual(channel.hasSubscribers, true); + + channel.asyncEnd.unsubscribe(handler); + t.strictEqual(channel.hasSubscribers, false); + } +});