@dudousxd/nestjs-notifications-sse 0.3.0 → 0.5.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/dist/index.d.ts CHANGED
@@ -1,8 +1,9 @@
1
1
  export { Sse, SseChannel, type SseChannelOptions, type SseNotification, } from './sse.channel';
2
2
  export { SseHub } from './sse.hub';
3
- export { SseChannelModule, type SseChannelModuleOptions } from './sse.module';
3
+ export { SseChannelModule, type SseChannelModuleOptions, type SseChannelModuleAsyncOptions, type SseChannelAsyncConfig, } from './sse.module';
4
4
  export { SSE_OPTIONS, SSE_BACKPLANE } from './tokens';
5
5
  export { sseKey } from './sse-key';
6
+ export { createNotificationsStreamController, type NotificationsStreamControllerOptions, } from './stream-controller';
6
7
  export type { SseBackplane, SseBackplaneMessage } from './backplane';
7
8
  export { RedisSseBackplane, type RedisSseBackplaneOptions, type RedisPubSubClient, } from './redis.backplane';
8
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,GAAG,EACH,UAAU,EACV,KAAK,iBAAiB,EACtB,KAAK,eAAe,GACrB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACnC,OAAO,EAAE,gBAAgB,EAAE,KAAK,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAC9E,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACtD,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACnC,YAAY,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACrE,OAAO,EACL,iBAAiB,EACjB,KAAK,wBAAwB,EAC7B,KAAK,iBAAiB,GACvB,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,GAAG,EACH,UAAU,EACV,KAAK,iBAAiB,EACtB,KAAK,eAAe,GACrB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACnC,OAAO,EACL,gBAAgB,EAChB,KAAK,uBAAuB,EAC5B,KAAK,4BAA4B,EACjC,KAAK,qBAAqB,GAC3B,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACtD,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACnC,OAAO,EACL,mCAAmC,EACnC,KAAK,oCAAoC,GAC1C,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACrE,OAAO,EACL,iBAAiB,EACjB,KAAK,wBAAwB,EAC7B,KAAK,iBAAiB,GACvB,MAAM,mBAAmB,CAAC"}
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.RedisSseBackplane = exports.sseKey = exports.SSE_BACKPLANE = exports.SSE_OPTIONS = exports.SseChannelModule = exports.SseHub = exports.SseChannel = exports.Sse = void 0;
3
+ exports.RedisSseBackplane = exports.createNotificationsStreamController = exports.sseKey = exports.SSE_BACKPLANE = exports.SSE_OPTIONS = exports.SseChannelModule = exports.SseHub = exports.SseChannel = exports.Sse = void 0;
4
4
  var sse_channel_1 = require("./sse.channel");
5
5
  Object.defineProperty(exports, "Sse", { enumerable: true, get: function () { return sse_channel_1.Sse; } });
6
6
  Object.defineProperty(exports, "SseChannel", { enumerable: true, get: function () { return sse_channel_1.SseChannel; } });
