@openfin/core 29.72.17 → 30.73.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/OpenFin.d.ts +5 -4
- package/package.json +1 -1
- package/src/api/interop/fdc3/PrivateChannelClient.d.ts +17 -0
- package/src/api/interop/fdc3/PrivateChannelClient.js +84 -0
- package/src/api/interop/fdc3/PrivateChannelProvider.d.ts +40 -0
- package/src/api/interop/fdc3/PrivateChannelProvider.js +224 -0
- package/src/api/interop/fdc3/fdc3-2.0.d.ts +24 -3
- package/src/api/interop/fdc3/fdc3-2.0.js +138 -11
- package/src/api/interop/fdc3/utils.d.ts +11 -0
- package/src/api/interop/fdc3/utils.js +54 -1
- package/src/api/interop/utils.js +1 -1
package/OpenFin.d.ts
CHANGED
|
@@ -250,13 +250,13 @@ declare namespace OpenFin {
|
|
|
250
250
|
|
|
251
251
|
export type WindowOptions = MutableWindowOptions & ConstWindowOptions;
|
|
252
252
|
|
|
253
|
-
export type ViewVisibilityOption = {
|
|
253
|
+
export type ViewVisibilityOption = {
|
|
254
254
|
enabled?: boolean;
|
|
255
|
-
}
|
|
255
|
+
};
|
|
256
256
|
|
|
257
|
-
export type ShowViewOnWindowResizeOptions =
|
|
257
|
+
export type ShowViewOnWindowResizeOptions = ViewVisibilityOption & {
|
|
258
258
|
paintIntervalMs?: number;
|
|
259
|
-
}
|
|
259
|
+
};
|
|
260
260
|
|
|
261
261
|
export type ViewVisibilityOptions = {
|
|
262
262
|
showViewsOnWindowResize?: ShowViewOnWindowResizeOptions;
|
|
@@ -1427,6 +1427,7 @@ declare namespace OpenFin {
|
|
|
1427
1427
|
export type IntentMetadata<TargetType = any> = {
|
|
1428
1428
|
target?: TargetType;
|
|
1429
1429
|
resultType?: string;
|
|
1430
|
+
intentResolutionResultId?: string;
|
|
1430
1431
|
};
|
|
1431
1432
|
|
|
1432
1433
|
export type IntentHandler = (intent: OpenFin.Intent) => void;
|
package/package.json
CHANGED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
declare type HandlerId = string;
|
|
2
|
+
export declare class PrivateChannelClient {
|
|
3
|
+
id: string;
|
|
4
|
+
client: OpenFin.ChannelClient;
|
|
5
|
+
listeners: Map<HandlerId, FDC3v2.Listener>;
|
|
6
|
+
constructor(client: OpenFin.ChannelClient, id: string);
|
|
7
|
+
broadcast(context: OpenFin.Context): Promise<void>;
|
|
8
|
+
getCurrentContext(contextType?: string): Promise<OpenFin.Context | null>;
|
|
9
|
+
addContextListener(contextType: string | null, handler: OpenFin.ContextHandler): Promise<FDC3v2.Listener>;
|
|
10
|
+
private createNonStandardUnsubscribeCb;
|
|
11
|
+
private createContextUnsubscribeCb;
|
|
12
|
+
onAddContextListener(handler: (contextType?: string) => void): FDC3v2.Listener;
|
|
13
|
+
onDisconnect(handler: () => void): FDC3v2.Listener;
|
|
14
|
+
onUnsubscribe(handler: (contextType?: string) => void): FDC3v2.Listener;
|
|
15
|
+
disconnect(): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PrivateChannelClient = void 0;
|
|
4
|
+
const utils_1 = require("../utils");
|
|
5
|
+
class PrivateChannelClient {
|
|
6
|
+
constructor(client, id) {
|
|
7
|
+
this.id = id;
|
|
8
|
+
this.client = client;
|
|
9
|
+
this.listeners = new Map();
|
|
10
|
+
}
|
|
11
|
+
async broadcast(context) {
|
|
12
|
+
this.client.dispatch('broadcast', { context });
|
|
13
|
+
}
|
|
14
|
+
async getCurrentContext(contextType) {
|
|
15
|
+
return this.client.dispatch('getCurrentContext', { contextType });
|
|
16
|
+
}
|
|
17
|
+
async addContextListener(contextType, handler) {
|
|
18
|
+
if (typeof handler !== 'function') {
|
|
19
|
+
throw new Error("Non-function argument passed to the second parameter 'handler'. Be aware that the argument order does not match the FDC3 standard.");
|
|
20
|
+
}
|
|
21
|
+
let handlerId;
|
|
22
|
+
if (contextType) {
|
|
23
|
+
handlerId = `contextHandler:invoke-${this.id}-${contextType}-${utils_1.generateId()}`;
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
handlerId = `contextHandler:invoke-${this.id}-${utils_1.generateId()}`;
|
|
27
|
+
}
|
|
28
|
+
this.client.register(handlerId, utils_1.wrapContextHandler(handler, handlerId));
|
|
29
|
+
const listener = { unsubscribe: await this.createContextUnsubscribeCb(handlerId) };
|
|
30
|
+
this.listeners.set(handlerId, listener);
|
|
31
|
+
await this.client.dispatch(`contextHandlerAdded`, { handlerId, contextType });
|
|
32
|
+
return listener;
|
|
33
|
+
}
|
|
34
|
+
createNonStandardUnsubscribeCb(handlerId) {
|
|
35
|
+
return async () => {
|
|
36
|
+
this.client.remove(handlerId);
|
|
37
|
+
this.listeners.delete(handlerId);
|
|
38
|
+
await this.client.dispatch('nonStandardHandlerRemoved', { handlerId });
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
createContextUnsubscribeCb(handlerId) {
|
|
42
|
+
return async () => {
|
|
43
|
+
this.client.remove(handlerId);
|
|
44
|
+
this.listeners.delete(handlerId);
|
|
45
|
+
await this.client.dispatch('contextHandlerRemoved', { handlerId });
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
onAddContextListener(handler) {
|
|
49
|
+
const handlerId = `onContextHandlerAdded:invoke-${this.id}-${utils_1.generateId()}`;
|
|
50
|
+
this.client.register(handlerId, handler);
|
|
51
|
+
const listener = { unsubscribe: this.createNonStandardUnsubscribeCb(handlerId) };
|
|
52
|
+
this.listeners.set(handlerId, listener);
|
|
53
|
+
this.client.dispatch(`onAddContextHandlerAdded`, { handlerId });
|
|
54
|
+
return listener;
|
|
55
|
+
}
|
|
56
|
+
onDisconnect(handler) {
|
|
57
|
+
const handlerId = `onDisconnect:invoke-${this.id}-${utils_1.generateId()}`;
|
|
58
|
+
this.client.register(handlerId, handler);
|
|
59
|
+
const listener = { unsubscribe: this.createNonStandardUnsubscribeCb(handlerId) };
|
|
60
|
+
this.listeners.set(handlerId, listener);
|
|
61
|
+
this.client.dispatch(`onDisconnectHandlerAdded`, { handlerId });
|
|
62
|
+
return listener;
|
|
63
|
+
}
|
|
64
|
+
onUnsubscribe(handler) {
|
|
65
|
+
const handlerId = `onUnsubscribe:invoke-${this.id}-${utils_1.generateId()}`;
|
|
66
|
+
this.client.register(handlerId, handler);
|
|
67
|
+
const listener = { unsubscribe: this.createNonStandardUnsubscribeCb(handlerId) };
|
|
68
|
+
this.listeners.set(handlerId, listener);
|
|
69
|
+
this.client.dispatch(`onUnsubscribeHandlerAdded`, { handlerId });
|
|
70
|
+
return listener;
|
|
71
|
+
}
|
|
72
|
+
async disconnect() {
|
|
73
|
+
const promises = [];
|
|
74
|
+
const listenerUnsubscribers = Array.from(this.listeners.values());
|
|
75
|
+
for (let i = 0; i < listenerUnsubscribers.length; i++) {
|
|
76
|
+
const listener = listenerUnsubscribers[i];
|
|
77
|
+
promises.push(listener.unsubscribe());
|
|
78
|
+
}
|
|
79
|
+
await Promise.all(promises);
|
|
80
|
+
await this.client.dispatch('clientDisconnecting');
|
|
81
|
+
await this.client.disconnect();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
exports.PrivateChannelClient = PrivateChannelClient;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { ChannelProvider } from '../../interappbus/channel/provider';
|
|
2
|
+
declare type HandlerId = string;
|
|
3
|
+
export declare class PrivateChannelProvider {
|
|
4
|
+
id: string;
|
|
5
|
+
private provider;
|
|
6
|
+
private clients;
|
|
7
|
+
private contextByContextType;
|
|
8
|
+
private lastContext;
|
|
9
|
+
constructor(provider: ChannelProvider, id: string);
|
|
10
|
+
private getClientState;
|
|
11
|
+
registerListeners(): void;
|
|
12
|
+
broadcast(payload: {
|
|
13
|
+
context: OpenFin.Context;
|
|
14
|
+
}, broadcasterClientIdentity: OpenFin.ClientIdentity): void;
|
|
15
|
+
getCurrentContext(payload: {
|
|
16
|
+
contextType?: string;
|
|
17
|
+
}, senderClientIdentity: OpenFin.ClientIdentity): OpenFin.Context | null;
|
|
18
|
+
contextHandlerAdded(payload: {
|
|
19
|
+
handlerId: HandlerId;
|
|
20
|
+
contextType?: string;
|
|
21
|
+
}, senderClientIdentity: OpenFin.ClientIdentity): void;
|
|
22
|
+
contextHandlerRemoved(payload: {
|
|
23
|
+
handlerId: HandlerId;
|
|
24
|
+
}, removingClientIdentity: OpenFin.ClientIdentity): Promise<void>;
|
|
25
|
+
nonStandardHandlerRemoved(payload: {
|
|
26
|
+
handlerId: HandlerId;
|
|
27
|
+
}, id: OpenFin.ClientIdentity): void;
|
|
28
|
+
onAddContextHandlerAdded(payload: {
|
|
29
|
+
handlerId: HandlerId;
|
|
30
|
+
}, senderClientIdentity: OpenFin.ClientIdentity): void;
|
|
31
|
+
onDisconnectHandlerAdded(payload: {
|
|
32
|
+
handlerId: HandlerId;
|
|
33
|
+
}, id: OpenFin.ClientIdentity): void;
|
|
34
|
+
onUnsubscribeHandlerAdded(payload: {
|
|
35
|
+
handlerId: HandlerId;
|
|
36
|
+
}, id: OpenFin.ClientIdentity): void;
|
|
37
|
+
clientDisconnecting(payload: {}, disconnectingClientIdentity: OpenFin.ClientIdentity): Promise<void>;
|
|
38
|
+
registerNewClient(clientIdentity: OpenFin.ClientIdentity): void;
|
|
39
|
+
}
|
|
40
|
+
export {};
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PrivateChannelProvider = void 0;
|
|
4
|
+
const InteropBroker_1 = require("../InteropBroker");
|
|
5
|
+
class PrivateChannelProvider {
|
|
6
|
+
constructor(provider, id) {
|
|
7
|
+
this.provider = provider;
|
|
8
|
+
this.id = id;
|
|
9
|
+
this.clients = new Map();
|
|
10
|
+
this.registerListeners();
|
|
11
|
+
this.contextByContextType = new Map();
|
|
12
|
+
this.lastContext = undefined;
|
|
13
|
+
this.provider.onConnection((clientIdentity) => this.registerNewClient(clientIdentity));
|
|
14
|
+
this.provider.onDisconnection((clientIdentity) => this.clients.delete(clientIdentity.endpointId));
|
|
15
|
+
}
|
|
16
|
+
getClientState(id) {
|
|
17
|
+
return this.clients.get(id.endpointId);
|
|
18
|
+
}
|
|
19
|
+
registerListeners() {
|
|
20
|
+
this.provider.register('broadcast', this.broadcast.bind(this));
|
|
21
|
+
this.provider.register('getCurrentContext', this.getCurrentContext.bind(this));
|
|
22
|
+
this.provider.register('contextHandlerAdded', this.contextHandlerAdded.bind(this));
|
|
23
|
+
this.provider.register('contextHandlerRemoved', this.contextHandlerRemoved.bind(this));
|
|
24
|
+
this.provider.register('nonStandardHandlerRemoved', this.nonStandardHandlerRemoved.bind(this));
|
|
25
|
+
this.provider.register('onAddContextHandlerAdded', this.onAddContextHandlerAdded.bind(this));
|
|
26
|
+
this.provider.register('onDisconnectHandlerAdded', this.onDisconnectHandlerAdded.bind(this));
|
|
27
|
+
this.provider.register('onUnsubscribeHandlerAdded', this.onUnsubscribeHandlerAdded.bind(this));
|
|
28
|
+
this.provider.register('clientDisconnecting', this.clientDisconnecting.bind(this));
|
|
29
|
+
}
|
|
30
|
+
broadcast(payload, broadcasterClientIdentity) {
|
|
31
|
+
const { context } = payload;
|
|
32
|
+
const broadcasterClientState = this.getClientState(broadcasterClientIdentity);
|
|
33
|
+
if (!broadcasterClientState) {
|
|
34
|
+
throw new Error(`Client with Identity: ${broadcasterClientIdentity.uuid} ${broadcasterClientIdentity.name}, tried to call broadcast, is not connected to this Private Channel`);
|
|
35
|
+
}
|
|
36
|
+
const contextIntegrityCheckResult = InteropBroker_1.InteropBroker.checkContextIntegrity(context);
|
|
37
|
+
if (contextIntegrityCheckResult.isValid === false) {
|
|
38
|
+
throw new Error(`Failed to broadcast - bad Context. Reason: ${contextIntegrityCheckResult.reason}. Context: ${JSON.stringify(context)}`);
|
|
39
|
+
}
|
|
40
|
+
this.contextByContextType.set(context.type, context);
|
|
41
|
+
this.lastContext = context;
|
|
42
|
+
Array.from(this.clients.values()).forEach((currClientState) => {
|
|
43
|
+
const handlerIdsForContextType = currClientState.handlerIdsByContextTypes.get(context.type);
|
|
44
|
+
if (handlerIdsForContextType) {
|
|
45
|
+
handlerIdsForContextType.forEach((handlerId) => {
|
|
46
|
+
this.provider.dispatch(currClientState.clientIdentity, handlerId, context);
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
if (currClientState.globalHandler) {
|
|
50
|
+
this.provider.dispatch(currClientState.clientIdentity, currClientState.globalHandler, context);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
getCurrentContext(payload, senderClientIdentity) {
|
|
55
|
+
const { contextType } = payload;
|
|
56
|
+
const clientState = this.getClientState(senderClientIdentity);
|
|
57
|
+
if (!clientState) {
|
|
58
|
+
throw new Error(`Client with Identity: ${senderClientIdentity.uuid} ${senderClientIdentity.name}, tried to call getCurrentContext, is not connected to this Private Channel`);
|
|
59
|
+
}
|
|
60
|
+
if (contextType !== undefined) {
|
|
61
|
+
const currentContext = this.contextByContextType.get(contextType);
|
|
62
|
+
if (currentContext)
|
|
63
|
+
return currentContext;
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
return this.lastContext ? this.lastContext : null;
|
|
67
|
+
}
|
|
68
|
+
contextHandlerAdded(payload, senderClientIdentity) {
|
|
69
|
+
const { handlerId, contextType } = payload;
|
|
70
|
+
const senderClientState = this.getClientState(senderClientIdentity);
|
|
71
|
+
if (!senderClientState) {
|
|
72
|
+
throw new Error(`Client with Identity: ${senderClientIdentity.uuid} ${senderClientIdentity.name}, tried to call addContextListener, is not connected to this Private Channel`);
|
|
73
|
+
}
|
|
74
|
+
if (contextType) {
|
|
75
|
+
const currentHandlersList = senderClientState.handlerIdsByContextTypes.get(contextType) || [];
|
|
76
|
+
senderClientState.handlerIdsByContextTypes.set(contextType, [...currentHandlersList, handlerId]);
|
|
77
|
+
const currContext = this.contextByContextType.get(contextType);
|
|
78
|
+
if (currContext) {
|
|
79
|
+
this.provider.dispatch(senderClientIdentity, handlerId, currContext);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
senderClientState.globalHandler = handlerId;
|
|
84
|
+
if (this.lastContext) {
|
|
85
|
+
this.provider.dispatch(senderClientIdentity, handlerId, this.lastContext);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
Array.from(this.clients.values()).forEach((currClientState) => {
|
|
89
|
+
if (currClientState.clientIdentity.endpointId !== senderClientIdentity.endpointId &&
|
|
90
|
+
currClientState.onAddContextListenerHandlerId) {
|
|
91
|
+
this.provider.dispatch(currClientState.clientIdentity, currClientState.onAddContextListenerHandlerId, contextType);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
async contextHandlerRemoved(payload, removingClientIdentity) {
|
|
96
|
+
// MC: Made this removal async to ensure that onUnsubscribe handlers are hit before anything else happens.
|
|
97
|
+
const { handlerId } = payload;
|
|
98
|
+
const removingClientState = this.getClientState(removingClientIdentity);
|
|
99
|
+
if (removingClientState) {
|
|
100
|
+
let contextType;
|
|
101
|
+
if (removingClientState.globalHandler === handlerId) {
|
|
102
|
+
removingClientState.globalHandler = undefined;
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
for (const [currContextType, handlersIds] of removingClientState.handlerIdsByContextTypes) {
|
|
106
|
+
const index = handlersIds.indexOf(handlerId);
|
|
107
|
+
if (index > -1) {
|
|
108
|
+
handlersIds.splice(index, 1);
|
|
109
|
+
contextType = currContextType;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
const dispatchesToSend = [];
|
|
114
|
+
const otherClientStates = Array.from(this.clients.values());
|
|
115
|
+
for (let i = 0; i < otherClientStates.length; i++) {
|
|
116
|
+
const otherClientState = otherClientStates[i];
|
|
117
|
+
if (otherClientState.clientIdentity.endpointId !== removingClientIdentity.endpointId &&
|
|
118
|
+
otherClientState.onUnsubscribeHandlerId) {
|
|
119
|
+
dispatchesToSend.push(this.provider.dispatch(otherClientState.clientIdentity, otherClientState.onUnsubscribeHandlerId, contextType));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
try {
|
|
123
|
+
await Promise.all(dispatchesToSend);
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
console.error(`Problem when attempting to dispatch to onUnsubscribeHandlers. Error: ${error} Removing Client: ${handlerId}. uuid: ${removingClientIdentity.uuid}. name: ${removingClientIdentity.name}. endpointId: ${removingClientIdentity.endpointId}`);
|
|
127
|
+
throw new Error(error);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
console.warn(`Trying to remove a handler from a client that isn't mapped. handlerId: ${handlerId}. uuid: ${removingClientIdentity.uuid}. name: ${removingClientIdentity.name}. endpointId: ${removingClientIdentity.endpointId}.`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
nonStandardHandlerRemoved(payload, id) {
|
|
135
|
+
const { handlerId } = payload;
|
|
136
|
+
const clientState = this.getClientState(id);
|
|
137
|
+
if (clientState) {
|
|
138
|
+
if (clientState.onDisconnectHandlerId === handlerId) {
|
|
139
|
+
clientState.onDisconnectHandlerId = undefined;
|
|
140
|
+
}
|
|
141
|
+
else if (clientState.onAddContextListenerHandlerId === handlerId) {
|
|
142
|
+
clientState.onAddContextListenerHandlerId = undefined;
|
|
143
|
+
}
|
|
144
|
+
else if (clientState.onUnsubscribeHandlerId === handlerId) {
|
|
145
|
+
clientState.onUnsubscribeHandlerId = undefined;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
console.warn(`Trying to remove a handler from a client that isn't mapped. handlerId: ${handlerId}. clientIdentity: ${id}`);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
onAddContextHandlerAdded(payload, senderClientIdentity) {
|
|
153
|
+
const clientState = this.getClientState(senderClientIdentity);
|
|
154
|
+
const { handlerId } = payload;
|
|
155
|
+
if (!clientState) {
|
|
156
|
+
throw new Error(`Client with Identity: ${senderClientIdentity.uuid} ${senderClientIdentity.name}, tried to call onAddContextListener, is not connected to this Private Channel`);
|
|
157
|
+
}
|
|
158
|
+
clientState.onAddContextListenerHandlerId = handlerId;
|
|
159
|
+
// FDC3 Spec says that the added listener should fire for all previously-registered addContextListeners from the other client
|
|
160
|
+
Array.from(this.clients.values()).forEach((otherClientState) => {
|
|
161
|
+
if (otherClientState.clientIdentity.endpointId !== senderClientIdentity.endpointId) {
|
|
162
|
+
Array.from(otherClientState.handlerIdsByContextTypes.keys()).forEach((subscribedContextType) => {
|
|
163
|
+
this.provider.dispatch(senderClientIdentity, handlerId, subscribedContextType);
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
onDisconnectHandlerAdded(payload, id) {
|
|
169
|
+
const clientState = this.getClientState(id);
|
|
170
|
+
const { handlerId } = payload;
|
|
171
|
+
if (!clientState) {
|
|
172
|
+
throw new Error(`Client with Identity: ${id.uuid} ${id.name}, tried to call onDisconnect, is not connected to this Private Channel`);
|
|
173
|
+
}
|
|
174
|
+
clientState.onDisconnectHandlerId = handlerId;
|
|
175
|
+
}
|
|
176
|
+
onUnsubscribeHandlerAdded(payload, id) {
|
|
177
|
+
const clientState = this.getClientState(id);
|
|
178
|
+
const { handlerId } = payload;
|
|
179
|
+
if (!clientState) {
|
|
180
|
+
throw new Error(`Client with Identity: ${id.uuid} ${id.name}, tried to call onUnsubscribe, is not connected to this Private Channel`);
|
|
181
|
+
}
|
|
182
|
+
clientState.onUnsubscribeHandlerId = handlerId;
|
|
183
|
+
}
|
|
184
|
+
async clientDisconnecting(payload, disconnectingClientIdentity) {
|
|
185
|
+
const disconnectingClientState = this.getClientState(disconnectingClientIdentity);
|
|
186
|
+
if (!disconnectingClientState) {
|
|
187
|
+
throw new Error(`Client with Identity: ${disconnectingClientIdentity.uuid} ${disconnectingClientIdentity.name}, tried to call disconnect, is not connected to this Private Channel`);
|
|
188
|
+
}
|
|
189
|
+
disconnectingClientState.handlerIdsByContextTypes.clear();
|
|
190
|
+
this.clients.delete(disconnectingClientIdentity.endpointId);
|
|
191
|
+
const dispatchesToSend = [];
|
|
192
|
+
// TODO: call onDisconnect Handler of the other client only.
|
|
193
|
+
// CURRENTLY, just calling the onDisconnect handler for all the other clients. Once we limit it to just one other client, we can eliminate all the iteration code.
|
|
194
|
+
const otherClientStates = Array.from(this.clients.values());
|
|
195
|
+
for (let i = 0; i < otherClientStates.length; i++) {
|
|
196
|
+
const otherClientState = otherClientStates[i];
|
|
197
|
+
if (otherClientState.clientIdentity.endpointId !== disconnectingClientIdentity.endpointId &&
|
|
198
|
+
otherClientState.onDisconnectHandlerId) {
|
|
199
|
+
dispatchesToSend.push(this.provider.dispatch(otherClientState.clientIdentity, otherClientState.onDisconnectHandlerId));
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
try {
|
|
203
|
+
await Promise.all(dispatchesToSend);
|
|
204
|
+
}
|
|
205
|
+
catch (error) {
|
|
206
|
+
console.error(`Problem when attempting to dispatch to onDisconnectHandlers. Error: ${error} Disconnecting Client: uuid: ${disconnectingClientIdentity.uuid}. name: ${disconnectingClientIdentity.name}. endpointId: ${disconnectingClientIdentity.endpointId}`);
|
|
207
|
+
throw new Error(error);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
registerNewClient(clientIdentity) {
|
|
211
|
+
if (!this.clients.has(clientIdentity.endpointId)) {
|
|
212
|
+
const clientSubscriptionState = {
|
|
213
|
+
clientIdentity,
|
|
214
|
+
handlerIdsByContextTypes: new Map(),
|
|
215
|
+
globalHandler: undefined,
|
|
216
|
+
onAddContextListenerHandlerId: undefined,
|
|
217
|
+
onUnsubscribeHandlerId: undefined,
|
|
218
|
+
onDisconnectHandlerId: undefined
|
|
219
|
+
};
|
|
220
|
+
this.clients.set(clientIdentity.endpointId, clientSubscriptionState);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
exports.PrivateChannelProvider = PrivateChannelProvider;
|
|
@@ -102,6 +102,20 @@ import Transport from '../../../transport/transport';
|
|
|
102
102
|
* @property { string } [version] The version number of the Intents schema being used.
|
|
103
103
|
* @property { Function } getResult {@link getResult Function} that returns a promise that will resolve to either `Context` data returned by the application that resolves the raised intent or a `Channel` established and returned by the app resolving the intent.
|
|
104
104
|
*/
|
|
105
|
+
/**
|
|
106
|
+
* @typedef { object } PrivateChannel
|
|
107
|
+
* @summary Object representing a private context channel, which is intended to support secure communication between applications, and extends the Channel interface with event handlers which provide information on the connection state of both parties, ensuring that desktop agents do not need to queue or retain messages that are broadcast before a context listener is added and that applications are able to stop broadcasting messages when the other party has disconnected.
|
|
108
|
+
* @property { string } id Private Channel Id
|
|
109
|
+
* @property { string } type Channel Type
|
|
110
|
+
* @property { DisplayMetadata } [displayMetadata]
|
|
111
|
+
* @property { function } broadcast
|
|
112
|
+
* @property { function } getCurrentContext
|
|
113
|
+
* @property { function } addContextListener
|
|
114
|
+
* @property { function } onAddContextListener
|
|
115
|
+
* @property { function } onUnsubscribe
|
|
116
|
+
* @property { function } onDisconnect
|
|
117
|
+
* @property { function } disconnect
|
|
118
|
+
*/
|
|
105
119
|
/**
|
|
106
120
|
* @class
|
|
107
121
|
* @alias fdc3v2
|
|
@@ -203,7 +217,7 @@ export default class Fdc3Module2 extends Base implements FDC3v2.DesktopAgent {
|
|
|
203
217
|
* @param { Context } context Context associated with the Intent
|
|
204
218
|
* @param { AppIdentifier | TargetApp } [app]
|
|
205
219
|
* @returns { Promise<IntentResolution(2)> }
|
|
206
|
-
* @tutorial
|
|
220
|
+
* @tutorial fdc3v2.raiseIntent
|
|
207
221
|
*/
|
|
208
222
|
raiseIntent(intent: string, context: OpenFin.Context, app?: FDC3v2.AppIdentifier | FDC3.TargetApp): Promise<FDC3v2.IntentResolution>;
|
|
209
223
|
/**
|
|
@@ -211,7 +225,7 @@ export default class Fdc3Module2 extends Base implements FDC3v2.DesktopAgent {
|
|
|
211
225
|
* @param { Context } context Context associated with the Intent
|
|
212
226
|
* @param { AppIdentifier | TargetApp } [app]
|
|
213
227
|
* @returns { Promise<IntentResolution(2)> }
|
|
214
|
-
* @tutorial
|
|
228
|
+
* @tutorial fdc3v2.raiseIntentForContext
|
|
215
229
|
*/
|
|
216
230
|
raiseIntentForContext(context: OpenFin.Context, app?: FDC3v2.AppIdentifier | FDC3.TargetApp): Promise<FDC3v2.IntentResolution>;
|
|
217
231
|
/**
|
|
@@ -229,7 +243,14 @@ export default class Fdc3Module2 extends Base implements FDC3v2.DesktopAgent {
|
|
|
229
243
|
* @tutorial fdc3.getOrCreateChannel
|
|
230
244
|
*/
|
|
231
245
|
getOrCreateChannel(channelId: string): Promise<FDC3.Channel>;
|
|
232
|
-
|
|
246
|
+
/**
|
|
247
|
+
* Returns a Channel with an auto-generated identity that is intended for private communication between applications. Primarily used to create channels that will be returned to other applications via an IntentResolution for a raised intent.
|
|
248
|
+
* @returns { Promise<PrivateChannel> }
|
|
249
|
+
* @tutorial fdc3v2.createPrivateChannel
|
|
250
|
+
*/
|
|
251
|
+
createPrivateChannel(): Promise<FDC3v2.PrivateChannel>;
|
|
252
|
+
connectPrivateChannel(channelId: string): Promise<FDC3v2.PrivateChannel>;
|
|
253
|
+
private buildPrivateChannelObject;
|
|
233
254
|
/**
|
|
234
255
|
* Retrieves a list of the User Channels available for the app to join.
|
|
235
256
|
* @returns { Promise<Channel[]>}
|
|
@@ -3,7 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const base_1 = require("../../base");
|
|
4
4
|
const utils_1 = require("../utils");
|
|
5
5
|
const InteropClient_1 = require("../InteropClient");
|
|
6
|
+
const utils_2 = require("./utils");
|
|
6
7
|
const fdc3_1_2_1 = require("./fdc3-1.2");
|
|
8
|
+
const PrivateChannelClient_1 = require("./PrivateChannelClient");
|
|
9
|
+
const PrivateChannelProvider_1 = require("./PrivateChannelProvider");
|
|
7
10
|
/**
|
|
8
11
|
* @typedef { object } AppIdentifier
|
|
9
12
|
* @summary Identifies an application, or instance of an application, and is used to target FDC3 API calls at specific applications.
|
|
@@ -106,6 +109,20 @@ const fdc3_1_2_1 = require("./fdc3-1.2");
|
|
|
106
109
|
* @property { string } [version] The version number of the Intents schema being used.
|
|
107
110
|
* @property { Function } getResult {@link getResult Function} that returns a promise that will resolve to either `Context` data returned by the application that resolves the raised intent or a `Channel` established and returned by the app resolving the intent.
|
|
108
111
|
*/
|
|
112
|
+
/**
|
|
113
|
+
* @typedef { object } PrivateChannel
|
|
114
|
+
* @summary Object representing a private context channel, which is intended to support secure communication between applications, and extends the Channel interface with event handlers which provide information on the connection state of both parties, ensuring that desktop agents do not need to queue or retain messages that are broadcast before a context listener is added and that applications are able to stop broadcasting messages when the other party has disconnected.
|
|
115
|
+
* @property { string } id Private Channel Id
|
|
116
|
+
* @property { string } type Channel Type
|
|
117
|
+
* @property { DisplayMetadata } [displayMetadata]
|
|
118
|
+
* @property { function } broadcast
|
|
119
|
+
* @property { function } getCurrentContext
|
|
120
|
+
* @property { function } addContextListener
|
|
121
|
+
* @property { function } onAddContextListener
|
|
122
|
+
* @property { function } onUnsubscribe
|
|
123
|
+
* @property { function } onDisconnect
|
|
124
|
+
* @property { function } disconnect
|
|
125
|
+
*/
|
|
109
126
|
/**
|
|
110
127
|
* @class
|
|
111
128
|
* @alias fdc3v2
|
|
@@ -281,20 +298,39 @@ class Fdc3Module2 extends base_1.Base {
|
|
|
281
298
|
* @param { Context } context Context associated with the Intent
|
|
282
299
|
* @param { AppIdentifier | TargetApp } [app]
|
|
283
300
|
* @returns { Promise<IntentResolution(2)> }
|
|
284
|
-
* @tutorial
|
|
301
|
+
* @tutorial fdc3v2.raiseIntent
|
|
285
302
|
*/
|
|
286
303
|
async raiseIntent(intent, context, app) {
|
|
287
|
-
|
|
304
|
+
this.wire.sendAction('fdc3-raise-intent').catch((e) => {
|
|
305
|
+
// we do not want to expose this error, just continue if this analytics-only call fails
|
|
306
|
+
});
|
|
307
|
+
try {
|
|
308
|
+
return utils_2.getIntentResolution(this.fin.me.interop, context, app, intent);
|
|
309
|
+
}
|
|
310
|
+
catch (error) {
|
|
311
|
+
const errorToThrow = error.message === utils_1.BROKER_ERRORS.fireIntent ? 'ResolverUnavailable' : error;
|
|
312
|
+
throw new Error(errorToThrow);
|
|
313
|
+
}
|
|
288
314
|
}
|
|
289
315
|
/**
|
|
290
316
|
* Finds and raises an intent against apps registered with the desktop agent based purely on the type of the context data.
|
|
291
317
|
* @param { Context } context Context associated with the Intent
|
|
292
318
|
* @param { AppIdentifier | TargetApp } [app]
|
|
293
319
|
* @returns { Promise<IntentResolution(2)> }
|
|
294
|
-
* @tutorial
|
|
320
|
+
* @tutorial fdc3v2.raiseIntentForContext
|
|
295
321
|
*/
|
|
296
322
|
async raiseIntentForContext(context, app) {
|
|
297
|
-
|
|
323
|
+
// TODO: We have to do the same thing we do for raiseIntent here as well.
|
|
324
|
+
this.wire.sendAction('fdc3-raise-intent-for-context').catch((e) => {
|
|
325
|
+
// we do not want to expose this error, just continue if this analytics-only call fails
|
|
326
|
+
});
|
|
327
|
+
try {
|
|
328
|
+
return utils_2.getIntentResolution(this.fin.me.interop, context, app);
|
|
329
|
+
}
|
|
330
|
+
catch (error) {
|
|
331
|
+
const errorToThrow = error.message === utils_1.BROKER_ERRORS.fireIntent ? 'ResolverUnavailable' : error;
|
|
332
|
+
throw new Error(errorToThrow);
|
|
333
|
+
}
|
|
298
334
|
}
|
|
299
335
|
/**
|
|
300
336
|
* Adds a listener for incoming intents.
|
|
@@ -307,13 +343,33 @@ class Fdc3Module2 extends base_1.Base {
|
|
|
307
343
|
this.wire.sendAction('fdc3-add-intent-listener').catch((e) => {
|
|
308
344
|
// we do not want to expose this error, just continue if this analytics-only call fails
|
|
309
345
|
});
|
|
346
|
+
if (typeof intent !== 'string') {
|
|
347
|
+
throw new Error('First argument must be an Intent name');
|
|
348
|
+
}
|
|
310
349
|
// The FDC3 Intenter handler only expects the context and contextMetadata to be passed to the handler,
|
|
311
350
|
// so we wrap it here and only pass those paramaters.
|
|
312
|
-
const contextHandler = (raisedIntent) => {
|
|
313
|
-
const { context } = raisedIntent;
|
|
314
|
-
const { contextMetadata, ...rest } = context;
|
|
315
|
-
|
|
316
|
-
|
|
351
|
+
const contextHandler = async (raisedIntent) => {
|
|
352
|
+
const { context, metadata: intentMetadata } = raisedIntent;
|
|
353
|
+
const { contextMetadata, metadata, ...rest } = context;
|
|
354
|
+
let intentResult;
|
|
355
|
+
try {
|
|
356
|
+
const newContext = metadata ? { metadata, ...rest } : { ...rest };
|
|
357
|
+
intentResult = await handler(newContext, contextMetadata);
|
|
358
|
+
}
|
|
359
|
+
catch (error) {
|
|
360
|
+
intentResult = error;
|
|
361
|
+
}
|
|
362
|
+
const intentResolutionResultId = (intentMetadata === null || intentMetadata === void 0 ? void 0 : intentMetadata.intentResolutionResultId) || (metadata === null || metadata === void 0 ? void 0 : metadata.intentResolutionResultId);
|
|
363
|
+
if (intentResolutionResultId) {
|
|
364
|
+
// Send whatever the result is.
|
|
365
|
+
fin.InterApplicationBus.publish(intentResolutionResultId, intentResult);
|
|
366
|
+
}
|
|
367
|
+
if (intentResult instanceof Error) {
|
|
368
|
+
throw new Error(intentResult.message);
|
|
369
|
+
}
|
|
370
|
+
else {
|
|
371
|
+
return intentResult;
|
|
372
|
+
}
|
|
317
373
|
};
|
|
318
374
|
return this.fin.me.interop.registerIntentHandler(contextHandler, intent, { fdc3Version: '2.0' });
|
|
319
375
|
}
|
|
@@ -326,9 +382,80 @@ class Fdc3Module2 extends base_1.Base {
|
|
|
326
382
|
async getOrCreateChannel(channelId) {
|
|
327
383
|
return this.fdc3Module.getOrCreateChannel(channelId);
|
|
328
384
|
}
|
|
329
|
-
|
|
385
|
+
/**
|
|
386
|
+
* Returns a Channel with an auto-generated identity that is intended for private communication between applications. Primarily used to create channels that will be returned to other applications via an IntentResolution for a raised intent.
|
|
387
|
+
* @returns { Promise<PrivateChannel> }
|
|
388
|
+
* @tutorial fdc3v2.createPrivateChannel
|
|
389
|
+
*/
|
|
330
390
|
async createPrivateChannel() {
|
|
331
|
-
|
|
391
|
+
const channelId = utils_1.generateId();
|
|
392
|
+
const channelProvider = await this.fin.InterApplicationBus.Channel.create(channelId);
|
|
393
|
+
const newPrivateChannelProvider = new PrivateChannelProvider_1.PrivateChannelProvider(channelProvider, channelId);
|
|
394
|
+
const channelClient = await this.fin.InterApplicationBus.Channel.connect(channelId);
|
|
395
|
+
const newPrivateChannelClient = new PrivateChannelClient_1.PrivateChannelClient(channelClient, channelId);
|
|
396
|
+
return this.buildPrivateChannelObject(newPrivateChannelClient);
|
|
397
|
+
}
|
|
398
|
+
// TODO: need to remove this method and not expose it. Added temporarily for the purposes of testing.
|
|
399
|
+
// eslint-disable-next-line class-methods-use-this
|
|
400
|
+
async connectPrivateChannel(channelId) {
|
|
401
|
+
try {
|
|
402
|
+
const channelClient = await fin.InterApplicationBus.Channel.connect(channelId);
|
|
403
|
+
const privateChannelClient = new PrivateChannelClient_1.PrivateChannelClient(channelClient, channelId);
|
|
404
|
+
return this.buildPrivateChannelObject(privateChannelClient);
|
|
405
|
+
}
|
|
406
|
+
catch (error) {
|
|
407
|
+
throw new Error(`Private Channel with id: ${channelId} doesn't exist`);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
// eslint-disable-next-line class-methods-use-this
|
|
411
|
+
buildPrivateChannelObject(privateChannelClient) {
|
|
412
|
+
let clientDisconnected = false;
|
|
413
|
+
const checkIfClientDisconnected = () => {
|
|
414
|
+
if (clientDisconnected) {
|
|
415
|
+
throw new Error('Private Channel Client has been disconnected from the Private Channel');
|
|
416
|
+
}
|
|
417
|
+
};
|
|
418
|
+
return {
|
|
419
|
+
id: privateChannelClient.id,
|
|
420
|
+
type: 'private',
|
|
421
|
+
broadcast: async (context) => {
|
|
422
|
+
checkIfClientDisconnected();
|
|
423
|
+
return privateChannelClient.broadcast(context);
|
|
424
|
+
},
|
|
425
|
+
getCurrentContext: async (contextType) => {
|
|
426
|
+
checkIfClientDisconnected();
|
|
427
|
+
return privateChannelClient.getCurrentContext(contextType);
|
|
428
|
+
},
|
|
429
|
+
addContextListener: async (contextType, handler) => {
|
|
430
|
+
checkIfClientDisconnected();
|
|
431
|
+
let handlerInUse = handler;
|
|
432
|
+
let contextTypeInUse = contextType;
|
|
433
|
+
if (typeof contextType === 'function') {
|
|
434
|
+
console.warn('addContextListener(handler) has been deprecated. Please use addContextListener(null, handler)');
|
|
435
|
+
handlerInUse = contextType;
|
|
436
|
+
contextTypeInUse = null;
|
|
437
|
+
}
|
|
438
|
+
const listener = privateChannelClient.addContextListener(contextTypeInUse, handlerInUse);
|
|
439
|
+
return listener;
|
|
440
|
+
},
|
|
441
|
+
onAddContextListener: (handler) => {
|
|
442
|
+
checkIfClientDisconnected();
|
|
443
|
+
return privateChannelClient.onAddContextListener(handler);
|
|
444
|
+
},
|
|
445
|
+
disconnect: async () => {
|
|
446
|
+
checkIfClientDisconnected();
|
|
447
|
+
clientDisconnected = true;
|
|
448
|
+
return privateChannelClient.disconnect();
|
|
449
|
+
},
|
|
450
|
+
onDisconnect: (handler) => {
|
|
451
|
+
checkIfClientDisconnected();
|
|
452
|
+
return privateChannelClient.onDisconnect(handler);
|
|
453
|
+
},
|
|
454
|
+
onUnsubscribe: (handler) => {
|
|
455
|
+
checkIfClientDisconnected();
|
|
456
|
+
return privateChannelClient.onUnsubscribe(handler);
|
|
457
|
+
}
|
|
458
|
+
};
|
|
332
459
|
}
|
|
333
460
|
/**
|
|
334
461
|
* Retrieves a list of the User Channels available for the app to join.
|
|
@@ -1,3 +1,14 @@
|
|
|
1
1
|
export declare class UnsupportedChannelApiError extends Error {
|
|
2
2
|
constructor(apiName: string);
|
|
3
3
|
}
|
|
4
|
+
export declare enum ResultError {
|
|
5
|
+
/** Returned if the `IntentHandler` exited without returning a Promise or that
|
|
6
|
+
* Promise was not resolved with a Context or Channel object.
|
|
7
|
+
*/
|
|
8
|
+
NoResultReturned = "NoResultReturned",
|
|
9
|
+
/** Returned if the `IntentHandler` function processing the raised intent
|
|
10
|
+
* throws an error or rejects the Promise it returned.
|
|
11
|
+
*/
|
|
12
|
+
IntentHandlerRejected = "IntentHandlerRejected"
|
|
13
|
+
}
|
|
14
|
+
export declare const getIntentResolution: (interopModule: OpenFin.InteropClient, context: OpenFin.Context, app?: FDC3v2.AppIdentifier | FDC3.TargetApp, intent?: string) => Promise<FDC3v2.IntentResolution>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.UnsupportedChannelApiError = void 0;
|
|
3
|
+
exports.getIntentResolution = exports.ResultError = exports.UnsupportedChannelApiError = void 0;
|
|
4
|
+
const utils_1 = require("../utils");
|
|
4
5
|
class UnsupportedChannelApiError extends Error {
|
|
5
6
|
constructor(apiName) {
|
|
6
7
|
super(apiName);
|
|
@@ -8,3 +9,55 @@ class UnsupportedChannelApiError extends Error {
|
|
|
8
9
|
}
|
|
9
10
|
}
|
|
10
11
|
exports.UnsupportedChannelApiError = UnsupportedChannelApiError;
|
|
12
|
+
var ResultError;
|
|
13
|
+
(function (ResultError) {
|
|
14
|
+
/** Returned if the `IntentHandler` exited without returning a Promise or that
|
|
15
|
+
* Promise was not resolved with a Context or Channel object.
|
|
16
|
+
*/
|
|
17
|
+
ResultError["NoResultReturned"] = "NoResultReturned";
|
|
18
|
+
/** Returned if the `IntentHandler` function processing the raised intent
|
|
19
|
+
* throws an error or rejects the Promise it returned.
|
|
20
|
+
*/
|
|
21
|
+
ResultError["IntentHandlerRejected"] = "IntentHandlerRejected";
|
|
22
|
+
})(ResultError = exports.ResultError || (exports.ResultError = {}));
|
|
23
|
+
exports.getIntentResolution = async (interopModule, context, app, intent) => {
|
|
24
|
+
// Generate an ID to make a session context group with. We will pass that ID to the Broker.
|
|
25
|
+
// The broker will then setContext on that session context group later with our Intent Result,
|
|
26
|
+
const guid = utils_1.generateId();
|
|
27
|
+
// Promise we'll use in getResult
|
|
28
|
+
const getResultPromise = new Promise((resolve) => {
|
|
29
|
+
fin.InterApplicationBus.subscribe({ uuid: '*' }, guid, (intentResult) => {
|
|
30
|
+
resolve(intentResult);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
// Adding the intentResolutionResultId to the intentObj. Because fireIntent only accepts a single arg, we have to slap it in here.
|
|
34
|
+
const metadata = app ? { target: app, intentResolutionResultId: guid } : { intentResolutionResultId: guid };
|
|
35
|
+
const intentObj = intent ? { name: intent, context, metadata } : { ...context, metadata };
|
|
36
|
+
// Set up the getResult call.
|
|
37
|
+
const getResult = async () => {
|
|
38
|
+
const intentResult = await getResultPromise;
|
|
39
|
+
if (!intentResult) {
|
|
40
|
+
throw new Error(ResultError.NoResultReturned);
|
|
41
|
+
}
|
|
42
|
+
if (intentResult instanceof Error) {
|
|
43
|
+
throw new Error(ResultError.IntentHandlerRejected);
|
|
44
|
+
}
|
|
45
|
+
return intentResult;
|
|
46
|
+
};
|
|
47
|
+
// Finally fire the intent.
|
|
48
|
+
const intentResolutionInfoFromBroker = intent
|
|
49
|
+
? await interopModule.fireIntent(intentObj)
|
|
50
|
+
: await interopModule.fireIntentForContext(intentObj);
|
|
51
|
+
if (typeof intentResolutionInfoFromBroker !== 'object') {
|
|
52
|
+
return {
|
|
53
|
+
source: {
|
|
54
|
+
appId: '',
|
|
55
|
+
instanceId: ''
|
|
56
|
+
},
|
|
57
|
+
intent: '',
|
|
58
|
+
version: '2.0',
|
|
59
|
+
getResult
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
return { ...intentResolutionInfoFromBroker, getResult };
|
|
63
|
+
};
|
package/src/api/interop/utils.js
CHANGED
|
@@ -45,7 +45,7 @@ exports.BROKER_ERRORS = {
|
|
|
45
45
|
exports.wrapIntentHandler = (handler, handlerId) => {
|
|
46
46
|
return async (intent) => {
|
|
47
47
|
try {
|
|
48
|
-
|
|
48
|
+
return handler(intent);
|
|
49
49
|
}
|
|
50
50
|
catch (error) {
|
|
51
51
|
console.error(`Error thrown by handler ${handlerId}: ${error}`);
|