@dudousxd/nestjs-diagnostics 0.8.0 → 0.9.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 (64) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +31 -1
  3. package/dist/channel.d.ts +1 -1
  4. package/dist/channel.d.ts.map +1 -1
  5. package/dist/channel.js +3 -1
  6. package/dist/channel.js.map +1 -1
  7. package/dist/index.d.ts +2 -0
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +2 -0
  10. package/dist/index.js.map +1 -1
  11. package/dist/nestjs/delivery.d.ts +92 -0
  12. package/dist/nestjs/delivery.d.ts.map +1 -0
  13. package/dist/nestjs/delivery.js +2 -0
  14. package/dist/nestjs/delivery.js.map +1 -0
  15. package/dist/nestjs/diagnostics.explorer.d.ts +51 -11
  16. package/dist/nestjs/diagnostics.explorer.d.ts.map +1 -1
  17. package/dist/nestjs/diagnostics.explorer.js +209 -53
  18. package/dist/nestjs/diagnostics.explorer.js.map +1 -1
  19. package/dist/nestjs/diagnostics.module.d.ts +9 -2
  20. package/dist/nestjs/diagnostics.module.d.ts.map +1 -1
  21. package/dist/nestjs/diagnostics.module.js +23 -3
  22. package/dist/nestjs/diagnostics.module.js.map +1 -1
  23. package/dist/nestjs/diagnostics.options.d.ts +31 -0
  24. package/dist/nestjs/diagnostics.options.d.ts.map +1 -0
  25. package/dist/nestjs/diagnostics.options.js +3 -0
  26. package/dist/nestjs/diagnostics.options.js.map +1 -0
  27. package/dist/nestjs/index.d.ts +6 -3
  28. package/dist/nestjs/index.d.ts.map +1 -1
  29. package/dist/nestjs/index.js +4 -2
  30. package/dist/nestjs/index.js.map +1 -1
  31. package/dist/nestjs/on-diagnostic.decorator.d.ts +33 -7
  32. package/dist/nestjs/on-diagnostic.decorator.d.ts.map +1 -1
  33. package/dist/nestjs/on-diagnostic.decorator.js +0 -9
  34. package/dist/nestjs/on-diagnostic.decorator.js.map +1 -1
  35. package/dist/nestjs/queue-transport.d.ts +47 -0
  36. package/dist/nestjs/queue-transport.d.ts.map +1 -0
  37. package/dist/nestjs/queue-transport.js +45 -0
  38. package/dist/nestjs/queue-transport.js.map +1 -0
  39. package/dist/nestjs/role.d.ts +13 -0
  40. package/dist/nestjs/role.d.ts.map +1 -0
  41. package/dist/nestjs/role.js +13 -0
  42. package/dist/nestjs/role.js.map +1 -0
  43. package/dist/nestjs/transports.d.ts +125 -0
  44. package/dist/nestjs/transports.d.ts.map +1 -0
  45. package/dist/nestjs/transports.js +127 -0
  46. package/dist/nestjs/transports.js.map +1 -0
  47. package/dist/official.d.ts +2 -2
  48. package/dist/redis-relay.d.ts +60 -0
  49. package/dist/redis-relay.d.ts.map +1 -0
  50. package/dist/redis-relay.js +170 -0
  51. package/dist/redis-relay.js.map +1 -0
  52. package/dist/relayed.d.ts +8 -0
  53. package/dist/relayed.d.ts.map +1 -0
  54. package/dist/relayed.js +31 -0
  55. package/dist/relayed.js.map +1 -0
  56. package/dist/trace.d.ts.map +1 -1
  57. package/dist/trace.js +2 -0
  58. package/dist/trace.js.map +1 -1
  59. package/dist/types.d.ts +9 -0
  60. package/dist/types.d.ts.map +1 -1
  61. package/package.json +14 -6
  62. package/skills/emit-diagnostics/SKILL.md +5 -2
  63. package/skills/react-with-on-diagnostic/SKILL.md +41 -13
  64. package/skills/trace-spans/SKILL.md +2 -1
