@aicore/cocodb-ws-client 1.0.23 → 1.0.26

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": "@aicore/cocodb-ws-client",
3
- "version": "1.0.23",
3
+ "version": "1.0.26",
4
4
  "description": "Websocket client for cocoDb",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -38,7 +38,7 @@
38
38
  ],
39
39
  "repository": {
40
40
  "type": "git",
41
- "url": "git+https://github.com/aicore/template-nodejs.git"
41
+ "url": "git+https://github.com/aicore/cocoDbWsClient.git"
42
42
  },
43
43
  "author": "Charly, core.ai",
44
44
  "license": "AGPL-3.0-or-later",
@@ -47,20 +47,20 @@
47
47
  },
48
48
  "homepage": "https://github.com/aicore/cocoDbWsClient#readme",
49
49
  "devDependencies": {
50
- "@commitlint/cli": "19.8.0",
51
- "@commitlint/config-conventional": "19.8.0",
50
+ "@commitlint/cli": "19.8.1",
51
+ "@commitlint/config-conventional": "19.8.1",
52
52
  "c8": "10.1.3",
53
- "chai": "5.2.0",
53
+ "chai": "5.3.3",
54
54
  "cli-color": "2.0.4",
55
55
  "documentation": "14.0.3",
56
- "eslint": "9.25.0",
57
- "glob": "11.0.1",
56
+ "eslint": "9.39.2",
57
+ "glob": "11.1.0",
58
58
  "husky": "9.1.7",
59
- "mocha": "11.1.0"
59
+ "mocha": "11.7.5"
60
60
  },
61
61
  "dependencies": {
62
62
  "@aicore/libcommonutils": "1.0.20",
63
- "ws": "8.18.1"
63
+ "ws": "8.18.3"
64
64
  },
65
65
  "optionalDependencies": {
66
66
  "bufferutil": "4.0.9",
package/src/index.js CHANGED
@@ -35,7 +35,10 @@ export {
35
35
  getFromNonIndex,
36
36
  hello,
37
37
  update,
38
- query
38
+ query,
39
+ listDatabases,
40
+ listTables,
41
+ getTableIndexes
39
42
 
40
43
  } from './utils/api.js';
41
44
 
package/src/utils/api.js CHANGED
@@ -392,3 +392,51 @@ export function query(tableName, queryString, useIndexForFields = null, options=
392
392
  });
393
393
  }
394
394
 
395
+ /**
396
+ * Lists all databases on the MySQL server.
397
+ * @example <caption>Sample usage</caption>
398
+ * const response = await listDatabases();
399
+ * console.log(response.databases); // ['mysql', 'information_schema', 'mydb']
400
+ * @returns {Promise<Object>} A promise resolving to {isSuccess: boolean, databases: string[]}
401
+ */
402
+ export function listDatabases() {
403
+ return sendMessage({
404
+ fn: 'listDatabases',
405
+ request: {}
406
+ });
407
+ }
408
+
409
+ /**
410
+ * Lists all tables in a database.
411
+ * @param {string} databaseName - The name of the database
412
+ * @returns {Promise<Object>} A promise resolving to {isSuccess: boolean, tables: string[]}
413
+ */
414
+ export function listTables(databaseName) {
415
+ if (isStringEmpty(databaseName)) {
416
+ throw new Error('Please provide valid databaseName');
417
+ }
418
+ return sendMessage({
419
+ fn: 'listTables',
420
+ request: {
421
+ databaseName: databaseName
422
+ }
423
+ });
424
+ }
425
+
426
+ /**
427
+ * Gets index information for a table.
428
+ * @param {string} tableName - The table name in database.tableName format
429
+ * @returns {Promise<Object>} A promise resolving to {isSuccess: boolean, indexes: IndexInfo[]}
430
+ */
431
+ export function getTableIndexes(tableName) {
432
+ if (isStringEmpty(tableName)) {
433
+ throw new Error('Please provide valid tableName');
434
+ }
435
+ return sendMessage({
436
+ fn: 'getTableIndexes',
437
+ request: {
438
+ tableName: tableName
439
+ }
440
+ });
441
+ }
442
+
@@ -1,6 +1,22 @@
1
1
  import {WS} from "./WebSocket.js";
2
2
  import {isString, isObject, isStringEmpty, COCO_DB_FUNCTIONS} from "@aicore/libcommonutils";
3
3
 
4
+ // Additional functions not yet in COCO_DB_FUNCTIONS from libcommonutils
5
+ const ADDITIONAL_FUNCTIONS = {
6
+ listDatabases: 'listDatabases',
7
+ listTables: 'listTables',
8
+ getTableIndexes: 'getTableIndexes'
9
+ };
10
+
11
+ /**
12
+ * Checks if a function name is a valid cocodb function
13
+ * @param {string} fn - The function name to check
14
+ * @returns {boolean} True if the function is valid
15
+ */
16
+ function isValidFunction(fn) {
17
+ return (fn in COCO_DB_FUNCTIONS) || (fn in ADDITIONAL_FUNCTIONS);
18
+ }
19
+
4
20
  let client = null,
5
21
  cocoDBEndPointURL = null,
6
22
  cocoAuthKey = null,
@@ -34,6 +50,7 @@ function _checkActivityForHibernation() {
34
50
  return;
35
51
  }