@@ -13,6 +13,8 @@ Object.defineProperty(exports, "SSE_OPTIONS", { enumerable: true, get: function
13
13
  Object.defineProperty(exports, "SSE_BACKPLANE", { enumerable: true, get: function () { return tokens_1.SSE_BACKPLANE; } });
14
14
  var sse_key_1 = require("./sse-key");
15
15
  Object.defineProperty(exports, "sseKey", { enumerable: true, get: function () { return sse_key_1.sseKey; } });
16
+ var stream_controller_1 = require("./stream-controller");
17
+ Object.defineProperty(exports, "createNotificationsStreamController", { enumerable: true, get: function () { return stream_controller_1.createNotificationsStreamController; } });
16
18
  var redis_backplane_1 = require("./redis.backplane");
17
19
  Object.defineProperty(exports, "RedisSseBackplane", { enumerable: true, get: function () { return redis_backplane_1.RedisSseBackplane; } });
18
20
  //# 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,6CAKuB;AAJrB,kGAAA,GAAG,OAAA;AACH,yGAAA,UAAU,OAAA;AAIZ,qCAAmC;AAA1B,iGAAA,MAAM,OAAA;AACf,2CAA8E;AAArE,8GAAA,gBAAgB,OAAA;AACzB,mCAAsD;AAA7C,qGAAA,WAAW,OAAA;AAAE,uGAAA,aAAa,OAAA;AACnC,qCAAmC;AAA1B,iGAAA,MAAM,OAAA;AAEf,qDAI2B;AAHzB,oHAAA,iBAAiB,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,6CAKuB;AAJrB,kGAAA,GAAG,OAAA;AACH,yGAAA,UAAU,OAAA;AAIZ,qCAAmC;AAA1B,iGAAA,MAAM,OAAA;AACf,2CAKsB;AAJpB,8GAAA,gBAAgB,OAAA;AAKlB,mCAAsD;AAA7C,qGAAA,WAAW,OAAA;AAAE,uGAAA,aAAa,OAAA;AACnC,qCAAmC;AAA1B,iGAAA,MAAM,OAAA;AACf,yDAG6B;AAF3B,wIAAA,mCAAmC,OAAA;AAIrC,qDAI2B;AAHzB,oHAAA,iBAAiB,OAAA"}
@@ -1,4 +1,4 @@
1
- import { type DynamicModule } from '@nestjs/common';
1
+ import { type DynamicModule, type FactoryProvider, type ModuleMetadata } from '@nestjs/common';
2
2
  import type { SseBackplane } from './backplane';
3
3
  export interface SseChannelModuleOptions {
4
4
  /** SSE event name (`type`) emitted to clients. Defaults to `'notification'`. */
@@ -12,19 +12,39 @@ export interface SseChannelModuleOptions {
12
12
  /** Register globally so the channel is discoverable app-wide. Default true. */
13
13
  global?: boolean;
14
14
  }
15
+ /** The slice of {@link SseChannelModuleOptions} a {@link SseChannelModuleAsyncOptions} factory returns. */
16
+ export interface SseChannelAsyncConfig {
17
+ event?: string;
18
+ backplane?: SseBackplane;
19
+ }
20
+ /** Async registration for {@link SseChannelModule.forRootAsync} — build options (e.g. the backplane) from DI. */
21
+ export interface SseChannelModuleAsyncOptions extends Pick<ModuleMetadata, 'imports'> {
22
+ useFactory: (...args: never[]) => SseChannelAsyncConfig | Promise<SseChannelAsyncConfig>;
23
+ inject?: FactoryProvider['inject'];
24
+ /** Register globally so the channel is discoverable app-wide. Default true. */
25
+ global?: boolean;
26
+ }
15
27
  /**
16
28
  * Registers the SSE channel and its {@link SseHub}.
17
29
  *
18
- * The streaming endpoint itself is mounted by the consumer in their own
19
- * controller via NestJS's native `@Sse()` decorator — this module only provides
20
- * the hub (to read from) and the channel (to push into). See {@link SseChannel}
21
- * for the controller example.
30
+ * The streaming endpoint itself is mounted by the consumer in their own controller via NestJS's
31
+ * native `@Sse()` decorator — this module only provides the hub (to read from) and the channel (to
32
+ * push into). See {@link SseChannel} for the controller example.
22
33
  *
23
34
  * ```ts
24
35
  * SseChannelModule.forRoot({ event: 'notification' });
36
+ *
37
+ * // Build the backplane from DI (e.g. a Redis config service):
38
+ * SseChannelModule.forRootAsync({
39
+ * inject: [AppConfig],
40
+ * useFactory: (cfg: AppConfig) => ({
41
+ * backplane: new RedisSseBackplane({ publisher: new Redis(cfg.redis), subscriber: new Redis(cfg.redis) }),
42
+ * }),
43
+ * });
25
44
  * ```
26
45
  */
27
46
  export declare class SseChannelModule {
28
47
  static forRoot(options?: SseChannelModuleOptions): DynamicModule;
48
+ static forRootAsync(options: SseChannelModuleAsyncOptions): DynamicModule;
29
49
  }
30
50
  //# sourceMappingURL=sse.module.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sse.module.d.ts","sourceRoot":"","sources":["../src/sse.module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAyB,MAAM,gBAAgB,CAAC;AAC3E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAKhD,MAAM,WAAW,uBAAuB;IACtC,gFAAgF;IAChF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,SAAS,CAAC,EAAE,YAAY,CAAC;IACzB,+EAA+E;IAC/E,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;;;;;;GAWG;AACH,qBACa,gBAAgB;IAC3B,MAAM,CAAC,OAAO,CAAC,OAAO,GAAE,uBAA4B,GAAG,aAAa;CAmBrE"}
1
+ {"version":3,"file":"sse.module.d.ts","sourceRoot":"","sources":["../src/sse.module.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,eAAe,EAEpB,KAAK,cAAc,EAEpB,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAKhD,MAAM,WAAW,uBAAuB;IACtC,gFAAgF;IAChF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,SAAS,CAAC,EAAE,YAAY,CAAC;IACzB,+EAA+E;IAC/E,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,2GAA2G;AAC3G,MAAM,WAAW,qBAAqB;IACpC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,YAAY,CAAC;CAC1B;AAED,iHAAiH;AACjH,MAAM,WAAW,4BAA6B,SAAQ,IAAI,CAAC,cAAc,EAAE,SAAS,CAAC;IACnF,UAAU,EAAE,CAAC,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,qBAAqB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAAC;IACzF,MAAM,CAAC,EAAE,eAAe,CAAC,QAAQ,CAAC,CAAC;IACnC,+EAA+E;IAC/E,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAKD;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBACa,gBAAgB;IAC3B,MAAM,CAAC,OAAO,CAAC,OAAO,GAAE,uBAA4B,GAAG,aAAa;IAgBpE,MAAM,CAAC,YAAY,CAAC,OAAO,EAAE,4BAA4B,GAAG,aAAa;CAgC1E"}
@@ -12,32 +12,68 @@ const common_1 = require("@nestjs/common");
12
12
  const sse_channel_1 = require("./sse.channel");
13
13
  const sse_hub_1 = require("./sse.hub");
14
14
  const tokens_1 = require("./tokens");
15
+ /** Internal token holding the resolved async config. */
16
+ const SSE_RESOLVED_CONFIG = Symbol('SSE_RESOLVED_CONFIG');
15
17
  /**
16
18
  * Registers the SSE channel and its {@link SseHub}.
17
19
  *
18
- * The streaming endpoint itself is mounted by the consumer in their own
19
- * controller via NestJS's native `@Sse()` decorator — this module only provides
20
- * the hub (to read from) and the channel (to push into). See {@link SseChannel}
21
- * for the controller example.
20
+ * The streaming endpoint itself is mounted by the consumer in their own controller via NestJS's
21
+ * native `@Sse()` decorator — this module only provides the hub (to read from) and the channel (to
22
+ * push into). See {@link SseChannel} for the controller example.
22
23
  *
23
24
  * ```ts
24
25
  * SseChannelModule.forRoot({ event: 'notification' });
26
+ *
27
+ * // Build the backplane from DI (e.g. a Redis config service):
28
+ * SseChannelModule.forRootAsync({
29
+ * inject: [AppConfig],
30
+ * useFactory: (cfg: AppConfig) => ({
31
+ * backplane: new RedisSseBackplane({ publisher: new Redis(cfg.redis), subscriber: new Redis(cfg.redis) }),
32
+ * }),
33
+ * });
25
34
  * ```
26
35
  */
27
36
  let SseChannelModule = SseChannelModule_1 = class SseChannelModule {
28
37
  static forRoot(options = {}) {
29
- const channelOptions = {
30
- event: options.event ?? 'notification',
38
+ const channelOptions = { event: options.event ?? 'notification' };
39
+ return {
40
+ module: SseChannelModule_1,
41
+ global: options.global ?? true,
42
+ providers: [
43
+ { provide: tokens_1.SSE_OPTIONS, useValue: channelOptions },
44
+ { provide: tokens_1.SSE_BACKPLANE, useValue: options.backplane ?? null },
45
+ sse_hub_1.SseHub,
46
+ sse_channel_1.SseChannel,
47
+ ],
48
+ exports: [sse_hub_1.SseHub, sse_channel_1.SseChannel],
31
49
  };
50
+ }
51
+ static forRootAsync(options) {
32
52
  const providers = [
33
- { provide: tokens_1.SSE_OPTIONS, useValue: channelOptions },
34
- { provide: tokens_1.SSE_BACKPLANE, useValue: options.backplane ?? null },
53
+ {
54
+ provide: SSE_RESOLVED_CONFIG,
55
+ useFactory: options.useFactory,
56
+ inject: options.inject ?? [],
57
+ },
58
+ {
59
+ provide: tokens_1.SSE_OPTIONS,
60
+ useFactory: (config) => ({
61
+ event: config.event ?? 'notification',
62
+ }),
63
+ inject: [SSE_RESOLVED_CONFIG],
64
+ },
65
+ {
66
+ provide: tokens_1.SSE_BACKPLANE,
67
+ useFactory: (config) => config.backplane ?? null,
68
+ inject: [SSE_RESOLVED_CONFIG],
69
+ },
35
70
  sse_hub_1.SseHub,
36
71
  sse_channel_1.SseChannel,
37
72
  ];
38
73
  return {
39
74
  module: SseChannelModule_1,
40
75
  global: options.global ?? true,
76
+ imports: options.imports ?? [],
41
77
  providers,
42
78
  exports: [sse_hub_1.SseHub, sse_channel_1.SseChannel],
43
79
  };
@@ -1 +1 @@
1
- {"version":3,"file":"sse.module.js","sourceRoot":"","sources":["../src/sse.module.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,2CAA2E;AAE3E,+CAAmE;AACnE,uCAAmC;AACnC,qCAAsD;AAetD;;;;;;;;;;;GAWG;AAEI,IAAM,gBAAgB,wBAAtB,MAAM,gBAAgB;IAC3B,MAAM,CAAC,OAAO,CAAC,UAAmC,EAAE;QAClD,MAAM,cAAc,GAAsB;YACxC,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,cAAc;SACvC,CAAC;QAEF,MAAM,SAAS,GAAe;YAC5B,EAAE,OAAO,EAAE,oBAAW,EAAE,QAAQ,EAAE,cAAc,EAAE;YAClD,EAAE,OAAO,EAAE,sBAAa,EAAE,QAAQ,EAAE,OAAO,CAAC,SAAS,IAAI,IAAI,EAAE;YAC/D,gBAAM;YACN,wBAAU;SACX,CAAC;QAEF,OAAO;YACL,MAAM,EAAE,kBAAgB;YACxB,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,IAAI;YAC9B,SAAS;YACT,OAAO,EAAE,CAAC,gBAAM,EAAE,wBAAU,CAAC;SAC9B,CAAC;IACJ,CAAC;CACF,CAAA;AApBY,4CAAgB;2BAAhB,gBAAgB;IAD5B,IAAA,eAAM,EAAC,EAAE,CAAC;GACE,gBAAgB,CAoB5B"}
1
+ {"version":3,"file":"sse.module.js","sourceRoot":"","sources":["../src/sse.module.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,2CAMwB;AAExB,+CAAmE;AACnE,uCAAmC;AACnC,qCAAsD;AA6BtD,wDAAwD;AACxD,MAAM,mBAAmB,GAAG,MAAM,CAAC,qBAAqB,CAAC,CAAC;AAE1D;;;;;;;;;;;;;;;;;;GAkBG;AAEI,IAAM,gBAAgB,wBAAtB,MAAM,gBAAgB;IAC3B,MAAM,CAAC,OAAO,CAAC,UAAmC,EAAE;QAClD,MAAM,cAAc,GAAsB,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,cAAc,EAAE,CAAC;QAErF,OAAO;YACL,MAAM,EAAE,kBAAgB;YACxB,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,IAAI;YAC9B,SAAS,EAAE;gBACT,EAAE,OAAO,EAAE,oBAAW,EAAE,QAAQ,EAAE,cAAc,EAAE;gBAClD,EAAE,OAAO,EAAE,sBAAa,EAAE,QAAQ,EAAE,OAAO,CAAC,SAAS,IAAI,IAAI,EAAE;gBAC/D,gBAAM;gBACN,wBAAU;aACX;YACD,OAAO,EAAE,CAAC,gBAAM,EAAE,wBAAU,CAAC;SAC9B,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,YAAY,CAAC,OAAqC;QACvD,MAAM,SAAS,GAAe;YAC5B;gBACE,OAAO,EAAE,mBAAmB;gBAC5B,UAAU,EAAE,OAAO,CAAC,UAAU;gBAC9B,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,EAAE;aAC7B;YACD;gBACE,OAAO,EAAE,oBAAW;gBACpB,UAAU,EAAE,CAAC,MAA6B,EAAqB,EAAE,CAAC,CAAC;oBACjE,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,cAAc;iBACtC,CAAC;gBACF,MAAM,EAAE,CAAC,mBAAmB,CAAC;aAC9B;YACD;gBACE,OAAO,EAAE,sBAAa;gBACtB,UAAU,EAAE,CAAC,MAA6B,EAAuB,EAAE,CACjE,MAAM,CAAC,SAAS,IAAI,IAAI;gBAC1B,MAAM,EAAE,CAAC,mBAAmB,CAAC;aAC9B;YACD,gBAAM;YACN,wBAAU;SACX,CAAC;QAEF,OAAO;YACL,MAAM,EAAE,kBAAgB;YACxB,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,IAAI;YAC9B,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,EAAE;YAC9B,SAAS;YACT,OAAO,EAAE,CAAC,gBAAM,EAAE,wBAAU,CAAC;SAC9B,CAAC;IACJ,CAAC;CACF,CAAA;AAjDY,4CAAgB;2BAAhB,gBAAgB;IAD5B,IAAA,eAAM,EAAC,EAAE,CAAC;GACE,gBAAgB,CAiD5B"}
@@ -0,0 +1,45 @@
1
+ import { type CanActivate, type Type } from '@nestjs/common';
2
+ /** Options for {@link createNotificationsStreamController}. */
3
+ export interface NotificationsStreamControllerOptions {
4
+ /**
5
+ * Resolve the SSE route value from the request — must match what the notifiable returns from
6
+ * `routeNotificationFor('sse')` (typically the user id). E.g. `(req) => req.user.id`.
7
+ */
8
+ resolveRoute: (req: any) => string | Promise<string>;
9
+ /**
10
+ * Optional tenant resolver — must match the tenant the notification was sent under, so the stream
11
+ * key lines up with what the SSE channel published to.
12
+ */
13
+ resolveTenant?: (req: any) => string | undefined | Promise<string | undefined>;
14
+ /** Controller base path. Default `'notifications'`. */
15
+ path?: string;
16
+ /** Sub-path for the `@Sse()` endpoint. Default `'stream'` → `GET {path}/stream`. */
17
+ streamPath?: string;
18
+ /** Guards to protect the stream with (e.g. your auth guard). Applied via `@UseGuards`. */
19
+ guards?: Array<Type<CanActivate> | CanActivate>;
20
+ /**
21
+ * Keep-alive interval (ms) emitting a `{ type: 'heartbeat' }` event so idle connections survive
22
+ * proxy/load-balancer timeouts. Default `25000`; set `0` to disable.
23
+ */
24
+ heartbeatMs?: number;
25
+ }
26
+ /**
27
+ * Builds a `@Controller` exposing the Server-Sent Events stream endpoint for in-app notifications —
28
+ * the consumer-side counterpart the SSE channel publishes to. Mounts a native `@Sse()` route that
29
+ * subscribes to {@link SseHub} under the same key the channel uses (`sseKey(tenant, routeValue)`),
30
+ * so apps don't hand-write the streaming endpoint.
31
+ *
32
+ * ```ts
33
+ * const NotificationsStreamController = createNotificationsStreamController({
34
+ * resolveRoute: (req) => String(req.user.id),
35
+ * });
36
+ *
37
+ * @Module({ controllers: [NotificationsStreamController] })
38
+ * export class InboxModule {}
39
+ * ```
40
+ *
41
+ * Requires `SseChannelModule` (which provides {@link SseHub}) in scope. Pair with a cross-pod
42
+ * `backplane` so a publish on any node reaches connections held by another.
43
+ */
44
+ export declare function createNotificationsStreamController(options: NotificationsStreamControllerOptions): Type<unknown>;
45
+ //# sourceMappingURL=stream-controller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream-controller.d.ts","sourceRoot":"","sources":["../src/stream-controller.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,WAAW,EAKhB,KAAK,IAAI,EAEV,MAAM,gBAAgB,CAAC;AAKxB,+DAA+D;AAC/D,MAAM,WAAW,oCAAoC;IACnD;;;OAGG;IACH,YAAY,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACrD;;;OAGG;IACH,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAC/E,uDAAuD;IACvD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,oFAAoF;IACpF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,0FAA0F;IAC1F,MAAM,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,WAAW,CAAC,CAAC;IAChD;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,mCAAmC,CACjD,OAAO,EAAE,oCAAoC,GAC5C,IAAI,CAAC,OAAO,CAAC,CA8Bf"}
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.createNotificationsStreamController = createNotificationsStreamController;
16
+ const common_1 = require("@nestjs/common");
17
+ const rxjs_1 = require("rxjs");
18
+ const sse_key_1 = require("./sse-key");
19
+ const sse_hub_1 = require("./sse.hub");
20
+ /**
21
+ * Builds a `@Controller` exposing the Server-Sent Events stream endpoint for in-app notifications —
22
+ * the consumer-side counterpart the SSE channel publishes to. Mounts a native `@Sse()` route that
23
+ * subscribes to {@link SseHub} under the same key the channel uses (`sseKey(tenant, routeValue)`),
24
+ * so apps don't hand-write the streaming endpoint.
25
+ *
26
+ * ```ts
27
+ * const NotificationsStreamController = createNotificationsStreamController({
28
+ * resolveRoute: (req) => String(req.user.id),
29
+ * });
30
+ *
31
+ * @Module({ controllers: [NotificationsStreamController] })
32
+ * export class InboxModule {}
33
+ * ```
34
+ *
35
+ * Requires `SseChannelModule` (which provides {@link SseHub}) in scope. Pair with a cross-pod
36
+ * `backplane` so a publish on any node reaches connections held by another.
37
+ */
38
+ function createNotificationsStreamController(options) {
39
+ const heartbeatMs = options.heartbeatMs ?? 25_000;
40
+ let NotificationsStreamController = class NotificationsStreamController {
41
+ hub;
42
+ constructor(hub) {
43
+ this.hub = hub;
44
+ }
45
+ stream(req) {
46
+ const key$ = (0, rxjs_1.from)((async () => {
47
+ const route = await options.resolveRoute(req);
48
+ const tenant = options.resolveTenant ? await options.resolveTenant(req) : undefined;
49
+ return (0, sse_key_1.sseKey)(tenant, route);
50
+ })());
51
+ const events$ = key$.pipe((0, rxjs_1.switchMap)((key) => this.hub.stream(key)));
52
+ if (!heartbeatMs)
53
+ return events$;
54
+ const heartbeat$ = (0, rxjs_1.interval)(heartbeatMs).pipe((0, rxjs_1.map)(() => ({ data: '', type: 'heartbeat' })));
55
+ return (0, rxjs_1.merge)(events$, heartbeat$);
56
+ }
57
+ };
58
+ __decorate([
59
+ (0, common_1.Sse)(options.streamPath ?? 'stream'),
60
+ __param(0, (0, common_1.Req)()),
61
+ __metadata("design:type", Function),
62
+ __metadata("design:paramtypes", [Object]),
63
+ __metadata("design:returntype", Function)
64
+ ], NotificationsStreamController.prototype, "stream", null);
65
+ NotificationsStreamController = __decorate([
66
+ (0, common_1.Controller)(options.path ?? 'notifications'),
67
+ __metadata("design:paramtypes", [sse_hub_1.SseHub])
68
+ ], NotificationsStreamController);
69
+ if (options.guards && options.guards.length > 0) {
70
+ (0, common_1.UseGuards)(...options.guards)(NotificationsStreamController);
71
+ }
72
+ return NotificationsStreamController;
73
+ }
74
+ //# sourceMappingURL=stream-controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream-controller.js","sourceRoot":"","sources":["../src/stream-controller.ts"],"names":[],"mappings":";;;;;;;;;;;;;;AAwDA,kFAgCC;AAxFD,2CAQwB;AACxB,+BAA8E;AAC9E,uCAAmC;AACnC,uCAAmC;AA2BnC;;;;;;;;;;;;;;;;;GAiBG;AACH,SAAgB,mCAAmC,CACjD,OAA6C;IAE7C,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,MAAM,CAAC;IAElD,IACM,6BAA6B,GADnC,MACM,6BAA6B;QACJ;QAA7B,YAA6B,GAAW;YAAX,QAAG,GAAH,GAAG,CAAQ;QAAG,CAAC;QAG5C,MAAM,CAAQ,GAAY;YACxB,MAAM,IAAI,GAAG,IAAA,WAAI,EACf,CAAC,KAAK,IAAI,EAAE;gBACV,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;gBAC9C,MAAM,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM,OAAO,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;gBACpF,OAAO,IAAA,gBAAM,EAAC,MAAM,EAAE,KAAK,CAAC,CAAC;YAC/B,CAAC,CAAC,EAAE,CACL,CAAC;YACF,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,IAAA,gBAAS,EAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YACpE,IAAI,CAAC,WAAW;gBAAE,OAAO,OAAO,CAAC;YACjC,MAAM,UAAU,GAAG,IAAA,eAAQ,EAAC,WAAW,CAAC,CAAC,IAAI,CAC3C,IAAA,UAAG,EAAC,GAAiB,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC,CAC3D,CAAC;YACF,OAAO,IAAA,YAAK,EAAC,OAAO,EAAE,UAAU,CAAC,CAAC;QACpC,CAAC;KACF,CAAA;IAfC;QADC,IAAA,YAAG,EAAC,OAAO,CAAC,UAAU,IAAI,QAAQ,CAAC;QAC5B,WAAA,IAAA,YAAG,GAAE,CAAA;;;;+DAcZ;IAlBG,6BAA6B;QADlC,IAAA,mBAAU,EAAC,OAAO,CAAC,IAAI,IAAI,eAAe,CAAC;yCAER,gBAAM;OADpC,6BAA6B,CAmBlC;IAED,IAAI,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAChD,IAAA,kBAAS,EAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,6BAA6B,CAAC,CAAC;IAC9D,CAAC;IAED,OAAO,6BAA6B,CAAC;AACvC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dudousxd/nestjs-notifications-sse",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "description": "Server-Sent Events channel for nestjs-notifications — push real-time notifications over native NestJS SSE",
5
5
  "license": "MIT",
6
6
  "author": "Davide Carvalho",