@@ -0,0 +1,47 @@
1
+ import type { DeliveredEvent, ListenerDelivery } from './delivery.js';
2
+ import type { QueueTransportConfig, TransportContext, TransportHandle } from './transports.js';
3
+ /** Data of one listener job. */
4
+ export interface DiagnosticsListenerJobData {
5
+ listenerId: string;
6
+ envelope: DeliveredEvent;
7
+ }
8
+ interface JobOptionsLike {
9
+ jobId: string;
10
+ attempts: number;
11
+ backoff: {
12
+ type: 'fixed' | 'exponential';
13
+ delay: number;
14
+ };
15
+ removeOnComplete: boolean | number;
16
+ removeOnFail: boolean | number;
17
+ }
18
+ interface QueueLike {
19
+ addBulk(jobs: Array<{
20
+ name: string;
21
+ data: DiagnosticsListenerJobData;
22
+ opts: JobOptionsLike;
23
+ }>): Promise<unknown>;
24
+ on(event: 'error', listener: (error: Error) => void): unknown;
25
+ close(): Promise<void>;
26
+ }
27
+ interface WorkerLike {
28
+ on(event: 'error', listener: (error: Error) => void): unknown;
29
+ on(event: 'failed', listener: (job: {
30
+ data: DiagnosticsListenerJobData;
31
+ } | undefined, error: Error) => void): unknown;
32
+ close(): Promise<void>;
33
+ }
34
+ /** The slice of the `bullmq` module this transport uses. */
35
+ export interface BullmqModuleLike {
36
+ Queue: new (name: string, opts: Record<string, unknown>) => QueueLike;
37
+ Worker: new (name: string, processor: (job: {
38
+ data: DiagnosticsListenerJobData;
39
+ }) => Promise<void>, opts: Record<string, unknown>) => WorkerLike;
40
+ }
41
+ export declare const DEFAULT_QUEUE = "aviary-diagnostics";
42
+ export declare const LISTENER_JOB_NAME = "aviary.diagnostics.listener";
43
+ /** BullMQ job id of a delivery: unique per `(event id, listener)`, so re-adding is a no-op. */
44
+ export declare function deliveryJobId(delivery: ListenerDelivery): string;
45
+ export declare function createQueueTransport(bullmq: BullmqModuleLike, config: QueueTransportConfig, ctx: TransportContext): TransportHandle;
46
+ export {};
47
+ //# sourceMappingURL=queue-transport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queue-transport.d.ts","sourceRoot":"","sources":["../../src/nestjs/queue-transport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACtE,OAAO,KAAK,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAE/F,gCAAgC;AAChC,MAAM,WAAW,0BAA0B;IACzC,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,cAAc,CAAC;CAC1B;AAED,UAAU,cAAc;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE;QAAE,IAAI,EAAE,OAAO,GAAG,aAAa,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1D,gBAAgB,EAAE,OAAO,GAAG,MAAM,CAAC;IACnC,YAAY,EAAE,OAAO,GAAG,MAAM,CAAC;CAChC;AAED,UAAU,SAAS;IACjB,OAAO,CACL,IAAI,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,0BAA0B,CAAC;QAAC,IAAI,EAAE,cAAc,CAAA;KAAE,CAAC,GACpF,OAAO,CAAC,OAAO,CAAC,CAAC;IACpB,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,GAAG,OAAO,CAAC;IAC9D,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,UAAU,UAAU;IAClB,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,GAAG,OAAO,CAAC;IAC9D,EAAE,CACA,KAAK,EAAE,QAAQ,EACf,QAAQ,EAAE,CAAC,GAAG,EAAE;QAAE,IAAI,EAAE,0BAA0B,CAAA;KAAE,GAAG,SAAS,EAAE,KAAK,EAAE,KAAK,KAAK,IAAI,GACtF,OAAO,CAAC;IACX,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,4DAA4D;AAC5D,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,KAAK,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,SAAS,CAAC;IACtE,MAAM,EAAE,KACN,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,CAAC,GAAG,EAAE;QAAE,IAAI,EAAE,0BAA0B,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,EACvE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC1B,UAAU,CAAC;CACjB;AAED,eAAO,MAAM,aAAa,uBAAuB,CAAC;AAClD,eAAO,MAAM,iBAAiB,gCAAgC,CAAC;AAE/D,+FAA+F;AAC/F,wBAAgB,aAAa,CAAC,QAAQ,EAAE,gBAAgB,GAAG,MAAM,CAGhE;AAED,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,oBAAoB,EAC5B,GAAG,EAAE,gBAAgB,GACpB,eAAe,CA8CjB"}
@@ -0,0 +1,45 @@
1
+ export const DEFAULT_QUEUE = 'aviary-diagnostics';
2
+ export const LISTENER_JOB_NAME = 'aviary.diagnostics.listener';
3
+ /** BullMQ job id of a delivery: unique per `(event id, listener)`, so re-adding is a no-op. */
4
+ export function deliveryJobId(delivery) {
5
+ // BullMQ refuses custom ids containing ':'.
6
+ return `diag.${delivery.envelope.id}.${delivery.listenerId}`.replaceAll(':', '.');
7
+ }
8
+ export function createQueueTransport(bullmq, config, ctx) {
9
+ const name = config.queue ?? DEFAULT_QUEUE;
10
+ const base = {
11
+ connection: config.connection,
12
+ ...(config.prefix !== undefined ? { prefix: config.prefix } : {}),
13
+ };
14
+ const jobOptions = {
15
+ attempts: (config.retries ?? 3) + 1,
16
+ backoff: config.backoff ?? { type: 'exponential', delay: 1000 },
17
+ removeOnComplete: config.removeOnComplete ?? true,
18
+ removeOnFail: config.removeOnFail ?? false,
19
+ };
20
+ const queue = new bullmq.Queue(name, base);
21
+ queue.on('error', (error) => ctx.logger.error(`diagnostics queue error: ${error.message}`));
22
+ // An 'api' process enqueues only: no worker, so listeners never run here.
23
+ const worker = ctx.role === 'api'
24
+ ? null
25
+ : new bullmq.Worker(name, (job) => ctx.invoke(job.data.listenerId, job.data.envelope), {
26
+ ...base,
27
+ concurrency: config.concurrency ?? 1,
28
+ });
29
+ worker?.on('error', (error) => ctx.logger.error(`diagnostics worker error: ${error.message}`));
30
+ worker?.on('failed', (job, error) => ctx.logger.error(`diagnostics listener ${job?.data.listenerId ?? '?'} failed for event ${job?.data.envelope.id ?? '?'}: ${error.message}`));
31
+ return {
32
+ async deliver(deliveries) {
33
+ await queue.addBulk(deliveries.map((delivery) => ({
34
+ name: LISTENER_JOB_NAME,
35
+ data: { listenerId: delivery.listenerId, envelope: delivery.envelope },
36
+ opts: { ...jobOptions, jobId: deliveryJobId(delivery) },
37
+ })));
38
+ },
39
+ async stop() {
40
+ await worker?.close();
41
+ await queue.close();
42
+ },
43
+ };
44
+ }
45
+ //# sourceMappingURL=queue-transport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queue-transport.js","sourceRoot":"","sources":["../../src/nestjs/queue-transport.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Which half of a split deployment this process is:
3
+ *
4
+ * - `'all'` (default): emit, forward, consume and run listeners. One process does everything.
5
+ * - `'api'`: emit and forward through the selected transport, and hand listener work to it;
6
+ * never consume what other processes forward, never run a listener.
7
+ * - `'worker'`: consume and run listeners. Diagnostics mounts no HTTP surface, so a worker does
8
+ * everything `'all'` does; the role exists so one setting drives every library of the app.
9
+ */
10
+ export type DiagnosticsRole = 'all' | 'api' | 'worker';
11
+ /** Validate `role` (default `'all'`) and refuse combinations that cannot work. */
12
+ export declare function resolveRole(role: DiagnosticsRole | undefined, transport: string | undefined): DiagnosticsRole;
13
+ //# sourceMappingURL=role.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"role.d.ts","sourceRoot":"","sources":["../../src/nestjs/role.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,KAAK,GAAG,QAAQ,CAAC;AAIvD,kFAAkF;AAClF,wBAAgB,WAAW,CACzB,IAAI,EAAE,eAAe,GAAG,SAAS,EACjC,SAAS,EAAE,MAAM,GAAG,SAAS,GAC5B,eAAe,CAajB"}
@@ -0,0 +1,13 @@
1
+ const ROLES = ['all', 'api', 'worker'];
2
+ /** Validate `role` (default `'all'`) and refuse combinations that cannot work. */
3
+ export function resolveRole(role, transport) {
4
+ const resolved = role ?? 'all';
5
+ if (!ROLES.includes(resolved)) {
6
+ throw new Error(`@dudousxd/nestjs-diagnostics: role must be 'all', 'api' or 'worker' (got ${JSON.stringify(role)})`);
7
+ }
8
+ if (resolved !== 'all' && !transport) {
9
+ throw new Error(`@dudousxd/nestjs-diagnostics: role is "${resolved}", which splits emitting from consuming, but no \`default\` transport is selected, so nothing carries events between the two halves. Select a transport with \`default\`, or drop \`role\`.`);
10
+ }
11
+ return resolved;
12
+ }
13
+ //# sourceMappingURL=role.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"role.js","sourceRoot":"","sources":["../../src/nestjs/role.ts"],"names":[],"mappings":"AAWA,MAAM,KAAK,GAA+B,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;AAEnE,kFAAkF;AAClF,MAAM,UAAU,WAAW,CACzB,IAAiC,EACjC,SAA6B;IAE7B,MAAM,QAAQ,GAAG,IAAI,IAAI,KAAK,CAAC;IAC/B,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CACb,4EAA4E,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CACpG,CAAC;IACJ,CAAC;IACD,IAAI,QAAQ,KAAK,KAAK,IAAI,CAAC,SAAS,EAAE,CAAC;QACrC,MAAM,IAAI,KAAK,CACb,0CAA0C,QAAQ,6LAA6L,CAChP,CAAC;IACJ,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -0,0 +1,125 @@
1
+ import type { LoggerService } from '@nestjs/common';
2
+ import type { ModuleRef } from '@nestjs/core';
3
+ import { type ChannelRef, type RedisLike } from '../redis-relay.js';
4
+ import type { DurableTransportConfig, ListenerDelivery, ListenerInvoker } from './delivery.js';
5
+ import type { DiagnosticsRole } from './role.js';
6
+ /** What a {@link TransportProvider} receives when `DiagnosticsModule` starts it at bootstrap. */
7
+ export interface TransportContext {
8
+ /** The application's module ref, to resolve providers. */
9
+ moduleRef: ModuleRef;
10
+ /** Which half of a split deployment this process is. On `'api'`, never consume or run listeners. */
11
+ role: DiagnosticsRole;
12
+ /** Unique id of THIS process, for echo suppression. */
13
+ nodeId: string;
14
+ /** The libs that have listeners in this process. A broadcasting transport relays them. */
15
+ listenerLibs: readonly string[];
16
+ /** Ids of the listeners registered in this process. */
17
+ listenerIds: readonly string[];
18
+ /** Runs a registered listener in this process; rejects with the handler's error. */
19
+ invoke: ListenerInvoker;
20
+ /** Logger for transport failures. */
21
+ logger: LoggerService;
22
+ }
23
+ /**
24
+ * A started transport: `stop` for shutdown and, for a delivering transport, `deliver`, which
25
+ * takes over listener execution (see `ListenerDeliveryTransport`).
26
+ */
27
+ export interface TransportHandle {
28
+ stop?(): void | Promise<void>;
29
+ deliver?(deliveries: readonly ListenerDelivery[]): void | Promise<void>;
30
+ }
31
+ /**
32
+ * A configured transport: a thunk `DiagnosticsModule` calls at bootstrap when it is the `default`.
33
+ * Optional dependencies (`bullmq`, durable) are imported inside the thunk, so listing a transport
34
+ * you don't select costs nothing.
35
+ */
36
+ export type TransportProvider = (ctx: TransportContext) => Promise<TransportHandle>;
37
+ /** Options of `transports.redis()`: Redis pub/sub broadcast. */
38
+ export interface RedisTransportConfig {
39
+ /** Publisher connection (an ioredis client). */
40
+ pub: RedisLike;
41
+ /**
42
+ * Subscriber connection, separate from `pub` (for ioredis: `pub.duplicate()`). Required unless
43
+ * `role` is `'api'`, which publishes and never subscribes.
44
+ */
45
+ sub?: RedisLike;
46
+ /** Also forward every event of these libs, for observers in other processes. */
47
+ libs?: string[];
48
+ /** Also forward these exact channels. */
49
+ channels?: ChannelRef[];
50
+ /** Forward every aviary channel. */
51
+ all?: boolean;
52
+ /** Redis channel to relay on. Default `aviary:diagnostics:relay`. */
53
+ redisChannel?: string;
54
+ }
55
+ /** Options of `transports.queue()`: one BullMQ job per `(listener, event)`. */
56
+ export interface QueueTransportConfig {
57
+ /**
58
+ * BullMQ connection: `ConnectionOptions` (`{ host, port }`) or an ioredis client created with
59
+ * `maxRetriesPerRequest: null`.
60
+ */
61
+ connection: object;
62
+ /** Queue name. Default `aviary-diagnostics`. */
63
+ queue?: string;
64
+ /** BullMQ key prefix. Default BullMQ's (`bull`). */
65
+ prefix?: string;
66
+ /** How many times a job is retried after its handler throws. Default `3`. */
67
+ retries?: number;
68
+ /** Delay between attempts. Default `{ type: 'exponential', delay: 1000 }`. */
69
+ backoff?: {
70
+ type: 'fixed' | 'exponential';
71
+ delay: number;
72
+ };
73
+ /** Jobs a worker process runs at once. Default `1`. */
74
+ concurrency?: number;
75
+ /** BullMQ `removeOnComplete`. Default `true`. */
76
+ removeOnComplete?: boolean | number;
77
+ /** BullMQ `removeOnFail`. Default `false` (failed jobs stay for inspection). */
78
+ removeOnFail?: boolean | number;
79
+ }
80
+ export type { DurableTransportConfig };
81
+ export declare const DURABLE_DELIVERY_MODULE = "@dudousxd/nestjs-durable-diagnostics/delivery";
82
+ export declare const DURABLE_NOT_INSTALLED = "`transports.durable()` requires @dudousxd/nestjs-durable-diagnostics. Install it and import DurableModule in your app, or pick another transport.";
83
+ export declare const DURABLE_OUTDATED = "`transports.durable()` requires a version of @dudousxd/nestjs-durable-diagnostics that exports `@dudousxd/nestjs-durable-diagnostics/delivery`. Upgrade it, or pick another transport.";
84
+ export declare const DURABLE_NOT_CONFIGURED = "`transports.durable()` requires durable to be configured: no durable engine resolves in this app. Import DurableModule, or pick another transport.";
85
+ export declare const BULLMQ_NOT_INSTALLED = "`transports.queue()` requires bullmq. Install it (`pnpm add bullmq`), or pick another transport.";
86
+ /** Replace how optional modules are imported. Internal, for tests. */
87
+ export declare function setTransportModuleLoader(name: 'durable' | 'bullmq', loader: (() => Promise<unknown>) | null): void;
88
+ /**
89
+ * The transport factories for `DiagnosticsModule.forRoot({ default, transports })`:
90
+ *
91
+ * ```ts
92
+ * DiagnosticsModule.forRoot({
93
+ * default: 'queue',
94
+ * transports: {
95
+ * redis: transports.redis({ pub, sub }),
96
+ * queue: transports.queue({ connection: { host: 'localhost', port: 6379 } }),
97
+ * durable: transports.durable({ retries: 5 }),
98
+ * },
99
+ * })
100
+ * ```
101
+ *
102
+ * Each factory returns a lazy {@link TransportProvider}: only the selected `default` starts, and
103
+ * only it imports its optional dependency.
104
+ */
105
+ export declare const transports: {
106
+ /**
107
+ * Broadcast over Redis pub/sub, best-effort: listeners run in every process the event reaches.
108
+ * Events of libs with listeners are forwarded automatically; `libs`/`channels`/`all` forward more.
109
+ */
110
+ redis(config: RedisTransportConfig): TransportProvider;
111
+ /**
112
+ * Run listeners through BullMQ: one job per `(listener, event)`, retried when the handler throws
113
+ * (at-least-once). Every process that is not `role: 'api'` runs a worker for the queue.
114
+ * Requires the optional peer `bullmq`.
115
+ */
116
+ queue(config: QueueTransportConfig): TransportProvider;
117
+ /**
118
+ * Run listeners as durable runs: one run per `(listener, event)`, retried with backoff and
119
+ * idempotent per event id. Imports `@dudousxd/nestjs-durable-diagnostics/delivery` only when this
120
+ * transport is the selected `default`; bootstrap fails with a clear error when it is missing or
121
+ * durable is not configured.
122
+ */
123
+ durable(config?: DurableTransportConfig): TransportProvider;
124
+ };
125
+ //# sourceMappingURL=transports.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transports.d.ts","sourceRoot":"","sources":["../../src/nestjs/transports.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,127 @@
1
+ import { createDiagnosticsRedisRelay } from '../redis-relay.js';
2
+ export const DURABLE_DELIVERY_MODULE = '@dudousxd/nestjs-durable-diagnostics/delivery';
3
+ const DURABLE_PACKAGE = '@dudousxd/nestjs-durable-diagnostics';
4
+ export const DURABLE_NOT_INSTALLED = `\`transports.durable()\` requires ${DURABLE_PACKAGE}. Install it and import DurableModule in your app, or pick another transport.`;
5
+ export const DURABLE_OUTDATED = `\`transports.durable()\` requires a version of ${DURABLE_PACKAGE} that exports \`${DURABLE_DELIVERY_MODULE}\`. Upgrade it, or pick another transport.`;
6
+ export const DURABLE_NOT_CONFIGURED = '`transports.durable()` requires durable to be configured: no durable engine resolves in this app. Import DurableModule, or pick another transport.';
7
+ export const BULLMQ_NOT_INSTALLED = '`transports.queue()` requires bullmq. Install it (`pnpm add bullmq`), or pick another transport.';
8
+ const importDurable = () => import(/* @vite-ignore */ DURABLE_DELIVERY_MODULE);
9
+ const importBullmq = () => import(/* @vite-ignore */ 'bullmq');
10
+ const loaders = { durable: importDurable, bullmq: importBullmq };
11
+ /** Replace how optional modules are imported. Internal, for tests. */
12
+ export function setTransportModuleLoader(name, loader) {
13
+ if (name === 'durable') {
14
+ loaders.durable = loader ?? importDurable;
15
+ }
16
+ else {
17
+ loaders.bullmq = loader ?? importBullmq;
18
+ }
19
+ }
20
+ /** `'missing'` when `pkg` is not installed, `'outdated'` when it lacks the subpath, else `null`. */
21
+ function classifyImportError(error, pkg) {
22
+ const { code, message } = (error ?? {});
23
+ const text = typeof message === 'string' ? message : '';
24
+ if (!text.includes(pkg))
25
+ return null;
26
+ if (code === 'ERR_PACKAGE_PATH_NOT_EXPORTED')
27
+ return 'outdated';
28
+ if (code === 'ERR_MODULE_NOT_FOUND' || code === 'MODULE_NOT_FOUND')
29
+ return 'missing';
30
+ return null;
31
+ }
32
+ /**
33
+ * The transport factories for `DiagnosticsModule.forRoot({ default, transports })`:
34
+ *
35
+ * ```ts
36
+ * DiagnosticsModule.forRoot({
37
+ * default: 'queue',
38
+ * transports: {
39
+ * redis: transports.redis({ pub, sub }),
40
+ * queue: transports.queue({ connection: { host: 'localhost', port: 6379 } }),
41
+ * durable: transports.durable({ retries: 5 }),
42
+ * },
43
+ * })
44
+ * ```
45
+ *
46
+ * Each factory returns a lazy {@link TransportProvider}: only the selected `default` starts, and
47
+ * only it imports its optional dependency.
48
+ */
49
+ export const transports = {
50
+ /**
51
+ * Broadcast over Redis pub/sub, best-effort: listeners run in every process the event reaches.
52
+ * Events of libs with listeners are forwarded automatically; `libs`/`channels`/`all` forward more.
53
+ */
54
+ redis(config) {
55
+ return async (ctx) => {
56
+ const libs = [...new Set([...(config.libs ?? []), ...ctx.listenerLibs])];
57
+ const receive = ctx.role !== 'api';
58
+ const stop = createDiagnosticsRedisRelay({
59
+ pub: config.pub,
60
+ ...(receive ? (config.sub !== undefined ? { sub: config.sub } : {}) : { receive: false }),
61
+ libs,
62
+ ...(config.channels !== undefined ? { channels: config.channels } : {}),
63
+ ...(config.all !== undefined ? { all: config.all } : {}),
64
+ ...(config.redisChannel !== undefined ? { redisChannel: config.redisChannel } : {}),
65
+ nodeId: ctx.nodeId,
66
+ });
67
+ return { stop };
68
+ };
69
+ },
70
+ /**
71
+ * Run listeners through BullMQ: one job per `(listener, event)`, retried when the handler throws
72
+ * (at-least-once). Every process that is not `role: 'api'` runs a worker for the queue.
73
+ * Requires the optional peer `bullmq`.
74
+ */
75
+ queue(config) {
76
+ return async (ctx) => {
77
+ let bullmq;
78
+ try {
79
+ bullmq = await loaders.bullmq();
80
+ }
81
+ catch (error) {
82
+ if (classifyImportError(error, 'bullmq') === 'missing') {
83
+ throw new Error(BULLMQ_NOT_INSTALLED, { cause: error });
84
+ }
85
+ throw error;
86
+ }
87
+ const { createQueueTransport } = await import('./queue-transport.js');
88
+ return createQueueTransport(bullmq, config, ctx);
89
+ };
90
+ },
91
+ /**
92
+ * Run listeners as durable runs: one run per `(listener, event)`, retried with backoff and
93
+ * idempotent per event id. Imports `@dudousxd/nestjs-durable-diagnostics/delivery` only when this
94
+ * transport is the selected `default`; bootstrap fails with a clear error when it is missing or
95
+ * durable is not configured.
96
+ */
97
+ durable(config = {}) {
98
+ return async (ctx) => {
99
+ let durable;
100
+ try {
101
+ durable = await loaders.durable();
102
+ }
103
+ catch (error) {
104
+ const kind = classifyImportError(error, DURABLE_PACKAGE);
105
+ if (kind === 'missing')
106
+ throw new Error(DURABLE_NOT_INSTALLED, { cause: error });
107
+ if (kind === 'outdated')
108
+ throw new Error(DURABLE_OUTDATED, { cause: error });
109
+ throw error;
110
+ }
111
+ if (typeof durable?.isConfigured !== 'function' ||
112
+ typeof durable.createDeliveryTransport !== 'function') {
113
+ throw new Error(DURABLE_OUTDATED);
114
+ }
115
+ if (!(await durable.isConfigured(ctx.moduleRef)))
116
+ throw new Error(DURABLE_NOT_CONFIGURED);
117
+ return durable.createDeliveryTransport({
118
+ moduleRef: ctx.moduleRef,
119
+ nodeId: ctx.nodeId,
120
+ role: ctx.role,
121
+ invoke: ctx.invoke,
122
+ listenerIds: ctx.listenerIds,
123
+ }, config);
124
+ };
125
+ },
126
+ };
127
+ //# sourceMappingURL=transports.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transports.js","sourceRoot":"","sources":["../../src/nestjs/transports.ts"],"names":[],"mappings":"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"}
@@ -51,8 +51,8 @@ export declare function officialChannelNames(): string[];
51
51
  /**
52
52
  * The Aviary envelope wrapped around an official payload: the usual
53
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`.
54
+ * both kinds can tell them apart. Aviary's own `emit()` envelopes carry no
55
+ * `source`.
56
56
  */
