@joystick.js/db-canary 0.0.0-canary.2251 → 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.
- package/dist/client/database.js +1 -1
- package/dist/client/index.js +1 -1
- package/dist/server/cluster/master.js +4 -4
- package/dist/server/cluster/worker.js +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/lib/auto_index_manager.js +1 -1
- package/dist/server/lib/backup_manager.js +1 -1
- package/dist/server/lib/index_manager.js +1 -1
- package/dist/server/lib/operation_dispatcher.js +1 -1
- package/dist/server/lib/operations/admin.js +1 -1
- package/dist/server/lib/operations/bulk_write.js +1 -1
- package/dist/server/lib/operations/create_index.js +1 -1
- package/dist/server/lib/operations/delete_many.js +1 -1
- package/dist/server/lib/operations/delete_one.js +1 -1
- package/dist/server/lib/operations/find.js +1 -1
- package/dist/server/lib/operations/find_one.js +1 -1
- package/dist/server/lib/operations/insert_one.js +1 -1
- package/dist/server/lib/operations/update_one.js +1 -1
- package/dist/server/lib/send_response.js +1 -1
- package/dist/server/lib/tcp_protocol.js +1 -1
- package/package.json +2 -2
- package/src/client/database.js +92 -119
- package/src/client/index.js +279 -345
- package/src/server/cluster/master.js +265 -156
- package/src/server/cluster/worker.js +26 -18
- package/src/server/index.js +553 -330
- package/src/server/lib/auto_index_manager.js +85 -23
- package/src/server/lib/backup_manager.js +117 -70
- package/src/server/lib/index_manager.js +63 -25
- package/src/server/lib/operation_dispatcher.js +339 -168
- package/src/server/lib/operations/admin.js +343 -205
- package/src/server/lib/operations/bulk_write.js +458 -194
- package/src/server/lib/operations/create_index.js +127 -34
- package/src/server/lib/operations/delete_many.js +204 -67
- package/src/server/lib/operations/delete_one.js +164 -52
- package/src/server/lib/operations/find.js +552 -319
- package/src/server/lib/operations/find_one.js +530 -304
- package/src/server/lib/operations/insert_one.js +147 -52
- package/src/server/lib/operations/update_one.js +334 -93
- package/src/server/lib/send_response.js +37 -17
- package/src/server/lib/tcp_protocol.js +158 -53
- package/test_data_api_key_1758233848259_cglfjzhou/data.mdb +0 -0
- package/test_data_api_key_1758233848259_cglfjzhou/lock.mdb +0 -0
- package/test_data_api_key_1758233848502_urlje2utd/data.mdb +0 -0
- package/test_data_api_key_1758233848502_urlje2utd/lock.mdb +0 -0
- package/test_data_api_key_1758233848738_mtcpfe5ns/data.mdb +0 -0
- package/test_data_api_key_1758233848738_mtcpfe5ns/lock.mdb +0 -0
- package/test_data_api_key_1758233848856_9g97p6gag/data.mdb +0 -0
- package/test_data_api_key_1758233848856_9g97p6gag/lock.mdb +0 -0
- package/test_data_api_key_1758233857008_0tl9zzhj8/data.mdb +0 -0
- package/test_data_api_key_1758233857008_0tl9zzhj8/lock.mdb +0 -0
- package/test_data_api_key_1758233857120_60c2f2uhu/data.mdb +0 -0
- package/test_data_api_key_1758233857120_60c2f2uhu/lock.mdb +0 -0
- package/test_data_api_key_1758233857232_aw7fkqgd9/data.mdb +0 -0
- package/test_data_api_key_1758233857232_aw7fkqgd9/lock.mdb +0 -0
- package/test_data_api_key_1758234881285_4aeflubjb/data.mdb +0 -0
- package/test_data_api_key_1758234881285_4aeflubjb/lock.mdb +0 -0
- package/test_data_api_key_1758234881520_kb0amvtqb/data.mdb +0 -0
- package/test_data_api_key_1758234881520_kb0amvtqb/lock.mdb +0 -0
- package/test_data_api_key_1758234881756_k04gfv2va/data.mdb +0 -0
- package/test_data_api_key_1758234881756_k04gfv2va/lock.mdb +0 -0
- package/test_data_api_key_1758234881876_wn90dpo1z/data.mdb +0 -0
- package/test_data_api_key_1758234881876_wn90dpo1z/lock.mdb +0 -0
- package/test_data_api_key_1758234889461_26xz3dmbr/data.mdb +0 -0
- package/test_data_api_key_1758234889461_26xz3dmbr/lock.mdb +0 -0
- package/test_data_api_key_1758234889572_uziz7e0p5/data.mdb +0 -0
- package/test_data_api_key_1758234889572_uziz7e0p5/lock.mdb +0 -0
- package/test_data_api_key_1758234889684_5f9wmposh/data.mdb +0 -0
- package/test_data_api_key_1758234889684_5f9wmposh/lock.mdb +0 -0
- package/test_data_api_key_1758235657729_prwgm6mxr/data.mdb +0 -0
- package/test_data_api_key_1758235657729_prwgm6mxr/lock.mdb +0 -0
- package/test_data_api_key_1758235657961_rc2da0dc2/data.mdb +0 -0
- package/test_data_api_key_1758235657961_rc2da0dc2/lock.mdb +0 -0
- package/test_data_api_key_1758235658193_oqqxm0sny/data.mdb +0 -0
- package/test_data_api_key_1758235658193_oqqxm0sny/lock.mdb +0 -0
- package/test_data_api_key_1758235658309_vggac1pj6/data.mdb +0 -0
- package/test_data_api_key_1758235658309_vggac1pj6/lock.mdb +0 -0
- package/test_data_api_key_1758235665968_61ko07dd1/data.mdb +0 -0
- package/test_data_api_key_1758235665968_61ko07dd1/lock.mdb +0 -0
- package/test_data_api_key_1758235666082_50lrt6sq8/data.mdb +0 -0
- package/test_data_api_key_1758235666082_50lrt6sq8/lock.mdb +0 -0
- package/test_data_api_key_1758235666194_ykvauwlzh/data.mdb +0 -0
- package/test_data_api_key_1758235666194_ykvauwlzh/lock.mdb +0 -0
- package/test_data_api_key_1758236187207_9c4paeh09/data.mdb +0 -0
- package/test_data_api_key_1758236187207_9c4paeh09/lock.mdb +0 -0
- package/test_data_api_key_1758236187441_4n3o3gkkl/data.mdb +0 -0
- package/test_data_api_key_1758236187441_4n3o3gkkl/lock.mdb +0 -0
- package/test_data_api_key_1758236187672_jt6b21ye0/data.mdb +0 -0
- package/test_data_api_key_1758236187672_jt6b21ye0/lock.mdb +0 -0
- package/test_data_api_key_1758236187788_oo84fz9u6/data.mdb +0 -0
- package/test_data_api_key_1758236187788_oo84fz9u6/lock.mdb +0 -0
- package/test_data_api_key_1758236195507_o9zeznwlm/data.mdb +0 -0
- package/test_data_api_key_1758236195507_o9zeznwlm/lock.mdb +0 -0
- package/test_data_api_key_1758236195619_qsqd60y41/data.mdb +0 -0
- package/test_data_api_key_1758236195619_qsqd60y41/lock.mdb +0 -0
- package/test_data_api_key_1758236195731_im13iq284/data.mdb +0 -0
- package/test_data_api_key_1758236195731_im13iq284/lock.mdb +0 -0
package/src/client/index.js
CHANGED
|
@@ -3,23 +3,61 @@ import { EventEmitter } from 'events';
|
|
|
3
3
|
import { encode as encode_messagepack, decode as decode_messagepack } from 'msgpackr';
|
|
4
4
|
import Database from './database.js';
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Creates MessagePack encoding options for consistent serialization.
|
|
8
|
+
* @returns {Object} MessagePack encoding options
|
|
9
|
+
*/
|
|
10
|
+
const create_messagepack_options = () => ({
|
|
11
|
+
useFloat32: false,
|
|
12
|
+
int64AsType: 'number',
|
|
13
|
+
mapsAsObjects: true
|
|
14
|
+
});
|
|
15
|
+
|
|
6
16
|
/**
|
|
7
17
|
* Encodes a message with MessagePack and prepends a 4-byte length header.
|
|
8
18
|
* @param {any} data - The data to encode
|
|
9
19
|
* @returns {Buffer} The encoded message with length header
|
|
10
20
|
*/
|
|
11
21
|
const encode_message = (data) => {
|
|
12
|
-
|
|
13
|
-
const messagepack_data = encode_messagepack(data, {
|
|
14
|
-
useFloat32: false,
|
|
15
|
-
int64AsType: 'number',
|
|
16
|
-
mapsAsObjects: true
|
|
17
|
-
});
|
|
22
|
+
const messagepack_data = encode_messagepack(data, create_messagepack_options());
|
|
18
23
|
const length_buffer = Buffer.allocUnsafe(4);
|
|
19
24
|
length_buffer.writeUInt32BE(messagepack_data.length, 0);
|
|
20
25
|
return Buffer.concat([length_buffer, messagepack_data]);
|
|
21
26
|
};
|
|
22
27
|
|
|
28
|
+
/**
|
|
29
|
+
* Extracts complete message from buffer when enough data is available.
|
|
30
|
+
* @param {Buffer} buffer - Current buffer
|
|
31
|
+
* @param {number} expected_length - Expected message length
|
|
32
|
+
* @returns {Object} Result with message data and remaining buffer
|
|
33
|
+
*/
|
|
34
|
+
const extract_complete_message = (buffer, expected_length) => {
|
|
35
|
+
const message_data = buffer.slice(0, expected_length);
|
|
36
|
+
const remaining_buffer = buffer.slice(expected_length);
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
const decoded_message = decode_messagepack(message_data, create_messagepack_options());
|
|
40
|
+
return { message: decoded_message, buffer: remaining_buffer };
|
|
41
|
+
} catch (error) {
|
|
42
|
+
throw new Error(`Invalid message format: ${error.message}`);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Reads length header from buffer if available.
|
|
48
|
+
* @param {Buffer} buffer - Current buffer
|
|
49
|
+
* @returns {Object} Result with expected length and remaining buffer
|
|
50
|
+
*/
|
|
51
|
+
const read_length_header = (buffer) => {
|
|
52
|
+
if (buffer.length < 4) {
|
|
53
|
+
return { expected_length: null, buffer };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const expected_length = buffer.readUInt32BE(0);
|
|
57
|
+
const remaining_buffer = buffer.slice(4);
|
|
58
|
+
return { expected_length, buffer: remaining_buffer };
|
|
59
|
+
};
|
|
60
|
+
|
|
23
61
|
/**
|
|
24
62
|
* Creates a message parser for handling TCP stream data with length-prefixed MessagePack messages.
|
|
25
63
|
* @returns {Object} Parser object with parse_messages and reset methods
|
|
@@ -28,64 +66,143 @@ const create_message_parser = () => {
|
|
|
28
66
|
let buffer = Buffer.alloc(0);
|
|
29
67
|
let expected_length = null;
|
|
30
68
|
|
|
31
|
-
/**
|
|
32
|
-
* Parses incoming data and extracts complete messages.
|
|
33
|
-
* @param {Buffer} data - Raw TCP data
|
|
34
|
-
* @returns {Array} Array of decoded messages
|
|
35
|
-
* @throws {Error} When message format is invalid
|
|
36
|
-
*/
|
|
37
69
|
const parse_messages = (data) => {
|
|
38
70
|
buffer = Buffer.concat([buffer, data]);
|
|
39
71
|
const messages = [];
|
|
40
72
|
|
|
41
73
|
while (buffer.length > 0) {
|
|
42
74
|
if (expected_length === null) {
|
|
43
|
-
|
|
75
|
+
const header_result = read_length_header(buffer);
|
|
76
|
+
expected_length = header_result.expected_length;
|
|
77
|
+
buffer = header_result.buffer;
|
|
78
|
+
|
|
79
|
+
if (expected_length === null) {
|
|
44
80
|
break;
|
|
45
81
|
}
|
|
46
|
-
|
|
47
|
-
expected_length = buffer.readUInt32BE(0);
|
|
48
|
-
buffer = buffer.slice(4);
|
|
49
82
|
}
|
|
50
83
|
|
|
51
84
|
if (buffer.length < expected_length) {
|
|
52
85
|
break;
|
|
53
86
|
}
|
|
54
87
|
|
|
55
|
-
const
|
|
56
|
-
|
|
88
|
+
const message_result = extract_complete_message(buffer, expected_length);
|
|
89
|
+
messages.push(message_result.message);
|
|
90
|
+
buffer = message_result.buffer;
|
|
57
91
|
expected_length = null;
|
|
58
|
-
|
|
59
|
-
try {
|
|
60
|
-
// NOTE: Use compatible MessagePack options to avoid parsing issues.
|
|
61
|
-
const decoded_message = decode_messagepack(message_data, {
|
|
62
|
-
useFloat32: false,
|
|
63
|
-
int64AsType: 'number',
|
|
64
|
-
mapsAsObjects: true
|
|
65
|
-
});
|
|
66
|
-
messages.push(decoded_message);
|
|
67
|
-
} catch (error) {
|
|
68
|
-
throw new Error(`Invalid message format: ${error.message}`);
|
|
69
|
-
}
|
|
70
92
|
}
|
|
71
93
|
|
|
72
94
|
return messages;
|
|
73
95
|
};
|
|
74
96
|
|
|
75
|
-
/**
|
|
76
|
-
* Resets the parser state, clearing buffers and expected length.
|
|
77
|
-
*/
|
|
78
97
|
const reset = () => {
|
|
79
98
|
buffer = Buffer.alloc(0);
|
|
80
99
|
expected_length = null;
|
|
81
100
|
};
|
|
82
101
|
|
|
83
|
-
return {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
102
|
+
return { parse_messages, reset };
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Calculates exponential backoff delay with maximum limit.
|
|
107
|
+
* @param {number} attempt_number - Current attempt number
|
|
108
|
+
* @param {number} base_delay - Base delay in milliseconds
|
|
109
|
+
* @returns {number} Calculated delay in milliseconds
|
|
110
|
+
*/
|
|
111
|
+
const calculate_reconnect_delay = (attempt_number, base_delay) => {
|
|
112
|
+
return Math.min(base_delay * Math.pow(2, attempt_number - 1), 30000);
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Creates default client options with fallback values.
|
|
117
|
+
* @param {Object} options - User provided options
|
|
118
|
+
* @returns {Object} Complete options object with defaults
|
|
119
|
+
*/
|
|
120
|
+
const create_client_options = (options = {}) => ({
|
|
121
|
+
host: options.host || 'localhost',
|
|
122
|
+
port: options.port || 1983,
|
|
123
|
+
password: options.password || null,
|
|
124
|
+
timeout: options.timeout || 5000,
|
|
125
|
+
reconnect: options.reconnect !== false,
|
|
126
|
+
max_reconnect_attempts: options.max_reconnect_attempts || 10,
|
|
127
|
+
reconnect_delay: options.reconnect_delay || 1000,
|
|
128
|
+
auto_connect: options.auto_connect !== false
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Creates a connection timeout handler.
|
|
133
|
+
* @param {net.Socket} socket - Socket connection
|
|
134
|
+
* @param {Function} error_handler - Error handling function
|
|
135
|
+
* @param {number} timeout_ms - Timeout duration in milliseconds
|
|
136
|
+
* @returns {NodeJS.Timeout} Timeout reference
|
|
137
|
+
*/
|
|
138
|
+
const create_connection_timeout = (socket, error_handler, timeout_ms) => {
|
|
139
|
+
return setTimeout(() => {
|
|
140
|
+
if (socket && !socket.destroyed) {
|
|
141
|
+
socket.destroy();
|
|
142
|
+
error_handler(new Error('Connection timeout'));
|
|
143
|
+
}
|
|
144
|
+
}, timeout_ms);
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Creates a request timeout handler.
|
|
149
|
+
* @param {Map} pending_requests - Map of pending requests
|
|
150
|
+
* @param {number} request_id - Request identifier
|
|
151
|
+
* @param {number} timeout_ms - Timeout duration in milliseconds
|
|
152
|
+
* @returns {NodeJS.Timeout} Timeout reference
|
|
153
|
+
*/
|
|
154
|
+
const create_request_timeout = (pending_requests, request_id, timeout_ms) => {
|
|
155
|
+
return setTimeout(() => {
|
|
156
|
+
const request = pending_requests.get(request_id);
|
|
157
|
+
if (request) {
|
|
158
|
+
pending_requests.delete(request_id);
|
|
159
|
+
request.reject(new Error('Request timeout'));
|
|
160
|
+
}
|
|
161
|
+
}, timeout_ms);
|
|
87
162
|
};
|
|
88
163
|
|
|
164
|
+
/**
|
|
165
|
+
* Clears all pending requests with error.
|
|
166
|
+
* @param {Map} pending_requests - Map of pending requests
|
|
167
|
+
* @param {string} error_message - Error message to send
|
|
168
|
+
*/
|
|
169
|
+
const clear_pending_requests = (pending_requests, error_message) => {
|
|
170
|
+
for (const [request_id, { reject, timeout }] of pending_requests) {
|
|
171
|
+
clearTimeout(timeout);
|
|
172
|
+
reject(new Error(error_message));
|
|
173
|
+
}
|
|
174
|
+
pending_requests.clear();
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Determines if response indicates success.
|
|
179
|
+
* @param {Object} message - Server response message
|
|
180
|
+
* @returns {boolean} True if response indicates success
|
|
181
|
+
*/
|
|
182
|
+
const is_successful_response = (message) => {
|
|
183
|
+
return message.ok === 1 || message.ok === true;
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Determines if response indicates failure.
|
|
188
|
+
* @param {Object} message - Server response message
|
|
189
|
+
* @returns {boolean} True if response indicates failure
|
|
190
|
+
*/
|
|
191
|
+
const is_error_response = (message) => {
|
|
192
|
+
return message.ok === 0 || message.ok === false;
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Extracts error message from server response.
|
|
197
|
+
* @param {Object} message - Server response message
|
|
198
|
+
* @returns {string} Error message
|
|
199
|
+
*/
|
|
200
|
+
const extract_error_message = (message) => {
|
|
201
|
+
if (typeof message.error === 'string') {
|
|
202
|
+
return message.error;
|
|
203
|
+
}
|
|
204
|
+
return JSON.stringify(message.error) || 'Operation failed';
|
|
205
|
+
};
|
|
89
206
|
|
|
90
207
|
/**
|
|
91
208
|
* @typedef {Object} ClientOptions
|
|
@@ -110,20 +227,17 @@ const create_message_parser = () => {
|
|
|
110
227
|
* @fires JoystickDBClient#response
|
|
111
228
|
*/
|
|
112
229
|
class JoystickDBClient extends EventEmitter {
|
|
113
|
-
/**
|
|
114
|
-
* Creates a new JoystickDB client instance.
|
|
115
|
-
* @param {ClientOptions} [options={}] - Client configuration options
|
|
116
|
-
*/
|
|
117
230
|
constructor(options = {}) {
|
|
118
231
|
super();
|
|
119
232
|
|
|
120
|
-
|
|
121
|
-
this.
|
|
122
|
-
this.
|
|
123
|
-
this.
|
|
124
|
-
this.
|
|
125
|
-
this.
|
|
126
|
-
this.
|
|
233
|
+
const client_options = create_client_options(options);
|
|
234
|
+
this.host = client_options.host;
|
|
235
|
+
this.port = client_options.port;
|
|
236
|
+
this.password = client_options.password;
|
|
237
|
+
this.timeout = client_options.timeout;
|
|
238
|
+
this.reconnect = client_options.reconnect;
|
|
239
|
+
this.max_reconnect_attempts = client_options.max_reconnect_attempts;
|
|
240
|
+
this.reconnect_delay = client_options.reconnect_delay;
|
|
127
241
|
|
|
128
242
|
this.socket = null;
|
|
129
243
|
this.message_parser = null;
|
|
@@ -137,14 +251,11 @@ class JoystickDBClient extends EventEmitter {
|
|
|
137
251
|
this.request_id_counter = 0;
|
|
138
252
|
this.request_queue = [];
|
|
139
253
|
|
|
140
|
-
if (
|
|
254
|
+
if (client_options.auto_connect) {
|
|
141
255
|
this.connect();
|
|
142
256
|
}
|
|
143
257
|
}
|
|
144
258
|
|
|
145
|
-
/**
|
|
146
|
-
* Establishes connection to the JoystickDB server.
|
|
147
|
-
*/
|
|
148
259
|
connect() {
|
|
149
260
|
if (this.is_connecting || this.is_connected) {
|
|
150
261
|
return;
|
|
@@ -154,41 +265,25 @@ class JoystickDBClient extends EventEmitter {
|
|
|
154
265
|
this.socket = new net.Socket();
|
|
155
266
|
this.message_parser = create_message_parser();
|
|
156
267
|
|
|
157
|
-
const connection_timeout =
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
}, this.timeout);
|
|
268
|
+
const connection_timeout = create_connection_timeout(
|
|
269
|
+
this.socket,
|
|
270
|
+
this.handle_connection_error.bind(this),
|
|
271
|
+
this.timeout
|
|
272
|
+
);
|
|
163
273
|
|
|
274
|
+
this.setup_socket_handlers(connection_timeout);
|
|
164
275
|
this.socket.connect(this.port, this.host, () => {
|
|
165
|
-
|
|
166
|
-
this.is_connected = true;
|
|
167
|
-
this.is_connecting = false;
|
|
168
|
-
this.reconnect_attempts = 0;
|
|
169
|
-
|
|
170
|
-
this.emit('connect');
|
|
171
|
-
|
|
172
|
-
if (this.password) {
|
|
173
|
-
this.authenticate();
|
|
174
|
-
} else {
|
|
175
|
-
// NOTE: If no password provided, assume development mode and skip authentication.
|
|
176
|
-
this.is_authenticated = true;
|
|
177
|
-
this.emit('authenticated');
|
|
178
|
-
this.process_request_queue();
|
|
179
|
-
}
|
|
276
|
+
this.handle_successful_connection(connection_timeout);
|
|
180
277
|
});
|
|
181
|
-
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Sets up socket event handlers.
|
|
282
|
+
* @param {NodeJS.Timeout} connection_timeout - Connection timeout reference
|
|
283
|
+
*/
|
|
284
|
+
setup_socket_handlers(connection_timeout) {
|
|
182
285
|
this.socket.on('data', (data) => {
|
|
183
|
-
|
|
184
|
-
const messages = this.message_parser.parse_messages(data);
|
|
185
|
-
|
|
186
|
-
for (const message of messages) {
|
|
187
|
-
this.handle_message(message);
|
|
188
|
-
}
|
|
189
|
-
} catch (error) {
|
|
190
|
-
this.emit('error', new Error(`Message parsing failed: ${error.message}`));
|
|
191
|
-
}
|
|
286
|
+
this.handle_incoming_data(data);
|
|
192
287
|
});
|
|
193
288
|
|
|
194
289
|
this.socket.on('error', (error) => {
|
|
@@ -203,8 +298,49 @@ class JoystickDBClient extends EventEmitter {
|
|
|
203
298
|
}
|
|
204
299
|
|
|
205
300
|
/**
|
|
206
|
-
*
|
|
301
|
+
* Handles successful socket connection.
|
|
302
|
+
* @param {NodeJS.Timeout} connection_timeout - Connection timeout reference
|
|
303
|
+
*/
|
|
304
|
+
handle_successful_connection(connection_timeout) {
|
|
305
|
+
clearTimeout(connection_timeout);
|
|
306
|
+
this.is_connected = true;
|
|
307
|
+
this.is_connecting = false;
|
|
308
|
+
this.reconnect_attempts = 0;
|
|
309
|
+
|
|
310
|
+
this.emit('connect');
|
|
311
|
+
|
|
312
|
+
if (this.password) {
|
|
313
|
+
this.authenticate();
|
|
314
|
+
} else {
|
|
315
|
+
this.handle_authentication_complete();
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Handles completion of authentication process.
|
|
321
|
+
*/
|
|
322
|
+
handle_authentication_complete() {
|
|
323
|
+
this.is_authenticated = true;
|
|
324
|
+
this.emit('authenticated');
|
|
325
|
+
this.process_request_queue();
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Handles incoming data from socket.
|
|
330
|
+
* @param {Buffer} data - Raw TCP data
|
|
207
331
|
*/
|
|
332
|
+
handle_incoming_data(data) {
|
|
333
|
+
try {
|
|
334
|
+
const messages = this.message_parser.parse_messages(data);
|
|
335
|
+
|
|
336
|
+
for (const message of messages) {
|
|
337
|
+
this.handle_message(message);
|
|
338
|
+
}
|
|
339
|
+
} catch (error) {
|
|
340
|
+
this.emit('error', new Error(`Message parsing failed: ${error.message}`));
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
208
344
|
async authenticate() {
|
|
209
345
|
if (!this.password) {
|
|
210
346
|
this.emit('error', new Error('Password required for authentication. Provide password in client options: joystickdb.client({ password: "your_password" })'));
|
|
@@ -218,9 +354,7 @@ class JoystickDBClient extends EventEmitter {
|
|
|
218
354
|
});
|
|
219
355
|
|
|
220
356
|
if (result.ok === 1) {
|
|
221
|
-
this.
|
|
222
|
-
this.emit('authenticated');
|
|
223
|
-
this.process_request_queue();
|
|
357
|
+
this.handle_authentication_complete();
|
|
224
358
|
} else {
|
|
225
359
|
throw new Error('Authentication failed');
|
|
226
360
|
}
|
|
@@ -230,58 +364,51 @@ class JoystickDBClient extends EventEmitter {
|
|
|
230
364
|
}
|
|
231
365
|
}
|
|
232
366
|
|
|
233
|
-
/**
|
|
234
|
-
* Handles incoming messages from the server.
|
|
235
|
-
* @param {Object} message - Decoded message from server
|
|
236
|
-
*/
|
|
237
367
|
handle_message(message) {
|
|
238
368
|
if (this.pending_requests.size > 0) {
|
|
239
|
-
|
|
240
|
-
clearTimeout(timeout);
|
|
241
|
-
this.pending_requests.delete(request_id);
|
|
242
|
-
|
|
243
|
-
if (message.ok === 1 || message.ok === true) {
|
|
244
|
-
resolve(message);
|
|
245
|
-
} else if (message.ok === 0 || message.ok === false) {
|
|
246
|
-
const error_message = typeof message.error === 'string' ? message.error : JSON.stringify(message.error) || 'Operation failed';
|
|
247
|
-
reject(new Error(error_message));
|
|
248
|
-
} else {
|
|
249
|
-
resolve(message);
|
|
250
|
-
}
|
|
369
|
+
this.handle_pending_request_response(message);
|
|
251
370
|
} else {
|
|
252
371
|
this.emit('response', message);
|
|
253
372
|
}
|
|
254
373
|
}
|
|
255
374
|
|
|
256
375
|
/**
|
|
257
|
-
* Handles
|
|
258
|
-
* @param {
|
|
376
|
+
* Handles response for pending request.
|
|
377
|
+
* @param {Object} message - Server response message
|
|
259
378
|
*/
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
this.
|
|
379
|
+
handle_pending_request_response(message) {
|
|
380
|
+
const [request_id, { resolve, reject, timeout }] = this.pending_requests.entries().next().value;
|
|
381
|
+
clearTimeout(timeout);
|
|
382
|
+
this.pending_requests.delete(request_id);
|
|
264
383
|
|
|
265
|
-
if (
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
384
|
+
if (is_successful_response(message)) {
|
|
385
|
+
resolve(message);
|
|
386
|
+
} else if (is_error_response(message)) {
|
|
387
|
+
const error_message = extract_error_message(message);
|
|
388
|
+
reject(new Error(error_message));
|
|
389
|
+
} else {
|
|
390
|
+
resolve(message);
|
|
269
391
|
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
handle_connection_error(error) {
|
|
395
|
+
this.reset_connection_state();
|
|
396
|
+
clear_pending_requests(this.pending_requests, 'Connection lost');
|
|
270
397
|
|
|
271
|
-
|
|
272
|
-
this.message_parser.reset();
|
|
273
|
-
}
|
|
398
|
+
this.emit('error', error);
|
|
274
399
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
400
|
+
if (this.should_attempt_reconnect()) {
|
|
401
|
+
this.schedule_reconnect();
|
|
402
|
+
} else {
|
|
403
|
+
this.emit('disconnect');
|
|
279
404
|
}
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
handle_disconnect() {
|
|
408
|
+
this.reset_connection_state();
|
|
409
|
+
clear_pending_requests(this.pending_requests, 'Connection closed');
|
|
283
410
|
|
|
284
|
-
if (this.
|
|
411
|
+
if (this.should_attempt_reconnect()) {
|
|
285
412
|
this.schedule_reconnect();
|
|
286
413
|
} else {
|
|
287
414
|
this.emit('disconnect');
|
|
@@ -289,45 +416,35 @@ class JoystickDBClient extends EventEmitter {
|
|
|
289
416
|
}
|
|
290
417
|
|
|
291
418
|
/**
|
|
292
|
-
*
|
|
419
|
+
* Resets connection state variables.
|
|
293
420
|
*/
|
|
294
|
-
|
|
421
|
+
reset_connection_state() {
|
|
422
|
+
this.is_connecting = false;
|
|
295
423
|
this.is_connected = false;
|
|
296
424
|
this.is_authenticated = false;
|
|
297
|
-
this.is_connecting = false;
|
|
298
425
|
|
|
299
426
|
if (this.socket) {
|
|
300
427
|
this.socket.removeAllListeners();
|
|
428
|
+
this.socket.destroy();
|
|
301
429
|
this.socket = null;
|
|
302
430
|
}
|
|
303
431
|
|
|
304
432
|
if (this.message_parser) {
|
|
305
433
|
this.message_parser.reset();
|
|
306
434
|
}
|
|
307
|
-
|
|
308
|
-
// NOTE: Reject all pending requests.
|
|
309
|
-
for (const [request_id, { reject, timeout }] of this.pending_requests) {
|
|
310
|
-
clearTimeout(timeout);
|
|
311
|
-
reject(new Error('Connection closed'));
|
|
312
|
-
}
|
|
313
|
-
this.pending_requests.clear();
|
|
314
|
-
|
|
315
|
-
if (this.reconnect && this.reconnect_attempts < this.max_reconnect_attempts) {
|
|
316
|
-
this.schedule_reconnect();
|
|
317
|
-
} else {
|
|
318
|
-
this.emit('disconnect');
|
|
319
|
-
}
|
|
320
435
|
}
|
|
321
436
|
|
|
322
437
|
/**
|
|
323
|
-
*
|
|
438
|
+
* Determines if reconnection should be attempted.
|
|
439
|
+
* @returns {boolean} True if reconnection should be attempted
|
|
324
440
|
*/
|
|
441
|
+
should_attempt_reconnect() {
|
|
442
|
+
return this.reconnect && this.reconnect_attempts < this.max_reconnect_attempts;
|
|
443
|
+
}
|
|
444
|
+
|
|
325
445
|
schedule_reconnect() {
|
|
326
446
|
this.reconnect_attempts++;
|
|
327
|
-
const delay =
|
|
328
|
-
this.reconnect_delay * Math.pow(2, this.reconnect_attempts - 1),
|
|
329
|
-
30000
|
|
330
|
-
);
|
|
447
|
+
const delay = calculate_reconnect_delay(this.reconnect_attempts, this.reconnect_delay);
|
|
331
448
|
|
|
332
449
|
this.emit('reconnecting', { attempt: this.reconnect_attempts, delay });
|
|
333
450
|
|
|
@@ -336,36 +453,16 @@ class JoystickDBClient extends EventEmitter {
|
|
|
336
453
|
}, delay);
|
|
337
454
|
}
|
|
338
455
|
|
|
339
|
-
/**
|
|
340
|
-
* Sends a request to the server.
|
|
341
|
-
* @param {string} op - Operation name
|
|
342
|
-
* @param {Object} [data={}] - Request data
|
|
343
|
-
* @param {boolean} [use_queue=true] - Whether to queue request if not connected
|
|
344
|
-
* @returns {Promise<Object>} Server response
|
|
345
|
-
*/
|
|
346
456
|
send_request(op, data = {}, use_queue = true) {
|
|
347
457
|
return new Promise((resolve, reject) => {
|
|
348
458
|
const request_id = ++this.request_id_counter;
|
|
349
|
-
const message = {
|
|
350
|
-
op,
|
|
351
|
-
data
|
|
352
|
-
};
|
|
459
|
+
const message = { op, data };
|
|
353
460
|
|
|
354
|
-
const request = {
|
|
355
|
-
message,
|
|
356
|
-
resolve,
|
|
357
|
-
reject,
|
|
358
|
-
request_id
|
|
359
|
-
};
|
|
461
|
+
const request = { message, resolve, reject, request_id };
|
|
360
462
|
|
|
361
|
-
if (
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
return;
|
|
365
|
-
} else {
|
|
366
|
-
reject(new Error('Not connected or authenticated'));
|
|
367
|
-
return;
|
|
368
|
-
}
|
|
463
|
+
if (this.should_queue_request(op, use_queue)) {
|
|
464
|
+
this.request_queue.push(request);
|
|
465
|
+
return;
|
|
369
466
|
}
|
|
370
467
|
|
|
371
468
|
this.send_request_now(request);
|
|
@@ -373,17 +470,22 @@ class JoystickDBClient extends EventEmitter {
|
|
|
373
470
|
}
|
|
374
471
|
|
|
375
472
|
/**
|
|
376
|
-
*
|
|
377
|
-
* @param {
|
|
473
|
+
* Determines if request should be queued.
|
|
474
|
+
* @param {string} op - Operation type
|
|
475
|
+
* @param {boolean} use_queue - Whether to use queue
|
|
476
|
+
* @returns {boolean} True if request should be queued
|
|
378
477
|
*/
|
|
478
|
+
should_queue_request(op, use_queue) {
|
|
479
|
+
const bypass_auth_ops = ['authentication', 'setup', 'ping'];
|
|
480
|
+
const needs_auth = !bypass_auth_ops.includes(op);
|
|
481
|
+
|
|
482
|
+
return (!this.is_connected || (needs_auth && !this.is_authenticated)) && use_queue;
|
|
483
|
+
}
|
|
484
|
+
|
|
379
485
|
send_request_now(request) {
|
|
380
486
|
const { message, resolve, reject, request_id } = request;
|
|
381
487
|
|
|
382
|
-
const timeout =
|
|
383
|
-
this.pending_requests.delete(request_id);
|
|
384
|
-
reject(new Error('Request timeout'));
|
|
385
|
-
}, this.timeout);
|
|
386
|
-
|
|
488
|
+
const timeout = create_request_timeout(this.pending_requests, request_id, this.timeout);
|
|
387
489
|
this.pending_requests.set(request_id, { resolve, reject, timeout });
|
|
388
490
|
|
|
389
491
|
try {
|
|
@@ -396,9 +498,6 @@ class JoystickDBClient extends EventEmitter {
|
|
|
396
498
|
}
|
|
397
499
|
}
|
|
398
500
|
|
|
399
|
-
/**
|
|
400
|
-
* Processes queued requests after connection and authentication.
|
|
401
|
-
*/
|
|
402
501
|
process_request_queue() {
|
|
403
502
|
while (this.request_queue.length > 0 && this.is_connected && this.is_authenticated) {
|
|
404
503
|
const request = this.request_queue.shift();
|
|
@@ -406,9 +505,6 @@ class JoystickDBClient extends EventEmitter {
|
|
|
406
505
|
}
|
|
407
506
|
}
|
|
408
507
|
|
|
409
|
-
/**
|
|
410
|
-
* Disconnects from the server and disables reconnection.
|
|
411
|
-
*/
|
|
412
508
|
disconnect() {
|
|
413
509
|
this.reconnect = false;
|
|
414
510
|
|
|
@@ -422,30 +518,15 @@ class JoystickDBClient extends EventEmitter {
|
|
|
422
518
|
}
|
|
423
519
|
}
|
|
424
520
|
|
|
425
|
-
|
|
426
|
-
|
|
427
521
|
// NOTE: Backup Operations.
|
|
428
|
-
/**
|
|
429
|
-
* Triggers an immediate backup.
|
|
430
|
-
* @returns {Promise<Object>} Backup result
|
|
431
|
-
*/
|
|
432
522
|
async backup_now() {
|
|
433
523
|
return this.send_request('admin', { admin_action: 'backup_now' });
|
|
434
524
|
}
|
|
435
525
|
|
|
436
|
-
/**
|
|
437
|
-
* Lists all available backups.
|
|
438
|
-
* @returns {Promise<Object>} Backups list
|
|
439
|
-
*/
|
|
440
526
|
async list_backups() {
|
|
441
527
|
return this.send_request('admin', { admin_action: 'list_backups' });
|
|
442
528
|
}
|
|
443
529
|
|
|
444
|
-
/**
|
|
445
|
-
* Restores from a specific backup.
|
|
446
|
-
* @param {string} backup_name - Name of backup to restore
|
|
447
|
-
* @returns {Promise<Object>} Restore result
|
|
448
|
-
*/
|
|
449
530
|
async restore_backup(backup_name) {
|
|
450
531
|
return this.send_request('admin', {
|
|
451
532
|
admin_action: 'restore_backup',
|
|
@@ -454,96 +535,48 @@ class JoystickDBClient extends EventEmitter {
|
|
|
454
535
|
}
|
|
455
536
|
|
|
456
537
|
// NOTE: Replication Operations.
|
|
457
|
-
/**
|
|
458
|
-
* Gets replication status and statistics.
|
|
459
|
-
* @returns {Promise<Object>} Replication status
|
|
460
|
-
*/
|
|
461
538
|
async get_replication_status() {
|
|
462
539
|
return this.send_request('admin', { admin_action: 'get_replication_status' });
|
|
463
540
|
}
|
|
464
541
|
|
|
465
|
-
/**
|
|
466
|
-
* Adds a secondary node to replication.
|
|
467
|
-
* @param {Object} secondary - Secondary node configuration
|
|
468
|
-
* @param {string} secondary.id - Secondary node ID
|
|
469
|
-
* @param {string} secondary.ip - Secondary node IP address
|
|
470
|
-
* @param {number} secondary.port - Secondary node port
|
|
471
|
-
* @param {string} secondary.private_key - Base64 encoded private key
|
|
472
|
-
* @returns {Promise<Object>} Add secondary result
|
|
473
|
-
*/
|
|
474
542
|
async add_secondary(secondary) {
|
|
475
543
|
return this.send_request('admin', { admin_action: 'add_secondary', ...secondary });
|
|
476
544
|
}
|
|
477
545
|
|
|
478
|
-
/**
|
|
479
|
-
* Removes a secondary node from replication.
|
|
480
|
-
* @param {string} secondary_id - Secondary node ID to remove
|
|
481
|
-
* @returns {Promise<Object>} Remove secondary result
|
|
482
|
-
*/
|
|
483
546
|
async remove_secondary(secondary_id) {
|
|
484
547
|
return this.send_request('admin', { admin_action: 'remove_secondary', secondary_id });
|
|
485
548
|
}
|
|
486
549
|
|
|
487
|
-
/**
|
|
488
|
-
* Forces synchronization with all secondary nodes.
|
|
489
|
-
* @returns {Promise<Object>} Sync result
|
|
490
|
-
*/
|
|
491
550
|
async sync_secondaries() {
|
|
492
551
|
return this.send_request('admin', { admin_action: 'sync_secondaries' });
|
|
493
552
|
}
|
|
494
553
|
|
|
495
|
-
/**
|
|
496
|
-
* Gets health status of secondary nodes.
|
|
497
|
-
* @returns {Promise<Object>} Secondary health status
|
|
498
|
-
*/
|
|
499
554
|
async get_secondary_health() {
|
|
500
555
|
return this.send_request('admin', { admin_action: 'get_secondary_health' });
|
|
501
556
|
}
|
|
502
557
|
|
|
503
|
-
/**
|
|
504
|
-
* Gets write forwarder status (for secondary nodes).
|
|
505
|
-
* @returns {Promise<Object>} Write forwarder status
|
|
506
|
-
*/
|
|
507
558
|
async get_forwarder_status() {
|
|
508
559
|
return this.send_request('admin', { admin_action: 'get_forwarder_status' });
|
|
509
560
|
}
|
|
510
561
|
|
|
511
562
|
// NOTE: Health and Utility Operations.
|
|
512
|
-
/**
|
|
513
|
-
* Pings the server to check connectivity.
|
|
514
|
-
* @returns {Promise<Object>} Ping result
|
|
515
|
-
*/
|
|
516
563
|
async ping() {
|
|
517
564
|
return this.send_request('ping', {}, false);
|
|
518
565
|
}
|
|
519
566
|
|
|
520
|
-
/**
|
|
521
|
-
* Reloads server configuration.
|
|
522
|
-
* @returns {Promise<Object>} Reload result
|
|
523
|
-
*/
|
|
524
567
|
async reload() {
|
|
525
568
|
return this.send_request('reload');
|
|
526
569
|
}
|
|
527
570
|
|
|
528
571
|
// NOTE: Auto-Indexing Operations.
|
|
529
|
-
/**
|
|
530
|
-
* Gets automatic indexing statistics.
|
|
531
|
-
* @returns {Promise<Object>} Auto-indexing statistics
|
|
532
|
-
*/
|
|
533
572
|
async get_auto_index_stats() {
|
|
534
573
|
return this.send_request('admin', { admin_action: 'get_auto_index_stats' });
|
|
535
574
|
}
|
|
536
575
|
|
|
537
|
-
|
|
538
576
|
// NOTE: Setup Operation.
|
|
539
|
-
/**
|
|
540
|
-
* Performs initial server setup.
|
|
541
|
-
* @returns {Promise<Object>} Setup result
|
|
542
|
-
*/
|
|
543
577
|
async setup() {
|
|
544
578
|
const result = await this.send_request('setup', {}, false);
|
|
545
579
|
|
|
546
|
-
// NOTE: Display setup instructions to user.
|
|
547
580
|
if (result.data && result.data.instructions) {
|
|
548
581
|
console.log(result.data.instructions);
|
|
549
582
|
}
|
|
@@ -552,14 +585,6 @@ class JoystickDBClient extends EventEmitter {
|
|
|
552
585
|
}
|
|
553
586
|
|
|
554
587
|
// NOTE: Database Operations.
|
|
555
|
-
/**
|
|
556
|
-
* Deletes multiple documents from a collection.
|
|
557
|
-
* @param {string} collection - Collection name
|
|
558
|
-
* @param {Object} [filter={}] - Query filter to match documents
|
|
559
|
-
* @param {Object} [options={}] - Delete options
|
|
560
|
-
* @param {number} [options.limit] - Maximum number of documents to delete
|
|
561
|
-
* @returns {Promise<Object>} Delete result with acknowledged, deleted_count, and operation_time
|
|
562
|
-
*/
|
|
563
588
|
async delete_many(collection, filter = {}, options = {}) {
|
|
564
589
|
return this.send_request('delete_many', {
|
|
565
590
|
collection,
|
|
@@ -569,21 +594,11 @@ class JoystickDBClient extends EventEmitter {
|
|
|
569
594
|
}
|
|
570
595
|
|
|
571
596
|
// NOTE: Database Interface.
|
|
572
|
-
/**
|
|
573
|
-
* Returns a database interface for method chaining operations.
|
|
574
|
-
* @param {string} database_name - Database name
|
|
575
|
-
* @returns {Database} Database interface instance
|
|
576
|
-
*/
|
|
577
597
|
db(database_name) {
|
|
578
598
|
return new Database(this, database_name);
|
|
579
599
|
}
|
|
580
600
|
|
|
581
|
-
|
|
582
601
|
// NOTE: Multi-Database Admin Operations.
|
|
583
|
-
/**
|
|
584
|
-
* Lists all databases on the server.
|
|
585
|
-
* @returns {Promise<Object>} Databases list
|
|
586
|
-
*/
|
|
587
602
|
async list_databases() {
|
|
588
603
|
return this.send_request('admin', { admin_action: 'list_databases' });
|
|
589
604
|
}
|
|
@@ -594,24 +609,12 @@ class JoystickDBClient extends EventEmitter {
|
|
|
594
609
|
* Provides a fluent API for database operations on a specific collection.
|
|
595
610
|
*/
|
|
596
611
|
class Collection {
|
|
597
|
-
/**
|
|
598
|
-
* Creates a new Collection instance.
|
|
599
|
-
* @param {JoystickDBClient} client - The client instance
|
|
600
|
-
* @param {string} database_name - Name of the database
|
|
601
|
-
* @param {string} collection_name - Name of the collection
|
|
602
|
-
*/
|
|
603
612
|
constructor(client, database_name, collection_name) {
|
|
604
613
|
this.client = client;
|
|
605
614
|
this.database_name = database_name;
|
|
606
615
|
this.collection_name = collection_name;
|
|
607
616
|
}
|
|
608
617
|
|
|
609
|
-
/**
|
|
610
|
-
* Inserts a single document into the collection.
|
|
611
|
-
* @param {Object} document - Document to insert
|
|
612
|
-
* @param {Object} [options={}] - Insert options
|
|
613
|
-
* @returns {Promise<Object>} Insert result
|
|
614
|
-
*/
|
|
615
618
|
async insert_one(document, options = {}) {
|
|
616
619
|
return this.client.send_request('insert_one', {
|
|
617
620
|
database: this.database_name,
|
|
@@ -621,12 +624,6 @@ class Collection {
|
|
|
621
624
|
});
|
|
622
625
|
}
|
|
623
626
|
|
|
624
|
-
/**
|
|
625
|
-
* Finds a single document in the collection.
|
|
626
|
-
* @param {Object} [filter={}] - Query filter
|
|
627
|
-
* @param {Object} [options={}] - Find options
|
|
628
|
-
* @returns {Promise<Object|null>} Found document or null
|
|
629
|
-
*/
|
|
630
627
|
async find_one(filter = {}, options = {}) {
|
|
631
628
|
const result = await this.client.send_request('find_one', {
|
|
632
629
|
database: this.database_name,
|
|
@@ -637,12 +634,6 @@ class Collection {
|
|
|
637
634
|
return result.document;
|
|
638
635
|
}
|
|
639
636
|
|
|
640
|
-
/**
|
|
641
|
-
* Finds multiple documents in the collection.
|
|
642
|
-
* @param {Object} [filter={}] - Query filter
|
|
643
|
-
* @param {Object} [options={}] - Find options
|
|
644
|
-
* @returns {Promise<Array>} Array of found documents
|
|
645
|
-
*/
|
|
646
637
|
async find(filter = {}, options = {}) {
|
|
647
638
|
const result = await this.client.send_request('find', {
|
|
648
639
|
database: this.database_name,
|
|
@@ -653,13 +644,6 @@ class Collection {
|
|
|
653
644
|
return result.documents || [];
|
|
654
645
|
}
|
|
655
646
|
|
|
656
|
-
/**
|
|
657
|
-
* Updates a single document in the collection.
|
|
658
|
-
* @param {Object} filter - Query filter to match document
|
|
659
|
-
* @param {Object} update - Update operations
|
|
660
|
-
* @param {Object} [options={}] - Update options
|
|
661
|
-
* @returns {Promise<Object>} Update result
|
|
662
|
-
*/
|
|
663
647
|
async update_one(filter, update, options = {}) {
|
|
664
648
|
return this.client.send_request('update_one', {
|
|
665
649
|
database: this.database_name,
|
|
@@ -670,12 +654,6 @@ class Collection {
|
|
|
670
654
|
});
|
|
671
655
|
}
|
|
672
656
|
|
|
673
|
-
/**
|
|
674
|
-
* Deletes a single document from the collection.
|
|
675
|
-
* @param {Object} filter - Query filter to match document
|
|
676
|
-
* @param {Object} [options={}] - Delete options
|
|
677
|
-
* @returns {Promise<Object>} Delete result
|
|
678
|
-
*/
|
|
679
657
|
async delete_one(filter, options = {}) {
|
|
680
658
|
return this.client.send_request('delete_one', {
|
|
681
659
|
database: this.database_name,
|
|
@@ -685,13 +663,6 @@ class Collection {
|
|
|
685
663
|
});
|
|
686
664
|
}
|
|
687
665
|
|
|
688
|
-
/**
|
|
689
|
-
* Deletes multiple documents from the collection.
|
|
690
|
-
* @param {Object} [filter={}] - Query filter to match documents
|
|
691
|
-
* @param {Object} [options={}] - Delete options
|
|
692
|
-
* @param {number} [options.limit] - Maximum number of documents to delete
|
|
693
|
-
* @returns {Promise<Object>} Delete result with acknowledged, deleted_count, and operation_time
|
|
694
|
-
*/
|
|
695
666
|
async delete_many(filter = {}, options = {}) {
|
|
696
667
|
return this.client.send_request('delete_many', {
|
|
697
668
|
database: this.database_name,
|
|
@@ -701,12 +672,6 @@ class Collection {
|
|
|
701
672
|
});
|
|
702
673
|
}
|
|
703
674
|
|
|
704
|
-
/**
|
|
705
|
-
* Performs multiple write operations in a single request.
|
|
706
|
-
* @param {Array<Object>} operations - Array of write operations
|
|
707
|
-
* @param {Object} [options={}] - Bulk write options
|
|
708
|
-
* @returns {Promise<Object>} Bulk write result
|
|
709
|
-
*/
|
|
710
675
|
async bulk_write(operations, options = {}) {
|
|
711
676
|
return this.client.send_request('bulk_write', {
|
|
712
677
|
database: this.database_name,
|
|
@@ -716,12 +681,6 @@ class Collection {
|
|
|
716
681
|
});
|
|
717
682
|
}
|
|
718
683
|
|
|
719
|
-
/**
|
|
720
|
-
* Creates an index on a collection field.
|
|
721
|
-
* @param {string} field - Field name to index
|
|
722
|
-
* @param {Object} [options={}] - Index options
|
|
723
|
-
* @returns {Promise<Object>} Index creation result
|
|
724
|
-
*/
|
|
725
684
|
async create_index(field, options = {}) {
|
|
726
685
|
return this.client.send_request('create_index', {
|
|
727
686
|
database: this.database_name,
|
|
@@ -731,12 +690,6 @@ class Collection {
|
|
|
731
690
|
});
|
|
732
691
|
}
|
|
733
692
|
|
|
734
|
-
/**
|
|
735
|
-
* Creates or updates an index on a collection field (upsert operation).
|
|
736
|
-
* @param {string} field - Field name to index
|
|
737
|
-
* @param {Object} [options={}] - Index options
|
|
738
|
-
* @returns {Promise<Object>} Index upsert result
|
|
739
|
-
*/
|
|
740
693
|
async upsert_index(field, options = {}) {
|
|
741
694
|
return this.client.send_request('create_index', {
|
|
742
695
|
database: this.database_name,
|
|
@@ -746,11 +699,6 @@ class Collection {
|
|
|
746
699
|
});
|
|
747
700
|
}
|
|
748
701
|
|
|
749
|
-
/**
|
|
750
|
-
* Drops an index from a collection field.
|
|
751
|
-
* @param {string} field - Field name of index to drop
|
|
752
|
-
* @returns {Promise<Object>} Index drop result
|
|
753
|
-
*/
|
|
754
702
|
async drop_index(field) {
|
|
755
703
|
return this.client.send_request('drop_index', {
|
|
756
704
|
database: this.database_name,
|
|
@@ -759,10 +707,6 @@ class Collection {
|
|
|
759
707
|
});
|
|
760
708
|
}
|
|
761
709
|
|
|
762
|
-
/**
|
|
763
|
-
* Gets all indexes for the collection.
|
|
764
|
-
* @returns {Promise<Object>} Indexes information
|
|
765
|
-
*/
|
|
766
710
|
async get_indexes() {
|
|
767
711
|
return this.client.send_request('get_indexes', {
|
|
768
712
|
database: this.database_name,
|
|
@@ -771,19 +715,9 @@ class Collection {
|
|
|
771
715
|
}
|
|
772
716
|
}
|
|
773
717
|
|
|
774
|
-
// NOTE: Add Collection class as static property for Database class access.
|
|
775
718
|
JoystickDBClient.Collection = Collection;
|
|
776
719
|
|
|
777
|
-
/**
|
|
778
|
-
* JoystickDB client factory and utilities.
|
|
779
|
-
* @namespace
|
|
780
|
-
*/
|
|
781
720
|
const joystickdb = {
|
|
782
|
-
/**
|
|
783
|
-
* Creates a new JoystickDB client instance.
|
|
784
|
-
* @param {ClientOptions} options - Client configuration options
|
|
785
|
-
* @returns {JoystickDBClient} New client instance
|
|
786
|
-
*/
|
|
787
721
|
client: (options) => new JoystickDBClient(options)
|
|
788
722
|
};
|
|
789
723
|
|