@nestjs/core 7.4.2 → 7.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/Readme.md CHANGED
@@ -89,7 +89,7 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
89
89
  <a href="https://sparkfabrik.com/" target="_blank"><img src="https://nestjs.com/img/sparkfabrik-logo.png" width="120" valign="middle" /></a></td><td align="center" valign="middle"><a href="https://www.thebigphonestore.co.uk/" target="_blank"><img src="https://nestjs.com/img/the-big-phone-store-logo.png" width="65" valign="middle" /></a></td>
90
90
  <td align="center" valign="middle">
91
91
  <a href="https://genuinebee.com/" target="_blank"><img src="https://nestjs.com/img/genuinebee.svg" width="97" valign="middle" /></a> </td></tr>
92
- <tr><td align="center" valign="middle"><a href="https://sanyodigital.com/" target="_blank"><img src="https://nestjs.com/img/sanyo-digital.png" width="130" valign="middle" /></a></td>
92
+ <tr><td align="center" valign="middle"><a href="https://sanyodigital.com/" target="_blank"><img src="https://nestjs.com/img/sanyo-digital.png" width="130" valign="middle" /></a></td><td align="center" valign="middle"><a href="https://vpn-review.com/vpn-for-torrenting" target="_blank"><img src="https://nestjs.com/img/vpn-review-logo.png" width="85" valign="middle" /></a></td>
93
93
  </tr></table>
94
94
 
95
95
  ## Backers
@@ -1,7 +1,14 @@
1
+ /// <reference types="node" />
1
2
  import { Type } from '@nestjs/common';
3
+ import { IncomingMessage } from 'http';
4
+ import { Observable } from 'rxjs';
2
5
  import { ContextId } from '../injector/instance-wrapper';
6
+ import { HeaderStream } from '../router/sse-stream';
3
7
  import { ParamProperties } from './context-utils';
4
8
  export declare const HANDLER_METADATA_SYMBOL: unique symbol;
