opinionated-machine 5.1.0 → 5.2.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 (55) hide show
  1. package/README.md +1237 -278
  2. package/dist/index.d.ts +3 -2
  3. package/dist/index.js +5 -1
  4. package/dist/index.js.map +1 -1
  5. package/dist/lib/AbstractController.d.ts +3 -3
  6. package/dist/lib/AbstractController.js.map +1 -1
  7. package/dist/lib/AbstractModule.d.ts +14 -0
  8. package/dist/lib/AbstractModule.js +16 -0
  9. package/dist/lib/AbstractModule.js.map +1 -1
  10. package/dist/lib/DIContext.d.ts +35 -0
  11. package/dist/lib/DIContext.js +99 -0
  12. package/dist/lib/DIContext.js.map +1 -1
  13. package/dist/lib/resolverFunctions.d.ts +33 -0
  14. package/dist/lib/resolverFunctions.js +46 -0
  15. package/dist/lib/resolverFunctions.js.map +1 -1
  16. package/dist/lib/sse/AbstractSSEController.d.ts +163 -0
  17. package/dist/lib/sse/AbstractSSEController.js +228 -0
  18. package/dist/lib/sse/AbstractSSEController.js.map +1 -0
  19. package/dist/lib/sse/SSEConnectionSpy.d.ts +55 -0
  20. package/dist/lib/sse/SSEConnectionSpy.js +136 -0
  21. package/dist/lib/sse/SSEConnectionSpy.js.map +1 -0
  22. package/dist/lib/sse/index.d.ts +5 -0
  23. package/dist/lib/sse/index.js +6 -0
  24. package/dist/lib/sse/index.js.map +1 -0
  25. package/dist/lib/sse/sseContracts.d.ts +132 -0
  26. package/dist/lib/sse/sseContracts.js +102 -0
  27. package/dist/lib/sse/sseContracts.js.map +1 -0
  28. package/dist/lib/sse/sseParser.d.ts +167 -0
  29. package/dist/lib/sse/sseParser.js +225 -0
  30. package/dist/lib/sse/sseParser.js.map +1 -0
  31. package/dist/lib/sse/sseRouteBuilder.d.ts +47 -0
  32. package/dist/lib/sse/sseRouteBuilder.js +114 -0
  33. package/dist/lib/sse/sseRouteBuilder.js.map +1 -0
  34. package/dist/lib/sse/sseTypes.d.ts +164 -0
  35. package/dist/lib/sse/sseTypes.js +2 -0
  36. package/dist/lib/sse/sseTypes.js.map +1 -0
  37. package/dist/lib/testing/index.d.ts +5 -0
  38. package/dist/lib/testing/index.js +5 -0
  39. package/dist/lib/testing/index.js.map +1 -0
  40. package/dist/lib/testing/sseHttpClient.d.ts +203 -0
  41. package/dist/lib/testing/sseHttpClient.js +262 -0
  42. package/dist/lib/testing/sseHttpClient.js.map +1 -0
  43. package/dist/lib/testing/sseInjectClient.d.ts +173 -0
  44. package/dist/lib/testing/sseInjectClient.js +234 -0
  45. package/dist/lib/testing/sseInjectClient.js.map +1 -0
  46. package/dist/lib/testing/sseInjectHelpers.d.ts +59 -0
  47. package/dist/lib/testing/sseInjectHelpers.js +117 -0
  48. package/dist/lib/testing/sseInjectHelpers.js.map +1 -0
  49. package/dist/lib/testing/sseTestServer.d.ts +93 -0
  50. package/dist/lib/testing/sseTestServer.js +108 -0
  51. package/dist/lib/testing/sseTestServer.js.map +1 -0
  52. package/dist/lib/testing/sseTestTypes.d.ts +106 -0
  53. package/dist/lib/testing/sseTestTypes.js +2 -0
  54. package/dist/lib/testing/sseTestTypes.js.map +1 -0
  55. package/package.json +80 -78
package/dist/index.d.ts CHANGED
@@ -4,5 +4,6 @@ export { AbstractTestContextFactory, type CreateTestContextParams, } from './lib
4
4
  export type { NestedPartial } from './lib/configUtils.js';
5
5
  export { type DependencyInjectionOptions, DIContext, type RegisterDependenciesParams, } from './lib/DIContext.js';
6
6
  export { ENABLE_ALL, isAnyMessageQueueConsumerEnabled, isEnqueuedJobWorkersEnabled, isJobQueueEnabled, isMessageQueueConsumerEnabled, isPeriodicJobEnabled, resolveJobQueuesEnabled, } from './lib/diConfigUtils.js';
7
- export type { EnqueuedJobWorkerModuleOptions, JobQueueModuleOptions, MessageQueueConsumerModuleOptions, PeriodicJobOptions, } from './lib/resolverFunctions.js';
8
- export { asControllerClass, asEnqueuedJobQueueManagerFunction, asEnqueuedJobWorkerClass, asJobQueueClass, asMessageQueueHandlerClass, asPeriodicJobClass, asPgBossProcessorClass, asRepositoryClass, asServiceClass, asSingletonClass, asSingletonFunction, asUseCaseClass, type EnqueuedJobQueueManager, } from './lib/resolverFunctions.js';
7
+ export * from './lib/resolverFunctions.js';
8
+ export * from './lib/sse/index.js';
9
+ export * from './lib/testing/index.js';
package/dist/index.js CHANGED
@@ -3,5 +3,9 @@ export { AbstractModule, } from './lib/AbstractModule.js';
3
3
  export { AbstractTestContextFactory, } from './lib/AbstractTestContextFactory.js';
4
4
  export { DIContext, } from './lib/DIContext.js';
5
5
  export { ENABLE_ALL, isAnyMessageQueueConsumerEnabled, isEnqueuedJobWorkersEnabled, isJobQueueEnabled, isMessageQueueConsumerEnabled, isPeriodicJobEnabled, resolveJobQueuesEnabled, } from './lib/diConfigUtils.js';
