• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nestjs / nest / f21074a3-8fcf-4ec1-a93c-75675d264f01

22 Oct 2025 12:34AM UTC coverage: 88.726% (-0.2%) from 88.888%
f21074a3-8fcf-4ec1-a93c-75675d264f01

Pull #15815

circleci

mag123c
test(core): add nested transient isolation integration test
Pull Request #15815: fix(core): ensure nested transient provider isolation

2742 of 3477 branches covered (78.86%)

7 of 7 new or added lines in 1 file covered. (100.0%)

52 existing lines in 5 files now uncovered.

7280 of 8205 relevant lines covered (88.73%)

17.3 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.67
/packages/websockets/web-sockets-controller.ts
1
import { NestApplicationContextOptions } from '@nestjs/common/interfaces/nest-application-context-options.interface';
2
import { Type } from '@nestjs/common/interfaces/type.interface';
3
import { Logger } from '@nestjs/common/services/logger.service';
1✔
4
import { ApplicationConfig } from '@nestjs/core/application-config';
5
import { GraphInspector } from '@nestjs/core/inspector/graph-inspector';
6
import { MetadataScanner } from '@nestjs/core/metadata-scanner';
1✔
7
import {
1✔
8
  from as fromPromise,
9
  isObservable,
10
  Observable,
11
  of,
12
  Subject,
13
} from 'rxjs';
14
import { distinctUntilChanged, mergeAll } from 'rxjs/operators';
1✔
15
import { GATEWAY_OPTIONS, PORT_METADATA } from './constants';
1✔
16
import { WsContextCreator } from './context/ws-context-creator';
17
import { InvalidSocketPortException } from './errors/invalid-socket-port.exception';
1✔
18
import {
1✔
19
  GatewayMetadataExplorer,
20
  MessageMappingProperties,
21
} from './gateway-metadata-explorer';
22
import { GatewayMetadata } from './interfaces/gateway-metadata.interface';
23
import { NestGateway } from './interfaces/nest-gateway.interface';
24
import { ServerAndEventStreamsHost } from './interfaces/server-and-event-streams-host.interface';
25
import { WebsocketEntrypointMetadata } from './interfaces/websockets-entrypoint-metadata.interface';
26
import { SocketServerProvider } from './socket-server-provider';
27
import { compareElementAt } from './utils/compare-element.util';
1✔
28