36
52
  if(!client || client.hibernating
53
+ || client.userClosedConnection
37
54
  || !client.connectionEstablished // cant hibernate if connection isnt already established/ is being establised
38
55
  || ID_TO_RESOLVE_REJECT_MAP.size > 0){ // if there are any pending responses, we cant hibernate
39
56
  return;
@@ -91,8 +108,18 @@ function _setupClientAndWaitForClose(connectedCb) {
91
108
  __receiveMessage(data);
92
109
  });
93
110
 
111
+ let terminated = false;
94
112
  function _connectionTerminated(reason) {
113
+ if (terminated) {
114
+ return;
115
+ }
116
+ terminated = true;
117
+
95
118
  console.log(reason);
119
+ // not set bufferRequests = true for unexpected failures.
120
+ // Real connection failures → reject immediately, don't buffer
121
+ // Hibernation → buffer and reconnect transparently
122
+
96
123
  client.connectionEstablished = false;
97
124
  for (let [sequenceNumber, handler] of ID_TO_RESOLVE_REJECT_MAP) {
98
125
  handler.reject(reason);
@@ -100,11 +127,20 @@ function _setupClientAndWaitForClose(connectedCb) {
100
127
  }
101
128
  resolve();
102
129
  }
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');
130
+
131
+ client.on('error', function (err) {
132
+ // ECONNREFUSED, ENOTFOUND, TLS errors, etc. can land here
133
+ _connectionTerminated(err);
134
+ });
135
+
136
+ client.on('close', function (code, reasonBuf) {
137
+ // reasonBuf may be a Buffer in ws; keep it simple
138
+ _connectionTerminated(`connection closed (${code})`);
139
+ });
140
+
141
+ // Optional but useful: HTTP-level failures (proxies, 401, 403, 502...)
142
+ client.on('unexpected-response', function (req, res) {
143
+ _connectionTerminated(`unexpected-response: ${res.statusCode}`);
108
144
  });
109
145
  });
110
146
  }
@@ -131,13 +167,14 @@ function _cancelBackoffTimer() {
131
167
  }
132
168
  }
133
169
 
134
- async function _setupAndMaintainConnection(firstConnectionCb, neverConnectedCB) {
170
+ async function _setupAndMaintainConnection(resolveOnFirstConnect, reject) {
135
171
  backoffTimer = null;
136
172
  function connected() {
137
173
  _resetBackoffTime();
138
- if(firstConnectionCb){
139
- firstConnectionCb("connected");
140
- firstConnectionCb = null;
174
+ if(resolveOnFirstConnect){
175
+ resolveOnFirstConnect("connected");
176
+ resolveOnFirstConnect = null;
177
+ reject = null;
141
178
  // setup hibernate timer on first connection
142
179
  activityInHibernateInterval = 1;
143
180
  hibernateTimer = setInterval(_checkActivityForHibernation, INACTIVITY_TIME_FOR_HIBERNATE);
@@ -160,8 +197,8 @@ async function _setupAndMaintainConnection(firstConnectionCb, neverConnectedCB)
160
197
  client && client.userClosedConnectionCB && client.userClosedConnectionCB();
161
198
  client = cocoDBEndPointURL = cocoAuthKey = null;
162
199
  id = 0;
163
- if(neverConnectedCB){
164
- neverConnectedCB(new Error("user Cancelled"));
200
+ if(reject){
201
+ reject(new Error("user Cancelled"));
165
202
  }
166
203
  }
167
204
 
@@ -219,7 +256,7 @@ export function close() {
219
256
  currentClient.userClosedConnection = true;
220
257
  currentClient.userClosedConnectionCB = function () {
221
258
  for(let entry of pendingSendMessages){
222
- entry.reject();
259
+ entry.reject(new Error('Connection closed'));
223
260
  }
224
261
  pendingSendMessages = [];
225
262
  resolve();
@@ -239,7 +276,7 @@ export function close() {
239
276
  * @returns {string} A function that increments the id variable and returns the new value as a hexadecimal string.
240
277
  */
241
278
  function getId() {
242
- id++;
279
+ id++; // Will take about 300 years to run out at 1 million sustained tps to db with Number.MAX_SAFE_INTEGER
243
280
  return id.toString(16);
244
281
  }
245
282
 
@@ -256,7 +293,7 @@ function _sendMessage(message, resolve, reject) {
256
293
  reject('Please provide valid Object');
257
294
  return;
258
295
  }
259
- if (!isString(message.fn) || !(message.fn in COCO_DB_FUNCTIONS)) {
296
+ if (!isString(message.fn) || !isValidFunction(message.fn)) {
260
297
  reject('please provide valid function name');
261
298
  return;
262
299
  }
@@ -284,7 +321,7 @@ function _sendPendingMessages() {
284
321
  */
285
322
  export function sendMessage(message) {
286
323
  // make a copy as the user may start modifying the object while we are sending it.
287
- message = structuredClone(message);
324
+ message = JSON.parse(JSON.stringify(message)); // faster than structured clone for most cases
288
325
  return new Promise(function (resolve, reject) {
289
326
  if(bufferRequests){
290
327
  if(pendingSendMessages.length > MAX_PENDING_SEND_BUFFER_SIZE){