@tsed/sse 8.11.0 → 8.11.2-rc.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.
@@ -5,4 +5,3 @@ import { EventStreamMiddleware } from "../middlewares/EventStreamMiddleware.js";
5
5
  export function EventStream(opts = {}) {
6
6
  return useDecorators(StoreSet(EventStreamMiddleware, opts), Use(EventStreamMiddleware), AcceptMime("text/event-stream"), Header("Content-Type", "text/event-stream"));
7
7
  }
8
- //# sourceMappingURL=eventStream.js.map
@@ -2,4 +2,3 @@ import { Context } from "@tsed/platform-params";
2
2
  export function EventStreamCtx() {
3
3
  return Context("EventStreamContext");
4
4
  }
5
- //# sourceMappingURL=eventStreamCtx.js.map
@@ -95,4 +95,3 @@ export class EventStreamContext {
95
95
  this.$ctx.getRes().end();
96
96
  }
97
97
  }
98
- //# sourceMappingURL=EventStreamContext.js.map
package/lib/esm/index.js CHANGED
@@ -6,4 +6,3 @@ export * from "./decorators/eventStreamCtx.js";
6
6
  export * from "./domain/EventStreamContext.js";
7
7
  export * from "./interceptors/EventStreamInterceptor.js";
8
8
  export * from "./middlewares/EventStreamMiddleware.js";
9
- //# sourceMappingURL=index.js.map
@@ -26,4 +26,3 @@ EventStreamInterceptor = __decorate([
26
26
  Injectable()
27
27
  ], EventStreamInterceptor);
28
28
  export { EventStreamInterceptor };
29
- //# sourceMappingURL=EventStreamInterceptor.js.map
@@ -28,4 +28,3 @@ EventStreamMiddleware = EventStreamMiddleware_1 = __decorate([
28
28
  Middleware()
29
29
  ], EventStreamMiddleware);
30
30
  export { EventStreamMiddleware };
31
- //# sourceMappingURL=EventStreamMiddleware.js.map
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@tsed/sse",
3
3
  "description": "Server-sent events package for Ts.ED framework",
4
4
  "type": "module",
5
- "version": "8.11.0",
5
+ "version": "8.11.2-rc.1",
6
6
  "keywords": [
7
7
  "tsed",
8
8
  "server-sent events",
@@ -34,16 +34,16 @@
34
34
  "tslib": "2.7.0"
35
35
  },
36
36
  "devDependencies": {
37
- "@tsed/barrels": "8.11.0",
38
- "@tsed/core": "8.11.0",
39
- "@tsed/di": "8.11.0",
40
- "@tsed/json-mapper": "8.11.0",
41
- "@tsed/openspec": "8.11.0",
42
- "@tsed/platform-http": "8.11.0",
43
- "@tsed/platform-middlewares": "8.11.0",
44
- "@tsed/schema": "8.11.0",
45
- "@tsed/socketio-testing": "8.11.0",
46
- "@tsed/typescript": "8.11.0",
37
+ "@tsed/barrels": "8.11.2-rc.1",
38
+ "@tsed/core": "8.11.2-rc.1",
39
+ "@tsed/di": "8.11.2-rc.1",
40
+ "@tsed/json-mapper": "8.11.2-rc.1",
41
+ "@tsed/openspec": "8.11.2-rc.1",
42
+ "@tsed/platform-http": "8.11.2-rc.1",
43
+ "@tsed/platform-middlewares": "8.11.2-rc.1",
44
+ "@tsed/schema": "8.11.2-rc.1",
45
+ "@tsed/socketio-testing": "8.11.2-rc.1",
46
+ "@tsed/typescript": "8.11.2-rc.1",
47
47
  "eslint": "9.12.0",
48
48
  "express": "^4.21.0",
49
49
  "typescript": "5.4.5",
@@ -76,5 +76,8 @@
76
76
  },
77
77
  "homepage": "https://github.com/tsedio/tsed/tree/production/packages/third-parties/sse",
