@dudousxd/nestjs-diagnostics 0.6.1 → 0.7.1

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,31 @@
1
1
  # @dudousxd/nestjs-diagnostics
2
2
 
3
+ ## 0.7.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [`96d300d`](https://github.com/DavideCarvalho/nestjs-diagnostics/commit/96d300d3af86b77fbdf16b307d986aec922945c4) Thanks [@DavideCarvalho](https://github.com/DavideCarvalho)! - add NestJS 12 to the supported peer range
8
+
9
+ ## 0.7.0
10
+
11
+ ### Minor Changes
12
+
13
+ - [`1568de4`](https://github.com/DavideCarvalho/nestjs-diagnostics/commit/1568de44f05770ad2559f7d6d4ea38d9d213d7e5) - Cross-lib dedup, no consumer config: lib-specific telescope watchers (nestjs-agent's,
14
+ nestjs-media's) record their `aviary:<lib>:<event>` channels as first-class typed entries — and
15
+ the generic bridge recorded them AGAIN as `diagnostic` entries unless the consumer hand-maintained
16
+ an exclude list. Now the libs resolve it themselves:
17
+
18
+ - core: `claimDiagnostics(lib, events)` registers `lib:event` keys in a process-global,
19
+ refcounted claim registry (`Symbol.for('aviary:diagnostics:claims')` — the raw convention is
20
+ documented so packages without this dependency can participate); returns a release fn.
21
+ `isDiagnosticClaimed(lib, event)` reads it.
22
+ - telescope: the generic watcher checks claims AT RECORD TIME (order-independent) and skips
23
+ claimed keys by default. `recordClaimed: true` opts back in; `exclude` still exists — its job
24
+ is muting noisy events (e.g. `media:upload.progress`), not dedup.
25
+
26
+ Lib watchers claim in their own upcoming releases; until then behavior is unchanged (nothing
27
+ claims → everything records, exactly as today).
28
+
3
29
  ## 0.6.1
4
30
 
5
31
  ### Patch Changes
@@ -0,0 +1,68 @@
1
+ /**
2
+ * A process-wide registry of which `aviary:<lib>:<event>` channels are already
3
+ * recorded as a first-class, typed Telescope entry by a lib-specific watcher
4
+ * (e.g. nestjs-agent's own watcher for `aviary:agent:*`, nestjs-media's for
5
+ * `aviary:media:*`). The generic `DiagnosticWatcher` in
6
+ * `@dudousxd/nestjs-diagnostics-telescope` auto-subscribes to EVERY registered
7
+ * channel (see {@link registerChannel}), so without this registry every event a
8
+ * claiming lib emits would be recorded TWICE: once as its own typed entry, once
9
+ * as a generic `diagnostic` entry. Claiming a key tells the generic watcher "I
10
+ * already record this — skip it by default."
11
+ *
12
+ * ## RAW convention (no dependency on this package required)
13
+ * The registry is a plain `Map<string, number>` behind a well-known
14
+ * `Symbol.for` key, so a package that does NOT want to depend on
15
+ * `@dudousxd/nestjs-diagnostics` can still participate by replicating this
16
+ * structure exactly, without importing anything from here:
17
+ *
18
+ * - `Symbol.for('aviary:diagnostics:claims')` resolves to a
19
+ * `Map<string, number>` stored on `globalThis`.
20
+ * - Each key is `` `${lib}:${event}` `` — the same label the "Busiest events"
21
+ * dashboard panel and `DiagnosticWatcherOptions.exclude` use.
22
+ * - The value is a reference count, always `>= 1` while the key is claimed by
23
+ * at least one caller. Claiming an already-claimed key increments the count;
24
+ * releasing decrements it; the key is deleted (unclaimed again) only once the
25
+ * count reaches `0`. This is what lets two independent watchers claim the
26
+ * same key without one's release un-claiming it for the other.
27
+ * - A key is "claimed" iff the map has it; "unclaimed" iff absent (or its count
28
+ * reached `0` and was deleted).
29
+ *
30
+ * ## Record-time-check contract
31
+ * A generic observer (like `DiagnosticWatcher`) MUST call
32
+ * {@link isDiagnosticClaimed} at RECORD time — when an event is actually
33
+ * published — not at subscribe time. This makes claiming order-independent: a
34
+ * lib-specific watcher may call {@link claimDiagnostics} before or after the
35
+ * generic watcher's `register()` runs, and every event recorded after the claim
36
+ * exists is skipped either way. Checking once at subscribe time would miss
37
+ * claims registered later and could not un-skip a released claim either.
38
+ */
39
+ /**
40
+ * Claim `lib:event` for every event in `events`, so a generic observer skips
41
+ * them by default (see the record-time-check contract above). Call this once
42
+ * when a lib-specific watcher registers, passing every event it records as a
43
+ * typed entry.
44
+ *
45
+ * Reference-counted: claiming a key already claimed by a different call (e.g.
46
+ * two Telescope instances, or overlapping event lists) increments its count
47
+ * instead of overwriting it, so releasing one caller's claim never un-claims a
48
+ * key another caller still holds.
49
+ *
50
+ * Returns a release function that removes EXACTLY the keys this call added —
51
+ * decrementing each of `events`' counts by one, deleting the key only once its
52
+ * count reaches `0`. Idempotent: calling the release function more than once
53
+ * has no additional effect.
54
+ *
55
+ * ```ts
56
+ * const release = claimDiagnostics('agent', ['chat-request', 'tool-call']);
57
+ * // ... later, e.g. on watcher cleanup:
58
+ * release();
59
+ * ```
60
+ */
61
+ export declare function claimDiagnostics(lib: string, events: readonly string[]): () => void;
62
+ /**
63
+ * Whether `lib:event` is currently claimed by at least one
64
+ * {@link claimDiagnostics} call. Intended to be checked at record time by a
65
+ * generic observer, per the contract above.
66
+ */
67
+ export declare function isDiagnosticClaimed(lib: string, event: string): boolean;
68
+ //# sourceMappingURL=claims.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claims.d.ts","sourceRoot":"","sources":["../src/claims.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAeH;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,IAAI,CAgBnF;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAEvE"}
package/dist/claims.js ADDED
@@ -0,0 +1,98 @@
1
+ /**
2
+ * A process-wide registry of which `aviary:<lib>:<event>` channels are already
3
+ * recorded as a first-class, typed Telescope entry by a lib-specific watcher
4
+ * (e.g. nestjs-agent's own watcher for `aviary:agent:*`, nestjs-media's for
5
+ * `aviary:media:*`). The generic `DiagnosticWatcher` in
6
+ * `@dudousxd/nestjs-diagnostics-telescope` auto-subscribes to EVERY registered
7
+ * channel (see {@link registerChannel}), so without this registry every event a
8
+ * claiming lib emits would be recorded TWICE: once as its own typed entry, once
9
+ * as a generic `diagnostic` entry. Claiming a key tells the generic watcher "I
10
+ * already record this — skip it by default."
11
+ *
12
+ * ## RAW convention (no dependency on this package required)
13
+ * The registry is a plain `Map<string, number>` behind a well-known
14
+ * `Symbol.for` key, so a package that does NOT want to depend on
15
+ * `@dudousxd/nestjs-diagnostics` can still participate by replicating this
16
+ * structure exactly, without importing anything from here:
17
+ *
18
+ * - `Symbol.for('aviary:diagnostics:claims')` resolves to a
19
+ * `Map<string, number>` stored on `globalThis`.
20
+ * - Each key is `` `${lib}:${event}` `` — the same label the "Busiest events"
21
+ * dashboard panel and `DiagnosticWatcherOptions.exclude` use.
22
+ * - The value is a reference count, always `>= 1` while the key is claimed by
23
+ * at least one caller. Claiming an already-claimed key increments the count;
24
+ * releasing decrements it; the key is deleted (unclaimed again) only once the
25
+ * count reaches `0`. This is what lets two independent watchers claim the
26
+ * same key without one's release un-claiming it for the other.
27
+ * - A key is "claimed" iff the map has it; "unclaimed" iff absent (or its count
28
+ * reached `0` and was deleted).
29
+ *
30
+ * ## Record-time-check contract
31
+ * A generic observer (like `DiagnosticWatcher`) MUST call
32
+ * {@link isDiagnosticClaimed} at RECORD time — when an event is actually
33
+ * published — not at subscribe time. This makes claiming order-independent: a
34
+ * lib-specific watcher may call {@link claimDiagnostics} before or after the
35
+ * generic watcher's `register()` runs, and every event recorded after the claim
36
+ * exists is skipped either way. Checking once at subscribe time would miss
37
+ * claims registered later and could not un-skip a released claim either.
38
+ */
39
+ const CLAIMS_KEY = Symbol.for('aviary:diagnostics:claims');
40
+ const claimsGlobal = globalThis;
41
+ const claims = claimsGlobal[CLAIMS_KEY] ?? new Map();
42
+ claimsGlobal[CLAIMS_KEY] = claims;
43
+ /** The `lib:event` label used as the claim-store key, same shape as `exclude` entries. */
44
+ function claimKey(lib, event) {
45
+ return `${lib}:${event}`;
46
+ }
47
+ /**
48
+ * Claim `lib:event` for every event in `events`, so a generic observer skips
49
+ * them by default (see the record-time-check contract above). Call this once
50
+ * when a lib-specific watcher registers, passing every event it records as a
51
+ * typed entry.
52
+ *
53
+ * Reference-counted: claiming a key already claimed by a different call (e.g.
54
+ * two Telescope instances, or overlapping event lists) increments its count
55
+ * instead of overwriting it, so releasing one caller's claim never un-claims a
56
+ * key another caller still holds.
57
+ *
58
+ * Returns a release function that removes EXACTLY the keys this call added —
59
+ * decrementing each of `events`' counts by one, deleting the key only once its
60
+ * count reaches `0`. Idempotent: calling the release function more than once
61
+ * has no additional effect.
62
+ *
63
+ * ```ts
64
+ * const release = claimDiagnostics('agent', ['chat-request', 'tool-call']);
65
+ * // ... later, e.g. on watcher cleanup:
66
+ * release();
67
+ * ```
68
+ */
69
+ export function claimDiagnostics(lib, events) {
70
+ const keys = events.map((event) => claimKey(lib, event));
71
+ for (const key of keys) {
72
+ claims.set(key, (claims.get(key) ?? 0) + 1);
73
+ }
74
+ let released = false;
75
+ return () => {
76
+ if (released)
77
+ return;
78
+ released = true;
79
+ for (const key of keys) {
80
+ const count = claims.get(key);
81
+ if (count === undefined)
82
+ continue;
83
+ if (count <= 1)
84
+ claims.delete(key);
85
+ else
86
+ claims.set(key, count - 1);
87
+ }
88
+ };
89
+ }
90
+ /**
91
+ * Whether `lib:event` is currently claimed by at least one
92
+ * {@link claimDiagnostics} call. Intended to be checked at record time by a
93
+ * generic observer, per the contract above.
94
+ */
95
+ export function isDiagnosticClaimed(lib, event) {
96
+ return claims.has(claimKey(lib, event));
97
+ }
98
+ //# sourceMappingURL=claims.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claims.js","sourceRoot":"","sources":["../src/claims.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAKH,MAAM,UAAU,GAAG,MAAM,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAC;AAC3D,MAAM,YAAY,GAAG,UAA+D,CAAC;AACrF,MAAM,MAAM,GAAe,YAAY,CAAC,UAAU,CAAC,IAAI,IAAI,GAAG,EAAkB,CAAC;AACjF,YAAY,CAAC,UAAU,CAAC,GAAG,MAAM,CAAC;AAElC,0FAA0F;AAC1F,SAAS,QAAQ,CAAC,GAAW,EAAE,KAAa;IAC1C,OAAO,GAAG,GAAG,IAAI,KAAK,EAAE,CAAC;AAC3B,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAW,EAAE,MAAyB;IACrE,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC;IACzD,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAC9C,CAAC;IACD,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,OAAO,GAAG,EAAE;QACV,IAAI,QAAQ;YAAE,OAAO;QACrB,QAAQ,GAAG,IAAI,CAAC;QAChB,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAC9B,IAAI,KAAK,KAAK,SAAS;gBAAE,SAAS;YAClC,IAAI,KAAK,IAAI,CAAC;gBAAE,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;;gBAC9B,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;QAClC,CAAC;IACH,CAAC,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,GAAW,EAAE,KAAa;IAC5D,OAAO,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC;AAC1C,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  export { capability } from './capability.js';
2
2
  export type { CapabilityOf, CapabilityRegistry } from './capability.js';
3
- export { assertCapabilityNaming } from './conformance.js';
4
3
  export { CHANNEL_PREFIX, channelName, emit, getChannel, SCHEMA_VERSION } from './channel.js';
4
+ export { claimDiagnostics, isDiagnosticClaimed } from './claims.js';
5
+ export { assertCapabilityNaming } from './conformance.js';
5
6
  export { CONTEXT_ACCESSOR, type ContextAccessor, type ContextStore, getContextAccessor, resolveTraceId, setContextAccessor, type UserRef, } from './context-accessor.js';
6
7
  export { onChannelRegistered, registerChannel, registeredChannels, resetRegistry, } from './registry.js';
7
8
  export { SPAN_SCHEMA_VERSION, trace, type TraceChannelNames, traceChannelNames, type TracingChannel, tracingChannel, } from './trace.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,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,IAAI,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC7F,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,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
@@ -1,6 +1,7 @@
1
1
  export { capability } from './capability.js';
2
- export { assertCapabilityNaming } from './conformance.js';
3
2
  export { CHANNEL_PREFIX, channelName, emit, getChannel, SCHEMA_VERSION } from './channel.js';
3
+ export { claimDiagnostics, isDiagnosticClaimed } from './claims.js';
4
+ export { assertCapabilityNaming } from './conformance.js';
4
5
  export { CONTEXT_ACCESSOR, getContextAccessor, resolveTraceId, setContextAccessor, } from './context-accessor.js';
5
6
  export { onChannelRegistered, registerChannel, registeredChannels, resetRegistry, } from './registry.js';
6
7
  export { SPAN_SCHEMA_VERSION, trace, traceChannelNames, tracingChannel, } from './trace.js';
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,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,IAAI,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC7F,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,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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dudousxd/nestjs-diagnostics",
3
- "version": "0.6.1",
3
+ "version": "0.7.1",
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": {
@@ -25,8 +25,8 @@
25
25
  }
26
26
  },
27
27
  "peerDependencies": {
28
- "@nestjs/common": "^10 || ^11",
29
- "@nestjs/core": "^10 || ^11",
28
+ "@nestjs/common": "^10 || ^11 || ^12",
29
+ "@nestjs/core": "^10 || ^11 || ^12",
30
30
  "reflect-metadata": "^0.2"
31
31
  },
32
32
  "peerDependenciesMeta": {
@@ -58,9 +58,9 @@
58
58
  "**/trace.ts"
59
59
  ],
60
60
  "devDependencies": {
61
- "@nestjs/common": "^11",
62
- "@nestjs/core": "^11",
63
- "@nestjs/testing": "^11",
61
+ "@nestjs/common": "^12",
62
+ "@nestjs/core": "^12",
63
+ "@nestjs/testing": "^12",
64
64
  "@types/node": "^20.0.0",
65
65
  "reflect-metadata": "^0.2",
66
66
  "typescript": "^5.4.0",