57
57
  export interface OfficialDiagnosticEvent<TPayload = unknown> extends DiagnosticEvent<TPayload> {
58
58
  source: 'nestjs';
@@ -0,0 +1,60 @@
1
+ /** The minimal Redis pub/sub surface the relay uses. An ioredis instance satisfies it structurally. */
2
+ export interface RedisLike {
3
+ publish(channel: string, message: string): unknown;
4
+ /** When present, a tick's forwarded events go out as one pipeline instead of one round trip each. */
5
+ pipeline?(): RedisPipelineLike;
6
+ subscribe(channel: string, callback?: (err: Error | null, count: number) => void): unknown;
7
+ on(event: 'message', listener: (channel: string, message: string) => void): unknown;
8
+ removeListener(event: 'message', listener: (channel: string, message: string) => void): unknown;
9
+ unsubscribe(channel: string): unknown;
10
+ }
11
+ /** The slice of an ioredis pipeline the relay uses to batch publishes. */
12
+ export interface RedisPipelineLike {
13
+ publish(channel: string, message: string): unknown;
14
+ exec(): unknown;
15
+ }
16
+ export interface ChannelRef {
17
+ lib: string;
18
+ event: string;
19
+ }
20
+ export interface DiagnosticsRedisRelayOptions {
21
+ /** Publisher connection. */
22
+ pub: RedisLike;
23
+ /**
24
+ * Subscriber connection (separate from `pub`). For ioredis: `const sub = pub.duplicate()`.
25
+ * Required unless `receive` is `false`.
26
+ */
27
+ sub?: RedisLike;
28
+ /**
29
+ * Re-emit events other processes forward onto this process's bus. Default `true`; `false` makes
30
+ * a publish-only relay that never subscribes (a `role: 'api'` process).
31
+ */
32
+ receive?: boolean;
33
+ /** Forward every event of these libs (current + future channels). */
34
+ libs?: string[];
35
+ /** Forward these exact channels, in addition to `libs`. */
36
+ channels?: ChannelRef[];
37
+ /** Forward EVERY aviary channel (current + future). Overrides `libs`/`channels`. Default false. */
38
+ all?: boolean;
39
+ /** Redis channel to relay on. Default 'aviary:diagnostics:relay'. */
40
+ redisChannel?: string;
41
+ /** Unique id for THIS process, for echo suppression. Default a random id. */
42
+ nodeId?: string;
43
+ }
44
+ /**
45
+ * Relay diagnostics events across processes over Redis pub/sub. The envelope crosses as JSON with
46
+ * its `id` intact, and the re-emitted copy is marked as relayed (see {@link publishRelayed}). Forwards selected local
47
+ * `aviary:<lib>:<event>` channels to Redis and re-emits Redis-received events onto the local bus, so
48
+ * `@OnDiagnostic` handlers / `getChannel(...).subscribe(...)` fire cross-process. Loop-safe via nodeId
49
+ * echo suppression and a re-emit guard. Never throws into `emit()` or the Redis handler. Does NOT
50
+ * close the `pub`/`sub` connections — the caller owns them.
51
+ *
52
+ * Forwarding happens off the emitter's call stack: selected events are queued and, once per event
53
+ * loop turn, serialized and published in emit order (one Redis message per event, sent as a single
54
+ * pipeline when `pub` supports it).
55
+ *
56
+ * @returns a teardown that publishes any queued events, then removes all local subscriptions and
57
+ * the Redis message handler.
58
+ */
59
+ export declare function createDiagnosticsRedisRelay(options: DiagnosticsRedisRelayOptions): () => void;
60
+ //# sourceMappingURL=redis-relay.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redis-relay.d.ts","sourceRoot":"","sources":["../src/redis-relay.ts"],"names":[],"mappings":"AAMA,uGAAuG;AACvG,MAAM,WAAW,SAAS;IACxB,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;IACnD,qGAAqG;IACrG,QAAQ,CAAC,IAAI,iBAAiB,CAAC;IAC/B,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,GAAG,IAAI,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC;IAC3F,EAAE,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC;IACpF,cAAc,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC;IAChG,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;CACvC;AAED,0EAA0E;AAC1E,MAAM,WAAW,iBAAiB;IAChC,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;IACnD,IAAI,IAAI,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,4BAA4B;IAC3C,4BAA4B;IAC5B,GAAG,EAAE,SAAS,CAAC;IACf;;;OAGG;IACH,GAAG,CAAC,EAAE,SAAS,CAAC;IAChB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,qEAAqE;IACrE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,UAAU,EAAE,CAAC;IACxB,mGAAmG;IACnG,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,qEAAqE;IACrE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,6EAA6E;IAC7E,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAeD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,4BAA4B,GAAG,MAAM,IAAI,CAmI7F"}