@tsed/socketio 8.0.1 → 8.0.3

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 (65) hide show
  1. package/package.json +16 -15
  2. package/src/SocketIOModule.spec.ts +87 -0
  3. package/src/SocketIOModule.ts +113 -0
  4. package/src/class/SocketHandlersBuilder.spec.ts +988 -0
  5. package/src/class/SocketHandlersBuilder.ts +266 -0
  6. package/src/class/SocketProviderMetadata.ts +57 -0
  7. package/src/class/SocketSessionData.spec.ts +190 -0
  8. package/src/class/SocketSessionData.ts +81 -0
  9. package/src/constants/constants.ts +1 -0
  10. package/src/decorators/args.spec.ts +57 -0
  11. package/src/decorators/args.ts +60 -0
  12. package/src/decorators/broadcast.spec.ts +23 -0
  13. package/src/decorators/broadcast.ts +28 -0
  14. package/src/decorators/broadcastOthers.spec.ts +23 -0
  15. package/src/decorators/broadcastOthers.ts +28 -0
  16. package/src/decorators/emit.spec.ts +23 -0
  17. package/src/decorators/emit.ts +28 -0
  18. package/src/decorators/input.spec.ts +22 -0
  19. package/src/decorators/input.ts +33 -0
  20. package/src/decorators/inputAndBroadcast.spec.ts +25 -0
  21. package/src/decorators/inputAndBroadcast.ts +28 -0
  22. package/src/decorators/inputAndBroadcastOthers.spec.ts +24 -0
  23. package/src/decorators/inputAndBroadcastOthers.ts +28 -0
  24. package/src/decorators/inputAndEmit.spec.ts +24 -0
  25. package/src/decorators/inputAndEmit.ts +28 -0
  26. package/src/decorators/io.ts +34 -0
  27. package/src/decorators/nsp.spec.ts +70 -0
  28. package/src/decorators/nsp.ts +101 -0
  29. package/src/decorators/reason.spec.ts +26 -0
  30. package/src/decorators/reason.ts +30 -0
  31. package/src/decorators/socket.spec.ts +26 -0
  32. package/src/decorators/socket.ts +32 -0
  33. package/src/decorators/socketErr.spec.ts +25 -0
  34. package/src/decorators/socketErr.ts +26 -0
  35. package/src/decorators/socketEventName.spec.ts +25 -0
  36. package/src/decorators/socketEventName.ts +26 -0
  37. package/src/decorators/socketFilter.ts +27 -0
  38. package/src/decorators/socketMiddleware.spec.ts +21 -0
  39. package/src/decorators/socketMiddleware.ts +72 -0
  40. package/src/decorators/socketMiddlewareError.spec.ts +22 -0
  41. package/src/decorators/socketMiddlewareError.ts +70 -0
  42. package/src/decorators/socketNsp.spec.ts +26 -0
  43. package/src/decorators/socketNsp.ts +32 -0
  44. package/src/decorators/socketReturns.ts +25 -0
  45. package/src/decorators/socketService.spec.ts +44 -0
  46. package/src/decorators/socketService.ts +24 -0
  47. package/src/decorators/socketSession.spec.ts +47 -0
  48. package/src/decorators/socketSession.ts +53 -0
  49. package/src/decorators/socketUseAfter.spec.ts +38 -0
  50. package/src/decorators/socketUseAfter.ts +69 -0
  51. package/src/decorators/socketUseBefore.spec.ts +34 -0
  52. package/src/decorators/socketUseBefore.ts +73 -0
  53. package/src/index.ts +41 -0
  54. package/src/interfaces/OnNamespaceInit.ts +8 -0
  55. package/src/interfaces/SocketFilters.ts +14 -0
  56. package/src/interfaces/SocketHandlerMetadata.ts +17 -0
  57. package/src/interfaces/SocketInjectableNsp.ts +4 -0
  58. package/src/interfaces/SocketParamMetadata.ts +9 -0
  59. package/src/interfaces/SocketProviderTypes.ts +7 -0
  60. package/src/interfaces/SocketReturnsTypes.ts +8 -0
  61. package/src/interfaces/interfaces.ts +9 -0
  62. package/src/services/SocketIOServer.spec.ts +33 -0
  63. package/src/services/SocketIOServer.ts +15 -0
  64. package/src/services/SocketIOService.spec.ts +54 -0
  65. package/src/services/SocketIOService.ts +69 -0