6
- export { asControllerClass, asEnqueuedJobQueueManagerFunction, asEnqueuedJobWorkerClass, asJobQueueClass, asMessageQueueHandlerClass, asPeriodicJobClass, asPgBossProcessorClass, asRepositoryClass, asServiceClass, asSingletonClass, asSingletonFunction, asUseCaseClass, } from './lib/resolverFunctions.js';
6
+ export * from './lib/resolverFunctions.js';
7
+ // SSE
8
+ export * from './lib/sse/index.js';
9
+ // SSE testing utilities
10
+ export * from './lib/testing/index.js';
7
11
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAA8B,MAAM,6BAA6B,CAAA;AAC5F,OAAO,EACL,cAAc,GAGf,MAAM,yBAAyB,CAAA;AAChC,OAAO,EACL,0BAA0B,GAE3B,MAAM,qCAAqC,CAAA;AAE5C,OAAO,EAEL,SAAS,GAEV,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EACL,UAAU,EACV,gCAAgC,EAChC,2BAA2B,EAC3B,iBAAiB,EACjB,6BAA6B,EAC7B,oBAAoB,EACpB,uBAAuB,GACxB,MAAM,wBAAwB,CAAA;AAO/B,OAAO,EACL,iBAAiB,EACjB,iCAAiC,EACjC,wBAAwB,EACxB,eAAe,EACf,0BAA0B,EAC1B,kBAAkB,EAClB,sBAAsB,EACtB,iBAAiB,EACjB,cAAc,EACd,gBAAgB,EAChB,mBAAmB,EACnB,cAAc,GAEf,MAAM,4BAA4B,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAA8B,MAAM,6BAA6B,CAAA;AAC5F,OAAO,EACL,cAAc,GAGf,MAAM,yBAAyB,CAAA;AAChC,OAAO,EACL,0BAA0B,GAE3B,MAAM,qCAAqC,CAAA;AAE5C,OAAO,EAEL,SAAS,GAEV,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EACL,UAAU,EACV,gCAAgC,EAChC,2BAA2B,EAC3B,iBAAiB,EACjB,6BAA6B,EAC7B,oBAAoB,EACpB,uBAAuB,GACxB,MAAM,wBAAwB,CAAA;AAC/B,cAAc,4BAA4B,CAAA;AAC1C,MAAM;AACN,cAAc,oBAAoB,CAAA;AAClC,wBAAwB;AACxB,cAAc,wBAAwB,CAAA"}
@@ -3,10 +3,10 @@ import type { buildFastifyNoPayloadRoute, buildFastifyPayloadRoute } from '@loka
3
3
  import type { z } from 'zod/v4';
4
4
  type AnyCommonRouteDefinition = CommonRouteDefinition<any, any, any, any, any, any, any, any>;
5
5
  type OptionalZodSchema = z.Schema | undefined;
6
- type FastifyPayloadRouteReturnType<RequestBody extends OptionalZodSchema, ResponseBody extends OptionalZodSchema, Path extends OptionalZodSchema, Query extends OptionalZodSchema, Headers extends OptionalZodSchema, ResponseHeaders extends OptionalZodSchema, IsNonJSONResponseExpected extends boolean, IsEmptyResponseExpected extends boolean> = ReturnType<typeof buildFastifyPayloadRoute<RequestBody, ResponseBody, Path, Query, Headers, ResponseHeaders, IsNonJSONResponseExpected, IsEmptyResponseExpected>>;
7
- type FastifyNoPayloadRouteReturnType<RequestBody extends OptionalZodSchema, Path extends OptionalZodSchema, Query extends OptionalZodSchema, Headers extends OptionalZodSchema, ResponseHeaders extends OptionalZodSchema> = ReturnType<typeof buildFastifyNoPayloadRoute<RequestBody, Path, Query, Headers, ResponseHeaders>>;
6
+ type FastifyPayloadRouteReturnType<RequestBody extends OptionalZodSchema, ResponseBody extends OptionalZodSchema, Path extends OptionalZodSchema, Query extends OptionalZodSchema, Headers extends OptionalZodSchema, ResponseHeaders extends OptionalZodSchema, IsNonJSONResponseExpected extends boolean, IsEmptyResponseExpected extends boolean, ResponseSchemasByStatusCode extends Record<number, any> | undefined> = ReturnType<typeof buildFastifyPayloadRoute<RequestBody, ResponseBody, Path, Query, Headers, ResponseHeaders, IsNonJSONResponseExpected, IsEmptyResponseExpected, ResponseSchemasByStatusCode>>;
7
+ type FastifyNoPayloadRouteReturnType<RequestBody extends OptionalZodSchema, Path extends OptionalZodSchema, Query extends OptionalZodSchema, Headers extends OptionalZodSchema, ResponseHeaders extends OptionalZodSchema, ResponseSchemasByStatusCode extends Record<number, any> | undefined> = ReturnType<typeof buildFastifyNoPayloadRoute<RequestBody, Path, Query, Headers, ResponseHeaders, ResponseSchemasByStatusCode>>;
8
8
  export type BuildRoutesReturnType<APIContracts extends Record<string, AnyCommonRouteDefinition>> = {
9
- [K in keyof APIContracts]: APIContracts[K] extends PayloadRouteDefinition<infer RequestBody, infer ResponseBody, infer Path, infer Query, infer Headers, infer ResponseHeaders, infer IsNonJSONResponseExpected, infer IsEmptyResponseExpected, infer _ResponseSchemasByStatusCode> ? FastifyPayloadRouteReturnType<RequestBody, ResponseBody, Path, Query, Headers, ResponseHeaders, IsNonJSONResponseExpected, IsEmptyResponseExpected> : APIContracts[K] extends GetRouteDefinition<infer GetResponseBody, infer GetPath, infer GetQuery, infer GetHeaders, infer GetResponseHeaders, infer _GetIsNonJSONResponseExpected, infer _GetIsEmptyResponseExpected, infer _GetResponseSchemasByStatusCode> | DeleteRouteDefinition<infer DeleteResponseBody, infer DeletePath, infer DeleteQuery, infer DeleteHeaders, infer DeleteResponseHeaders, infer _DeleteIsNonJSONResponseExpected, infer _DeleteIsEmptyResponseExpected, infer _DeleteResponseSchemasByStatusCode> ? FastifyNoPayloadRouteReturnType<GetResponseBody | DeleteResponseBody, GetPath | DeletePath, GetQuery | DeleteQuery, GetHeaders | DeleteHeaders, GetResponseHeaders | DeleteResponseHeaders> : never;
9
+ [K in keyof APIContracts]: APIContracts[K] extends PayloadRouteDefinition<infer RequestBody, infer ResponseBody, infer Path, infer Query, infer Headers, infer ResponseHeaders, infer IsNonJSONResponseExpected, infer IsEmptyResponseExpected, infer ResponseSchemasByStatusCode> ? FastifyPayloadRouteReturnType<RequestBody, ResponseBody, Path, Query, Headers, ResponseHeaders, IsNonJSONResponseExpected, IsEmptyResponseExpected, ResponseSchemasByStatusCode> : APIContracts[K] extends GetRouteDefinition<infer GetResponseBody, infer GetPath, infer GetQuery, infer GetHeaders, infer GetResponseHeaders, infer _GetIsNonJSONResponseExpected, infer _GetIsEmptyResponseExpected, infer GetResponseSchemasByStatusCode> | DeleteRouteDefinition<infer DeleteResponseBody, infer DeletePath, infer DeleteQuery, infer DeleteHeaders, infer DeleteResponseHeaders, infer _DeleteIsNonJSONResponseExpected, infer _DeleteIsEmptyResponseExpected, infer DeleteResponseSchemasByStatusCode> ? FastifyNoPayloadRouteReturnType<GetResponseBody | DeleteResponseBody, GetPath | DeletePath, GetQuery | DeleteQuery, GetHeaders | DeleteHeaders, GetResponseHeaders | DeleteResponseHeaders, GetResponseSchemasByStatusCode | DeleteResponseSchemasByStatusCode> : never;
10
10
  };
