@grest-ts/websocket 0.0.6 → 0.0.7

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.
@@ -1,136 +1,136 @@
1
- /**
2
- * Server extension for WebSocketSchema - adds startServer and register methods
3
- * This file should only be imported in server (Node.js) context
4
- */
5
-
6
- import type {GGSocket} from "../socket/GGSocket"
7
- import {GGWebSocketSchema} from "../schema/GGWebSocketSchema";
8
- import {GGWebSocketMiddleware} from "../schema/GGWebSocketMiddleware";
9
- import {GGSocketServer} from "./GGSocketServer";
10
- import {GGLocator} from "@grest-ts/locator";
11
- import {WebSocketIncoming, WebSocketOutgoing} from "../socket/WebSocketTypes";
12
- import {GG_HTTP_SERVER, GGHttpServer} from "@grest-ts/http";
13
-
14
- export interface WebSocketSchemaConfig {
15
- /**
16
- * The HTTP server adapter to attach the WebSocket server to.
17
- * If not provided, will look up from locator.
18
- */
19
- http?: GGHttpServer;
20
- /**
21
- * Additional middlewares to apply to all connections.
22
- */
23
- middlewares?: GGWebSocketMiddleware[];
24
- }
25
-
26
- declare module "../schema/GGWebSocketSchema" {
27
- interface GGWebSocketSchema<TClientToServer, TServerToClient, TContext = {}, TQuery = undefined, TClientToServerImpl = TClientToServer> {
28
- /**
29
- * Start the WebSocket server for this API.
30
- */
31
- startServer(
32
- onConnection: (incoming: WebSocketIncoming<TClientToServerImpl>, outgoing: WebSocketOutgoing<TServerToClient>) => void,
33
- config: WebSocketSchemaConfig
34
- ): GGSocketServer<TContext, TQuery>
35
-
36
- /**
37
- * Register this WebSocket API with the default HTTP server.
38
- * Uses GGHttpServerAdapter from locator if not explicitly provided.
39
- */
40
- register(
41
- onConnection: (incoming: WebSocketIncoming<TClientToServerImpl>, outgoing: WebSocketOutgoing<TServerToClient>) => void,
42
- config?: WebSocketSchemaConfig
43
- ): void
44
- }
45
- }
46
-
47
- GGWebSocketSchema.prototype.startServer = function (
48
- this: GGWebSocketSchema<any, any, any, any>,
49
- onConnection: any,
50
- config: WebSocketSchemaConfig
51
- ): GGSocketServer<any, any> {
52
- const contract = this.contract
53
- if (!contract) {
54
- throw new Error(`WebSocketSchema "${this.name}" has no contract.`)
55
- }
56
-
57
- const normalizedPath = this.path.startsWith('/') ? this.path : '/' + this.path
58
- const schemaName = this.name
59
- const http = config.http ?? GGLocator.getScope().get(GG_HTTP_SERVER);
60
-
61
- // @TODO We might want some lookup here based on path/middlewares etc. If I use same socket for multiple paths, we need to reuse also same GGSocketServer.
62
- const socketServer = new GGSocketServer(http, {
63
- apiName: schemaName,
64
- path: normalizedPath,
65
- middlewares: [...this.middlewares, ...(config?.middlewares ?? [])]
66
- });
67
-
68
- socketServer.onConnection((socket: GGSocket) => {
69
- const clientToServerContract = contract.clientToServer
70
- const serverToClientContract = contract.serverToClient
71
-
72
- const incoming: any = {
73
- on(handlers: any) {
74
- const impl: Record<string, any> = {};
75
- for (const methodName of Object.keys(clientToServerContract.methods)) {
76
- const methodDef = clientToServerContract.methods[methodName] as any;
77
- const params = methodDef.params;
78
- impl[methodName] = (data: any) => {
79
- // If method has params info, unpack data object to positional args
80
- if (params && params.length > 0 && data && typeof data === 'object') {
81
- const args = params.map((p: any) => data[p.name]);
82
- return handlers[methodName](...args);
83
- }
84
- // Single or no parameter - pass directly
85
- return handlers[methodName](data);
86
- };
87
- }
88
-
89
- const incomingInstance = clientToServerContract.implement(impl);
90
-
91
- for (const methodName of Object.keys(clientToServerContract.methods)) {
92
- socket.registerHandler({
93
- path: `${schemaName}.${methodName}`,
94
- handler: (incomingInstance as any)[methodName]
95
- });
96
- }
97
- }
98
- }
99
-
100
- const impl: Record<string, any> = {};
101
- for (const methodName of Object.keys(serverToClientContract.methods)) {
102
- const method = serverToClientContract.methods[methodName];
103
- const expectsResponse = 'success' in method;
104
- impl[methodName] = (data: any) => {
105
- return socket.send(`${schemaName}.${methodName}`, data, expectsResponse);
106
- };
107
- }
108
-
109
- const outgoingInstance = serverToClientContract.implement(impl);
110
- (outgoingInstance as any).onClose = (callback: () => void) => {
111
- socket.onClose(callback)
112
- }
113
- onConnection(incoming, outgoingInstance)
114
- });
115
-
116
- return socketServer;
117
- }
118
-
119
- GGWebSocketSchema.prototype.register = function (
120
- this: GGWebSocketSchema<any, any, any, any>,
121
- onConnection: any,
122
- config?: WebSocketSchemaConfig
123
- ): void {
124
- let httpServer = config?.http;
125
- if (!httpServer) {
126
- httpServer = GGLocator.getScope().get(GG_HTTP_SERVER);
127
- }
128
- if (!httpServer) {
129
- throw new Error(`No HTTP server found. Make sure to register GGHttpServerAdapter in the scope or pass it via config`)
130
- }
131
-
132
- this.startServer(onConnection, {
133
- http: httpServer,
134
- middlewares: config?.middlewares
135
- });
136
- }
1
+ /**
2
+ * Server extension for WebSocketSchema - adds startServer and register methods
3
+ * This file should only be imported in server (Node.js) context
4
+ */
5
+
6
+ import type {GGSocket} from "../socket/GGSocket"
7
+ import {GGWebSocketSchema} from "../schema/GGWebSocketSchema";
8
+ import {GGWebSocketMiddleware} from "../schema/GGWebSocketMiddleware";
9
+ import {GGSocketServer} from "./GGSocketServer";
10
+ import {GGLocator} from "@grest-ts/locator";
11
+ import {WebSocketIncoming, WebSocketOutgoing} from "../socket/WebSocketTypes";
12
+ import {GG_HTTP_SERVER, GGHttpServer} from "@grest-ts/http";
13
+
14
+ export interface WebSocketSchemaConfig {
15
+ /**
16
+ * The HTTP server adapter to attach the WebSocket server to.
17
+ * If not provided, will look up from locator.
18
+ */
19
+ http?: GGHttpServer;
20
+ /**
21
+ * Additional middlewares to apply to all connections.
22
+ */
23
+ middlewares?: GGWebSocketMiddleware[];
24
+ }
25
+
26
+ declare module "../schema/GGWebSocketSchema" {
27
+ interface GGWebSocketSchema<TClientToServer, TServerToClient, TContext = {}, TQuery = undefined, TClientToServerImpl = TClientToServer> {
28
+ /**
29
+ * Start the WebSocket server for this API.
30
+ */
31
+ startServer(
32
+ onConnection: (incoming: WebSocketIncoming<TClientToServerImpl>, outgoing: WebSocketOutgoing<TServerToClient>) => void,
33
+ config: WebSocketSchemaConfig
34
+ ): GGSocketServer<TContext, TQuery>
35
+
36
+ /**
37
+ * Register this WebSocket API with the default HTTP server.
38
+ * Uses GGHttpServerAdapter from locator if not explicitly provided.
39
+ */
40
+ register(
41
+ onConnection: (incoming: WebSocketIncoming<TClientToServerImpl>, outgoing: WebSocketOutgoing<TServerToClient>) => void,
42
+ config?: WebSocketSchemaConfig
43
+ ): void
44
+ }
45
+ }
46
+
47
+ GGWebSocketSchema.prototype.startServer = function (
48
+ this: GGWebSocketSchema<any, any, any, any>,
49
+ onConnection: any,
50
+ config: WebSocketSchemaConfig
51
+ ): GGSocketServer<any, any> {
52
+ const contract = this.contract
53
+ if (!contract) {
54
+ throw new Error(`WebSocketSchema "${this.name}" has no contract.`)
55
+ }
56
+
57
+ const normalizedPath = this.path.startsWith('/') ? this.path : '/' + this.path
58
+ const schemaName = this.name
59
+ const http = config.http ?? GGLocator.getScope().get(GG_HTTP_SERVER);
60
+
61
+ // @TODO We might want some lookup here based on path/middlewares etc. If I use same socket for multiple paths, we need to reuse also same GGSocketServer.
62
+ const socketServer = new GGSocketServer(http, {
63
+ apiName: schemaName,
64
+ path: normalizedPath,
65
+ middlewares: [...this.middlewares, ...(config?.middlewares ?? [])]
66
+ });
67
+
68
+ socketServer.onConnection((socket: GGSocket) => {
69
+ const clientToServerContract = contract.clientToServer
70
+ const serverToClientContract = contract.serverToClient
71
+
72
+ const incoming: any = {
73
+ on(handlers: any) {
74
+ const impl: Record<string, any> = {};
75
+ for (const methodName of Object.keys(clientToServerContract.methods)) {
76
+ const methodDef = clientToServerContract.methods[methodName] as any;
77
+ const params = methodDef.params;
78
+ impl[methodName] = (data: any) => {
79
+ // If method has params info, unpack data object to positional args
80
+ if (params && params.length > 0 && data && typeof data === 'object') {
81
+ const args = params.map((p: any) => data[p.name]);
82
+ return handlers[methodName](...args);
83
+ }
84
+ // Single or no parameter - pass directly
85
+ return handlers[methodName](data);
86
+ };
87
+ }
88
+
89
+ const incomingInstance = clientToServerContract.implement(impl);
90
+
91
+ for (const methodName of Object.keys(clientToServerContract.methods)) {
92
+ socket.registerHandler({
93
+ path: `${schemaName}.${methodName}`,
94
+ handler: (incomingInstance as any)[methodName]
95
+ });
96
+ }
97
+ }
98
+ }
99
+
100
+ const impl: Record<string, any> = {};
101
+ for (const methodName of Object.keys(serverToClientContract.methods)) {
102
+ const method = serverToClientContract.methods[methodName];
103
+ const expectsResponse = 'success' in method;
104
+ impl[methodName] = (data: any) => {
105
+ return socket.send(`${schemaName}.${methodName}`, data, expectsResponse);
106
+ };
107
+ }
108
+
109
+ const outgoingInstance = serverToClientContract.implement(impl);
110
+ (outgoingInstance as any).onClose = (callback: () => void) => {
111
+ socket.onClose(callback)
112
+ }
113
+ onConnection(incoming, outgoingInstance)
114
+ });
115
+
116
+ return socketServer;
117
+ }
118
+
119
+ GGWebSocketSchema.prototype.register = function (
120
+ this: GGWebSocketSchema<any, any, any, any>,
121
+ onConnection: any,
122
+ config?: WebSocketSchemaConfig
123
+ ): void {
124
+ let httpServer = config?.http;
125
+ if (!httpServer) {
126
+ httpServer = GGLocator.getScope().get(GG_HTTP_SERVER);
127
+ }
128
+ if (!httpServer) {
129
+ throw new Error(`No HTTP server found. Make sure to register GGHttpServerAdapter in the scope or pass it via config`)
130
+ }
131
+
132
+ this.startServer(onConnection, {
133
+ http: httpServer,
134
+ middlewares: config?.middlewares
135
+ });
136
+ }
@@ -1,10 +1,10 @@
1
- import {GGContextKey} from "@grest-ts/context";
2
- import {IsNumber, IsObject, IsString} from "@grest-ts/schema";
3
-
4
- const IsWsConnectionContext = IsObject({
5
- port: IsNumber.orUndefined,
6
- path: IsString
7
- });
8
- export type WsConnectionContext = typeof IsWsConnectionContext.infer;
9
-
10
- export const GG_WS_CONNECTION = new GGContextKey<WsConnectionContext>('ws-connection', IsWsConnectionContext);
1
+ import {GGContextKey} from "@grest-ts/context";
2
+ import {IsNumber, IsObject, IsString} from "@grest-ts/schema";
3
+
4
+ const IsWsConnectionContext = IsObject({
5
+ port: IsNumber.orUndefined,
6
+ path: IsString
7
+ });
8
+ export type WsConnectionContext = typeof IsWsConnectionContext.infer;
9
+
10
+ export const GG_WS_CONNECTION = new GGContextKey<WsConnectionContext>('ws-connection', IsWsConnectionContext);
@@ -1,9 +1,9 @@
1
- import {GGContextKey} from "@grest-ts/context";
2
- import {IsObject, IsString} from "@grest-ts/schema";
3
-
4
- const IsWsMessageContext = IsObject({
5
- path: IsString
6
- });
7
- export type WsMessageContext = typeof IsWsMessageContext.infer;
8
-
9
- export const GG_WS_MESSAGE = new GGContextKey<WsMessageContext>('ws-message', IsWsMessageContext);
1
+ import {GGContextKey} from "@grest-ts/context";
2
+ import {IsObject, IsString} from "@grest-ts/schema";
3
+
4
+ const IsWsMessageContext = IsObject({
5
+ path: IsString
6
+ });
7
+ export type WsMessageContext = typeof IsWsMessageContext.infer;
8
+
9
+ export const GG_WS_MESSAGE = new GGContextKey<WsMessageContext>('ws-message', IsWsMessageContext);