29
export class WebSocketsController {
1✔
30
  private readonly logger = new Logger(WebSocketsController.name, {
28✔
31
    timestamp: true,
32
  });
33
  private readonly metadataExplorer = new GatewayMetadataExplorer(
28✔
34
    new MetadataScanner(),
35
  );
36

37
  constructor(
38
    private readonly socketServerProvider: SocketServerProvider,
28✔
39
    private readonly config: ApplicationConfig,
28✔
40
    private readonly contextCreator: WsContextCreator,
28✔
41
    private readonly graphInspector: GraphInspector,
28✔
42
    private readonly appOptions: NestApplicationContextOptions = {},
28✔
43
  ) {}
44

45
  public connectGatewayToServer(
46
    instance: NestGateway,
47
    metatype: Type<unknown> | Function,
48
    moduleKey: string,
49
    instanceWrapperId: string,
50
  ) {
51
    const options = Reflect.getMetadata(GATEWAY_OPTIONS, metatype) || {};
3!
52
    const port = Reflect.getMetadata(PORT_METADATA, metatype) || 0;
3✔
53

54
    if (!Number.isInteger(port)) {
3✔
55
      throw new InvalidSocketPortException(port, metatype);
1✔
56
    }
57
    this.subscribeToServerEvents(
2✔
58
      instance,
59
      options,
60
      port,
61
      moduleKey,
62
      instanceWrapperId,
63
    );
64
  }
65

66
  public subscribeToServerEvents<T extends GatewayMetadata>(
67
    instance: NestGateway,
68
    options: T,
69
    port: number,
70
    moduleKey: string,
71
    instanceWrapperId: string,
72
  ) {
73
    const nativeMessageHandlers = this.metadataExplorer.explore(instance);
2✔
74
    const messageHandlers = nativeMessageHandlers.map(
2✔
75
      ({ callback, message, methodName }) => ({
2✔
76
        message,
77
        methodName,
78
        callback: this.contextCreator.create(
79
          instance,
80
          callback,
81
          moduleKey,
82
          methodName,
83
        ),
84
      }),
85
    );
86

87
    this.inspectEntrypointDefinitions(
2✔
88
      instance,
89
      port,
90
      messageHandlers,
91
      instanceWrapperId,
92
    );
93

94
    if (this.appOptions.preview) {
2!
UNCOV
95
      return;
×
96
    }
97
    const observableServer = this.socketServerProvider.scanForSocketServer<T>(
2✔
98
      options,
99
      port,
100
    );
101
    this.assignServerToProperties(instance, observableServer.server);
2✔
102
    this.subscribeEvents(instance, messageHandlers, observableServer);
2✔
103
  }
104

105
  public subscribeEvents(
106
    instance: NestGateway,
107
    subscribersMap: MessageMappingProperties[],
108
    observableServer: ServerAndEventStreamsHost,
109
  ) {
110
    const { init, disconnect, connection, server } = observableServer;
5✔
111
    const adapter = this.config.getIoAdapter();
5✔
112

113
    this.subscribeInitEvent(instance, init);
5✔
114
    this.subscribeConnectionEvent(instance, connection);
5✔
115
    this.subscribeDisconnectEvent(instance, disconnect);
5✔
116

117
    const handler = this.getConnectionHandler(
5✔
118
      this,
119
      instance,
120
      subscribersMap,
121
      disconnect,
122
      connection,
123
    );
124
    adapter.bindClientConnect(server, handler);
5✔
125
    this.printSubscriptionLogs(instance, subscribersMap);
5✔
126
  }
127

128
  public getConnectionHandler(
129
    context: WebSocketsController,
130
    instance: NestGateway,
131
    subscribersMap: MessageMappingProperties[],
132
    disconnect: Subject<any>,
133
    connection: Subject<any>,
134
  ) {
135
    const adapter = this.config.getIoAdapter();
5✔
136
    return (...args: unknown[]) => {
5✔
137
      const [client] = args;
4✔
138
      connection.next(args);
4✔
139
      context.subscribeMessages(subscribersMap, client, instance);
4✔
140

141
      const disconnectHook = adapter.bindClientDisconnect;
4✔
142
      disconnectHook &&
4✔
UNCOV
143
        disconnectHook.call(adapter, client, () => disconnect.next(client));
×
144
    };
145
  }
146

147
  public subscribeInitEvent(instance: NestGateway, event: Subject<any>) {
148
    if (instance.afterInit) {
2✔
149
      event.subscribe(instance.afterInit.bind(instance));
1✔
150
    }
151
  }
152

153
  public subscribeConnectionEvent(instance: NestGateway, event: Subject<any>) {
154
    if (instance.handleConnection) {
2✔
155
      event
1✔
156
        .pipe(
UNCOV
157
          distinctUntilChanged((prev, curr) => compareElementAt(prev, curr, 0)),
×
158
        )
UNCOV
159
        .subscribe((args: unknown[]) => instance.handleConnection!(...args));
×
160
    }
161
  }
162

163
  public subscribeDisconnectEvent(instance: NestGateway, event: Subject<any>) {
164
    if (instance.handleDisconnect) {
2✔
165
      event
1✔
166
        .pipe(distinctUntilChanged())
167
        .subscribe(instance.handleDisconnect.bind(instance));
168
    }
169
  }
170

171
  public subscribeMessages<T = any>(
172
    subscribersMap: MessageMappingProperties[],
173
    client: T,
174
    instance: NestGateway,
175
  ) {
176
    const adapter = this.config.getIoAdapter();
1✔
177
    const handlers = subscribersMap.map(({ callback, message }) => ({
2✔
178
      message,
179
      callback: callback.bind(instance, client),
180
    }));
181
    adapter.bindMessageHandlers(client, handlers, data =>
1✔
UNCOV
182
      fromPromise(this.pickResult(data)).pipe(mergeAll()),
×
183
    );
184
  }
185

186
  public async pickResult(
187
    deferredResult: Promise<any>,
188
  ): Promise<Observable<any>> {
189
    const result = await deferredResult;
4✔
190
    if (isObservable(result)) {
4✔
191
      return result;
1✔
192
    }
193
    if (result instanceof Promise) {
3!
UNCOV
194
      return fromPromise(result);
×
195
    }
196
    return of(result);
3✔
197
  }
198

199
  public inspectEntrypointDefinitions(
200
    instance: NestGateway,
201
    port: number,
202
    messageHandlers: MessageMappingProperties[],
203
    instanceWrapperId: string,
204
  ) {
205
    messageHandlers.forEach(handler => {
3✔
206
      this.graphInspector.insertEntrypointDefinition<WebsocketEntrypointMetadata>(
4✔
207
        {
208
          type: 'websocket',
209
          methodName: handler.methodName,
210
          className: instance.constructor?.name,
211
          classNodeId: instanceWrapperId,
212
          metadata: {
213
            port,
214
            key: handler.message,
215
            message: handler.message,
216
          },
217
        },
218
        instanceWrapperId,
219
      );
220
    });
221
  }
222

223
  private assignServerToProperties<T = any>(
224
    instance: NestGateway,
225
    server: object,
226
  ) {
UNCOV
227
    for (const propertyKey of this.metadataExplorer.scanForServerHooks(
×
228
      instance,
229
    )) {
UNCOV
230
      Reflect.set(instance, propertyKey, server);
×
231
    }
232
  }
233

234
  private printSubscriptionLogs(
235
    instance: NestGateway,
236
    subscribersMap: MessageMappingProperties[],
237
  ) {
238
    const gatewayClassName = (instance as object)?.constructor?.name;
5✔
239
    if (!gatewayClassName) {
5!
UNCOV
240
      return;
×
241
    }
242
    subscribersMap.forEach(({ message }) =>
5✔
243
      this.logger.log(
5✔
244
        `${gatewayClassName} subscribed to the "${message}" message`,
245
      ),
246
    );
247
  }
248
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc