@grest-ts/websocket 0.0.5 → 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.
- package/LICENSE +21 -21
- package/README.md +5 -0
- package/dist/tsconfig.publish.tsbuildinfo +1 -1
- package/package.json +12 -12
- package/src/adapter/BrowserSocketAdapter.ts +63 -63
- package/src/adapter/NodeSocketAdapter.ts +67 -67
- package/src/adapter/getDefaultAdapter.ts +13 -13
- package/src/client/GGSocketClient.ts +25 -25
- package/src/client/GGSocketPool.ts +244 -244
- package/src/index-browser.ts +20 -20
- package/src/index-node.ts +28 -28
- package/src/schema/GGWebSocketMiddleware.ts +43 -43
- package/src/schema/GGWebSocketSchema.ts +57 -57
- package/src/schema/webSocketSchema.ts +109 -109
- package/src/server/GGSocketServer.ts +217 -217
- package/src/server/GGWebSocketMetrics.ts +58 -58
- package/src/server/GGWebSocketSchema.startServer.ts +136 -136
- package/src/server/GG_WS_CONNECTION.ts +10 -10
- package/src/server/GG_WS_MESSAGE.ts +9 -9
- package/src/socket/GGSocket.ts +394 -394
- package/src/socket/SocketAdapter.ts +21 -21
- package/src/socket/SocketMessage.ts +97 -97
- package/src/socket/WebSocketTypes.ts +19 -19
- package/src/socket/utils/PendingRequestsMap.ts +128 -128
|
@@ -1,43 +1,43 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* WebSocket-specific middleware interface.
|
|
3
|
-
* Independent from @grest-ts/http - WebSocket has its own middleware system.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Context available during WebSocket handshake.
|
|
8
|
-
*/
|
|
9
|
-
export interface GGWebSocketHandshakeContext {
|
|
10
|
-
/**
|
|
11
|
-
* Headers from the handshake message (client-sent or server-received).
|
|
12
|
-
*/
|
|
13
|
-
headers: Record<string, string>;
|
|
14
|
-
/**
|
|
15
|
-
* Query parameters from the WebSocket URL.
|
|
16
|
-
*/
|
|
17
|
-
queryArgs: Record<string, string>;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* WebSocket middleware interface for both client and server.
|
|
22
|
-
* Unlike HTTP middleware, WebSocket middleware works with handshake data.
|
|
23
|
-
*/
|
|
24
|
-
export interface GGWebSocketMiddleware {
|
|
25
|
-
/**
|
|
26
|
-
* Client-side: Add headers to the handshake message before sending.
|
|
27
|
-
* Called by GGSocketPool when establishing a connection.
|
|
28
|
-
*/
|
|
29
|
-
updateHandshake?(context: GGWebSocketHandshakeContext): void;
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Server-side: Parse headers from the received handshake message.
|
|
33
|
-
* Called by GGSocketServer when receiving a connection.
|
|
34
|
-
*/
|
|
35
|
-
parseHandshake?(context: GGWebSocketHandshakeContext): void;
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Server-side: Async processing after parseHandshake.
|
|
39
|
-
* Can be used for authentication, authorization, etc.
|
|
40
|
-
* Throwing an error will reject the connection.
|
|
41
|
-
*/
|
|
42
|
-
process?(): Promise<void>;
|
|
43
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* WebSocket-specific middleware interface.
|
|
3
|
+
* Independent from @grest-ts/http - WebSocket has its own middleware system.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Context available during WebSocket handshake.
|
|
8
|
+
*/
|
|
9
|
+
export interface GGWebSocketHandshakeContext {
|
|
10
|
+
/**
|
|
11
|
+
* Headers from the handshake message (client-sent or server-received).
|
|
12
|
+
*/
|
|
13
|
+
headers: Record<string, string>;
|
|
14
|
+
/**
|
|
15
|
+
* Query parameters from the WebSocket URL.
|
|
16
|
+
*/
|
|
17
|
+
queryArgs: Record<string, string>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* WebSocket middleware interface for both client and server.
|
|
22
|
+
* Unlike HTTP middleware, WebSocket middleware works with handshake data.
|
|
23
|
+
*/
|
|
24
|
+
export interface GGWebSocketMiddleware {
|
|
25
|
+
/**
|
|
26
|
+
* Client-side: Add headers to the handshake message before sending.
|
|
27
|
+
* Called by GGSocketPool when establishing a connection.
|
|
28
|
+
*/
|
|
29
|
+
updateHandshake?(context: GGWebSocketHandshakeContext): void;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Server-side: Parse headers from the received handshake message.
|
|
33
|
+
* Called by GGSocketServer when receiving a connection.
|
|
34
|
+
*/
|
|
35
|
+
parseHandshake?(context: GGWebSocketHandshakeContext): void;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Server-side: Async processing after parseHandshake.
|
|
39
|
+
* Can be used for authentication, authorization, etc.
|
|
40
|
+
* Throwing an error will reject the connection.
|
|
41
|
+
*/
|
|
42
|
+
process?(): Promise<void>;
|
|
43
|
+
}
|
|
@@ -1,57 +1,57 @@
|
|
|
1
|
-
import {GGWebSocketMiddleware} from "./GGWebSocketMiddleware";
|
|
2
|
-
import {GGContractApiDefinition, GGContractClass} from "@grest-ts/schema";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* WebSocket API Schema - pure data definition with typed context
|
|
6
|
-
*
|
|
7
|
-
* Type parameters:
|
|
8
|
-
* - TClientToServer: Client-facing type for clientToServer methods (returns GGPromise)
|
|
9
|
-
* - TServerToClient: Client-facing type for serverToClient methods (returns GGPromise)
|
|
10
|
-
* - TContext: Accumulated context type (parseHandshake return types)
|
|
11
|
-
* - TQuery: Query parameters on connect
|
|
12
|
-
* - TClientToServerImpl: Server implementation type for clientToServer handlers (returns Promise)
|
|
13
|
-
*/
|
|
14
|
-
export class GGWebSocketSchema<
|
|
15
|
-
TClientToServer,
|
|
16
|
-
TServerToClient,
|
|
17
|
-
TContext = {},
|
|
18
|
-
TQuery = undefined,
|
|
19
|
-
TClientToServerImpl = TClientToServer
|
|
20
|
-
> {
|
|
21
|
-
public readonly name: string
|
|
22
|
-
public readonly path: string
|
|
23
|
-
public readonly middlewares: readonly GGWebSocketMiddleware[]
|
|
24
|
-
private readonly contractFactory: () => GGWebSocketContractRuntime
|
|
25
|
-
private contractCache: GGWebSocketContractRuntime | null = null
|
|
26
|
-
|
|
27
|
-
constructor(
|
|
28
|
-
name: string,
|
|
29
|
-
path: string,
|
|
30
|
-
contractFactory: () => GGWebSocketContractRuntime,
|
|
31
|
-
middlewares: readonly GGWebSocketMiddleware[] = []
|
|
32
|
-
) {
|
|
33
|
-
this.name = name
|
|
34
|
-
this.path = path
|
|
35
|
-
this.middlewares = middlewares
|
|
36
|
-
this.contractFactory = contractFactory
|
|
37
|
-
Object.freeze(this.middlewares)
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
get contract(): GGWebSocketContractRuntime {
|
|
41
|
-
if (!this.contractCache) {
|
|
42
|
-
this.contractCache = this.contractFactory()
|
|
43
|
-
}
|
|
44
|
-
Object.freeze(this)
|
|
45
|
-
return this.contractCache
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Runtime contract structure for WebSocket APIs.
|
|
51
|
-
* Contains GGContractClass instances for proper validation via implement().
|
|
52
|
-
*/
|
|
53
|
-
export interface GGWebSocketContractRuntime {
|
|
54
|
-
apiName: string
|
|
55
|
-
clientToServer: GGContractClass<GGContractApiDefinition>
|
|
56
|
-
serverToClient: GGContractClass<GGContractApiDefinition>
|
|
57
|
-
}
|
|
1
|
+
import {GGWebSocketMiddleware} from "./GGWebSocketMiddleware";
|
|
2
|
+
import {GGContractApiDefinition, GGContractClass} from "@grest-ts/schema";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* WebSocket API Schema - pure data definition with typed context
|
|
6
|
+
*
|
|
7
|
+
* Type parameters:
|
|
8
|
+
* - TClientToServer: Client-facing type for clientToServer methods (returns GGPromise)
|
|
9
|
+
* - TServerToClient: Client-facing type for serverToClient methods (returns GGPromise)
|
|
10
|
+
* - TContext: Accumulated context type (parseHandshake return types)
|
|
11
|
+
* - TQuery: Query parameters on connect
|
|
12
|
+
* - TClientToServerImpl: Server implementation type for clientToServer handlers (returns Promise)
|
|
13
|
+
*/
|
|
14
|
+
export class GGWebSocketSchema<
|
|
15
|
+
TClientToServer,
|
|
16
|
+
TServerToClient,
|
|
17
|
+
TContext = {},
|
|
18
|
+
TQuery = undefined,
|
|
19
|
+
TClientToServerImpl = TClientToServer
|
|
20
|
+
> {
|
|
21
|
+
public readonly name: string
|
|
22
|
+
public readonly path: string
|
|
23
|
+
public readonly middlewares: readonly GGWebSocketMiddleware[]
|
|
24
|
+
private readonly contractFactory: () => GGWebSocketContractRuntime
|
|
25
|
+
private contractCache: GGWebSocketContractRuntime | null = null
|
|
26
|
+
|
|
27
|
+
constructor(
|
|
28
|
+
name: string,
|
|
29
|
+
path: string,
|
|
30
|
+
contractFactory: () => GGWebSocketContractRuntime,
|
|
31
|
+
middlewares: readonly GGWebSocketMiddleware[] = []
|
|
32
|
+
) {
|
|
33
|
+
this.name = name
|
|
34
|
+
this.path = path
|
|
35
|
+
this.middlewares = middlewares
|
|
36
|
+
this.contractFactory = contractFactory
|
|
37
|
+
Object.freeze(this.middlewares)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
get contract(): GGWebSocketContractRuntime {
|
|
41
|
+
if (!this.contractCache) {
|
|
42
|
+
this.contractCache = this.contractFactory()
|
|
43
|
+
}
|
|
44
|
+
Object.freeze(this)
|
|
45
|
+
return this.contractCache
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Runtime contract structure for WebSocket APIs.
|
|
51
|
+
* Contains GGContractClass instances for proper validation via implement().
|
|
52
|
+
*/
|
|
53
|
+
export interface GGWebSocketContractRuntime {
|
|
54
|
+
apiName: string
|
|
55
|
+
clientToServer: GGContractClass<GGContractApiDefinition>
|
|
56
|
+
serverToClient: GGContractClass<GGContractApiDefinition>
|
|
57
|
+
}
|
|
@@ -1,109 +1,109 @@
|
|
|
1
|
-
import {GGWebSocketSchema, GGWebSocketContractRuntime} from "./GGWebSocketSchema";
|
|
2
|
-
import {GGWebSocketMiddleware} from "./GGWebSocketMiddleware";
|
|
3
|
-
import {GGContractClass, GGContractClient, GGContractImplementation, GGContractMethod} from "@grest-ts/schema";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Bidirectional websocket contract methods
|
|
7
|
-
*/
|
|
8
|
-
export interface GGSocketContractMethods {
|
|
9
|
-
clientToServer: Record<string, GGContractMethod>
|
|
10
|
-
serverToClient: Record<string, GGContractMethod>
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* WebSocket contract definition
|
|
15
|
-
*/
|
|
16
|
-
export interface GGSocketContract<TDef extends GGSocketContractMethods = GGSocketContractMethods> {
|
|
17
|
-
name: string
|
|
18
|
-
methods: TDef
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Create a websocket contract
|
|
23
|
-
*/
|
|
24
|
-
export function defineSocketContract<TDef extends GGSocketContractMethods>(
|
|
25
|
-
name: string,
|
|
26
|
-
methods: TDef
|
|
27
|
-
): GGSocketContract<TDef> {
|
|
28
|
-
return {name, methods}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Create a WebSocket API schema builder from a contract.
|
|
33
|
-
*
|
|
34
|
-
* @example
|
|
35
|
-
* export const ChatContract = defineSocketContract("Chat", {
|
|
36
|
-
* clientToServer: {
|
|
37
|
-
* sendMessage: { input: IsMessage, success: IsVoid, errors: [SERVER_ERROR] }
|
|
38
|
-
* },
|
|
39
|
-
* serverToClient: {
|
|
40
|
-
* onMessage: { input: IsMessage }
|
|
41
|
-
* }
|
|
42
|
-
* })
|
|
43
|
-
*
|
|
44
|
-
* export const ChatApi = webSocketSchema(ChatContract)
|
|
45
|
-
* .path("/chat")
|
|
46
|
-
* .use(AuthMiddleware)
|
|
47
|
-
* .done()
|
|
48
|
-
*/
|
|
49
|
-
export function webSocketSchema<TDef extends GGSocketContractMethods>(
|
|
50
|
-
contract: GGSocketContract<TDef>
|
|
51
|
-
): GGWebSocketSchemaBuilder<
|
|
52
|
-
GGContractClient<TDef["clientToServer"]>,
|
|
53
|
-
GGContractClient<TDef["serverToClient"]>,
|
|
54
|
-
undefined,
|
|
55
|
-
undefined,
|
|
56
|
-
GGContractImplementation<TDef["clientToServer"]>
|
|
57
|
-
> {
|
|
58
|
-
return new GGWebSocketSchemaBuilder(contract)
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
class GGWebSocketSchemaBuilder<
|
|
62
|
-
TClientToServer,
|
|
63
|
-
TServerToClient,
|
|
64
|
-
TContext = undefined,
|
|
65
|
-
TQuery = undefined,
|
|
66
|
-
TClientToServerImpl = TClientToServer
|
|
67
|
-
> {
|
|
68
|
-
private _path: string = ""
|
|
69
|
-
private _middlewares: GGWebSocketMiddleware[] = []
|
|
70
|
-
|
|
71
|
-
constructor(
|
|
72
|
-
private readonly _contract: GGSocketContract
|
|
73
|
-
) {
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
path(path: string): this {
|
|
77
|
-
this._path = path
|
|
78
|
-
return this
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
use<M extends GGWebSocketMiddleware>(middleware: M): GGWebSocketSchemaBuilder<TClientToServer, TServerToClient, TContext | M, TQuery, TClientToServerImpl> {
|
|
82
|
-
this._middlewares.push(middleware)
|
|
83
|
-
return this as any
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
queryOnConnect<TNewQuery>(): GGWebSocketSchemaBuilder<TClientToServer, TServerToClient, TContext, TNewQuery, TClientToServerImpl> {
|
|
87
|
-
return this as any
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
done(): GGWebSocketSchema<TClientToServer, TServerToClient, TContext, TQuery, TClientToServerImpl> {
|
|
91
|
-
const contract = this._contract;
|
|
92
|
-
const contractFactory = (): GGWebSocketContractRuntime => {
|
|
93
|
-
const methods = contract.methods;
|
|
94
|
-
const name = contract.name;
|
|
95
|
-
return {
|
|
96
|
-
apiName: name,
|
|
97
|
-
clientToServer: new GGContractClass(name + ".clientToServer", methods.clientToServer),
|
|
98
|
-
serverToClient: new GGContractClass(name + ".serverToClient", methods.serverToClient)
|
|
99
|
-
};
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
return new GGWebSocketSchema<TClientToServer, TServerToClient, TContext, TQuery, TClientToServerImpl>(
|
|
103
|
-
contract.name,
|
|
104
|
-
this._path,
|
|
105
|
-
contractFactory,
|
|
106
|
-
this._middlewares
|
|
107
|
-
)
|
|
108
|
-
}
|
|
109
|
-
}
|
|
1
|
+
import {GGWebSocketSchema, GGWebSocketContractRuntime} from "./GGWebSocketSchema";
|
|
2
|
+
import {GGWebSocketMiddleware} from "./GGWebSocketMiddleware";
|
|
3
|
+
import {GGContractClass, GGContractClient, GGContractImplementation, GGContractMethod} from "@grest-ts/schema";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Bidirectional websocket contract methods
|
|
7
|
+
*/
|
|
8
|
+
export interface GGSocketContractMethods {
|
|
9
|
+
clientToServer: Record<string, GGContractMethod>
|
|
10
|
+
serverToClient: Record<string, GGContractMethod>
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* WebSocket contract definition
|
|
15
|
+
*/
|
|
16
|
+
export interface GGSocketContract<TDef extends GGSocketContractMethods = GGSocketContractMethods> {
|
|
17
|
+
name: string
|
|
18
|
+
methods: TDef
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Create a websocket contract
|
|
23
|
+
*/
|
|
24
|
+
export function defineSocketContract<TDef extends GGSocketContractMethods>(
|
|
25
|
+
name: string,
|
|
26
|
+
methods: TDef
|
|
27
|
+
): GGSocketContract<TDef> {
|
|
28
|
+
return {name, methods}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Create a WebSocket API schema builder from a contract.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* export const ChatContract = defineSocketContract("Chat", {
|
|
36
|
+
* clientToServer: {
|
|
37
|
+
* sendMessage: { input: IsMessage, success: IsVoid, errors: [SERVER_ERROR] }
|
|
38
|
+
* },
|
|
39
|
+
* serverToClient: {
|
|
40
|
+
* onMessage: { input: IsMessage }
|
|
41
|
+
* }
|
|
42
|
+
* })
|
|
43
|
+
*
|
|
44
|
+
* export const ChatApi = webSocketSchema(ChatContract)
|
|
45
|
+
* .path("/chat")
|
|
46
|
+
* .use(AuthMiddleware)
|
|
47
|
+
* .done()
|
|
48
|
+
*/
|
|
49
|
+
export function webSocketSchema<TDef extends GGSocketContractMethods>(
|
|
50
|
+
contract: GGSocketContract<TDef>
|
|
51
|
+
): GGWebSocketSchemaBuilder<
|
|
52
|
+
GGContractClient<TDef["clientToServer"]>,
|
|
53
|
+
GGContractClient<TDef["serverToClient"]>,
|
|
54
|
+
undefined,
|
|
55
|
+
undefined,
|
|
56
|
+
GGContractImplementation<TDef["clientToServer"]>
|
|
57
|
+
> {
|
|
58
|
+
return new GGWebSocketSchemaBuilder(contract)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
class GGWebSocketSchemaBuilder<
|
|
62
|
+
TClientToServer,
|
|
63
|
+
TServerToClient,
|
|
64
|
+
TContext = undefined,
|
|
65
|
+
TQuery = undefined,
|
|
66
|
+
TClientToServerImpl = TClientToServer
|
|
67
|
+
> {
|
|
68
|
+
private _path: string = ""
|
|
69
|
+
private _middlewares: GGWebSocketMiddleware[] = []
|
|
70
|
+
|
|
71
|
+
constructor(
|
|
72
|
+
private readonly _contract: GGSocketContract
|
|
73
|
+
) {
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
path(path: string): this {
|
|
77
|
+
this._path = path
|
|
78
|
+
return this
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
use<M extends GGWebSocketMiddleware>(middleware: M): GGWebSocketSchemaBuilder<TClientToServer, TServerToClient, TContext | M, TQuery, TClientToServerImpl> {
|
|
82
|
+
this._middlewares.push(middleware)
|
|
83
|
+
return this as any
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
queryOnConnect<TNewQuery>(): GGWebSocketSchemaBuilder<TClientToServer, TServerToClient, TContext, TNewQuery, TClientToServerImpl> {
|
|
87
|
+
return this as any
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
done(): GGWebSocketSchema<TClientToServer, TServerToClient, TContext, TQuery, TClientToServerImpl> {
|
|
91
|
+
const contract = this._contract;
|
|
92
|
+
const contractFactory = (): GGWebSocketContractRuntime => {
|
|
93
|
+
const methods = contract.methods;
|
|
94
|
+
const name = contract.name;
|
|
95
|
+
return {
|
|
96
|
+
apiName: name,
|
|
97
|
+
clientToServer: new GGContractClass(name + ".clientToServer", methods.clientToServer),
|
|
98
|
+
serverToClient: new GGContractClass(name + ".serverToClient", methods.serverToClient)
|
|
99
|
+
};
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
return new GGWebSocketSchema<TClientToServer, TServerToClient, TContext, TQuery, TClientToServerImpl>(
|
|
103
|
+
contract.name,
|
|
104
|
+
this._path,
|
|
105
|
+
contractFactory,
|
|
106
|
+
this._middlewares
|
|
107
|
+
)
|
|
108
|
+
}
|
|
109
|
+
}
|