@aicore/cocodb-ws-client 1.0.8 → 1.0.10

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/README.md CHANGED
@@ -2,33 +2,74 @@
2
2
  WebSocket client for cocoDb. This will client can pipeline the requests to increase through put connection
3
3
 
4
4
  ## Code Guardian
5
- [![<app> build verification](https://github.com/aicore/template-nodejs/actions/workflows/build_verify.yml/badge.svg)](https://github.com/aicore/template-nodejs/actions/workflows/build_verify.yml)
6
-
7
- <a href="https://sonarcloud.io/summary/new_code?id=aicore_template-nodejs-ts">
8
- <img src="https://sonarcloud.io/api/project_badges/measure?project=aicore_template-nodejs-ts&metric=alert_status" alt="Sonar code quality check" />
9
- <img src="https://sonarcloud.io/api/project_badges/measure?project=aicore_template-nodejs-ts&metric=security_rating" alt="Security rating" />
10
- <img src="https://sonarcloud.io/api/project_badges/measure?project=aicore_template-nodejs-ts&metric=vulnerabilities" alt="vulnerabilities" />
11
- <img src="https://sonarcloud.io/api/project_badges/measure?project=aicore_template-nodejs-ts&metric=coverage" alt="Code Coverage" />
12
- <img src="https://sonarcloud.io/api/project_badges/measure?project=aicore_template-nodejs-ts&metric=bugs" alt="Code Bugs" />
13
- <img src="https://sonarcloud.io/api/project_badges/measure?project=aicore_template-nodejs-ts&metric=reliability_rating" alt="Reliability Rating" />
14
- <img src="https://sonarcloud.io/api/project_badges/measure?project=aicore_template-nodejs-ts&metric=sqale_rating" alt="Maintainability Rating" />
15
- <img src="https://sonarcloud.io/api/project_badges/measure?project=aicore_template-nodejs-ts&metric=ncloc" alt="Lines of Code" />
16
- <img src="https://sonarcloud.io/api/project_badges/measure?project=aicore_template-nodejs-ts&metric=sqale_index" alt="Technical debt" />
5
+ [![<app> build verification](https://github.com/aicore/cocoDbWsClient/actions/workflows/build_verify.yml/badge.svg)](https://github.com/aicore/template-nodejs/actions/workflows/build_verify.yml)
6
+
7
+ <a href="https://sonarcloud.io/summary/new_code?id=aicore_cocoDbWsClient">
8
+ <img src="https://sonarcloud.io/api/project_badges/measure?project=aicore_cocoDbWsClient&metric=alert_status" alt="Sonar code quality check" />
9
+ <img src="https://sonarcloud.io/api/project_badges/measure?project=aicore_cocoDbWsClient&metric=security_rating" alt="Security rating" />
10
+ <img src="https://sonarcloud.io/api/project_badges/measure?project=aicore_cocoDbWsClient&metric=vulnerabilities" alt="vulnerabilities" />
11
+ <img src="https://sonarcloud.io/api/project_badges/measure?project=aicore_cocoDbWsClient&metric=coverage" alt="Code Coverage" />
12
+ <img src="https://sonarcloud.io/api/project_badges/measure?project=aicore_cocoDbWsClient&metric=bugs" alt="Code Bugs" />
13
+ <img src="https://sonarcloud.io/api/project_badges/measure?project=aicore_cocoDbWsClient&metric=reliability_rating" alt="Reliability Rating" />
14
+ <img src="https://sonarcloud.io/api/project_badges/measure?project=aicore_cocoDbWsClient&metric=sqale_rating" alt="Maintainability Rating" />
15
+ <img src="https://sonarcloud.io/api/project_badges/measure?project=aicore_cocoDbWsClient&metric=ncloc" alt="Lines of Code" />
16
+ <img src="https://sonarcloud.io/api/project_badges/measure?project=aicore_cocoDbWsClient&metric=sqale_index" alt="Technical debt" />
17
17
  </a>
18
18
 
19
+ ## Installing the library
20
+
21
+ ```bash
22
+ npm install @aicore/cocodb-ws-client
23
+ ```
24
+
25
+ ## importing in your js file
26
+
27
+ ```js
28
+ import * as coco from "@aicore/cocodb-ws-client"; // to import all functions
29
+ ```
30
+
31
+ ## Initializing the client
32
+
33
+ Create a connection to the cocoDbServiceEndPoint and listens for messages. The connection will
34
+ be maintained and it will try to automatically re-establish broken connections if there are network issues.
35
+ You need to await on this function before staring to use any db APIs. Any APIs called while the connection is
36
+ not fully setup will throw an error.
37
+
38
+ ### Parameters
39
+
40
+ * `cocoDbServiceEndPoint` **\[string]\[1]** The URL of the coco-db service.
41
+ * `authKey` **\[string]\[1]** The authKey is a base64 encoded string of the username and password.
42
+
43
+ Returns **\[Promise]\[2]\<null>** Resolves when the cocodb client is ready to send/receive requests for the first time.
44
+ Rejects only if the user calls `close` API before any connection is established.
45
+
46
+ ```js
47
+ await db.init("ws://endpoint.coco", "your_auth_key");
48
+ ```
49
+
50
+ ## Detailed API Docs
51
+
52
+ See this wiki for detailed API docs
53
+
54
+ * [https://github.com/aicore/cocoDbWsClient/wiki/api-API][1]
55
+ * More coco lib level detailed docs can be found at [https://github.com/aicore/libmysql/wiki/db-API][2]
56
+
57
+ ## close the client
58
+
59
+ Closes the connection to the server. You need to await on this function before you can call init again.
60
+
61
+ Returns **\[Promise]\[2]\<null>** Resolves when the cocodb client is closed and you are free to call init again. Never rejects.
62
+
63
+ ```js
64
+ await db.close();
65
+ ```
66
+
67
+ [1]: https://github.com/aicore/cocoDbWsClient/wiki/api-API
68
+
69
+ [2]: https://github.com/aicore/libmysql/wiki/db-API
19
70
 
20
- # TODOs after template use
21
- ## !!!Please see all issues in the generated repository as an issue will be generated tracking the fix of each of the below items.
22
- 1. Update package.json with your app defaults
23
- 2. Check Build actions on pull requests.
24
- 3. create a home page in wiki by going to wiki link https://github.com/<your_org>/<your_repo>/wiki
25
- 4. Goto github `repository` > `settings`> and uncheck `Allow merge commits`. this is usually automatically done by code guardian bots in core.ai org. so you may just need to verify it.
26
- 5. In sonar cloud, enable Automatic analysis from `Administration
27
- Analysis Method` for the first time before a pull request is raised: ![image](https://user-images.githubusercontent.com/5336369/148695840-65585d04-5e59-450b-8794-54ca3c62b9fe.png)
28
- 6. Check codacy runs on pull requests, set codacy defaults. You may remove codacy if sonar cloud is only needed.
29
- 7. Update the above Code Guardian badges; change all `id=aicore_template-nodejs-ts` to the sonar id of your project fields. see this PR: https://github.com/aicore/libcache/pull/13
30
71
 
31
- # Commands available
72
+ # Development notes
32
73
 
33
74
  ## Building
34
75
  Since this is a pure JS template project, build command just runs test with coverage.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aicore/cocodb-ws-client",
3
- "version": "1.0.8",
3
+ "version": "1.0.10",
4
4
  "description": "Websocket client for cocoDb",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -47,23 +47,23 @@
47
47
  },
48
48
  "homepage": "https://github.com/aicore/cocoDbWsClient#readme",
49
49
  "devDependencies": {
50
- "@commitlint/cli": "17.1.2",
51
- "@commitlint/config-conventional": "17.1.0",
50
+ "@commitlint/cli": "17.4.2",
51
+ "@commitlint/config-conventional": "17.4.2",
52
52
  "c8": "7.12.0",
53
- "chai": "4.3.6",
53
+ "chai": "4.3.7",
54
54
  "cli-color": "2.0.3",
55
- "documentation": "14.0.0",
56
- "eslint": "8.25.0",
57
- "glob": "8.0.3",
58
- "husky": "8.0.1",
59
- "mocha": "10.1.0"
55
+ "documentation": "14.0.1",
56
+ "eslint": "8.33.0",
57
+ "glob": "8.1.0",
58
+ "husky": "8.0.3",
59
+ "mocha": "10.2.0"
60
60
  },
61
61
  "dependencies": {
62
62
  "@aicore/libcommonutils": "1.0.19",
63
- "ws": "8.9.0"
63
+ "ws": "8.12.0"
64
64
  },
65
65
  "optionalDependencies": {
66
66
  "bufferutil": "4.0.7",
67
- "utf-8-validate": "5.0.10"
67
+ "utf-8-validate": "6.0.2"
68
68
  }
69
69
  }
package/src/index.js CHANGED
@@ -16,6 +16,8 @@
16
16
  *
17
17
  */
18
18
 
19
+ // @INCLUDE_IN_API_DOCS
20
+
19
21
  export {
20
22
  get,
21
23
  init,
@@ -35,3 +37,47 @@ export {
35
37
  query
36
38
 
37
39
  } from './utils/api.js';
40
+
41
+ /**
42
+ * ## Installing the library
43
+ * ```bash
44
+ * npm install @aicore/cocodb-ws-client
45
+ * ```
46
+ * ## importing in your js file
47
+ * ```js
48
+ * import * as coco from "@aicore/cocodb-ws-client"; // to import all functions
49
+ * ```
50
+ *
51
+ * ## Initializing the client
52
+ *
53
+ * Create a connection to the cocoDbServiceEndPoint and listens for messages. The connection will
54
+ * be maintained and it will try to automatically re-establish broken connections if there are network issues.
55
+ * You need to await on this function before staring to use any db APIs. Any APIs called while the connection is
56
+ * not fully setup will throw an error.
57
+ *
58
+ * ### Parameters
59
+ *
60
+ * * `cocoDbServiceEndPoint` **[string][1]** The URL of the coco-db service.
61
+ * * `authKey` **[string][1]** The authKey is a base64 encoded string of the username and password.
62
+ *
63
+ * Returns **[Promise][2]\<null>** Resolves when the cocodb client is ready to send/receive requests for the first time.
64
+ * Rejects only if the user calls `close` API before any connection is established.
65
+ * ```js
66
+ * await db.init("ws://endpoint.coco", "your_auth_key");
67
+ * ```
68
+ *
69
+ * ## Detailed API Docs
70
+ * See this wiki for detailed API docs
71
+ * * https://github.com/aicore/cocoDbWsClient/wiki/api-API
72
+ * * More coco lib level detailed docs can be found at https://github.com/aicore/libmysql/wiki/db-API
73
+ *
74
+ * ## close the client
75
+ *
76
+ * Closes the connection to the server. You need to await on this function before you can call init again.
77
+ *
78
+ * Returns **[Promise][2]\<null>** Resolves when the cocodb client is closed and you are free to call init again. Never rejects.
79
+ * ```js
80
+ * await db.close();
81
+ * ```
82
+ * @module @aicore/cocodb-ws-client
83
+ */
@@ -1,19 +1,191 @@
1
1
  import {WS} from "./WebSocket.js";
2
2
  import {isString, isObject, isStringEmpty, COCO_DB_FUNCTIONS} from "@aicore/libcommonutils";
3
3
 
4
- let client = null;
4
+ let client = null,
5
+ cocoDBEndPointURL = null,
6
+ cocoAuthKey = null,
7
+ hibernateTimer = null,
8
+ bufferRequests = false,
9
+ pendingSendMessages = [];
10
+ const MAX_PENDING_SEND_BUFFER_SIZE = 2000;
5
11
  const WEBSOCKET_ENDPOINT_COCO_DB = '/ws/';
6
- const ID_TO_RESOLVE_REJECT_MAP = {};
7
- let id = 0;
12
+ const ID_TO_RESOLVE_REJECT_MAP = new Map();
13
+ const CONNECT_BACKOFF_TIME_MS = [1, 500, 1000, 3000, 5000, 10000, 20000];
14
+ const INACTIVITY_TIME_FOR_HIBERNATE = 8000;
15
+ let id = 0, activityInHibernateInterval = 0;
16
+
17
+ let currentBackoffIndex = 0;
18
+ function _resetBackoffTime() {
19
+ currentBackoffIndex = 0;
20
+ }
21
+ function _getBackoffTime() {
22
+ if(currentBackoffIndex >= CONNECT_BACKOFF_TIME_MS.length){
23
+ currentBackoffIndex = CONNECT_BACKOFF_TIME_MS.length - 1;
24
+ }
25
+ return CONNECT_BACKOFF_TIME_MS[currentBackoffIndex++];
26
+ }
8
27
 
9
28
  // @INCLUDE_IN_API_DOCS
10
29
 
30
+
31
+ function _checkActivityForHibernation() {
32
+ if(activityInHibernateInterval > 0){
33
+ activityInHibernateInterval = 0;
34
+ return;
35
+ }
36
+ if(!client || client.hibernating
37
+ || !client.connectionEstablished // cant hibernate if connection isnt already established/ is being establised
38
+ || ID_TO_RESOLVE_REJECT_MAP.size > 0){ // if there are any pending responses, we cant hibernate
39
+ return;
40
+ }
41
+ // hibernate
42
+ client.hibernating = true;
43
+ client.hibernatingPromise = new Promise((resolve=>{
44
+ client.hibernatingPromiseResolve = resolve;
45
+ }));
46
+ bufferRequests = true;
47
+ client.terminate();
48
+ }
49
+
50
+ /**
51
+ * returns a promise that resolves when hibernation ends.
52
+ * @return {Promise<unknown>}
53
+ * @private
54
+ */
55
+ function _toAwakeFromHibernate() {
56
+ return client.hibernatingPromise;
57
+ }
58
+
59
+ function _wakeupHibernatingClient() {
60
+ if(client.hibernatingPromiseResolved) {
61
+ return;
62
+ }
63
+ client.hibernatingPromiseResolve();
64
+ client.hibernatingPromiseResolved = true;
65
+ }
66
+
67
+ /**
68
+ * Sets up the websocket client and returns a promise that will be resolved when the connection is closed or broken.
69
+ *
70
+ * @param {function} connectedCb a callbak that will be executed when a connection is established to db
71
+ * @return {Promise<null>} promise that will be resolved when the connection is closed/broken/error.
72
+ * @private
73
+ */
74
+ function _setupClientAndWaitForClose(connectedCb) {
75
+ return new Promise(resolve =>{
76
+ client = new WS.WebSocket(cocoDBEndPointURL.trim() + WEBSOCKET_ENDPOINT_COCO_DB, {
77
+ perMessageDeflate: false,
78
+ headers: {
79
+ Authorization: `Basic ${cocoAuthKey}`
80
+ }
81
+ });
82
+ client.on('open', function open() {
83
+ console.log('connected to server');
84
+ client.connectionEstablished = true;
85
+ bufferRequests = false;
86
+ connectedCb && connectedCb();
87
+ _sendPendingMessages();
88
+ });
89
+
90
+ client.on('message', function message(data) {
91
+ __receiveMessage(data);
92
+ });
93
+
94
+ function _connectionTerminated(reason) {
95
+ console.log(reason);
96
+ client.connectionEstablished = false;
97
+ for (let [sequenceNumber, handler] of ID_TO_RESOLVE_REJECT_MAP) {
98
+ handler.reject(reason);
99
+ ID_TO_RESOLVE_REJECT_MAP.delete(sequenceNumber);
100
+ }
101
+ resolve();
102
+ }
103
+ client.on('close', function () {
104
+ // https://websockets.spec.whatwg.org/#eventdef-websocket-error
105
+ // https://stackoverflow.com/questions/40084398/is-onclose-always-called-after-onerror-for-websocket
106
+ // we do not need to listen for error event as an error event is immediately followed by a close event.
107
+ _connectionTerminated('connection closed');
108
+ });
109
+ });
110
+ }
111
+
112
+ let backoffTimer = null, backoffResolveFn = null;
113
+ function _backoffTimer(timeInMilliSec) {
114
+ return new Promise(resolve => {
115
+ backoffResolveFn = resolve;
116
+ backoffTimer = setTimeout(()=>{
117
+ backoffTimer = null;
118
+ backoffResolveFn = null;
119
+ resolve();
120
+ }, timeInMilliSec);
121
+ });
122
+ }
123
+
124
+ function _cancelBackoffTimer() {
125
+ _resetBackoffTime();
126
+ if(backoffTimer){
127
+ clearTimeout(backoffTimer);
128
+ backoffTimer = null;
129
+ backoffResolveFn();
130
+ backoffResolveFn = null;
131
+ }
132
+ }
133
+
134
+ async function _setupAndMaintainConnection(firstConnectionCb, neverConnectedCB) {
135
+ backoffTimer = null;
136
+ function connected() {
137
+ _resetBackoffTime();
138
+ if(firstConnectionCb){
139
+ firstConnectionCb("connected");
140
+ firstConnectionCb = null;
141
+ // setup hibernate timer on first connection
142
+ activityInHibernateInterval = 1;
143
+ hibernateTimer = setInterval(_checkActivityForHibernation, INACTIVITY_TIME_FOR_HIBERNATE);
144
+ }
145
+ }
146
+ while(!client || !client.userClosedConnection){
147
+ await _setupClientAndWaitForClose(connected);
148
+ if(client && client.hibernating && !client.userClosedConnection){
149
+ await _toAwakeFromHibernate();
150
+ continue;
151
+ }
152
+ if(!client || !client.userClosedConnection){
153
+ await _backoffTimer(_getBackoffTime());
154
+ }
155
+ }
156
+ if(hibernateTimer){
157
+ clearInterval(hibernateTimer);
158
+ hibernateTimer = null;
159
+ }
160
+ client && client.userClosedConnectionCB && client.userClosedConnectionCB();
161
+ client = cocoDBEndPointURL = cocoAuthKey = null;
162
+ id = 0;
163
+ if(neverConnectedCB){
164
+ neverConnectedCB(new Error("user Cancelled"));
165
+ }
166
+ }
167
+
11
168
  /**
12
- * It creates a websocket connection to the cocoDbServiceEndPoint and listens for messages
169
+ * Create a connection to the cocoDbServiceEndPoint and listens for messages. The connection will
170
+ * be maintained and, it will try to automatically re-establish broken connections if there are network issues.
171
+ * You need to await on this function before staring to use any db APIs. Any APIs called while the connection is
172
+ * not fully setup will throw an error.
173
+ *
174
+ * ## Hibernation after inactivity
175
+ * After around 10 seconds of no send activity and if there are no outstanding requests, the db connection will be
176
+ * dropped and the client move into a hibernation state. The connection will be immediately re-established on any
177
+ * db activity transparently, though a slight jitter may be observed during the connection establishment time. This
178
+ * auto start-stop will save database resources as servers can be on for months on end.
179
+ *
13
180
  * @param {string} cocoDbServiceEndPoint - The URL of the coco-db service.
14
181
  * @param {string} authKey - The authKey is a base64 encoded string of the username and password.
182
+ * @return {Promise<null>} Resolves when the cocodb client is ready to send/receive requests for the first time.
183
+ * Rejects only if the user calls `close` API before any connection is established.
15
184
  */
16
185
  export function init(cocoDbServiceEndPoint, authKey) {
186
+ if(client) {
187
+ throw new Error('Please close the existing connection before calling init again');
188
+ }
17
189
  if (isStringEmpty(cocoDbServiceEndPoint) || !(cocoDbServiceEndPoint.startsWith('ws://')
18
190
  || cocoDbServiceEndPoint.startsWith('wss://'))) {
19
191
  throw new Error('Please provide valid cocoDbServiceEndPoint');
@@ -21,40 +193,45 @@ export function init(cocoDbServiceEndPoint, authKey) {
21
193
  if (isStringEmpty(authKey)) {
22
194
  throw new Error('Please provide valid authKey');
23
195
  }
24
- client = new WS.WebSocket(cocoDbServiceEndPoint.trim() + WEBSOCKET_ENDPOINT_COCO_DB, {
25
- perMessageDeflate: false,
26
- headers: {
27
- Authorization: `Basic ${authKey}`
28
- }
29
- });
30
- client.on('open', function open() {
31
- console.log('connected to server');
32
- });
33
-
34
- client.on('message', function message(data) {
35
- __receiveMessage(data);
36
- });
37
- client.on('close', function terminate() {
38
- console.log('closing connection');
39
- for (let sequenceNumber in ID_TO_RESOLVE_REJECT_MAP) {
40
- let reject = ID_TO_RESOLVE_REJECT_MAP[sequenceNumber].reject;
41
- reject('connection closed');
42
- delete ID_TO_RESOLVE_REJECT_MAP[sequenceNumber];
43
- }
44
- client = null;
45
- id = 0;
196
+ cocoDBEndPointURL = cocoDbServiceEndPoint;
197
+ cocoAuthKey = authKey;
198
+ return new Promise((resolve, reject) => {
199
+ _setupAndMaintainConnection(resolve, reject);
46
200
  });
47
201
  }
48
202
 
49
203
  /**
50
- * It closes the connection to the server
51
- * @returns The function close() is being returned.
204
+ * Closes the connection to the server. You need to await on this function before you can call init again.
205
+ *
206
+ * @return {Promise<null>} Resolves when the cocodb client is closed and you are free to call init again. Never rejects.
52
207
  */
53
208
  export function close() {
54
- if (!client) {
55
- return;
209
+ // we need to save the current client for the callback function hooks below. the global client may change
210
+ // as init and close gets called by the user.
211
+ let currentClient = client;
212
+ if (!currentClient) {
213
+ return Promise.resolve();
56
214
  }
57
- client.terminate();
215
+ if(currentClient.closePromise){
216
+ return currentClient.closePromise;
217
+ }
218
+ currentClient.closePromise = new Promise((resolve)=>{
219
+ currentClient.userClosedConnection = true;
220
+ currentClient.userClosedConnectionCB = function () {
221
+ for(let entry of pendingSendMessages){
222
+ entry.reject();
223
+ }
224
+ pendingSendMessages = [];
225
+ resolve();
226
+ };
227
+ _cancelBackoffTimer(); // this is for if the connection is broken and, we are retrying the connection
228
+ if(currentClient.hibernating){
229
+ _wakeupHibernatingClient();
230
+ } else {
231
+ currentClient.terminate();
232
+ }
233
+ });
234
+ return currentClient.closePromise;
58
235
  }
59
236
 
60
237
  /**
@@ -66,6 +243,39 @@ function getId() {
66
243
  return id.toString(16);
67
244
  }
68
245
 
246
+ function _sendMessage(message, resolve, reject) {
247
+ if (!client) {
248
+ reject('Please call init before sending message');
249
+ return;
250
+ }
251
+ if (!client.connectionEstablished) {
252
+ reject('Db connection is not ready, please retry in some time');
253
+ return;
254
+ }
255
+ if (!isObject(message)) {
256
+ reject('Please provide valid Object');
257
+ return;
258
+ }
259
+ if (!isString(message.fn) || !(message.fn in COCO_DB_FUNCTIONS)) {
260
+ reject('please provide valid function name');
261
+ return;
262
+ }
263
+ const sequenceNumber = getId();
264
+ message.id = sequenceNumber;
265
+ ID_TO_RESOLVE_REJECT_MAP.set(sequenceNumber, {
266
+ resolve: resolve,
267
+ reject: reject
268
+ });
269
+ activityInHibernateInterval++;
270
+ client.send(JSON.stringify(message));
271
+ }
272
+
273
+ function _sendPendingMessages() {
274
+ for(let entry of pendingSendMessages){
275
+ _sendMessage(entry.message, entry.resolve, entry.reject);
276
+ }
277
+ pendingSendMessages = [];
278
+ }
69
279
 
70
280
  /**
71
281
  * It takes a message object, sends it to the server, and returns a promise that resolves when the server responds
@@ -74,25 +284,18 @@ function getId() {
74
284
  */
75
285
  export function sendMessage(message) {
76
286
  return new Promise(function (resolve, reject) {
77
- if (!client) {
78
- reject('Please call init before sending message');
79
- return;
80
- }
81
- if (!isObject(message)) {
82
- reject('Please provide valid Object');
83
- return;
287
+ if(bufferRequests){
288
+ if(pendingSendMessages.length > MAX_PENDING_SEND_BUFFER_SIZE){
289
+ reject('Too many requests sent while waking up from hibernation');
290
+ return;
291
+ }
292
+ pendingSendMessages.push({message, resolve, reject});
293
+ if(client&& client.hibernating){
294
+ _wakeupHibernatingClient();
295
+ }
296
+ } else {
297
+ _sendMessage(message, resolve, reject);
84
298
  }
85
- if (!isString(message.fn) || !(message.fn in COCO_DB_FUNCTIONS)) {
86
- reject('please provide valid function name');
87
- return;
88
- }
89
- const sequenceNumber = getId();
90
- message.id = sequenceNumber;
91
- ID_TO_RESOLVE_REJECT_MAP[sequenceNumber] = {
92
- resolve: resolve,
93
- reject: reject
94
- };
95
- client.send(JSON.stringify(message));
96
299
  });
97
300
  }
98
301
 
@@ -111,15 +314,14 @@ export function __receiveMessage(rawData) {
111
314
  console.error('Server message does not have an Id');
112
315
  return false;
113
316
  }
114
- const requestResolve = ID_TO_RESOLVE_REJECT_MAP[message.id];
115
- if (!isObject(requestResolve)) {
317
+ const requestHandler = ID_TO_RESOLVE_REJECT_MAP.get(message.id);
318
+ if (!isObject(requestHandler)) {
116
319
  //TODO: Emit metrics
117
320
 
118
321
  console.error(`Client did not send message with Id ${message.id} to server`);
119
322
  return false;
120
323
  }
121
- const response = message.response;
122
- requestResolve.resolve(response);
123
- delete ID_TO_RESOLVE_REJECT_MAP[message.id];
324
+ requestHandler.resolve(message.response);
325
+ ID_TO_RESOLVE_REJECT_MAP.delete(message.id);
124
326
  return true;
125
327
  }