@joystick.js/db-canary 0.0.0-canary.2250 → 0.0.0-canary.2252

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.
Files changed (49) hide show
  1. package/dist/client/database.js +1 -1
  2. package/dist/client/index.js +1 -1
  3. package/dist/server/cluster/master.js +4 -4
  4. package/dist/server/cluster/worker.js +1 -1
  5. package/dist/server/index.js +1 -1
  6. package/dist/server/lib/auto_index_manager.js +1 -1
  7. package/dist/server/lib/backup_manager.js +1 -1
  8. package/dist/server/lib/index_manager.js +1 -1
  9. package/dist/server/lib/operation_dispatcher.js +1 -1
  10. package/dist/server/lib/operations/admin.js +1 -1
  11. package/dist/server/lib/operations/bulk_write.js +1 -1
  12. package/dist/server/lib/operations/create_index.js +1 -1
  13. package/dist/server/lib/operations/delete_many.js +1 -1
  14. package/dist/server/lib/operations/delete_one.js +1 -1
  15. package/dist/server/lib/operations/find.js +1 -1
  16. package/dist/server/lib/operations/find_one.js +1 -1
  17. package/dist/server/lib/operations/insert_one.js +1 -1
  18. package/dist/server/lib/operations/update_one.js +1 -1
  19. package/dist/server/lib/send_response.js +1 -1
  20. package/dist/server/lib/tcp_protocol.js +1 -1
  21. package/package.json +2 -2
  22. package/src/client/database.js +92 -119
  23. package/src/client/index.js +279 -345
  24. package/src/server/cluster/master.js +265 -156
  25. package/src/server/cluster/worker.js +26 -18
  26. package/src/server/index.js +553 -330
  27. package/src/server/lib/auto_index_manager.js +85 -23
  28. package/src/server/lib/backup_manager.js +117 -70
  29. package/src/server/lib/index_manager.js +63 -25
  30. package/src/server/lib/operation_dispatcher.js +339 -168
  31. package/src/server/lib/operations/admin.js +343 -205
  32. package/src/server/lib/operations/bulk_write.js +458 -194
  33. package/src/server/lib/operations/create_index.js +127 -34
  34. package/src/server/lib/operations/delete_many.js +204 -67
  35. package/src/server/lib/operations/delete_one.js +164 -52
  36. package/src/server/lib/operations/find.js +563 -201
  37. package/src/server/lib/operations/find_one.js +544 -188
  38. package/src/server/lib/operations/insert_one.js +147 -52
  39. package/src/server/lib/operations/update_one.js +334 -93
  40. package/src/server/lib/send_response.js +37 -17
  41. package/src/server/lib/tcp_protocol.js +158 -53
  42. package/tests/server/cluster/master_read_write_operations.test.js +5 -14
  43. package/tests/server/integration/authentication_integration.test.js +18 -10
  44. package/tests/server/integration/backup_integration.test.js +35 -27
  45. package/tests/server/lib/api_key_manager.test.js +88 -32
  46. package/tests/server/lib/development_mode.test.js +2 -2
  47. package/tests/server/lib/operations/admin.test.js +20 -12
  48. package/tests/server/lib/operations/delete_one.test.js +10 -4
  49. package/tests/server/lib/operations/find_array_queries.test.js +261 -0
@@ -5,6 +5,77 @@ import create_logger from '../logger.js';
5
5
 
6
6
  const { create_context_logger } = create_logger('update_one');
7
7
 
