@aidra/pmx-io 0.0.0-stage → 9.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/README.md +69 -2
  2. package/bin/pmx-io-node.js +143 -0
  3. package/index.d.ts +381 -0
  4. package/index.js +27 -0
  5. package/index.mjs +47 -0
  6. package/lib/actions.js +120 -0
  7. package/lib/activate.js +402 -0
  8. package/lib/application.js +106 -0
  9. package/lib/bun.js +155 -0
  10. package/lib/config.js +67 -0
  11. package/lib/constants.js +17 -0
  12. package/lib/debug.js +48 -0
  13. package/lib/esm-hooks.mjs +111 -0
  14. package/lib/exit.js +79 -0
  15. package/lib/features/metrics.js +409 -0
  16. package/lib/features/notify.js +358 -0
  17. package/lib/features/profiling.js +208 -0
  18. package/lib/hooks.js +151 -0
  19. package/lib/inbound.js +174 -0
  20. package/lib/integrations.js +372 -0
  21. package/lib/itg/amqplib.js +169 -0
  22. package/lib/itg/bullmq.js +141 -0
  23. package/lib/itg/common.js +657 -0
  24. package/lib/itg/dbcomment.js +114 -0
  25. package/lib/itg/index.js +103 -0
  26. package/lib/itg/ioredis.js +212 -0
  27. package/lib/itg/kafkajs.js +205 -0
  28. package/lib/itg/mongodb.js +322 -0
  29. package/lib/itg/mysql2.js +205 -0
  30. package/lib/itg/nats.js +531 -0
  31. package/lib/itg/noderedis.js +236 -0
  32. package/lib/itg/pg.js +253 -0
  33. package/lib/itg/pgqueue.js +305 -0
  34. package/lib/itg/postgres.js +252 -0
  35. package/lib/itg/redis-common.js +265 -0
  36. package/lib/itg/sql.js +302 -0
  37. package/lib/metrics/service.js +286 -0
  38. package/lib/metrics/types.js +342 -0
  39. package/lib/modules.js +260 -0
  40. package/lib/node-options.js +62 -0
  41. package/lib/otel/bridge.js +797 -0
  42. package/lib/otel/env.js +114 -0
  43. package/lib/outbound.js +362 -0
  44. package/lib/pmx.js +757 -0
  45. package/lib/relay/hub.js +279 -0
  46. package/lib/relay/transport.js +253 -0
  47. package/lib/standalone/activate.js +307 -0
  48. package/lib/standalone/capture.js +55 -0
  49. package/lib/standalone/container.js +909 -0
  50. package/lib/standalone/transport.js +1023 -0
  51. package/lib/tracing/context.js +90 -0
  52. package/lib/tracing/control.js +124 -0
  53. package/lib/tracing/exporter.js +115 -0
  54. package/lib/tracing/index.js +242 -0
  55. package/lib/tracing/logs.js +176 -0
  56. package/lib/tracing/options.js +105 -0
  57. package/lib/tracing/paths.js +34 -0
  58. package/lib/tracing/tracer.js +850 -0
  59. package/lib/tracing/w3c.js +415 -0
  60. package/lib/transport/ipc.js +97 -0
  61. package/lib/transport/ws.js +511 -0
  62. package/lib/util.js +109 -0
  63. package/package.json +48 -4
  64. package/preload.cjs +26 -0
  65. package/preload.mjs +25 -0
  66. package/standalone.cjs +16 -0
  67. package/standalone.mjs +17 -0
