@openfin/core 30.74.11 → 30.74.13

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openfin/core",
3
- "version": "30.74.11",
3
+ "version": "30.74.13",
4
4
  "license": "SEE LICENSE IN LICENSE.MD",
5
5
  "main": "./src/mock.js",
6
6
  "types": "./src/mock.d.ts",
@@ -4,9 +4,9 @@ import { Transport } from '../../../transport/transport';
4
4
  import { AnyStrategy } from './protocols/strategy-types';
5
5
  declare type ProviderIdentity = OpenFin.ProviderIdentity;
6
6
  declare type DisconnectionListener = (providerIdentity: ProviderIdentity) => any;
7
- interface RoutingInfo extends ProviderIdentity {
7
+ export declare type RoutingInfo = ProviderIdentity & {
8
8
  endpointId: string;
9
- }
9
+ };
10
10
  export default class ChannelClient extends ChannelBase {
11
11
  #private;
12
12
  private disconnectListener;
@@ -1,9 +1,9 @@
1
1
  import type * as OpenFin from '../../../OpenFin';
2
- import ChannelClient from './client';
3
- import { ChannelProvider } from './provider';
2
+ import { Message, Transport } from '../../../transport/transport';
4
3
  import { EmitterBase } from '../../base';
5
- import { Transport, Message } from '../../../transport/transport';
6
4
  import { ChannelEvent } from '../../events/channel';
5
+ import ChannelClient from './client';
6
+ import { ChannelProvider } from './provider';
7
7
  declare type ProviderIdentity = OpenFin.ProviderIdentity;
8
8
  declare type Identity = OpenFin.Identity;
9
9
  export interface ChannelMessage extends Message<any> {
@@ -15,10 +15,10 @@ export interface ChannelMessage extends Message<any> {
15
15
  export declare class Channel extends EmitterBase<ChannelEvent> {
16
16
  #private;
17
17
  constructor(wire: Transport);
18
- private channelExists;
19
18
  getAllChannels(): Promise<ProviderIdentity[]>;
20
19
  onChannelConnect(listener: (...args: any[]) => void): Promise<void>;
21
20
  onChannelDisconnect(listener: (...args: any[]) => void): Promise<void>;
21
+ private safeConnect;
22
22
  connect(channelName: string, options?: OpenFin.ChannelConnectOptions): Promise<ChannelClient>;
23
23
  create(channelName: string, options?: OpenFin.ChannelCreateOptions): Promise<ChannelProvider>;
24
24
  }
@@ -14,12 +14,25 @@ var _Channel_connectionManager, _Channel_internalEmitter, _Channel_readyToConnec
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.Channel = void 0;
16
16
  /* eslint-disable no-console */
17
- const client_1 = require("./client");
18
- const provider_1 = require("./provider");
19
- const base_1 = require("../../base");
20
- const connection_manager_1 = require("./connection-manager");
21
17
  const events_1 = require("events");
22
18
  const lazy_1 = require("../../../util/lazy");
19
+ const base_1 = require("../../base");
20
+ const client_1 = require("./client");
21
+ const connection_manager_1 = require("./connection-manager");
22
+ const provider_1 = require("./provider");
23
+ function retryDelay(count) {
24
+ const interval = 500; // base delay
25
+ const steps = 10; // How many retries to do before incrementing the delay
26
+ const base = 2; // How much to multiply the previous delay by
27
+ const max = 30000; // max delay
28
+ const step = Math.floor(count / steps);
29
+ const delay = Math.min(max, interval * base ** step);
30
+ return new Promise((resolve) => {
31
+ setTimeout(() => {
32
+ resolve(false);
33
+ }, delay);
34
+ });
35
+ }
23
36
  class Channel extends base_1.EmitterBase {
24
37
  constructor(wire) {
25
38
  super(wire, 'channel');
@@ -38,10 +51,6 @@ class Channel extends base_1.EmitterBase {
38
51
  }));
39
52
  __classPrivateFieldSet(this, _Channel_connectionManager, new connection_manager_1.ConnectionManager(wire), "f");
40
53
  }
41
- async channelExists(channelName) {
42
- const channels = await this.getAllChannels();
43
- return channels.some((providerIdentity) => providerIdentity.channelName === channelName);
44
- }
45
54
  async getAllChannels() {
46
55
  return this.wire.sendAction('get-all-channels').then(({ payload }) => payload.data);
47
56
  }
@@ -51,6 +60,57 @@ class Channel extends base_1.EmitterBase {
51
60
  async onChannelDisconnect(listener) {
52
61
  await this.on('disconnected', listener);
53
62
  }
63
+ async safeConnect(channelName, shouldWait, connectPayload) {
64
+ const retryInfo = { count: 0 };
65
+ /* eslint-disable no-await-in-loop, no-constant-condition */
66
+ do {
67
+ // setup a listener and a connected promise to await in case we connect before the channel is ready
68
+ let connectedListener = () => undefined;
69
+ const connectedPromise = new Promise((resolve) => {
70
+ connectedListener = (payload) => {
71
+ if (channelName === payload.channelName) {
72
+ resolve(true);
73
+ }
74
+ };
75
+ __classPrivateFieldGet(this, _Channel_internalEmitter, "f").on('connected', connectedListener);
76
+ });
77
+ try {
78
+ if (retryInfo.count > 0) {
79
+ // Wait before retrying
80
+ // Delay returns false connectedPromise returns true so we can know if a retry is due to connected event
81
+ retryInfo.gotConnectedEvent = await Promise.race([retryDelay(retryInfo.count), connectedPromise]);
82
+ const result = await this.wire.sendAction('connect-to-channel', { ...connectPayload, retryInfo });
83
+ // log only if there was a retry
84
+ console.log(`Successfully connected to channelName: ${channelName}`);
85
+ return result.payload.data;
86
+ }
87
+ // Send retryInfo to the core for debug log inclusion
88
+ const sentMessagePromise = this.wire.sendAction('connect-to-channel', connectPayload);
89
+ // Save messageId from the first connection attempt
90
+ retryInfo.originalMessageId = sentMessagePromise.messageId;
91
+ const result = await sentMessagePromise;
92
+ return result.payload.data;
93
+ }
94
+ catch (error) {
95
+ if (!error.message.includes('internal-nack')) {
96
+ // Not an internal nack, break the loop
97
+ throw error;
98
+ }
99
+ if (shouldWait && retryInfo.count === 0) {
100
+ // start waiting on the next iteration, warn the user
101
+ console.warn(`No channel found for channelName: ${channelName}. Waiting for connection...`);
102
+ }
103
+ }
104
+ finally {
105
+ retryInfo.count += 1;
106
+ // in case of other errors, remove our listener
107
+ __classPrivateFieldGet(this, _Channel_internalEmitter, "f").removeListener('connected', connectedListener);
108
+ }
109
+ } while (shouldWait); // If we're waiting we retry the above loop
110
+ // Should wait was false, no channel was found.
111
+ throw new Error(`No channel found for channelName: ${channelName}.`);
112
+ /* eslint-enable no-await-in-loop, no-constant-condition */
113
+ }
54
114
  async connect(channelName, options = {}) {
55
115
  // Make sure we don't connect before listeners are set up
56
116
  // This also errors if we're not in OpenFin, ensuring we don't run unnecessary code
@@ -59,58 +119,35 @@ class Channel extends base_1.EmitterBase {
59
119
  throw new Error('Please provide a channelName string to connect to a channel.');
60
120
  }
61
121
  const opts = { wait: true, ...this.wire.environment.getDefaultChannelOptions().connect, ...options };
62
- const shouldWait = opts.wait;
63
- if (shouldWait) {
64
- const channelExists = await this.channelExists(channelName);
65
- if (!channelExists) {
66
- console.warn(`Channel not found for channelName: ${channelName}, waiting for channel connection.`);
67
- await new Promise((resolve) => {
68
- const connectedListener = (payload) => {
69
- if (channelName === payload.channelName) {
70
- __classPrivateFieldGet(this, _Channel_internalEmitter, "f").removeListener('connected', connectedListener);
71
- resolve();
72
- }
73
- };
74
- __classPrivateFieldGet(this, _Channel_internalEmitter, "f").on('connected', connectedListener);
75
- });
76
- }
77
- }
78
- try {
79
- const { offer, rtc: rtcPacket } = await __classPrivateFieldGet(this, _Channel_connectionManager, "f").createClientOffer(opts);
80
- const connectionUrl = (await this.fin.me.getInfo()).url;
81
- const { payload: { data: routingInfo } } = await this.wire.sendAction('connect-to-channel', {
82
- channelName,
83
- ...opts,
84
- offer,
85
- connectionUrl
86
- });
87
- const strategy = await __classPrivateFieldGet(this, _Channel_connectionManager, "f").createClientStrategy(rtcPacket, routingInfo);
88
- const channel = new client_1.default(routingInfo, this.wire, strategy);
89
- // It is the client's responsibility to handle endpoint disconnection to the provider.
90
- // If the endpoint dies, the client will force a disconnection through the core.
91
- // The provider does not care about endpoint disconnection.
92
- strategy.onEndpointDisconnect(routingInfo.channelId, async () => {
93
- try {
94
- await channel.sendDisconnectAction();
95
- }
96
- catch (error) {
97
- console.warn(`Something went wrong during disconnect for client with uuid: ${routingInfo.uuid} / name: ${routingInfo.name} / endpointId: ${routingInfo.endpointId}.`);
98
- }
99
- finally {
100
- client_1.default.handleProviderDisconnect(routingInfo);
101
- }
102
- });
103
- return channel;
122
+ const { offer, rtc: rtcPacket } = await __classPrivateFieldGet(this, _Channel_connectionManager, "f").createClientOffer(opts);
123
+ let connectionUrl;
124
+ if (this.fin.me.isFrame || this.fin.me.isView || this.fin.me.isWindow) {
125
+ connectionUrl = (await this.fin.me.getInfo()).url;
104
126
  }
105
- catch (error) {
106
- const internalNackMessage = 'internal-nack';
107
- if (error.message.includes(internalNackMessage)) {
108
- throw new Error(`No channel found for channelName: ${channelName}.`);
127
+ const connectPayload = {
128
+ channelName,
129
+ ...opts,
130
+ offer,
131
+ connectionUrl
132
+ };
133
+ const routingInfo = await this.safeConnect(channelName, opts.wait, connectPayload);
134
+ const strategy = await __classPrivateFieldGet(this, _Channel_connectionManager, "f").createClientStrategy(rtcPacket, routingInfo);
135
+ const channel = new client_1.default(routingInfo, this.wire, strategy);
136
+ // It is the client's responsibility to handle endpoint disconnection to the provider.
137
+ // If the endpoint dies, the client will force a disconnection through the core.
138
+ // The provider does not care about endpoint disconnection.
139
+ strategy.onEndpointDisconnect(routingInfo.channelId, async () => {
140
+ try {
141
+ await channel.sendDisconnectAction();
109
142
  }
110
- else {
111
- throw new Error(error);
143
+ catch (error) {
144
+ console.warn(`Something went wrong during disconnect for client with uuid: ${routingInfo.uuid} / name: ${routingInfo.name} / endpointId: ${routingInfo.endpointId}.`);
112
145
  }
113
- }
146
+ finally {
147
+ client_1.default.handleProviderDisconnect(routingInfo);
148
+ }
149
+ });
150
+ return channel;
114
151
  }
115
152
  async create(channelName, options) {
116
153
  if (!channelName) {
@@ -10,7 +10,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
10
10
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
11
11
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
12
12
  };
13
- var _ClassicStrategy_wire, _ClassicStrategy_endpointIdentityMap;
13
+ var _ClassicStrategy_wire, _ClassicStrategy_endpointIdentityMap, _ClassicStrategy_pendingMessagesByEndpointId;
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.ClassicInfo = exports.ClassicStrategy = void 0;
16
16
  /*
@@ -26,6 +26,9 @@ class ClassicStrategy {
26
26
  // Store full endpointIdentity by endpointId of all known endpoints for this strategy instance.
27
27
  // (clients will only have 1: the provider, the provider will have all clients)
28
28
  _ClassicStrategy_endpointIdentityMap.set(this, new Map());
29
+ // Store a set of cancellable promises to be able to reject them when client
30
+ // connection problems occur
31
+ _ClassicStrategy_pendingMessagesByEndpointId.set(this, new Map());
29
32
  this.send = async (endpointId, action, payload) => {
30
33
  const to = __classPrivateFieldGet(this, _ClassicStrategy_endpointIdentityMap, "f").get(endpointId);
31
34
  if (!to) {
@@ -78,6 +81,6 @@ class ClassicStrategy {
78
81
  }
79
82
  }
80
83
  exports.ClassicStrategy = ClassicStrategy;
81
- _ClassicStrategy_wire = new WeakMap(), _ClassicStrategy_endpointIdentityMap = new WeakMap();
84
+ _ClassicStrategy_wire = new WeakMap(), _ClassicStrategy_endpointIdentityMap = new WeakMap(), _ClassicStrategy_pendingMessagesByEndpointId = new WeakMap();
82
85
  // Arbitrarily starting at 5 to leave the door open to backfilling pre endpointId etc.
83
86
  exports.ClassicInfo = { version: 5, minimumVersion: 0, type: 'classic' };
@@ -1,15 +1,24 @@
1
1
  /// <reference types="node" />
2
+ /// <reference types="node" />
2
3
  import { EventEmitter } from 'events';
3
4
  import type * as OpenFin from '../OpenFin';
4
5
  import { ExistingConnectConfig, InternalConnectConfig, RemoteConfig, Wire, WireConstructor } from './wire';
5
6
  import { Environment } from '../environment/environment';
7
+ import { RuntimeErrorPayload } from './transport-errors';
6
8
  import EventAggregator from '../api/events/eventAggregator';
7
9
  import { EntityTypeHelpers } from '../api/me';
8
10
  import { ProtocolMap } from '../shapes/protocol';
9
11
  import { NamedEvent } from '../api/events/base';
10
12
  import { ErrorPlainObject } from '../util/errors';
11
13
  declare type EntityType = OpenFin.EntityType;
14
+ declare type SendActionResponse<T extends keyof ProtocolMap> = Message<{
15
+ data: ProtocolMap[T]['response'];
16
+ } & ProtocolMap[T]['specialResponse']>;
12
17
  export declare type MessageHandler = (data: any) => boolean;
18
+ export declare type SentMessage<Value> = Promise<Value> & {
19
+ cancel: (reason?: any) => void;
20
+ messageId: ReturnType<Environment['getNextMessageId']>;
21
+ };
13
22
  export declare class Transport<MeType extends EntityType = EntityType> extends EventEmitter {
14
23
  #private;
15
24
  protected wireListeners: Map<number, {
@@ -31,9 +40,8 @@ export declare class Transport<MeType extends EntityType = EntityType> extends E
31
40
  private connectRemote;
32
41
  connectByPort(config: ExistingConnectConfig): Promise<void>;
33
42
  private authorize;
34
- sendAction<T extends keyof ProtocolMap = string>(action: T, payload?: ProtocolMap[T]['request'], uncorrelated?: boolean): Promise<Message<{
35
- data: ProtocolMap[T]['response'];
36
- } & ProtocolMap[T]['specialResponse']>>;
43
+ sendAction<T extends keyof ProtocolMap = string>(action: T, payload?: ProtocolMap[T]['request'], uncorrelated?: boolean): SentMessage<SendActionResponse<T>>;
44
+ protected nackHandler(payloadOrMessage: RuntimeErrorPayload | string, reject: Function, callSites?: NodeJS.CallSite[]): void;
37
45
  ferryAction(origData: any): Promise<Message<any>>;
38
46
  registerMessageHandler(handler: MessageHandler): void;
39
47
  protected addWireListener(id: number, resolve: Function, reject: Function, uncorrelated: boolean): void;
@@ -101,17 +101,31 @@ class Transport extends events_1.EventEmitter {
101
101
  sendAction(action, payload = {}, uncorrelated = false
102
102
  // specialResponse type is only used for 'requestAuthorization'
103
103
  ) {
104
- return new Promise((resolve, reject) => {
105
- const id = this.environment.getNextMessageId();
104
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
105
+ let cancel = () => { };
106
+ // We want the callsite from the caller of this function, not from here.
107
+ const messageId = this.environment.getNextMessageId();
108
+ const prom = new Promise((resolve, reject) => {
109
+ cancel = reject;
106
110
  const msg = {
107
111
  action,
108
112
  payload,
109
- messageId: id
113
+ messageId
110
114
  };
111
115
  const wire = __classPrivateFieldGet(this, _Transport_wire, "f");
112
- this.addWireListener(id, resolve, reject, uncorrelated);
116
+ this.addWireListener(messageId, resolve, reject, uncorrelated);
113
117
  return wire.send(msg).catch(reject);
114
118
  });
119
+ return Object.assign(prom, { cancel, messageId });
120
+ }
121
+ nackHandler(payloadOrMessage, reject, callSites) {
122
+ if (typeof payloadOrMessage === 'string') {
123
+ // NOTE: this is for backwards compatibility to support plain string rejections
124
+ reject(payloadOrMessage);
125
+ }
126
+ else {
127
+ reject(new transport_errors_1.RuntimeError(payloadOrMessage));
128
+ }
115
129
  }
116
130
  ferryAction(origData) {
117
131
  return new Promise((resolve, reject) => {