@@ -0,0 +1,38 @@
1
+ import {Store} from "@tsed/core";
2
+
3
+ import {SocketUseAfter} from "../index.js";
4
+
5
+ describe("@SocketUseAfter", () => {
6
+ describe("when the decorator is used on a class", () => {
7
+ beforeAll(() => {});
8
+
9
+ it("should store metadata", () => {
10
+ class Test {}
11
+
12
+ const middleware: any = () => {};
13
+ SocketUseAfter(middleware)(Test);
14
+
15
+ expect(Store.from(Test).get("socketIO")).toEqual({
16
+ useAfter: [middleware]
17
+ });
18
+ });
19
+ });
20
+
21
+ describe("when the decorator is used on a method", () => {
22
+ it("should store metadata", () => {
23
+ class Test {}
24
+
25
+ const middleware: any = () => {};
26
+
27
+ SocketUseAfter(middleware)(Test, "test", {});
28
+
29
+ expect(Store.from(Test).get("socketIO")).toEqual({
30
+ handlers: {
31
+ test: {
32
+ useAfter: [middleware]
33
+ }
34
+ }
35
+ });
36
+ });
37
+ });
38
+ });
@@ -0,0 +1,69 @@
1
+ import {Store, Type} from "@tsed/core";
2
+
3
+ /**
4
+ * Attach a Socket Middleware to a method or a class.
5
+ *
6
+ * ### Example
7
+ *
8
+ * A middleware can be also used on a `SocketService` either on a class or on a method.
9
+ *
10
+ * Here an example of a middleware:
11
+ *
12
+ * ```typescript
13
+ * import {SocketMiddlewareError, SocketErr, Socket, Args} from "@tsed/socketio";
14
+ *
15
+ * @SocketMiddlewareError()
16
+ * export class ErrorHandlerSocketMiddleware {
17
+ * async use(@SocketErr() err: any, @Socket socket: SocketIO.Socket) {
18
+ * console.error(err);
19
+ * socket.emit("error", {message: "An error has occured"})
20
+ * }
21
+ * }
22
+ * ```
23
+ *
24
+ * Then:
25
+ *
26
+ * ```typescript
27
+ * import {SocketService, SocketUseAfter, SocketUseBefore, Emit, Input, Args} from "@tsed/socketio";
28
+ * import {ErrorHandlerSocketMiddleware} from "../middlewares.js";
29
+ * import {User} from "../models/User.js";
30
+ *
31
+ * @SocketService("/my-namespace")
32
+ * @SocketUseAfter(ErrorHandlerSocketMiddleware) // global version
33
+ * export class MySocketService {
34
+ *
35
+ * @Input("eventName")
36
+ * @Emit("responseEventName") // or Broadcast or BroadcastOthers
37
+ * @SocketUseAfter(ErrorHandlerSocketMiddleware)
38
+ * async myMethod(@Args(0) userName: User) {
39
+ *
40
+ * console.log(user);
41
+ * throw new Error("Error");
42
+ *
43
+ * return user;
44
+ * }
45
+ * }
46
+ * ```
47
+ *
48
+ * @returns {(target: any, propertyKey?: string, descriptor?: PropertyDescriptor) => void}
49
+ * @decorator
50
+ * @experimental
51
+ * @param middlewares
52
+ */
53
+ export function SocketUseAfter(...middlewares: Type<any>[]) {
54
+ return (target: any, propertyKey?: string, descriptor?: PropertyDescriptor) => {
55
+ if (propertyKey) {
56
+ Store.from(target).merge("socketIO", {
57
+ handlers: {
58
+ [propertyKey]: {
59
+ useAfter: middlewares
60
+ }
61
+ }
62
+ });
63
+ } else {
64
+ Store.from(target).merge("socketIO", {
65
+ useAfter: middlewares
66
+ });
67
+ }
68
+ };
69
+ }
@@ -0,0 +1,34 @@
1
+ import {Store} from "@tsed/core";
2
+
3
+ import {SocketUseBefore} from "../index.js";
4
+
5
+ describe("@SocketUseBefore", () => {
6
+ describe("when the decorator is used on a class", () => {
7
+ it("should store metadata", () => {
8
+ class Test {}
9
+ const middleware: any = () => {};
10
+ SocketUseBefore(middleware)(Test);
11
+
12
+ expect(Store.from(Test).get("socketIO")).toEqual({
13
+ useBefore: [middleware]
14
+ });
15
+ });
16
+ });
17
+
18
+ describe("when the decorator is used on a method", () => {
19
+ it("should store metadata", () => {
20
+ class Test {}
21
+ const middleware: any = () => {};
22
+
23
+ SocketUseBefore(middleware)(Test, "test", {});
24
+
25
+ expect(Store.from(Test).get("socketIO")).toEqual({
26
+ handlers: {
27
+ test: {
28
+ useBefore: [middleware]
29
+ }
30
+ }
31
+ });
32
+ });
33
+ });
34
+ });
@@ -0,0 +1,73 @@
1
+ import {Store, Type} from "@tsed/core";
2
+
3
+ /**
4
+ * Attach a Socket Middleware to a method or a class.
5
+ *
6
+ * ### Example
7
+ *
8
+ * A middleware can be also used on a `SocketService` either on a class or on a method.
9
+ *
10
+ * Here an example of a middleware:
11
+ *
12
+ * ```typescript
13
+ * @SocketMiddleware()
14
+ * export class UserConverterSocketMiddleware {
15
+ * constructor(private converterService: ConverterService) {
16
+ * }
17
+ * async use(@Args() args: any[]) {
18
+ *
19
+ * let [user] = args;
20
+ * // update Arguments
21
+ * user = ConverterService.deserialize(user, User);
22
+ *
23
+ * return [user];
24
+ * }
25
+ * }
26
+ * ```
27
+ * > The user instance will be forwarded to the next middleware and to your decorated method.
28
+ *
29
+ * Then:
30
+ *
31
+ * ```typescript
32
+ * import {SocketService, SocketUseAfter, SocketUseBefore, Emit, Input, Args} from "@tsed/socketio";
33
+ * import {UserConverterSocketMiddleware} from "../middlewares.js";
34
+ * import {User} from "../models/User.js";
35
+ *
36
+ * @SocketService("/my-namespace")
37
+ * @SocketUseBefore(UserConverterSocketMiddleware) // global version
38
+ * export class MySocketService {
39
+ *
40
+ * @Input("eventName")
41
+ * @Emit("responseEventName") // or Broadcast or BroadcastOthers
42
+ * @SocketUseBefore(UserConverterSocketMiddleware)
43
+ * async myMethod(@Args(0) user: User) {
44
+ *
45
+ * console.log(user);
46
+ *
47
+ * return user;
48
+ * }
49
+ * }
50
+ * ```
51
+ *
52
+ * @returns {(target: any, propertyKey?: string, descriptor?: PropertyDescriptor) => void}
53
+ * @decorator
54
+ * @experimental
55
+ * @param middlewares
56
+ */
57
+ export function SocketUseBefore(...middlewares: Type<any>[]) {
58
+ return (target: any, propertyKey?: string, descriptor?: PropertyDescriptor) => {
59
+ if (propertyKey) {
60
+ Store.from(target).merge("socketIO", {
61
+ handlers: {
62
+ [propertyKey]: {
63
+ useBefore: middlewares
64
+ }
65
+ }
66
+ });
67
+ } else {
68
+ Store.from(target).merge("socketIO", {
69
+ useBefore: middlewares
70
+ });
71
+ }
72
+ };
73
+ }
package/src/index.ts ADDED
@@ -0,0 +1,41 @@
1
+ /**
2
+ * @file Automatically generated by @tsed/barrels.
3
+ */
4
+ export * from "./class/SocketHandlersBuilder.js";
5
+ export * from "./class/SocketProviderMetadata.js";
6
+ export * from "./class/SocketSessionData.js";
7
+ export * from "./constants/constants.js";
8
+ export * from "./decorators/args.js";
9
+ export * from "./decorators/broadcast.js";
10
+ export * from "./decorators/broadcastOthers.js";
11
+ export * from "./decorators/emit.js";
12
+ export * from "./decorators/input.js";
13
+ export * from "./decorators/inputAndBroadcast.js";
14
+ export * from "./decorators/inputAndBroadcastOthers.js";
15
+ export * from "./decorators/inputAndEmit.js";
16
+ export * from "./decorators/io.js";
17
+ export * from "./decorators/nsp.js";
18
+ export * from "./decorators/reason.js";
19
+ export * from "./decorators/socket.js";
20
+ export * from "./decorators/socketErr.js";
21
+ export * from "./decorators/socketEventName.js";
22
+ export * from "./decorators/socketFilter.js";
23
+ export * from "./decorators/socketMiddleware.js";
24
+ export * from "./decorators/socketMiddlewareError.js";
25
+ export * from "./decorators/socketNsp.js";
26
+ export * from "./decorators/socketReturns.js";
27
+ export * from "./decorators/socketService.js";
28
+ export * from "./decorators/socketSession.js";
29
+ export * from "./decorators/socketUseAfter.js";
30
+ export * from "./decorators/socketUseBefore.js";
31
+ export * from "./interfaces/interfaces.js";
32
+ export * from "./interfaces/OnNamespaceInit.js";
33
+ export * from "./interfaces/SocketFilters.js";
34
+ export * from "./interfaces/SocketHandlerMetadata.js";
35
+ export * from "./interfaces/SocketInjectableNsp.js";
36
+ export * from "./interfaces/SocketParamMetadata.js";
37
+ export * from "./interfaces/SocketProviderTypes.js";
38
+ export * from "./interfaces/SocketReturnsTypes.js";
39
+ export * from "./services/SocketIOServer.js";
40
+ export * from "./services/SocketIOService.js";
41
+ export * from "./SocketIOModule.js";
@@ -0,0 +1,8 @@
1
+ import {Namespace} from "socket.io";
2
+
3
+ /**
4
+ *
5
+ */
6
+ export interface OnNamespaceInit {
7
+ $onNamespaceInit(nsp: Namespace): void;
8
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * @ignore
3
+ */
4
+ export enum SocketFilters {
5
+ ARGS = "args",
6
+ SOCKET = "socket",
7
+ EVENT_NAME = "eventName",
8
+ NSP = "nsp",
9
+ SESSION = "session",
10
+ RAW_SESSION = "raw_session",
11
+ ERR = "error",
12
+ SOCKET_NSP = "socket_nsp",
13
+ REASON = "reason"
14
+ }
@@ -0,0 +1,17 @@
1
+ import {SocketParamMetadata} from "./SocketParamMetadata.js";
2
+ import {SocketReturnsTypes} from "./SocketReturnsTypes.js";
3
+
4
+ /**
5
+ *
6
+ */
7
+ export interface SocketHandlerMetadata {
8
+ eventName?: string;
9
+ methodClassName: string;
10
+ useBefore?: any[];
11
+ useAfter?: any[];
12
+ parameters?: {[key: number]: SocketParamMetadata};
13
+ returns?: {
14
+ type: SocketReturnsTypes;
15
+ eventName: string;
16
+ };
17
+ }
@@ -0,0 +1,4 @@
1
+ export interface SocketInjectableNsp {
2
+ propertyKey: string;
3
+ nsp?: string | RegExp;
4
+ }
@@ -0,0 +1,9 @@
1
+ import {MetadataTypes} from "@tsed/core";
2
+
3
+ import {SocketFilters} from "./SocketFilters.js";
4
+
5
+ export interface SocketParamMetadata extends Partial<MetadataTypes> {
6
+ filter: SocketFilters;
7
+ mapIndex?: number;
8
+ useMapper?: boolean;
9
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ *
3
+ */
4
+ export enum SocketProviderTypes {
5
+ SERVICE = "service",
6
+ MIDDLEWARE = "middleware"
7
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @ignore
3
+ */
4
+ export enum SocketReturnsTypes {
5
+ BROADCAST = "broadcast",
6
+ BROADCAST_OTHERS = "broadcastOthers",
7
+ EMIT = "emit"
8
+ }
@@ -0,0 +1,9 @@
1
+ import SocketIO from "socket.io";
2
+
3
+ declare global {
4
+ namespace TsED {
5
+ interface Configuration {
6
+ socketIO: Partial<SocketIO.ServerOptions>;
7
+ }
8
+ }
9
+ }
@@ -0,0 +1,33 @@
1
+ import {PlatformTest} from "@tsed/platform-http/testing";
2
+
3
+ import {Server, SocketIOServer} from "./SocketIOServer.js";
4
+
5
+ describe("SocketIOServer", () => {
6
+ describe("when there is configuration", () => {
7
+ beforeEach(() =>
8
+ PlatformTest.create({
9
+ socketIO: {
10
+ path: "/api/ws"
11
+ }
12
+ })
13
+ );
14
+ afterEach(PlatformTest.reset);
15
+
16
+ it("should inject the SocketIO server", () => {
17
+ const server = PlatformTest.get<SocketIOServer>(SocketIOServer);
18
+
19
+ expect(server).toBeInstanceOf(Server);
20
+ });
21
+ });
22
+
23
+ describe("when there is no configuration", () => {
24
+ beforeEach(() => PlatformTest.create({}));
25
+ afterEach(PlatformTest.reset);
26
+
27
+ it("should inject the SocketIO server", () => {
28
+ const server = PlatformTest.get<SocketIOServer>(SocketIOServer);
29
+
30
+ expect(server).toBeInstanceOf(Server);
31
+ });
32
+ });
33
+ });
@@ -0,0 +1,15 @@
1
+ import {constant, injectable} from "@tsed/di";
2
+ import {Server, type ServerOptions} from "socket.io";
3
+
4
+ export type SocketIOServer = Server;
5
+
6
+ // tslint:disable-next-line: variable-name
7
+ export const SocketIOServer = Server;
8
+
9
+ injectable(Server).factory(() => {
10
+ const socketIO = constant<Partial<ServerOptions>>("socketIO");
11
+
12
+ return new Server(socketIO);
13
+ });
14
+
15
+ export {Server};
@@ -0,0 +1,54 @@
1
+ import {PlatformTest} from "@tsed/platform-http/testing";
2
+
3
+ import {SocketIOService} from "../index.js";
4
+ import {Server} from "./SocketIOServer.js";
5
+
6
+ async function createServiceFixture() {
7
+ const namespace = {
8
+ on: vi.fn()
9
+ };
10
+ const ioStub = {
11
+ of: vi.fn().mockReturnValue(namespace)
12
+ };
13
+ const instance = {
14
+ onConnection: vi.fn(),
15
+ onDisconnect: vi.fn()
16
+ };
17
+
18
+ const socket = {
19
+ on: vi.fn()
20
+ };
21
+
22
+ const service = await PlatformTest.invoke(SocketIOService, [
23
+ {
24
+ token: Server,
25
+ use: ioStub
26
+ }
27
+ ]);
28
+ const reason = "transport error";
29
+
30
+ return {namespace, ioStub, service, instance, socket, reason};
31
+ }
32
+
33
+ describe("SocketIOService", () => {
34
+ beforeEach(() => PlatformTest.create());
35
+ afterEach(() => PlatformTest.reset());
36
+
37
+ describe("getNsp", () => {
38
+ it.each([{input: "/"}, {input: /test/}])("should call io.of with $input and create namespace", async ({input}) => {
39
+ const {service, namespace, ioStub, socket, instance, reason} = await createServiceFixture();
40
+
41
+ const nspConf = service.getNsp(input);
42
+ nspConf.instances.push(instance);
43
+
44
+ namespace.on.mock.calls[0][1](socket);
45
+ socket.on.mock.calls[0][1](reason);
46
+
47
+ expect(ioStub.of).toHaveBeenCalledWith(input);
48
+ expect(namespace.on).toHaveBeenCalledWith("connection", expect.any(Function));
49
+ expect(instance.onConnection).toHaveBeenCalledWith(socket, namespace);
50
+ expect(socket.on).toHaveBeenCalledWith("disconnect", expect.any(Function));
51
+ expect(instance.onDisconnect).toHaveBeenCalledWith(socket, namespace, reason);
52
+ });
53
+ });
54
+ });
@@ -0,0 +1,69 @@
1
+ import {InjectorService, Provider, Service} from "@tsed/di";
2
+ import * as SocketIO from "socket.io"; // tslint:disable-line: no-unused-variable
3
+
4
+ import {SocketHandlersBuilder} from "../class/SocketHandlersBuilder.js";
5
+ import {SocketProviderMetadata} from "../class/SocketProviderMetadata.js";
6
+ import {IO} from "../decorators/io.js";
7
+
8
+ /**
9
+ *
10
+ */
11
+ @Service()
12
+ export class SocketIOService {
13
+ /**
14
+ *
15
+ * @type {Map<any, any>}
16
+ */
17
+ private namespaces: Map<string | RegExp, {nsp: SocketIO.Namespace; instances: any}> = new Map();
18
+
19
+ constructor(
20
+ private injector: InjectorService,
21
+ @IO private io: SocketIO.Server
22
+ ) {}
23
+
24
+ /**
25
+ *
26
+ * @param {string} namespace
27
+ * @returns {SocketIO.Namespace}
28
+ */
29
+ public getNsp(namespace: string | RegExp = "/"): {nsp: SocketIO.Namespace; instances: any[]} {
30
+ if (!this.namespaces.has(namespace)) {
31
+ const conf = {nsp: this.io.of(namespace), instances: []};
32
+
33
+ this.namespaces.set(namespace, conf);
34
+
35
+ conf.nsp.on("connection", (socket) => {
36
+ conf.instances.forEach((builder: SocketHandlersBuilder) => {
37
+ builder.onConnection(socket, conf.nsp);
38
+ });
39
+
40
+ socket.on("disconnect", (reason: string) => {
41
+ conf.instances.forEach((builder: SocketHandlersBuilder) => {
42
+ builder.onDisconnect(socket, conf.nsp, reason);
43
+ });
44
+ });
45
+ });
46
+ }
47
+
48
+ return this.namespaces.get(namespace)!;
49
+ }
50
+
51
+ /**
52
+ *
53
+ * @param {Provider<any>} provider
54
+ */
55
+ public addSocketProvider(provider: Provider<any>) {
56
+ const wsConfig: SocketProviderMetadata = new SocketProviderMetadata(provider.store.get("socketIO"));
57
+
58
+ const nspConfig = this.getNsp(wsConfig.namespace);
59
+ const nsps = new Map();
60
+
61
+ this.namespaces.forEach((value, nsp) => {
62
+ nsps.set(nsp, value.nsp);
63
+ });
64
+
65
+ const builder = new SocketHandlersBuilder(provider, this.injector).build(nsps);
66
+
67
+ nspConfig.instances.push(builder);
68
+ }
69
+ }