@finsemble/finsemble-ui 8.2.1 → 8.3.0-beta.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.
Files changed (145) hide show
  1. package/clients/Interop/FinsembleDesktopAgent.d.ts +63 -19
  2. package/clients/Interop/FinsembleDesktopAgent.d.ts.map +1 -1
  3. package/clients/Interop/FinsembleDesktopAgent.js +522 -125
  4. package/clients/Interop/FinsembleDesktopAgent.js.map +1 -1
  5. package/clients/Interop/InteropAdmin.d.ts +1 -1
  6. package/clients/Interop/InteropAdmin.d.ts.map +1 -1
  7. package/clients/Interop/InteropAdmin.js.map +1 -1
  8. package/clients/Interop/types.d.ts +112 -11
  9. package/clients/Interop/types.d.ts.map +1 -1
  10. package/clients/Interop/types.js.map +1 -1
  11. package/clients/Startup/FSBLDesktop.d.ts +7 -1
  12. package/clients/Startup/FSBLDesktop.d.ts.map +1 -1
  13. package/clients/Startup/FSBLDesktop.js +13 -2
  14. package/clients/Startup/FSBLDesktop.js.map +1 -1
  15. package/clients/Startup/FSBLFreestanding.d.ts.map +1 -1
  16. package/clients/Startup/FSBLFreestanding.js +5 -2
  17. package/clients/Startup/FSBLFreestanding.js.map +1 -1
  18. package/clients/Startup/FSBLService.d.ts.map +1 -1
  19. package/clients/Startup/FSBLService.js +1 -0
  20. package/clients/Startup/FSBLService.js.map +1 -1
  21. package/clients/Startup/types.d.ts +7 -2
  22. package/clients/Startup/types.d.ts.map +1 -1
  23. package/clients/configClient.js +1 -1
  24. package/clients/configClient.js.map +1 -1
  25. package/common/FinsembleWindow.d.ts +3 -1
  26. package/common/FinsembleWindow.d.ts.map +1 -1
  27. package/common/FinsembleWindow.js +66 -31
  28. package/common/FinsembleWindow.js.map +1 -1
  29. package/common/configUtil.d.ts +9 -0
  30. package/common/configUtil.d.ts.map +1 -1
  31. package/common/configUtil.js +15 -0
  32. package/common/configUtil.js.map +1 -1
  33. package/common/redux/createReducer.js +1 -1
  34. package/common/redux/createReducer.js.map +1 -1
  35. package/common/redux/remoteReduxEnhancer.js +2 -2
  36. package/common/redux/remoteReduxEnhancer.js.map +1 -1
  37. package/deprecated/dragAndDropClient.d.ts.map +1 -1
  38. package/deprecated/dragAndDropClient.js +1 -8
  39. package/deprecated/dragAndDropClient.js.map +1 -1
  40. package/index.d.ts +2 -0
  41. package/index.d.ts.map +1 -1
  42. package/index.js +2 -0
  43. package/index.js.map +1 -1
  44. package/package.json +1 -1
  45. package/platform/services/Interop/modules/actions.d.ts +2 -1
  46. package/platform/services/Interop/modules/actions.d.ts.map +1 -1
  47. package/platform/services/Interop/modules/actions.js.map +1 -1
  48. package/platform/services/Interop/modules/types.d.ts +3 -1
  49. package/platform/services/Interop/modules/types.d.ts.map +1 -1
  50. package/platform/services/Interop/types.d.ts +116 -15
  51. package/platform/services/Interop/types.d.ts.map +1 -1
  52. package/platform/services/Interop/types.js +1 -1
  53. package/platform/services/Interop/types.js.map +1 -1
  54. package/platform/services/window/types.d.ts +3 -3
  55. package/platform/services/window/types.d.ts.map +1 -1
  56. package/react/assets/css/core/windowFrame.css +13 -4
  57. package/react/components/appCatalog/components/AppResults.js +1 -1
  58. package/react/components/appCatalog/components/AppResults.js.map +1 -1
  59. package/react/components/appCatalog/components/Showcase/Header.js +1 -1
  60. package/react/components/appCatalog/components/Showcase/Header.js.map +1 -1
  61. package/react/components/common/Dropdown.d.ts +15 -0
  62. package/react/components/common/Dropdown.d.ts.map +1 -0
  63. package/react/components/common/Dropdown.js +83 -0
  64. package/react/components/common/Dropdown.js.map +1 -0
  65. package/react/components/common/FinsembleToggleButtonBar.d.ts +1 -0
  66. package/react/components/common/FinsembleToggleButtonBar.d.ts.map +1 -1
  67. package/react/components/common/FinsembleToggleButtonBar.js +21 -18
  68. package/react/components/common/FinsembleToggleButtonBar.js.map +1 -1
  69. package/react/components/common/css/FinsembleToggle.css +4 -0
  70. package/react/components/common/css/dropdown.css +56 -0
  71. package/react/components/fdc3Resolver/ResolverContainer.d.ts.map +1 -1
  72. package/react/components/fdc3Resolver/ResolverContainer.js +78 -1
  73. package/react/components/fdc3Resolver/ResolverContainer.js.map +1 -1
  74. package/react/components/fdc3Resolver/ResolverDialog.css +60 -103
  75. package/react/components/fdc3Resolver/ResolverDialog.d.ts +4 -2
  76. package/react/components/fdc3Resolver/ResolverDialog.d.ts.map +1 -1
  77. package/react/components/fdc3Resolver/ResolverDialog.js +113 -69
  78. package/react/components/fdc3Resolver/ResolverDialog.js.map +1 -1
  79. package/react/components/toolbar/advancedAppLauncher/stores/StoreActions.js +1 -1
  80. package/react/components/toolbar/advancedAppLauncher/stores/StoreActions.js.map +1 -1
  81. package/react/components/toolbar/appLauncher/components/componentList.js +2 -2
  82. package/react/components/toolbar/appLauncher/components/componentList.js.map +1 -1
  83. package/react/components/userPreferences/components/content/Workspaces.js +1 -1
  84. package/react/components/userPreferences/components/content/Workspaces.js.map +1 -1
  85. package/react/components/windowTitleBar/components/windowTitle.js +4 -5
  86. package/react/components/windowTitleBar/components/windowTitle.js.map +1 -1
  87. package/typedefs/FDC3/api/AppIdentifier.d.ts +18 -0
  88. package/typedefs/FDC3/api/AppIdentifier.d.ts.map +1 -0
  89. package/typedefs/FDC3/api/AppIdentifier.js +6 -0
  90. package/typedefs/FDC3/api/AppIdentifier.js.map +1 -0
  91. package/typedefs/FDC3/api/AppIntent.d.ts +3 -1
  92. package/typedefs/FDC3/api/AppIntent.d.ts.map +1 -1
  93. package/typedefs/FDC3/api/AppIntent.js +1 -1
  94. package/typedefs/FDC3/api/AppMetadata.d.ts +22 -9
  95. package/typedefs/FDC3/api/AppMetadata.d.ts.map +1 -1
  96. package/typedefs/FDC3/api/AppMetadata.js +1 -1
  97. package/typedefs/FDC3/api/Channel.d.ts +8 -7
  98. package/typedefs/FDC3/api/Channel.d.ts.map +1 -1
  99. package/typedefs/FDC3/api/Channel.js +1 -1
  100. package/typedefs/FDC3/api/ContextMetadata.d.ts +19 -0
  101. package/typedefs/FDC3/api/ContextMetadata.d.ts.map +1 -0
  102. package/typedefs/FDC3/api/ContextMetadata.js +6 -0
  103. package/typedefs/FDC3/api/ContextMetadata.js.map +1 -0
  104. package/typedefs/FDC3/api/DesktopAgent.d.ts +387 -75
  105. package/typedefs/FDC3/api/DesktopAgent.d.ts.map +1 -1
  106. package/typedefs/FDC3/api/DesktopAgent.js +1 -1
  107. package/typedefs/FDC3/api/DisplayMetadata.d.ts +1 -1
  108. package/typedefs/FDC3/api/DisplayMetadata.js +1 -1
  109. package/typedefs/FDC3/api/Errors.d.ts +28 -2
  110. package/typedefs/FDC3/api/Errors.d.ts.map +1 -1
  111. package/typedefs/FDC3/api/Errors.js +28 -1
  112. package/typedefs/FDC3/api/Errors.js.map +1 -1
  113. package/typedefs/FDC3/api/Icon.d.ts +13 -0
  114. package/typedefs/FDC3/api/Icon.d.ts.map +1 -0
  115. package/typedefs/FDC3/api/Icon.js +6 -0
  116. package/typedefs/FDC3/api/Icon.js.map +1 -0
  117. package/typedefs/FDC3/api/Image.d.ts +15 -0
  118. package/typedefs/FDC3/api/Image.d.ts.map +1 -0
  119. package/typedefs/FDC3/api/Image.js +6 -0
  120. package/typedefs/FDC3/api/Image.js.map +1 -0
  121. package/typedefs/FDC3/api/ImplementationMetadata.d.ts +4 -1
  122. package/typedefs/FDC3/api/ImplementationMetadata.d.ts.map +1 -1
  123. package/typedefs/FDC3/api/ImplementationMetadata.js +1 -1
  124. package/typedefs/FDC3/api/IntentMetadata.d.ts +1 -1
  125. package/typedefs/FDC3/api/IntentMetadata.js +1 -1
  126. package/typedefs/FDC3/api/IntentResolution.d.ts +48 -10
  127. package/typedefs/FDC3/api/IntentResolution.d.ts.map +1 -1
  128. package/typedefs/FDC3/api/IntentResolution.js +1 -1
  129. package/typedefs/FDC3/api/Listener.d.ts +1 -1
  130. package/typedefs/FDC3/api/Listener.js +1 -1
  131. package/typedefs/FDC3/api/PrivateChannel.d.ts +54 -0
  132. package/typedefs/FDC3/api/PrivateChannel.d.ts.map +1 -0
  133. package/typedefs/FDC3/api/PrivateChannel.js +6 -0
  134. package/typedefs/FDC3/api/PrivateChannel.js.map +1 -0
  135. package/typedefs/FDC3/api/Types.d.ts +25 -4
  136. package/typedefs/FDC3/api/Types.d.ts.map +1 -1
  137. package/typedefs/FDC3/api/Types.js +1 -1
  138. package/typedefs/FDC3/api/index.d.ts +13 -0
  139. package/typedefs/FDC3/api/index.d.ts.map +1 -0
  140. package/typedefs/FDC3/api/index.js +13 -0
  141. package/typedefs/FDC3/api/index.js.map +1 -0
  142. package/react/components/common/FinsembleSelect.d.ts +0 -14
  143. package/react/components/common/FinsembleSelect.d.ts.map +0 -1
  144. package/react/components/common/FinsembleSelect.js +0 -62
  145. package/react/components/common/FinsembleSelect.js.map +0 -1
