@dudousxd/nestjs-diagnostics 0.7.2 → 0.8.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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @dudousxd/nestjs-diagnostics
2
2
 
3
+ ## 0.8.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#71](https://github.com/DavideCarvalho/nestjs-diagnostics/pull/71) [`f4c910b`](https://github.com/DavideCarvalho/nestjs-diagnostics/commit/f4c910b0651ee9419aa452268f23646533d10306) Thanks [@DavideCarvalho](https://github.com/DavideCarvalho)! - Add `subscribeOfficial(lib, types | 'all', fn)`, which subscribes to the first-party `nestjs:<lib>:<type>` channels (`@nestjs/resilience`, `workflows`, `authentication`, `authorization`, `mail`) and wraps each raw payload in the Aviary envelope, with `traceId` from the context accessor and `source: 'nestjs'`. The channel lists are exported per lib (`NESTJS_*_CHANNELS`, `OFFICIAL_CHANNELS`), pinned to `OFFICIAL_PACKAGE_VERSIONS`, and guarded by a drift test. Aviary never publishes on `nestjs:*`.
8
+
3
9
  ## 0.7.2
4
10
 
5
11
  ### Patch Changes
package/README.md CHANGED
@@ -94,6 +94,45 @@ const off = onChannelRegistered(subscribe);
94
94
  This is exactly how `@dudousxd/nestjs-diagnostics-telescope`'s single generic
95
95
  watcher records every diagnostic event in the Telescope dashboard.
96
96
 
