@openfin/cloud-interop-core-api 0.0.1-alpha.77c0e53 → 0.0.1-alpha.77c90ef

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.
@@ -1,7 +1,5 @@
1
- 'use strict';
2
-
3
- var mqtt = require('mqtt');
4
- var sharedUtils = require('@openfin/shared-utils');
1
+ import { Buffer } from 'buffer';
2
+ import mqtt from 'mqtt';
5
3
 
6
4
  class CloudInteropAPIError extends Error {
7
5
  code;
@@ -46,6 +44,8 @@ class EventController {
46
44
  }
47
45
  }
48
46
 
47
+ const isErrorIntentResult = (result) => 'error' in result;
48
+
49
49
  const APP_ID_DELIM = '::';
50
50
  const getRequestHeaders = (connectionParameters) => {
51
51
  const headers = {};
@@ -72,13 +72,9 @@ const getRequestHeaders = (connectionParameters) => {
72
72
  * @param source
73
73
  * @returns
74
74
  */
75
- const encodeAppIntents = (appIntents, { sessionId, sourceId }) => appIntents.map((intent) => ({
75
+ const encodeAppIntents = (appIntents, source) => appIntents.map((intent) => ({
76
76
  ...intent,
77
- apps: intent.apps.map((app) => {
78
- const id = encodeURIComponent(app.appId);
79
- const sId = encodeURIComponent(sourceId);
80
- return { ...app, appId: `${id}${APP_ID_DELIM}${sId}${APP_ID_DELIM}${sessionId}` };
81
- }),
77
+ apps: intent.apps.map((app) => ({ ...app, appId: encodeAppId(app.appId, source) })),
82
78
  }));
83
79
  /**
84
80
  * Decodes all app intents by URI decoding the parts previously encoded by `encodeAppIntents`
@@ -87,19 +83,35 @@ const encodeAppIntents = (appIntents, { sessionId, sourceId }) => appIntents.map
87
83
  */
88
84
  const decodeAppIntents = (appIntents) => appIntents.map((intent) => ({
89
85
  ...intent,
90
- apps: intent.apps.map((app) => {
91
- const [encodedAppId, encodedSourceId, sessionId] = app.appId.split(APP_ID_DELIM);
92
- const id = decodeURIComponent(encodedAppId);
93
- const sourceId = decodeURIComponent(encodedSourceId);
94
- return { ...app, appId: `${id}${APP_ID_DELIM}${sourceId}${APP_ID_DELIM}${sessionId}` };
95
- }),
86
+ apps: intent.apps.map((app) => ({ ...app, appId: decodeAppId(app.appId) })),
96
87
  }));
88
+ const encodeAppId = (appIdString, { sessionId, sourceId }) => {
89
+ const id = encodeURIComponent(appIdString);
90
+ const sId = encodeURIComponent(sourceId);
91
+ return `${id}${APP_ID_DELIM}${sId}${APP_ID_DELIM}${sessionId}`;
92
+ };
93
+ const decodeAppId = (appId) => {
94
+ const [encodedAppId, encodedSourceId, sessionId] = appId.split(APP_ID_DELIM);
95
+ const id = decodeURIComponent(encodedAppId);
96
+ const sourceId = decodeURIComponent(encodedSourceId);
97
+ return `${id}${APP_ID_DELIM}${sourceId}${APP_ID_DELIM}${sessionId}`;
98
+ };
97
99
  /**
98
- * Decodes the app id to extract the sessionId, returns '' if not able to parse
99
- * @param app
100
- * @returns
100
+ * Decodes the AppIdentifier to extract the appId, sourceId, and sessionId.
101
+ * @returns an object with:
102
+ * - appId: The appId, or the original appId if unable to parse.
103
+ * - sourceId: The sourceId, or an '' if unable to parse.
104
+ * - sessionId: The sessionId, or an '' if unable to parse.
101
105
  */
102
- const parseSessionId = (appId) => (typeof appId === 'string' ? appId : appId.appId).split(APP_ID_DELIM)?.[2]?.trim() ?? '';
106
+ const parseCloudAppId = (appId = '') => {
107
+ const originalAppString = typeof appId === 'string' ? appId : (appId.appId ?? '');
108
+ const parts = originalAppString.split(APP_ID_DELIM);
109
+ return {
110
+ appId: parts[0]?.trim() ?? originalAppString,
111
+ sourceId: parts[1]?.trim() ?? '',
112
+ sessionId: parts[2]?.trim() ?? '',
113
+ };
114
+ };
103
115
  const getSourceFromSession = (sessionDetails) => ({
104
116
  sessionId: sessionDetails.sessionId,
105
117
  sourceId: sessionDetails.sourceId,
@@ -148,13 +160,18 @@ class IntentController {
148
160
  body: JSON.stringify({ findOptions }),
149
161
  });
150
162
  if (!startResponse.ok) {
151
- throw new Error(startResponse.statusText);
163
+ throw new Error(`Error creating intent discovery record: ${startResponse.statusText}`);
152
164
  }
153
165
  // TODO: type this response?
154
166
  const json = await startResponse.json();
155
167
  this.#discovery.id = json.discoveryId;
156
168
  this.#discovery.sessionCount = json.sessionCount;
157
169
  this.#discovery.state = 'in-progress';
170
+ if (this.#discovery.sessionCount === 1) {
171
+ // since we have no other connected sessions, we can end discovery immediately
172
+ await this.#endIntentDiscovery(false);
173
+ return;
174
+ }
158
175
  // Listen out for discovery results directly sent to us
159
176
  await this.#mqttClient.subscribeAsync(`${this.#sessionDetails.sessionRootTopic}/commands/${this.#discovery.id}`);
160
177
  this.#discoveryTimeout = setTimeout(() => this.#endIntentDiscovery(), clampedTimeout);
@@ -165,10 +182,8 @@ class IntentController {
165
182
  throw new CloudInteropAPIError('Error starting intent discovery', 'ERR_STARTING_INTENT_DISCOVERY', error);
166
183
  }
167
184
  }
168
- async #endIntentDiscovery() {
185
+ async #endIntentDiscovery(mqttUnsubscribe = true) {
169
186
  if (this.#discovery.state !== 'in-progress') {
170
- // TODO: remove debug logs
171
- this.#logger('debug', 'Intent discovery not in progress');
172
187
  return;
173
188
  }
174
189
  if (this.#discoveryTimeout) {
@@ -178,10 +193,12 @@ class IntentController {
178
193
  this.#discovery.state = 'ended';
179
194
  // emit our aggregated events
180
195
  this.#events.emitEvent('aggregate-intent-details', { responses: this.#discovery.pendingIntentDetailsEvents });
181
- // gracefully end discovery
182
- await this.#mqttClient.unsubscribeAsync(`${this.#sessionDetails.sessionRootTopic}/commands/${this.#discovery.id}`).catch(() => {
183
- this.#logger('warn', `Error ending intent discovery: could not unsubscribe from discovery id ${this.#discovery.id}`);
184
- });
196
+ if (mqttUnsubscribe) {
197
+ // gracefully end discovery
198
+ await this.#mqttClient.unsubscribeAsync(`${this.#sessionDetails.sessionRootTopic}/commands/${this.#discovery.id}`).catch(() => {
199
+ this.#logger('warn', `Error ending intent discovery: could not unsubscribe from discovery id ${this.#discovery.id}`);
200
+ });
201
+ }
185
202
  await fetch(`${this.#url}/api/intents/${this.#sessionDetails.sessionId}/${this.#discovery.id}`, {
186
203
  method: 'DELETE',
187
204
  headers: getRequestHeaders(this.#connectionParams),
@@ -190,7 +207,6 @@ class IntentController {
190
207
  if (!deleteResponse.ok) {
191
208
  throw new Error(`Error ending intent discovery: ${deleteResponse.statusText}`);
192
209
  }
193
- this.#logger('debug', 'Intent discovery ended');
194
210
  })
195
211
  .catch((error) => {
196
212
  this.#logger('warn', `Error ending intent discovery: ${error}`);
@@ -199,7 +215,7 @@ class IntentController {
199
215
  this.#discovery = newDiscovery();
200
216
  }
201
217
  async raiseIntent({ raiseOptions, appId }) {
202
- const targetSessionId = parseSessionId(appId);
218
+ const targetSessionId = parseCloudAppId(appId).sessionId;
203
219
  if (!targetSessionId) {
204
220
  // TODO: should we add more info here about the format?
205
221
  throw new CloudInteropAPIError(`Invalid AppId specified, must be encoded as a cloud-session app id`, 'ERR_INVALID_TARGET_SESSION_ID');
@@ -234,6 +250,13 @@ class IntentController {
234
250
  return false;
235
251
  }
236
252
  async sendIntentResult(initiatingSessionId, result) {
253
+ if (!isErrorIntentResult(result)) {
254
+ // cloud-encode the source app id to support chained intent actions over cloud
255
+ // https://fdc3.finos.org/docs/2.0/api/spec#resolution-object -> "Use metadata about the resolving app instance to target a further intent"
256
+ const source = getSourceFromSession(this.#sessionDetails);
257
+ const encoded = encodeAppId(typeof result.source === 'string' ? result.source : result.source.appId, source);
258
+ result.source = typeof result.source === 'string' ? encoded : { ...result.source, appId: encoded };
259
+ }
237
260
  const { sessionId } = getSourceFromSession(this.#sessionDetails);
238
261
  const resultResponse = await fetch(`${this.#url}/api/intents/${initiatingSessionId}/result/${sessionId}`, {
239
262
  method: 'POST',
@@ -311,9 +334,8 @@ const BadUserNamePasswordError = 134;
311
334
  /**
312
335
  * Represents a single connection to a Cloud Interop service
313
336
  *
314
- * @export
315
- * @class CloudInteropAPI
316
- * @implements {Client}
337
+ * @public
338
+ * @class
317
339
  */
318
340
  class CloudInteropAPI {
319
341
  #cloudInteropSettings;
@@ -329,6 +351,7 @@ class CloudInteropAPI {
329
351
  #attemptingToReconnect = false;
330
352
  #events = new EventController();
331
353
  #intents;
354
+ #sessionTimer;
332
355
  constructor(cloudInteropSettings) {
333
356
  this.#cloudInteropSettings = cloudInteropSettings;
334
357
  }
@@ -341,11 +364,11 @@ class CloudInteropAPI {
341
364
  /**
342
365
  * Connects and creates a session on the Cloud Interop service
343
366
  *
344
- * @param {ConnectParameters} parameters - The parameters to use to connect
345
- * @return {*} {Promise<void>}
367
+ * @param parameters - The parameters to use to connect
368
+ * @returns Promise that resolves when connection is established
346
369
  * @memberof CloudInteropAPI
347
- * @throws {CloudInteropAPIError} - If an error occurs during connection
348
- * @throws {AuthorizationError} - If the connection is unauthorized
370
+ * @throws CloudInteropAPIError - If an error occurs during connection
371
+ * @throws AuthorizationError - If the connection is unauthorized
349
372
  */
350
373
  async connect(parameters) {
351
374
  this.#validateConnectParams(parameters);
@@ -369,6 +392,11 @@ class CloudInteropAPI {
369
392
  throw new CloudInteropAPIError(`Failed to connect to the Cloud Interop service: ${this.#cloudInteropSettings.url}`, 'ERR_CONNECT', new Error(createSessionResponse.statusText));
370
393
  }
371
394
  this.#sessionDetails = (await createSessionResponse.json());
395
+ // If local session expiry handling is enabled, start the session timer
396
+ if (this.#sessionDetails.localSessionExpiryHandling) {
397
+ this.#logger('debug', `Local session expiry handling is enabled`);
398
+ this.#startSessionTimer();
399
+ }
372
400
  const sessionRootTopic = this.#sessionDetails.sessionRootTopic;
373
401
  const clientOptions = {
374
402
  keepalive: this.#keepAliveIntervalSeconds,
@@ -398,9 +426,7 @@ class CloudInteropAPI {
398
426
  if (error instanceof mqtt.ErrorWithReasonCode) {
399
427
  switch (error.code) {
400
428
  case BadUserNamePasswordError: {
401
- await this.#disconnect(false);
402
- this.#logger('warn', `Session expired`);
403
- this.#events.emitEvent('session-expired');
429
+ this.#handleSessionExpiry();
404
430
  return;
405
431
  }
406
432
  default: {
@@ -454,9 +480,9 @@ class CloudInteropAPI {
454
480
  /**
455
481
  * Disconnects from the Cloud Interop service
456
482
  *
457
- * @return {*} {Promise<void>}
483
+ * @returns Promise that resolves when disconnected
458
484
  * @memberof CloudInteropAPI
459
- * @throws {CloudInteropAPIError} - If an error occurs during disconnection
485
+ * @throws CloudInteropAPIError - If an error occurs during disconnection
460
486
  */
461
487
  async disconnect() {
462
488
  await this.#disconnect(true);
@@ -464,9 +490,9 @@ class CloudInteropAPI {
464
490
  /**
465
491
  * Publishes a new context for the given context group to the other connected sessions
466
492
  *
467
- * @param {string} contextGroup - The context group to publish to
468
- * @param {object} context - The context to publish
469
- * @return {*} {Promise<void>}
493
+ * @param contextGroup - The context group to publish to
494
+ * @param context - The context to publish
495
+ * @returns Promise that resolves when context is published
470
496
  * @memberof CloudInteropAPI
471
497
  */
472
498
  async setContext(contextGroup, context) {
@@ -493,9 +519,9 @@ class CloudInteropAPI {
493
519
  /**
494
520
  * Starts an intent discovery operation
495
521
  *
496
- * @return {*} {Promise<void>}
522
+ * @returns Promise that resolves when intent discovery is started
497
523
  * @memberof CloudInteropAPI
498
- * @throws {CloudInteropAPIError} - If an error occurs during intent discovery
524
+ * @throws CloudInteropAPIError - If an error occurs during intent discovery
499
525
  */
500
526
  async startIntentDiscovery(options) {
501
527
  this.#throwIfNotConnected();
@@ -514,7 +540,10 @@ class CloudInteropAPI {
514
540
  return this.#intents?.sendIntentResult(initiatingSessionId, result);
515
541
  }
516
542
  parseSessionId(appId) {
517
- return parseSessionId(appId);
543
+ return parseCloudAppId(appId).sessionId;
544
+ }
545
+ parseAppId(appId) {
546
+ return parseCloudAppId(appId).appId;
518
547
  }
519
548
  addEventListener(type, callback) {
520
549
  this.#events.addEventListener(type, callback);
@@ -526,10 +555,18 @@ class CloudInteropAPI {
526
555
  this.#events.once(type, callback);
527
556
  }
528
557
  async #disconnect(fireDisconnectedEvent) {
529
- if (!this.#sessionDetails || !this.#connectionParams) {
558
+ if (!this.#sessionDetails) {
530
559
  return;
531
560
  }
532
561
  try {
562
+ if (!this.#connectionParams) {
563
+ throw new Error('Connect parameters must be provided');
564
+ }
565
+ // Cancel session timer if it's running
566
+ if (this.#sessionTimer) {
567
+ clearTimeout(this.#sessionTimer);
568
+ this.#sessionTimer = undefined;
569
+ }
533
570
  const disconnectResponse = await fetch(`${this.#cloudInteropSettings.url}/api/sessions/${this.#sessionDetails.sessionId}`, {
534
571
  method: 'DELETE',
535
572
  headers: getRequestHeaders(this.#connectionParams),
@@ -565,8 +602,13 @@ class CloudInteropAPI {
565
602
  if (contextEvent.source.sessionId === sessionDetails.sessionId) {
566
603
  return;
567
604
  }
568
- const { contextGroup, context, source, history } = contextEvent;
569
- this.#events.emitEvent('context', { contextGroup, context, source, history: { ...history, clientReceived: Date.now() } });
605
+ const { context, payload, contextGroup, channelName, source, history } = contextEvent;
606
+ this.#events.emitEvent('context', {
607
+ contextGroup: channelName || contextGroup,
608
+ context: payload || context,
609
+ source,
610
+ history: { ...history, clientReceived: Date.now() },
611
+ });
570
612
  }
571
613
  else if (topic.startsWith(`${sessionDetails.sessionRootTopic}/commands`)) {
572
614
  this.#handleCommandMessage(messageEnvelope);
@@ -623,14 +665,88 @@ class CloudInteropAPI {
623
665
  throw new Error('MQTT client not connected');
624
666
  }
625
667
  }
668
+ /**
669
+ * Extracts the expiration timestamp from a JWT token.
670
+ *
671
+ * @param token - The JWT token string
672
+ * @returns The expiration timestamp in seconds, or null if extraction fails
673
+ */
674
+ #extractExpirationFromJwt(token) {
675
+ try {
676
+ // JWT tokens have three parts separated by dots: header.payload.signature
677
+ // The exp claim is in the payload
678
+ const parts = token.split('.');
679
+ if (parts.length < 2) {
680
+ this.#logger('warn', 'Invalid JWT token format: expected at least 2 parts');
681
+ return null;
682
+ }
683
+ const payload = parts[1];
684
+ // Decode base64url encoded payload
685
+ const decodedBytes = Buffer.from(payload, 'base64url');
686
+ const payloadJson = decodedBytes.toString('utf8');
687
+ // Parse JSON to get the exp claim
688
+ const claims = JSON.parse(payloadJson);
689
+ const exp = claims.exp;
690
+ if (exp === undefined || exp === null) {
691
+ this.#logger('warn', "JWT token does not contain 'exp' claim");
692
+ return null;
693
+ }
694
+ if (typeof exp !== 'number') {
695
+ this.#logger('warn', `JWT token 'exp' claim is not a number: ${exp}`);
696
+ return null;
697
+ }
698
+ return exp;
699
+ }
700
+ catch (error) {
701
+ this.#logger('error', `Failed to extract expiration from JWT token: ${error instanceof Error ? error.message : error}`);
702
+ return null;
703
+ }
704
+ }
705
+ /**
706
+ * Start a session timer that will expire at the time specified in the JWT token's exp claim.
707
+ * When the timer fires, it executes the same actions as the BadUserNamePasswordError case.
708
+ */
709
+ #startSessionTimer() {
710
+ if (!this.#sessionDetails?.localSessionExpiryHandling) {
711
+ return;
712
+ }
713
+ const token = this.#sessionDetails.token;
714
+ if (!token) {
715
+ this.#logger('warn', 'Cannot start session timer: token not available');
716
+ return;
717
+ }
718
+ // Extract expiration time from JWT token
719
+ const expTimestamp = this.#extractExpirationFromJwt(token);
720
+ if (expTimestamp === null) {
721
+ this.#logger('warn', 'Cannot start session timer: could not extract expiration from JWT token');
722
+ return;
723
+ }
724
+ const currentTimeSeconds = Math.floor(Date.now() / 1000);
725
+ const delaySeconds = expTimestamp - currentTimeSeconds;
726
+ if (delaySeconds <= 0) {
727
+ this.#logger('warn', 'JWT token has already expired or expires immediately');
728
+ // Execute the same actions as BadUserNamePasswordError case
729
+ this.#handleSessionExpiry();
730
+ return;
731
+ }
732
+ // Clear any existing timer
733
+ if (this.#sessionTimer) {
734
+ clearTimeout(this.#sessionTimer);
735
+ }
736
+ const expirationTimeString = new Date(expTimestamp * 1000).toISOString();
737
+ this.#logger('debug', `Starting session timer to expire in ${delaySeconds} seconds (at ${expirationTimeString})`);
738
+ this.#sessionTimer = setTimeout(async () => {
739
+ this.#handleSessionExpiry();
740
+ }, delaySeconds * 1000);
741
+ }
742
+ /**
743
+ * Handles session expiry by executing the same actions as the BadUserNamePasswordError case.
744
+ */
745
+ async #handleSessionExpiry() {
746
+ await this.#disconnect(false);
747
+ this.#logger('warn', 'Session expired');
748
+ this.#events.emitEvent('session-expired');
749
+ }
626
750
  }
627
751
 
628
- exports.AuthorizationError = AuthorizationError;
629
- exports.CloudInteropAPI = CloudInteropAPI;
630
- exports.CloudInteropAPIError = CloudInteropAPIError;
631
- Object.keys(sharedUtils).forEach(function (k) {
632
- if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
633
- enumerable: true,
634
- get: function () { return sharedUtils[k]; }
635
- });
636
- });
752
+ export { AuthorizationError, CloudInteropAPI, CloudInteropAPIError };
package/package.json CHANGED
@@ -1,231 +1,18 @@
1
1
  {
2
- "name": "@openfin/cloud-interop-core-api",
3
- "version": "0.0.1-alpha.77c0e53",
4
- "type": "module",
5
- "description": "",
6
- "files": [
7
- "./dist/*"
8
- ],
9
- "main": "./dist/index.cjs",
10
- "browser": "./dist/index.mjs",
11
- "types": "./dist/index.d.ts",
12
- "scripts": {
13
- "build": "rimraf dist && rollup -c",
14
- "build:watch": "rollup -c --watch",
15
- "typecheck": "tsc --noEmit",
16
- "test": "jest --coverage",
17
- "lint": "eslint . --max-warnings 0",
18
- "lint:fix": "eslint . --fix --max-warnings 0"
19
- },
20
- "author": "",
21
- "license": "SEE LICENSE IN LICENSE.md",
22
- "devDependencies": {
23
- "@rollup/plugin-commonjs": "^28.0.1",
24
- "@rollup/plugin-inject": "^5.0.5",
25
- "@rollup/plugin-node-resolve": "^15.2.3",
26
- "@rollup/plugin-typescript": "^11.1.6",
27
- "@types/jest": "^29.5.14",
28
- "@types/node": "^22.7.7",
29
- "@typescript-eslint/eslint-plugin": "^7.5.0",
30
- "@typescript-eslint/parser": "^7.15.0",
31
- "eslint": "^8.57.0",
32
- "eslint-config-prettier": "^9.1.0",
33
- "eslint-plugin-check-file": "^2.8.0",
34
- "eslint-plugin-prettier": "^5.2.1",
35
- "eslint-plugin-simple-import-sort": "^12.1.1",
36
- "eslint-plugin-unicorn": "^55.0.0",
37
- "eslint-plugin-unused-imports": "^4.1.4",
38
- "jest": "^29.7.0",
39
- "prettier": "^3.3.3",
40
- "rollup": "^4.9.6",
41
- "ts-jest": "^29.2.5",
42
- "typescript": "^5.6.3"
43
- },
44
- "dependencies": {
45
- "@openfin/shared-utils": "file:../shared-utils",
46
- "mqtt": "^5.3.1"
47
- },
48
- "eslintConfig": {
49
- "env": {
50
- "browser": true,
51
- "node": true
52
- },
53
- "parser": "@typescript-eslint/parser",
54
- "parserOptions": {
55
- "ecmaVersion": "latest",
56
- "project": true,
57
- "sourceType": "module"
58
- },
59
- "extends": [
60
- "eslint:recommended",
61
- "plugin:@typescript-eslint/recommended",
62
- "plugin:@typescript-eslint/strict",
63
- "plugin:unicorn/recommended",
64
- "plugin:prettier/recommended"
65
- ],
66
- "plugins": [
67
- "prettier",
68
- "check-file",
69
- "simple-import-sort",
70
- "unused-imports"
71
- ],
72
- "rules": {
73
- "unused-imports/no-unused-imports": "warn",
74
- "unicorn/prevent-abbreviations": [
75
- "error",
76
- {
77
- "replacements": {
78
- "props": false,
79
- "prop": false,
80
- "ref": false,
81
- "args": false,
82
- "arg": false,
83
- "src": false,
84
- "dev": false,
85
- "str": false,
86
- "req": false,
87
- "res": false
88
- }
89
- }
90
- ],
91
- "@typescript-eslint/no-non-null-assertion": "error",
92
- "unicorn/no-nested-ternary": "off",
93
- "unicorn/no-array-for-each": "off",
94
- "unicorn/no-useless-undefined": "off",
95
- "unicorn/no-null": "off",
96
- "eqeqeq": [
97
- "error",
98
- "always"
99
- ],
100
- "no-alert": "error",
101
- "no-eval": "error",
102
- "prettier/prettier": "warn",
103
- "simple-import-sort/imports": [
104
- "error",
105
- {
106
- "groups": [
107
- [
108
- "^react$"
109
- ],
110
- [
111
- "^react"
112
- ],
113
- [
114
- "^next"
115
- ],
116
- [
117
- "^zod"
118
- ],
119
- [
120
- "^@radix-ui/"
121
- ],
122
- [
123
- "^[^.]"
124
- ],
125
- [
126
- "@/components/ui/.*"
127
- ],
128
- [
129
- "@/components/.*"
130
- ],
131
- [
132
- "@/config/.*"
133
- ],
134
- [
135
- "@/lib/.*"
136
- ],
137
- [
138
- "^\\.\\.(?!/?$)",
139
- "^\\.\\./?$"
140
- ],
141
- [
142
- "^\\./(?=.*/)(?!/?$)",
143
- "^\\.(?!/?$)",
144
- "^\\./?$"
145
- ],
146
- [
147
- "^.+\\.s?css$"
148
- ]
149
- ]
150
- }
151
- ],
152
- "check-file/no-index": "off",
153
- "check-file/filename-naming-convention": [
154
- "error",
155
- {
156
- "**/*.{jsx,tsx}": "KEBAB_CASE",
157
- "**/*.{js,ts}": "KEBAB_CASE"
158
- },
159
- {
160
- "ignoreMiddleExtensions": true
161
- }
162
- ],
163
- "check-file/filename-blocklist": [
164
- "error",
165
- {
166
- "**/*.spec.js": "*.test.js",
167
- "**/*.spec.jsx": "*.test.jsx",
168
- "**/*.spec.ts": "*.test.ts",
169
- "**/*.spec.tsx": "*.test.tsx"
170
- }
171
- ],
172
- "no-restricted-syntax": [
173
- "error",
174
- {
175
- "selector": "TSEnumDeclaration",
176
- "message": "Prefer string unions to enums."
177
- }
178
- ],
179
- "curly": [
180
- "error",
181
- "multi-line"
182
- ],
183
- "@typescript-eslint/consistent-type-definitions": [
184
- "error",
185
- "type"
186
- ],
187
- "@typescript-eslint/no-unused-vars": [
188
- "warn",
189
- {
190
- "argsIgnorePattern": "^_",
191
- "varsIgnorePattern": "^_"
192
- }
193
- ],
194
- "@typescript-eslint/no-explicit-any": "warn"
195
- },
196
- "ignorePatterns": [
197
- "node_modules",
198
- "out",
199
- "build",
200
- "dist",
201
- "coverage",
202
- "tests",
203
- "rollup.config.mjs",
204
- "examples"
205
- ]
206
- },
207
- "jest": {
208
- "collectCoverage": true,
209
- "collectCoverageFrom": [
210
- "src/**/*.ts"
211
- ],
212
- "coverageReporters": [
213
- "lcov",
214
- "text-summary"
215
- ],
216
- "preset": "ts-jest",
217
- "restoreMocks": true,
218
- "setupFiles": [],
219
- "testMatch": [
220
- "**/tests/*.test.ts"
221
- ],
222
- "testTimeout": 100000
223
- },
224
- "prettier": {
225
- "printWidth": 160,
226
- "semi": true,
227
- "singleQuote": true,
228
- "tabWidth": 4,
229
- "trailingComma": "all"
230
- }
2
+ "name": "@openfin/cloud-interop-core-api",
3
+ "version": "0.0.1-alpha.77c90ef",
4
+ "type": "module",
5
+ "description": "",
6
+ "main": "./index.cjs",
7
+ "browser": "./index.mjs",
8
+ "types": "./bundle.d.ts",
9
+ "author": "",
10
+ "license": "SEE LICENSE IN LICENSE.md",
11
+ "dependencies": {
12
+ "mqtt": "^5.13.0",
13
+ "zod": "^3.24.4"
14
+ },
15
+ "optionalDependencies": {
16
+ "@rollup/rollup-linux-x64-gnu": "*"
17
+ }
231
18
  }