@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
package/README.md CHANGED
@@ -1,3 +1,70 @@
1
- # Temporary Holding Version
1
+ # @aidra/pmx-io
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ In-process instrumentation for Node.js and Bun apps that report to
4
+ [prodman](https://github.com/westonpark/prodman): metrics, custom actions, exceptions, events,
5
+ logs, CPU and heap profiling, and distributed tracing with database and message-broker
6
+ integrations. It keeps the `@pm2/io` API, so code written for PM2.io works unchanged.
7
+
8
+ * **Run by pmx:** pmx injects this library into every app it starts; nothing to install. An
9
+ installed copy stays idle and `require` returns the injected instance.
10
+ * **Run by PM2 / pm2_prodman:** reports through the PM2 daemon, like `@pm2/io`.
11
+ * **Standalone** (a container's `node app.js`, an ECS task, a worker): connects to prodman
12
+ itself as a one-process agent.
13
+
14
+ Node.js 18 or later, or Bun 1.1 or later. No dependencies.
15
+
16
+ ## Install
17
+
18
+ ```sh
19
+ npm install @aidra/pmx-io
20
+ ```
21
+
22
+ Code that requires `@pm2/io` can keep doing so: install the package under that name.
23
+
24
+ ```sh
25
+ npm install @pm2/io@npm:@aidra/pmx-io
26
+ ```
27
+
28
+ ## Standalone mode
29
+
30
+ Set the bucket keys (prodman: *Bucket settings → Connect servers*) and an app name:
31
+
32
+ ```sh
33
+ PRODMAN_URL=https://prodman.example.com
34
+ PM2_PUBLIC_KEY=<public key>
35
+ PM2_SECRET_KEY=<secret key>
36
+ PM2_APP_NAME=orders-api
37
+ ```
38
+
39
+ Then load the library without changing code:
40
+
41
+ ```sh
42
+ npx pmx-io-node run -- node server.js
43
+ node --require @aidra/pmx-io/standalone server.js
44
+ NODE_OPTIONS='--import @aidra/pmx-io/standalone.mjs' node server.mjs
45
+ ```
46
+
47
+ or require it first thing in the app:
48
+
49
+ ```js
50
+ const io = require('@aidra/pmx-io')
51
+ io.init({ apmOptions: { sendLogs: true, application: 'shop' } })
52
+ ```
53
+
54
+ Settings, server names, containers and troubleshooting: prodman's documentation page
55
+ *Node.js apps without pmx* (`/docs/node-apm`).
56
+
57
+ ## API
58
+
59
+ ```js
60
+ const io = require('@aidra/pmx-io')
61
+
62
+ io.metric({ name: 'Queue length' }).set(12)
63
+ io.counter({ name: 'Jobs done' }).inc()
64
+ io.action('flush cache', (reply) => reply({ ok: true }))
65
+ io.notifyError(new Error('payment failed'), { custom: { orderId } })
66
+ ```
67
+
68
+ TypeScript declarations are included (`index.d.ts`), a superset of `@pm2/io`'s.
69
+
70
+ Proprietary. © 2026 Mike Warriner.
@@ -0,0 +1,143 @@
1
+ #!/usr/bin/env node
2
+ // Copyright (c) 2026 Mike Warriner. All rights reserved. Proprietary — see LICENSE.
3
+ 'use strict'
4
+
5
+ // The standalone launcher (80 NIO-261): `pmx-io-node run [--] <command> [args…]`. Never
6
+ // activates the library. Prepends the standalone preload to NODE_OPTIONS (plus `--import` of
7
+ // standalone.mjs when env trace=true and the Node runs module hooks), sets PMX_IO_STANDALONE=1
8
+ // and PMX_IO_ROOT_PID=<its pid>, inserts `--preload <abs>/standalone.cjs` after a `bun` command,
9
+ // then replaces itself with the command through process.execve (same pid: signals reach the app,
10
+ // its exit status is the app's). With PMX_IO_OTEL_EXPORT=1 it also sets the OTEL_* variables
11
+ // that point an app's own OpenTelemetry SDK at prodman (85 OTX-052, lib/otel/env.js).
12
+ // Without process.execve it spawns the command with inherited
13
+ // stdio, names itself PMX_IO_ROOT_PPID (the child becomes the root), forwards signals and exits
14
+ // with the child's status.
15
+
16
+ const path = require('path')
17
+ const fs = require('fs')
18
+ const { pathToFileURL } = require('url')
19
+
20
+ const PKG = path.resolve(__dirname, '..')
21
+ const CJS = path.join(PKG, 'standalone.cjs')
22
+ const MJS = path.join(PKG, 'standalone.mjs')
23
+ const FORWARDED = ['SIGTERM', 'SIGINT', 'SIGHUP', 'SIGQUIT', 'SIGUSR1', 'SIGUSR2']
24
+ const USAGE = 'usage: pmx-io-node run [--] <command> [args...]\n' +
25
+ ' Runs <command> with @aidra/pmx-io in standalone mode (reports to prodman without pmx).\n' +
26
+ ' Needs PM2_PUBLIC_KEY, PM2_SECRET_KEY and PM2_APP_NAME (and PRODMAN_URL) in the environment.\n'
27
+
28
+ function quote (p) {
29
+ return '"' + p.replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '"'
30
+ }
31
+
32
+ /** Node ≥ 18.19 / ≥ 20.6 runs --import preloads (INJ-010 rule). */
33
+ function supportsImport () {
34
+ const [a, b] = process.versions.node.split('.').map(Number)
35
+ return (a === 18 && b >= 19) || (a === 20 && b >= 6) || a > 20
36
+ }
37
+
38
+ function isBun (cmd) {
39
+ const base = path.basename(cmd)
40
+ return base === 'bun' || base.endsWith('bun')
41
+ }
42
+
43
+ function which (cmd) {
44
+ if (cmd.includes('/')) return cmd
45
+ for (const dir of (process.env.PATH || '').split(path.delimiter)) {
46
+ if (!dir) continue
47
+ const full = path.join(dir, cmd)
48
+ try {
49
+ fs.accessSync(full, fs.constants.X_OK)
50
+ if (fs.statSync(full).isFile()) return full
51
+ } catch (_) {}
52
+ }
53
+ return null
54
+ }
55
+
56
+ function prepare (argv, env) {
57
+ const tokens = [`--require ${quote(CJS)}`]
58
+ if (env.trace === 'true' && supportsImport()) tokens.push(`--import ${quote(pathToFileURL(MJS).href)}`)
59
+ const cur = env.NODE_OPTIONS || ''
60
+ if (!cur.includes(CJS)) env.NODE_OPTIONS = tokens.join(' ') + (cur ? ' ' + cur : '')
61
+ env.PMX_IO_STANDALONE = '1'
62
+ if (env.PMX_IO_OTEL_EXPORT === '1') {
63
+ // 85 OTX-052: wire the app's own OpenTelemetry SDK to prodman's OTLP endpoint (opt-in)
64
+ try {
65
+ require('../lib/otel/env').wireLauncher(env)
66
+ } catch (err) {
67
+ process.stderr.write('pmx-io-node: OpenTelemetry wiring failed: ' + (err && err.message) + '\n')
68
+ }
69
+ }
70
+ const args = argv.slice()
71
+ if (isBun(args[0]) && !args.includes(CJS)) args.splice(1, 0, '--preload', CJS)
72
+ return args
73
+ }
74
+
75
+ function main (argv) {
76
+ let rest = argv.slice(2)
77
+ if (rest[0] !== 'run') {
78
+ process.stderr.write(USAGE)
79
+ return 2
80
+ }
81
+ rest = rest.slice(1)
82
+ if (rest[0] === '--') rest = rest.slice(1)
83
+ if (rest.length === 0) {
84
+ process.stderr.write(USAGE)
85
+ return 2
86
+ }
87
+ const env = Object.assign({}, process.env)
88
+ delete env.PMX_IO_ROOT_PPID
89
+ const args = prepare(rest, env)
90
+ const file = which(args[0])
91
+ if (!file) {
92
+ process.stderr.write(`pmx-io-node: ${args[0]}: command not found\n`)
93
+ return 127
94
+ }
95
+ if (typeof process.execve === 'function' && env.PMX_IO_LAUNCHER_NO_EXECVE !== '1') {
96
+ env.PMX_IO_ROOT_PID = String(process.pid)
97
+ try {
98
+ process.execve(file, args, env)
99
+ } catch (err) {
100
+ process.stderr.write(`pmx-io-node: cannot run ${args[0]}: ${err && err.message}\n`)
101
+ return 126
102
+ }
103
+ }
104
+ // fallback: a parent process
105
+ delete env.PMX_IO_ROOT_PID
106
+ env.PMX_IO_ROOT_PPID = String(process.pid)
107
+ const { spawn } = require('child_process')
108
+ const child = spawn(file, args.slice(1), { stdio: 'inherit', env, argv0: args[0] })
109
+ for (const sig of FORWARDED) {
110
+ try {
111
+ process.on(sig, () => {
112
+ try {
113
+ child.kill(sig)
114
+ } catch (_) {}
115
+ })
116
+ } catch (_) {}
117
+ }
118
+ child.on('error', (err) => {
119
+ process.stderr.write(`pmx-io-node: cannot run ${args[0]}: ${err && err.message}\n`)
120
+ process.exit(126)
121
+ })
122
+ child.on('exit', (code, signal) => {
123
+ if (signal) {
124
+ const n = require('os').constants.signals[signal] || 1
125
+ if (process.pid === 1) process.exit(128 + n)
126
+ for (const sig of FORWARDED) process.removeAllListeners(sig)
127
+ try {
128
+ process.kill(process.pid, signal)
129
+ } catch (_) {}
130
+ setTimeout(() => process.exit(128 + n), 100)
131
+ return
132
+ }
133
+ process.exit(code === null ? 1 : code)
134
+ })
135
+ return null
136
+ }
137
+
138
+ if (require.main === module) {
139
+ const code = main(process.argv)
140
+ if (code !== null && code !== undefined) process.exitCode = code
141
+ }
142
+
143
+ module.exports = { main, prepare, isBun }
package/index.d.ts ADDED
@@ -0,0 +1,381 @@
1
+ // Copyright (c) 2026 Mike Warriner. All rights reserved. Proprietary — see LICENSE.
2
+
3
+ // Type declarations for @aidra/pmx-io. The surface is a superset of @pm2/io's public API
4
+ // (80 NIO-001), so `import io from '@pm2/io'` code type-checks against this package.
5
+
6
+ export type MetricMeasurement =
7
+ | 'min' | 'max' | 'sum' | 'count' | 'variance' | 'mean' | 'stddev'
8
+ | 'median' | 'p75' | 'p95' | 'p99' | 'p999' | 'ema'
9
+
10
+ export declare const MetricMeasurements: { readonly [K in MetricMeasurement]: K }
11
+ export declare const MetricType: {
12
+ readonly meter: 'meter'
13
+ readonly histogram: 'histogram'
14
+ readonly counter: 'counter'
15
+ readonly gauge: 'gauge'
16
+ readonly metric: 'metric'
17
+ }
18
+
19
+ export interface MetricOptions {
20
+ /** Metric name; the key in axm:monitor. */
21
+ name: string
22
+ /** Free-form type label, reported as `type`. */
23
+ id?: string
24
+ unit?: string
25
+ /** Whether history is kept (default true). */
26
+ historic?: boolean
27
+ }
28
+
29
+ export interface GaugeOptions extends MetricOptions {
30
+ /** Passive gauge: evaluated at every push. */
31
+ value?: () => number | string | boolean | null | undefined
32
+ }
33
+
34
+ export interface CounterOptions extends MetricOptions {
35
+ count?: number
36
+ }
37
+
38
+ export interface MeterOptions extends MetricOptions {
39
+ /** Rate unit in seconds (default 1). */
40
+ samples?: number
41
+ seconds?: number
42
+ /** Averaging window in seconds (default 60). */
43
+ timeframe?: number
44
+ /** Tick interval in ms (default 5000). */
45
+ tickInterval?: number
46
+ }
47
+
48
+ export interface HistogramOptions extends MetricOptions {
49
+ measurement?: MetricMeasurement
50
+ }
51
+
52
+ export interface MetricBulkOptions extends GaugeOptions, CounterOptions, MeterOptions, HistogramOptions {
53
+ type?: 'counter' | 'gauge' | 'histogram' | 'meter' | 'metric'
54
+ }
55
+
56
+ export declare class Gauge {
57
+ set (value: number | string | boolean): void
58
+ val (): any
59
+ isUsed (): boolean
60
+ }
61
+
62
+ export declare class Counter {
63
+ inc (n?: number): void
64
+ dec (n?: number): void
65
+ reset (count?: number): void
66
+ val (): number
67
+ isUsed (): boolean
68
+ }
69
+
70
+ export declare class Meter {
71
+ mark (n?: number): void
72
+ val (): number
73
+ isUsed (): boolean
74
+ }
75
+
76
+ export interface HistogramResults {
77
+ min: number | null
78
+ max: number | null
79
+ sum: number
80
+ variance: number | null
81
+ mean: number
82
+ count: number
83
+ median: number | null
84
+ p75: number | null
85
+ p95: number | null
86
+ p99: number | null
87
+ p999: number | null
88
+ ema: number
89
+ }
90
+
91
+ export declare class Histogram {
92
+ update (value: number): void
93
+ val (): number | null
94
+ percentiles (fractions: number[]): { [fraction: number]: number | null }
95
+ fullResults (): HistogramResults
96
+ getMin (): number | null
97
+ getMax (): number | null
98
+ getSum (): number
99
+ getCount (): number
100
+ getEma (): number
101
+ isUsed (): boolean
102
+ }
103
+
104
+ export interface IOConfig {
105
+ catchExceptions?: boolean
106
+ profiling?: boolean | {
107
+ cpuJS?: boolean
108
+ heapSnapshot?: boolean
109
+ heapSampling?: boolean
110
+ implementation?: 'inspector' | 'addon' | 'both' | 'none'
111
+ }
112
+ metrics?: {
113
+ eventLoop?: boolean | { eventLoopActive?: boolean, eventLoopDelay?: boolean }
114
+ network?: boolean | { upload?: boolean, download?: boolean }
115
+ http?: boolean
116
+ /** `HTTP Outbound*` metrics (80 NIO-055), default true */
117
+ httpOutbound?: boolean
118
+ runtime?: boolean | { gcNewPause?: boolean, gcOldPause?: boolean, pageFaults?: boolean, contextSwitchs?: boolean }
119
+ v8?: boolean | { [key: string]: boolean }
120
+ fds?: boolean
121
+ }
122
+ actions?: { eventLoopDump?: boolean }
123
+ standalone?: boolean
124
+ /**
125
+ * The prodman application this process belongs to (pmx 97 PAP-050): 1-63 of `[a-z0-9._-]`,
126
+ * case-insensitive. Standalone only (init > `PMX_APPLICATION` > `default`); under pmx the
127
+ * daemon decides and a differing value is ignored with a warning.
128
+ */
129
+ application?: string
130
+ apmOptions?: {
131
+ url?: string
132
+ publicKey?: string
133
+ secretKey?: string
134
+ appName?: string
135
+ serverName?: string
136
+ sendLogs?: boolean
137
+ disableLogs?: boolean
138
+ logFilter?: string | RegExp
139
+ /** standalone log capture (80 NIO-267), default 'stdio' */
140
+ captureLogs?: 'stdio' | 'off'
141
+ /** `http://[user:pass@]host:port` (default env PMX_IO_PROXY) */
142
+ proxy?: string
143
+ }
144
+ tracing?: boolean | TracingConfig
145
+ /** report exceptions that frameworks turn into ≥ 500 responses (80 NIO-223), default true */
146
+ httpErrors?: boolean
147
+ /** instrument Node descendants and worker threads through this process (80 NIO-290), default true */
148
+ children?: boolean
149
+ [key: string]: any
150
+ }
151
+
152
+ export type UrlMatcher = string | RegExp | ((url: string, request: any) => boolean)
153
+
154
+ export interface TracingConfig {
155
+ /** default true when the object is given */
156
+ enabled?: boolean
157
+ serviceName?: string
158
+ /** false: no CLIENT spans for outbound HTTP */
159
+ outbound?: boolean
160
+ /** accepted and ignored (A.6 quirk); use sampleRatio */
161
+ samplingRate?: number
162
+ ignoreIncomingPaths?: UrlMatcher[]
163
+ ignoreOutgoingUrls?: UrlMatcher[]
164
+ /** trace-id-ratio sampling of new traces, [0, 1], default 1 (80 NIO-205) */
165
+ sampleRatio?: number
166
+ /** default true (80 NIO-205) */
167
+ exportUnsampledErrors?: boolean
168
+ /** export rate limit, default 2000, 0 = unlimited (80 NIO-206) */
169
+ maxSpansPerSecond?: number
170
+ /** duration floor in µs, default 1000 (0 with NODE_ENV=test) */
171
+ minDurationUs?: number
172
+ db?: boolean | {
173
+ statement?: 'sanitized' | 'raw' | 'off',
174
+ poolSpans?: boolean,
175
+ /** Trace comment on SQL statements / MongoDB commands of sampled traces (default 'on'; env PMX_DB_COMMENT). */
176
+ comment?: 'on' | 'off' | 'force',
177
+ /** Set PostgreSQL application_name / MongoDB appName to the app name when the app set none (default true). */
178
+ applicationName?: boolean
179
+ }
180
+ messaging?: boolean | { rootProducers?: boolean, redisStreamFields?: boolean, payloadCarrier?: boolean, maxLinks?: number }
181
+ logCorrelation?: boolean | { console?: boolean }
182
+ baggageTags?: string[]
183
+ /** route-less SERVER span names normalised (80 Part G.2), default true */
184
+ normalizePaths?: boolean
185
+ createSpanWithNet?: boolean
186
+ detailedDatabasesCalls?: boolean
187
+ [key: string]: any
188
+ }
189
+
190
+ /** OpenTelemetry-compatible numbers are accepted too: 0 internal … 4 consumer. */
191
+ export type SpanKind = 'internal' | 'server' | 'client' | 'producer' | 'consumer' | string | 0 | 1 | 2 | 3 | 4
192
+
193
+ export type Carrier =
194
+ | { [name: string]: any }
195
+ | Map<string, any>
196
+ | Array<[string, any]>
197
+ | { get (name: string): any }
198
+ | { set (name: string, value: any): any, has (name: string): boolean }
199
+
200
+ /** The W3C trace context of a span (80 NIO-240). */
201
+ export declare class SpanContext {
202
+ constructor (opts: { traceId: string, spanId: string, sampled?: boolean, traceState?: string | null, baggage?: { [k: string]: string }, isRemote?: boolean })
203
+ readonly traceId: string
204
+ readonly spanId: string
205
+ readonly sampled: boolean
206
+ readonly traceState: string | null
207
+ readonly baggage: Readonly<{ [k: string]: string }>
208
+ readonly isRemote: boolean
209
+ traceparent (): string
210
+ }
211
+
212
+ export type Link = SpanContext | { context: SpanContext, attributes?: { [k: string]: any } }
213
+
214
+ export interface SpanOptions {
215
+ kind?: SpanKind
216
+ attributes?: { [k: string]: any }
217
+ /** undefined: the current span (or a new trace); a Span; a SpanContext; or NEW_TRACE */
218
+ parent?: Span | SpanContext | typeof NEW_TRACE
219
+ links?: Link[]
220
+ /** ms since the epoch */
221
+ startTime?: number | Date
222
+ }
223
+
224
+ export interface Span {
225
+ /** null for the no-op span (tracing disabled) */
226
+ readonly context: SpanContext | null
227
+ setAttribute (key: string, value: any): this
228
+ setAttributes (attributes: { [k: string]: any }): this
229
+ addEvent (name: string): this
230
+ addLink (context: SpanContext, attributes?: { [k: string]: any }): this
231
+ recordException (error: any): this
232
+ setStatus (status: 'OK' | 'ERROR' | 'UNSET' | string | { code: 0 | 1 | 2, message?: string }, message?: string): this
233
+ end (endTime?: number | Date): void
234
+ isRecording (): boolean
235
+ traceparent (): string | null
236
+ }
237
+
238
+ export interface Tracer {
239
+ readonly enabled: boolean
240
+ startSpan (name: string, options?: SpanOptions): Span
241
+ /** runs fn with the span current; the span ends when fn returns, throws or its promise settles */
242
+ startActiveSpan<T> (name: string, fn: (span: Span) => T): T
243
+ startActiveSpan<T> (name: string, options: SpanOptions, fn: (span: Span) => T): T
244
+ withSpan<T> (span: Span, fn: (span: Span) => T): T
245
+ currentSpan (): Span | undefined
246
+ }
247
+
248
+ export interface InjectOptions {
249
+ context?: SpanContext
250
+ overwrite?: boolean
251
+ asBytes?: boolean
252
+ }
253
+
254
+ export interface ConsumerSpanOptions {
255
+ name?: string
256
+ system?: string
257
+ destination?: string
258
+ attributes?: { [k: string]: any }
259
+ }
260
+
261
+ export interface TraceContextFields {
262
+ trace_id?: string
263
+ span_id?: string
264
+ trace_sampled?: boolean
265
+ }
266
+
267
+ /** As `parent`, starts a new trace even inside a span (80 NIO-247). */
268
+ export declare const NEW_TRACE: { readonly __newTrace?: never }
269
+
270
+ export type ActionReply = (data: any) => void
271
+ export type ActionHandler =
272
+ | ((reply: ActionReply) => any)
273
+ | ((params: any, reply: ActionReply) => any)
274
+
275
+ export interface ScopedActionStream {
276
+ send (data: any): void
277
+ error (data: any): void
278
+ end (data: any): void
279
+ }
280
+
281
+ export interface ErrorContext {
282
+ http?: { [key: string]: any }
283
+ custom?: { [key: string]: any }
284
+ [key: string]: any
285
+ }
286
+
287
+ export declare class Entrypoint {
288
+ io: PMX
289
+ constructor ()
290
+ onStart (cb: (err?: Error) => void): void
291
+ onStop (err: Error | undefined, cb: () => void, code: number | null, signal: string | null): void
292
+ sensors (): void
293
+ events (): void
294
+ actuators (): void
295
+ conf (): IOConfig | undefined
296
+ }
297
+
298
+ export interface Probe {
299
+ metric (opts: GaugeOptions | string): Gauge | undefined
300
+ gauge (opts: GaugeOptions | string): Gauge | undefined
301
+ counter (opts: CounterOptions | string): Counter | undefined
302
+ meter (opts: MeterOptions | string): Meter | undefined
303
+ histogram (opts: HistogramOptions | string): Histogram | undefined
304
+ transpose (name: string | { name: string, data: () => any }, fn?: () => any): void
305
+ }
306
+
307
+ export interface PMX {
308
+ init (config?: IOConfig): PMX
309
+ destroy (): void
310
+ getConfig (): IOConfig | undefined
311
+ notifyError (err: Error | any, context?: ErrorContext): void
312
+ /** Legacy alias of notifyError. */
313
+ notify (err: Error | any, context?: ErrorContext): void
314
+ metric (opts: GaugeOptions | string): Gauge | undefined
315
+ gauge (opts: GaugeOptions | string): Gauge | undefined
316
+ counter (opts: CounterOptions | string): Counter | undefined
317
+ meter (opts: MeterOptions | string): Meter | undefined
318
+ histogram (opts: HistogramOptions | string): Histogram | undefined
319
+ metrics (defs: MetricBulkOptions | MetricBulkOptions[]): any[]
320
+ probe (): Probe
321
+ transpose (name: string | { name: string, data: () => any }, fn?: () => any): void
322
+ action (name: string, handler: ActionHandler): void
323
+ action (name: string, opts: any, handler: ActionHandler): void
324
+ action (def: { name: string, options?: any, action: ActionHandler }): void
325
+ scopedAction (name: string, handler: (params: any, stream: ScopedActionStream) => any): void
326
+ emit (name: string, data: { [key: string]: any }): void
327
+ onExit (callback: (code: number | null, signal: string | null) => any): void
328
+ getTracer (): Tracer
329
+ inject<C extends Carrier> (carrier: C, options?: InjectOptions): C
330
+ extract (carrier: Carrier): SpanContext | null
331
+ getBaggage (): Readonly<{ [k: string]: string }>
332
+ withBaggage<T> (entries: { [k: string]: any }, fn: () => T): T
333
+ wrap<F extends (...args: any[]) => any> (fn: F): F
334
+ traceContext (): TraceContextFields
335
+ consumerSpan<T> (carrier: Carrier, options: ConsumerSpanOptions, fn: (span: Span) => T): T
336
+ SpanContext: typeof SpanContext
337
+ NEW_TRACE: typeof NEW_TRACE
338
+ initModule (opts?: any, cb?: (err: Error | null, conf: any) => void): any
339
+ configureModule (opts: { [key: string]: any }): void
340
+ expressErrorHandler (): (err: any, req: any, res: any, next: (err?: any) => void) => void
341
+ koaErrorHandler (): (ctx: any, next: () => Promise<any>) => Promise<void>
342
+ Entrypoint: typeof Entrypoint
343
+ MetricType: typeof MetricType
344
+ MetricMeasurements: typeof MetricMeasurements
345
+ version: string
346
+ }
347
+
348
+ export declare function init (config?: IOConfig): PMX
349
+ export declare function destroy (): void
350
+ export declare function getConfig (): IOConfig | undefined
351
+ export declare function notifyError (err: Error | any, context?: ErrorContext): void
352
+ export declare function notify (err: Error | any, context?: ErrorContext): void
353
+ export declare function metric (opts: GaugeOptions | string): Gauge | undefined
354
+ export declare function gauge (opts: GaugeOptions | string): Gauge | undefined
355
+ export declare function counter (opts: CounterOptions | string): Counter | undefined
356
+ export declare function meter (opts: MeterOptions | string): Meter | undefined
357
+ export declare function histogram (opts: HistogramOptions | string): Histogram | undefined
358
+ export declare function metrics (defs: MetricBulkOptions | MetricBulkOptions[]): any[]
359
+ export declare function probe (): Probe
360
+ export declare function transpose (name: string | { name: string, data: () => any }, fn?: () => any): void
361
+ export declare function action (name: string, handler: ActionHandler): void
362
+ export declare function action (name: string, opts: any, handler: ActionHandler): void
363
+ export declare function action (def: { name: string, options?: any, action: ActionHandler }): void
364
+ export declare function scopedAction (name: string, handler: (params: any, stream: ScopedActionStream) => any): void
365
+ export declare function emit (name: string, data: { [key: string]: any }): void
366
+ export declare function onExit (callback: (code: number | null, signal: string | null) => any): void
367
+ export declare function getTracer (): Tracer
368
+ export declare function inject<C extends Carrier> (carrier: C, options?: InjectOptions): C
369
+ export declare function extract (carrier: Carrier): SpanContext | null
370
+ export declare function getBaggage (): Readonly<{ [k: string]: string }>
371
+ export declare function withBaggage<T> (entries: { [k: string]: any }, fn: () => T): T
372
+ export declare function wrap<F extends (...args: any[]) => any> (fn: F): F
373
+ export declare function traceContext (): TraceContextFields
374
+ export declare function consumerSpan<T> (carrier: Carrier, options: ConsumerSpanOptions, fn: (span: Span) => T): T
375
+ export declare function initModule (opts?: any, cb?: (err: Error | null, conf: any) => void): any
376
+ export declare function configureModule (opts: { [key: string]: any }): void
377
+ export declare function expressErrorHandler (): (err: any, req: any, res: any, next: (err?: any) => void) => void
378
+ export declare function koaErrorHandler (): (ctx: any, next: () => Promise<any>) => Promise<void>
379
+
380
+ declare const io: PMX
381
+ export default io
package/index.js ADDED
@@ -0,0 +1,27 @@
1
+ // Copyright (c) 2026 Mike Warriner. All rights reserved. Proprietary — see LICENSE.
2
+ 'use strict'
3
+
4
+ // Public entry (CommonJS). The module value is the process-wide instance stored under
5
+ // Symbol.for('@pm2/io') (80 IO-001): when an instance already exists (the one injected by
6
+ // pmx, or any compatible copy) it is returned and nothing new is created; otherwise a new
7
+ // instance is created, initialised with the default configuration and stored.
8
+
9
+ const SYMBOL = Symbol.for('@pm2/io')
10
+
11
+ let instance
12
+ if (Object.prototype.hasOwnProperty.call(globalThis, SYMBOL)) {
13
+ instance = globalThis[SYMBOL]
14
+ } else {
15
+ const { PMX } = require('./lib/pmx')
16
+ instance = new PMX()
17
+ Object.defineProperty(globalThis, SYMBOL, { value: instance, writable: true, configurable: true, enumerable: false })
18
+ try {
19
+ instance.init()
20
+ } catch (err) {
21
+ try {
22
+ process.stderr.write('[@pm2/io] initialisation failed: ' + (err && err.message) + '\n')
23
+ } catch (_) {}
24
+ }
25
+ }
26
+
27
+ module.exports = instance
package/index.mjs ADDED
@@ -0,0 +1,47 @@
1
+ // Copyright (c) 2026 Mike Warriner. All rights reserved. Proprietary — see LICENSE.
2
+
3
+ // Public entry (ES module): the default export is the process-wide instance (the same object
4
+ // as require('@aidra/pmx-io')); every API method is also a named export (80 NIO-002).
5
+
6
+ import { createRequire } from 'node:module'
7
+
8
+ const require = createRequire(import.meta.url)
9
+ const io = require('./index.js')
10
+
11
+ const call = (name) => (...args) => (typeof io[name] === 'function' ? io[name](...args) : undefined)
12
+
13
+ export default io
14
+ export const init = call('init')
15
+ export const destroy = call('destroy')
16
+ export const getConfig = call('getConfig')
17
+ export const notifyError = call('notifyError')
18
+ export const notify = call('notify')
19
+ export const metric = call('metric')
20
+ export const gauge = call('gauge')
21
+ export const counter = call('counter')
22
+ export const meter = call('meter')
23
+ export const histogram = call('histogram')
24
+ export const metrics = call('metrics')
25
+ export const probe = call('probe')
26
+ export const transpose = call('transpose')
27
+ export const action = call('action')
28
+ export const scopedAction = call('scopedAction')
29
+ export const emit = call('emit')
30
+ export const onExit = call('onExit')
31
+ export const getTracer = call('getTracer')
32
+ export const initModule = call('initModule')
33
+ export const expressErrorHandler = call('expressErrorHandler')
34
+ export const koaErrorHandler = call('koaErrorHandler')
35
+ export const configureModule = call('configureModule')
36
+ export const inject = call('inject')
37
+ export const extract = call('extract')
38
+ export const getBaggage = call('getBaggage')
39
+ export const withBaggage = call('withBaggage')
40
+ export const wrap = call('wrap')
41
+ export const traceContext = call('traceContext')
42
+ export const consumerSpan = call('consumerSpan')
43
+ export const SpanContext = io.SpanContext
44
+ export const NEW_TRACE = io.NEW_TRACE
45
+ export const Entrypoint = io.Entrypoint
46
+ export const MetricType = io.MetricType
47
+ export const MetricMeasurements = io.MetricMeasurements