@openfin/cloud-interop-core-api 0.0.1-alpha.fba3468 → 0.0.1-alpha.fe79c9b

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,231 +1,18 @@
1
1
  {
2
- "name": "@openfin/cloud-interop-core-api",
3
- "version": "0.0.1-alpha.fba3468",
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": "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": "^20.10.0",
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
- "axios": "^1.6.2",
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.fe79c9b",
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
  }
package/dist/api.d.ts DELETED
@@ -1,63 +0,0 @@
1
- import mqtt from 'mqtt';
2
- import { CloudInteropSettings, ConnectParameters as ConnectParameters, ContextEvent } from './interfaces';
3
- type CreateSessionResponse = {
4
- sessionId: string;
5
- sessionRootTopic: string;
6
- url: string;
7
- token: string;
8
- orgId: string;
9
- sub: string;
10
- platformId: string;
11
- sourceId: string;
12
- };
13
- type EventMap = {
14
- reconnected: () => void;
15
- disconnected: () => void;
16
- context: (event: ContextEvent) => void;
17
- reconnecting: (attemptNo: number) => void;
18
- error: (error: Error) => void;
19
- 'session-expired': () => void;
20
- };
21
- /**
22
- * Represents a single connection to a Cloud Interop service
23
- *
24
- * @export
25
- * @class CloudInteropAPI
26
- * @implements {Client}
27
- */
28
- export declare class CloudInteropAPI {
29
- #private;
30
- constructor(cloudInteropSettings: CloudInteropSettings);
31
- get sessionDetails(): CreateSessionResponse | undefined;
32
- get mqttClient(): mqtt.MqttClient | undefined;
33
- /**
34
- * Connects and creates a session on the Cloud Interop service
35
- *
36
- * @param {ConnectParameters} parameters - The parameters to use to connect
37
- * @return {*} {Promise<void>}
38
- * @memberof CloudInteropAPI
39
- * @throws {CloudInteropAPIError} - If an error occurs during connection
40
- * @throws {AuthorizationError} - If the connection is unauthorized
41
- */
42
- connect(parameters: ConnectParameters): Promise<void>;
43
- /**
44
- * Disconnects from the Cloud Interop service
45
- *
46
- * @return {*} {Promise<void>}
47
- * @memberof CloudInteropAPI
48
- * @throws {CloudInteropAPIError} - If an error occurs during disconnection
49
- */
50
- disconnect(): Promise<void>;
51
- /**
52
- * Publishes a new context for the given context group to the other connected sessions
53
- *
54
- * @param {string} contextGroup - The context group to publish to
55
- * @param {object} context - The context to publish
56
- * @return {*} {Promise<void>}
57
- * @memberof CloudInteropAPI
58
- */
59
- setContext(contextGroup: string, context: object): Promise<void>;
60
- addEventListener<K extends keyof EventMap>(type: K, callback: EventMap[K]): void;
61
- removeEventListener<K extends keyof EventMap>(type: K, callback: EventMap[K]): void;
62
- }
63
- export {};
@@ -1,7 +0,0 @@
1
- export declare class CloudInteropAPIError extends Error {
2
- code: string;
3
- constructor(message?: string, code?: string, cause?: unknown);
4
- }
5
- export declare class AuthorizationError extends CloudInteropAPIError {
6
- constructor(message?: string, code?: string);
7
- }
package/dist/index.cjs DELETED
@@ -1,296 +0,0 @@
1
- 'use strict';
2
-
3
- var axios = require('axios');
4
- var mqtt = require('mqtt');
5
-
6
- class CloudInteropAPIError extends Error {
7
- code;
8
- constructor(message = 'An unexpected error has occurred', code = 'UNEXPECTED_ERROR', cause) {
9
- super(message, { cause: cause });
10
- this.name = this.constructor.name;
11
- this.code = code;
12
- }
13
- }
14
- class AuthorizationError extends CloudInteropAPIError {
15
- constructor(message = 'Not authorized', code = 'ERR_UNAUTHORIZED') {
16
- super(message, code, undefined);
17
- }
18
- }
19
-
20
- // Error codes as defined in https://docs.emqx.com/en/cloud/latest/connect_to_deployments/mqtt_client_error_codes.html
21
- const BadUserNamePasswordError = 134;
22
- /**
23
- * Represents a single connection to a Cloud Interop service
24
- *
25
- * @export
26
- * @class CloudInteropAPI
27
- * @implements {Client}
28
- */
29
- class CloudInteropAPI {
30
- #cloudInteropSettings;
31
- #sessionDetails;
32
- #mqttClient;
33
- #reconnectRetryLimit = 30;
34
- #keepAliveIntervalSeconds = 30;
35
- #logger = (level, message) => {
36
- console[level](message);
37
- };
38
- #reconnectRetries = 0;
39
- #connectionParams;
40
- #eventListeners = new Map();
41
- #attemptingToReconnect = false;
42
- constructor(cloudInteropSettings) {
43
- this.#cloudInteropSettings = cloudInteropSettings;
44
- }
45
- get sessionDetails() {
46
- return this.#sessionDetails;
47
- }
48
- get mqttClient() {
49
- return this.#mqttClient;
50
- }
51
- /**
52
- * Connects and creates a session on the Cloud Interop service
53
- *
54
- * @param {ConnectParameters} parameters - The parameters to use to connect
55
- * @return {*} {Promise<void>}
56
- * @memberof CloudInteropAPI
57
- * @throws {CloudInteropAPIError} - If an error occurs during connection
58
- * @throws {AuthorizationError} - If the connection is unauthorized
59
- */
60
- async connect(parameters) {
61
- this.#validateConnectParams(parameters);
62
- this.#connectionParams = parameters;
63
- this.#reconnectRetryLimit = parameters.reconnectRetryLimit || this.#reconnectRetryLimit;
64
- this.#keepAliveIntervalSeconds = parameters.keepAliveIntervalSeconds || this.#keepAliveIntervalSeconds;
65
- this.#logger = parameters.logger || this.#logger;
66
- const { sourceId, platformId } = this.#connectionParams;
67
- try {
68
- const createSessionResponse = await axios.post(`${this.#cloudInteropSettings.url}/api/sessions`, {
69
- sourceId,
70
- platformId,
71
- }, {
72
- headers: this.#getRequestHeaders(),
73
- });
74
- if (createSessionResponse.status !== 201) {
75
- throw new CloudInteropAPIError(`Failed to connect to the Cloud Interop service: ${this.#cloudInteropSettings.url}`, 'ERR_CONNECT', createSessionResponse.status);
76
- }
77
- this.#sessionDetails = createSessionResponse.data;
78
- const sessionRootTopic = this.#sessionDetails.sessionRootTopic;
79
- const clientOptions = {
80
- keepalive: this.#keepAliveIntervalSeconds,
81
- clientId: this.#sessionDetails.sessionId,
82
- clean: true,
83
- protocolVersion: 5,
84
- // The "will" message will be published on an unexpected disconnection
85
- // The server can then tidy up. So it needs every for this client to do that, the session details is perfect
86
- will: {
87
- topic: 'interop/lastwill',
88
- payload: Buffer.from(JSON.stringify(this.#sessionDetails)),
89
- qos: 0,
90
- retain: false,
91
- properties: {
92
- willDelayInterval: 10,
93
- },
94
- },
95
- username: this.#sessionDetails.token,
96
- };
97
- this.#mqttClient = await mqtt.connectAsync(this.#sessionDetails.url, clientOptions);
98
- this.#logger('log', `Cloud Interop successfully connected to ${this.#cloudInteropSettings.url}`);
99
- this.#mqttClient.on('error', async (error) => {
100
- // We will receive errors for each failed reconnection attempt
101
- // We don't won't to disconnect on these else we will never reconnect
102
- if (!this.#attemptingToReconnect) {
103
- await this.#disconnect(false);
104
- }
105
- if (error instanceof mqtt.ErrorWithReasonCode) {
106
- switch (error.code) {
107
- case BadUserNamePasswordError: {
108
- await this.#disconnect(false);
109
- this.#logger('warn', `Session expired`);
110
- this.#emitEvent('session-expired');
111
- return;
112
- }
113
- default: {
114
- this.#logger('error', `Unknown Infrastructure Error Code ${error.code} : ${error.message}${this.#attemptingToReconnect ? ' during reconnection attempt' : ''}`);
115
- // As we are in the middle of a reconnect, lets not emit an error to cut down on the event noise
116
- if (!this.#attemptingToReconnect) {
117
- this.#emitEvent('error', new CloudInteropAPIError(`Unknown Infrastructure Error Code ${error.code} : ${error.message}`, 'ERR_INFRASTRUCTURE', error));
118
- break;
119
- }
120
- }
121
- }
122
- }
123
- else {
124
- this.#logger('error', `Unknown Error${this.#attemptingToReconnect ? ' during reconnection attempt' : ''}: ${error}`);
125
- // As we are in the middle of a reconnect, lets not emit an error to cut down on the event noise
126
- if (!this.#attemptingToReconnect) {
127
- this.#emitEvent('error', new CloudInteropAPIError(`Unknown Error`, 'ERR_UNKNOWN', error));
128
- }
129
- }
130
- });
131
- this.#mqttClient.on('reconnect', () => {
132
- this.#attemptingToReconnect = true;
133
- this.#reconnectRetries += 1;
134
- this.#logger('debug', `Cloud Interop attempting reconnection - ${this.#reconnectRetries}...`);
135
- if (this.#reconnectRetries === this.#reconnectRetryLimit) {
136
- this.#logger('warn', `Cloud Interop reached max reconnection attempts - ${this.#reconnectRetryLimit}...`);
137
- this.#disconnect(true);
138
- }
139
- this.#emitEvent('reconnecting', this.#reconnectRetries);
140
- });
141
- // Does not fire on initial connection, only successful reconnection attempts
142
- this.#mqttClient.on('connect', () => {
143
- this.#logger('debug', `Cloud Interop successfully reconnected after ${this.#reconnectRetries} attempts`);
144
- this.#reconnectRetries = 0;
145
- this.#attemptingToReconnect = false;
146
- this.#emitEvent('reconnected');
147
- });
148
- this.#mqttClient.on('message', (topic, message) => {
149
- if (!this.#sessionDetails) {
150
- this.#logger('warn', 'Received message when session not connected');
151
- return;
152
- }
153
- this.#handleCommand(topic, message, this.#sessionDetails);
154
- });
155
- // Subscribe to all context groups
156
- this.#mqttClient.subscribe(`${sessionRootTopic}/context-groups/#`);
157
- // Listen out for global commands
158
- this.#mqttClient.subscribe(`${sessionRootTopic}/commands`);
159
- }
160
- catch (error) {
161
- if (axios.isAxiosError(error)) {
162
- if (error.response?.status === 401 || error.response?.status === 403) {
163
- throw new AuthorizationError();
164
- }
165
- throw new CloudInteropAPIError();
166
- }
167
- throw error;
168
- }
169
- }
170
- /**
171
- * Disconnects from the Cloud Interop service
172
- *
173
- * @return {*} {Promise<void>}
174
- * @memberof CloudInteropAPI
175
- * @throws {CloudInteropAPIError} - If an error occurs during disconnection
176
- */
177
- async disconnect() {
178
- await this.#disconnect(true);
179
- }
180
- /**
181
- * Publishes a new context for the given context group to the other connected sessions
182
- *
183
- * @param {string} contextGroup - The context group to publish to
184
- * @param {object} context - The context to publish
185
- * @return {*} {Promise<void>}
186
- * @memberof CloudInteropAPI
187
- */
188
- async setContext(contextGroup, context) {
189
- if (!this.#sessionDetails || !this.#connectionParams) {
190
- throw new Error('Session not connected');
191
- }
192
- const payload = {
193
- context,
194
- timestamp: Date.now(),
195
- };
196
- await axios.post(`${this.#cloudInteropSettings.url}/api/context-groups/${this.#sessionDetails.sessionId}/${contextGroup}`, payload, {
197
- headers: this.#getRequestHeaders(),
198
- });
199
- }
200
- addEventListener(type, callback) {
201
- const listeners = this.#eventListeners.get(type) || [];
202
- listeners.push(callback);
203
- this.#eventListeners.set(type, listeners);
204
- }
205
- removeEventListener(type, callback) {
206
- const listeners = this.#eventListeners.get(type) || [];
207
- const index = listeners.indexOf(callback);
208
- if (index !== -1) {
209
- listeners.splice(index, 1);
210
- }
211
- this.#eventListeners.set(type, listeners);
212
- }
213
- async #disconnect(fireDisconnectedEvent) {
214
- if (!this.#sessionDetails) {
215
- return;
216
- }
217
- try {
218
- const disconnectResponse = await axios.delete(`${this.#cloudInteropSettings.url}/api/sessions/${this.#sessionDetails.sessionId}`, {
219
- headers: this.#getRequestHeaders(),
220
- });
221
- if (disconnectResponse.status !== 200) {
222
- throw new CloudInteropAPIError('Error during session tear down - unexpected status', 'ERR_DISCONNECT', disconnectResponse.status);
223
- }
224
- }
225
- catch {
226
- throw new CloudInteropAPIError('Error during disconnection', 'ERR_DISCONNECT');
227
- }
228
- finally {
229
- this.#mqttClient?.removeAllListeners();
230
- await this.#mqttClient?.endAsync(true);
231
- this.#sessionDetails = undefined;
232
- this.#mqttClient = undefined;
233
- this.#reconnectRetries = 0;
234
- this.#attemptingToReconnect = false;
235
- if (fireDisconnectedEvent) {
236
- this.#emitEvent('disconnected');
237
- }
238
- }
239
- }
240
- #handleCommand(topic, message, sessionDetails) {
241
- if (message.length === 0 || !sessionDetails) {
242
- // Ignore clean up messages
243
- return;
244
- }
245
- const messageEnvelope = JSON.parse(message.toString());
246
- if (topic.startsWith(`${sessionDetails.sessionRootTopic}/context-groups/`)) {
247
- if (messageEnvelope.source.sessionId === sessionDetails.sessionId) {
248
- return;
249
- }
250
- const { channelName: contextGroup, payload: context, source, history } = messageEnvelope;
251
- this.#emitEvent('context', { contextGroup, context, source, history: { ...history, clientReceived: Date.now() } });
252
- }
253
- }
254
- #emitEvent(type, ...args) {
255
- const listeners = this.#eventListeners.get(type) || [];
256
- listeners.forEach((listener) => listener(...args));
257
- }
258
- #validateConnectParams = (parameters) => {
259
- if (!parameters) {
260
- throw new Error('Connect parameters must be provided');
261
- }
262
- if (parameters.authenticationType === 'jwt' &&
263
- (!parameters.jwtAuthenticationParameters?.jwtRequestCallback || !parameters.jwtAuthenticationParameters?.authenticationId)) {
264
- throw new Error('jwtAuthenticationParameters must be provided when using jwt authentication');
265
- }
266
- if (parameters.authenticationType === 'basic' &&
267
- (!parameters.basicAuthenticationParameters?.username || !parameters.basicAuthenticationParameters?.password)) {
268
- throw new Error('basicAuthenticationParameters must be provided when using basic authentication');
269
- }
270
- };
271
- #getRequestHeaders = () => {
272
- if (!this.#connectionParams) {
273
- throw new Error('Connect parameters must be provided');
274
- }
275
- const headers = new axios.AxiosHeaders();
276
- headers['Content-Type'] = 'application/json';
277
- if (this.#connectionParams.authenticationType === 'jwt' && this.#connectionParams.jwtAuthenticationParameters) {
278
- const tokenResult = this.#connectionParams.jwtAuthenticationParameters.jwtRequestCallback();
279
- if (!tokenResult) {
280
- throw new Error('jwtRequestCallback must return a token');
281
- }
282
- headers['x-of-auth-id'] = this.#connectionParams.jwtAuthenticationParameters.authenticationId;
283
- headers['Authorization'] =
284
- typeof tokenResult === 'string' ? `Bearer ${tokenResult}` : `Bearer ${Buffer.from(JSON.stringify(tokenResult)).toString('base64')}`;
285
- }
286
- if (this.#connectionParams.authenticationType === 'basic' && this.#connectionParams.basicAuthenticationParameters) {
287
- const { username, password } = this.#connectionParams.basicAuthenticationParameters;
288
- headers['Authorization'] = `Basic ${Buffer.from(`${username}:${password}`).toString('base64')}`;
289
- }
290
- return headers;
291
- };
292
- }
293
-
294
- exports.AuthorizationError = AuthorizationError;
295
- exports.CloudInteropAPI = CloudInteropAPI;
296
- exports.CloudInteropAPIError = CloudInteropAPIError;
package/dist/index.d.ts DELETED
@@ -1,3 +0,0 @@
1
- export * from './api';
2
- export * from './errors/api.error';
3
- export * from './interfaces';