8
+ /**
9
+ * Applies $set operator to document.
10
+ * @param {Object} document - Document to update
11
+ * @param {Object} operations - Set operations
12
+ * @returns {Object} Updated document
13
+ */
14
+ const apply_set_operator = (document, operations) => {
15
+ return { ...document, ...operations };
16
+ };
17
+
18
+ /**
19
+ * Applies $unset operator to document.
20
+ * @param {Object} document - Document to update
21
+ * @param {Object} operations - Unset operations
22
+ * @returns {Object} Updated document
23
+ */
24
+ const apply_unset_operator = (document, operations) => {
25
+ const updated_document = { ...document };
26
+ for (const field of Object.keys(operations)) {
27
+ delete updated_document[field];
28
+ }
29
+ return updated_document;
30
+ };
31
+
32
+ /**
33
+ * Applies $inc operator to document.
34
+ * @param {Object} document - Document to update
35
+ * @param {Object} operations - Increment operations
36
+ * @returns {Object} Updated document
37
+ */
38
+ const apply_inc_operator = (document, operations) => {
39
+ const updated_document = { ...document };
40
+ for (const [field, value] of Object.entries(operations)) {
41
+ updated_document[field] = (updated_document[field] || 0) + value;
42
+ }
43
+ return updated_document;
44
+ };
45
+
46
+ /**
47
+ * Applies $push operator to document.
48
+ * @param {Object} document - Document to update
49
+ * @param {Object} operations - Push operations
50
+ * @returns {Object} Updated document
51
+ */
52
+ const apply_push_operator = (document, operations) => {
53
+ const updated_document = { ...document };
54
+ for (const [field, value] of Object.entries(operations)) {
55
+ if (!Array.isArray(updated_document[field])) {
56
+ updated_document[field] = [];
57
+ }
58
+ updated_document[field] = [...updated_document[field], value];
59
+ }
60
+ return updated_document;
61
+ };
62
+
63
+ /**
64
+ * Applies $pull operator to document.
65
+ * @param {Object} document - Document to update
66
+ * @param {Object} operations - Pull operations
67
+ * @returns {Object} Updated document
68
+ */
69
+ const apply_pull_operator = (document, operations) => {
70
+ const updated_document = { ...document };
71
+ for (const [field, value] of Object.entries(operations)) {
72
+ if (Array.isArray(updated_document[field])) {
73
+ updated_document[field] = updated_document[field].filter(item => item !== value);
74
+ }
75
+ }
76
+ return updated_document;
77
+ };
78
+
8
79
  /**
9
80
  * Applies MongoDB-style update operators to a document.
10
81
  * @param {Object} document - Original document
@@ -13,41 +84,28 @@ const { create_context_logger } = create_logger('update_one');
13
84
  * @throws {Error} When unsupported update operator is used
14
85
  */