97
+ ## First-party `nestjs:*` channels
98
+
99
+ The official NestJS packages publish their own events on
100
+ `diagnostics_channel` with the convention `nestjs:<lib>:<type>`: a raw
101
+ payload, with no envelope and no traceId (see
102
+ [docs.nestjs.com](https://docs.nestjs.com)). **`nestjs:*` is the first-party
103
+ convention, and Aviary never publishes on it.** `aviary:*` is only for what the
104
+ official packages don't emit.
105
+
106
+ `subscribeOfficial` lets one consumer record both. It wraps each official
107
+ payload in the Aviary envelope, with `traceId` taken from the context accessor
108
+ and `source: 'nestjs'`:
109
+
110
+ ```ts
111
+ import { subscribeOfficial } from '@dudousxd/nestjs-diagnostics';
112
+
113
+ const off = subscribeOfficial('resilience', ['retry', 'circuit-open'], (event, channel) => {
114
+ // event: { v, ts, lib: 'resilience', event: 'retry', traceId, payload, source: 'nestjs' }
115
+ });
116
+ subscribeOfficial('authentication', 'all', record);
117
+ off();
118
+ ```
119
+
120
+ The channel lists are exported per lib in one file, pinned to the official
121
+ versions they were verified against (`OFFICIAL_PACKAGE_VERSIONS`). A test
122
+ re-reads the installed packages and fails if a channel is added or renamed.
123
+
124
+ | Lib | Constant | Channels |
125
+ | --- | --- | --- |
126
+ | `@nestjs/resilience` 0.0.3 | `NESTJS_RESILIENCE_CHANNELS` | 9 |
127
+ | `@nestjs/workflows` 0.0.5 | `NESTJS_WORKFLOWS_CHANNELS` | 19 |
128
+ | `@nestjs/authentication` 0.1.5 | `NESTJS_AUTHENTICATION_CHANNELS` | 13 |
129
+ | `@nestjs/authorization` 0.1.3 | `NESTJS_AUTHORIZATION_CHANNELS` | 1 (`denied`) |
130
+ | `@nestjs/mail` 0.0.2 | `NESTJS_MAIL_CHANNELS` | 2 (`sent`, `failed`) |
131
+
132
+ These channels are not added to the `aviary:*` registry
133
+ (`registeredChannels`/`onChannelRegistered`). Their lists are fixed, so subscribe
134
+ to them explicitly.
135
+
97
136
  ## API
98
137
 
99
138
  | Export | Description |
@@ -106,7 +145,9 @@ watcher records every diagnostic event in the Telescope dashboard.
106
145
  | `onChannelRegistered(cb)` | Notified once per future channel registration; returns an unsubscribe. |
107
146
  | `setContextAccessor(accessor \| null)` | Register the accessor `emit` reads `traceId` from. |
108
147
  | `CONTEXT_ACCESSOR` | Shared DI token for the optional context accessor. |
109
- | `DiagnosticEvent`, `EmitOptions`, `ContextAccessor` | Types. |
148
+ | `subscribeOfficial(lib, types \| 'all', fn)` | Subscribe to first-party `nestjs:<lib>:<type>` channels, each payload wrapped in the envelope (+ `source: 'nestjs'`). Returns an unsubscribe. |
149
+ | `OFFICIAL_CHANNELS`, `NESTJS_*_CHANNELS`, `OFFICIAL_PACKAGE_VERSIONS`, `officialChannelNames()` | The pinned first-party channel lists. |
150
+ | `DiagnosticEvent`, `EmitOptions`, `ContextAccessor`, `OfficialDiagnosticEvent` | Types. |
110
151
 
111
152
  ## Reacting to events in NestJS — `@OnDiagnostic`
112
153
 
package/dist/index.d.ts CHANGED
@@ -4,6 +4,7 @@ export { CHANNEL_PREFIX, channelName, emit, getChannel, SCHEMA_VERSION } from '.
4
4
  export { claimDiagnostics, isDiagnosticClaimed } from './claims.js';
5
5
  export { assertCapabilityNaming } from './conformance.js';
6
6
  export { CONTEXT_ACCESSOR, type ContextAccessor, type ContextStore, getContextAccessor, resolveTraceId, setContextAccessor, type UserRef, } from './context-accessor.js';
7
+ export { NESTJS_AUTHENTICATION_CHANNELS, NESTJS_AUTHORIZATION_CHANNELS, NESTJS_MAIL_CHANNELS, NESTJS_RESILIENCE_CHANNELS, NESTJS_WORKFLOWS_CHANNELS, OFFICIAL_CHANNEL_PREFIX, OFFICIAL_CHANNELS, OFFICIAL_PACKAGE_VERSIONS, type OfficialChannelName, type OfficialDiagnosticEvent, type OfficialLib, type OfficialListener, type OfficialType, officialChannelNames, subscribeOfficial, } from './official.js';
7
8
  export { onChannelRegistered, registerChannel, registeredChannels, resetRegistry, } from './registry.js';
8
9
  export { SPAN_SCHEMA_VERSION, trace, type TraceChannelNames, traceChannelNames, type TracingChannel, tracingChannel, } from './trace.js';
9
10
  export type { ChannelRegistry, DiagnosticEvent, EmitOptions, EventOf, LibOf, PayloadOf, SpanEvent, SpanPhase, TraceOptions, } from './types.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,YAAY,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,IAAI,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC7F,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACpE,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EACL,gBAAgB,EAChB,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,kBAAkB,EAClB,cAAc,EACd,kBAAkB,EAClB,KAAK,OAAO,GACb,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,mBAAmB,EACnB,eAAe,EACf,kBAAkB,EAClB,aAAa,GACd,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,mBAAmB,EACnB,KAAK,EACL,KAAK,iBAAiB,EACtB,iBAAiB,EACjB,KAAK,cAAc,EACnB,cAAc,GACf,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,eAAe,EACf,eAAe,EACf,WAAW,EACX,OAAO,EACP,KAAK,EACL,SAAS,EACT,SAAS,EACT,SAAS,EACT,YAAY,GACb,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,YAAY,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,IAAI,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC7F,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACpE,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EACL,gBAAgB,EAChB,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,kBAAkB,EAClB,cAAc,EACd,kBAAkB,EAClB,KAAK,OAAO,GACb,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,8BAA8B,EAC9B,6BAA6B,EAC7B,oBAAoB,EACpB,0BAA0B,EAC1B,yBAAyB,EACzB,uBAAuB,EACvB,iBAAiB,EACjB,yBAAyB,EACzB,KAAK,mBAAmB,EACxB,KAAK,uBAAuB,EAC5B,KAAK,WAAW,EAChB,KAAK,gBAAgB,EACrB,KAAK,YAAY,EACjB,oBAAoB,EACpB,iBAAiB,GAClB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,mBAAmB,EACnB,eAAe,EACf,kBAAkB,EAClB,aAAa,GACd,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,mBAAmB,EACnB,KAAK,EACL,KAAK,iBAAiB,EACtB,iBAAiB,EACjB,KAAK,cAAc,EACnB,cAAc,GACf,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,eAAe,EACf,eAAe,EACf,WAAW,EACX,OAAO,EACP,KAAK,EACL,SAAS,EACT,SAAS,EACT,SAAS,EACT,YAAY,GACb,MAAM,YAAY,CAAC"}
package/dist/index.js CHANGED
@@ -3,6 +3,7 @@ export { CHANNEL_PREFIX, channelName, emit, getChannel, SCHEMA_VERSION } from '.
3
3
  export { claimDiagnostics, isDiagnosticClaimed } from './claims.js';
4
4
  export { assertCapabilityNaming } from './conformance.js';
5
5
  export { CONTEXT_ACCESSOR, getContextAccessor, resolveTraceId, setContextAccessor, } from './context-accessor.js';
6
+ export { NESTJS_AUTHENTICATION_CHANNELS, NESTJS_AUTHORIZATION_CHANNELS, NESTJS_MAIL_CHANNELS, NESTJS_RESILIENCE_CHANNELS, NESTJS_WORKFLOWS_CHANNELS, OFFICIAL_CHANNEL_PREFIX, OFFICIAL_CHANNELS, OFFICIAL_PACKAGE_VERSIONS, officialChannelNames, subscribeOfficial, } from './official.js';
6
7
  export { onChannelRegistered, registerChannel, registeredChannels, resetRegistry, } from './registry.js';
7
8
  export { SPAN_SCHEMA_VERSION, trace, traceChannelNames, tracingChannel, } from './trace.js';
8
9
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,IAAI,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC7F,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACpE,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EACL,gBAAgB,EAGhB,kBAAkB,EAClB,cAAc,EACd,kBAAkB,GAEnB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,mBAAmB,EACnB,eAAe,EACf,kBAAkB,EAClB,aAAa,GACd,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,mBAAmB,EACnB,KAAK,EAEL,iBAAiB,EAEjB,cAAc,GACf,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,IAAI,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC7F,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACpE,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EACL,gBAAgB,EAGhB,kBAAkB,EAClB,cAAc,EACd,kBAAkB,GAEnB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,8BAA8B,EAC9B,6BAA6B,EAC7B,oBAAoB,EACpB,0BAA0B,EAC1B,yBAAyB,EACzB,uBAAuB,EACvB,iBAAiB,EACjB,yBAAyB,EAMzB,oBAAoB,EACpB,iBAAiB,GAClB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,mBAAmB,EACnB,eAAe,EACf,kBAAkB,EAClB,aAAa,GACd,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,mBAAmB,EACnB,KAAK,EAEL,iBAAiB,EAEjB,cAAc,GACf,MAAM,YAAY,CAAC"}
@@ -0,0 +1,83 @@
1
+ import type { DiagnosticEvent } from './types.js';
2
+ /**
3
+ * The first-party NestJS packages publish raw events on `node:diagnostics_channel`
4
+ * under the fixed convention `nestjs:<lib>:<type>`: no envelope, no traceId. This
5
+ * file is the ONE place their channel lists live. Each list was read from the
6
+ * published tarball of the version in {@link OFFICIAL_PACKAGE_VERSIONS}, and
7
+ * `test/official.spec.ts` re-reads the installed (pinned) packages and fails when
8
+ * a channel is added, renamed or removed.
9
+ *
10
+ * Aviary never publishes on `nestjs:*`. It only subscribes, through
11
+ * {@link subscribeOfficial}. `aviary:*` is for what the official packages don't emit.
12
+ */
13
+ export declare const OFFICIAL_CHANNEL_PREFIX = "nestjs";
14
+ /** `nestjs:<lib>:<type>`, the first-party channel name. */
15
+ export type OfficialChannelName<L extends string, T extends string> = `nestjs:${L}:${T}`;
16
+ type ChannelMap<L extends string, T extends readonly string[]> = {
17
+ readonly [K in T[number]]: OfficialChannelName<L, K>;
18
+ };
19
+ /** `@nestjs/resilience`: the 9 fixed channels of `events/resilience.channels.js`. */
20
+ export declare const NESTJS_RESILIENCE_CHANNELS: ChannelMap<"resilience", readonly ["retry", "timeout", "circuit-open", "circuit-half-open", "circuit-closed", "circuit-rejected", "bulkhead-rejected", "rate-limited", "fallback"]>;
21
+ /** `@nestjs/workflows`: `channelFor(event.type)` over the 19 `WorkflowEvent` types. */
22
+ export declare const NESTJS_WORKFLOWS_CHANNELS: ChannelMap<"workflows", readonly ["workflow-started", "workflow-resumed", "workflow-suspended", "workflow-compensating", "workflow-completed", "workflow-failed", "workflow-cancelled", "workflow-compensation-failed", "step-completed", "step-failed", "step-compensated", "child-started", "signal-received", "signal-timed-out", "journal-large", "custom-status", "workflow-retried", "workflow-deleted", "schedule-skipped"]>;
23
+ /** `@nestjs/authentication`: `channelFor(event.type)` over the 13 `AuthenticationEvent` types. */
24
+ export declare const NESTJS_AUTHENTICATION_CHANNELS: ChannelMap<"authentication", readonly ["sign-in", "sign-out", "mfa-verified", "mfa-failed", "mfa-enabled", "mfa-disabled", "recovery-codes-generated", "refresh-token-reused", "password-reset-requested", "password-reset", "email-verified", "magic-link-refused", "session-touch-failed"]>;
25
+ /** `@nestjs/authorization`: the single `deniedChannel`. */
26
+ export declare const NESTJS_AUTHORIZATION_CHANNELS: ChannelMap<"authorization", readonly ["denied"]>;
27
+ /** `@nestjs/mail`: `channels.sent` and `channels.failed`. */
28
+ export declare const NESTJS_MAIL_CHANNELS: ChannelMap<"mail", readonly ["sent", "failed"]>;
29
+ /** Every known first-party lib → its `type → channel name` map. */
30
+ export declare const OFFICIAL_CHANNELS: Readonly<{
31
+ resilience: ChannelMap<"resilience", readonly ["retry", "timeout", "circuit-open", "circuit-half-open", "circuit-closed", "circuit-rejected", "bulkhead-rejected", "rate-limited", "fallback"]>;
32
+ workflows: ChannelMap<"workflows", readonly ["workflow-started", "workflow-resumed", "workflow-suspended", "workflow-compensating", "workflow-completed", "workflow-failed", "workflow-cancelled", "workflow-compensation-failed", "step-completed", "step-failed", "step-compensated", "child-started", "signal-received", "signal-timed-out", "journal-large", "custom-status", "workflow-retried", "workflow-deleted", "schedule-skipped"]>;
33
+ authentication: ChannelMap<"authentication", readonly ["sign-in", "sign-out", "mfa-verified", "mfa-failed", "mfa-enabled", "mfa-disabled", "recovery-codes-generated", "refresh-token-reused", "password-reset-requested", "password-reset", "email-verified", "magic-link-refused", "session-touch-failed"]>;
34
+ authorization: ChannelMap<"authorization", readonly ["denied"]>;
35
+ mail: ChannelMap<"mail", readonly ["sent", "failed"]>;
36
+ }>;
37
+ /** The official package version each list above was verified against. */
38
+ export declare const OFFICIAL_PACKAGE_VERSIONS: Readonly<{
39
+ resilience: string;
40
+ workflows: string;
41
+ authentication: string;
42
+ authorization: string;
43
+ mail: string;
44
+ }>;
45
+ /** A first-party lib whose channels are listed here. */
46
+ export type OfficialLib = keyof typeof OFFICIAL_CHANNELS;
47
+ /** The event types `L` publishes, e.g. `'retry' | 'timeout' | …` for `'resilience'`. */
48
+ export type OfficialType<L extends OfficialLib> = keyof (typeof OFFICIAL_CHANNELS)[L] & string;
49
+ /** Every first-party channel name, flattened. */
50
+ export declare function officialChannelNames(): string[];
51
+ /**
52
+ * The Aviary envelope wrapped around an official payload: the usual
53
+ * {@link DiagnosticEvent} plus `source: 'nestjs'`, so an observer that records
54
+ * both kinds (Telescope's `DiagnosticWatcher`) can tell them apart. Aviary's own
55
+ * `emit()` envelopes carry no `source`.
56
+ */
57
+ export interface OfficialDiagnosticEvent<TPayload = unknown> extends DiagnosticEvent<TPayload> {
58
+ source: 'nestjs';
59
+ }
60
+ /** Receives each wrapped official event and the `nestjs:*` channel it came from. */
61
+ export type OfficialListener = (event: OfficialDiagnosticEvent, channel: string) => void;
62
+ /**
63
+ * Subscribe to first-party `nestjs:<lib>:<type>` channels and receive each raw
64
+ * payload wrapped in the Aviary envelope `{ v, ts, lib, event, traceId, payload,
65
+ * source: 'nestjs' }`:
66
+ *
67
+ * - `traceId` comes from the registered context accessor. Official publishers run
68
+ * subscribers synchronously in the caller's async context, so it is the
69
+ * request's traceId;
70
+ * - `durationMs` is copied from the payload when it carries a numeric one
71
+ * (e.g. workflows' `step-completed`).
72
+ *
73
+ * Returns an unsubscribe function. A throwing `fn` never reaches the official
74
+ * publisher. Subscribing only reads `nestjs:*`; Aviary never publishes there.
75
+ *
76
+ * ```ts
77
+ * const off = subscribeOfficial('resilience', ['retry', 'circuit-open'], (e) => log(e));
78
+ * subscribeOfficial('authentication', 'all', record);
79
+ * ```
80
+ */
81
+ export declare function subscribeOfficial<L extends OfficialLib>(lib: L, types: readonly OfficialType<L>[] | 'all', fn: OfficialListener): () => void;
82
+ export {};
83
+ //# sourceMappingURL=official.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"official.d.ts","sourceRoot":"","sources":["../src/official.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,uBAAuB,WAAW,CAAC;AAEhD,2DAA2D;AAC3D,MAAM,MAAM,mBAAmB,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;AAEzF,KAAK,UAAU,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,SAAS,MAAM,EAAE,IAAI;IAC/D,QAAQ,EAAE,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,GAAG,mBAAmB,CAAC,CAAC,EAAE,CAAC,CAAC;CACrD,CAAC;AAWF,qFAAqF;AACrF,eAAO,MAAM,0BAA0B,qLAUrC,CAAC;AAEH,uFAAuF;AACvF,eAAO,MAAM,yBAAyB,qaAoBpC,CAAC;AAEH,kGAAkG;AAClG,eAAO,MAAM,8BAA8B,+RAczC,CAAC;AAEH,2DAA2D;AAC3D,eAAO,MAAM,6BAA6B,kDAA0C,CAAC;AAErF,6DAA6D;AAC7D,eAAO,MAAM,oBAAoB,iDAAyC,CAAC;AAE3E,mEAAmE;AACnE,eAAO,MAAM,iBAAiB;;;;;;EAM5B,CAAC;AAEH,yEAAyE;AACzE,eAAO,MAAM,yBAAyB;;;;;;EAME,CAAC;AAEzC,wDAAwD;AACxD,MAAM,MAAM,WAAW,GAAG,MAAM,OAAO,iBAAiB,CAAC;AAEzD,wFAAwF;AACxF,MAAM,MAAM,YAAY,CAAC,CAAC,SAAS,WAAW,IAAI,MAAM,CAAC,OAAO,iBAAiB,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC;AAE/F,iDAAiD;AACjD,wBAAgB,oBAAoB,IAAI,MAAM,EAAE,CAE/C;AAED;;;;;GAKG;AACH,MAAM,WAAW,uBAAuB,CAAC,QAAQ,GAAG,OAAO,CAAE,SAAQ,eAAe,CAAC,QAAQ,CAAC;IAC5F,MAAM,EAAE,QAAQ,CAAC;CAClB;AAED,oFAAoF;AACpF,MAAM,MAAM,gBAAgB,GAAG,CAAC,KAAK,EAAE,uBAAuB,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;AAEzF;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,WAAW,EACrD,GAAG,EAAE,CAAC,EACN,KAAK,EAAE,SAAS,YAAY,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,EACzC,EAAE,EAAE,gBAAgB,GACnB,MAAM,IAAI,CA8CZ"}
@@ -0,0 +1,159 @@
1
+ import diagnostics_channel from 'node:diagnostics_channel';
2
+ import { SCHEMA_VERSION } from './channel.js';
3
+ import { resolveTraceId } from './context-accessor.js';
4
+ /**
5
+ * The first-party NestJS packages publish raw events on `node:diagnostics_channel`
6
+ * under the fixed convention `nestjs:<lib>:<type>`: no envelope, no traceId. This
7
+ * file is the ONE place their channel lists live. Each list was read from the
8
+ * published tarball of the version in {@link OFFICIAL_PACKAGE_VERSIONS}, and
9
+ * `test/official.spec.ts` re-reads the installed (pinned) packages and fails when
10
+ * a channel is added, renamed or removed.
11
+ *
12
+ * Aviary never publishes on `nestjs:*`. It only subscribes, through
13
+ * {@link subscribeOfficial}. `aviary:*` is for what the official packages don't emit.
14
+ */
15
+ export const OFFICIAL_CHANNEL_PREFIX = 'nestjs';
16
+ function channelsOf(lib, types) {
17
+ return Object.freeze(Object.fromEntries(types.map((type) => [type, `${OFFICIAL_CHANNEL_PREFIX}:${lib}:${type}`])));
18
+ }
19
+ /** `@nestjs/resilience`: the 9 fixed channels of `events/resilience.channels.js`. */
20
+ export const NESTJS_RESILIENCE_CHANNELS = channelsOf('resilience', [
21
+ 'retry',
22
+ 'timeout',
23
+ 'circuit-open',
24
+ 'circuit-half-open',
25
+ 'circuit-closed',
26
+ 'circuit-rejected',
27
+ 'bulkhead-rejected',
28
+ 'rate-limited',
29
+ 'fallback',
30
+ ]);
31
+ /** `@nestjs/workflows`: `channelFor(event.type)` over the 19 `WorkflowEvent` types. */
32
+ export const NESTJS_WORKFLOWS_CHANNELS = channelsOf('workflows', [
33
+ 'workflow-started',
34
+ 'workflow-resumed',
35
+ 'workflow-suspended',
36
+ 'workflow-compensating',
37
+ 'workflow-completed',
38
+ 'workflow-failed',
39
+ 'workflow-cancelled',
40
+ 'workflow-compensation-failed',
41
+ 'step-completed',
42
+ 'step-failed',
43
+ 'step-compensated',
44
+ 'child-started',
45
+ 'signal-received',
46
+ 'signal-timed-out',
47
+ 'journal-large',
48
+ 'custom-status',
49
+ 'workflow-retried',
50
+ 'workflow-deleted',
51
+ 'schedule-skipped',
52
+ ]);
53
+ /** `@nestjs/authentication`: `channelFor(event.type)` over the 13 `AuthenticationEvent` types. */
54
+ export const NESTJS_AUTHENTICATION_CHANNELS = channelsOf('authentication', [
55
+ 'sign-in',
56
+ 'sign-out',
57
+ 'mfa-verified',
58
+ 'mfa-failed',
59
+ 'mfa-enabled',
60
+ 'mfa-disabled',
61
+ 'recovery-codes-generated',
62
+ 'refresh-token-reused',
63
+ 'password-reset-requested',
64
+ 'password-reset',
65
+ 'email-verified',
66
+ 'magic-link-refused',
67
+ 'session-touch-failed',
68
+ ]);
69
+ /** `@nestjs/authorization`: the single `deniedChannel`. */
70
+ export const NESTJS_AUTHORIZATION_CHANNELS = channelsOf('authorization', ['denied']);
71
+ /** `@nestjs/mail`: `channels.sent` and `channels.failed`. */
72
+ export const NESTJS_MAIL_CHANNELS = channelsOf('mail', ['sent', 'failed']);
73
+ /** Every known first-party lib → its `type → channel name` map. */
74
+ export const OFFICIAL_CHANNELS = Object.freeze({
75
+ resilience: NESTJS_RESILIENCE_CHANNELS,
76
+ workflows: NESTJS_WORKFLOWS_CHANNELS,
77
+ authentication: NESTJS_AUTHENTICATION_CHANNELS,
78
+ authorization: NESTJS_AUTHORIZATION_CHANNELS,
79
+ mail: NESTJS_MAIL_CHANNELS,
80
+ });
81
+ /** The official package version each list above was verified against. */
82
+ export const OFFICIAL_PACKAGE_VERSIONS = Object.freeze({
83
+ resilience: '0.0.3',
84
+ workflows: '0.0.5',
85
+ authentication: '0.1.5',
86
+ authorization: '0.1.3',
87
+ mail: '0.0.2',
88
+ });
89
+ /** Every first-party channel name, flattened. */
90
+ export function officialChannelNames() {
91
+ return Object.values(OFFICIAL_CHANNELS).flatMap((map) => Object.values(map));
92
+ }
93
+ /**
94
+ * Subscribe to first-party `nestjs:<lib>:<type>` channels and receive each raw
95
+ * payload wrapped in the Aviary envelope `{ v, ts, lib, event, traceId, payload,
96
+ * source: 'nestjs' }`:
97
+ *
98
+ * - `traceId` comes from the registered context accessor. Official publishers run
99
+ * subscribers synchronously in the caller's async context, so it is the
100
+ * request's traceId;
101
+ * - `durationMs` is copied from the payload when it carries a numeric one
102
+ * (e.g. workflows' `step-completed`).
103
+ *
104
+ * Returns an unsubscribe function. A throwing `fn` never reaches the official
105
+ * publisher. Subscribing only reads `nestjs:*`; Aviary never publishes there.
106
+ *
107
+ * ```ts
108
+ * const off = subscribeOfficial('resilience', ['retry', 'circuit-open'], (e) => log(e));
109
+ * subscribeOfficial('authentication', 'all', record);
110
+ * ```
111
+ */
112
+ export function subscribeOfficial(lib, types, fn) {
113
+ const map = OFFICIAL_CHANNELS[lib];
114
+ if (!map) {
115
+ throw new TypeError(`subscribeOfficial: unknown first-party lib '${String(lib)}'`);
116
+ }
117
+ const selected = types === 'all' ? Object.keys(map) : [...new Set(types)];
118
+ if (selected.length === 0) {
119
+ throw new TypeError(`subscribeOfficial: no '${lib}' channel selected; pass one or more types, or 'all'`);
120
+ }
121
+ const subscriptions = [];
122
+ for (const type of selected) {
123
+ const name = map[type];
124
+ if (name === undefined) {
125
+ throw new TypeError(`subscribeOfficial: '${lib}' publishes no '${type}' channel`);
126
+ }
127
+ const listener = (message) => {
128
+ try {
129
+ const duration = message?.durationMs;
130
+ const event = {
131
+ v: SCHEMA_VERSION,
132
+ ts: Date.now(),
133
+ lib,
134
+ event: type,
135
+ traceId: resolveTraceId(),
136
+ payload: message,
137
+ source: 'nestjs',
138
+ ...(typeof duration === 'number' && { durationMs: duration }),
139
+ };
140
+ fn(event, name);
141
+ }
142
+ catch {
143
+ // An observer must never change the outcome of the call it observes.
144
+ }
145
+ };
146
+ diagnostics_channel.subscribe(name, listener);
147
+ subscriptions.push([name, listener]);
148
+ }
149
+ let active = true;
150
+ return () => {
151
+ if (!active)
152
+ return;
153
+ active = false;
154
+ for (const [name, listener] of subscriptions) {
155
+ diagnostics_channel.unsubscribe(name, listener);
156
+ }
157
+ };
158
+ }
159
+ //# sourceMappingURL=official.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"official.js","sourceRoot":"","sources":["../src/official.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dudousxd/nestjs-diagnostics",
3
- "version": "0.7.2",
3
+ "version": "0.8.0",
4
4
  "description": "A standard convention for @dudousxd/nestjs-* libs to emit observability events over node:diagnostics_channel.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -8,6 +8,19 @@
8
8
  "url": "git+https://github.com/DavideCarvalho/nestjs-diagnostics.git",
9
9
  "directory": "packages/core"
10
10
  },
11
+ "homepage": "https://davidecarvalho.github.io/aviary/docs/diagnostics",
12
+ "bugs": {
13
+ "url": "https://github.com/DavideCarvalho/nestjs-diagnostics/issues"
14
+ },
15
+ "keywords": [
16
+ "nestjs",
17
+ "diagnostics",
18
+ "diagnostics-channel",
19
+ "observability",
20
+ "telemetry",
21
+ "events",
22
+ "tanstack-intent"
23
+ ],
11
24
  "author": "Davi Carvalho <davi@goflip.ai>",
12
25
  "type": "module",
13
26
  "main": "./dist/index.js",
@@ -58,9 +71,14 @@
58
71
  "**/trace.ts"
59
72
  ],
