@aidra/pmx-io 0.0.0-stage → 9.13.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.
- package/README.md +69 -2
- package/bin/pmx-io-node.js +143 -0
- package/index.d.ts +381 -0
- package/index.js +27 -0
- package/index.mjs +47 -0
- package/lib/actions.js +120 -0
- package/lib/activate.js +402 -0
- package/lib/application.js +106 -0
- package/lib/bun.js +155 -0
- package/lib/config.js +67 -0
- package/lib/constants.js +17 -0
- package/lib/debug.js +48 -0
- package/lib/esm-hooks.mjs +111 -0
- package/lib/exit.js +79 -0
- package/lib/features/metrics.js +409 -0
- package/lib/features/notify.js +358 -0
- package/lib/features/profiling.js +208 -0
- package/lib/hooks.js +151 -0
- package/lib/inbound.js +174 -0
- package/lib/integrations.js +372 -0
- package/lib/itg/amqplib.js +169 -0
- package/lib/itg/bullmq.js +141 -0
- package/lib/itg/common.js +657 -0
- package/lib/itg/dbcomment.js +114 -0
- package/lib/itg/index.js +103 -0
- package/lib/itg/ioredis.js +212 -0
- package/lib/itg/kafkajs.js +205 -0
- package/lib/itg/mongodb.js +322 -0
- package/lib/itg/mysql2.js +205 -0
- package/lib/itg/nats.js +531 -0
- package/lib/itg/noderedis.js +236 -0
- package/lib/itg/pg.js +253 -0
- package/lib/itg/pgqueue.js +305 -0
- package/lib/itg/postgres.js +252 -0
- package/lib/itg/redis-common.js +265 -0
- package/lib/itg/sql.js +302 -0
- package/lib/metrics/service.js +286 -0
- package/lib/metrics/types.js +342 -0
- package/lib/modules.js +260 -0
- package/lib/node-options.js +62 -0
- package/lib/otel/bridge.js +797 -0
- package/lib/otel/env.js +114 -0
- package/lib/outbound.js +362 -0
- package/lib/pmx.js +757 -0
- package/lib/relay/hub.js +279 -0
- package/lib/relay/transport.js +253 -0
- package/lib/standalone/activate.js +307 -0
- package/lib/standalone/capture.js +55 -0
- package/lib/standalone/container.js +909 -0
- package/lib/standalone/transport.js +1023 -0
- package/lib/tracing/context.js +90 -0
- package/lib/tracing/control.js +124 -0
- package/lib/tracing/exporter.js +115 -0
- package/lib/tracing/index.js +242 -0
- package/lib/tracing/logs.js +176 -0
- package/lib/tracing/options.js +105 -0
- package/lib/tracing/paths.js +34 -0
- package/lib/tracing/tracer.js +850 -0
- package/lib/tracing/w3c.js +415 -0
- package/lib/transport/ipc.js +97 -0
- package/lib/transport/ws.js +511 -0
- package/lib/util.js +109 -0
- package/package.json +48 -4
- package/preload.cjs +26 -0
- package/preload.mjs +25 -0
- package/standalone.cjs +16 -0
- package/standalone.mjs +17 -0
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
// Copyright (c) 2026 Mike Warriner. All rights reserved. Proprietary — see LICENSE.
|
|
2
|
+
'use strict'
|
|
3
|
+
|
|
4
|
+
// amqplib (RabbitMQ, AMQP 0-9-1; 82 §13 ITG-242, ITG-243), promise API (lib/channel_model.js) and
|
|
5
|
+
// callback API (lib/callback_model.js), normal and confirm channels:
|
|
6
|
+
// * publish(exchange, routingKey, content, options) and sendToQueue(queue, content, options) →
|
|
7
|
+
// PRODUCER `publish <exchange>` (the routing key / queue for the default exchange), trace headers
|
|
8
|
+
// in a copy of `options.headers`; the span ends when the call returns (also on confirm
|
|
9
|
+
// channels, not at the broker confirm);
|
|
10
|
+
// * consume(queue, onMessage, options) → callback style per delivery (a `null` delivery, the
|
|
11
|
+
// consumer being cancelled, makes none), parent extracted from `properties.headers`;
|
|
12
|
+
// * get(queue) → batch pull of one (`receive <queue>`, one link), only when a message came back.
|
|
13
|
+
|
|
14
|
+
const c = require('./common')
|
|
15
|
+
|
|
16
|
+
const SYSTEM = 'rabbitmq'
|
|
17
|
+
|
|
18
|
+
function peer (ch) {
|
|
19
|
+
const tags = {}
|
|
20
|
+
try {
|
|
21
|
+
const s = ch && ch.connection && ch.connection.stream
|
|
22
|
+
if (s && s.remoteAddress) {
|
|
23
|
+
tags['net.peer.name'] = String(s.remoteAddress)
|
|
24
|
+
if (s.remotePort) tags['net.peer.port'] = String(s.remotePort)
|
|
25
|
+
}
|
|
26
|
+
} catch (_) {}
|
|
27
|
+
return tags
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function publishSpan (ch, exchange, routingKey, content, options) {
|
|
31
|
+
const tags = peer(ch)
|
|
32
|
+
if (routingKey !== undefined && routingKey !== null) tags['messaging.rabbitmq.destination.routing_key'] = String(routingKey)
|
|
33
|
+
if (options && options.messageId !== undefined) tags['messaging.message.id'] = String(options.messageId)
|
|
34
|
+
const size = c.bodySize(content)
|
|
35
|
+
if (size !== undefined) tags['messaging.message.body.size'] = String(size)
|
|
36
|
+
const dest = exchange ? String(exchange) : (routingKey !== undefined && routingKey !== null ? String(routingKey) : undefined)
|
|
37
|
+
return c.producer(SYSTEM, dest, tags)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function withHeaders (options, span) {
|
|
41
|
+
const o = Object.assign({}, options || {})
|
|
42
|
+
o.headers = c.headersObject(span, options && options.headers)
|
|
43
|
+
return o
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function wrapPublish (orig) {
|
|
47
|
+
return function publish (exchange, routingKey, content, options) {
|
|
48
|
+
if (!c.msgTracer() || c.nested()) return orig.apply(this, arguments)
|
|
49
|
+
const span = publishSpan(this, exchange, routingKey, content, options)
|
|
50
|
+
if (!span) return c.outer(orig, this, arguments)
|
|
51
|
+
const args = Array.prototype.slice.call(arguments)
|
|
52
|
+
while (args.length < 4) args.push(undefined)
|
|
53
|
+
args[3] = withHeaders(options, span)
|
|
54
|
+
try {
|
|
55
|
+
const r = c.outer(orig, this, args)
|
|
56
|
+
c.end(span)
|
|
57
|
+
return r
|
|
58
|
+
} catch (err) {
|
|
59
|
+
c.end(span, err)
|
|
60
|
+
throw err
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function wrapSendToQueue (orig) {
|
|
66
|
+
return function sendToQueue (queue, content, options) {
|
|
67
|
+
if (!c.msgTracer() || c.nested()) return orig.apply(this, arguments)
|
|
68
|
+
const span = publishSpan(this, '', queue, content, options)
|
|
69
|
+
if (!span) return c.outer(orig, this, arguments)
|
|
70
|
+
const args = Array.prototype.slice.call(arguments)
|
|
71
|
+
while (args.length < 3) args.push(undefined)
|
|
72
|
+
args[2] = withHeaders(options, span)
|
|
73
|
+
try {
|
|
74
|
+
const r = c.outer(orig, this, args)
|
|
75
|
+
c.end(span)
|
|
76
|
+
return r
|
|
77
|
+
} catch (err) {
|
|
78
|
+
c.end(span, err)
|
|
79
|
+
throw err
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function msgTags (ch, msg, queue) {
|
|
85
|
+
const tags = peer(ch)
|
|
86
|
+
const f = msg.fields || {}
|
|
87
|
+
const p = msg.properties || {}
|
|
88
|
+
if (f.routingKey !== undefined) tags['messaging.rabbitmq.destination.routing_key'] = String(f.routingKey)
|
|
89
|
+
if (p.messageId !== undefined && p.messageId !== null) tags['messaging.message.id'] = String(p.messageId)
|
|
90
|
+
const size = c.bodySize(msg.content)
|
|
91
|
+
if (size !== undefined) tags['messaging.message.body.size'] = String(size)
|
|
92
|
+
return tags
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function wrapConsume (orig) {
|
|
96
|
+
return function consume (queue, onMessage) {
|
|
97
|
+
if (!c.msgTracer() || typeof onMessage !== 'function') return orig.apply(this, arguments)
|
|
98
|
+
const ch = this
|
|
99
|
+
const args = Array.prototype.slice.call(arguments)
|
|
100
|
+
args[1] = function (msg) {
|
|
101
|
+
if (msg === null || msg === undefined || !c.msgTracer()) return onMessage.apply(this, arguments)
|
|
102
|
+
const headers = msg.properties && msg.properties.headers
|
|
103
|
+
const span = c.consumer(SYSTEM, String(queue), c.extract(headers), msgTags(ch, msg, queue))
|
|
104
|
+
return c.runConsumer(span, onMessage, this, arguments)
|
|
105
|
+
}
|
|
106
|
+
return orig.apply(this, args)
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function receiveDone (span, msg) {
|
|
111
|
+
if (!span) return
|
|
112
|
+
if (!msg) return // nothing returned: no span (ITG-023)
|
|
113
|
+
try {
|
|
114
|
+
c.addLink(span, c.extract(msg.properties && msg.properties.headers))
|
|
115
|
+
} catch (_) {}
|
|
116
|
+
c.end(span)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function wrapGet (orig) {
|
|
120
|
+
return function get (queue, options, cb) {
|
|
121
|
+
if (!c.msgTracer()) return orig.apply(this, arguments)
|
|
122
|
+
const span = c.batchConsumer(SYSTEM, String(queue), null, peer(this), 'receive')
|
|
123
|
+
if (!span) return orig.apply(this, arguments)
|
|
124
|
+
const args = Array.prototype.slice.call(arguments)
|
|
125
|
+
const cbIndex = typeof options === 'function' ? 1 : (typeof cb === 'function' ? 2 : -1)
|
|
126
|
+
if (cbIndex !== -1) {
|
|
127
|
+
const fn = c.bind(args[cbIndex])
|
|
128
|
+
args[cbIndex] = function (err, msg) {
|
|
129
|
+
if (err) c.end(span, err)
|
|
130
|
+
else receiveDone(span, msg)
|
|
131
|
+
return fn.apply(this, arguments)
|
|
132
|
+
}
|
|
133
|
+
return orig.apply(this, args)
|
|
134
|
+
}
|
|
135
|
+
let r
|
|
136
|
+
try {
|
|
137
|
+
r = orig.apply(this, args)
|
|
138
|
+
} catch (err) {
|
|
139
|
+
c.end(span, err)
|
|
140
|
+
throw err
|
|
141
|
+
}
|
|
142
|
+
if (!c.isThenable(r)) return r
|
|
143
|
+
return r.then((msg) => {
|
|
144
|
+
receiveDone(span, msg)
|
|
145
|
+
return msg
|
|
146
|
+
}, (err) => {
|
|
147
|
+
c.end(span, err)
|
|
148
|
+
throw err
|
|
149
|
+
})
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function patchModel (exports, info) {
|
|
154
|
+
let any = false
|
|
155
|
+
for (const name of ['Channel', 'ConfirmChannel']) {
|
|
156
|
+
const K = exports && exports[name]
|
|
157
|
+
const proto = K && K.prototype
|
|
158
|
+
if (!proto) continue
|
|
159
|
+
for (const [m, w] of [['publish', wrapPublish], ['sendToQueue', wrapSendToQueue], ['consume', wrapConsume], ['get', wrapGet]]) {
|
|
160
|
+
if (Object.prototype.hasOwnProperty.call(proto, m) && typeof proto[m] === 'function') {
|
|
161
|
+
c.wrap(proto, m, w)
|
|
162
|
+
any = true
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
if (!any) c.disabled('amqplib', 'Channel.publish not found in ' + info.file)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
module.exports = { patchModel }
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
// Copyright (c) 2026 Mike Warriner. All rights reserved. Proprietary — see LICENSE.
|
|
2
|
+
'use strict'
|
|
3
|
+
|
|
4
|
+
// BullMQ (82 §10 ITG-212). Producer: `Queue.prototype.add(name, data, opts)` → PRODUCER
|
|
5
|
+
// `publish <queue name>` (current during the call, so BullMQ's Redis commands are its children,
|
|
6
|
+
// ITG-005), the carrier `pmxTrace` = { traceparent, tracestate?, baggage? } in a copy of `opts`
|
|
7
|
+
// (BullMQ stores job options with the job); `addBulk(jobs)` → one PRODUCER span with
|
|
8
|
+
// `messaging.batch.message_count`, every job carrying that span's context. Worker: the export
|
|
9
|
+
// `Worker` is replaced by a Proxy whose construct trap wraps a processor function (CONSUMER
|
|
10
|
+
// `process <queue name>`, callback style until its promise settles, parent from
|
|
11
|
+
// `job.opts.pmxTrace`, `messaging.pmx.attempt` = attemptsMade + 1 on BullMQ ≥ 5, attemptsMade on
|
|
12
|
+
// 4.x). Sandboxed processors (a file
|
|
13
|
+
// path) are covered when the child that runs them is instrumented (80 NIO-290–296). BullMQ's own
|
|
14
|
+
// `telemetry` option is left untouched.
|
|
15
|
+
|
|
16
|
+
const c = require('./common')
|
|
17
|
+
|
|
18
|
+
const SYSTEM = 'bullmq'
|
|
19
|
+
const KEY = 'pmxTrace'
|
|
20
|
+
|
|
21
|
+
function withCarrier (opts, span) {
|
|
22
|
+
// a repeatable job (`repeat`) is a schedule: BullMQ copies its options into every job it creates
|
|
23
|
+
// from it, which would make each run (for days) a child of this one call. Its runs start new
|
|
24
|
+
// traces, as jobs from `upsertJobScheduler` do.
|
|
25
|
+
if (opts && typeof opts === 'object' && opts.repeat) return opts
|
|
26
|
+
const o = Object.assign({}, opts || {})
|
|
27
|
+
if (o[KEY] === undefined) o[KEY] = c.traceCarrier(span)
|
|
28
|
+
return o
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function wrapAdd (orig) {
|
|
32
|
+
return function add (name, data, opts) {
|
|
33
|
+
if (!c.msgTracer()) return orig.apply(this, arguments)
|
|
34
|
+
const tags = {}
|
|
35
|
+
if (name !== undefined) tags['messaging.bullmq.job.name'] = String(name)
|
|
36
|
+
const span = c.producer(SYSTEM, this.name, tags)
|
|
37
|
+
if (!span) return orig.apply(this, arguments)
|
|
38
|
+
const args = Array.prototype.slice.call(arguments)
|
|
39
|
+
while (args.length < 3) args.push(undefined)
|
|
40
|
+
args[2] = withCarrier(opts, span)
|
|
41
|
+
let p
|
|
42
|
+
try {
|
|
43
|
+
p = c.withSpan(span, orig, this, args)
|
|
44
|
+
} catch (err) {
|
|
45
|
+
c.end(span, err)
|
|
46
|
+
throw err
|
|
47
|
+
}
|
|
48
|
+
if (!c.isThenable(p)) {
|
|
49
|
+
c.end(span)
|
|
50
|
+
return p
|
|
51
|
+
}
|
|
52
|
+
return c.settle(p, span, (job) => {
|
|
53
|
+
if (job && job.id !== undefined) span.setAttribute('messaging.message.id', String(job.id))
|
|
54
|
+
})
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function wrapAddBulk (orig) {
|
|
59
|
+
return function addBulk (jobs) {
|
|
60
|
+
if (!c.msgTracer() || !Array.isArray(jobs)) return orig.apply(this, arguments)
|
|
61
|
+
const tags = {}
|
|
62
|
+
if (jobs.length > 1) tags['messaging.batch.message_count'] = String(jobs.length)
|
|
63
|
+
const span = c.producer(SYSTEM, this.name, tags)
|
|
64
|
+
if (!span) return orig.apply(this, arguments)
|
|
65
|
+
const copy = jobs.map((j) => (j && typeof j === 'object' ? Object.assign({}, j, { opts: withCarrier(j.opts, span) }) : j))
|
|
66
|
+
let p
|
|
67
|
+
try {
|
|
68
|
+
p = c.withSpan(span, orig, this, [copy])
|
|
69
|
+
} catch (err) {
|
|
70
|
+
c.end(span, err)
|
|
71
|
+
throw err
|
|
72
|
+
}
|
|
73
|
+
if (!c.isThenable(p)) {
|
|
74
|
+
c.end(span)
|
|
75
|
+
return p
|
|
76
|
+
}
|
|
77
|
+
return c.settle(p, span)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function wrapProcessor (queueName, processor) {
|
|
82
|
+
const w = function (job) {
|
|
83
|
+
if (!c.msgTracer() || !job) return processor.apply(this, arguments)
|
|
84
|
+
const tags = {}
|
|
85
|
+
if (job.id !== undefined) tags['messaging.message.id'] = String(job.id)
|
|
86
|
+
if (job.name !== undefined) tags['messaging.bullmq.job.name'] = String(job.name)
|
|
87
|
+
// BullMQ ≥ 5 counts failed attempts in attemptsMade (and has attemptsStarted); 4.x increments
|
|
88
|
+
// attemptsMade when the job starts, so it already is the 1-based attempt number
|
|
89
|
+
if (typeof job.attemptsMade === 'number') {
|
|
90
|
+
tags['messaging.pmx.attempt'] = String(typeof job.attemptsStarted === 'number' ? job.attemptsMade + 1 : Math.max(1, job.attemptsMade))
|
|
91
|
+
}
|
|
92
|
+
const carrier = job.opts && job.opts[KEY]
|
|
93
|
+
const span = c.consumer(SYSTEM, queueName, carrier ? c.extract(carrier) : null, tags)
|
|
94
|
+
return c.runConsumer(span, processor, this, arguments)
|
|
95
|
+
}
|
|
96
|
+
return c.copyMeta(w, processor)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function proxyWorker (Worker) {
|
|
100
|
+
return new Proxy(Worker, {
|
|
101
|
+
construct (target, args, newTarget) {
|
|
102
|
+
let a = args
|
|
103
|
+
try {
|
|
104
|
+
if (typeof args[1] === 'function' && c.tracer()) {
|
|
105
|
+
a = args.slice()
|
|
106
|
+
a[1] = wrapProcessor(String(args[0]), args[1])
|
|
107
|
+
}
|
|
108
|
+
} catch (_) {
|
|
109
|
+
a = args
|
|
110
|
+
}
|
|
111
|
+
return Reflect.construct(target, a, newTarget)
|
|
112
|
+
}
|
|
113
|
+
})
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** classes/queue.js: Queue.prototype.add / addBulk. */
|
|
117
|
+
function patchQueue (exports) {
|
|
118
|
+
const Queue = exports && exports.Queue
|
|
119
|
+
const proto = Queue && Queue.prototype
|
|
120
|
+
if (!proto || typeof proto.add !== 'function') {
|
|
121
|
+
c.disabled('bullmq', 'Queue.prototype.add not found')
|
|
122
|
+
return
|
|
123
|
+
}
|
|
124
|
+
c.wrap(proto, 'add', wrapAdd)
|
|
125
|
+
if (typeof proto.addBulk === 'function') c.wrap(proto, 'addBulk', wrapAddBulk)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** classes/worker.js: the Worker export. */
|
|
129
|
+
function patchWorker (exports) {
|
|
130
|
+
const Worker = exports && exports.Worker
|
|
131
|
+
if (typeof Worker !== 'function') {
|
|
132
|
+
c.disabled('bullmq', 'Worker not found')
|
|
133
|
+
return
|
|
134
|
+
}
|
|
135
|
+
const P = proxyWorker(Worker)
|
|
136
|
+
const d = Object.getOwnPropertyDescriptor(exports, 'Worker')
|
|
137
|
+
if (d && d.writable) exports.Worker = P
|
|
138
|
+
else Object.defineProperty(exports, 'Worker', { value: P, writable: true, enumerable: true, configurable: true })
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
module.exports = { patchQueue, patchWorker }
|