15
86
  const apply_update_operators = (document, update_operations) => {
16
- const updated_document = { ...document };
87
+ let updated_document = { ...document };
17
88
 
18
89
  for (const [operator, operations] of Object.entries(update_operations)) {
19
90
  switch (operator) {
20
91
  case '$set':
21
- Object.assign(updated_document, operations);
92
+ updated_document = apply_set_operator(updated_document, operations);
22
93
  break;
23
94
 
24
95
  case '$unset':
25
- for (const field of Object.keys(operations)) {
26
- delete updated_document[field];
27
- }
96
+ updated_document = apply_unset_operator(updated_document, operations);
28
97
  break;
29
98
 
30
99
  case '$inc':
31
- for (const [field, value] of Object.entries(operations)) {
32
- updated_document[field] = (updated_document[field] || 0) + value;
33
- }
100
+ updated_document = apply_inc_operator(updated_document, operations);
34
101
  break;
35
102
 
36
103
  case '$push':
37
- for (const [field, value] of Object.entries(operations)) {
38
- if (!Array.isArray(updated_document[field])) {
39
- updated_document[field] = [];
40
- }
41
- updated_document[field].push(value);
42
- }
104
+ updated_document = apply_push_operator(updated_document, operations);
43
105
  break;
44
106
 
45
107
  case '$pull':
46
- for (const [field, value] of Object.entries(operations)) {
47
- if (Array.isArray(updated_document[field])) {
48
- updated_document[field] = updated_document[field].filter(item => item !== value);
49
- }
50
- }
108
+ updated_document = apply_pull_operator(updated_document, operations);
51
109
  break;
52
110
 
53
111
  default:
@@ -58,6 +116,17 @@ const apply_update_operators = (document, update_operations) => {
58
116
  return updated_document;
59
117
  };
60
118
 
119
+ /**
120
+ * Checks if document field matches filter value.
121
+ * @param {Object} document - Document to check
122
+ * @param {string} field - Field name
123
+ * @param {any} value - Expected value
124
+ * @returns {boolean} True if field matches value
125
+ */
126
+ const field_matches_value = (document, field, value) => {
127
+ return document[field] === value;
128
+ };
129
+
61
130
  /**
62
131
  * Checks if a document matches the given filter criteria (simple equality check).
63
132
  * @param {Object} document - Document to test
@@ -70,7 +139,7 @@ const matches_filter = (document, filter) => {
70
139
  }
71
140
 
72
141
  for (const [field, value] of Object.entries(filter)) {
73
- if (document[field] !== value) {
142
+ if (!field_matches_value(document, field, value)) {
74
143
  return false;
75
144
  }
76
145
  }
@@ -79,104 +148,228 @@ const matches_filter = (document, filter) => {
79
148
  };
80
149
 
81
150
  /**
82
- * Internal implementation of update_one operation without write queue serialization.
83
- * @param {string} database_name - Name of the database
84
- * @param {string} collection_name - Name of the collection
85
- * @param {Object} filter - Filter criteria to match document
86
- * @param {Object} update - Update operations to apply
87
- * @param {Object} [options={}] - Update options
88
- * @param {boolean} [options.upsert] - Create document if not found
89
- * @returns {Promise<Object>} Update result with acknowledged, matched_count, modified_count, and optional upserted_id
90
- * @throws {Error} When database name, collection name, filter, or update is invalid
151
+ * Validates database name parameter.
152
+ * @param {string} database_name - Database name to validate
153
+ * @throws {Error} When database name is missing
91
154
  */
92
- const update_one_internal = async (database_name, collection_name, filter, update, options = {}) => {
93
- const log = create_context_logger();
94
-
155
+ const validate_database_name = (database_name) => {
95
156
  if (!database_name) {
96
157
  throw new Error('Database name is required');
97
158
  }
98
-
159
+ };
160
+
161
+ /**
162
+ * Validates collection name parameter.
163
+ * @param {string} collection_name - Collection name to validate
164
+ * @throws {Error} When collection name is missing
165
+ */
166
+ const validate_collection_name = (collection_name) => {
99
167
  if (!collection_name) {
100
168
  throw new Error('Collection name is required');
101
169
  }
102
-
170
+ };
171
+
172
+ /**
173
+ * Validates filter parameter.
174
+ * @param {Object} filter - Filter to validate
175
+ * @throws {Error} When filter is invalid
176
+ */
177
+ const validate_filter = (filter) => {
103
178
  if (!filter || typeof filter !== 'object') {
104
179
  throw new Error('Filter must be a valid object');
105
180
  }
106
-
181
+ };
182
+
183
+ /**
184
+ * Validates update parameter.
185
+ * @param {Object} update - Update to validate
186
+ * @throws {Error} When update is invalid
187
+ */
188
+ const validate_update = (update) => {
107
189
  if (!update || typeof update !== 'object') {
108
190
  throw new Error('Update must be a valid object');
109
191
  }
192
+ };
193
+
194
+ /**
195
+ * Validates all update operation parameters.
196
+ * @param {string} database_name - Database name
197
+ * @param {string} collection_name - Collection name
198
+ * @param {Object} filter - Filter criteria
199
+ * @param {Object} update - Update operations
200
+ */
201
+ const validate_update_parameters = (database_name, collection_name, filter, update) => {
202
+ validate_database_name(database_name);
203
+ validate_collection_name(collection_name);
204
+ validate_filter(filter);
205
+ validate_update(update);
206
+ };
207
+
208
+ /**
209
+ * Attempts to parse document from JSON string.
210
+ * @param {string} value - JSON string
211
+ * @returns {Object|null} Parsed document or null
212
+ */
213
+ const parse_document_safely = (value) => {
214
+ try {
215
+ return JSON.parse(value);
216
+ } catch (parse_error) {
217
+ return null;
218
+ }
219
+ };
220
+
221
+ /**
222
+ * Creates current timestamp string.
223
+ * @returns {string} ISO timestamp string
224
+ */
225
+ const create_current_timestamp = () => {
226
+ return new Date().toISOString();
227
+ };
228
+
229
+ /**
230
+ * Adds updated timestamp to document.
231
+ * @param {Object} document - Document to update
232
+ * @returns {Object} Document with updated timestamp
233
+ */
234
+ const add_updated_timestamp = (document) => {
235
+ return {
236
+ ...document,
237
+ _updated_at: create_current_timestamp()
238
+ };
239
+ };
240
+
241
+ /**
242
+ * Checks if document has been modified.
243
+ * @param {Object} original_document - Original document
244
+ * @param {Object} updated_document - Updated document
245
+ * @returns {boolean} True if document was modified
246
+ */
247
+ const document_was_modified = (original_document, updated_document) => {
248
+ return JSON.stringify(original_document) !== JSON.stringify(updated_document);
249
+ };
250
+
251
+ /**
252
+ * Creates new document for upsert operation.
253
+ * @param {Object} filter - Filter criteria
254
+ * @param {Object} update - Update operations
255
+ * @returns {Object} New document for upsert
256
+ */
257
+ const create_upsert_document = (filter, update) => {
258
+ const document_id = generate_document_id();
259
+ const current_timestamp = create_current_timestamp();
110
260
 
111
- const db = get_database();
261
+ const base_document = {
262
+ ...filter,
263
+ _id: document_id,
264
+ _created_at: current_timestamp,
265
+ _updated_at: current_timestamp
266
+ };
267
+
268
+ return apply_update_operators(base_document, update);
269
+ };
270
+
271
+ /**
272
+ * Processes document update within transaction.
273
+ * @param {Object} db - Database instance
274
+ * @param {string} database_name - Database name
275
+ * @param {string} collection_name - Collection name
276
+ * @param {Object} filter - Filter criteria
277
+ * @param {Object} update - Update operations
278
+ * @param {Object} options - Update options
279
+ * @returns {Object} Update result data
280
+ */
281
+ const process_document_update = (db, database_name, collection_name, filter, update, options) => {
112
282
  let matched_count = 0;
113
283
  let modified_count = 0;
114
284
  let upserted_id = null;
115
-
116
285
  let old_document = null;
117
286
  let new_document = null;
118
287
  let upserted_document = null;
119
288
 
120
- await db.transaction(() => {
121
- const collection_prefix = `${database_name}:${collection_name}:`;
122
- let document_found = false;
123
-
124
- const range = db.getRange({ start: collection_prefix, end: collection_prefix + '\xFF' });
125
- for (const { key, value } of range) {
126
- let document;
127
- try {
128
- document = JSON.parse(value);
129
- } catch (parse_error) {
130
- // Skip documents that can't be parsed
131
- continue;
132
- }
133
-
134
- if (matches_filter(document, filter)) {
135
- document_found = true;
136
- matched_count = 1;
137
-
138
- const updated_document = apply_update_operators(document, update);
139
- updated_document._updated_at = new Date().toISOString();
140
-
141
- if (JSON.stringify(document) !== JSON.stringify(updated_document)) {
142
- db.put(key, JSON.stringify(updated_document));
143
- old_document = document;
144
- new_document = updated_document;
145
- modified_count = 1;
146
- }
147
-
148
- break;
149
- }
289
+ const collection_prefix = `${database_name}:${collection_name}:`;
290
+ let document_found = false;
291
+
292
+ const range = db.getRange({ start: collection_prefix, end: collection_prefix + '\xFF' });
293
+ for (const { key, value } of range) {
294
+ const document = parse_document_safely(value);
295
+ if (!document) {
296
+ continue;
150
297
  }
151
298
 
152
- if (!document_found && options.upsert) {
153
- const document_id = generate_document_id();
154
- const collection_key = build_collection_key(database_name, collection_name, document_id);
299
+ if (matches_filter(document, filter)) {
300
+ document_found = true;
301
+ matched_count = 1;
155
302
 
156
- const new_doc = {
157
- ...filter,
158
- _id: document_id,
159
- _created_at: new Date().toISOString(),
160
- _updated_at: new Date().toISOString()
161
- };
303
+ const updated_document = apply_update_operators(document, update);
304
+ const timestamped_document = add_updated_timestamp(updated_document);
162
305
 
163
- upserted_document = apply_update_operators(new_doc, update);
164
- db.put(collection_key, JSON.stringify(upserted_document));
306
+ if (document_was_modified(document, timestamped_document)) {
307
+ db.put(key, JSON.stringify(timestamped_document));
308
+ old_document = document;
309
+ new_document = timestamped_document;
310
+ modified_count = 1;
311
+ }
165
312
 
166
- upserted_id = document_id;
167
- matched_count = 0;
168
- modified_count = 0;
313
+ break;
169
314
  }
170
- });
315
+ }
171
316
 
317
+ if (!document_found && options.upsert) {
318
+ upserted_document = create_upsert_document(filter, update);
319
+ const collection_key = build_collection_key(database_name, collection_name, upserted_document._id);
320
+
321
+ db.put(collection_key, JSON.stringify(upserted_document));
322
+
323
+ upserted_id = upserted_document._id;
324
+ matched_count = 0;
325
+ modified_count = 0;
326
+ }
327
+
328
+ return {
329
+ matched_count,
330
+ modified_count,
331
+ upserted_id,
332
+ old_document,
333
+ new_document,
334
+ upserted_document
335
+ };
336
+ };
337
+
338
+ /**
339
+ * Updates indexes after document modification.
340
+ * @param {string} database_name - Database name
341
+ * @param {string} collection_name - Collection name
342
+ * @param {Object} old_document - Original document
343
+ * @param {Object} new_document - Updated document
344
+ */
345
+ const update_indexes_after_modification = async (database_name, collection_name, old_document, new_document) => {
172
346
  if (old_document && new_document) {
173
347
  await update_indexes_on_update(database_name, collection_name, old_document, new_document);
174
348
  }
175
-
349
+ };
350
+
351
+ /**
352
+ * Updates indexes after document upsert.
353
+ * @param {string} database_name - Database name
354
+ * @param {string} collection_name - Collection name
355
+ * @param {Object} upserted_document - Upserted document
356
+ */
357
+ const update_indexes_after_upsert = async (database_name, collection_name, upserted_document) => {
176
358
  if (upserted_document) {
177
359
  await update_indexes_on_insert(database_name, collection_name, upserted_document);
178
360
  }
179
-
361
+ };
362
+
363
+ /**
364
+ * Logs update operation completion.
365
+ * @param {Function} log - Logger function
366
+ * @param {string} database_name - Database name
367
+ * @param {string} collection_name - Collection name
368
+ * @param {number} matched_count - Number of matched documents
369
+ * @param {number} modified_count - Number of modified documents
370
+ * @param {string} upserted_id - ID of upserted document
371
+ */
372
+ const log_update_completion = (log, database_name, collection_name, matched_count, modified_count, upserted_id) => {
180
373
  log.info('Update operation completed', {
181
374
  database: database_name,
182
375
  collection: collection_name,
@@ -184,7 +377,16 @@ const update_one_internal = async (database_name, collection_name, filter, updat
184
377
  modified_count,
185
378
  upserted_id
186
379
  });
187
-
380
+ };
381
+
382
+ /**
383
+ * Creates update operation result.
384
+ * @param {number} matched_count - Number of matched documents
385
+ * @param {number} modified_count - Number of modified documents
386
+ * @param {string} upserted_id - ID of upserted document
387
+ * @returns {Object} Update result object
388
+ */
389
+ const create_update_result = (matched_count, modified_count, upserted_id) => {
188
390
  const result = {
189
391
  acknowledged: true,
190
392
  matched_count,
@@ -198,27 +400,66 @@ const update_one_internal = async (database_name, collection_name, filter, updat
198
400
  return result;
199
401
  };
200
402
 
403
+ /**
404
+ * Creates write queue operation metadata.
405
+ * @param {string} database_name - Database name
406
+ * @param {string} collection_name - Collection name
407
+ * @param {Object} filter - Filter criteria
408
+ * @returns {Object} Operation metadata
409
+ */
410
+ const create_write_queue_metadata = (database_name, collection_name, filter) => ({
411
+ operation: 'update_one',
412
+ database: database_name,
413
+ collection: collection_name,
414
+ filter_keys: Object.keys(filter || {})
415
+ });
416
+
417
+ /**
418
+ * Internal implementation of update_one operation without write queue serialization.
419
+ * @param {string} database_name - Name of the database
420
+ * @param {string} collection_name - Name of the collection
421
+ * @param {Object} filter - Filter criteria to match document
422
+ * @param {Object} update - Update operations to apply
423
+ * @param {Object} options - Update options
424
+ * @returns {Promise<Object>} Update result with acknowledged, matched_count, modified_count, and optional upserted_id
425
+ */
426
+ const update_one_internal = async (database_name, collection_name, filter, update, options = {}) => {
427
+ const log = create_context_logger();
428
+
429
+ validate_update_parameters(database_name, collection_name, filter, update);
430
+
431
+ const db = get_database();
432
+
433
+ const update_result = await db.transaction(() => {
434
+ return process_document_update(db, database_name, collection_name, filter, update, options);
435
+ });
436
+
437
+ const { matched_count, modified_count, upserted_id, old_document, new_document, upserted_document } = update_result;
438
+
439
+ await update_indexes_after_modification(database_name, collection_name, old_document, new_document);
440
+ await update_indexes_after_upsert(database_name, collection_name, upserted_document);
441
+
442
+ log_update_completion(log, database_name, collection_name, matched_count, modified_count, upserted_id);
443
+
444
+ return create_update_result(matched_count, modified_count, upserted_id);
445
+ };
446
+
201
447
  /**
202
448
  * Updates a single document in a collection with write queue serialization.
203
449
  * @param {string} database_name - Name of the database
204
450
  * @param {string} collection_name - Name of the collection
205
451
  * @param {Object} filter - Filter criteria to match document
206
452
  * @param {Object} update - Update operations to apply
207
- * @param {Object} [options={}] - Update options
208
- * @param {boolean} [options.upsert] - Create document if not found
453
+ * @param {Object} options - Update options
209
454
  * @returns {Promise<Object>} Update result with acknowledged, matched_count, modified_count, and optional upserted_id
210
455
  */
211
456
  const update_one = async (database_name, collection_name, filter, update, options = {}) => {
212
457
  const write_queue = get_write_queue();
458
+ const operation_metadata = create_write_queue_metadata(database_name, collection_name, filter);
213
459
 
214
460
  return await write_queue.enqueue_write_operation(
215
461
  () => update_one_internal(database_name, collection_name, filter, update, options),
216
- {
217
- operation: 'update_one',
218
- database: database_name,
219
- collection: collection_name,
220
- filter_keys: Object.keys(filter || {})
221
- }
462
+ operation_metadata
222
463
  );
223
464
  };
224
465
 
@@ -8,39 +8,59 @@
8
8
  import { encode_message } from './tcp_protocol.js';
9
9
 
10
10
  /**
11
- * Sends a success response to the client.
11
+ * Creates a success response object.
12
+ * @param {Object} data - Response data
13
+ * @returns {Object} Success response object
14
+ */
15
+ const create_success_response = (data) => ({
16
+ ok: true,
17
+ data: data
18
+ });
19
+
20
+ /**
21
+ * Creates an error response object.
22
+ * @param {Object} error - Error information
23
+ * @returns {Object} Error response object
24
+ */
25
+ const create_error_response = (error) => ({
26
+ ok: false,
27
+ error: error
28
+ });
29
+
30
+ /**
31
+ * Encodes and writes response to socket.
12
32
  * @param {net.Socket} socket - Client socket connection
13
- * @param {Object} [data={}] - Response data to send
33
+ * @param {Object} response - Response object to send
14
34
  */
15
- export const send_success = (socket, data = {}) => {
16
- const response = {
17
- ok: true,
18
- data: data
19
- };
20
-
35
+ const write_encoded_response = (socket, response) => {
21
36
  const encoded_response = encode_message(response);
22
37
  socket.write(encoded_response);
23
38
  };
24
39
 
40
+ /**
41
+ * Sends a success response to the client.
42
+ * @param {net.Socket} socket - Client socket connection
43
+ * @param {Object} data - Response data to send
44
+ */
45
+ export const send_success = (socket, data = {}) => {
46
+ const response = create_success_response(data);
47
+ write_encoded_response(socket, response);
48
+ };
49
+
25
50
  /**
26
51
  * Sends an error response to the client.
27
52
  * @param {net.Socket} socket - Client socket connection
28
- * @param {Object} [error={}] - Error information to send
53
+ * @param {Object} error - Error information to send
29
54
  */
30
55
  export const send_error = (socket, error = {}) => {
31
- const response = {
32
- ok: false,
33
- error: error
34
- };
35
-
36
- const encoded_response = encode_message(response);
37
- socket.write(encoded_response);
56
+ const response = create_error_response(error);
57
+ write_encoded_response(socket, response);
38
58
  };
39
59
 
40
60
  /**
41
61
  * Sends a simple message response to the client.
42
62
  * @param {net.Socket} socket - Client socket connection
43
- * @param {string} [message=''] - Message to send
63
+ * @param {string} message - Message to send
44
64
  */
45
65
  export const send_message = (socket, message = '') => {
46
66
  send_success(socket, { message: message });