@@ -9,16 +9,29 @@ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (
9
9
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
10
10
  return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
11
11
  };
12
- var _HeartbeatMonitor_declareDead, _HeartbeatMonitor_timeoutHandle, _HeartbeatMonitor_disconnectHandlers, _ChannelProxy_desktopAgent, _FinsembleDesktopAgent_connected, _FinsembleDesktopAgent_heartbeatMonitor, _FinsembleDesktopAgent_startHasBeenCalled, _FinsembleDesktopAgent_uuid, _FinsembleDesktopAgent_clientChannel, _FinsembleDesktopAgent_intentHandlers, _FinsembleDesktopAgent_contextHandlers, _FinsembleDesktopAgent_queue, _FinsembleDesktopAgent_routerClient, _FinsembleDesktopAgent_startup, _FinsembleDesktopAgent_FSBLVersion;
12
+ var _HeartbeatMonitor_declareDead, _HeartbeatMonitor_timeoutHandle, _HeartbeatMonitor_disconnectHandlers, _ChannelProxy_desktopAgent, _FinsembleDesktopAgent_connected, _FinsembleDesktopAgent_heartbeatMonitor, _FinsembleDesktopAgent_startHasBeenCalled, _FinsembleDesktopAgent_uuid, _FinsembleDesktopAgent_clientChannel, _FinsembleDesktopAgent_intentHandlers, _FinsembleDesktopAgent_contextHandlers, _FinsembleDesktopAgent_privateChannelSubscription, _FinsembleDesktopAgent_intentResultPromiseMap, _FinsembleDesktopAgent_intentResultPromiseResolveMap, _FinsembleDesktopAgent_queue, _FinsembleDesktopAgent_routerClient, _FinsembleDesktopAgent_startup, _FinsembleDesktopAgent_existingChannelProxy;
13
+ /**
14
+ * Regarding 1.2 compatibility. FDC3 1.2 defined some functions as synchronous which are now asynchronous. To provide
15
+ * backward compatibility for these functions, we return a Promise which has additional members attached (usually an
16
+ * unsubscribe function). Apps using either 1.2 or 2.0 will receive an interface that is a superset of what they expect.
17
+ *
18
+ * The resulting code is complex. It should be aggressively removed when FDC3 1.2 is retired, back to simple async functions
19
+ * that return Promise<Listener>.
20
+ *
21
+ * For Typescript support, 1.2 apps can simply install the 1.2 version of the "@finos/fdc3" npm module
22
+ */
13
23
  import uuidv4 from "uuid-random";
14
- import { OpenError, ResolveError, } from "@finos/fdc3";
24
+ import { OpenError, ResolveError, ResultError, ChannelError, } from "@finos/fdc3";
15
25
  import Logger from "../logger";
16
26
  import ConfigClient from "../configClient";
17
27
  import SystemManagerClient from "../../common/systemManagerClient";
18
28
  import { Globals } from "../../common/Globals";
19
29
  import { SERVER_CHANNEL } from "./types";
20
30
  import { mmAdd, mmRemove, errorAsString, } from "./types";
21
- const Uninitialized = "";
31
+ // Timeout value for logging warning when intent response is delayed.
32
+ const WaitForIntentResultTimeout = 5000;
33
+ const PrivateChannelIdPrefix = "Private-Channel-Id";
34
+ const isPrivateChannelId = (channelId) => channelId.includes(PrivateChannelIdPrefix);
22
35
  /**
23
36
  * Heartbeats are used by the InteropService to detect dead fdc3 client connections. A connection
24
37
  * can die if a window crashes or is terminated. Heartbeats are also currently the only way to detect
@@ -64,6 +77,8 @@ class HeartbeatMonitor {
64
77
  _HeartbeatMonitor_declareDead = new WeakMap(), _HeartbeatMonitor_timeoutHandle = new WeakMap(), _HeartbeatMonitor_disconnectHandlers = new WeakMap();
65
78
  /**
66
79
  * Implements FDC3's Channel interface. All actual transmissions still occur through the desktop agent via private functions.
80
+ *
81
+ * First pass the ChannelProxy implements PrivateChannel, which is a superset of Channel -- may refine this in second pass.
67
82
  */
