@@ -49,6 +49,16 @@ function getSuppressionsStorage() {
4949function withSuppressionsContext ( set , fn , thisArg , args ) {
5050 return getSuppressionsStorage ( ) . run ( set , fn . bind ( thisArg ) , ...args ) ;
5151}
52+ function validateBypassKey ( value , name ) {
53+ if ( value == null ) {
54+ throw new ERR_INVALID_ARG_TYPE ( name , [ 'object' , 'symbol' ] , value ) ;
55+ }
56+ const type = typeof value ;
57+ if ( type !== 'object' && type !== 'symbol' ) {
58+ throw new ERR_INVALID_ARG_TYPE ( name , [ 'object' , 'symbol' ] , value ) ;
59+ }
60+ }
61+
5262// Can't delete when weakref count reaches 0 as it could increment again.
5363// Only GC can be used as a valid time to clean up the channels map.
5464class WeakRefMap extends SafeMap {
@@ -83,17 +93,24 @@ class WeakRefMap extends SafeMap {
8393function markActive ( channel ) {
8494 // eslint-disable-next-line no-use-before-define
8595 ObjectSetPrototypeOf ( channel , ActiveChannel . prototype ) ;
86- channel . _subscribers = [ ] ;
87- channel . _stores = new SafeMap ( ) ;
96+ channel . _subscribers = [ ] ; // normal — plain functions
97+ channel . _bypassSubscribers = null ; // lazy — only if subscriberId used
98+ channel . _stores = new SafeMap ( ) ; // normal — plain transforms
99+ channel . _bypassStores = null ; // lazy — only if subscriberId used
88100}
89101
90102function maybeMarkInactive ( channel ) {
91103 // When there are no more active subscribers or bound, restore to fast prototype.
92- if ( ! channel . _subscribers . length && ! channel . _stores . size ) {
104+ if ( ! channel . _subscribers . length &&
105+ ! channel . _stores . size &&
106+ ! channel . _bypassSubscribers ?. length &&
107+ ! channel . _bypassStores ?. size ) {
93108 // eslint-disable-next-line no-use-before-define
94109 ObjectSetPrototypeOf ( channel , Channel . prototype ) ;
95110 channel . _subscribers = undefined ;
96111 channel . _stores = undefined ;
112+ channel . _bypassSubscribers = undefined ;
113+ channel . _bypassStores = undefined ;
97114 }
98115}
99116
@@ -104,19 +121,33 @@ class RunStoresScope {
104121 // eslint-disable-next-line no-restricted-globals
105122 using stack = new DisposableStack ( ) ;
106123
107- // Enter stores using withScope
124+ // Normal stores — exactly as before, zero extra cost
108125 if ( activeChannel . _stores ) {
109- const activeKeys = getSuppressionsStorage ( ) . getStore ( ) ;
110126 for ( const entry of activeChannel . _stores . entries ( ) ) {
111127 const store = entry [ 0 ] ;
112- const { transform, subscriberId = null } = entry [ 1 ] ;
113-
114- // Skip this bound store if it opted into suppression and its key
115- // is active in the current async context.
116- if ( subscriberId !== null && activeKeys ?. has ( subscriberId ) ) {
117- continue ;
128+ const transform = entry [ 1 ] ;
129+ let newContext = data ;
130+ if ( transform ) {
131+ try {
132+ newContext = transform ( data ) ;
133+ } catch ( err ) {
134+ process . nextTick ( ( ) => {
135+ triggerUncaughtException ( err , false ) ;
136+ } ) ;
137+ continue ;
138+ }
118139 }
140+ stack . use ( store . withScope ( newContext ) ) ;
141+ }
142+ }
119143
144+ // Bypass stores — only entered if bypass stores exist
145+ if ( activeChannel . _bypassStores ) {
146+ const activeKeys = getSuppressionsStorage ( ) . getStore ( ) ;
147+ for ( const entry of activeChannel . _bypassStores . entries ( ) ) {
148+ const store = entry [ 0 ] ;
149+ const { transform, subscriberId } = entry [ 1 ] ;
150+ if ( activeKeys ?. has ( subscriberId ) ) continue ;
120151 let newContext = data ;
121152 if ( transform ) {
122153 try {
@@ -128,7 +159,6 @@ class RunStoresScope {
128159 continue ;
129160 }
130161 }
131-
132162 stack . use ( store . withScope ( newContext ) ) ;
133163 }
134164 }
@@ -149,95 +179,156 @@ class RunStoresScope {
149179class ActiveChannel {
150180 subscribe ( subscription , options = { } ) {
151181 validateFunction ( subscription , 'subscription' ) ;
152- const subscriberId = options && options . subscriberId !== undefined ? options . subscriberId : null ;
153- if ( subscriberId !== null ) {
154- const t = typeof subscriberId ;
155- if ( t === 'string' || t === 'number' || t === 'bigint' || t === 'boolean' ) {
156- throw new ERR_INVALID_ARG_TYPE ( 'subscriberId' , [ 'object' , 'symbol' , 'function' ] , subscriberId ) ;
182+ const subscriberId = options ?. subscriberId ;
183+
184+ if ( subscriberId !== undefined ) {
185+ validateBypassKey ( subscriberId , 'subscriberId' ) ;
186+ // Bypass path — lazy separate array
187+ if ( this . _bypassSubscribers === null ) {
188+ this . _bypassSubscribers = [ ] ;
157189 }
190+ this . _bypassSubscribers = ArrayPrototypeSlice ( this . _bypassSubscribers ) ;
191+ ArrayPrototypePush ( this . _bypassSubscribers , { handler : subscription , subscriberId } ) ;
192+ } else {
193+ // Normal path — plain function, zero extra cost
194+ this . _subscribers = ArrayPrototypeSlice ( this . _subscribers ) ;
195+ ArrayPrototypePush ( this . _subscribers , subscription ) ;
158196 }
159197
160- const handler = subscription ;
161- this . _subscribers = ArrayPrototypeSlice ( this . _subscribers ) ;
162- ArrayPrototypePush ( this . _subscribers , { handler, subscriberId } ) ;
163198 channels . incRef ( this . name ) ;
164199 if ( this . _index !== undefined ) subscriberCounts [ this . _index ] ++ ;
165200 }
166201
167202 unsubscribe ( subscription ) {
168- // Find subscriber entry by handler identity.
203+ // Check normal subscribers first
169204 let index = - 1 ;
170- for ( let i = 0 ; i < ( this . _subscribers ? .length || 0 ) ; i ++ ) {
171- if ( this . _subscribers [ i ] . handler === subscription ) {
205+ for ( let i = 0 ; i < this . _subscribers . length ; i ++ ) {
206+ if ( this . _subscribers [ i ] === subscription ) {
172207 index = i ;
173208 break ;
174209 }
175210 }
176- if ( index === - 1 ) return false ;
177211
178- const before = ArrayPrototypeSlice ( this . _subscribers , 0 , index ) ;
179- const after = ArrayPrototypeSlice ( this . _subscribers , index + 1 ) ;
180- this . _subscribers = before ;
181- ArrayPrototypePushApply ( this . _subscribers , after ) ;
212+ if ( index !== - 1 ) {
213+ const before = ArrayPrototypeSlice ( this . _subscribers , 0 , index ) ;
214+ const after = ArrayPrototypeSlice ( this . _subscribers , index + 1 ) ;
215+ this . _subscribers = before ;
216+ ArrayPrototypePushApply ( this . _subscribers , after ) ;
217+ channels . decRef ( this . name ) ;
218+ if ( this . _index !== undefined ) subscriberCounts [ this . _index ] -- ;
219+ maybeMarkInactive ( this ) ;
220+ return true ;
221+ }
182222
183- channels . decRef ( this . name ) ;
184- if ( this . _index !== undefined ) subscriberCounts [ this . _index ] -- ;
185- maybeMarkInactive ( this ) ;
223+ // Check bypass subscribers
224+ if ( this . _bypassSubscribers !== null ) {
225+ let bypassIndex = - 1 ;
226+ for ( let i = 0 ; i < this . _bypassSubscribers . length ; i ++ ) {
227+ if ( this . _bypassSubscribers [ i ] . handler === subscription ) {
228+ bypassIndex = i ;
229+ break ;
230+ }
231+ }
232+ if ( bypassIndex !== - 1 ) {
233+ const before = ArrayPrototypeSlice ( this . _bypassSubscribers , 0 , bypassIndex ) ;
234+ const after = ArrayPrototypeSlice ( this . _bypassSubscribers , bypassIndex + 1 ) ;
235+ this . _bypassSubscribers = before ;
236+ ArrayPrototypePushApply ( this . _bypassSubscribers , after ) ;
237+ if ( this . _bypassSubscribers . length === 0 ) {
238+ this . _bypassSubscribers = null ;
239+ }
240+ channels . decRef ( this . name ) ;
241+ if ( this . _index !== undefined ) subscriberCounts [ this . _index ] -- ;
242+ maybeMarkInactive ( this ) ;
243+ return true ;
244+ }
245+ }
186246
187- return true ;
247+ return false ;
188248 }
189249
190250 bindStore ( store , transform , options = { } ) {
191- const subscriberId = options && options . subscriberId !== undefined ? options . subscriberId : null ;
192- if ( subscriberId !== null ) {
193- const t = typeof subscriberId ;
194- if ( t === 'string' || t === 'number' || t === 'bigint' || t === 'boolean' ) {
195- throw new ERR_INVALID_ARG_TYPE ( 'subscriberId' , [ 'object' , 'symbol' , 'function' ] , subscriberId ) ;
196- }
197- }
251+ const subscriberId = options ?. subscriberId ;
198252
199- const replacing = this . _stores . has ( store ) ;
200- if ( ! replacing ) {
201- channels . incRef ( this . name ) ;
202- if ( this . _index !== undefined ) subscriberCounts [ this . _index ] ++ ;
253+ if ( subscriberId !== undefined ) {
254+ validateBypassKey ( subscriberId , 'subscriberId' ) ;
255+ // Bypass path — lazy separate SafeMap
256+ if ( this . _bypassStores === null ) {
257+ this . _bypassStores = new SafeMap ( ) ;
258+ }
259+ const replacing = this . _bypassStores . has ( store ) ;
260+ if ( ! replacing ) {
261+ channels . incRef ( this . name ) ;
262+ if ( this . _index !== undefined ) subscriberCounts [ this . _index ] ++ ;
263+ }
264+ this . _bypassStores . set ( store , { transform, subscriberId } ) ;
265+ } else {
266+ // Normal path — plain transform, zero extra cost
267+ const replacing = this . _stores . has ( store ) ;
268+ if ( ! replacing ) {
269+ channels . incRef ( this . name ) ;
270+ if ( this . _index !== undefined ) subscriberCounts [ this . _index ] ++ ;
271+ }
272+ this . _stores . set ( store , transform ) ;
203273 }
204- this . _stores . set ( store , { transform, subscriberId } ) ;
205274 }
206275
207276 unbindStore ( store ) {
208- if ( ! this . _stores . has ( store ) ) {
209- return false ;
277+ if ( this . _stores . has ( store ) ) {
278+ this . _stores . delete ( store ) ;
279+ channels . decRef ( this . name ) ;
280+ if ( this . _index !== undefined ) subscriberCounts [ this . _index ] -- ;
281+ maybeMarkInactive ( this ) ;
282+ return true ;
210283 }
211284
212- this . _stores . delete ( store ) ;
213-
214- channels . decRef ( this . name ) ;
215- if ( this . _index !== undefined ) subscriberCounts [ this . _index ] -- ;
216- maybeMarkInactive ( this ) ;
285+ if ( this . _bypassStores ?. has ( store ) ) {
286+ this . _bypassStores . delete ( store ) ;
287+ if ( this . _bypassStores . size === 0 ) {
288+ this . _bypassStores = null ;
289+ }
290+ channels . decRef ( this . name ) ;
291+ if ( this . _index !== undefined ) subscriberCounts [ this . _index ] -- ;
292+ maybeMarkInactive ( this ) ;
293+ return true ;
294+ }
217295
218- return true ;
296+ return false ;
219297 }
220298
221299 get hasSubscribers ( ) {
222300 return true ;
223301 }
224302
225303 publish ( data ) {
304+ // Normal path — no ALS lookup, plain function call, zero overhead
226305 const subscribers = this . _subscribers ;
227- const activeKeys = getSuppressionsStorage ( ) . getStore ( ) ;
228- for ( let i = 0 ; i < ( subscribers ?. length || 0 ) ; i ++ ) {
306+ for ( let i = 0 ; i < subscribers . length ; i ++ ) {
229307 try {
230- const { handler, subscriberId = null } = subscribers [ i ] ;
231- if ( subscriberId !== null && activeKeys ?. has ( subscriberId ) ) {
232- continue ;
233- }
234- handler ( data , this . name ) ;
308+ subscribers [ i ] ( data , this . name ) ;
235309 } catch ( err ) {
236310 process . nextTick ( ( ) => {
237311 triggerUncaughtException ( err , false ) ;
238312 } ) ;
239313 }
240314 }
315+
316+ // Bypass path — only entered if bypass subscribers exist
317+ if ( this . _bypassSubscribers !== null ) {
318+ const activeKeys = getSuppressionsStorage ( ) . getStore ( ) ;
319+ const bypassSubscribers = this . _bypassSubscribers ;
320+ for ( let i = 0 ; i < bypassSubscribers . length ; i ++ ) {
321+ try {
322+ const { handler, subscriberId } = bypassSubscribers [ i ] ;
323+ if ( activeKeys ?. has ( subscriberId ) ) continue ;
324+ handler ( data , this . name ) ;
325+ } catch ( err ) {
326+ process . nextTick ( ( ) => {
327+ triggerUncaughtException ( err , false ) ;
328+ } ) ;
329+ }
330+ }
331+ }
241332 }
242333
243334 withStoreScope ( data ) {
@@ -684,13 +775,7 @@ dc_binding.linkNativeChannel((name) => channel(name));
684775function suppressed ( key , fn , thisArg , ...args ) {
685776 validateFunction ( fn , 'fn' ) ;
686777
687- if ( key === null || key === undefined ) {
688- throw new ERR_INVALID_ARG_TYPE ( 'key' , [ 'object' , 'symbol' , 'function' ] , key ) ;
689- }
690- const t = typeof key ;
691- if ( t === 'string' || t === 'number' || t === 'bigint' || t === 'boolean' ) {
692- throw new ERR_INVALID_ARG_TYPE ( 'key' , [ 'object' , 'symbol' , 'function' ] , key ) ;
693- }
778+ validateBypassKey ( key , 'key' ) ;
694779
695780 const currentSet = getSuppressionsStorage ( ) . getStore ( ) ;
696781 const next = currentSet ? new SafeSet ( currentSet ) : new SafeSet ( ) ;
0 commit comments