@equinor/fusion-framework-module-signalr 14.0.0 → 14.0.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.
- package/dist/esm/version.js +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/version.d.ts +1 -1
- package/package.json +10 -7
- package/CHANGELOG.md +0 -605
- package/src/SignalRConfigurator.ts +0 -66
- package/src/SignalRModule.ts +0 -55
- package/src/SignalRModuleConfigBuilder.ts +0 -112
- package/src/SignalRModuleProvider.ts +0 -142
- package/src/index.ts +0 -31
- package/src/lib/Topic.ts +0 -79
- package/src/lib/utils/configure-from-framework.ts +0 -47
- package/src/lib/utils/enable-signalr.ts +0 -95
- package/src/version.ts +0 -2
- package/tsconfig.json +0 -21
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import type { ModuleInitializerArgs } from '@equinor/fusion-framework-module';
|
|
2
|
-
|
|
3
|
-
import type { ServiceDiscoveryModule } from '@equinor/fusion-framework-module-service-discovery';
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
type ISignalRConfigurator,
|
|
7
|
-
SignalRModuleConfigBuilder,
|
|
8
|
-
type SignalRConfig,
|
|
9
|
-
type SignalRHubConfig,
|
|
10
|
-
type SignalRModuleConfigBuilderCallback,
|
|
11
|
-
} from './SignalRModuleConfigBuilder';
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Default {@link ISignalRConfigurator} implementation.
|
|
15
|
-
*
|
|
16
|
-
* Collects hub registrations and builder callbacks, then produces a
|
|
17
|
-
* {@link SignalRConfig} during the module initialization phase.
|
|
18
|
-
*/
|
|
19
|
-
export class SignalRConfigurator implements ISignalRConfigurator {
|
|
20
|
-
#builderCallbacks: Array<SignalRModuleConfigBuilderCallback> = [];
|
|
21
|
-
|
|
22
|
-
#hubs: Record<string, SignalRHubConfig> = {};
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Register a named SignalR hub connection.
|
|
26
|
-
*
|
|
27
|
-
* @param name - Unique identifier for the hub
|
|
28
|
-
* @param config - Hub connection configuration
|
|
29
|
-
*/
|
|
30
|
-
public addHub(name: string, config: SignalRHubConfig) {
|
|
31
|
-
this.#hubs[name] = config;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Register a configuration builder callback that will run during
|
|
36
|
-
* {@link SignalRConfigurator.createConfig}.
|
|
37
|
-
*
|
|
38
|
-
* @param cb - Callback receiving a {@link SignalRModuleConfigBuilder}
|
|
39
|
-
* @template T - Type of module dependencies made available to the builder callback
|
|
40
|
-
*/
|
|
41
|
-
public onCreateConfig<T>(cb: SignalRModuleConfigBuilderCallback<T>): void {
|
|
42
|
-
this.#builderCallbacks.push(cb);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Build the final {@link SignalRConfig} by executing all registered
|
|
47
|
-
* builder callbacks and collecting hub configurations.
|
|
48
|
-
*
|
|
49
|
-
* Normally called during the module `initialize` phase.
|
|
50
|
-
*
|
|
51
|
-
* @param init - Module initializer arguments providing access to resolved dependencies
|
|
52
|
-
* @returns Resolved configuration containing all registered hubs
|
|
53
|
-
*/
|
|
54
|
-
public async createConfig(
|
|
55
|
-
init: ModuleInitializerArgs<ISignalRConfigurator, [ServiceDiscoveryModule]>,
|
|
56
|
-
): Promise<SignalRConfig> {
|
|
57
|
-
/** trigger all builder callbacks */
|
|
58
|
-
for (const cb of this.#builderCallbacks) {
|
|
59
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
60
|
-
const builder = new SignalRModuleConfigBuilder<[ServiceDiscoveryModule]>(init, this);
|
|
61
|
-
await Promise.resolve(cb(builder));
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return { hubs: this.#hubs };
|
|
65
|
-
}
|
|
66
|
-
}
|
package/src/SignalRModule.ts
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import type { Module } from '@equinor/fusion-framework-module';
|
|
2
|
-
import type { ISignalRConfigurator } from './SignalRModuleConfigBuilder';
|
|
3
|
-
import { SignalRConfigurator } from './SignalRConfigurator';
|
|
4
|
-
|
|
5
|
-
import { type ISignalRProvider, SignalRModuleProvider } from './SignalRModuleProvider';
|
|
6
|
-
|
|
7
|
-
/** String literal key used to register the SignalR module in the Fusion Framework module system. */
|
|
8
|
-
export type SignalRModuleKey = 'signalR';
|
|
9
|
-
|
|
10
|
-
/** Module registration key for the SignalR module (`'signalR'`). */
|
|
11
|
-
export const moduleKey: SignalRModuleKey = 'signalR';
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Module type definition for the SignalR module.
|
|
15
|
-
*
|
|
16
|
-
* Binds the module key, provider interface ({@link ISignalRProvider}), and
|
|
17
|
-
* configurator interface ({@link ISignalRConfigurator}) together for the
|
|
18
|
-
* Fusion Framework module system.
|
|
19
|
-
*/
|
|
20
|
-
export type SignalRModule = Module<SignalRModuleKey, ISignalRProvider, ISignalRConfigurator>;
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* SignalR module instance that can be registered with a Fusion Framework configurator.
|
|
24
|
-
*
|
|
25
|
-
* During the `configure` phase, a {@link SignalRConfigurator} is created.
|
|
26
|
-
* During the `initialize` phase, the configurator builds its {@link SignalRConfig}
|
|
27
|
-
* and produces a {@link SignalRModuleProvider}.
|
|
28
|
-
*
|
|
29
|
-
* @example
|
|
30
|
-
* ```ts
|
|
31
|
-
* import { ModuleConfigurator } from '@equinor/fusion-framework-module';
|
|
32
|
-
* import signalR from '@equinor/fusion-framework-module-signalr';
|
|
33
|
-
*
|
|
34
|
-
* configurator.addConfig({ module: signalR });
|
|
35
|
-
* ```
|
|
36
|
-
*/
|
|
37
|
-
// Deliberately co-located with the `moduleKey` constant it references
|
|
38
|
-
// fusion-lint-disable-next-line single-export-per-file
|
|
39
|
-
export const module: SignalRModule = {
|
|
40
|
-
name: moduleKey,
|
|
41
|
-
configure: () => new SignalRConfigurator(),
|
|
42
|
-
initialize: async (init) => {
|
|
43
|
-
const config = await (init.config as SignalRConfigurator).createConfig(init);
|
|
44
|
-
return new SignalRModuleProvider(config);
|
|
45
|
-
},
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
export default module;
|
|
49
|
-
|
|
50
|
-
/** Augments the global `Modules` interface so that `'signalR'` is a known module key. */
|
|
51
|
-
declare module '@equinor/fusion-framework-module' {
|
|
52
|
-
interface Modules {
|
|
53
|
-
signalR: SignalRModule;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
import { type AnyModule, ModuleConfigBuilder } from '@equinor/fusion-framework-module';
|
|
2
|
-
|
|
3
|
-
import type { IHttpConnectionOptions, LogLevel } from '@microsoft/signalr';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Public configuration interface for the SignalR module.
|
|
7
|
-
*
|
|
8
|
-
* Consumers use this interface to register hub connections and configuration
|
|
9
|
-
* callbacks during the module `configure` phase.
|
|
10
|
-
*/
|
|
11
|
-
export interface ISignalRConfigurator {
|
|
12
|
-
/**
|
|
13
|
-
* Register a named SignalR hub connection.
|
|
14
|
-
*
|
|
15
|
-
* @param name - Unique identifier for the hub (used later with {@link ISignalRProvider.connect})
|
|
16
|
-
* @param config - Hub connection configuration or a promise that resolves to one
|
|
17
|
-
*/
|
|
18
|
-
addHub(name: string, config: SignalRHubConfig | Promise<SignalRHubConfig>): void;
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Register a callback that will run during the module initialization phase
|
|
22
|
-
* to build configuration using a {@link SignalRModuleConfigBuilder}.
|
|
23
|
-
*
|
|
24
|
-
* Use this when hub configuration depends on resolved module dependencies
|
|
25
|
-
* such as service-discovery or authentication.
|
|
26
|
-
*
|
|
27
|
-
* @param cb - Callback that receives a {@link SignalRModuleConfigBuilder}
|
|
28
|
-
*/
|
|
29
|
-
onCreateConfig(cb: SignalRModuleConfigBuilderCallback): void;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Callback invoked during module initialization to configure SignalR hubs.
|
|
34
|
-
*
|
|
35
|
-
* Receives a {@link SignalRModuleConfigBuilder} that provides access to
|
|
36
|
-
* resolved module dependencies, allowing dynamic hub registration.
|
|
37
|
-
*
|
|
38
|
-
* @template TDeps - Tuple of module dependencies available through the builder
|
|
39
|
-
*/
|
|
40
|
-
export type SignalRModuleConfigBuilderCallback<TDeps = unknown> = (
|
|
41
|
-
builder: SignalRModuleConfigBuilder<TDeps>,
|
|
42
|
-
) => void | Promise<void>;
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Configuration for a single SignalR hub connection.
|
|
46
|
-
*
|
|
47
|
-
* Defines the endpoint URL, transport options, reconnection behavior,
|
|
48
|
-
* and logging level for a `@microsoft/signalr` `HubConnection`.
|
|
49
|
-
*/
|
|
50
|
-
export type SignalRHubConfig = {
|
|
51
|
-
/** Absolute URL of the SignalR hub endpoint. */
|
|
52
|
-
url: string;
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Transport and authentication options forwarded to the underlying
|
|
56
|
-
* `HubConnectionBuilder.withUrl()` call.
|
|
57
|
-
*
|
|
58
|
-
* The `httpClient` option is excluded because the module manages the
|
|
59
|
-
* HTTP client internally.
|
|
60
|
-
*/
|
|
61
|
-
options: Omit<IHttpConnectionOptions, 'httpClient'>;
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* When `true`, the connection will automatically attempt to reconnect
|
|
65
|
-
* after an unintentional disconnection.
|
|
66
|
-
*
|
|
67
|
-
* @defaultValue `undefined` (no automatic reconnect)
|
|
68
|
-
*/
|
|
69
|
-
automaticReconnect?: boolean;
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Minimum log level for the SignalR connection logger.
|
|
73
|
-
*
|
|
74
|
-
* @defaultValue `LogLevel.Critical` (5) when omitted
|
|
75
|
-
*/
|
|
76
|
-
logLevel?: LogLevel;
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Resolved configuration used to create a {@link SignalRModuleProvider}.
|
|
81
|
-
*
|
|
82
|
-
* Contains all registered hub configurations keyed by hub name.
|
|
83
|
-
*/
|
|
84
|
-
export type SignalRConfig = {
|
|
85
|
-
/** Map of hub name to its {@link SignalRHubConfig}. */
|
|
86
|
-
hubs: Record<string, SignalRHubConfig>;
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Builder utility for registering SignalR hub configurations during
|
|
91
|
-
* module initialization.
|
|
92
|
-
*
|
|
93
|
-
* Extends {@link ModuleConfigBuilder} to provide access to resolved
|
|
94
|
-
* module dependencies (e.g., service-discovery, authentication) so that
|
|
95
|
-
* hub URLs and access-token factories can be built dynamically.
|
|
96
|
-
*
|
|
97
|
-
* @template TDeps - Tuple of module dependencies available through the builder
|
|
98
|
-
*/
|
|
99
|
-
export class SignalRModuleConfigBuilder<
|
|
100
|
-
TDeps extends AnyModule[] | unknown = unknown,
|
|
101
|
-
// TODO(#5096): use BaseConfigBuilder
|
|
102
|
-
> extends ModuleConfigBuilder<TDeps, ISignalRConfigurator> {
|
|
103
|
-
/**
|
|
104
|
-
* Register a named hub connection through the underlying configurator.
|
|
105
|
-
*
|
|
106
|
-
* @param name - Unique hub identifier
|
|
107
|
-
* @param config - Hub connection configuration
|
|
108
|
-
*/
|
|
109
|
-
async addHub(name: string, config: SignalRHubConfig) {
|
|
110
|
-
this._config.addHub(name, config);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
import { HubConnectionBuilder, type HubConnection, AbortError } from '@microsoft/signalr';
|
|
2
|
-
import { Observable, shareReplay } from 'rxjs';
|
|
3
|
-
|
|
4
|
-
import type { SignalRConfig } from './SignalRModuleConfigBuilder';
|
|
5
|
-
|
|
6
|
-
import { Topic } from './lib/Topic';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Public interface for the SignalR module provider.
|
|
10
|
-
*
|
|
11
|
-
* Use {@link ISignalRProvider.connect} to subscribe to a named hub method
|
|
12
|
-
* through an RxJS-based {@link Topic}. Connections are reference-counted
|
|
13
|
-
* and automatically torn down when all subscribers unsubscribe.
|
|
14
|
-
*/
|
|
15
|
-
export interface ISignalRProvider {
|
|
16
|
-
/**
|
|
17
|
-
* Connect to a SignalR hub method and return an observable {@link Topic}.
|
|
18
|
-
*
|
|
19
|
-
* Existing hub connections are reused (shared via `shareReplay` with
|
|
20
|
-
* `refCount`). When the last subscriber unsubscribes, the underlying
|
|
21
|
-
* `HubConnection` is stopped automatically.
|
|
22
|
-
*
|
|
23
|
-
* @template T - Type of messages received from the hub method
|
|
24
|
-
* @param hubId - Name of the hub as registered in the configurator
|
|
25
|
-
* @param methodName - Server-side method name to listen on
|
|
26
|
-
* @returns A {@link Topic} observable that emits messages from the hub method
|
|
27
|
-
*
|
|
28
|
-
* @example
|
|
29
|
-
* ```ts
|
|
30
|
-
* const provider = modules.signalR;
|
|
31
|
-
* const topic = provider.connect<MyMessage>('notifications', 'OnNewMessage');
|
|
32
|
-
* topic.subscribe((msg) => console.log('Received:', msg));
|
|
33
|
-
* ```
|
|
34
|
-
*/
|
|
35
|
-
connect<T>(hubId: string, methodName: string): Topic<T>;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Default {@link ISignalRProvider} implementation.
|
|
40
|
-
*
|
|
41
|
-
* Creates and manages `@microsoft/signalr` `HubConnection` instances based on
|
|
42
|
-
* the resolved {@link SignalRConfig}. Hub connections are lazily created on
|
|
43
|
-
* first subscription and shared across all callers via `shareReplay`.
|
|
44
|
-
*/
|
|
45
|
-
export class SignalRModuleProvider implements ISignalRProvider {
|
|
46
|
-
#config: SignalRConfig;
|
|
47
|
-
#hubConnections: Record<string, Observable<HubConnection>> = {};
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* @param config - Resolved SignalR configuration containing all registered hubs
|
|
51
|
-
*/
|
|
52
|
-
constructor(config: SignalRConfig) {
|
|
53
|
-
this.#config = config;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Connect to a named hub method and return an observable {@link Topic}.
|
|
58
|
-
*
|
|
59
|
-
* @template T - Type of messages received from the hub method
|
|
60
|
-
* @param hubId - Name of the hub as registered in the configurator
|
|
61
|
-
* @param methodName - Server-side method name to listen on
|
|
62
|
-
* @returns A {@link Topic} observable emitting hub messages
|
|
63
|
-
* @throws {Error} When no hub configuration exists for `hubId`
|
|
64
|
-
*/
|
|
65
|
-
public connect<T>(hubId: string, methodName: string): Topic<T> {
|
|
66
|
-
return new Topic<T>(methodName, this._createHubConnection(hubId));
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Create or retrieve a shared `HubConnection` observable for the given hub.
|
|
71
|
-
*
|
|
72
|
-
* The connection is lazily built using `HubConnectionBuilder` and shared
|
|
73
|
-
* with `shareReplay({ bufferSize: 1, refCount: true })` so that:
|
|
74
|
-
* - New subscribers immediately receive the current connection.
|
|
75
|
-
* - The connection is stopped when the last subscriber unsubscribes.
|
|
76
|
-
*
|
|
77
|
-
* @param hubId - Name of the hub as registered in the configurator
|
|
78
|
-
* @returns Observable that emits the active `HubConnection`
|
|
79
|
-
* @throws {Error} When no hub configuration exists for `hubId`
|
|
80
|
-
*/
|
|
81
|
-
protected _createHubConnection(hubId: string): Observable<HubConnection> {
|
|
82
|
-
const LOG_LEVEL_CRITICAL = 5;
|
|
83
|
-
|
|
84
|
-
// Reuse an already-established connection observable for this hub
|
|
85
|
-
if (hubId in this.#hubConnections) {
|
|
86
|
-
return this.#hubConnections[hubId];
|
|
87
|
-
}
|
|
88
|
-
const config = this.#config.hubs[hubId];
|
|
89
|
-
// Fail loudly when the hub was never registered with the configurator
|
|
90
|
-
if (!config) {
|
|
91
|
-
throw Error(`could not find any configuration for hub [${hubId}]`);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
this.#hubConnections[hubId] = new Observable<HubConnection>((observer) => {
|
|
95
|
-
const builder = new HubConnectionBuilder().withUrl(config.url, {
|
|
96
|
-
...config.options,
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
config.automaticReconnect && builder.withAutomaticReconnect();
|
|
100
|
-
|
|
101
|
-
builder.configureLogging(config.logLevel || LOG_LEVEL_CRITICAL);
|
|
102
|
-
|
|
103
|
-
const connection = builder.build();
|
|
104
|
-
|
|
105
|
-
connection
|
|
106
|
-
.start()
|
|
107
|
-
.then(() => {
|
|
108
|
-
observer.next(connection);
|
|
109
|
-
})
|
|
110
|
-
.catch((error: unknown) => {
|
|
111
|
-
// Swallow expected teardown aborts; re-throw anything else
|
|
112
|
-
if (error instanceof AbortError) {
|
|
113
|
-
// AbortError is expected during teardown — safe to ignore
|
|
114
|
-
} else {
|
|
115
|
-
throw error;
|
|
116
|
-
}
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
// Stop the connection and clean up the cache entry on unsubscribe
|
|
120
|
-
const teardown = () => {
|
|
121
|
-
connection.stop();
|
|
122
|
-
observer.complete();
|
|
123
|
-
delete this.#hubConnections[hubId];
|
|
124
|
-
};
|
|
125
|
-
|
|
126
|
-
return teardown;
|
|
127
|
-
})
|
|
128
|
-
// Share a single connection across subscribers and tear it down when unused
|
|
129
|
-
.pipe(
|
|
130
|
-
shareReplay({
|
|
131
|
-
/** only emit last connection when new subscriber connects */
|
|
132
|
-
bufferSize: 1,
|
|
133
|
-
/** when no subscribers, teardown observable */
|
|
134
|
-
refCount: true,
|
|
135
|
-
}),
|
|
136
|
-
);
|
|
137
|
-
|
|
138
|
-
return this.#hubConnections[hubId];
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
export default SignalRModuleProvider;
|
package/src/index.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @packageDocumentation
|
|
3
|
-
*
|
|
4
|
-
* Fusion Framework module for real-time communication via
|
|
5
|
-
* [SignalR](https://learn.microsoft.com/aspnet/core/signalr/introduction).
|
|
6
|
-
*
|
|
7
|
-
* Provides an RxJS-based API for connecting to SignalR hubs, subscribing to
|
|
8
|
-
* server-side methods, and sending messages. Hub connections are reference-counted
|
|
9
|
-
* and automatically stopped when no subscribers remain.
|
|
10
|
-
*
|
|
11
|
-
* @see {@link enableSignalR} for the quickest way to register the module.
|
|
12
|
-
* @see {@link ISignalRProvider.connect} for subscribing to hub methods at runtime.
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
export {
|
|
16
|
-
ISignalRConfigurator,
|
|
17
|
-
SignalRConfig,
|
|
18
|
-
SignalRHubConfig,
|
|
19
|
-
SignalRModuleConfigBuilder,
|
|
20
|
-
SignalRModuleConfigBuilderCallback,
|
|
21
|
-
} from './SignalRModuleConfigBuilder';
|
|
22
|
-
|
|
23
|
-
export { SignalRConfigurator } from './SignalRConfigurator';
|
|
24
|
-
|
|
25
|
-
export { ISignalRProvider, SignalRModuleProvider } from './SignalRModuleProvider';
|
|
26
|
-
|
|
27
|
-
export { Topic } from './lib/Topic';
|
|
28
|
-
|
|
29
|
-
export { enableSignalR } from './lib/utils/enable-signalr';
|
|
30
|
-
|
|
31
|
-
export { default, module, moduleKey, SignalRModule, SignalRModuleKey } from './SignalRModule';
|
package/src/lib/Topic.ts
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import type { HubConnection } from '@microsoft/signalr';
|
|
2
|
-
import { Observable } from 'rxjs';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* RxJS Observable wrapper around a SignalR hub method.
|
|
6
|
-
*
|
|
7
|
-
* A `Topic` subscribes to a named method on a `HubConnection` and emits
|
|
8
|
-
* incoming messages as observable values. It also exposes {@link Topic.send}
|
|
9
|
-
* and {@link Topic.invoke} for sending messages back to the server.
|
|
10
|
-
*
|
|
11
|
-
* Created by {@link SignalRModuleProvider.connect} — consumers typically
|
|
12
|
-
* do not instantiate `Topic` directly.
|
|
13
|
-
*
|
|
14
|
-
* @template T - Type of messages received from the hub method
|
|
15
|
-
*
|
|
16
|
-
* @example
|
|
17
|
-
* ```ts
|
|
18
|
-
* const topic = provider.connect<ChatMessage>('chat', 'ReceiveMessage');
|
|
19
|
-
* topic.subscribe((msg) => console.log(msg.text));
|
|
20
|
-
*
|
|
21
|
-
* // Send a message to the server on the same method
|
|
22
|
-
* topic.send('Hello, world!');
|
|
23
|
-
* ```
|
|
24
|
-
*/
|
|
25
|
-
export class Topic<T> extends Observable<T> {
|
|
26
|
-
/** The active hub connection, set once the connection observable emits. */
|
|
27
|
-
connection: HubConnection | undefined;
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* @param topic - Server-side method name to listen on and send to
|
|
31
|
-
* @param hubConnection - Observable that emits the active `HubConnection`
|
|
32
|
-
*/
|
|
33
|
-
constructor(
|
|
34
|
-
public topic: string,
|
|
35
|
-
public hubConnection: Observable<HubConnection>,
|
|
36
|
-
) {
|
|
37
|
-
super((subscriber) => {
|
|
38
|
-
const hubConnectionSubscription = hubConnection.subscribe((connection) => {
|
|
39
|
-
const cb = subscriber.next.bind(subscriber);
|
|
40
|
-
connection.on(topic, cb);
|
|
41
|
-
subscriber.add(() => connection.off(topic, cb));
|
|
42
|
-
this.connection = connection;
|
|
43
|
-
});
|
|
44
|
-
subscriber.add(() => {
|
|
45
|
-
hubConnectionSubscription.unsubscribe();
|
|
46
|
-
});
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Send a fire-and-forget message to the server on this topic.
|
|
52
|
-
*
|
|
53
|
-
* @param args - Arguments forwarded to `HubConnection.send()`
|
|
54
|
-
* @throws {Error} When the hub connection has not been established yet
|
|
55
|
-
*/
|
|
56
|
-
public send(...args: unknown[]): void {
|
|
57
|
-
// A connection must have been established before sending is possible
|
|
58
|
-
if (!this.connection) {
|
|
59
|
-
throw new Error('No hub connection awaitable');
|
|
60
|
-
}
|
|
61
|
-
this.connection.send(this.topic, args);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Invoke a server method on this topic and wait for a response.
|
|
66
|
-
*
|
|
67
|
-
* @template T - Expected return type from the server method
|
|
68
|
-
* @param args - Arguments forwarded to `HubConnection.invoke()`
|
|
69
|
-
* @returns Promise resolving with the server's response
|
|
70
|
-
* @throws {Error} When the hub connection has not been established yet
|
|
71
|
-
*/
|
|
72
|
-
public invoke<T>(...args: unknown[]): Promise<T> {
|
|
73
|
-
// A connection must have been established before invoking is possible
|
|
74
|
-
if (!this.connection) {
|
|
75
|
-
throw new Error('No hub connection awaitable');
|
|
76
|
-
}
|
|
77
|
-
return this.connection?.invoke(this.topic, args);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import type { MsalModule } from '@equinor/fusion-framework-module-msal';
|
|
2
|
-
import type { ServiceDiscoveryModule } from '@equinor/fusion-framework-module-service-discovery';
|
|
3
|
-
|
|
4
|
-
import type { SignalRModuleConfigBuilder } from '../../SignalRModuleConfigBuilder';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Configure a SignalR hub connection using Fusion Framework service-discovery
|
|
8
|
-
* and MSAL authentication.
|
|
9
|
-
*
|
|
10
|
-
* Resolves the hub endpoint URL from the service registry and creates an
|
|
11
|
-
* `accessTokenFactory` that acquires tokens via the MSAL auth provider.
|
|
12
|
-
*
|
|
13
|
-
* @param args - Hub name, service identifier, and path to append to the resolved service URI
|
|
14
|
-
* @param builder - Module config builder with access to MSAL and service-discovery instances
|
|
15
|
-
*
|
|
16
|
-
* @internal
|
|
17
|
-
*/
|
|
18
|
-
export const configureFromFramework = async (
|
|
19
|
-
args: { name: string; service: string; path: string },
|
|
20
|
-
builder: SignalRModuleConfigBuilder<[MsalModule, ServiceDiscoveryModule]>,
|
|
21
|
-
) => {
|
|
22
|
-
const authProvider = await builder.requireInstance('auth');
|
|
23
|
-
const serviceDiscovery = await builder.requireInstance('serviceDiscovery');
|
|
24
|
-
const service = await serviceDiscovery.resolveService(args.name);
|
|
25
|
-
builder.addHub(args.name, {
|
|
26
|
-
url: new URL(args.path, service.uri).toString(),
|
|
27
|
-
options: {
|
|
28
|
-
accessTokenFactory: async () => {
|
|
29
|
-
// Scopes are required to acquire a token for this service
|
|
30
|
-
if (!service.scopes) {
|
|
31
|
-
throw Error(
|
|
32
|
-
`service [${service.name}] does not have authentication scopes, please configure an endpoint with scopes`,
|
|
33
|
-
);
|
|
34
|
-
}
|
|
35
|
-
const token = await authProvider.acquireAccessToken({
|
|
36
|
-
request: { scopes: service.scopes ?? service.defaultScopes },
|
|
37
|
-
});
|
|
38
|
-
// Fail loudly rather than connecting the hub without a valid token
|
|
39
|
-
if (!token) {
|
|
40
|
-
throw Error('failed to acquire access token');
|
|
41
|
-
}
|
|
42
|
-
return token;
|
|
43
|
-
},
|
|
44
|
-
},
|
|
45
|
-
automaticReconnect: true,
|
|
46
|
-
});
|
|
47
|
-
};
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import type { IModulesConfigurator } from '@equinor/fusion-framework-module';
|
|
2
|
-
import type { SignalRModuleConfigBuilderCallback } from '../../SignalRModuleConfigBuilder';
|
|
3
|
-
import { module } from '../../SignalRModule';
|
|
4
|
-
import { configureFromFramework } from './configure-from-framework';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Call-signature overloads for {@link enableSignalR}.
|
|
8
|
-
*/
|
|
9
|
-
export interface enableSignalR {
|
|
10
|
-
/**
|
|
11
|
-
* Enable SignalR with a custom configuration builder callback.
|
|
12
|
-
*
|
|
13
|
-
* @param configurator - The module configurator instance
|
|
14
|
-
* @param name - Hub name identifier
|
|
15
|
-
* @param cb - Builder callback for manual hub configuration
|
|
16
|
-
*/
|
|
17
|
-
(
|
|
18
|
-
// biome-ignore lint/suspicious/noExplicitAny: IModulesConfigurator<any, any> widens to accept a configurator for any concrete module set
|
|
19
|
-
configurator: IModulesConfigurator<any, any>,
|
|
20
|
-
name: string,
|
|
21
|
-
cb: SignalRModuleConfigBuilderCallback,
|
|
22
|
-
): void;
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Enable SignalR using service-discovery to resolve the hub URL automatically.
|
|
26
|
-
*
|
|
27
|
-
* @param configurator - The module configurator instance
|
|
28
|
-
* @param name - Hub name identifier
|
|
29
|
-
* @param options - Service name and path used to resolve the hub endpoint
|
|
30
|
-
*/
|
|
31
|
-
(
|
|
32
|
-
// biome-ignore lint/suspicious/noExplicitAny: IModulesConfigurator<any, any> widens to accept a configurator for any concrete module set
|
|
33
|
-
configurator: IModulesConfigurator<any, any>,
|
|
34
|
-
name: string,
|
|
35
|
-
options: { service: string; path: string },
|
|
36
|
-
): void;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Register the SignalR module on a Fusion Framework configurator and add hub
|
|
41
|
-
* configuration in a single call.
|
|
42
|
-
*
|
|
43
|
-
* Accepts either a manual builder callback or a service-discovery shorthand.
|
|
44
|
-
* When the shorthand form is used, the hub URL and authentication token are
|
|
45
|
-
* resolved automatically via the service-discovery and MSAL modules.
|
|
46
|
-
*
|
|
47
|
-
* @param configurator - The module configurator to register the SignalR module on
|
|
48
|
-
* @param name - Hub name identifier (used as key in the hub registry)
|
|
49
|
-
* @param optionsOrCallback - A builder callback for custom configuration, or
|
|
50
|
-
* `{ service, path }` to resolve the hub endpoint through service-discovery
|
|
51
|
-
*
|
|
52
|
-
* @example
|
|
53
|
-
* ```ts
|
|
54
|
-
* import { enableSignalR } from '@equinor/fusion-framework-module-signalr';
|
|
55
|
-
*
|
|
56
|
-
* // Using service-discovery shorthand
|
|
57
|
-
* enableSignalR(configurator, 'portal', {
|
|
58
|
-
* service: 'portal',
|
|
59
|
-
* path: '/signalr/hubs/service-message',
|
|
60
|
-
* });
|
|
61
|
-
*
|
|
62
|
-
* // Using a custom builder callback
|
|
63
|
-
* enableSignalR(configurator, 'custom', (builder) => {
|
|
64
|
-
* builder.addHub('custom', {
|
|
65
|
-
* url: 'https://my-service.example.com/hub',
|
|
66
|
-
* options: { accessTokenFactory: () => getToken() },
|
|
67
|
-
* });
|
|
68
|
-
* });
|
|
69
|
-
* ```
|
|
70
|
-
*/
|
|
71
|
-
export function enableSignalR(
|
|
72
|
-
// biome-ignore lint/suspicious/noExplicitAny: IModulesConfigurator<any, any> widens to accept a configurator for any concrete module set
|
|
73
|
-
configurator: IModulesConfigurator<any, any>,
|
|
74
|
-
name: string,
|
|
75
|
-
optionsOrCallback: SignalRModuleConfigBuilderCallback | { service: string; path: string },
|
|
76
|
-
) {
|
|
77
|
-
// Callback form configures the hub directly against the resolved builder
|
|
78
|
-
if (typeof optionsOrCallback === 'function') {
|
|
79
|
-
configurator.addConfig({
|
|
80
|
-
module,
|
|
81
|
-
configure: (signalRConfigurator) => {
|
|
82
|
-
signalRConfigurator.onCreateConfig(optionsOrCallback);
|
|
83
|
-
},
|
|
84
|
-
});
|
|
85
|
-
} else {
|
|
86
|
-
configurator.addConfig({
|
|
87
|
-
module,
|
|
88
|
-
configure: (signalRConfigurator) => {
|
|
89
|
-
signalRConfigurator.onCreateConfig((builder) =>
|
|
90
|
-
configureFromFramework({ name, ...optionsOrCallback }, builder),
|
|
91
|
-
);
|
|
92
|
-
},
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
}
|
package/src/version.ts
DELETED
package/tsconfig.json
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": "../../../tsconfig.base.json",
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"outDir": "dist/esm",
|
|
5
|
-
"rootDir": "src",
|
|
6
|
-
"declarationDir": "./dist/types"
|
|
7
|
-
},
|
|
8
|
-
"references": [
|
|
9
|
-
{
|
|
10
|
-
"path": "../module"
|
|
11
|
-
},
|
|
12
|
-
{
|
|
13
|
-
"path": "../services"
|
|
14
|
-
},
|
|
15
|
-
{
|
|
16
|
-
"path": "../event"
|
|
17
|
-
}
|
|
18
|
-
],
|
|
19
|
-
"include": ["src/**/*"],
|
|
20
|
-
"exclude": ["node_modules", "dist"]
|
|
21
|
-
}
|