9
+ export declare type HandleResponseFn = HandlerResponseBasicFn | HandleSseResponseFn;
10
+ export declare type HandlerResponseBasicFn = <TResult, TResponse>(result: TResult, res: TResponse, req?: any) => any;
11
+ export declare type HandleSseResponseFn = <TResult extends Observable<unknown> = any, TResponse extends HeaderStream = any, TRequest extends IncomingMessage = any>(result: TResult, res: TResponse, req: TRequest) => any;
5
12
  export interface HandlerMetadata {
6
13
  argsLength: number;
7
14
  paramtypes: any[];
@@ -11,7 +18,7 @@ export interface HandlerMetadata {
11
18
  getParamsMetadata: (moduleKey: string, contextId?: ContextId, inquirerId?: string) => (ParamProperties & {
12
19
  metatype?: any;
13
20
  })[];
14
- fnHandleResponse: <TResult, TResponse>(result: TResult, res: TResponse) => any;
21
+ fnHandleResponse: HandleResponseFn;
15
22
  }
16
23
  export declare class HandlerMetadataStorage<TValue = HandlerMetadata, TKey extends Type<unknown> = any> {
17
24
  private readonly [HANDLER_METADATA_SYMBOL];
@@ -0,0 +1 @@
1
+ export declare const rethrow: (err: unknown) => never;
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.rethrow = void 0;
4
+ exports.rethrow = (err) => {
5
+ throw err;
6
+ };
package/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NestFactory = exports.APP_PIPE = exports.APP_INTERCEPTOR = exports.APP_GUARD = exports.APP_FILTER = void 0;
3
4
  const tslib_1 = require("tslib");
4
5
  /*
5
6
  * Nest @core
@@ -20,8 +20,8 @@ export declare class NestContainer {
20
20
  setHttpAdapter(httpAdapter: any): void;
21
21
  getHttpAdapterRef(): import("..").AbstractHttpAdapter<any, any, any>;
22
22
  addModule(metatype: Type<any> | DynamicModule | Promise<DynamicModule>, scope: Type<any>[]): Promise<Module>;
23
- addDynamicMetadata(token: string, dynamicModuleMetadata: Partial<DynamicModule>, scope: Type<any>[]): void;
24
- addDynamicModules(modules: any[], scope: Type<any>[]): void;
23
+ addDynamicMetadata(token: string, dynamicModuleMetadata: Partial<DynamicModule>, scope: Type<any>[]): Promise<void>;
24
+ addDynamicModules(modules: any[], scope: Type<any>[]): Promise<void>;
25
25
  isGlobalModule(metatype: Type<any>, dynamicMetadata?: Partial<DynamicModule>): boolean;
26
26
  addGlobalModule(module: Module): void;
27
27
  getModules(): ModulesContainer;
@@ -50,25 +50,25 @@ class NestContainer {
50
50
  }
51
51
  const moduleRef = new module_1.Module(type, this);
52
52
  this.modules.set(token, moduleRef);
53
- this.addDynamicMetadata(token, dynamicMetadata, [].concat(scope, type));
53
+ await this.addDynamicMetadata(token, dynamicMetadata, [].concat(scope, type));
54
54
  if (this.isGlobalModule(type, dynamicMetadata)) {
55
55
  this.addGlobalModule(moduleRef);
56
56
  }
57
57
  return moduleRef;
58
58
  }
59
- addDynamicMetadata(token, dynamicModuleMetadata, scope) {
59
+ async addDynamicMetadata(token, dynamicModuleMetadata, scope) {
60
60
  if (!dynamicModuleMetadata) {
61
61
  return;
62
62
  }
63
63
  this.dynamicModulesMetadata.set(token, dynamicModuleMetadata);
64
64
  const { imports } = dynamicModuleMetadata;
65
- this.addDynamicModules(imports, scope);
65
+ await this.addDynamicModules(imports, scope);
66
66
  }
67
- addDynamicModules(modules, scope) {
67
+ async addDynamicModules(modules, scope) {
68
68
  if (!modules) {
69
69
  return;
70
70
  }
71
- modules.forEach(module => this.addModule(module, scope));
71
+ await Promise.all(modules.map(module => this.addModule(module, scope)));
72
72
  }
73
73
  isGlobalModule(metatype, dynamicMetadata) {
74
74
  if (dynamicMetadata && dynamicMetadata.global) {
@@ -39,7 +39,11 @@ class ModuleTokenFactory {
39
39
  }
40
40
  replacer(key, value) {
41
41
  if (typeof value === 'function') {
42
- return value.name;
42
+ const isClass = /^class\s/.test(Function.prototype.toString.call(value));
43
+ if (isClass) {
44
+ return value.name;
45
+ }
46
+ return hash(value.toString(), { ignoreUnknown: true });
43
47
  }
44
48
  return value;
45
49
  }
@@ -53,36 +53,36 @@ class MiddlewareModule {
53
53
  }
54
54
  async registerMiddleware(middlewareContainer, applicationRef) {
55
55
  const configs = middlewareContainer.getConfigurations();
56
- const registerAllConfigs = (moduleKey, middlewareConfig) => middlewareConfig.map(async (config) => {
57
- await this.registerMiddlewareConfig(middlewareContainer, config, moduleKey, applicationRef);
58
- });
56
+ const registerAllConfigs = async (moduleKey, middlewareConfig) => {
57
+ for (const config of middlewareConfig) {
58
+ await this.registerMiddlewareConfig(middlewareContainer, config, moduleKey, applicationRef);
59
+ }
60
+ };
59
61
  const entriesSortedByDistance = [...configs.entries()].sort(([moduleA], [moduleB]) => {
60
62
  return (this.container.getModuleByKey(moduleA).distance -
61
63
  this.container.getModuleByKey(moduleB).distance);
62
64
  });
63
- const registerModuleConfigs = async ([module, moduleConfigs]) => {
64
- await Promise.all(registerAllConfigs(module, [...moduleConfigs]));
65
- };
66
- await Promise.all(entriesSortedByDistance.map(registerModuleConfigs));
65
+ for (const [moduleRef, moduleConfigurations] of entriesSortedByDistance) {
66
+ await registerAllConfigs(moduleRef, [...moduleConfigurations]);
67
+ }
67
68
  }
68
69
  async registerMiddlewareConfig(middlewareContainer, config, moduleKey, applicationRef) {
69
70
  const { forRoutes } = config;
70
- const registerRouteMiddleware = async (routeInfo) => {
71
+ for (const routeInfo of forRoutes) {
71
72
  await this.registerRouteMiddleware(middlewareContainer, routeInfo, config, moduleKey, applicationRef);
72
- };
73
- await Promise.all(forRoutes.map(registerRouteMiddleware));
73
+ }
74
74
  }
75
75
  async registerRouteMiddleware(middlewareContainer, routeInfo, config, moduleKey, applicationRef) {
76
76
  const middlewareCollection = [].concat(config.middleware);
77
77
  const moduleRef = this.container.getModuleByKey(moduleKey);
78
- await Promise.all(middlewareCollection.map(async (metatype) => {
78
+ for (const metatype of middlewareCollection) {
79
79
  const collection = middlewareContainer.getMiddlewareCollection(moduleKey);
80
80
  const instanceWrapper = collection.get(metatype.name);
81
81
  if (shared_utils_1.isUndefined(instanceWrapper)) {
82
82
  throw new runtime_exception_1.RuntimeException();
83
83
  }
84
84
  await this.bindHandler(instanceWrapper, applicationRef, routeInfo.method, routeInfo.path, moduleRef, collection);
85
- }));
85
+ }
86
86
  }
87
87
  async bindHandler(wrapper, applicationRef, method, path, moduleRef, collection) {
88
88
  const { instance, metatype } = wrapper;
@@ -1,4 +1,4 @@
1
- import { INestApplicationContext, LoggerService, ShutdownSignal } from '@nestjs/common';
1
+ import { INestApplicationContext, LoggerService, LogLevel, ShutdownSignal } from '@nestjs/common';
2
2
  import { Abstract } from '@nestjs/common/interfaces';
3
3
  import { Type } from '@nestjs/common/interfaces/type.interface';
4
4
  import { ContextId } from './injector';
@@ -36,7 +36,7 @@ export declare class NestApplicationContext implements INestApplicationContext {
36
36
  */
37
37
  init(): Promise<this>;
38
38
  close(): Promise<void>;
39
- useLogger(logger: LoggerService): void;
39
+ useLogger(logger: LoggerService | LogLevel[] | false): void;
40
40
  /**
41
41
  * Enables the usage of shutdown hooks. Will call the
42
42
  * `onApplicationShutdown` function of a provider if the
package/nest-factory.d.ts CHANGED
@@ -8,6 +8,7 @@ import { AbstractHttpAdapter } from './adapters/http-adapter';
8
8
  */
9
9
  export declare class NestFactoryStatic {
10
10
  private readonly logger;
11
+ private abortOnError;
11
12
  /**
12
13
  * Creates an instance of NestApplication.
13
14
  *
@@ -52,12 +53,14 @@ export declare class NestFactoryStatic {
52
53
  createApplicationContext(module: any, options?: NestApplicationContextOptions): Promise<INestApplicationContext>;
53
54
  private createNestInstance;
54
55
  private initialize;
56
+ private handleInitializationError;
55
57
  private createProxy;
56
58
  private createExceptionProxy;
57
59
  private createExceptionZone;
58
60
  private applyLogger;
59
61
  private createHttpAdapter;
60
62
  private isHttpServer;
63
+ private setAbortOnError;
61
64
  private createAdapterProxy;
62
65
  }
63
66
  /**
package/nest-factory.js CHANGED
@@ -8,6 +8,7 @@ const application_config_1 = require("./application-config");
8
8
  const constants_1 = require("./constants");
9
9
  const exceptions_zone_1 = require("./errors/exceptions-zone");
10
10
  const load_adapter_1 = require("./helpers/load-adapter");
11
+ const rethrow_1 = require("./helpers/rethrow");
11
12
  const container_1 = require("./injector/container");
12
13
  const instance_loader_1 = require("./injector/instance-loader");
13
14
  const metadata_scanner_1 = require("./metadata-scanner");
@@ -20,6 +21,7 @@ const scanner_1 = require("./scanner");
20
21
  class NestFactoryStatic {
21
22
  constructor() {
22
23
  this.logger = new logger_service_1.Logger('NestFactory', true);
24
+ this.abortOnError = true;
23
25
  }
24
26
  async create(module, serverOrOptions, options) {
25
27
  const [httpServer, appOptions] = this.isHttpServer(serverOrOptions)
@@ -27,6 +29,7 @@ class NestFactoryStatic {
27
29
  : [this.createHttpAdapter(), serverOrOptions];
28
30
  const applicationConfig = new application_config_1.ApplicationConfig();
29
31
  const container = new container_1.NestContainer(applicationConfig);
32
+ this.setAbortOnError(serverOrOptions, options);
30
33
  this.applyLogger(appOptions);
31
34
  await this.initialize(module, container, applicationConfig, httpServer);
32
35
  const instance = new nest_application_1.NestApplication(container, httpServer, applicationConfig, appOptions);
@@ -46,6 +49,7 @@ class NestFactoryStatic {
46
49
  const { NestMicroservice } = load_package_util_1.loadPackage('@nestjs/microservices', 'NestFactory', () => require('@nestjs/microservices'));
47
50
  const applicationConfig = new application_config_1.ApplicationConfig();
48
51
  const container = new container_1.NestContainer(applicationConfig);
52
+ this.setAbortOnError(options);
49
53
  this.applyLogger(options);
50
54
  await this.initialize(module, container, applicationConfig);
51
55
  return this.createNestInstance(new NestMicroservice(container, options, applicationConfig));
@@ -61,6 +65,7 @@ class NestFactoryStatic {
61
65
  */
62
66
  async createApplicationContext(module, options) {
63
67
  const container = new container_1.NestContainer();
68
+ this.setAbortOnError(options);
64
69
  this.applyLogger(options);
65
70
  await this.initialize(module, container);
66
71
  const modules = container.getModules().values();
@@ -76,6 +81,7 @@ class NestFactoryStatic {
76
81
  const metadataScanner = new metadata_scanner_1.MetadataScanner();
77
82
  const dependenciesScanner = new scanner_1.DependenciesScanner(container, metadataScanner, config);
78
83
  container.setHttpAdapter(httpServer);
84
+ const teardown = this.abortOnError === false ? rethrow_1.rethrow : undefined;
79
85
  await (httpServer === null || httpServer === void 0 ? void 0 : httpServer.init());
80
86
  try {
81
87
  this.logger.log(constants_1.MESSAGES.APPLICATION_START);
@@ -83,11 +89,17 @@ class NestFactoryStatic {
83
89
  await dependenciesScanner.scan(module);
84
90
  await instanceLoader.createInstancesOfDependencies();
85
91
  dependenciesScanner.applyApplicationProviders();
86
- });
92
+ }, teardown);
87
93
  }
88
94
  catch (e) {
95
+ this.handleInitializationError(e);
96
+ }
97
+ }
98
+ handleInitializationError(err) {
99
+ if (this.abortOnError) {
89
100
  process.abort();
90
101
  }
102
+ rethrow_1.rethrow(err);
91
103
  }
92
104
  createProxy(target) {
93
105
  const proxy = this.createExceptionProxy();
@@ -108,11 +120,12 @@ class NestFactoryStatic {
108
120
  };
109
121
  }
110
122
  createExceptionZone(receiver, prop) {
123
+ const teardown = this.abortOnError === false ? rethrow_1.rethrow : undefined;
111
124
  return (...args) => {
112
125
  let result;
113
126
  exceptions_zone_1.ExceptionsZone.run(() => {
114
127
  result = receiver[prop](...args);
115
- });
128
+ }, teardown);
116
129
  return result;
117
130
  };