60
73
  "devDependencies": {
61
- "@nestjs/common": "12.0.1",
62
- "@nestjs/core": "12.0.1",
63
- "@nestjs/testing": "12.0.1",
74
+ "@nestjs/authentication": "0.1.5",
75
+ "@nestjs/authorization": "0.1.3",
76
+ "@nestjs/common": "12.0.4",
77
+ "@nestjs/core": "12.0.4",
78
+ "@nestjs/mail": "0.0.2",
79
+ "@nestjs/resilience": "0.0.3",
80
+ "@nestjs/testing": "12.0.4",
81
+ "@nestjs/workflows": "0.0.5",
64
82
  "@types/node": "20.19.43",
65
83
  "reflect-metadata": "0.2.2",
66
84
  "typescript": "5.9.3",
@@ -70,15 +88,6 @@
70
88
  "engines": {
71
89
  "node": ">=20"
72
90
  },
73
- "keywords": [
74
- "nestjs",
75
- "diagnostics",
76
- "diagnostics-channel",
77
- "observability",
78
- "telemetry",
79
- "events",
80
- "tanstack-intent"
81
- ],
82
91
  "scripts": {
83
92
  "build": "tsc -p tsconfig.json",
84
93
  "test": "vitest run --passWithNoTests",
@@ -162,3 +162,12 @@ The core swallows errors in `emit`/`registerChannel`, but a listener you attach
162
162
  directly runs inside the synchronous `publish` — guard it yourself, as the
163
163
  Telescope watcher's `safeRecord` does.
164
164
  Source: `packages/telescope/src/diagnostic.watcher.ts` (`safeRecord`).
165
+
166
+ ## First-party `nestjs:*` channels
167
+
168
+ The official NestJS packages (`@nestjs/resilience`, `workflows`,
169
+ `authentication`, `authorization`, `mail`) publish raw payloads on
170
+ `nestjs:<lib>:<type>`. These channels are not in the registry. Use
171
+ `subscribeOfficial(lib, types | 'all', fn)`, which wraps each payload in the
172
+ envelope with `traceId` and `source: 'nestjs'`. Never publish on `nestjs:*`
173
+ yourself. Lists: `OFFICIAL_CHANNELS` / `NESTJS_*_CHANNELS`.