68
83
  class ChannelProxy {
69
84
  constructor(desktopAgent, id, type = "app") {
@@ -81,11 +96,11 @@ class ChannelProxy {
81
96
  *
82
97
  * `Error` with a string from the `ChannelError` enumeration.
83
98
  */
84
- broadcast(context) {
99
+ async broadcast(context) {
85
100
  if (!context) {
86
101
  throw new Error("Invalid arguments. The required context parameter is missing.");
87
102
  }
88
- __classPrivateFieldGet(this, _ChannelProxy_desktopAgent, "f").channelBroadcast(this.id, context);
103
+ await __classPrivateFieldGet(this, _ChannelProxy_desktopAgent, "f").channelBroadcast(this.id, context);
89
104
  }
90
105
  /**
91
106
  * Returns the last context that was broadcast on this channel. All channels initially have no context, until a
@@ -108,6 +123,7 @@ class ChannelProxy {
108
123
  /**
109
124
  * Adds a listener for incoming contexts whenever a broadcast happens on this channel.
110
125
  */
126
+ // @ts-ignore <---------------------------------------------------------------------------------------------------------------------
111
127
  addContextListener(contextTypeOrHandler, handler) {
112
128
  if (contextTypeOrHandler === undefined) {
113
129
  throw new Error("Invalid arguments. The contextTypeOrHandler parameter shouldn't be undefined.");
@@ -115,7 +131,60 @@ class ChannelProxy {
115
131
  if (typeof contextTypeOrHandler !== "function" && !handler) {
116
132
  throw new Error("Invalid arguments. The required handler parameter is missing.");
117
133
  }
118
- return __classPrivateFieldGet(this, _ChannelProxy_desktopAgent, "f").channelAddContextListener(this.id, contextTypeOrHandler, handler);
134
+ // Dangerous code: See note at top of file about 1.2 compatibility
135
+ const { exchangePromise, unsubscribe } = __classPrivateFieldGet(this, _ChannelProxy_desktopAgent, "f").channelAddContextListener(this.id, contextTypeOrHandler, handler);
136
+ let promise = new Promise((resolve) => {
137
+ exchangePromise.then(() => {
138
+ resolve({
139
+ unsubscribe,
140
+ });
141
+ });
142
+ });
143
+ promise.unsubscribe = () => {
144
+ Logger.system.deprecated("Use of the synchronous version of addContextListener from FDC3 1.2 is deprecated. Please switch to `await addContextListener`.");
145
+ unsubscribe();
146
+ };
147
+ return promise;
148
+ }
149
+ /**
150
+ * Adds a listener that will be called each time that the remote app invokes
151
+ * addContextListener on this channel.
152
+ *
153
+ * Desktop Agents MUST call this for each invocation of addContextListener on this
154
+ * channel, including those that occurred before this handler was registered
155
+ * (to prevent race conditions).
156
+ */
157
+ onAddContextListener(handler) {
158
+ return __classPrivateFieldGet(this, _ChannelProxy_desktopAgent, "f").channelOnAddContextListener(this.id, handler);
159
+ }
160
+ /**
161
+ * Adds a listener that will be called whenever the remote app invokes
162
+ * Listener.unsubscribe() on a context listener that it previously added.
163
+ *
164
+ * Desktop Agents MUST call this when disconnect() is called by the other party, for
165
+ * each listener that they had added.
166
+ */
167
+ onUnsubscribe(handler) {
168
+ return __classPrivateFieldGet(this, _ChannelProxy_desktopAgent, "f").channelOnUnsubscribe(this.id, handler);
169
+ }
170
+ /**
171
+ * Adds a listener that will be called when the remote app terminates, for example
172
+ * when its window is closed or because disconnect was called. This is in addition
173
+ * to calls that will be made to onUnsubscribe listeners.
174
+ */
175
+ onDisconnect(handler) {
176
+ return __classPrivateFieldGet(this, _ChannelProxy_desktopAgent, "f").channelOnDisconnect(this.id, handler);
177
+ }
178
+ /**
179
+ * May be called to indicate that a participant will no longer interact with this channel.
180
+ *
181
+ * After this function has been called, Desktop Agents SHOULD prevent apps from broadcasting
182
+ * on this channel and MUST automatically call Listener.unsubscribe() for each listener that
183
+ * they've added (causing any onUnsubscribe handler added by the other party to be called)
184
+ * before triggering any onDisconnect handler added by the other party.
185
+ */
186
+ disconnect() {
187
+ return __classPrivateFieldGet(this, _ChannelProxy_desktopAgent, "f").channelDisconnect(this.id);
119
188
  }
120
189
  }
121
190
  _ChannelProxy_desktopAgent = new WeakMap();
@@ -128,21 +197,22 @@ export class FinsembleDesktopAgent {
128
197
  _FinsembleDesktopAgent_heartbeatMonitor.set(this, new HeartbeatMonitor());
129
198
  // A lock that prevents start() from running multiple times
130
199
  _FinsembleDesktopAgent_startHasBeenCalled.set(this, false);
131
- // uuid is set in connect(), when startup.onReady
132
- _FinsembleDesktopAgent_uuid.set(this, "");
200
+ _FinsembleDesktopAgent_uuid.set(this, "undefined");
133
201
  _FinsembleDesktopAgent_clientChannel.set(this, `FSBL.FDC3.Client.${uuidv4()}`);
134
202
  _FinsembleDesktopAgent_intentHandlers.set(this, {});
135
203
  _FinsembleDesktopAgent_contextHandlers.set(this, {});
204
+ _FinsembleDesktopAgent_privateChannelSubscription.set(this, {});
205
+ _FinsembleDesktopAgent_intentResultPromiseMap.set(this, {});
206
+ _FinsembleDesktopAgent_intentResultPromiseResolveMap.set(this, {});
136
207
  // Stores fdc3 calls that are received before a connection is established with the interop service
137
208
  // Once a connection is established then the queue is released.
138
209
  _FinsembleDesktopAgent_queue.set(this, []);
139
210
  _FinsembleDesktopAgent_routerClient.set(this, void 0);
140
211
  _FinsembleDesktopAgent_startup.set(this, void 0);
141
- _FinsembleDesktopAgent_FSBLVersion.set(this, Uninitialized);
212
+ _FinsembleDesktopAgent_existingChannelProxy.set(this, {});
142
213
  __classPrivateFieldSet(this, _FinsembleDesktopAgent_routerClient, routerClient, "f");
143
214
  __classPrivateFieldSet(this, _FinsembleDesktopAgent_startup, startup, "f");
144
- this.cacheFSBLVersion();
145
- // Per 1.2 spec, dispatch this event as soon as the global fdc3 object exists and is ready to accept API calls.
215
+ // Per spec, dispatch this event as soon as the global fdc3 object exists and is ready to accept API calls.
146
216
  // We should not wait until start() because in some circumstances, start() is not called *until* an API call is made.
147
217
  // Skip this if the global Event class is undefined (if running in Node.js such as a unit test). Note: also needed to confirm window.dispatchEvent was available, perhaps because of latest Node version.
148
218
  if (Globals.Event && Globals.dispatchEvent) {
@@ -150,21 +220,21 @@ export class FinsembleDesktopAgent {
150
220
  Globals.dispatchEvent(event);
151
221
  }
152
222
  }
153
- // Cache FSBL version for use by fdc3.getInfo.
154
- async cacheFSBLVersion() {
155
- // Since setFSBLVersion is async, there is a timing gap until FDC3 2.0 supports async getInfo(). However, the default for #FSBLVersion covers the gap for now.
156
- // It's possible to close this gap now by not dispatching "fdc3Ready" until FSBLVersion is set, but doing that properly requires a change to the fdc3 preload and
157
- // then breaks existing apps that don't wait as defined in 1.2 spec.
158
- if (__classPrivateFieldGet(this, _FinsembleDesktopAgent_FSBLVersion, "f") == Uninitialized) {
159
- __classPrivateFieldSet(this, _FinsembleDesktopAgent_FSBLVersion, "8.1.0", "f");
160
- }
161
- const { err, data: FSBLVersion } = await ConfigClient.getValue({ field: "finsemble.system.FSBLVersion" });
162
- if (!err) {
163
- __classPrivateFieldSet(this, _FinsembleDesktopAgent_FSBLVersion, FSBLVersion, "f");
164
- }
223
+ privateChannelId() {
224
+ return `${PrivateChannelIdPrefix}-${uuidv4()}`;
165
225
  }
166
- fdc3Error(msg) {
167
- return new Error(msg);
226
+ // This wrapper around startup data is necessary to syncronize access to it since it might not have the correct data until startup.onReady() fires.
227
+ async fetchStartupData() {
228
+ await __classPrivateFieldGet(this, _FinsembleDesktopAgent_startup, "f").onReady();
229
+ const startupData = __classPrivateFieldGet(this, _FinsembleDesktopAgent_startup, "f").getStartupData();
230
+ const { authenticationToken, windowName } = startupData;
231
+ // The uuid has the appId encoded for diagnostics, but appId is not available until startup data is ready; therefore must set uuid here the first time this function is called
232
+ if (__classPrivateFieldGet(this, _FinsembleDesktopAgent_uuid, "f") === "undefined") {
233
+ __classPrivateFieldSet(this, _FinsembleDesktopAgent_uuid, `fdc3-instanceId-${authenticationToken.meta.appId}=${uuidv4()}`, "f");
234
+ }
235
+ const appMetadata = Object.assign(Object.assign({}, authenticationToken.meta), { instanceId: __classPrivateFieldGet(this, _FinsembleDesktopAgent_uuid, "f") });
236
+ Logger.system.debug(`fetchStartupData`, startupData, windowName, authenticationToken, appMetadata);
237
+ return { windowName, authenticationToken, appMetadata };
168
238
  }
169
239
  meta() {
170
240
  return {
@@ -217,12 +287,118 @@ export class FinsembleDesktopAgent {
217
287
  }
218
288
  });
219
289
  }
220
- triggerIntentListeners(message) {
221
- const { intent, context } = message.payload;
222
- const handlers = __classPrivateFieldGet(this, _FinsembleDesktopAgent_intentHandlers, "f")[intent];
223
- const functionArray = handlers ? handlers : [];
224
- if (context)
225
- functionArray.forEach((func) => func(context));
290
+ // See https://fdc3.finos.org/docs/api/ref/DesktopAgent#examples-1 for supported examples of intents with various return characteristics
291
+ async triggerIntentListener(message) {
292
+ const { intent, context, requestId } = message.payload;
293
+ const handler = __classPrivateFieldGet(this, _FinsembleDesktopAgent_intentHandlers, "f")[intent];
294
+ let intentResultError;
295
+ let result = null;
296
+ let localIntentResult = undefined;
297
+ if (typeof handler === "function") {
298
+ // Set timer to issue warning if handler doesn't return in expect time period (but don't fail since handler could be interacting with user and therefore be very slow)
299
+ let intentResultTimer = setInterval(() => {
300
+ Logger.system.warn(`tag:FDC3:raiseIntent No intent result has been returned yet for intent ${intent} -- will continue to wait`, message.payload);
301
+ intentResultTimer = null;
302
+ }, WaitForIntentResultTimeout);
303
+ Logger.system.log(`tag:FDC3:raiseIntent triggering intent listener for intent ${intent}`, message.payload);
304
+ try {
305
+ // Wait for the intent handler to return a result
306
+ localIntentResult = await handler(context);
307
+ Logger.system.log(`tag:FDC3:raiseIntent handler response for intent ${intent}`, localIntentResult);
308
+ }
309
+ catch (err) {
310
+ Logger.system.error(`tag:FDC3:raiseIntent -- error thrown in handler for intent ${intent}`, context, message.payload);
311
+ intentResultError = ResultError.IntentHandlerRejected;
312
+ result = {
313
+ type: "intentResult",
314
+ meta: this.meta(),
315
+ payload: {
316
+ intentResultError,
317
+ intent,
318
+ requestId,
319
+ },
320
+ };
321
+ this.exchange(result);
322
+ }
323
+ // If no error was thrown above when the intent handler was invoked, then return the handler result
324
+ if (!intentResultError) {
325
+ clearInterval(intentResultTimer);
326
+ // convert IntentResult to InteropIntentResult, a form than can be transmitted to InteropService
327
+ // `?addContextListener` is how we dynamically tell whether the user returned a Channel
328
+ const intentResult = (localIntentResult === null || localIntentResult === void 0 ? void 0 : localIntentResult.addContextListener)
329
+ ? { channelId: localIntentResult === null || localIntentResult === void 0 ? void 0 : localIntentResult.id, channelType: localIntentResult.type }
330
+ : localIntentResult;
331
+ // If intentResult isn't void, then ensure it appears to be either a channel or context
332
+ if (intentResult && typeof intentResult !== "object") {
333
+ Logger.system.error(`IntentResult returned by FDC3 client app for intent ${intent} is rejected since it doesn't match expected channel or context definition`, intentResult);
334
+ intentResultError = ResultError.IntentHandlerRejected;
335
+ }
336
+ else if (!intentResult) {
337
+ intentResultError = ResultError.NoResultReturned;
338
+ }
339
+ result = {
340
+ type: "intentResult",
341
+ meta: this.meta(),
342
+ payload: {
343
+ intentResult,
344
+ intentResultError,
345
+ intent,
346
+ requestId,
347
+ },
348
+ };
349
+ this.exchange(result);
350
+ }
351
+ }
352
+ else {
353
+ Logger.system.error(`Legal listener not found for intent ${intent}`);
354
+ result = {
355
+ type: "intentResult",
356
+ meta: this.meta(),
357
+ payload: {
358
+ intentResultError: ResultError.IntentHandlerRejected,
359
+ intent,
360
+ requestId,
361
+ },
362
+ };
363
+ this.exchange(result);
364
+ }
365
+ Logger.system.log(`tag:FDC3:raiseIntent handler response for intent ${intent}`, result);
366
+ }
367
+ async handlePrivateChannelEvent(message) {
368
+ const { channel, context, targetSubscriberId } = message.payload;
369
+ const channelSubscriptions = __classPrivateFieldGet(this, _FinsembleDesktopAgent_privateChannelSubscription, "f")[channel];
370
+ const targetSubscription = channelSubscriptions.find((s) => s.targetSubscriberId === targetSubscriberId);
371
+ if (targetSubscription) {
372
+ switch (targetSubscription.subscribeType) {
373
+ case "onAddContextListener":
374
+ targetSubscription.handler(context);
375
+ break;
376
+ case "onUnsubscribe":
377
+ targetSubscription.handler(context);
378
+ break;
379
+ case "onDisconnect":
380
+ targetSubscription.handler();
381
+ break;
382
+ default: {
383
+ Logger.system.error("FDC3 Client: unknown subscribeType in matching targetSubscription", targetSubscription);
384
+ }
385
+ }
386
+ }
387
+ else {
388
+ Logger.system.error("FDC3 Client: could not find target subscription", targetSubscriberId, channelSubscriptions);
389
+ }
390
+ }
391
+ async handleIntentResult(message) {
392
+ const { intent: intentName, intentResult, requestId, intentResultError } = message.payload;
393
+ // If there is an outstanding resolve function associated with the request id, then invoke it
394
+ if (typeof __classPrivateFieldGet(this, _FinsembleDesktopAgent_intentResultPromiseResolveMap, "f")[requestId] === "function") {
395
+ Logger.system.log(`tag:FDC3:raiseIntent result message received for intent ${intentName}`, message);
396
+ // Resolve the outstanding promise, waited on in the corresponding getResult function, returning the intent result or an error
397
+ __classPrivateFieldGet(this, _FinsembleDesktopAgent_intentResultPromiseResolveMap, "f")[requestId]({ errorResult: intentResultError, intentResult });
398
+ }
399
+ else {
400
+ Logger.system.error(`Legal internal handler for intent result not found`, message, __classPrivateFieldGet(this, _FinsembleDesktopAgent_intentResultPromiseResolveMap, "f"));
401
+ }
226
402
  }
227
403
  handleIncoming(message) {
228
404
  Logger.system.debug("FDC3 Client: handleIncoming", message.type, message);
@@ -231,7 +407,13 @@ export class FinsembleDesktopAgent {
231
407
  this.triggerContextListeners(message);
232
408
  break;
233
409
  case "intent":
234
- this.triggerIntentListeners(message);
410
+ this.triggerIntentListener(message);
411
+ break;
412
+ case "privateChannelEvent":
413
+ this.handlePrivateChannelEvent(message);
414
+ break;
415
+ case "intentResult":
416
+ this.handleIntentResult(message);
235
417
  break;
236
418
  case "heartbeat":
237
419
  this.handleHeartbeat();
@@ -258,6 +440,7 @@ export class FinsembleDesktopAgent {
258
440
  return message;
259
441
  };
260
442
  __classPrivateFieldGet(this, _FinsembleDesktopAgent_queue, "f").forEach(async (entry) => {
443
+ Logger.system.log(`fdc3.releaseQueue`, entry);
261
444
  const { message, handler, resolve } = entry;
262
445
  const { err, receiveMessage } = await this.exchange(updateUUID(message), handler);
263
446
  resolve({ err, receiveMessage });
@@ -287,7 +470,6 @@ export class FinsembleDesktopAgent {
287
470
  * @internal
288
471
  */
289
472
  async connect() {
290
- var _a;
291
473
  /**
292
474
  * StartupData contains the AuthenticationToken necessary to connect to the interop service. When an app
293
475
  * is spawned by Finsemble, the AuthenticationToken is automatically created by the launcher. When an app
@@ -295,10 +477,8 @@ export class FinsembleDesktopAgent {
295
477
  * digital signing key. If an app can't be authenticated then Startup ensures that an anonymous
296
478
  * AuthenticationToken exists (type "unauthenticated").
297
479
  */
298
- const startupData = __classPrivateFieldGet(this, _FinsembleDesktopAgent_startup, "f").getStartupData();
299
- const { windowName = "Unknown window", authenticationToken } = startupData;
300
- Logger.system.log(`fdc3.connect StartupData for ${windowName}`, startupData);
301
- __classPrivateFieldSet(this, _FinsembleDesktopAgent_uuid, `fdc3-${(_a = authenticationToken === null || authenticationToken === void 0 ? void 0 : authenticationToken.meta) === null || _a === void 0 ? void 0 : _a.name}-${uuidv4()}`, "f");
480
+ const { windowName, authenticationToken } = await this.fetchStartupData();
481
+ Logger.system.log(`fdc3.connect StartupData for ${windowName}`);
302
482
  const registerClient = {
303
483
  type: "register",
304
484
  meta: this.meta(),
@@ -421,7 +601,7 @@ export class FinsembleDesktopAgent {
421
601
  * Apps can still directly broadcast and listen to context on any channel via the methods on the Channel class.
422
602
  * @param context
423
603
  */
424
- broadcast(context) {
604
+ async broadcast(context) {
425
605
  if (!context) {
426
606
  throw new Error("Invalid arguments. The required context parameter is missing.");
427
607
  }
@@ -432,10 +612,10 @@ export class FinsembleDesktopAgent {
432
612
  context,
433
613
  },
434
614
  };
435
- this.exchange(message);
615
+ await this.exchange(message);
436
616
  }
437
617
  /** @internal */
438
- channelBroadcast(channel, context) {
618
+ async channelBroadcast(channel, context) {
439
619
  const message = {
440
620
  type: "broadcast",
441
621
  meta: Object.assign(Object.assign({}, this.meta()), { channel }),
@@ -443,13 +623,13 @@ export class FinsembleDesktopAgent {
443
623
  context,
444
624
  },
445
625
  };
446
- this.exchange(message);
626
+ await this.exchange(message);
447
627
  }
448
628
  /**
449
629
  * Retrieves a list of the System channels available for the app to join.
450
630
  * This should include the 'global' channel.
451
631
  */
452
- async getSystemChannels() {
632
+ async getUserChannels() {
453
633
  var _a;
454
634
  const message = {
455
635
  type: "systemChannelRequest",
@@ -459,37 +639,37 @@ export class FinsembleDesktopAgent {
459
639
  if (err)
460
640
  return [];
461
641
  const channelNames = (receiveMessage === null || receiveMessage === void 0 ? void 0 : receiveMessage.type) === "systemChannels" ? (_a = receiveMessage === null || receiveMessage === void 0 ? void 0 : receiveMessage.payload) === null || _a === void 0 ? void 0 : _a.channels : [];
462
- return channelNames.map((name) => new ChannelProxy(this, name, "system"));
642
+ return channelNames.map((name) => new ChannelProxy(this, name, "user"));
463
643
  }
464
644
  /**
465
- * Launches/links to an app by name.
466
- * If a Context object is passed in, this object will be provided to the opened application via a contextListener.
467
- * The Context argument is functionally equivalent to opening the target app with no context and broadcasting the context directly to it.
468
- * If opening errors, it returns an Error with a string from the OpenError enumeration.
469
- *
470
- * @param target
471
- * @param context
645
+ * For backwards compatability. Retrieves a list of the System channels available for the app to join.
472
646
  */
473
- async open(target, context) {
474
- if (!target) {
475
- throw new Error("Invalid arguments. The required target parameter is missing.");
647
+ async getSystemChannels() {
648
+ return this.getUserChannels();
649
+ }
650
+ async open(app, context) {
651
+ if (!app) {
652
+ throw new Error("Invalid arguments. The required 'app' parameter is missing.");
476
653
  }
654
+ const { appMetadata } = await this.fetchStartupData();
477
655
  const message = {
478
656
  type: "open",
479
657
  meta: this.meta(),
480
658
  payload: {
481
- target,
659
+ requestingAppId: appMetadata.appId,
660
+ app,
482
661
  context,
483
662
  },
484
663
  };
485
664
  const { err, receiveMessage } = await this.exchange(message);
486
665
  const interopOpenResponse = receiveMessage;
487
666
  if (err) {
488
- throw this.fdc3Error(OpenError.ResolverUnavailable);
667
+ throw new Error(OpenError.ResolverUnavailable);
489
668
  }
490
669
  else if (interopOpenResponse.payload.error) {
491
- throw this.fdc3Error(interopOpenResponse.payload.error);
670
+ throw new Error(interopOpenResponse.payload.error);
492
671
  }
672
+ return interopOpenResponse.payload.appIdentifier;
493
673
  }
494
674
  /**
495
675
  * Find out more information about a particular intent by passing its name, and optionally its context.
@@ -503,28 +683,35 @@ export class FinsembleDesktopAgent {
503
683
  * @param intent
504
684
  * @param context
505
685
  */
506
- async findIntent(intent, context) {
686
+ async findIntent(intent, context, resultType) {
507
687
  if (!intent) {
508
688
  throw new Error("Invalid arguments. The required intent parameter is missing.");
509
689
  }
690
+ if (context && !context.type) {
691
+ throw new Error("Invalid arguments. Second parameter must be a valid context.");
692
+ }
693
+ if (resultType && typeof resultType !== "string") {
694
+ throw new Error("Invalid arguments. Third parameter must be a string.");
695
+ }
510
696
  const message = {
511
697
  type: "findIntent",
512
698
  meta: this.meta(),
513
699
  payload: {
514
700
  intent,
515
701
  context,
702
+ resultType,
516
703
  },
517
704
  };
518
705
  const { err, receiveMessage } = await this.exchange(message);
519
706
  const interopFindIntentResponse = receiveMessage;
520
707
  if (err) {
521
- throw this.fdc3Error(ResolveError.ResolverUnavailable);
708
+ throw new Error(ResolveError.ResolverUnavailable);
522
709
  }
523
710
  else if (interopFindIntentResponse.payload.error) {
524
- throw this.fdc3Error(interopFindIntentResponse.payload.error);
711
+ throw new Error(interopFindIntentResponse.payload.error);
525
712
  }
526
713
  else if (!interopFindIntentResponse.payload.appIntent) {
527
- throw this.fdc3Error("Invalid response returned from InteropService for findIntent. No appIntent and no error.");
714
+ throw new Error("Invalid response returned from InteropService for findIntent. No appIntent and no error.");
528
715
  }
529
716
  return interopFindIntentResponse.payload.appIntent;
530
717
  }
@@ -538,10 +725,16 @@ export class FinsembleDesktopAgent {
538
725
  * If the resolution fails, the promise will return an `Error` with a string from the `ResolveError` enumeration.
539
726
  * @param context
540
727
  */
541
- async findIntentsByContext(context) {
728
+ async findIntentsByContext(context, resultType) {
542
729
  if (!context) {
543
730
  throw new Error("Invalid arguments. The required context parameter is missing.");
544
731
  }
732
+ if (!context.type) {
733
+ throw new Error("Invalid arguments. Second parameter must be a valid context.");
734
+ }
735
+ if (resultType && typeof resultType !== "string") {
736
+ throw new Error("Invalid arguments. Second parameter must be a string to identify result type.");
737
+ }
545
738
  // The context parameter was made optional to support a query for all intents. This is consistent with findIntent, however it is not consistent with FDC3 spec plus.
546
739
  // a mechanism is still needed to query for contexts. A better solution is to add two new InteropAdmin methods; e.g. getIntents() and getContexts().
547
740
  const message = {
@@ -549,28 +742,33 @@ export class FinsembleDesktopAgent {
549
742
  meta: this.meta(),
550
743
  payload: {
551
744
  context,
745
+ resultType,
552
746
  },
553
747
  };
554
748
  const { receiveMessage } = await this.exchange(message);
555
749
  const interopIntentsList = receiveMessage;
556
750
  if (interopIntentsList.payload.error) {
557
- throw this.fdc3Error(interopIntentsList.payload.error);
751
+ throw new Error(interopIntentsList.payload.error);
558
752
  }
559
753
  return interopIntentsList.payload.intents || [];
560
754
  }
755
+ // @ts-ignore <----------------------------------------------------------------------------------------------------------------------------------------------
756
+ async createPrivateChannel() {
757
+ return new ChannelProxy(this, this.privateChannelId(), "private");
758
+ }
561
759
  /**
562
- * Raises an intent to the desktop agent to resolve.
760
+ * Common implementation for raiseIntent() and raiseIntentForContext().
761
+ *
563
762
  * @param intent
564
763
  * @param context
565
764
  * @param target
566
765
  */
567
- async raiseIntent(intent, context, target) {
568
- if (!intent) {
569
- throw new Error("Invalid arguments. The required intent parameter is missing.");
570
- }
571
- if (!context) {
572
- throw new Error("Invalid arguments. The required context parameter is missing.");
766
+ async raiseIntentInternal(intent, context, target) {
767
+ if (context && !context.type) {
768
+ throw new Error("Invalid arguments. Second parameter must be a valid context.");
573
769
  }
770
+ const requestId = `raise-intent-request-Id-${uuidv4()}`;
771
+ Logger.system.log(`tag:FDC3:raiseIntent initiated for intent ${intent} with ${context ? " context type " : "no context"}${context === null || context === void 0 ? void 0 : context.type}`, context, target, requestId);
574
772
  const message = {
575
773
  type: "raiseIntent",
576
774
  meta: this.meta(),
@@ -578,67 +776,102 @@ export class FinsembleDesktopAgent {
578
776
  intent,
579
777
  context,
580
778
  target,
779
+ requestId,
581
780
  },
582
781
  };
782
+ Logger.system.log(`tag:FDC3:raiseIntent request sent to InteropService for intent ${intent}`, message);
783
+ // The message received back from the InteropService for the below exchange(message) corresponds to the IntentResolution (signaling the raiseIntent has been successfully sent to the target app, or an error).
784
+ // Asynchronously, an IntentResult will come in later.
583
785
  const { err, receiveMessage } = await this.exchange(message);
584
786
  const interopIntentResolution = receiveMessage;
787
+ Logger.system.log(`tag:FDC3:raiseIntent intent resolution received for intent ${intent}`, err, receiveMessage);
585
788
  if (err) {
586
- throw this.fdc3Error(ResolveError.ResolverUnavailable);
789
+ throw new Error(ResolveError.ResolverUnavailable);
587
790
  }
588
791
  else if (interopIntentResolution.payload.error) {
589
- throw this.fdc3Error(interopIntentResolution.payload.error);
792
+ throw new Error(interopIntentResolution.payload.error);
590
793
  }
591
794
  else if (!interopIntentResolution.payload.data) {
592
- throw this.fdc3Error("Invalid response returned from InteropService for raiseIntent. No data.");
795
+ throw new Error("Invalid response returned from InteropService for raiseIntent. No data.");
593
796
  }
594
- return interopIntentResolution.payload.data;
797
+ /* Allocate a promise for this raiseIntent and map it to the request id to handle the getResult() syncronization.
798
+
799
+ This promise is waited on below in the getResult function returned in the raiseIntent response,
800
+ then resolved in handleIntentResult when the result arrives for the requestId
801
+ */
802
+ __classPrivateFieldGet(this, _FinsembleDesktopAgent_intentResultPromiseMap, "f")[requestId] = new Promise((intentResultResolve) => {
803
+ // Map the resolve function to the requestID to be later invoked to complete this promise when the intent result arrives.
804
+ __classPrivateFieldGet(this, _FinsembleDesktopAgent_intentResultPromiseResolveMap, "f")[requestId] = intentResultResolve;
805
+ });
806
+ const intentResolution = {
807
+ source: interopIntentResolution.payload.data.source,
808
+ intent: interopIntentResolution.payload.data.intent,
809
+ version: interopIntentResolution.payload.data.version,
810
+ getResult: async () => {
811
+ let errorResult;
812
+ let intentResult;
813
+ Logger.system.log(`tag:FDC3:raiseIntent getResult initiated for intent ${intent}`, message);
814
+ if (typeof __classPrivateFieldGet(this, _FinsembleDesktopAgent_intentResultPromiseResolveMap, "f")[requestId] === "function") {
815
+ // Wait for the corresponding intent result to be returned.
816
+ // The promise waited on here is resolved in handleIntentResult when the result with the correct requestId arrives from InteropService.
817
+ ({ errorResult, intentResult } = await __classPrivateFieldGet(this, _FinsembleDesktopAgent_intentResultPromiseMap, "f")[requestId]);
818
+ }
819
+ else {
820
+ errorResult = "could not fulfill intent resolution due to no corresponding result promise";
821
+ }
822
+ // Any error has to be thrown here in this original thread of control for the app to receive it -- it cannot be thrown when the result is processed in handleIntentResult
823
+ if (errorResult) {
824
+ throw new Error(errorResult);
825
+ }
826
+ else if (!intentResult) {
827
+ throw new Error(ResultError.NoResultReturned);
828
+ }
829
+ const { channelId, channelType } = intentResult;
830
+ const fdc3IntentResult = channelId
831
+ ? new ChannelProxy(this, channelId, channelType)
832
+ : intentResult;
833
+ Logger.system.log(`tag:FDC3:raiseIntent result returned for intent ${intent}`, fdc3IntentResult);
834
+ return fdc3IntentResult;
835
+ },
836
+ };
837
+ return intentResolution;
595
838
  }
596
- /**
597
- * Finds and raises an intent against a target app based purely on context data.
598
- *
599
- * raiseIntentForContext is essentially a raiseIntent without a specific intent specified, hence the same "raiseIntent" Interop message is used.
600
- *
601
- * @param context
602
- * @param target
603
- */
604
- async raiseIntentForContext(context, target) {
839
+ async raiseIntent(intent, context, target) {
840
+ if (!intent) {
841
+ throw new Error("Invalid arguments. The required intent parameter is missing.");
842
+ }
605
843
  if (!context) {
606
844
  throw new Error("Invalid arguments. The required context parameter is missing.");
607
845
  }
608
- const message = {
609
- type: "raiseIntent",
610
- meta: this.meta(),
611
- payload: {
612
- context,
613
- target,
614
- },
615
- };
616
- const { err, receiveMessage } = await this.exchange(message);
617
- const interopRaiseIntentResponse = receiveMessage;
618
- if (err) {
619
- throw this.fdc3Error(ResolveError.ResolverUnavailable);
620
- }
621
- else if (interopRaiseIntentResponse.payload.error) {
622
- throw this.fdc3Error(interopRaiseIntentResponse.payload.error);
846
+ if (!context.type) {
847
+ throw new Error("Invalid arguments. Second parameter must be a valid context.");
623
848
  }
624
- else if (!interopRaiseIntentResponse.payload.data) {
625
- throw this.fdc3Error("Invalid response returned from InteropService for raiseIntentForContext. No data.");
849
+ return this.raiseIntentInternal(intent, context, target);
850
+ }
851
+ async raiseIntentForContext(context, target) {
852
+ if (!context) {
853
+ throw new Error("Invalid arguments. The required context parameter is missing.");
626
854
  }
627
- return interopRaiseIntentResponse.payload.data;
855
+ return this.raiseIntentInternal(null, context, target);
628
856
  }
629
857
  /**
630
- * Adds a listener for incoming Intents from the Agent.
858
+ * Adds a listener for incoming Intents from the Agent. Unlike addContextListener, there can only be one listener for an intent.
631
859
  * @param intent
632
860
  * @param handler
633
861
  */
634
862
  addIntentListener(intent, handler) {
635
- if (!handler) {
863
+ if (!intent) {
636
864
  throw new Error("Invalid arguments. The required intent parameter is missing.");
637
865
  }
638
866
  if (!handler) {
639
867
  throw new Error("Invalid arguments. The required handler parameter is missing.");
640
868
  }
641
- __classPrivateFieldGet(this, _FinsembleDesktopAgent_intentHandlers, "f")[intent] = mmAdd(__classPrivateFieldGet(this, _FinsembleDesktopAgent_intentHandlers, "f"), intent, handler);
869
+ if (__classPrivateFieldGet(this, _FinsembleDesktopAgent_intentHandlers, "f")[intent]) {
870
+ throw new Error(`Intent listener already defined for intent ${intent}`);
871
+ }
872
+ else {
873
+ __classPrivateFieldGet(this, _FinsembleDesktopAgent_intentHandlers, "f")[intent] = handler;
874
+ }
642
875
  const message = {
643
876
  type: "subscribeIntent",
644
877
  meta: this.meta(),
@@ -646,8 +879,9 @@ export class FinsembleDesktopAgent {
646
879
  intent,
647
880
  },
648
881
  };
649
- this.exchange(message); // don't wait for response
650
- return {
882
+ // Dangerous code: See note at top of file about 1.2 compatibility
883
+ const exchangePromise = this.exchange(message); // don't wait for response
884
+ const listener = {
651
885
  unsubscribe: () => {
652
886
  const unsubscribeMessage = {
653
887
  type: "unsubscribeIntent",
@@ -657,11 +891,75 @@ export class FinsembleDesktopAgent {
657
891
  },
658
892
  };
659
893
  this.exchange(unsubscribeMessage); // don't wait for response
660
- __classPrivateFieldGet(this, _FinsembleDesktopAgent_intentHandlers, "f")[intent] = mmRemove(__classPrivateFieldGet(this, _FinsembleDesktopAgent_intentHandlers, "f"), intent, handler);
894
+ delete __classPrivateFieldGet(this, _FinsembleDesktopAgent_intentHandlers, "f")[intent];
895
+ },
896
+ };
897
+ // Dangerous code: See note at top of file about 1.2 compatibility
898
+ let promise = new Promise((resolve) => {
899
+ exchangePromise.then(() => {
900
+ resolve(listener);
901
+ });
902
+ });
903
+ promise.unsubscribe = () => {
904
+ Logger.system.deprecated("Use of the synchronous version of addIntentListener from FDC3 1.2 is deprecated. Please switch to `await addIntentListener`.");
905
+ listener.unsubscribe();
906
+ };
907
+ return promise;
908
+ }
909
+ // Common functions to handler incoming Private Channel events to support onAddListener, onSubscribe, and oDisconnect
910
+ listenerForPrivateChannelEvent(subscribeType, channel, handler) {
911
+ // A unique targetSubscriberId must be passed in InteropSubscribeChannelOnAddContext; the InteropService can direct messages to this specific listener by returning the targetSubscriberId in a broadcast message,
912
+ const targetSubscriberId = `subscriberId.${channel}.${subscribeType}.${Date.now()}_${Math.random()}`;
913
+ // Bind targetSubscriberId to the specific handler and save in map
914
+ const handlerSet = { subscribeType, handler, channel, targetSubscriberId };
915
+ __classPrivateFieldGet(this, _FinsembleDesktopAgent_privateChannelSubscription, "f")[channel] = mmAdd(__classPrivateFieldGet(this, _FinsembleDesktopAgent_privateChannelSubscription, "f"), channel, handlerSet);
916
+ let message = {
917
+ type: "subscribePrivateChannelEvent",
918
+ payload: {
919
+ subscribeType,
920
+ channel,
921
+ targetSubscriberId,
922
+ },
923
+ meta: this.meta(),
924
+ };
925
+ this.exchange(message);
926
+ return {
927
+ unsubscribe: () => {
928
+ let unsubscribeMessage = {
929
+ type: "unsubscribePrivateChannelEvent",
930
+ payload: {
931
+ subscribeType,
932
+ channel,
933
+ targetSubscriberId,
934
+ },
935
+ meta: this.meta(),
936
+ };
937
+ __classPrivateFieldGet(this, _FinsembleDesktopAgent_privateChannelSubscription, "f")[channel] = mmRemove(__classPrivateFieldGet(this, _FinsembleDesktopAgent_privateChannelSubscription, "f"), channel, handlerSet);
938
+ this.exchange(unsubscribeMessage); // Don't wait for response
661
939
  },
662
940
  };
663
941
  }
664
942
  /** @internal */
943
+ channelOnAddContextListener(channel, handler) {
944
+ return this.listenerForPrivateChannelEvent("onAddContextListener", channel, handler);
945
+ }
946
+ /** @internal */
947
+ channelOnUnsubscribe(channel, handler) {
948
+ return this.listenerForPrivateChannelEvent("onUnsubscribe", channel, handler);
949
+ }
950
+ /** @internal */
951
+ channelOnDisconnect(channel, handler) {
952
+ return this.listenerForPrivateChannelEvent("onDisconnect", channel, handler);
953
+ }
954
+ /** @internal */
955
+ channelDisconnect(channel) {
956
+ let message = {
957
+ type: "channelDisconnect",
958
+ meta: Object.assign(Object.assign({}, this.meta()), { channel }),
959
+ };
960
+ this.exchange(message);
961
+ }
962
+ /** @internal */
665
963
  channelAddContextListener(channel, contextTypeOrHandler, handler) {
666
964
  // Derives a definitive contextType and handler from the varargs
667
965
  // Possibilities: addContextListener(handler) - deprecated 1.1, addContextListener(null, handler), addContextListener(contextType, handler)
@@ -683,8 +981,9 @@ export class FinsembleDesktopAgent {
683
981
  };
684
982
  if (channel)
685
983
  message.meta.channel = channel;
686
- this.exchange(message); // Don't wait for response
984
+ const exchangePromise = this.exchange(message);
687
985
  return {
986
+ exchangePromise,
688
987
  unsubscribe: () => {
689
988
  let unsubscribeMessage = {
690
989
  type: "unsubscribeContext",
@@ -713,7 +1012,20 @@ export class FinsembleDesktopAgent {
713
1012
  if (typeof contextTypeOrHandler !== "function" && !handler) {
714
1013
  throw new Error("Invalid arguments. The required handler parameter is missing.");
715
1014
  }
716
- return this.channelAddContextListener(null, contextTypeOrHandler, handler);
1015
+ // Dangerous code: See note at top of file about 1.2 compatibility
1016
+ const { exchangePromise, unsubscribe } = this.channelAddContextListener(null, contextTypeOrHandler, handler);
1017
+ let promise = new Promise((resolve) => {
1018
+ exchangePromise.then(() => {
1019
+ resolve({
1020
+ unsubscribe,
1021
+ });
1022
+ });
1023
+ });
1024
+ promise.unsubscribe = () => {
1025
+ Logger.system.deprecated("Use of the synchronous version of addContextListener from FDC3 1.2 is deprecated. Please switch to `await addContextListener`.");
1026
+ unsubscribe();
1027
+ };
1028
+ return promise;
717
1029
  }
718
1030
  /**
719
1031
  * Joins the app to the specified channel.
@@ -723,10 +1035,18 @@ export class FinsembleDesktopAgent {
723
1035
  * Error with a string from the ChannelError enumeration.
724
1036
  * @param channelId
725
1037
  */
726
- async joinChannel(channelId) {
1038
+ async joinUserChannel(channelId) {
727
1039
  if (!channelId) {
728
1040
  throw new Error("Invalid arguments. The required channelId parameter is missing.");
729
1041
  }
1042
+ if (__classPrivateFieldGet(this, _FinsembleDesktopAgent_existingChannelProxy, "f")[channelId]) {
1043
+ Logger.system.error("joinUserChannel: An `app` or `private` channel id was input to joinUserChannels. Only `user` channels can be joined. ");
1044
+ }
1045
+ // Move this test to the InteropService so no dely is added, which might cause a backwards compatability issue
1046
+ // const userChannels = await this.getUserChannels();
1047
+ // if (userChannels.find((app) => channelId === app.id) === undefined) {
1048
+ // throw new Error(ChannelError.NoChannelFound);
1049
+ // }
730
1050
  const message = {
731
1051
  type: "joinChannels",
732
1052
  meta: this.meta(),
@@ -734,7 +1054,17 @@ export class FinsembleDesktopAgent {
734
1054
  channels: [channelId],
735
1055
  },
736
1056
  };
737
- await this.exchange(message);
1057
+ const { err, receiveMessage } = await this.exchange(message);
1058
+ const interopJoinChannelsResponse = receiveMessage;
1059
+ if (err) {
1060
+ throw new Error(err);
1061
+ }
1062
+ else if (interopJoinChannelsResponse.payload.error) {
1063
+ throw new Error(interopJoinChannelsResponse.payload.error);
1064
+ }
1065
+ }
1066
+ async joinChannel(channelId) {
1067
+ return this.joinUserChannel(channelId);
738
1068
  }
739
1069
  /**
740
1070
  * Returns a Channel object for the specified channel, creating it (as an App channel) - if it does not exist.
@@ -743,13 +1073,21 @@ export class FinsembleDesktopAgent {
743
1073
  */
744
1074
  async getOrCreateChannel(channelId) {
745
1075
  if (!channelId) {
746
- throw new Error("Invalid arguments. The required channelId parameter is missing.");
1076
+ Logger.system.debug("getOrCreateChannel: The required channelId parameter is missing.");
1077
+ throw new Error(ChannelError.CreationFailed);
747
1078
  }
748
- return new ChannelProxy(this, channelId);
1079
+ else if (isPrivateChannelId(channelId)) {
1080
+ throw new Error(ChannelError.AccessDenied);
1081
+ }
1082
+ // if channel proxy has already been allocated for the channelId, then return it; otherwise allocate a new one
1083
+ let channelInstance = __classPrivateFieldGet(this, _FinsembleDesktopAgent_existingChannelProxy, "f")[channelId];
1084
+ if (!channelInstance) {
1085
+ channelInstance = new ChannelProxy(this, channelId);
1086
+ __classPrivateFieldGet(this, _FinsembleDesktopAgent_existingChannelProxy, "f")[channelId] = channelInstance;
1087
+ }
1088
+ return channelInstance;
749
1089
  }
750
1090
  /**
751
- * FDC3 2.0 pre-release
752
- *
753
1091
  * Get all joined channels
754
1092
  *
755
1093
  * Known race condition: getCurrentChannels() can return an empty string if it is called before the InteropService's linker module
@@ -765,15 +1103,15 @@ export class FinsembleDesktopAgent {
765
1103
  const { err, receiveMessage } = await this.exchange(message);
766
1104
  const interopGetCurrentChannelsResponse = receiveMessage;
767
1105
  if (err) {
768
- throw this.fdc3Error(err);
1106
+ throw new Error(err);
769
1107
  }
770
1108
  else if (interopGetCurrentChannelsResponse.payload.error) {
771
- throw this.fdc3Error(interopGetCurrentChannelsResponse.payload.error);
1109
+ throw new Error(interopGetCurrentChannelsResponse.payload.error);
772
1110
  }
773
1111
  else if (!interopGetCurrentChannelsResponse.payload.data) {
774
- throw this.fdc3Error("Invalid response returned from InteropService for getCurrentChannels. No data.");
1112
+ throw new Error("Invalid response returned from InteropService for getCurrentChannels. No data.");
775
1113
  }
776
- return interopGetCurrentChannelsResponse.payload.data.map((entry) => new ChannelProxy(this, entry.channel, "system"));
1114
+ return interopGetCurrentChannelsResponse.payload.data.map((entry) => new ChannelProxy(this, entry.channel, "user"));
777
1115
  }
778
1116
  /**
779
1117
  * Returns the Channel object for the current channel membership.
@@ -795,19 +1133,78 @@ export class FinsembleDesktopAgent {
795
1133
  };
796
1134
  await this.exchange(message);
797
1135
  }
1136
+ /**
1137
+ * Return specified app's meta data.
1138
+ */
1139
+ async getAppMetadata(app) {
1140
+ var _a;
1141
+ if (!app || !(app === null || app === void 0 ? void 0 : app.appId)) {
1142
+ throw new Error("getAppMetadata: The required app parameter is missing or not an AppIdentifier.");
1143
+ }
1144
+ const message = {
1145
+ type: "getAppMetadata",
1146
+ app,
1147
+ meta: this.meta(),
1148
+ };
1149
+ const { err, receiveMessage } = await this.exchange(message);
1150
+ const interopGetCurrentChannelsResponse = receiveMessage;
1151
+ if (err) {
1152
+ throw new Error(err);
1153
+ }
1154
+ else if (interopGetCurrentChannelsResponse.payload.error) {
1155
+ throw new Error(interopGetCurrentChannelsResponse.payload.error);
1156
+ }
1157
+ else if (!interopGetCurrentChannelsResponse.payload.appMetadata) {
1158
+ throw new Error("Invalid response returned from InteropService for getCurrentChannels. No data.");
1159
+ }
1160
+ return (_a = interopGetCurrentChannelsResponse.payload) === null || _a === void 0 ? void 0 : _a.appMetadata;
1161
+ }
1162
+ /**
1163
+ * Return all the open instances of given appID.
1164
+ */
1165
+ async findInstances(app) {
1166
+ let instances = [];
1167
+ const message = {
1168
+ type: "findInstances",
1169
+ app,
1170
+ meta: this.meta(),
1171
+ };
1172
+ if (app === null || app === void 0 ? void 0 : app.appId) {
1173
+ const { receiveMessage } = await this.exchange(message);
1174
+ const response = receiveMessage;
1175
+ ({ instances } = response.payload);
1176
+ }
1177
+ return instances;
1178
+ }
798
1179
  /**
799
1180
  * Retrieves information about the FDC3 Desktop Agent implementation, such as
800
1181
  * the implemented version of the FDC3 specification and the name of the implementation
801
1182
  * provider.
802
1183
  */
803
1184
  getInfo() {
804
- return {
805
- fdc3Version: "1.2",
806
- provider: "Finsemble",
807
- // This data is added at build time by webpack EnvironmentPlugin
808
- providerVersion: __classPrivateFieldGet(this, _FinsembleDesktopAgent_FSBLVersion, "f"),
809
- };
1185
+ // Dangerous code: See note at top of file about 1.2 compatibility
1186
+ let p = new Promise(async (resolve) => {
1187
+ const { data: FSBLVersion } = await ConfigClient.getValue({ field: "finsemble.system.FSBLVersion" });
1188
+ const { appMetadata: startupMetadata } = await this.fetchStartupData();
1189
+ // The interopService has more appMetaData than startup, plus it should be the single source of true for an app's metaData
1190
+ const fullAppMetadata = await this.getAppMetadata({ appId: startupMetadata.appId, instanceId: __classPrivateFieldGet(this, _FinsembleDesktopAgent_uuid, "f") });
1191
+ resolve({
1192
+ fdc3Version: "2.0",
1193
+ provider: "Finsemble",
1194
+ providerVersion: FSBLVersion,
1195
+ appMetadata: fullAppMetadata,
1196
+ optionalFeatures: {
1197
+ OriginatingAppMetadata: true,
1198
+ UserChannelMembershipAPIs: true,
1199
+ },
1200
+ });
1201
+ });
1202
+ // Backward compatibility for 1.2 which expects this to be synchronous
1203
+ p.fdc3Version = "1.2";
1204
+ p.provider = "Finsemble";
1205
+ p.providerVersion = "8.0.1";
1206
+ return p;
810
1207
  }
811
1208
  }
812
- _FinsembleDesktopAgent_connected = new WeakMap(), _FinsembleDesktopAgent_heartbeatMonitor = new WeakMap(), _FinsembleDesktopAgent_startHasBeenCalled = new WeakMap(), _FinsembleDesktopAgent_uuid = new WeakMap(), _FinsembleDesktopAgent_clientChannel = new WeakMap(), _FinsembleDesktopAgent_intentHandlers = new WeakMap(), _FinsembleDesktopAgent_contextHandlers = new WeakMap(), _FinsembleDesktopAgent_queue = new WeakMap(), _FinsembleDesktopAgent_routerClient = new WeakMap(), _FinsembleDesktopAgent_startup = new WeakMap(), _FinsembleDesktopAgent_FSBLVersion = new WeakMap();
1209
+ _FinsembleDesktopAgent_connected = new WeakMap(), _FinsembleDesktopAgent_heartbeatMonitor = new WeakMap(), _FinsembleDesktopAgent_startHasBeenCalled = new WeakMap(), _FinsembleDesktopAgent_uuid = new WeakMap(), _FinsembleDesktopAgent_clientChannel = new WeakMap(), _FinsembleDesktopAgent_intentHandlers = new WeakMap(), _FinsembleDesktopAgent_contextHandlers = new WeakMap(), _FinsembleDesktopAgent_privateChannelSubscription = new WeakMap(), _FinsembleDesktopAgent_intentResultPromiseMap = new WeakMap(), _FinsembleDesktopAgent_intentResultPromiseResolveMap = new WeakMap(), _FinsembleDesktopAgent_queue = new WeakMap(), _FinsembleDesktopAgent_routerClient = new WeakMap(), _FinsembleDesktopAgent_startup = new WeakMap(), _FinsembleDesktopAgent_existingChannelProxy = new WeakMap();
813
1210
  //# sourceMappingURL=FinsembleDesktopAgent.js.map