@@ -0,0 +1,657 @@
1
+ // Copyright (c) 2026 Mike Warriner. All rights reserved. Proprietary — see LICENSE.
2
+ 'use strict'
3
+
4
+ // Common rules of the database and broker integrations (82 §1, §2, §4; the Node counterpart of
5
+ // pmx-io's _msg.py / _db.py helpers): switches, CLIENT/PRODUCER/CONSUMER/receive span creation
6
+ // and parenting, error marking, promise/callback completion, header carriers, links for batches,
7
+ // iterator-style consumer spans and the exit flush of open iterator spans. The per-library modules
8
+ // (lib/itg/<library>.js) only map library calls onto these helpers.
9
+ //
10
+ // Everything here is defensive: a failure in pmx code is logged under `axm:tracing` and never
11
+ // reaches the app (ITG-003). Loaded only when an instrumented library is loaded with tracing on.
12
+
13
+ const ctx = require('../tracing/context')
14
+ const w3c = require('../tracing/w3c')
15
+ const debug = require('../debug')('axm:tracing')
16
+
17
+ const MODULES = Symbol.for('@pmx/io:modules')
18
+ const ERROR_MAX = 500
19
+ const TRACE_KEY = '__pmx_trace__' // pmx payload carrier (ITG-220)
20
+ const WRAPPED = Symbol.for('@pmx/io:itg-wrapped')
21
+ const ORIGINAL = Symbol.for('@pmx/io:itg-original')
22
+
23
+ // ---------------------------------------------------------------------------------------------
24
+ // switches (ITG-001)
25
+
26
+ /** The tracer while tracing is enabled, else null (wrappers then pass straight through). */
27
+ function tracer () {
28
+ const r = globalThis[MODULES]
29
+ if (!r || !r.wants.tracing) return null
30
+ const f = r.tracing
31
+ if (!f || !f._enabled) return null
32
+ const t = f._tracer
33
+ return t && t.enabled ? t : null
34
+ }
35
+
36
+ /** The tracer when database spans are wanted now, else null. */
37
+ function dbTracer () {
38
+ const t = tracer()
39
+ return t && t.db ? t : null
40
+ }
41
+
42
+ /** The tracer when broker spans are wanted now, else null. */
43
+ function msgTracer () {
44
+ const t = tracer()
45
+ return t && t.messaging ? t : null
46
+ }
47
+
48
+ /** The current span (a span of this process or, in bridge mode, of the app's SDK), or undefined. */
49
+ function currentSpan () {
50
+ const s = ctx.current()
51
+ return s ? s.span : undefined
52
+ }
53
+
54
+ // One logical operation, one span (ITG-005): an outer wrapper that created a span runs the inner
55
+ // layers with `depth` raised (synchronous nesting); asynchronous nesting is marked per library on
56
+ // the objects that pass between the layers.
57
+ let depth = 0
58
+
59
+ function nested () {
60
+ return depth > 0
61
+ }
62
+
63
+ /** Call fn with the nesting marker raised (inner wrappers then do nothing). */
64
+ function outer (fn, thisArg, args) {
65
+ depth++
66
+ try {
67
+ return fn.apply(thisArg, args)
68
+ } finally {
69
+ depth--
70
+ }
71
+ }
72
+
73
+ function disabled (lib, why) {
74
+ debug('%s: instrumentation disabled (%s)', lib, why)
75
+ }
76
+
77
+ // ---------------------------------------------------------------------------------------------
78
+ // spans
79
+
80
+ /** A CLIENT span under the current span, or null (no root CLIENT spans, ITG-010). */
81
+ function clientSpan (t, name, tags, kind) {
82
+ const parent = currentSpan()
83
+ if (!parent) return null
84
+ try {
85
+ return t.childOf(parent, name, kind || 'CLIENT', tags)
86
+ } catch (err) {
87
+ debug('span failed: %s', err && err.message)
88
+ return null
89
+ }
90
+ }
91
+
92
+ function msgTags (system, destination, operation, extra) {
93
+ const tags = { 'messaging.system': system }
94
+ if (destination) tags['messaging.destination.name'] = destination
95
+ if (operation) tags['messaging.operation'] = operation
96
+ if (extra) {
97
+ for (const k of Object.keys(extra)) {
98
+ if (extra[k] !== undefined && extra[k] !== null) tags[k] = extra[k]
99
+ }
100
+ }
101
+ return tags
102
+ }
103
+
104
+ /**
105
+ * ITG-020/021: `publish <destination>` (`request <destination>` for request/reply, kind CLIENT),
106
+ * a child of the current span; without one only with `messaging.rootProducers`. null when no span
107
+ * is to be created (nothing is injected then).
108
+ */
109
+ function producer (system, destination, tags, kind, verb) {
110
+ const t = msgTracer()
111
+ if (!t) return null
112
+ kind = kind || 'PRODUCER'
113
+ try {
114
+ const name = (verb || 'publish') + ' ' + (destination || 'multiple')
115
+ const attrs = msgTags(system, destination, kind === 'PRODUCER' ? 'publish' : undefined, tags)
116
+ const parent = currentSpan()
117
+ if (parent) return t.childOf(parent, name, kind, attrs)
118
+ if (t.messaging.rootProducers) return t.startSpan(name, { kind, attributes: attrs, parent: w3c.NEW_TRACE })
119
+ } catch (err) {
120
+ debug('producer span failed: %s', err && err.message)
121
+ }
122
+ return null
123
+ }
124
+
125
+ /**
126
+ * ITG-021: a CONSUMER `process <destination>` entry span; parent = the message's context, else
127
+ * the current span, else a new trace.
128
+ */
129
+ function consumer (system, destination, context, tags) {
130
+ const t = msgTracer()
131
+ if (!t) return null
132
+ try {
133
+ const name = 'process ' + (destination || 'multiple')
134
+ const attrs = msgTags(system, destination, 'process', tags)
135
+ if (w3c.isSpanContext(context)) return t.startSpan(name, { kind: 'CONSUMER', attributes: attrs, parent: context })
136
+ const cur = currentSpan()
137
+ return t.startSpan(name, { kind: 'CONSUMER', attributes: attrs, parent: cur || w3c.NEW_TRACE })
138
+ } catch (err) {
139
+ debug('consumer span failed: %s', err && err.message)
140
+ return null
141
+ }
142
+ }
143
+
144
+ /**
145
+ * ITG-023/024: a CONSUMER span handling several messages (`process` for a batch callback,
146
+ * `receive` for a batch pull): parent = the current span, else a new trace; one link per message
147
+ * carrying a context, at most `messaging.maxLinks`.
148
+ */
149
+ function batchConsumer (system, destination, contexts, tags, operation) {
150
+ const t = msgTracer()
151
+ if (!t) return null
152
+ operation = operation || 'process'
153
+ try {
154
+ const name = operation + ' ' + (destination || 'multiple')
155
+ const attrs = msgTags(system, destination, operation, tags)
156
+ const cur = currentSpan()
157
+ const span = t.startSpan(name, { kind: 'CONSUMER', attributes: attrs, parent: cur || w3c.NEW_TRACE })
158
+ span.maxLinks = t.messaging.maxLinks
159
+ if (contexts) for (const c of contexts) addLink(span, c)
160
+ return span
161
+ } catch (err) {
162
+ debug('batch span failed: %s', err && err.message)
163
+ return null
164
+ }
165
+ }
166
+
167
+ function addLink (span, c) {
168
+ if (span && w3c.isSpanContext(c)) span.addLink(c)
169
+ }
170
+
171
+ /** ITG-013: `<error.name>: <first line of error.message>`, at most 500 characters. */
172
+ function errorText (err) {
173
+ let name
174
+ let msg
175
+ if (err && typeof err === 'object') {
176
+ name = typeof err.name === 'string' && err.name ? err.name : (err.constructor && err.constructor.name) || 'Error'
177
+ msg = typeof err.message === 'string' ? err.message : ''
178
+ } else {
179
+ name = 'Error'
180
+ msg = String(err)
181
+ }
182
+ const first = msg.split(/\r?\n/)[0]
183
+ const text = first ? name + ': ' + first : name
184
+ return text.length > ERROR_MAX ? text.slice(0, ERROR_MAX) : text
185
+ }
186
+
187
+ function fail (span, err, code) {
188
+ if (!span) return
189
+ try {
190
+ span.setStatus('ERROR', errorText(err))
191
+ if (code !== undefined && code !== null && code !== '') span.setAttribute('db.response.status_code', String(code))
192
+ } catch (_) {}
193
+ }
194
+
195
+ function end (span, err, code) {
196
+ if (!span) return
197
+ try {
198
+ if (err !== undefined && err !== null) fail(span, err, code)
199
+ span.end()
200
+ } catch (e) {
201
+ debug('span end failed: %s', e && e.message)
202
+ }
203
+ }
204
+
205
+ function isThenable (v) {
206
+ return v !== null && (typeof v === 'object' || typeof v === 'function') && typeof v.then === 'function'
207
+ }
208
+
209
+ /**
210
+ * Ends `span` when `p` settles; returns a promise settling like `p` (a derived promise: an app
211
+ * that ignores a rejection still gets its unhandled-rejection report). `onValue(v)` may tag rows,
212
+ * `codeOf(err)` gives the server error code.
213
+ */
214
+ function settle (p, span, onValue, codeOf) {
215
+ return p.then((v) => {
216
+ if (onValue) {
217
+ try {
218
+ onValue(v)
219
+ } catch (_) {}
220
+ }
221
+ end(span)
222
+ return v
223
+ }, (err) => {
224
+ end(span, err, codeOf ? safe(codeOf, err) : undefined)
225
+ throw err
226
+ })
227
+ }
228
+
229
+ function safe (fn, arg) {
230
+ try {
231
+ return fn(arg)
232
+ } catch (_) {
233
+ return undefined
234
+ }
235
+ }
236
+
237
+ /** Run fn with `span` current (and its baggage); returns fn's result. */
238
+ function withSpan (span, fn, thisArg, args) {
239
+ if (!span) return fn.apply(thisArg, args || [])
240
+ const store = ctx.current()
241
+ return ctx.run({ span, bag: span.bag, server: store && store.server }, fn, thisArg, args)
242
+ }
243
+
244
+ /**
245
+ * Callback style (ITG-023): run fn with `span` current; the span ends when fn returns, throws,
246
+ * or (for a thenable result) when that settles; errors mark it ERROR and propagate unchanged.
247
+ */
248
+ function runConsumer (span, fn, thisArg, args) {
249
+ if (!span) return fn.apply(thisArg, args || [])
250
+ let r
251
+ try {
252
+ r = withSpan(span, fn, thisArg, args)
253
+ } catch (err) {
254
+ end(span, err)
255
+ throw err
256
+ }
257
+ if (isThenable(r)) {
258
+ try {
259
+ r.then(() => end(span), (err) => end(span, err))
260
+ } catch (err) {
261
+ end(span)
262
+ }
263
+ return r
264
+ }
265
+ end(span)
266
+ return r
267
+ }
268
+
269
+ /** Bind a callback to the context current now (callbacks a library calls later, NIO-203). */
270
+ function bind (fn) {
271
+ return typeof fn === 'function' ? ctx.bind(fn) : fn
272
+ }
273
+
274
+ // ---------------------------------------------------------------------------------------------
275
+ // carriers (ITG-022, ITG-220)
276
+
277
+ /**
278
+ * [[name, value]] of `traceparent` (+ `tracestate`, `baggage` when non-empty) of `span`; in 85
279
+ * bridge mode the headers the app's OpenTelemetry propagator writes (OTX-012, as pmx-io does).
280
+ */
281
+ function traceHeaders (span) {
282
+ if (!span || !span.context) return []
283
+ try {
284
+ const b = bridgeNow()
285
+ if (b !== null) {
286
+ const pairs = b.headersFor(span)
287
+ if (pairs) return pairs
288
+ }
289
+ return w3c.headersFor(span.context, span.bag)
290
+ } catch (err) {
291
+ debug('trace headers failed: %s', err && err.message)
292
+ return []
293
+ }
294
+ }
295
+
296
+ /** A new plain object: `base` (never modified) plus the trace headers the app did not set. */
297
+ function headersObject (span, base, asBytes) {
298
+ const out = Object.assign({}, base || {})
299
+ const present = new Set(Object.keys(out).map((k) => k.toLowerCase()))
300
+ for (const [k, v] of traceHeaders(span)) {
301
+ if (!present.has(k)) out[k] = asBytes ? Buffer.from(v, 'utf8') : v
302
+ }
303
+ return out
304
+ }
305
+
306
+ /** The `__pmx_trace__` / `pmxTrace` carrier value of `span` (ITG-212, ITG-220). */
307
+ function traceCarrier (span) {
308
+ const o = {}
309
+ for (const [k, v] of traceHeaders(span)) o[k] = v
310
+ return o
311
+ }
312
+
313
+ /**
314
+ * The remote context in a carrier (object, Map, headers object, pairs), or null; in 85 bridge
315
+ * mode the app's OpenTelemetry propagator reads it (OTX-012, as pmx-io does).
316
+ */
317
+ function extract (carrier) {
318
+ if (!carrier) return null
319
+ try {
320
+ const b = bridgeNow()
321
+ if (b !== null) {
322
+ const sc = b.extract(carrier)
323
+ if (sc !== undefined) return sc
324
+ }
325
+ return w3c.extract(carrier)
326
+ } catch (_) {
327
+ return null
328
+ }
329
+ }
330
+
331
+ /** ITG-025: bytes of a payload that already is bytes or a string (never serialised to measure). */
332
+ function bodySize (body) {
333
+ if (typeof body === 'string') return Buffer.byteLength(body, 'utf8')
334
+ if (body instanceof Uint8Array || body instanceof ArrayBuffer) return body.byteLength
335
+ return undefined
336
+ }
337
+
338
+ // ---------------------------------------------------------------------------------------------
339
+ // iterator style (ITG-023)
340
+
341
+ const openIters = new Set()
342
+ let exitHooked = false
343
+
344
+ function hookExit () {
345
+ if (exitHooked) return
346
+ exitHooked = true
347
+ // before the span sink's own exit flush, which was registered when tracing started
348
+ process.prependListener('exit', endOpenIterators)
349
+ }
350
+
351
+ /**
352
+ * Per-iterator state: a CONSUMER span starts when a message is handed to the app, is current while
353
+ * the loop body runs (an AsyncLocalStorage store entered synchronously inside the wrapped `next()`
354
+ * and filled in at delivery), and ends at the next request on the same iterator, at close or at
355
+ * exit flush (`pmx.end_inferred` for the last two). When iteration ends, the context of the code
356
+ * before the loop is restored.
357
+ */
358
+ class IterState {
359
+ constructor () {
360
+ this.span = null
361
+ this.store = null
362
+ this.prev = undefined
363
+ this.started = false
364
+ }
365
+
366
+ /** The app asks for the next message (synchronously inside next()). */
367
+ nextRequested () {
368
+ this._finish(false)
369
+ if (!ctx.active()) return
370
+ const cur = ctx.current()
371
+ if (!this.started) {
372
+ this.started = true
373
+ this.prev = cur
374
+ }
375
+ const base = this.prev
376
+ const store = { span: base && base.span, bag: base ? base.bag : w3c.EMPTY_BAG, server: base && base.server }
377
+ try {
378
+ ctx.enter(store)
379
+ this.store = store
380
+ } catch (_) {
381
+ this.store = null
382
+ }
383
+ }
384
+
385
+ /** A message is handed to the app: `span` becomes current for the loop body. */
386
+ delivered (span) {
387
+ if (!span) return
388
+ this.span = span
389
+ if (this.store) {
390
+ this.store.span = span
391
+ this.store.bag = span.bag
392
+ }
393
+ openIters.add(this)
394
+ hookExit()
395
+ }
396
+
397
+ /** Iteration finished (done, return(), close): end the span and restore the earlier context. */
398
+ closed (inferred) {
399
+ this._finish(inferred !== false)
400
+ if (this.started) {
401
+ try {
402
+ ctx.enter(this.prev)
403
+ } catch (_) {}
404
+ }
405
+ }
406
+
407
+ _finish (inferred) {
408
+ const span = this.span
409
+ this.span = null
410
+ if (this.store) {
411
+ // the loop body's store no longer names the span (late callbacks see the parent)
412
+ this.store.span = this.prev && this.prev.span
413
+ this.store.bag = this.prev ? this.prev.bag : w3c.EMPTY_BAG
414
+ }
415
+ openIters.delete(this)
416
+ if (span) {
417
+ if (inferred) span.setAttribute('pmx.end_inferred', 'true')
418
+ end(span)
419
+ }
420
+ }
421
+
422
+ atExit () {
423
+ const span = this.span
424
+ this.span = null
425
+ if (span) {
426
+ span.setAttribute('pmx.end_inferred', 'true')
427
+ end(span)
428
+ }
429
+ }
430
+ }
431
+
432
+ function endOpenIterators () {
433
+ for (const it of Array.from(openIters)) {
434
+ try {
435
+ it.atExit()
436
+ } catch (_) {}
437
+ }
438
+ openIters.clear()
439
+ }
440
+
441
+ const ITER = Symbol('pmx-iter')
442
+
443
+ /** The IterState attached to a library iterator/subscription object (created on demand). */
444
+ function iterState (obj) {
445
+ if (!obj || (typeof obj !== 'object' && typeof obj !== 'function')) return null
446
+ let st = obj[ITER]
447
+ if (st instanceof IterState) return st
448
+ st = new IterState()
449
+ try {
450
+ Object.defineProperty(obj, ITER, { value: st, configurable: true, enumerable: false, writable: true })
451
+ } catch (_) {
452
+ return null
453
+ }
454
+ return st
455
+ }
456
+
457
+ // ---------------------------------------------------------------------------------------------
458
+ // wrapping (ITG-003)
459
+
460
+ function copyMeta (fn, orig) {
461
+ try {
462
+ Object.defineProperty(fn, 'length', { value: orig.length, configurable: true })
463
+ Object.defineProperty(fn, 'name', { value: orig.name, configurable: true })
464
+ } catch (_) {}
465
+ try {
466
+ Object.defineProperty(fn, ORIGINAL, { value: orig })
467
+ Object.defineProperty(fn, WRAPPED, { value: true })
468
+ } catch (_) {}
469
+ // static properties (e.g. util.promisify.custom) stay reachable
470
+ try {
471
+ for (const k of Reflect.ownKeys(orig)) {
472
+ if (k === 'length' || k === 'name' || k === 'prototype' || k === WRAPPED || k === ORIGINAL) continue
473
+ if (!Object.prototype.hasOwnProperty.call(fn, k)) {
474
+ const d = Object.getOwnPropertyDescriptor(orig, k)
475
+ if (d) Object.defineProperty(fn, k, d)
476
+ }
477
+ }
478
+ } catch (_) {}
479
+ if (orig.prototype !== undefined) {
480
+ try {
481
+ fn.prototype = orig.prototype
482
+ } catch (_) {}
483
+ }
484
+ return fn
485
+ }
486
+
487
+ /**
488
+ * Replace obj[name] (a function) by make(original) once. The replacement keeps `name`, `length`
489
+ * and the property's enumerability; pmx marks it with a private symbol (never `__wrapped`, which
490
+ * OpenTelemetry's shimmer uses, 85 OTX-020). Returns true when wrapped (or already wrapped).
491
+ * `opts.lib` names the package (default: the one being patched), `opts.track: false` keeps a
492
+ * wrapper of a per-instance object out of the per-library list.
493
+ */
494
+ function wrap (obj, name, make, opts) {
495
+ if (!obj) return false
496
+ let desc
497
+ let owner = obj
498
+ while (owner && !(desc = Object.getOwnPropertyDescriptor(owner, name))) owner = Object.getPrototypeOf(owner)
499
+ if (!desc || typeof desc.value !== 'function') return false
500
+ const orig = desc.value
501
+ if (orig[WRAPPED]) return true
502
+ try {
503
+ const made = make(orig)
504
+ // objects wrapped at run time (kafkajs producers) name their library and are not tracked
505
+ const lib = (opts && opts.lib) || currentLib
506
+ // 85 OTX-020: in bridge mode, when an OpenTelemetry instrumentation covers this library (its
507
+ // shimmer wrapper, marked `__wrapped`, sits on this or another function pmx hooks in the
508
+ // library), pass straight through
509
+ const w = copyMeta(function () {
510
+ const b = bridgeNow()
511
+ if (b !== null && !b.disabledMarkerCheck && coveredByOtel(lib, this, name, w)) {
512
+ b.suppress(lib)
513
+ return orig.apply(this, arguments)
514
+ }
515
+ return made.apply(this, arguments)
516
+ }, orig)
517
+ Object.defineProperty(obj, name, { value: w, writable: desc.writable !== false, enumerable: desc.enumerable, configurable: true })
518
+ if (!(opts && opts.track === false)) hook(lib, obj, name, w, orig)
519
+ return true
520
+ } catch (err) {
521
+ debug('cannot wrap %s: %s', name, err && err.message)
522
+ return false
523
+ }
524
+ }
525
+
526
+ // 85 OTX-020 per library: the functions pmx hooked (and functions pmx watches without hooking
527
+ // them, e.g. the MongoDB driver's Connection.command), by package name. An OpenTelemetry
528
+ // instrumentation rarely wraps exactly the functions pmx wraps (amqplib: `publish` but not
529
+ // `sendToQueue`; node-redis: commands but not `MULTI`; MongoDB: the connection, not the events
530
+ // pmx observes), so one OTel-wrapped function suppresses all of the library's pmx spans.
531
+ const hooks = new Map()
532
+
533
+ function hook (lib, obj, name, w, orig) {
534
+ let list = hooks.get(lib)
535
+ if (!list) hooks.set(lib, (list = []))
536
+ list.push({ obj, name, w, orig, get: null })
537
+ }
538
+
539
+ /** Watch a function pmx does not wrap: `get()` returns [obj, name] (resolved lazily), or null. */
540
+ function watch (lib, get) {
541
+ let list = hooks.get(lib)
542
+ if (!list) hooks.set(lib, (list = []))
543
+ list.push({ obj: null, name: null, w: null, orig: null, get })
544
+ }
545
+
546
+ function otelWrapped (fn, own) {
547
+ return Boolean(fn && fn !== own && fn.__wrapped === true)
548
+ }
549
+
550
+ /**
551
+ * True when an OpenTelemetry instrumentation wrapped a function of `lib` that pmx hooks or
552
+ * watches: the method as the receiver sees it (OTel may wrap a subclass's prototype), the
553
+ * property pmx replaced (OTel wrapped pmx's wrapper), or the function pmx wrapped (OTel came first).
554
+ */
555
+ function coveredByOtel (lib, self, name, w) {
556
+ if (self !== null && self !== undefined && name !== undefined) {
557
+ try {
558
+ if (otelWrapped(self[name], w)) return true
559
+ } catch (_) {}
560
+ }
561
+ const list = hooks.get(lib)
562
+ if (!list) return false
563
+ for (const h of list) {
564
+ try {
565
+ if (h.get) {
566
+ // a watched function is resolved once
567
+ const get = h.get
568
+ h.get = null
569
+ const r = get()
570
+ if (r) {
571
+ h.obj = r[0]
572
+ h.name = r[1]
573
+ }
574
+ }
575
+ if (!h.obj) continue
576
+ if (otelWrapped(h.obj[h.name], h.w) || otelWrapped(h.orig, null)) return true
577
+ } catch (_) {}
578
+ }
579
+ return false
580
+ }
581
+
582
+ /** In bridge mode: true (and `lib` reported in `suppressed`) when OpenTelemetry covers `lib`. */
583
+ function suppressedLib (lib) {
584
+ const b = bridgeNow()
585
+ if (b === null || b.disabledMarkerCheck || !coveredByOtel(lib)) return false
586
+ b.suppress(lib)
587
+ return true
588
+ }
589
+
590
+ /** The 85 bridge while tracing runs in bridge mode, else null. */
591
+ function bridgeNow () {
592
+ const r = globalThis[MODULES]
593
+ const t = r && r.tracing && r.tracing._tracer
594
+ return t && t.bridge ? t.bridge : null
595
+ }
596
+
597
+ // the package being patched (set by lib/itg/index.js), reported in `suppressed` (OTX-004)
598
+ let currentLib = 'unknown'
599
+ function setLib (name) {
600
+ currentLib = name || 'unknown'
601
+ }
602
+
603
+ function isWrapped (fn) {
604
+ return Boolean(fn && fn[WRAPPED])
605
+ }
606
+
607
+ /** The library version from its package.json next to `dir`, or '?'. */
608
+ function versionOf (dir) {
609
+ try {
610
+ return require(require('path').join(dir, 'package.json')).version || '?'
611
+ } catch (_) {
612
+ return '?'
613
+ }
614
+ }
615
+
616
+ module.exports = {
617
+ tracer,
618
+ dbTracer,
619
+ msgTracer,
620
+ currentSpan,
621
+ nested,
622
+ outer,
623
+ disabled,
624
+ clientSpan,
625
+ producer,
626
+ consumer,
627
+ batchConsumer,
628
+ addLink,
629
+ errorText,
630
+ fail,
631
+ end,
632
+ isThenable,
633
+ settle,
634
+ withSpan,
635
+ runConsumer,
636
+ bind,
637
+ traceHeaders,
638
+ headersObject,
639
+ traceCarrier,
640
+ extract,
641
+ bodySize,
642
+ IterState,
643
+ iterState,
644
+ endOpenIterators,
645
+ wrap,
646
+ watch,
647
+ suppressedLib,
648
+ coveredByOtel,
649
+ setLib,
650
+ bridgeNow,
651
+ copyMeta,
652
+ isWrapped,
653
+ versionOf,
654
+ TRACE_KEY,
655
+ WRAPPED,
656
+ ORIGINAL
657
+ }