11
11
  export declare abstract class AbstractController<APIContracts extends Record<string, AnyCommonRouteDefinition>> {
12
12
  abstract buildRoutes(): BuildRoutesReturnType<APIContracts>;
@@ -1 +1 @@
1
- {"version":3,"file":"AbstractController.js","sourceRoot":"","sources":["../../lib/AbstractController.ts"],"names":[],"mappings":"AAoGA,MAAM,OAAgB,kBAAkB;CAIvC"}
1
+ {"version":3,"file":"AbstractController.js","sourceRoot":"","sources":["../../lib/AbstractController.ts"],"names":[],"mappings":"AAkHA,MAAM,OAAgB,kBAAkB;CAIvC"}
@@ -10,4 +10,18 @@ export type UnionToIntersection<U> = (U extends any ? (x: U) => void : never) ex
10
10
  export declare abstract class AbstractModule<ModuleDependencies, ExternalDependencies = never> {
11
11
  abstract resolveDependencies(diOptions: DependencyInjectionOptions, externalDependencies: ExternalDependencies): MandatoryNameAndRegistrationPair<ModuleDependencies>;
12
12
  abstract resolveControllers(): MandatoryNameAndRegistrationPair<unknown>;
13
+ /**
14
+ * Override to register SSE controllers.
15
+ * Returns empty object by default - no changes needed for modules without SSE.
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * public resolveSSEControllers() {
20
+ * return {
21
+ * notificationsSSEController: asSSEControllerClass(NotificationsSSEController),
22
+ * }
23
+ * }
24
+ * ```
25
+ */
26
+ resolveSSEControllers(): MandatoryNameAndRegistrationPair<unknown>;
13
27
  }
@@ -1,3 +1,19 @@
1
1
  export class AbstractModule {
2
+ /**
3
+ * Override to register SSE controllers.
4
+ * Returns empty object by default - no changes needed for modules without SSE.
5
+ *
6
+ * @example
7
+ * ```typescript
8
+ * public resolveSSEControllers() {
9
+ * return {
10
+ * notificationsSSEController: asSSEControllerClass(NotificationsSSEController),
11
+ * }
12
+ * }
13
+ * ```
14
+ */
15
+ resolveSSEControllers() {
16
+ return {};
17
+ }
2
18
  }
3
19
  //# sourceMappingURL=AbstractModule.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AbstractModule.js","sourceRoot":"","sources":["../../lib/AbstractModule.ts"],"names":[],"mappings":"AAcA,MAAM,OAAgB,cAAc;CAOnC"}
1
+ {"version":3,"file":"AbstractModule.js","sourceRoot":"","sources":["../../lib/AbstractModule.ts"],"names":[],"mappings":"AAcA,MAAM,OAAgB,cAAc;IAQlC;;;;;;;;;;;;OAYG;IACI,qBAAqB;QAC1B,OAAO,EAAE,CAAA;IACX,CAAC;CACF"}
@@ -4,6 +4,7 @@ import type { FastifyInstance } from 'fastify';
4
4
  import type { AbstractModule } from './AbstractModule.js';
5
5
  import { type NestedPartial } from './configUtils.js';
6
6
  import type { ENABLE_ALL } from './diConfigUtils.js';
7
+ import { type RegisterSSERoutesOptions } from './sse/sseRouteBuilder.js';
7
8
  export type RegisterDependenciesParams<Dependencies, Config, ExternalDependencies> = {
8
9
  modules: readonly AbstractModule<unknown, ExternalDependencies>[];
9
10
  secondaryModules?: readonly AbstractModule<unknown, ExternalDependencies>[];
@@ -16,17 +17,51 @@ export type DependencyInjectionOptions = {
16
17
  enqueuedJobWorkersEnabled?: false | typeof ENABLE_ALL | string[];
17
18
  messageQueueConsumersEnabled?: false | typeof ENABLE_ALL | string[];
18
19
  periodicJobsEnabled?: false | typeof ENABLE_ALL | string[];
20
+ /**
21
+ * Enable SSE test mode features like connection spying.
22
+ * Only relevant for SSE controllers. Set to true in test environments.
23
+ * @default false
24
+ */
25
+ isTestMode?: boolean;
19
26
  };
20
27
  export declare class DIContext<Dependencies extends object, Config extends object, ExternalDependencies = undefined> {
21
28
  private readonly options;
22
29
  readonly awilixManager: AwilixManager;
23
30
  readonly diContainer: AwilixContainer<Dependencies>;
24
31
  private readonly controllerResolvers;
32
+ private readonly sseControllerNames;
25
33
  private readonly appConfig;
26
34
  constructor(diContainer: AwilixContainer, options: DependencyInjectionOptions, appConfig: Config, awilixManager?: AwilixManager);
27
35
  private registerModule;
28
36
  registerDependencies(params: RegisterDependenciesParams<Dependencies, Config, ExternalDependencies>, externalDependencies: ExternalDependencies, resolveControllers?: boolean): void;
29
37
  registerRoutes(app: FastifyInstance<any, any, any, any>): void;
38
+ /**
39
+ * Check if any SSE controllers are registered.
40
+ * Use this to conditionally call registerSSERoutes().
41
+ */
42
+ hasSSEControllers(): boolean;
43
+ /**
44
+ * Register SSE routes with the Fastify app.
45
+ *
46
+ * Must be called separately from registerRoutes().
47
+ * Requires @fastify/sse plugin to be registered on the app.
48
+ *
49
+ * @param app - Fastify instance with @fastify/sse registered
50
+ * @param options - Optional configuration for SSE routes
51
+ *
52
+ * @example
53
+ * ```typescript
54
+ * // Register @fastify/sse plugin first
55
+ * await app.register(fastifySSE, { heartbeatInterval: 30000 })
56
+ *
57
+ * // Then register SSE routes
58
+ * context.registerSSERoutes(app)
59
+ * ```
60
+ */
61
+ registerSSERoutes(app: FastifyInstance<any, any, any, any>, options?: RegisterSSERoutesOptions): void;
62
+ private applySSERouteOptions;
63
+ private applyPreHandlers;
64
+ private applyRateLimit;
30
65
  destroy(): Promise<void>;
31
66
  init(): Promise<void>;
32
67
  }
@@ -1,11 +1,15 @@
1
1
  import { AwilixManager } from 'awilix-manager';
2
+ import { merge } from 'ts-deepmerge';
2
3
  import { mergeConfigAndDependencyOverrides } from './configUtils.js';
4
+ import { buildFastifySSERoute } from './sse/sseRouteBuilder.js';
3
5
  export class DIContext {
4
6
  options;
5
7
  awilixManager;
6
8
  diContainer;
7
9
  // biome-ignore lint/suspicious/noExplicitAny: all controllers are controllers
8
10
  controllerResolvers;
11
+ // SSE controller dependency names (resolved from container to preserve singletons)
12
+ sseControllerNames;
9
13
  appConfig;
10
14
  constructor(diContainer, options, appConfig, awilixManager) {
11
15
  this.options = options;
@@ -21,6 +25,7 @@ export class DIContext {
21
25
  strictBooleanEnforced: true,
22
26
  });
23
27
  this.controllerResolvers = [];
28
+ this.sseControllerNames = [];
24
29
  }
25
30
  registerModule(module, targetDiConfig, externalDependencies, resolveControllers, isPrimaryModule) {
26
31
  const resolvedDIConfig = module.resolveDependencies(this.options, externalDependencies);
@@ -33,6 +38,11 @@ export class DIContext {
33
38
  }
34
39
  if (isPrimaryModule && resolveControllers) {
35
40
  this.controllerResolvers.push(...Object.values(module.resolveControllers()));
41
+ // Collect SSE controller names (resolved from container to preserve singletons)
42
+ const sseControllers = module.resolveSSEControllers();
43
+ if (sseControllers && Object.keys(sseControllers).length > 0) {
44
+ this.sseControllerNames.push(...Object.keys(sseControllers));
45
+ }
36
46
  }
37
47
  }
38
48
  registerDependencies(params, externalDependencies, resolveControllers = true) {
@@ -68,10 +78,99 @@ export class DIContext {
68
78
  const controller = controllerResolver.resolve(this.diContainer);
69
79
  const routes = controller.buildRoutes();
70
80
  for (const route of Object.values(routes)) {
81
+ // Cast needed: GET/DELETE routes have body:undefined, POST/PATCH have body:unknown
82
+ // The union is incompatible with app.route() due to handler contravariance
83
+ app.route(route);
84
+ }
85
+ }
86
+ }
87
+ /**
88
+ * Check if any SSE controllers are registered.
89
+ * Use this to conditionally call registerSSERoutes().
90
+ */
91
+ hasSSEControllers() {
92
+ return this.sseControllerNames.length > 0;
93
+ }
94
+ /**
95
+ * Register SSE routes with the Fastify app.
96
+ *
97
+ * Must be called separately from registerRoutes().
98
+ * Requires @fastify/sse plugin to be registered on the app.
99
+ *
100
+ * @param app - Fastify instance with @fastify/sse registered
101
+ * @param options - Optional configuration for SSE routes
102
+ *
103
+ * @example
104
+ * ```typescript
105
+ * // Register @fastify/sse plugin first
106
+ * await app.register(fastifySSE, { heartbeatInterval: 30000 })
107
+ *
108
+ * // Then register SSE routes
109
+ * context.registerSSERoutes(app)
110
+ * ```
111
+ */
112
+ registerSSERoutes(
113
+ // biome-ignore lint/suspicious/noExplicitAny: Fastify instance types are complex
114
+ app, options) {
115
+ if (!this.hasSSEControllers()) {
116
+ return;
117
+ }
118
+ for (const controllerName of this.sseControllerNames) {
119
+ // Resolve from container to use the singleton instance
120
+ const sseController = this.diContainer.resolve(controllerName);
121
+ const sseRoutes = sseController.buildSSERoutes();
122
+ for (const routeConfig of Object.values(sseRoutes)) {
123
+ const route = buildFastifySSERoute(sseController, routeConfig);
124
+ this.applySSERouteOptions(route, options);
71
125
  app.route(route);
72
126
  }
73
127
  }
74
128
  }
129
+ applySSERouteOptions(route, options) {
130
+ if (options?.preHandler) {
131
+ this.applyPreHandlers(route, options.preHandler);
132
+ }
133
+ if (options?.rateLimit) {
134
+ this.applyRateLimit(route, options.rateLimit);
135
+ }
136
+ // Apply SSE-specific options (heartbeatInterval, serializer)
137
+ if (options?.heartbeatInterval !== undefined || options?.serializer !== undefined) {
138
+ // biome-ignore lint/suspicious/noExplicitAny: config types vary by plugins
139
+ const routeWithConfig = route;
140
+ routeWithConfig.config = merge(routeWithConfig.config || {}, {
141
+ sse: {
142
+ ...(options.heartbeatInterval !== undefined && {
143
+ heartbeatInterval: options.heartbeatInterval,
144
+ }),
145
+ ...(options.serializer !== undefined && { serializer: options.serializer }),
146
+ },
147
+ });
148
+ }
149
+ }
150
+ applyPreHandlers(route, globalPreHandler) {
151
+ const existingPreHandler = route.preHandler;
152
+ if (!existingPreHandler) {
153
+ route.preHandler = globalPreHandler;
154
+ return;
155
+ }
156
+ // biome-ignore lint/suspicious/noExplicitAny: preHandler types are complex
157
+ const handlers = Array.isArray(existingPreHandler)
158
+ ? existingPreHandler
159
+ : [existingPreHandler];
160
+ // biome-ignore lint/suspicious/noExplicitAny: preHandler types are complex
161
+ const globalHandlers = Array.isArray(globalPreHandler)
162
+ ? globalPreHandler
163
+ : [globalPreHandler];
164
+ route.preHandler = [...globalHandlers, ...handlers];
165
+ }
166
+ applyRateLimit(route, rateLimit) {
167
+ // biome-ignore lint/suspicious/noExplicitAny: config types vary by plugins
168
+ const routeWithConfig = route;
169
+ routeWithConfig.config = {
170
+ ...(routeWithConfig.config || {}),
171
+ rateLimit,
172
+ };
173
+ }
75
174
  async destroy() {
76
175
  await this.awilixManager.executeDispose();
77
176
  await this.diContainer.dispose();
@@ -1 +1 @@
1
- {"version":3,"file":"DIContext.js","sourceRoot":"","sources":["../../lib/DIContext.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"DIContext.js","sourceRoot":"","sources":["../../lib/DIContext.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAE9C,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAA;AAGpC,OAAO,EAAE,iCAAiC,EAAsB,MAAM,kBAAkB,CAAA;AAIxF,OAAO,EAAE,oBAAoB,EAAiC,MAAM,0BAA0B,CAAA;AAuB9F,MAAM,OAAO,SAAS;IAKH,OAAO,CAA4B;IACpC,aAAa,CAAe;IAC5B,WAAW,CAA+B;IAC1D,8EAA8E;IAC7D,mBAAmB,CAAiB;IACrD,mFAAmF;IAClE,kBAAkB,CAAU;IAC5B,SAAS,CAAQ;IAElC,YACE,WAA4B,EAC5B,OAAmC,EACnC,SAAiB,EACjB,aAA6B;QAE7B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;QACtB,IAAI,CAAC,WAAW,GAAG,WAAW,CAAA;QAC9B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAA;QAC1B,IAAI,CAAC,aAAa;YAChB,aAAa;gBACb,IAAI,aAAa,CAAC;oBAChB,YAAY,EAAE,IAAI;oBAClB,SAAS,EAAE,IAAI;oBACf,WAAW;oBACX,WAAW,EAAE,IAAI;oBACjB,qBAAqB,EAAE,IAAI;iBAC5B,CAAC,CAAA;QACJ,IAAI,CAAC,mBAAmB,GAAG,EAAE,CAAA;QAC7B,IAAI,CAAC,kBAAkB,GAAG,EAAE,CAAA;IAC9B,CAAC;IAEO,cAAc,CACpB,MAAqD,EACrD,cAAqD,EACrD,oBAA0C,EAC1C,kBAA2B,EAC3B,eAAwB;QAExB,MAAM,gBAAgB,GAAG,MAAM,CAAC,mBAAmB,CAAC,IAAI,CAAC,OAAO,EAAE,oBAAoB,CAAC,CAAA;QAEvF,KAAK,MAAM,GAAG,IAAI,gBAAgB,EAAE,CAAC;YACnC,2DAA2D;YAC3D,IAAI,eAAe,IAAI,gBAAgB,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC;gBACpD,2DAA2D;gBAC3D,cAAc,CAAC,GAAG,CAAC,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAA;YAC7C,CAAC;QACH,CAAC;QAED,IAAI,eAAe,IAAI,kBAAkB,EAAE,CAAC;YAC1C,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAC3B,GAAI,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,kBAAkB,EAAE,CAAyB,CACvE,CAAA;YAED,gFAAgF;YAChF,MAAM,cAAc,GAAG,MAAM,CAAC,qBAAqB,EAAE,CAAA;YACrD,IAAI,cAAc,IAAI,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC7D,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAA;YAC9D,CAAC;QACH,CAAC;IACH,CAAC;IAED,oBAAoB,CAClB,MAA8E,EAC9E,oBAA0C,EAC1C,kBAAkB,GAAG,IAAI;QAEzB,MAAM,eAAe,GAAG,iCAAiC,CACvD,IAAI,CAAC,SAAS,EACd,MAAM,CAAC,kBAAkB,IAAI,QAAQ,EACrC,MAAM,CAAC,eAAe,EACtB,MAAM,CAAC,mBAAmB,IAAI,EAAE,CACjC,CAAA;QACD,MAAM,cAAc,GAA0C,EAAE,CAAA;QAEhE,KAAK,MAAM,aAAa,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YAC3C,IAAI,CAAC,cAAc,CACjB,aAAa,EACb,cAAc,EACd,oBAAoB,EACpB,kBAAkB,EAClB,IAAI,CACL,CAAA;QACH,CAAC;QAED,IAAI,MAAM,CAAC,gBAAgB,EAAE,CAAC;YAC5B,KAAK,MAAM,eAAe,IAAI,MAAM,CAAC,gBAAgB,EAAE,CAAC;gBACtD,IAAI,CAAC,cAAc,CACjB,eAAe,EACf,cAAc,EACd,oBAAoB,EACpB,kBAAkB,EAClB,KAAK,CACN,CAAA;YACH,CAAC;QACH,CAAC;QAED,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAA;QAEzC,8BAA8B;QAC9B,0CAA0C;QAC1C,KAAK,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,EAAE,CAAC;YAChF,MAAM,eAAe,GAAG,EAAE,GAAI,gBAAsC,EAAE,CAAA;YAEtE,2CAA2C;YAC3C,MAAM,gBAAgB,GAAG,IAAI,CAAC,WAAW,CAAC,eAAe,CAAC,aAAa,CAAC,CAAA;YACxE,mBAAmB;YACnB,IAAI,eAAe,CAAC,QAAQ,KAAK,gBAAgB,CAAC,QAAQ,EAAE,CAAC;gBAC3D,mBAAmB;gBACnB,eAAe,CAAC,QAAQ,GAAG,gBAAgB,CAAC,QAAQ,CAAA;YACtD,CAAC;YAED,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,aAAa,EAAE,eAAe,CAAC,CAAA;QAC3D,CAAC;IACH,CAAC;IAED,4FAA4F;IAC5F,cAAc,CAAC,GAAwC;QACrD,KAAK,MAAM,kBAAkB,IAAI,IAAI,CAAC,mBAAmB,EAAE,CAAC;YAC1D,wEAAwE;YACxE,MAAM,UAAU,GAA4B,kBAAkB,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;YACxF,MAAM,MAAM,GAAG,UAAU,CAAC,WAAW,EAAE,CAAA;YACvC,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC1C,mFAAmF;gBACnF,2EAA2E;gBAC3E,GAAG,CAAC,KAAK,CAAC,KAAkB,CAAC,CAAA;YAC/B,CAAC;QACH,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,iBAAiB;QACf,OAAO,IAAI,CAAC,kBAAkB,CAAC,MAAM,GAAG,CAAC,CAAA;IAC3C,CAAC;IAED;;;;;;;;;;;;;;;;;OAiBG;IACH,iBAAiB;IACf,iFAAiF;IACjF,GAAwC,EACxC,OAAkC;QAElC,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,CAAC;YAC9B,OAAM;QACR,CAAC;QAED,KAAK,MAAM,cAAc,IAAI,IAAI,CAAC,kBAAkB,EAAE,CAAC;YACrD,uDAAuD;YACvD,MAAM,aAAa,GACjB,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,cAAc,CAAC,CAAA;YAC1C,MAAM,SAAS,GAAG,aAAa,CAAC,cAAc,EAAE,CAAA;YAEhD,KAAK,MAAM,WAAW,IAAI,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC;gBACnD,MAAM,KAAK,GAAG,oBAAoB,CAAC,aAAa,EAAE,WAAW,CAAC,CAAA;gBAC9D,IAAI,CAAC,oBAAoB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;gBACzC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;YAClB,CAAC;QACH,CAAC;IACH,CAAC;IAEO,oBAAoB,CAAC,KAAmB,EAAE,OAAkC;QAClF,IAAI,OAAO,EAAE,UAAU,EAAE,CAAC;YACxB,IAAI,CAAC,gBAAgB,CAAC,KAAK,EAAE,OAAO,CAAC,UAAU,CAAC,CAAA;QAClD,CAAC;QACD,IAAI,OAAO,EAAE,SAAS,EAAE,CAAC;YACvB,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,OAAO,CAAC,SAAS,CAAC,CAAA;QAC/C,CAAC;QACD,6DAA6D;QAC7D,IAAI,OAAO,EAAE,iBAAiB,KAAK,SAAS,IAAI,OAAO,EAAE,UAAU,KAAK,SAAS,EAAE,CAAC;YAClF,2EAA2E;YAC3E,MAAM,eAAe,GAAG,KAAwC,CAAA;YAChE,eAAe,CAAC,MAAM,GAAG,KAAK,CAAC,eAAe,CAAC,MAAM,IAAI,EAAE,EAAE;gBAC3D,GAAG,EAAE;oBACH,GAAG,CAAC,OAAO,CAAC,iBAAiB,KAAK,SAAS,IAAI;wBAC7C,iBAAiB,EAAE,OAAO,CAAC,iBAAiB;qBAC7C,CAAC;oBACF,GAAG,CAAC,OAAO,CAAC,UAAU,KAAK,SAAS,IAAI,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,CAAC;iBAC5E;aACF,CAAC,CAAA;QACJ,CAAC;IACH,CAAC;IAEO,gBAAgB,CACtB,KAAmB,EACnB,gBAA4C;QAE5C,MAAM,kBAAkB,GAAG,KAAK,CAAC,UAAU,CAAA;QAC3C,IAAI,CAAC,kBAAkB,EAAE,CAAC;YACxB,KAAK,CAAC,UAAU,GAAG,gBAAgB,CAAA;YACnC,OAAM;QACR,CAAC;QACD,2EAA2E;QAC3E,MAAM,QAAQ,GAAU,KAAK,CAAC,OAAO,CAAC,kBAAkB,CAAC;YACvD,CAAC,CAAC,kBAAkB;YACpB,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAA;QACxB,2EAA2E;QAC3E,MAAM,cAAc,GAAU,KAAK,CAAC,OAAO,CAAC,gBAAgB,CAAC;YAC3D,CAAC,CAAC,gBAAgB;YAClB,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAA;QACtB,KAAK,CAAC,UAAU,GAAG,CAAC,GAAG,cAAc,EAAE,GAAG,QAAQ,CAAC,CAAA;IACrD,CAAC;IAEO,cAAc,CACpB,KAAmB,EACnB,SAA6D;QAE7D,2EAA2E;QAC3E,MAAM,eAAe,GAAG,KAAwC,CAAA;QAChE,eAAe,CAAC,MAAM,GAAG;YACvB,GAAG,CAAC,eAAe,CAAC,MAAM,IAAI,EAAE,CAAC;YACjC,SAAS;SACV,CAAA;IACH,CAAC;IAED,KAAK,CAAC,OAAO;QACX,MAAM,IAAI,CAAC,aAAa,CAAC,cAAc,EAAE,CAAA;QACzC,MAAM,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,CAAA;IAClC,CAAC;IAED,KAAK,CAAC,IAAI;QACR,MAAM,IAAI,CAAC,aAAa,CAAC,WAAW,EAAE,CAAA;IACxC,CAAC;CACF"}
@@ -10,11 +10,44 @@ export interface EnqueuedJobQueueManager {
10
10
  start(enabled?: string[] | boolean): Promise<void>;
11
11
  }
12
12
  export declare function asSingletonClass<T = object>(Type: Constructor<T>, opts?: BuildResolverOptions<T>): BuildResolver<T> & DisposableResolver<T>;
13
+ /**
14
+ * Register a class with an additional config parameter passed to the constructor.
15
+ * Uses asFunction wrapper internally to pass the config as a second parameter.
16
+ * Requires PROXY injection mode.
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * myService: asClassWithConfig(MyService, { enableFeature: true }),
21
+ * ```
22
+ */
23
+ export declare function asClassWithConfig<T = object, Config = unknown>(Type: Constructor<T>, config: Config, opts?: BuildResolverOptions<T>): BuildResolver<T> & DisposableResolver<T>;
13
24
  export declare function asSingletonFunction<T>(fn: FunctionReturning<T>, opts?: BuildResolverOptions<T>): BuildResolver<T> & DisposableResolver<T>;
14
25
  export declare function asServiceClass<T = object>(Type: Constructor<T>, opts?: BuildResolverOptions<T>): BuildResolver<T> & DisposableResolver<T>;
15
26
  export declare function asUseCaseClass<T = object>(Type: Constructor<T>, opts?: BuildResolverOptions<T>): BuildResolver<T> & DisposableResolver<T>;
16
27
  export declare function asRepositoryClass<T = object>(Type: Constructor<T>, opts?: BuildResolverOptions<T>): BuildResolver<T> & DisposableResolver<T>;
17
28
  export declare function asControllerClass<T = object>(Type: Constructor<T>, opts?: BuildResolverOptions<T>): BuildResolver<T> & DisposableResolver<T>;
29
+ export type SSEControllerModuleOptions = {
30
+ diOptions: DependencyInjectionOptions;
31
+ };
32
+ /**
33
+ * Register an SSE controller class with the DI container.
34
+ *
35
+ * SSE controllers handle Server-Sent Events connections and require
36
+ * graceful shutdown to close all active connections.
37
+ *
38
+ * When `diOptions.isTestMode` is true, connection spying is enabled
39
+ * allowing tests to await connections via `controller.connectionSpy`.
40
+ *
41
+ * @example
42
+ * ```typescript
43
+ * // Without test mode
44
+ * notificationsSSEController: asSSEControllerClass(NotificationsSSEController),
45
+ *
46
+ * // With test mode (enables connection spy)
47
+ * notificationsSSEController: asSSEControllerClass(NotificationsSSEController, { diOptions }),
48
+ * ```
49
+ */
50
+ export declare function asSSEControllerClass<T = object>(Type: Constructor<T>, sseOptions?: SSEControllerModuleOptions, opts?: BuildResolverOptions<T>): BuildResolver<T> & DisposableResolver<T>;
18
51
  export type MessageQueueConsumerModuleOptions = {
19
52
  queueName: string;
20
53
  diOptions: DependencyInjectionOptions;
@@ -6,6 +6,23 @@ export function asSingletonClass(Type, opts) {
6
6
  lifetime: 'SINGLETON',
7
7
  });
8
8
  }
9
+ /**
10
+ * Register a class with an additional config parameter passed to the constructor.
11
+ * Uses asFunction wrapper internally to pass the config as a second parameter.
12
+ * Requires PROXY injection mode.
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * myService: asClassWithConfig(MyService, { enableFeature: true }),
17
+ * ```
18
+ */
19
+ export function asClassWithConfig(Type, config, opts) {
20
+ // biome-ignore lint/suspicious/noExplicitAny: Dynamic constructor invocation with cradle proxy
21
+ return asFunction((cradle) => new Type(cradle, config), {
22
+ ...opts,
23
+ lifetime: opts?.lifetime ?? 'SINGLETON',
24
+ });
25
+ }
9
26
  export function asSingletonFunction(fn, opts) {
10
27
  return asFunction(fn, {
11
28
  ...opts,
@@ -40,6 +57,35 @@ export function asControllerClass(Type, opts) {
40
57
  lifetime: 'SINGLETON',
41
58
  });
42
59
  }
60
+ /**
61
+ * Register an SSE controller class with the DI container.
62
+ *
63
+ * SSE controllers handle Server-Sent Events connections and require
64
+ * graceful shutdown to close all active connections.
65
+ *
66
+ * When `diOptions.isTestMode` is true, connection spying is enabled
67
+ * allowing tests to await connections via `controller.connectionSpy`.
68
+ *
69
+ * @example
70
+ * ```typescript
71
+ * // Without test mode
72
+ * notificationsSSEController: asSSEControllerClass(NotificationsSSEController),
73
+ *
74
+ * // With test mode (enables connection spy)
75
+ * notificationsSSEController: asSSEControllerClass(NotificationsSSEController, { diOptions }),
76
+ * ```
77
+ */
78
+ export function asSSEControllerClass(Type, sseOptions, opts) {
79
+ const enableConnectionSpy = sseOptions?.diOptions.isTestMode ?? false;
80
+ const sseConfig = enableConnectionSpy ? { enableConnectionSpy: true } : undefined;
81
+ return asClassWithConfig(Type, sseConfig, {
82
+ public: false,
83
+ asyncDispose: 'closeAllConnections',
84
+ asyncDisposePriority: 5, // Close SSE connections early in shutdown
85
+ ...opts,
86
+ lifetime: 'SINGLETON',
87
+ });
88
+ }
43
89
  export function asMessageQueueHandlerClass(Type, mqOptions, opts) {
44
90
  return asClass(Type, {
45
91
  // these follow message-queue-toolkit conventions
@@ -1 +1 @@
1
- {"version":3,"file":"resolverFunctions.js","sourceRoot":"","sources":["../../lib/resolverFunctions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"resolverFunctions.js","sourceRoot":"","sources":["../../lib/resolverFunctions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,163 @@
1
+ import { SSEConnectionSpy } from './SSEConnectionSpy.ts';
2
+ import type { AnySSERouteDefinition } from './sseContracts.ts';
3
+ import type { BuildSSERoutesReturnType, SSEConnection, SSEControllerConfig, SSEMessage } from './sseTypes.ts';
4
+ export type { SSEConnectionEvent } from './SSEConnectionSpy.ts';
5
+ export { SSEConnectionSpy } from './SSEConnectionSpy.ts';
6
+ export type { BuildSSERoutesReturnType, InferSSERequest, SSEConnection, SSEControllerConfig, SSEHandlerConfig, SSELogger, SSEMessage, SSEPreHandler, SSERouteHandler, SSERouteOptions, } from './sseTypes.ts';
7
+ /**
8
+ * Abstract base class for SSE controllers.
9
+ *
10
+ * Provides connection management, broadcasting, and lifecycle hooks.
11
+ * Extend this class to create SSE controllers that handle real-time
12
+ * streaming connections.
13
+ *
14
+ * @template APIContracts - Map of route names to SSE route definitions
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * class NotificationsSSEController extends AbstractSSEController<typeof contracts> {
19
+ * public static contracts = {
20
+ * notifications: buildSSERoute({ ... }),
21
+ * } as const
22
+ *
23
+ * public buildSSERoutes() {
24
+ * return {
25
+ * notifications: {
26
+ * contract: NotificationsSSEController.contracts.notifications,
27
+ * handler: this.handleNotifications,
28
+ * },
29
+ * }
30
+ * }
31
+ * }
32
+ * ```
33
+ */
34
+ export declare abstract class AbstractSSEController<APIContracts extends Record<string, AnySSERouteDefinition>> {
35
+ /** Map of connection ID to connection object */
36
+ protected connections: Map<string, SSEConnection>;
37
+ /** Private storage for connection spy */
38
+ private readonly _connectionSpy?;
39
+ /**
40
+ * SSE controllers must override this constructor and call super with their
41
+ * dependencies object and the SSE config.
42
+ *
43
+ * @param _dependencies - The dependencies object (cradle proxy in awilix)
44
+ * @param sseConfig - Optional SSE controller configuration
45
+ *
46
+ * @example
47
+ * ```typescript
48
+ * class MySSEController extends AbstractSSEController<MyContracts> {
49
+ * private myService: MyService
50
+ *
51
+ * constructor(deps: { myService: MyService }, sseConfig?: SSEControllerConfig) {
52
+ * super(deps, sseConfig)
53
+ * this.myService = deps.myService
54
+ * }
55
+ * }
56
+ * ```
57
+ */
58
+ constructor(_dependencies: object, sseConfig?: SSEControllerConfig);
59
+ /**
60
+ * Get the connection spy for testing.
61
+ * Throws an error if spies are not enabled.
62
+ * Enable spies by passing `{ enableConnectionSpy: true }` to the constructor.
63
+ *
64
+ * @example
65
+ * ```typescript
66
+ * // In test, create controller with spy enabled
67
+ * // Pass dependencies first, then config with enableConnectionSpy
68
+ * const controller = new MySSEController({}, { enableConnectionSpy: true })
69
+ *
70
+ * // Start connection (async)
71
+ * connectSSE(baseUrl, '/api/stream')
72
+ *
73
+ * // Wait for connection - handles race condition
74
+ * const connection = await controller.connectionSpy.waitForConnection()
75
+ * ```
76
+ *
77
+ * @throws Error if connection spy is not enabled
78
+ */
79
+ get connectionSpy(): SSEConnectionSpy;
80
+ /**
81
+ * Build and return SSE route configurations.
82
+ * Similar pattern to AbstractController.buildRoutes().
83
+ */
84
+ abstract buildSSERoutes(): BuildSSERoutesReturnType<APIContracts>;
85
+ /**
86
+ * Controller-level hook called when any connection is established.
87
+ * Override this method to add global connection handling logic.
88
+ * This is called AFTER the connection is registered and route-level onConnect.
89
+ *
90
+ * @param connection - The newly established connection
91
+ */
92
+ protected onConnectionEstablished?(connection: SSEConnection): void;
93
+ /**
94
+ * Controller-level hook called when any connection is closed.
95
+ * Override this method to add global disconnect handling logic.
96
+ * This is called BEFORE the connection is unregistered and route-level onDisconnect.
97
+ *
98
+ * @param connection - The connection being closed
99
+ */
100
+ protected onConnectionClosed?(connection: SSEConnection): void;
101
+ /**
102
+ * Send an event to a specific connection.
103
+ *
104
+ * @param connectionId - The connection to send to
105
+ * @param message - The SSE message to send
106
+ * @returns true if sent successfully, false if connection not found or closed
107
+ */
108
+ protected sendEvent<T>(connectionId: string, message: SSEMessage<T>): Promise<boolean>;
109
+ /**
110
+ * Broadcast an event to all connected clients.
111
+ *
112
+ * @param message - The SSE message to broadcast
113
+ * @returns Number of clients the message was sent to
114
+ */
115
+ protected broadcast<T>(message: SSEMessage<T>): Promise<number>;
116
+ /**
117
+ * Broadcast an event to connections matching a predicate.
118
+ *
119
+ * @param message - The SSE message to broadcast
120
+ * @param predicate - Function to filter connections
121
+ * @returns Number of clients the message was sent to
122
+ */
123
+ protected broadcastIf<T>(message: SSEMessage<T>, predicate: (connection: SSEConnection) => boolean): Promise<number>;
124
+ /**
125
+ * Get all active connections.
126
+ */
127
+ protected getConnections(): SSEConnection[];
128
+ /**
129
+ * Get the number of active connections.
130
+ */
131
+ protected getConnectionCount(): number;
132
+ /**
133
+ * Close a specific connection.
134
+ *
135
+ * This gracefully ends the SSE stream by calling the underlying `reply.sse.close()`.
136
+ * All previously sent data is flushed to the client before the connection terminates.
137
+ * Use this to signal end-of-stream after sending all events (e.g., in request-response
138
+ * style streaming like OpenAI completions).
139
+ *
140
+ * @param connectionId - The connection to close
141
+ * @returns true if connection was found and closed
142
+ */
143
+ protected closeConnection(connectionId: string): boolean;
144
+ /**
145
+ * Close all active connections.
146
+ * Called during graceful shutdown via asyncDispose.
147
+ */
148
+ closeAllConnections(): void;
149
+ /**
150
+ * Register a connection (called internally by route builder).
151
+ * Triggers the onConnectionEstablished hook and spy if defined.
152
+ * @internal
153
+ */
154
+ registerConnection(connection: SSEConnection): void;
155
+ /**
156
+ * Unregister a connection (called internally by route builder).
157
+ * Triggers the onConnectionClosed hook and spy if defined.
158
+ * This method is idempotent - calling it multiple times for the same
159
+ * connection ID has no effect after the first call.
160
+ * @internal
161
+ */
162
+ unregisterConnection(connectionId: string): void;
163
+ }