118
131
  }
@@ -129,6 +142,11 @@ class NestFactoryStatic {
129
142
  isHttpServer(serverOrOptions) {
130
143
  return !!(serverOrOptions && serverOrOptions.patch);
131
144
  }
145
+ setAbortOnError(serverOrOptions, options) {
146
+ this.abortOnError = this.isHttpServer(serverOrOptions)
147
+ ? !(options && options.abortOnError === false)
148
+ : !(serverOrOptions && serverOrOptions.abortOnError === false);
149
+ }
132
150
  createAdapterProxy(app, adapter) {
133
151
  return new Proxy(app, {
134
152
  get: (receiver, prop) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nestjs/core",
3
- "version": "7.4.2",
3
+ "version": "7.5.1",
4
4
  "description": "Nest - modern, fast, powerful node.js web framework (@core)",
5
5
  "author": "Kamil Mysliwiec",
6
6
  "license": "MIT",
@@ -32,11 +32,11 @@
32
32
  "iterare": "1.2.1",
33
33
  "object-hash": "2.0.3",
34
34
  "path-to-regexp": "3.2.0",
35
- "tslib": "2.0.0",
36
- "uuid": "8.3.0"
35
+ "tslib": "2.0.3",
36
+ "uuid": "8.3.1"
37
37
  },
38
38
  "devDependencies": {
39
- "@nestjs/common": "7.4.2"
39
+ "@nestjs/common": "7.5.1"
40
40
  },
41
41
  "peerDependencies": {
42
42
  "@nestjs/common": "^7.0.0",
@@ -1,4 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.REQUEST = void 0;
3
4
  var request_constants_1 = require("./request-constants");
4
5
  Object.defineProperty(exports, "REQUEST", { enumerable: true, get: function () { return request_constants_1.REQUEST; } });
@@ -5,7 +5,7 @@ import { ContextType, Controller } from '@nestjs/common/interfaces';
5
5
  import { GuardsConsumer } from '../guards/guards-consumer';
6
6
  import { GuardsContextCreator } from '../guards/guards-context-creator';
7
7
  import { ExecutionContextHost } from '../helpers/execution-context-host';
8
- import { HandlerMetadata } from '../helpers/handler-metadata-storage';
8
+ import { HandleResponseFn, HandlerMetadata } from '../helpers/handler-metadata-storage';
9
9
  import { InterceptorsConsumer } from '../interceptors/interceptors-consumer';
10
10
  import { InterceptorsContextCreator } from '../interceptors/interceptors-context-creator';
11
11
  import { PipesConsumer } from '../pipes/pipes-consumer';
@@ -38,6 +38,7 @@ export declare class RouterExecutionContext {
38
38
  reflectHttpStatusCode(callback: (...args: unknown[]) => unknown): number;
39
39
  reflectRenderTemplate(callback: (...args: unknown[]) => unknown): string;
40
40
  reflectResponseHeaders(callback: (...args: unknown[]) => unknown): CustomHeader[];
41
+ reflectSse(callback: (...args: unknown[]) => unknown): string;
41
42
  exchangeKeysForValues(keys: string[], metadata: Record<number, RouteParamMetadata>, moduleContext: string, contextId?: import("..").ContextId, inquirerId?: string, contextFactory?: (args: unknown[]) => ExecutionContextHost): ParamProperties[];
42
43
  getParamValue<T>(value: T, { metatype, type, data, }: {
43
44
  metatype: unknown;
@@ -49,5 +50,5 @@ export declare class RouterExecutionContext {
49
50
  createPipesFn(pipes: PipeTransform[], paramsOptions: (ParamProperties & {
50
51
  metatype?: any;
51
52
  })[]): <TRequest, TResponse>(args: any[], req: TRequest, res: TResponse, next: Function) => Promise<void>;
52
- createHandleResponseFn(callback: (...args: unknown[]) => unknown, isResponseHandled: boolean, redirectResponse?: RedirectResponse, httpStatusCode?: number): <TResult, TResponse>(result: TResult, res: TResponse) => Promise<void>;
53
+ createHandleResponseFn(callback: (...args: unknown[]) => unknown, isResponseHandled: boolean, redirectResponse?: RedirectResponse, httpStatusCode?: number): HandleResponseFn;
53
54
  }
@@ -44,7 +44,7 @@ class RouterExecutionContext {
44
44
  hasCustomHeaders &&
45
45
  this.responseController.setHeaders(res, responseHeaders);
46
46
  const result = await this.interceptorsConsumer.intercept(interceptors, [req, res, next], instance, callback, handler(args, req, res, next), contextType);
47
- await fnHandleResponse(result, res);
47
+ await fnHandleResponse(result, res, req);
48
48
  };
49
49
  }
50
50
  getMetadata(instance, callback, methodName, moduleKey, requestMethod, contextType) {
@@ -92,6 +92,9 @@ class RouterExecutionContext {
92
92
  reflectResponseHeaders(callback) {
93
93
  return Reflect.getMetadata(constants_1.HEADERS_METADATA, callback) || [];
94
94
  }
95
+ reflectSse(callback) {
96
+ return Reflect.getMetadata(constants_1.SSE_METADATA, callback);
97
+ }
95
98
  exchangeKeysForValues(keys, metadata, moduleContext, contextId = constants_3.STATIC_CONTEXT, inquirerId, contextFactory) {
96
99
  this.pipesContextCreator.setModuleContext(moduleContext);
97
100
  return keys.map(key => {
@@ -158,6 +161,12 @@ class RouterExecutionContext {
158
161
  await this.responseController.redirect(result, res, redirectResponse);
159
162
  };
160
163
  }
164
+ const isSseHandler = !!this.reflectSse(callback);
165
+ if (isSseHandler) {
166
+ return async (result, res, req) => {
167
+ await this.responseController.sse(result, res, req);
168
+ };
169
+ }
161
170
  return async (result, res) => {
162
171
  result = await this.responseController.transformToResult(result);
163
172
  !isResponseHandled &&
@@ -1,4 +1,8 @@
1
+ /// <reference types="node" />
1
2
  import { HttpServer, RequestMethod } from '@nestjs/common';
3
+ import { IncomingMessage } from 'http';
4
+ import { Observable } from 'rxjs';
5
+ import { HeaderStream } from './sse-stream';
2
6
  export interface CustomHeader {
3
7
  name: string;
4
8
  value: string;
@@ -17,4 +21,6 @@ export declare class RouterResponseController {
17
21
  getStatusByMethod(requestMethod: RequestMethod): number;
18
22
  setHeaders<TResponse = unknown>(response: TResponse, headers: CustomHeader[]): void;
19
23
  setStatus<TResponse = unknown>(response: TResponse, statusCode: number): void;
24
+ sse<TInput extends Observable<unknown> = any, TResponse extends HeaderStream = any, TRequest extends IncomingMessage = any>(result: TInput, response: TResponse, request: TRequest): Promise<void>;
25
+ private assertObservable;
20
26
  }
@@ -3,6 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.RouterResponseController = void 0;
4
4
  const common_1 = require("@nestjs/common");
5
5
  const shared_utils_1 = require("@nestjs/common/utils/shared.utils");
6
+ const operators_1 = require("rxjs/operators");
7
+ const sse_stream_1 = require("./sse-stream");
6
8
  class RouterResponseController {
7
9
  constructor(applicationRef) {
8
10
  this.applicationRef = applicationRef;
@@ -44,5 +46,27 @@ class RouterResponseController {
44
46
  setStatus(response, statusCode) {
45
47
  this.applicationRef.status(response, statusCode);
46
48
  }
49
+ async sse(result, response, request) {
50
+ this.assertObservable(result);
51
+ const stream = new sse_stream_1.SseStream(request);
52
+ stream.pipe(response);
53
+ const subscription = result
54
+ .pipe(operators_1.debounce((message) => new Promise(resolve => {
55
+ if (!shared_utils_1.isObject(message)) {
56
+ message = { data: message };
57
+ }
58
+ stream.writeMessage(message, resolve);
59
+ })))
60
+ .subscribe();
61
+ request.on('close', () => {
62
+ response.end();
63
+ subscription.unsubscribe();
64
+ });
65
+ }
66
+ assertObservable(result) {
67
+ if (!shared_utils_1.isFunction(result.subscribe)) {
68
+ throw new ReferenceError('You must return an Observable stream to use Server-Sent Events (SSE).');
69
+ }
70
+ }
47
71
  }
48
72
  exports.RouterResponseController = RouterResponseController;
@@ -0,0 +1,37 @@
1
+ /// <reference types="node" />
2
+ /// <reference types="vinyl-fs" />
3
+ import { MessageEvent } from '@nestjs/common/interfaces';
4
+ import { IncomingMessage, OutgoingHttpHeaders } from 'http';
5
+ import { Transform } from 'stream';
6
+ interface WriteHeaders {
7
+ writeHead?(statusCode: number, reasonPhrase?: string, headers?: OutgoingHttpHeaders): void;
8
+ writeHead?(statusCode: number, headers?: OutgoingHttpHeaders): void;
9
+ flushHeaders?(): void;
10
+ }
11
+ export declare type HeaderStream = NodeJS.WritableStream & WriteHeaders;
12
+ /**
13
+ * Adapted from https://raw.githubusercontent.com/EventSource/node-ssestream
14
+ * Transforms "messages" to W3C event stream content.
15
+ * See https://html.spec.whatwg.org/multipage/server-sent-events.html
16
+ * A message is an object with one or more of the following properties:
17
+ * - data (String or object, which gets turned into JSON)
18
+ * - type
19
+ * - id
20
+ * - retry
21
+ *
22
+ * If constructed with a HTTP Request, it will optimise the socket for streaming.
23
+ * If this stream is piped to an HTTP Response, it will set appropriate headers.
24
+ */
25
+ export declare class SseStream extends Transform {
26
+ private lastEventId;
27
+ constructor(req?: IncomingMessage);
28
+ pipe<T extends HeaderStream>(destination: T, options?: {
29
+ end?: boolean;
30
+ }): T;
31
+ _transform(message: MessageEvent, encoding: string, callback: (error?: Error | null, data?: any) => void): void;
32
+ /**
33
+ * Calls `.write` but handles the drain if needed
34
+ */
35
+ writeMessage(message: MessageEvent, cb: (error: Error | null | undefined) => void): void;
36
+ }
37
+ export {};
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SseStream = void 0;
4
+ const stream_1 = require("stream");
5
+ function toDataString(data) {
6
+ if (typeof data === 'object') {
7
+ return toDataString(JSON.stringify(data));
8
+ }
9
+ return data
10
+ .split(/\r\n|\r|\n/)
11
+ .map(line => `data: ${line}\n`)
12
+ .join('');
13
+ }
14
+ /**
15
+ * Adapted from https://raw.githubusercontent.com/EventSource/node-ssestream
16
+ * Transforms "messages" to W3C event stream content.
17
+ * See https://html.spec.whatwg.org/multipage/server-sent-events.html
18
+ * A message is an object with one or more of the following properties:
19
+ * - data (String or object, which gets turned into JSON)
20
+ * - type
21
+ * - id
22
+ * - retry
23
+ *
24
+ * If constructed with a HTTP Request, it will optimise the socket for streaming.
25
+ * If this stream is piped to an HTTP Response, it will set appropriate headers.
26
+ */
27
+ class SseStream extends stream_1.Transform {
28
+ constructor(req) {
29
+ super({ objectMode: true });
30
+ this.lastEventId = null;
31
+ if (req && req.socket) {
32
+ req.socket.setKeepAlive(true);
33
+ req.socket.setNoDelay(true);
34
+ req.socket.setTimeout(0);
35
+ }
36
+ }
37
+ pipe(destination, options) {
38
+ if (destination.writeHead) {
39
+ destination.writeHead(200, {
40
+ // See https://github.com/dunglas/mercure/blob/master/hub/subscribe.go#L124-L130
41
+ 'Content-Type': 'text/event-stream',
42
+ Connection: 'keep-alive',
43
+ // Disable cache, even for old browsers and proxies
44
+ 'Cache-Control': 'private, no-cache, no-store, must-revalidate, max-age=0',
45
+ 'Transfer-Encoding': 'identity',
46
+ Pragma: 'no-cache',
47
+ Expire: '0',
48
+ // NGINX support https://www.nginx.com/resources/wiki/start/topics/examples/x-accel/#x-accel-buffering
49
+ 'X-Accel-Buffering': 'no',
50
+ });
51
+ destination.flushHeaders();
52
+ }
53
+ destination.write(':\n');
54
+ return super.pipe(destination, options);
55
+ }
56
+ _transform(message, encoding, callback) {
57
+ let data = message.type ? `event: ${message.type}\n` : '';
58
+ data += message.id ? `id: ${message.id}\n` : '';
59
+ data += message.retry ? `retry: ${message.retry}\n` : '';
60
+ data += message.data ? toDataString(message.data) : '';
61
+ data += '\n';
62
+ this.push(data);
63
+ callback();
64
+ }
65
+ /**
66
+ * Calls `.write` but handles the drain if needed
67
+ */
68
+ writeMessage(message, cb) {
69
+ if (!message.id) {
70
+ this.lastEventId++;
71
+ message.id = this.lastEventId.toString();
72
+ }
73
+ if (!this.write(message, 'utf-8', cb)) {
74
+ this.once('drain', cb);
75
+ }
76
+ else {
77
+ process.nextTick(cb);
78
+ }
79
+ }
80
+ }
81
+ exports.SseStream = SseStream;