78
78
  "author": "Romain Lenzotti",
79
- "license": "MIT"
79
+ "license": "MIT",
80
+ "publishConfig": {
81
+ "tag": "rc"
82
+ }
80
83
  }
@@ -1 +0,0 @@
1
- {"version":3,"file":"eventStream.js","sourceRoot":"","sources":["../../../src/decorators/eventStream.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,QAAQ,EAAE,aAAa,EAAC,MAAM,YAAY,CAAC;AACnD,OAAO,EAAC,GAAG,EAAC,MAAM,4BAA4B,CAAC;AAC/C,OAAO,EAAC,UAAU,EAAE,MAAM,EAAC,MAAM,cAAc,CAAC;AAEhD,OAAO,EAAC,qBAAqB,EAAC,MAAM,yCAAyC,CAAC;AAO9E,MAAM,UAAU,WAAW,CAAC,OAAwB,EAAE;IACpD,OAAO,aAAa,CAClB,QAAQ,CAAC,qBAAqB,EAAE,IAAI,CAAC,EACrC,GAAG,CAAC,qBAAqB,CAAC,EAC1B,UAAU,CAAC,mBAAmB,CAAC,EAC/B,MAAM,CAAC,cAAc,EAAE,mBAAmB,CAAC,CAC5C,CAAC;AACJ,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"eventStreamCtx.js","sourceRoot":"","sources":["../../../src/decorators/eventStreamCtx.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,OAAO,EAAC,MAAM,uBAAuB,CAAC;AAM9C,MAAM,UAAU,cAAc;IAC5B,OAAO,OAAO,CAAC,oBAAoB,CAAC,CAAC;AACvC,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"EventStreamContext.js","sourceRoot":"","sources":["../../../src/domain/EventStreamContext.ts"],"names":[],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,cAAc,6BAA6B,CAAC;AAC5C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,0CAA0C,CAAC;AACzD,cAAc,wCAAwC,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"EventStreamInterceptor.js","sourceRoot":"","sources":["../../../src/interceptors/EventStreamInterceptor.ts"],"names":[],"mappings":";AAAA,OAAO,EAAC,YAAY,EAAE,QAAQ,EAAC,MAAM,YAAY,CAAC;AAElD,OAAO,EAAC,UAAU,EAAE,aAAa,EAAC,MAAM,UAAU,CAAC;AAO5C,IAAM,sBAAsB,GAA5B,MAAM,sBAAsB;IAIjC,KAAK,CAAC,SAAS,CAAC,OAA2B;QACzC,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC;QACpC,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAqB,oBAAoB,CAAC,CAAC;QAE5E,IAAI,WAAW,EAAE,CAAC;YAChB,IAAI,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC;gBACzB,WAAW,CAAC,SAAS,CAAC,MAA6B,CAAC,CAAC;gBAErD,OAAO;YACT,CAAC;YAED,IAAI,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;gBACrB,WAAW,CAAC,MAAM,CAAC,MAA+B,CAAC,CAAC;gBAEpD,OAAO;YACT,CAAC;QACH,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;CACF,CAAA;AAtBW;IADT,aAAa,EAAE;;oDACgB;AAFrB,sBAAsB;IADlC,UAAU,EAAE;GACA,sBAAsB,CAwBlC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"EventStreamMiddleware.js","sourceRoot":"","sources":["../../../src/middlewares/EventStreamMiddleware.ts"],"names":[],"mappings":";;AAAA,OAAO,EAAC,eAAe,EAAC,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAC,UAAU,EAAC,MAAM,4BAA4B,CAAC;AACtD,OAAO,EAAC,OAAO,EAAC,MAAM,uBAAuB,CAAC;AAE9C,OAAO,EAAC,kBAAkB,EAAC,MAAM,iCAAiC,CAAC;AAG5D,IAAM,qBAAqB,6BAA3B,MAAM,qBAAqB;IAChC,GAAG,CAAY,IAAqB;QAClC,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,uBAAqB,EAAE,EAAE,CAAC,CAAC;QAEhE,qBAAqB;QACrB,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QACpB,IAAI,CAAC,MAAM,CAAC,YAAY,GAAG,CAAC,CAAC;QAE7B,MAAM,WAAW,GAAG,IAAI,kBAAkB,CAAC;YACzC,IAAI;YACJ,GAAG,IAAI;SACR,CAAC,CAAC;QAEH,IAAI,CAAC,GAAG,CAAC,oBAAoB,EAAE,WAAW,CAAC,CAAC;QAE5C,WAAW,CAAC,SAAS,EAAE,CAAC;IAC1B,CAAC;CACF,CAAA;AAhBC;IAAK,WAAA,OAAO,EAAE,CAAA;;qCAAO,eAAe;;gDAenC;AAhBU,qBAAqB;IADjC,UAAU,EAAE;GACA,qBAAqB,CAiBjC"}
@@ -1,171 +0,0 @@
1
- // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
-
3
- exports[`SSE integration > should generate the swagger spec 1`] = `
4
- {
5
- "components": {
6
- "schemas": {
7
- "Model": {
8
- "properties": {
9
- "id": {
10
- "type": "string",
11
- },
12
- },
13
- "type": "object",
14
- },
15
- },
16
- },
17
- "paths": {
18
- "/sse/scenario1": {
19
- "get": {
20
- "operationId": "myCtrlScenario1",
21
- "parameters": [],
22
- "responses": {
23
- "200": {
24
- "content": {
25
- "*/*": {
26
- "schema": {
27
- "type": "object",
28
- },
29
- },
30
- },
31
- "headers": {
32
- "Content-Type": {
33
- "example": "text/event-stream",
34
- "schema": {
35
- "type": "string",
36
- },
37
- },
38
- },
39
- },
40
- },
41
- "tags": [
42
- "MyCtrl",
43
- ],
44
- },
45
- },
46
- "/sse/scenario2": {
47
- "get": {
48
- "operationId": "myCtrlScenario2",
49
- "parameters": [],
50
- "responses": {
51
- "200": {
52
- "content": {
53
- "*/*": {
54
- "schema": {
55
- "type": "object",
56
- },
57
- },
58
- },
59
- "headers": {
60
- "Content-Type": {
61
- "example": "text/event-stream",
62
- "schema": {
63
- "type": "string",
64
- },
65
- },
66
- },
67
- },
68
- },
69
- "tags": [
70
- "MyCtrl",
71
- ],
72
- },
73
- },
74
- "/sse/scenario3": {
75
- "get": {
76
- "operationId": "myCtrlScenario3",
77
- "parameters": [],
78
- "responses": {
79
- "200": {
80
- "content": {
81
- "*/*": {
82
- "schema": {
83
- "type": "object",
84
- },
85
- },
86
- },
87
- "headers": {
88
- "Content-Type": {
89
- "example": "text/event-stream",
90
- "schema": {
91
- "type": "string",
92
- },
93
- },
94
- },
95
- },
96
- },
97
- "tags": [
98
- "MyCtrl",
99
- ],
100
- },
101
- },
102
- "/sse/scenario4": {
103
- "get": {
104
- "operationId": "myCtrlScenario4",
105
- "parameters": [],
106
- "responses": {
107
- "200": {
108
- "content": {
109
- "*/*": {
110
- "schema": {
111
- "type": "object",
112
- },
113
- },
114
- },
115
- "headers": {
116
- "Content-Type": {
117
- "example": "text/event-stream",
118
- "schema": {
119
- "type": "string",
120
- },
121
- },
122
- },
123
- },
124
- },
125
- "tags": [
126
- "MyCtrl",
127
- ],
128
- },
129
- },
130
- "/sse/scenario5": {
131
- "get": {
132
- "operationId": "myCtrlScenario5",
133
- "parameters": [],
134
- "responses": {
135
- "200": {
136
- "content": {
137
- "*/*": {
138
- "schema": {
139
- "type": "object",
140
- },
141
- },
142
- "application/json": {
143
- "schema": {
144
- "$ref": "#/components/schemas/Model",
145
- },
146
- },
147
- },
148
- "description": "Success",
149
- "headers": {
150
- "Content-Type": {
151
- "example": "text/event-stream",
152
- "schema": {
153
- "type": "string",
154
- },
155
- },
156
- },
157
- },
158
- },
159
- "tags": [
160
- "MyCtrl",
161
- ],
162
- },
163
- },
164
- },
165
- "tags": [
166
- {
167
- "name": "MyCtrl",
168
- },
169
- ],
170
- }
171
- `;
@@ -1,19 +0,0 @@
1
- import {StoreSet, useDecorators} from "@tsed/core";
2
- import {Use} from "@tsed/platform-middlewares";
3
- import {AcceptMime, Header} from "@tsed/schema";
4
-
5
- import {EventStreamMiddleware} from "../middlewares/EventStreamMiddleware.js";
6
-
7
- export interface EventStreamOpts {
8
- headers?: Record<string, string>;
9
- event?: string;
10
- }
11
-
12
- export function EventStream(opts: EventStreamOpts = {}) {
13
- return useDecorators(
14
- StoreSet(EventStreamMiddleware, opts),
15
- Use(EventStreamMiddleware),
16
- AcceptMime("text/event-stream"),
17
- Header("Content-Type", "text/event-stream")
18
- );
19
- }
@@ -1,9 +0,0 @@
1
- import {Context} from "@tsed/platform-params";
2
-
3
- import type {EventStreamContext} from "../domain/EventStreamContext.js";
4
-
5
- export type EventStreamCtx = EventStreamContext;
6
-
7
- export function EventStreamCtx() {
8
- return Context("EventStreamContext");
9
- }
@@ -1,128 +0,0 @@
1
- import {isBoolean, isDate, isNumber, isString} from "@tsed/core";
2
- import {PlatformContext} from "@tsed/platform-http";
3
- import {PlatformResponseFilter} from "@tsed/platform-response-filter";
4
- import type {EventEmitter} from "events";
5
- import type {Observable} from "rxjs";
6
-
7
- import type {EventStreamOpts} from "../decorators/eventStream.js";
8
-
9
- export type EventStreamContextOptions = {
10
- $ctx: PlatformContext;
11
- } & EventStreamOpts;
12
-
13
- export class EventStreamContext {
14
- private $ctx: PlatformContext;
15
- private opts: EventStreamOpts;
16
- private responseFilter: PlatformResponseFilter;
17
- #isDone = false;
18
-
19
- constructor({$ctx, ...opts}: EventStreamContextOptions) {
20
- this.$ctx = $ctx;
21
-
22
- this.opts = opts;
23
- this.responseFilter = this.$ctx.injector.get<PlatformResponseFilter>(PlatformResponseFilter)!;
24
- }
25
-
26
- writeHead(headers: Record<string, string> = {}) {
27
- console.log("this.$ctx", this.$ctx);
28
- this.on("end", () => {
29
- this.$ctx.logger.info({
30
- event: "SSE_ENDED"
31
- });
32
- this.$ctx.getRes().end();
33
- this.#isDone = true;
34
- });
35
-
36
- this.on("close", () => {
37
- this.$ctx.logger.info({
38
- event: "SSE_CLOSED_BY_CLIENT"
39
- });
40
- this.$ctx.getRes().end();
41
- this.#isDone = true;
42
- });
43
-
44
- this.$ctx.getRes().writeHead(200, {
45
- ...this.opts.headers,
46
- ...headers,
47
- "Content-Type": "text/event-stream",
48
- "Cache-Control": "no-cache",
49
- Connection: "keep-alive"
50
- });
51
- }
52
-
53
- on(event: string, listener: (...args: any[]) => void): this {
54
- this.$ctx.getRes().on(event, listener);
55
- return this;
56
- }
57
-
58
- subscribe(observable: Observable<unknown>) {
59
- observable.subscribe((data) => {
60
- this.emit(this.opts.event, data);
61
- });
62
- return this;
63
- }
64
-
65
- stream(stream: NodeJS.ReadableStream | EventEmitter) {
66
- const listener = (data: unknown) => {
67
- if (this.isDone()) {
68
- stream.off("data", listener);
69
- } else {
70
- this.emit(this.opts.event, data);
71
- }
72
- };
73
-
74
- stream.on("data", listener);
75
- }
76
-
77
- emit(event: string | undefined, data: unknown) {
78
- if (this.isDone()) {
79
- return;
80
- }
81
- const {$ctx} = this;
82
- if (data === undefined) {
83
- return this;
84
- }
85
-
86
- if (Buffer.isBuffer(data)) {
87
- data = data.toString("utf8");
88
- }
89
-
90
- if (isBoolean(data) || isNumber(data) || isString(data) || isDate(data) || data === null) {
91
- this.write({
92
- event: event || "",
93
- data: JSON.stringify(data)
94
- });
95
- return this;
96
- }
97
-
98
- this.responseFilter.transform(data, $ctx as any).then((data: unknown) => {
99
- data = JSON.stringify(data);
100
-
101
- this.write({
102
- event: event || "",
103
- data: data as string
104
- });
105
- });
106
-
107
- return this;
108
- }
109
-
110
- write(obj: Record<string, string>) {
111
- const values = Object.entries(obj)
112
- .map(([key, value]) => {
113
- return `${key}: ${value}`;
114
- })
115
- .join("\n");
116
-
117
- this.$ctx.getRes().write(`${values}\n\n`);
118
- }
119
-
120
- isDone() {
121
- return this.#isDone;
122
- }
123
-
124
- end() {
125
- this.#isDone = true;
126
- this.$ctx.getRes().end();
127
- }
128
- }
package/src/index.ts DELETED
@@ -1,8 +0,0 @@
1
- /**
2
- * @file Automatically generated by @tsed/barrels.
3
- */
4
- export * from "./decorators/eventStream.js";
5
- export * from "./decorators/eventStreamCtx.js";
6
- export * from "./domain/EventStreamContext.js";
7
- export * from "./interceptors/EventStreamInterceptor.js";
8
- export * from "./middlewares/EventStreamMiddleware.js";
@@ -1,34 +0,0 @@
1
- import {isObservable, isStream} from "@tsed/core";
2
- import type {InterceptorContext, InterceptorMethods} from "@tsed/di";
3
- import {Injectable, InjectContext} from "@tsed/di";
4
- import type {PlatformContext} from "@tsed/platform-http";
5
- import type {Observable} from "rxjs";
6
-
7
- import {EventStreamContext} from "../domain/EventStreamContext.js";
8
-
9
- @Injectable()
10
- export class EventStreamInterceptor implements InterceptorMethods {
11
- @InjectContext()
12
- protected $ctx: PlatformContext;
13
-
14
- async intercept(context: InterceptorContext): Promise<unknown> {
15
- const result = await context.next();
16
- const eventStream = this.$ctx.get<EventStreamContext>("EventStreamContext");
17
-
18
- if (eventStream) {
19
- if (isObservable(result)) {
20
- eventStream.subscribe(result as Observable<unknown>);
21
-
22
- return;
23
- }
24
-
25
- if (isStream(result)) {
26
- eventStream.stream(result as NodeJS.ReadableStream);
27
-
28
- return;
29
- }
30
- }
31
-
32
- return result;
33
- }
34
- }
@@ -1,25 +0,0 @@
1
- import {PlatformContext} from "@tsed/platform-http";
2
- import {Middleware} from "@tsed/platform-middlewares";
3
- import {Context} from "@tsed/platform-params";
4
-
5
- import {EventStreamContext} from "../domain/EventStreamContext.js";
6
-
7
- @Middleware()
8
- export class EventStreamMiddleware {
9
- use(@Context() $ctx: PlatformContext) {
10
- const opts = $ctx.endpoint.store.get(EventStreamMiddleware, {});
11
-
12
- // Enable stream logs
13
- $ctx.logger.flush();
14
- $ctx.logger.maxStackSize = 0;
15
-
16
- const eventStream = new EventStreamContext({
17
- $ctx,
18
- ...opts
19
- });
20
-
21
- $ctx.set("EventStreamContext", eventStream);
22
-
23
- eventStream.writeHead();
24
- }
25
- }
@@ -1,207 +0,0 @@
1
- import {Controller} from "@tsed/di";
2
- import {PlatformContext} from "@tsed/platform-http";
3
- import {PlatformTest} from "@tsed/platform-http/testing";
4
- import {Context} from "@tsed/platform-params";
5
- import {Get, getSpec, JsonEntityStore, Name, Returns} from "@tsed/schema";
6
-
7
- import {EventStream} from "./decorators/eventStream.js";
8
- import {EventStreamCtx} from "./decorators/eventStreamCtx.js";
9
- import {EventStreamContext} from "./domain/EventStreamContext.js";
10
-
11
- function make(eventStream: EventStreamCtx, cb: () => any) {
12
- let intervalId: ReturnType<typeof setInterval>;
13
-
14
- eventStream.on("close", () => {
15
- clearInterval(intervalId);
16
- });
17
-
18
- eventStream.on("end", () => {
19
- clearInterval(intervalId);
20
- });
21
-
22
- intervalId = setInterval(() => {
23
- // Ts.ED support Model serialization using json-mapper here
24
- eventStream.emit("event", cb());
25
- }, 200);
26
- }
27
-
28
- class Model {
29
- @Name("id")
30
- _id: string;
31
- }
32
-
33
- @Controller("/sse")
34
- export class MyCtrl {
35
- @Get("/scenario1")
36
- @EventStream()
37
- scenario1(@EventStreamCtx() eventStream: EventStreamCtx) {
38
- make(eventStream, () => new Date());
39
- }
40
-
41
- @Get("/scenario2")
42
- @EventStream()
43
- scenario2(@EventStreamCtx() eventStream: EventStreamCtx) {
44
- make(eventStream, () => false);
45
- }
46
-
47
- @Get("/scenario3")
48
- @EventStream()
49
- scenario3(@EventStreamCtx() eventStream: EventStreamCtx) {
50
- make(eventStream, () => 0);
51
- }
52
-
53
- @Get("/scenario4")
54
- @EventStream()
55
- scenario4(@EventStreamCtx() eventStream: EventStreamCtx) {
56
- make(eventStream, () => ({
57
- date: new Date(),
58
- message: "Hello"
59
- }));
60
- }
61
-
62
- @Get("/scenario5")
63
- @EventStream()
64
- @(Returns(200).Type(Model))
65
- scenario5(@EventStreamCtx() eventStream: EventStreamCtx, @Context() $ctx: PlatformContext) {
66
- make(eventStream, () => {
67
- const model = new Model();
68
- model._id = new Date().toISOString();
69
-
70
- return model;
71
- });
72
- }
73
- }
74
-
75
- async function getControllerFixture(scenario: string) {
76
- const controller = await PlatformTest.invoke<MyCtrl>(MyCtrl);
77
-
78
- const endpoint = JsonEntityStore.fromMethod(MyCtrl, scenario);
79
- const $ctx = PlatformTest.createRequestContext({
80
- endpoint
81
- });
82
- const eventStream = new EventStreamContext({
83
- $ctx
84
- });
85
-
86
- return {controller, endpoint, eventStream, $ctx};
87
- }
88
-
89
- describe("SSE integration", () => {
90
- beforeEach(() => PlatformTest.create());
91
- afterEach(() => PlatformTest.reset());
92
- beforeEach(() => {
93
- vi.useFakeTimers({
94
- toFake: ["Date"],
95
- now: new Date("2025-01-01T00:00:00Z").getTime()
96
- });
97
- });
98
- afterEach(() => {
99
- vi.useRealTimers();
100
- });
101
- it("should generate the swagger spec", () => {
102
- expect(getSpec(MyCtrl)).toMatchSnapshot();
103
- });
104
-
105
- describe("Scenario1 - Date/String", () => {
106
- it("should pass the new EventStreamContext to function and process message", async () => {
107
- const {controller, eventStream} = await getControllerFixture("scenario1");
108
-
109
- controller.scenario1(eventStream);
110
-
111
- // Wait for the first message
112
- const result = await new Promise((resolve) => {
113
- eventStream.on("data", resolve);
114
- });
115
-
116
- expect(result).toEqual("event: event\ndata: " + JSON.stringify(new Date()) + "\n\n");
117
-
118
- // stop the interval
119
- eventStream.end();
120
- });
121
- });
122
-
123
- describe("Scenario2 - Boolean", () => {
124
- it("should pass the new EventStreamContext to function and process message", async () => {
125
- const {controller, eventStream} = await getControllerFixture("scenario2");
126
-
127
- controller.scenario2(eventStream);
128
-
129
- // Wait for the first message
130
- const result = await new Promise((resolve) => {
131
- eventStream.on("data", resolve);
132
- });
133
-
134
- expect(result).toEqual("event: event\ndata: false\n\n");
135
-
136
- // stop the interval
137
- eventStream.end();
138
- });
139
- });
140
-
141
- describe("Scenario3 - Number", () => {
142
- it("should pass the new EventStreamContext to function and process message", async () => {
143
- const {controller, eventStream} = await getControllerFixture("scenario3");
144
-
145
- controller.scenario3(eventStream);
146
-
147
- // Wait for the first message
148
- const result = await new Promise((resolve) => {
149
- eventStream.on("data", resolve);
150
- });
151
-
152
- expect(result).toEqual("event: event\ndata: 0\n\n");
153
-
154
- // stop the interval
155
- eventStream.end();
156
- });
157
- });
158
-
159
- describe("Scenario4 - Object", () => {
160
- it("should pass the new EventStreamContext to function and process message", async () => {
161
- const {controller, eventStream} = await getControllerFixture("scenario4");
162
-
163
- controller.scenario4(eventStream);
164
-
165
- // Wait for the first message
166
- const result = await new Promise((resolve) => {
167
- eventStream.on("data", resolve);
168
- });
169
-
170
- expect(result).toEqual(
171
- "event: event\ndata: " +
172
- JSON.stringify({
173
- date: "2025-01-01T00:00:00.000Z",
174
- message: "Hello"
175
- }) +
176
- "\n\n"
177
- );
178
-
179
- // stop the interval
180
- eventStream.end();
181
- });
182
- });
183
-
184
- describe("Scenario5 - Model", () => {
185
- it("should pass the new EventStreamContext to function and process message", async () => {
186
- const {controller, eventStream, $ctx} = await getControllerFixture("scenario5");
187
-
188
- controller.scenario5(eventStream, $ctx);
189
-
190
- // Wait for the first message
191
- const result = await new Promise((resolve) => {
192
- eventStream.on("data", resolve);
193
- });
194
-
195
- expect(result).toEqual(
196
- "event: event\ndata: " +
197
- JSON.stringify({
198
- id: "2025-01-01T00:00:00.000Z"
199
- }) +
200
- "\n\n"
201
- );
202
-
203
- // stop the interval
204
- eventStream.end();
205
- });
206
- });
207
- });
package/vitest.config.mts DELETED
@@ -1,21 +0,0 @@
1
- // @ts-ignore
2
- import {presets} from "@tsed/vitest/presets";
3
- import {defineConfig} from "vitest/config";
4
-
5
- export default defineConfig(
6
- {
7
- ...presets,
8
- test: {
9
- ...presets.test,
10
- coverage: {
11
- ...presets.test.coverage,
12
- thresholds: {
13
- statements: 0,
14
- branches: 0,
15
- functions: 0,
16
- lines: 0
17
- }
18
- }
19
- }
20
- }
21
- );