@@ -48,6 +48,7 @@ const { structuredClone } = require('internal/worker/js_transferable');
4848const { bigint : hrtime } = process . hrtime ;
4949const kDefaultSamples = 30 ;
5050const kDefaultWarmup = 0 ;
51+ const kEmptyDiagnosticChannels = ObjectFreeze ( [ ] ) ;
5152const kEmptyNamePath = ObjectFreeze ( [ ] ) ;
5253const kEmptyParams = ObjectFreeze ( { __proto__ : null } ) ;
5354const kEmptyTags = ObjectFreeze ( [ ] ) ;
@@ -82,6 +83,33 @@ function canonicalizeTags(tags, parentTags = kEmptyTags) {
8283 return ObjectFreeze ( result ) ;
8384}
8485
86+ function canonicalizeDiagnosticChannels (
87+ diagnosticChannels ,
88+ parentDiagnosticChannels = kEmptyDiagnosticChannels ,
89+ ) {
90+ if ( diagnosticChannels === undefined ) return parentDiagnosticChannels ;
91+ if ( ! ArrayIsArray ( diagnosticChannels ) ) {
92+ throw new ERR_INVALID_ARG_TYPE (
93+ 'options.diagnosticChannels' , 'Array' , diagnosticChannels ) ;
94+ }
95+
96+ const result = ArrayPrototypeSlice ( parentDiagnosticChannels ) ;
97+ const seen = new SafeSet ( parentDiagnosticChannels ) ;
98+ for ( let i = 0 ; i < diagnosticChannels . length ; i ++ ) {
99+ const name = diagnosticChannels [ i ] ;
100+ if ( typeof name === 'symbol' ) continue ;
101+ if ( typeof name !== 'string' ) {
102+ throw new ERR_INVALID_ARG_TYPE (
103+ `options.diagnosticChannels[${ i } ]` , [ 'string' , 'symbol' ] , name ) ;
104+ }
105+ if ( ! seen . has ( name ) ) {
106+ seen . add ( name ) ;
107+ ArrayPrototypePush ( result , name ) ;
108+ }
109+ }
110+ return ObjectFreeze ( result ) ;
111+ }
112+
85113function canonicalizeParams ( params ) {
86114 if ( params === undefined ) return kEmptyParams ;
87115 validateObject ( params , 'options.params' ) ;
@@ -106,15 +134,17 @@ function canonicalizeParams(params) {
106134 return ObjectFreeze ( result ) ;
107135}
108136
109- function validateNodeOptions ( options , parentTags ) {
137+ function validateNodeOptions ( options , parentTags , parentDiagnosticChannels ) {
110138 validateObject ( options , 'options' ) ;
111- const { only = false , skip, tags } = options ;
139+ const { diagnosticChannels , only = false , skip, tags } = options ;
112140 if ( typeof only !== 'boolean' ) {
113141 throw new ERR_INVALID_ARG_TYPE ( 'options.only' , 'boolean' , only ) ;
114142 }
115143 validateSkip ( skip ) ;
116144 return {
117145 __proto__ : null ,
146+ diagnosticChannels : canonicalizeDiagnosticChannels (
147+ diagnosticChannels , parentDiagnosticChannels ) ,
118148 only,
119149 skip,
120150 tags : canonicalizeTags ( tags , parentTags ) ,
@@ -157,7 +187,10 @@ class Suite extends AsyncResource {
157187 constructor ( harness , parent , name , options , fn , loc , isRoot = false ) {
158188 super ( 'BenchSuite' ) ;
159189 const validated = validateNodeOptions (
160- options , parent ?. tags ?? kEmptyTags ) ;
190+ options ,
191+ parent ?. tags ?? kEmptyTags ,
192+ parent ?. diagnosticChannels ?? kEmptyDiagnosticChannels ,
193+ ) ;
161194
162195 this . harness = harness ;
163196 this . parent = parent ;
@@ -174,6 +207,7 @@ class Suite extends AsyncResource {
174207 this . namePath ,
175208 ] ) ;
176209 this . parentId = isRoot || parent . isRoot ? null : parent . suiteId ;
210+ this . diagnosticChannels = validated . diagnosticChannels ;
177211 this . only = validated . only ;
178212 this . skip = validated . skip ;
179213 this . tags = validated . tags ;
@@ -195,7 +229,8 @@ class Suite extends AsyncResource {
195229class Bench extends AsyncResource {
196230 constructor ( harness , parent , name , options , fn , loc ) {
197231 super ( 'Benchmark' ) ;
198- const validated = validateNodeOptions ( options , parent . tags ) ;
232+ const validated = validateNodeOptions (
233+ options , parent . tags , parent . diagnosticChannels ) ;
199234 const {
200235 params,
201236 samples = kDefaultSamples ,
@@ -216,6 +251,7 @@ class Bench extends AsyncResource {
216251 this . name = name ;
217252 this . fn = fn ;
218253 this . loc = createLocation ( loc , harness . entryFile ) ;
254+ this . diagnosticChannels = validated . diagnosticChannels ;
219255 this . only = validated . only ;
220256 this . skip = validated . skip ;
221257 this . tags = validated . tags ;
@@ -275,15 +311,15 @@ class BenchContext {
275311 throw new ERR_INVALID_STATE ( 'benchmark sample is no longer active' ) ;
276312 }
277313 try {
278- validateString ( message , 'message' ) ;
314+ const clonedMessage = structuredClone ( message ) ;
279315 validateObject ( options , 'options' ) ;
280316 const { detail, level = 'info' } = options ;
281317 validateString ( level , 'options.level' ) ;
282318 if ( level !== 'info' && level !== 'warning' ) {
283319 throw new ERR_INVALID_ARG_VALUE (
284320 'options.level' , level , "must be 'info' or 'warning'" ) ;
285321 }
286- const diagnostic = { __proto__ : null , level, message } ;
322+ const diagnostic = { __proto__ : null , level, message : clonedMessage } ;
287323 if ( detail !== undefined ) diagnostic . detail = structuredClone ( detail ) ;
288324 this . #onDiagnostic( diagnostic ) ;
289325 } catch ( error ) {
0 commit comments