@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.
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,236 @@
1
+ // Copyright (c) 2026 Mike Warriner. All rights reserved. Proprietary — see LICENSE.
2
+ 'use strict'
3
+
4
+ // node-redis (`redis` 4.x/5.x/6.x through `@redis/client`; 82 §6 ITG-122, ITG-123, ITG-032; streams
5
+ // §10 ITG-210). Every command the client sends is one CLIENT span:
6
+ // * @redis/client ≥ 5 (`redis` ≥ 5): `RedisClient.prototype.sendCommand(args, options)` — the
7
+ // typed command methods, scripts and raw `sendCommand` all go through it; `_executeMulti`
8
+ // (`multi().exec()`, MULTI span) and `_executePipeline` (`multi().execAsPipeline()`, PIPELINE
9
+ // span) queue their commands directly.
10
+ // * @redis/client 1.x (`redis` 4.x): `commandsExecutor(command, args)` (typed methods) and
11
+ // `sendCommand(args, options)`; `multiExecutor(commands, selectedDB, chainId)` (MULTI when a
12
+ // chain id is given, PIPELINE otherwise).
13
+ // `duplicate()`, pool/isolation clients and the node clients of `createCluster` are instances of
14
+ // the same class. Subscriptions do not go through these methods and make no span.
15
+
16
+ const c = require('./common')
17
+ const r = require('./redis-common')
18
+
19
+ function peerTags (client) {
20
+ const tags = {}
21
+ try {
22
+ const o = client.options || {}
23
+ const s = o.socket || {}
24
+ if (s.path) tags['net.peer.name'] = String(s.path)
25
+ else if (s.host || o.url) {
26
+ let host = s.host
27
+ let port = s.port
28
+ if (!host && o.url) {
29
+ try {
30
+ const u = new URL(o.url)
31
+ host = u.hostname
32
+ port = u.port || 6379
33
+ } catch (_) {}
34
+ }
35
+ if (host) tags['net.peer.name'] = String(host)
36
+ if (port) tags['net.peer.port'] = String(port)
37
+ } else {
38
+ tags['net.peer.name'] = 'localhost'
39
+ tags['net.peer.port'] = String(s.port || 6379)
40
+ }
41
+ } catch (_) {}
42
+ return tags
43
+ }
44
+
45
+ function dbTags (client) {
46
+ const tags = Object.assign({ 'db.system': 'redis' }, peerTags(client))
47
+ try {
48
+ const db = client.options && client.options.database
49
+ tags['db.redis.database_index'] = String(db || 0)
50
+ } catch (_) {}
51
+ return tags
52
+ }
53
+
54
+ /** Start the span of one command given its Redis arguments (name first); returns a finisher. */
55
+ function startCommand (client, redisArgs) {
56
+ if (!Array.isArray(redisArgs) || redisArgs.length === 0) return null
57
+ const name = r.text(redisArgs[0]).toUpperCase()
58
+ const args = redisArgs.slice(1)
59
+ if (r.STREAM_COMMANDS.has(name) && c.msgTracer()) {
60
+ const call = r.streamCall(name, args, peerTags(client))
61
+ if (!call || !call.span) return { extra: [], done () {} }
62
+ return { extra: call.extra, done: (v, err) => call.done(v, err) }
63
+ }
64
+ const t = c.dbTracer()
65
+ if (!t) return null
66
+ const tags = dbTags(client)
67
+ tags['db.operation'] = name
68
+ const st = r.statement(name, args, t.db.statement)
69
+ if (st !== undefined) tags['db.statement'] = st
70
+ const span = c.clientSpan(t, name, tags)
71
+ if (!span) return null
72
+ return { extra: [], done: (v, err) => c.end(span, err, err ? r.errorCode(err) : undefined) }
73
+ }
74
+
75
+ function finishWith (p, done) {
76
+ if (!c.isThenable(p)) {
77
+ done(p)
78
+ return p
79
+ }
80
+ return p.then((v) => {
81
+ done(v)
82
+ return v
83
+ }, (err) => {
84
+ done(undefined, err)
85
+ throw err
86
+ })
87
+ }
88
+
89
+ function wrapSendCommand (orig) {
90
+ return function sendCommand (args, options) {
91
+ if (!c.tracer() || c.nested() || !Array.isArray(args)) return orig.apply(this, arguments)
92
+ let call = null
93
+ try {
94
+ call = startCommand(this, args)
95
+ } catch (_) {
96
+ call = null
97
+ }
98
+ if (!call) return orig.apply(this, arguments)
99
+ const a = Array.prototype.slice.call(arguments)
100
+ if (call.extra.length) a[0] = args.concat(call.extra)
101
+ let p
102
+ try {
103
+ p = c.outer(orig, this, a)
104
+ } catch (err) {
105
+ call.done(undefined, err)
106
+ throw err
107
+ }
108
+ return finishWith(p, call.done)
109
+ }
110
+ }
111
+
112
+ /**
113
+ * @redis/client 1.x typed commands: commandsExecutor(command, args). The executor sends through
114
+ * the client's private send, so an XADD whose entry gets the trace fields (ITG-210,
115
+ * `redisStreamFields`) is sent here through the public `sendCommand` with the extended arguments
116
+ * and its reply transformed as the executor would (`commander.transformCommandReply`).
117
+ */
118
+ function wrapCommandsExecutor (orig, transform, transformReply) {
119
+ return function commandsExecutor (command, args) {
120
+ if (!c.tracer() || c.nested()) return orig.apply(this, arguments)
121
+ let call = null
122
+ let t = null
123
+ try {
124
+ t = transform ? transform(command, args) : { args: command.transformArguments.apply(command, args) }
125
+ call = startCommand(this, Array.from(t.args))
126
+ } catch (_) {
127
+ call = null
128
+ }
129
+ if (!call) return c.outer(orig, this, arguments)
130
+ if (call.extra.length) {
131
+ if (transformReply && typeof this.sendCommand === 'function') {
132
+ let p
133
+ try {
134
+ const a = Array.from(t.args).concat(call.extra)
135
+ if (t.args.preserve !== undefined) a.preserve = t.args.preserve
136
+ p = c.outer(this.sendCommand, this, [a, t.options])
137
+ } catch (err) {
138
+ call.done(undefined, err)
139
+ throw err
140
+ }
141
+ return finishWith(p, call.done).then((reply) => transformReply(command, reply, t.args.preserve))
142
+ }
143
+ call.extra.length = 0 // no reply transform available: the span without the fields
144
+ }
145
+ let p
146
+ try {
147
+ p = c.outer(orig, this, arguments)
148
+ } catch (err) {
149
+ call.done(undefined, err)
150
+ throw err
151
+ }
152
+ return finishWith(p, call.done)
153
+ }
154
+ }
155
+
156
+ function batchSpan (client, commands, multi) {
157
+ const t = c.dbTracer()
158
+ if (!t || !Array.isArray(commands) || commands.length === 0) return null
159
+ const cmds = []
160
+ for (const cmd of commands) {
161
+ const a = Array.isArray(cmd) ? cmd : cmd && cmd.args
162
+ if (!Array.isArray(a) || a.length === 0) continue
163
+ const name = r.text(a[0]).toUpperCase()
164
+ if (multi && (name === 'MULTI' || name === 'EXEC')) continue // 1.x queues them with the commands
165
+ cmds.push([name, a.slice(1)])
166
+ }
167
+ const name = multi ? 'MULTI' : 'PIPELINE'
168
+ const tags = dbTags(client)
169
+ tags['db.operation'] = name
170
+ const st = r.pipelineStatement(cmds, t.db.statement)
171
+ if (st !== undefined) tags['db.statement'] = st
172
+ tags['db.batch_size'] = String(cmds.length)
173
+ return c.clientSpan(t, name, tags)
174
+ }
175
+
176
+ function wrapBatch (multiFromArgs) {
177
+ return (orig) => function (commands) {
178
+ if (!c.tracer() || c.nested()) return orig.apply(this, arguments)
179
+ let span = null
180
+ try {
181
+ span = batchSpan(this, commands, multiFromArgs(arguments))
182
+ } catch (_) {
183
+ span = null
184
+ }
185
+ if (!span) return orig.apply(this, arguments)
186
+ let p
187
+ try {
188
+ p = c.outer(orig, this, arguments)
189
+ } catch (err) {
190
+ c.end(span, err)
191
+ throw err
192
+ }
193
+ return finishWith(p, (v, err) => c.end(span, err, err ? r.errorCode(err) : undefined))
194
+ }
195
+ }
196
+
197
+ /** dist/lib/client/index.js: the RedisClient class (default export). */
198
+ function patchClient (exports, info) {
199
+ const Client = exports && (exports.default || exports.RedisClient || exports)
200
+ const proto = Client && Client.prototype
201
+ if (!proto || typeof proto.sendCommand !== 'function') {
202
+ c.disabled('@redis/client', 'RedisClient.prototype.sendCommand not found')
203
+ return
204
+ }
205
+ c.wrap(proto, 'sendCommand', wrapSendCommand)
206
+ if (typeof proto._executeMulti === 'function') c.wrap(proto, '_executeMulti', wrapBatch(() => true))
207
+ if (typeof proto._executePipeline === 'function') c.wrap(proto, '_executePipeline', wrapBatch(() => false))
208
+ if (typeof proto.multiExecutor === 'function') {
209
+ // 1.x: a chain id (third argument) marks a transaction
210
+ c.wrap(proto, 'multiExecutor', wrapBatch((args) => args.length >= 3 && args[2] !== undefined))
211
+ }
212
+ if (typeof proto.commandsExecutor === 'function') {
213
+ let transform = null
214
+ let transformReply = null
215
+ try {
216
+ const commander = require(require('path').join(info.dir, 'dist/lib/commander.js'))
217
+ if (typeof commander.transformCommandArguments === 'function') transform = commander.transformCommandArguments
218
+ if (typeof commander.transformCommandReply === 'function') transformReply = commander.transformCommandReply
219
+ } catch (_) {}
220
+ c.wrap(proto, 'commandsExecutor', (orig) => wrapCommandsExecutor(orig, transform, transformReply))
221
+ // 1.x binds the typed command methods to the executor it had when the module was evaluated
222
+ // (attachCommands); attaching them again with the wrapped executor routes them through it
223
+ try {
224
+ const path = require('path')
225
+ const commander = require(path.join(info.dir, 'dist/lib/commander.js'))
226
+ const commands = require(path.join(info.dir, 'dist/lib/client/commands.js'))
227
+ if (typeof commander.attachCommands === 'function' && commands && commands.default) {
228
+ commander.attachCommands({ BaseClass: Client, commands: commands.default, executor: proto.commandsExecutor })
229
+ }
230
+ } catch (err) {
231
+ c.disabled('@redis/client', 'typed commands not re-attached: ' + (err && err.message))
232
+ }
233
+ }
234
+ }
235
+
236
+ module.exports = { patchClient }
package/lib/itg/pg.js ADDED
@@ -0,0 +1,253 @@
1
+ // Copyright (c) 2026 Mike Warriner. All rights reserved. Proprietary — see LICENSE.
2
+ 'use strict'
3
+
4
+ // node-postgres (82 §5.3, ITG-110–113): `Client.prototype.query` in every form (text, text +
5
+ // values, config object, callback or promise, submittables such as pg-cursor / pg-query-stream)
6
+ // is one CLIENT span; `Pool.prototype.connect` binds its callback to the caller's context (so the
7
+ // query that `Pool.query` issues after a checkout belongs to the right request) and, with
8
+ // `db.poolSpans`, creates a `pool.acquire` span when the checkout has to wait. The native client
9
+ // (`pg.native`) is wrapped the same way.
10
+
11
+ const c = require('./common')
12
+ const sql = require('./sql')
13
+ const dbc = require('./dbcomment')
14
+
15
+ const TAGS = Symbol('pmx-pg-tags')
16
+ // a submittable (Query, Cursor, QueryStream) pmx tagged in place: { target, prefix }
17
+ const TAGGED = Symbol('pmx-pg-tagged')
18
+
19
+ function connTags (client) {
20
+ let tags = client[TAGS]
21
+ if (tags) return tags
22
+ tags = { 'db.system': 'postgresql' }
23
+ const p = client.connectionParameters || client
24
+ const db = client.database || p.database
25
+ const user = client.user || p.user
26
+ const host = client.host || p.host
27
+ const port = client.port || p.port
28
+ if (db) tags['db.name'] = String(db)
29
+ if (user) tags['db.user'] = String(user)
30
+ if (typeof host === 'string' && host.startsWith('/')) {
31
+ tags['net.peer.name'] = host.replace(/\/$/, '') + '/.s.PGSQL.' + (port || 5432)
32
+ } else {
33
+ if (host) tags['net.peer.name'] = String(host)
34
+ if (port) tags['net.peer.port'] = String(port)
35
+ }
36
+ try {
37
+ Object.defineProperty(client, TAGS, { value: tags, configurable: true })
38
+ } catch (_) {}
39
+ return tags
40
+ }
41
+
42
+ function makeSpan (t, client, text, name) {
43
+ const base = connTags(client)
44
+ const op = sql.operation(text)
45
+ const tags = Object.assign({}, base)
46
+ if (op) tags['db.operation'] = op
47
+ const st = sql.statementTag(text, t.db.statement, false)
48
+ if (st !== undefined) tags['db.statement'] = st
49
+ if (name) tags['db.statement.name'] = String(name)
50
+ const spanName = op ? (base['db.name'] ? op + ' ' + base['db.name'] : op) : (base['db.name'] || 'postgresql')
51
+ return c.clientSpan(t, spanName, tags)
52
+ }
53
+
54
+ const rows = (span) => (res) => {
55
+ if (res && typeof res.rowCount === 'number') span.setAttribute('db.rows', String(res.rowCount))
56
+ }
57
+
58
+ const codeOf = (err) => (err && typeof err.code === 'string' ? err.code : undefined)
59
+
60
+ /** Submittables (ITG-112): one span until ReadyForQuery or an error; db.rows = rows read. */
61
+ function traceSubmittable (span, q) {
62
+ let n = 0
63
+ const done = (err) => {
64
+ if (span.ended) return
65
+ span.setAttribute('db.rows', String(n))
66
+ c.end(span, err, codeOf(err))
67
+ }
68
+ const hook = (name, after) => {
69
+ const orig = q[name]
70
+ if (typeof orig !== 'function') return
71
+ Object.defineProperty(q, name, {
72
+ configurable: true,
73
+ writable: true,
74
+ enumerable: Object.prototype.propertyIsEnumerable.call(q, name),
75
+ value: function () {
76
+ try {
77
+ after.apply(null, arguments)
78
+ } catch (_) {}
79
+ return orig.apply(this, arguments)
80
+ }
81
+ })
82
+ }
83
+ hook('handleDataRow', () => { n++ })
84
+ hook('handleError', (err) => done(err || new Error('query failed')))
85
+ hook('handleReadyForQuery', () => done())
86
+ }
87
+
88
+ function wrapQuery (orig) {
89
+ return function query (config, values, callback) {
90
+ const t = c.dbTracer()
91
+ if (!t || config === null || config === undefined || c.nested()) return orig.apply(this, arguments)
92
+ let span = null
93
+ let text
94
+ try {
95
+ if (typeof config === 'object') untag(config)
96
+ text = typeof config === 'string' ? config : config.text
97
+ if (typeof text !== 'string' && typeof config.submit === 'function') text = config.text || (config.cursor && config.cursor.text)
98
+ span = makeSpan(t, this, typeof text === 'string' ? text : '', typeof config === 'object' ? config.name : undefined)
99
+ } catch (_) {
100
+ span = null
101
+ }
102
+ if (!span) return orig.apply(this, arguments)
103
+ const args = Array.prototype.slice.call(arguments)
104
+ try {
105
+ comment(t, span, config, text, args)
106
+ } catch (_) {}
107
+ try {
108
+ if (typeof config.submit === 'function') {
109
+ traceSubmittable(span, config)
110
+ return orig.apply(this, args)
111
+ }
112
+ // callback forms: (text, cb), (text, values, cb), (config, cb), config.callback
113
+ let cbIndex = -1
114
+ if (typeof values === 'function') cbIndex = 1
115
+ else if (typeof callback === 'function') cbIndex = 2
116
+ if (cbIndex !== -1) {
117
+ const cb = c.bind(args[cbIndex])
118
+ args[cbIndex] = function (err, res) {
119
+ if (!err) rows(span)(res)
120
+ c.end(span, err || undefined, codeOf(err))
121
+ return cb.apply(this, arguments)
122
+ }
123
+ return orig.apply(this, args)
124
+ }
125
+ if (typeof config === 'object' && typeof config.callback === 'function') {
126
+ const cb = c.bind(config.callback)
127
+ args[0] = Object.assign(Object.create(Object.getPrototypeOf(args[0])), args[0], {
128
+ callback (err, res) {
129
+ if (!err) rows(span)(res)
130
+ c.end(span, err || undefined, codeOf(err))
131
+ return cb.apply(this, arguments)
132
+ }
133
+ })
134
+ return orig.apply(this, args)
135
+ }
136
+ } catch (err) {
137
+ c.end(span)
138
+ return orig.apply(this, arguments)
139
+ }
140
+ let result
141
+ try {
142
+ result = orig.apply(this, args)
143
+ } catch (err) {
144
+ c.end(span, err, codeOf(err))
145
+ throw err
146
+ }
147
+ if (c.isThenable(result)) return c.settle(result, span, rows(span), codeOf)
148
+ c.end(span)
149
+ return result
150
+ }
151
+ }
152
+
153
+ /**
154
+ * 96 SVM-501/502: prepend the trace comment to the statement (args[0] replaced by a copy; a
155
+ * submittable's own text is set in place). Named statements (`name`) are prepared and cached by
156
+ * name: skipped with `on`; with `force` the copy is sent unnamed (parsed every time).
157
+ */
158
+ function comment (t, span, config, text, args) {
159
+ const named = typeof config === 'object' && config.name !== undefined && config.name !== null && config.name !== ''
160
+ if (!dbc.wants(t, span, text, named)) return
161
+ const prefix = dbc.sqlPrefix(t, span)
162
+ if (typeof config === 'string') {
163
+ args[0] = prefix + config
164
+ } else if (typeof config.submit === 'function') {
165
+ if (named) return // the app's object would have to be renamed
166
+ // tagged in place (the app holds the object); untag() removes it before a re-execution
167
+ const target = config.cursor && typeof config.cursor.text === 'string' && config.text === undefined ? config.cursor : config
168
+ if (typeof target.text !== 'string') return
169
+ target.text = prefix + target.text
170
+ try {
171
+ Object.defineProperty(config, TAGGED, { value: { target, prefix }, configurable: true, writable: true })
172
+ } catch (_) {}
173
+ } else if (typeof config.text === 'string') {
174
+ const copy = Object.assign(Object.create(Object.getPrototypeOf(config)), config, { text: prefix + config.text })
175
+ if (named) copy.name = undefined
176
+ args[0] = copy
177
+ }
178
+ }
179
+
180
+ /** A submittable reused by the app: remove the comment pmx added at its previous execution. */
181
+ function untag (config) {
182
+ const tag = config[TAGGED]
183
+ if (!tag) return
184
+ config[TAGGED] = undefined
185
+ const { target, prefix } = tag
186
+ if (typeof target.text === 'string' && target.text.startsWith(prefix)) target.text = target.text.slice(prefix.length)
187
+ }
188
+
189
+ /** 96 SVM-505: application_name = the pmx app name when the app set none (or PGAPPNAME). */
190
+ function wrapClientConnect (orig) {
191
+ return function connect () {
192
+ try {
193
+ const p = this.connectionParameters
194
+ const t = p && c.dbTracer()
195
+ if (t && !p.application_name && !p.fallback_application_name) {
196
+ const name = dbc.connectionAppName(t)
197
+ if (name) p.fallback_application_name = name
198
+ }
199
+ } catch (_) {}
200
+ return orig.apply(this, arguments)
201
+ }
202
+ }
203
+
204
+ function wrapConnect (orig) {
205
+ return function connect (cb) {
206
+ const t = c.tracer()
207
+ if (!t) return orig.apply(this, arguments)
208
+ let span = null
209
+ try {
210
+ const max = this.options && this.options.max
211
+ if (t.db && t.db.poolSpans && c.currentSpan() &&
212
+ this.idleCount === 0 && typeof max === 'number' && this.totalCount >= max) {
213
+ span = c.clientSpan(t, 'pool.acquire', {
214
+ 'db.system': 'postgresql',
215
+ 'db.pool.size': String(this.totalCount),
216
+ 'db.pool.max_size': String(max),
217
+ 'db.pool.waiting': String(this.waitingCount)
218
+ }, 'INTERNAL')
219
+ }
220
+ } catch (_) {
221
+ span = null
222
+ }
223
+ if (typeof cb === 'function') {
224
+ const bound = c.bind(cb)
225
+ return orig.call(this, function (err) {
226
+ c.end(span, err || undefined)
227
+ return bound.apply(this, arguments)
228
+ })
229
+ }
230
+ const p = orig.apply(this, arguments)
231
+ if (span && c.isThenable(p)) return c.settle(p, span)
232
+ return p
233
+ }
234
+ }
235
+
236
+ function patchClient (Client) {
237
+ if (!Client || !Client.prototype || typeof Client.prototype.query !== 'function') {
238
+ c.disabled('pg', 'Client.prototype.query not found')
239
+ return
240
+ }
241
+ c.wrap(Client.prototype, 'query', wrapQuery)
242
+ if (typeof Client.prototype.connect === 'function') c.wrap(Client.prototype, 'connect', wrapClientConnect)
243
+ }
244
+
245
+ function patchPool (Pool) {
246
+ if (!Pool || !Pool.prototype || typeof Pool.prototype.connect !== 'function') {
247
+ c.disabled('pg-pool', 'Pool.prototype.connect not found')
248
+ return
249
+ }
250
+ c.wrap(Pool.prototype, 'connect', wrapConnect)
251
+ }
252
+
253
+ module.exports = { patchClient, patchPool, connTags }