@signal-spec/react-client 0.1.0 → 0.2.0
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/dist/index.d.mts
CHANGED
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
import * as SignalR from "@microsoft/signalr";
|
|
2
2
|
import { HubConnection } from "@microsoft/signalr";
|
|
3
|
-
//#region src/helpers/
|
|
4
|
-
|
|
3
|
+
//#region src/helpers/useSignalRConnections.d.ts
|
|
4
|
+
interface SignalRConnectionData {
|
|
5
5
|
connection: SignalR.HubConnection;
|
|
6
6
|
componentsSubscribed: number;
|
|
7
7
|
}
|
|
8
8
|
export declare function isConnectionAlive(connectionState: SignalR.HubConnectionState | null | undefined): boolean | null | undefined;
|
|
9
|
-
|
|
10
|
-
backendUri: string;
|
|
11
|
-
EnableSignalRDebug: boolean;
|
|
12
|
-
connectionsStore: Map<string, SignalRConnectionData>;
|
|
9
|
+
interface SignalRConnections {
|
|
13
10
|
createConnection: (hubName: string) => Promise<SignalRConnectionData>;
|
|
14
11
|
setupEventHandlers: <T>(hubName: string, handlers: Partial<T>, onConnected?: (connection: SignalR.HubConnection) => void) => Promise<void>;
|
|
15
12
|
deleteConnection: <T>(hubName: string, handlers: Partial<T>) => void;
|
|
16
13
|
}
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
interface SignalRConfiguration {
|
|
15
|
+
backendUri: string;
|
|
16
|
+
enableDebug?: boolean;
|
|
17
|
+
}
|
|
18
|
+
/** Configure SignalR before mounting consumers. Backend switching is not supported. */
|
|
19
|
+
export declare function configureSignalR({ backendUri, enableDebug }: SignalRConfiguration): void;
|
|
20
|
+
export declare function useSignalRConnections(): SignalRConnections;
|
|
19
21
|
//#endregion
|
|
20
22
|
//#region src/helpers/useStableHandlers.d.ts
|
|
21
23
|
/**
|
|
@@ -25,15 +27,12 @@ export declare const useSignalRConnections: import("zustand").UseBoundStore<impo
|
|
|
25
27
|
*/
|
|
26
28
|
export declare function useStableHandlers<T extends Record<string, (...args: any[]) => any>>(handlers: T | undefined): T;
|
|
27
29
|
//#endregion
|
|
28
|
-
//#region src/
|
|
29
|
-
type Warning = {
|
|
30
|
-
warning: string;
|
|
31
|
-
};
|
|
30
|
+
//#region src/helpers/safeInvokeHubMethod.d.ts
|
|
32
31
|
/**
|
|
33
32
|
* Safely invokes the SignalR hub method, i.e. it will only throw in case of an actual error,
|
|
34
33
|
* and will return `Warning` object in case the connection hasn't yet been established or
|
|
35
34
|
* has been already closed.
|
|
36
35
|
*/
|
|
37
|
-
export declare function safeInvokeHubMethod(connection: HubConnection | null, methodName: string, ...args: any[]): Promise<
|
|
36
|
+
export declare function safeInvokeHubMethod(connection: HubConnection | null, methodName: string, ...args: any[]): Promise<any>;
|
|
38
37
|
//#endregion
|
|
39
|
-
export {};
|
|
38
|
+
export type { SignalRConfiguration, SignalRConnectionData, SignalRConnections };
|
package/dist/index.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import { create } from "zustand";
|
|
|
2
2
|
import * as SignalR from "@microsoft/signalr";
|
|
3
3
|
import { HubConnectionState } from "@microsoft/signalr";
|
|
4
4
|
import { useLayoutEffect, useMemo, useRef } from "react";
|
|
5
|
-
//#region src/helpers/
|
|
5
|
+
//#region src/helpers/useSignalRConnections.ts
|
|
6
6
|
function isConnectionAlive(connectionState) {
|
|
7
7
|
return connectionState && connectionState !== SignalR.HubConnectionState.Disconnected && connectionState !== SignalR.HubConnectionState.Disconnecting;
|
|
8
8
|
}
|
|
@@ -29,10 +29,8 @@ const useSignalRConnectionsStore = create((set, get) => ({
|
|
|
29
29
|
},
|
|
30
30
|
async setupEventHandlers(hubName, handlers, onConnected) {
|
|
31
31
|
let data = get().connectionsStore.get(hubName);
|
|
32
|
-
if (!data)
|
|
33
|
-
|
|
34
|
-
onConnected?.(data.connection);
|
|
35
|
-
}
|
|
32
|
+
if (!data) data = await get().createConnection(hubName);
|
|
33
|
+
onConnected?.(data.connection);
|
|
36
34
|
for (const key of Object.keys(handlers)) {
|
|
37
35
|
const handler = handlers[key];
|
|
38
36
|
if (handler !== void 0) data.connection.on(key, handler);
|
|
@@ -58,7 +56,20 @@ const useSignalRConnectionsStore = create((set, get) => ({
|
|
|
58
56
|
set({ connectionsStore });
|
|
59
57
|
}
|
|
60
58
|
}));
|
|
61
|
-
|
|
59
|
+
/** Configure SignalR before mounting consumers. Backend switching is not supported. */
|
|
60
|
+
function configureSignalR({ backendUri, enableDebug = false }) {
|
|
61
|
+
useSignalRConnectionsStore.setState({
|
|
62
|
+
backendUri,
|
|
63
|
+
EnableSignalRDebug: enableDebug
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
function useSignalRConnections() {
|
|
67
|
+
return {
|
|
68
|
+
createConnection: useSignalRConnectionsStore((state) => state.createConnection),
|
|
69
|
+
setupEventHandlers: useSignalRConnectionsStore((state) => state.setupEventHandlers),
|
|
70
|
+
deleteConnection: useSignalRConnectionsStore((state) => state.deleteConnection)
|
|
71
|
+
};
|
|
72
|
+
}
|
|
62
73
|
//#endregion
|
|
63
74
|
//#region src/helpers/useStableHandlers.ts
|
|
64
75
|
/**
|
|
@@ -81,20 +92,26 @@ function useStableHandlers(handlers) {
|
|
|
81
92
|
}, []);
|
|
82
93
|
}
|
|
83
94
|
//#endregion
|
|
84
|
-
//#region src/
|
|
95
|
+
//#region src/helpers/safeInvokeHubMethod.ts
|
|
85
96
|
/**
|
|
86
97
|
* Safely invokes the SignalR hub method, i.e. it will only throw in case of an actual error,
|
|
87
98
|
* and will return `Warning` object in case the connection hasn't yet been established or
|
|
88
99
|
* has been already closed.
|
|
89
100
|
*/
|
|
90
101
|
async function safeInvokeHubMethod(connection, methodName, ...args) {
|
|
91
|
-
if (connection?.state !== HubConnectionState.Connected)
|
|
102
|
+
if (connection?.state !== HubConnectionState.Connected) {
|
|
103
|
+
console.warn("[SignalSpec] Skipped invocation: not connected");
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
92
106
|
try {
|
|
93
107
|
return await connection.invoke(methodName, ...args);
|
|
94
108
|
} catch (err) {
|
|
95
|
-
if (err instanceof Error && err.message.includes("underlying connection being closed"))
|
|
109
|
+
if (err instanceof Error && err.message.includes("underlying connection being closed")) {
|
|
110
|
+
console.warn("[SignalSpec] Skipped invocation: connection is being closed");
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
96
113
|
throw err;
|
|
97
114
|
}
|
|
98
115
|
}
|
|
99
116
|
//#endregion
|
|
100
|
-
export { isConnectionAlive, safeInvokeHubMethod, useSignalRConnections,
|
|
117
|
+
export { configureSignalR, isConnectionAlive, safeInvokeHubMethod, useSignalRConnections, useStableHandlers };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@signal-spec/react-client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "A SignalR client generator for React based on the SignalSpec.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -48,6 +48,6 @@
|
|
|
48
48
|
"dev": "vp pack --watch",
|
|
49
49
|
"test": "vp test",
|
|
50
50
|
"check": "vp check",
|
|
51
|
-
"generate": "node ./bin/signal-spec.mjs generate"
|
|
51
|
+
"generate": "node --use-system-ca ./bin/signal-spec.mjs generate --input=https://localhost:49363/api/signal-spec.json --output=./src/result --backend-uri=https://localhost:49363"
|
|
52
52
|
}
|
|
53
53
|
}
|
|
@@ -1,5 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { configureSignalR } from "@signal-spec/react-client";
|
|
2
|
+
|
|
3
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
4
|
+
// <auto-generated>
|
|
5
|
+
// This file is auto-generated by "SignalSpec.ReactClient" (@signal-spec/react-client).
|
|
6
|
+
// Do not modify this file manually.
|
|
7
|
+
// If you want to adjust it, copy the template "config.hbs" and supply your edited version of it.
|
|
8
|
+
// </auto-generated>
|
|
9
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
2
10
|
|
|
3
11
|
export const backendUri = {{{backendUriLiteral}}};
|
|
4
12
|
export const EnableSignalRDebug = false;
|
|
5
|
-
|
|
13
|
+
configureSignalR({ backendUri, enableDebug: EnableSignalRDebug });
|
|
14
|
+
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
//--------------------------------------------------------------------------------------------------------------
|
|
2
2
|
// <auto-generated>
|
|
3
|
-
// This file is auto-generated by "SignalSpec.ReactClient".
|
|
3
|
+
// This file is auto-generated by "SignalSpec.ReactClient" (@signal-spec/react-client).
|
|
4
4
|
// Do not modify this file manually.
|
|
5
|
-
// If you want to adjust it, copy the template and supply your edited version of it.
|
|
5
|
+
// If you want to adjust it, copy the template "dtos.hbs" and supply your edited version of it.
|
|
6
6
|
// </auto-generated>
|
|
7
7
|
//--------------------------------------------------------------------------------------------------------------
|
|
8
8
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import "../config
|
|
1
|
+
import { backendUri } from "../config";
|
|
2
2
|
import type { HubConnection } from "@microsoft/signalr";
|
|
3
3
|
import { useState, useEffect } from "react";
|
|
4
4
|
{{#if dtoImports}}
|
|
5
|
-
import type { {{#each dtoImports}}{{this}}{{#unless @last}}, {{/unless}}{{/each}} } from "../DTOs
|
|
5
|
+
import type { {{#each dtoImports}}{{this}}{{#unless @last}}, {{/unless}}{{/each}} } from "../DTOs";
|
|
6
6
|
{{/if}}
|
|
7
7
|
import { useStableHandlers, useSignalRConnections{{#if (hasAnyBackendMethods hubContract.methods)}}, safeInvokeHubMethod{{/if}} } from "@signal-spec/react-client";
|
|
8
8
|
|
|
@@ -11,7 +11,7 @@ import { useStableHandlers, useSignalRConnections{{#if (hasAnyBackendMethods hub
|
|
|
11
11
|
// <auto-generated>
|
|
12
12
|
// This file is auto-generated by "SignalSpec.ReactClient" (@signal-spec/react-client).
|
|
13
13
|
// Do not modify this file manually.
|
|
14
|
-
// If you want to adjust it, copy the template and supply your edited version of it.
|
|
14
|
+
// If you want to adjust it, copy the template "hubClient.hbs" and supply your edited version of it.
|
|
15
15
|
// </auto-generated>
|
|
16
16
|
//--------------------------------------------------------------------------------------------------------------
|
|
17
17
|
|
|
@@ -50,64 +50,61 @@ interface {{clientName}}Backend {
|
|
|
50
50
|
|
|
51
51
|
export function use{{hubName}}(
|
|
52
52
|
{{#if (hasAnyFrontendMethods hubContract.methods)}}handlers: Partial<{{clientName}}Frontend> = {},{{/if}}
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
enabled: boolean = true,
|
|
54
|
+
): { {{#if (hasAnyBackendMethods hubContract.methods)}}invoker: {{clientName}}Methods;{{/if}} connection: HubConnection | null; } {
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
56
|
+
const { setupEventHandlers, deleteConnection } = useSignalRConnections();
|
|
57
|
+
const [connection, setConnection] = useState<HubConnection | null>(
|
|
58
|
+
null,
|
|
59
|
+
);
|
|
60
60
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
61
|
+
const stableHandlers = useStableHandlers(handlers);
|
|
62
|
+
const onConnected = (connection: HubConnection) =>
|
|
63
|
+
setConnection(connection);
|
|
64
64
|
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
if (!enabled) return;
|
|
67
67
|
|
|
68
|
-
|
|
69
|
-
onConnected(data.connection);
|
|
70
|
-
setupEventHandlers('{{hubName}}', stableHandlers);
|
|
68
|
+
setupEventHandlers('{{hubName}}', stableHandlers, onConnected);
|
|
71
69
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
};
|
|
75
|
-
}, [enabled]);
|
|
76
|
-
|
|
77
|
-
{{#if (hasAnyBackendMethods hubContract.methods)}}
|
|
78
|
-
const invoker = {
|
|
79
|
-
{{#each hubContract.methods}}
|
|
80
|
-
{{#if (isBackendMethod this)}}
|
|
81
|
-
{{name}}: async (
|
|
82
|
-
{{#if parameters}}
|
|
83
|
-
{{#each parameters}}
|
|
84
|
-
{{name}}: {{safeType this}}{{#unless @last}}, {{/unless}}
|
|
85
|
-
{{/each}}
|
|
86
|
-
{{/if}}
|
|
87
|
-
) => {
|
|
88
|
-
{{#if returns}}
|
|
89
|
-
return await safeInvokeHubMethod(connection,
|
|
90
|
-
"{{name}}"
|
|
91
|
-
{{#if parameters}}, {{/if}}
|
|
92
|
-
{{#each parameters}}
|
|
93
|
-
{{name}}{{#unless @last}}, {{/unless}}
|
|
94
|
-
{{/each}}
|
|
95
|
-
) as {{safeType returns}};
|
|
96
|
-
{{else}}
|
|
97
|
-
await safeInvokeHubMethod(connection,
|
|
98
|
-
"{{name}}"
|
|
99
|
-
{{#if parameters}}, {{/if}}
|
|
100
|
-
{{#each parameters}}
|
|
101
|
-
{{name}}{{#unless @last}}, {{/unless}}
|
|
102
|
-
{{/each}}
|
|
103
|
-
);
|
|
104
|
-
{{/if}}
|
|
105
|
-
}{{#unless @last}},{{/unless}}
|
|
106
|
-
{{/if}}
|
|
107
|
-
{{/each}}
|
|
70
|
+
return () => {
|
|
71
|
+
deleteConnection('{{hubName}}', stableHandlers);
|
|
108
72
|
};
|
|
109
|
-
|
|
73
|
+
}, [backendUri, enabled]);
|
|
110
74
|
|
|
111
|
-
|
|
112
|
-
|
|
75
|
+
{{#if (hasAnyBackendMethods hubContract.methods)}}
|
|
76
|
+
const invoker = {
|
|
77
|
+
{{#each hubContract.methods}}
|
|
78
|
+
{{#if (isBackendMethod this)}}
|
|
79
|
+
{{name}}: async (
|
|
80
|
+
{{#if parameters}}
|
|
81
|
+
{{#each parameters}}
|
|
82
|
+
{{name}}: {{safeType this}}{{#unless @last}}, {{/unless}}
|
|
83
|
+
{{/each}}
|
|
84
|
+
{{/if}}
|
|
85
|
+
) => {
|
|
86
|
+
{{#if returns}}
|
|
87
|
+
return await safeInvokeHubMethod(connection,
|
|
88
|
+
'{{name}}'
|
|
89
|
+
{{#if parameters}}, {{/if}}
|
|
90
|
+
{{#each parameters}}
|
|
91
|
+
{{name}}{{#unless @last}}, {{/unless}}
|
|
92
|
+
{{/each}}
|
|
93
|
+
) as {{safeType returns}};
|
|
94
|
+
{{else}}
|
|
95
|
+
await safeInvokeHubMethod(connection,
|
|
96
|
+
'{{name}}'
|
|
97
|
+
{{#if parameters}}, {{/if}}
|
|
98
|
+
{{#each parameters}}
|
|
99
|
+
{{name}}{{#unless @last}}, {{/unless}}
|
|
100
|
+
{{/each}}
|
|
101
|
+
);
|
|
102
|
+
{{/if}}
|
|
103
|
+
}{{#unless @last}},{{/unless}}
|
|
104
|
+
{{/if}}
|
|
105
|
+
{{/each}}
|
|
106
|
+
};
|
|
107
|
+
{{/if}}
|
|
113
108
|
|
|
109
|
+
return { {{#if (hasAnyBackendMethods hubContract.methods)}}invoker,{{/if}} connection };
|
|
110
|
+
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { HubConnectionState, type HubConnection } from "@microsoft/signalr";
|
|
2
|
-
|
|
3
|
-
type Warning = { warning: string };
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Safely invokes the SignalR hub method, i.e. it will only throw in case of an actual error,
|
|
7
|
-
* and will return `Warning` object in case the connection hasn't yet been established or
|
|
8
|
-
* has been already closed.
|
|
9
|
-
*/
|
|
10
|
-
export async function safeInvokeHubMethod(
|
|
11
|
-
connection: HubConnection | null,
|
|
12
|
-
methodName: string,
|
|
13
|
-
...args: any[]
|
|
14
|
-
): Promise<Warning | any> {
|
|
15
|
-
if (connection?.state !== HubConnectionState.Connected) {
|
|
16
|
-
return { warning: "Skipped invocation: not connected" };
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
try {
|
|
20
|
-
return await connection.invoke(methodName, ...args);
|
|
21
|
-
} catch (err) {
|
|
22
|
-
if (err instanceof Error && err.message.includes("underlying connection being closed")) {
|
|
23
|
-
return { warning: "Skipped invocation: connection is being closed" };
|
|
24
|
-
}
|
|
25
|
-
throw err;
|
|
26
|
-
}
|
|
27
|
-
}
|