@joystick.js/db-canary 0.0.0-canary.2251 → 0.0.0-canary.2253
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 +159 -133
- package/src/client/index.js +285 -346
- 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
|
-
parse_messages,
|
|
85
|
-
reset
|
|
86
|
-
};
|
|
102
|
+
return { parse_messages, reset };
|
|
87
103
|
};
|
|
88
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);
|
|
162
|
+
};
|
|
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,24 +594,19 @@ 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
|
}
|
|
605
|
+
|
|
606
|
+
// NOTE: Statistics Operations.
|
|
607
|
+
async get_stats() {
|
|
608
|
+
return this.send_request('admin', { admin_action: 'stats' });
|
|
609
|
+
}
|
|
590
610
|
}
|
|
591
611
|
|
|
592
612
|
/**
|
|
@@ -594,24 +614,12 @@ class JoystickDBClient extends EventEmitter {
|
|
|
594
614
|
* Provides a fluent API for database operations on a specific collection.
|
|
595
615
|
*/
|
|
596
616
|
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
617
|
constructor(client, database_name, collection_name) {
|
|
604
618
|
this.client = client;
|
|
605
619
|
this.database_name = database_name;
|
|
606
620
|
this.collection_name = collection_name;
|
|
607
621
|
}
|
|
608
622
|
|
|
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
623
|
async insert_one(document, options = {}) {
|
|
616
624
|
return this.client.send_request('insert_one', {
|
|
617
625
|
database: this.database_name,
|
|
@@ -621,12 +629,6 @@ class Collection {
|
|
|
621
629
|
});
|
|
622
630
|
}
|
|
623
631
|
|
|
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
632
|
async find_one(filter = {}, options = {}) {
|
|
631
633
|
const result = await this.client.send_request('find_one', {
|
|
632
634
|
database: this.database_name,
|
|
@@ -637,12 +639,6 @@ class Collection {
|
|
|
637
639
|
return result.document;
|
|
638
640
|
}
|
|
639
641
|
|
|
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
642
|
async find(filter = {}, options = {}) {
|
|
647
643
|
const result = await this.client.send_request('find', {
|
|
648
644
|
database: this.database_name,
|
|
@@ -650,16 +646,9 @@ class Collection {
|
|
|
650
646
|
filter,
|
|
651
647
|
options
|
|
652
648
|
});
|
|
653
|
-
return result.documents || [];
|
|
649
|
+
return { documents: result.documents || [] };
|
|
654
650
|
}
|
|
655
651
|
|
|
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
652
|
async update_one(filter, update, options = {}) {
|
|
664
653
|
return this.client.send_request('update_one', {
|
|
665
654
|
database: this.database_name,
|
|
@@ -670,12 +659,6 @@ class Collection {
|
|
|
670
659
|
});
|
|
671
660
|
}
|
|
672
661
|
|
|
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
662
|
async delete_one(filter, options = {}) {
|
|
680
663
|
return this.client.send_request('delete_one', {
|
|
681
664
|
database: this.database_name,
|
|
@@ -685,13 +668,6 @@ class Collection {
|
|
|
685
668
|
});
|
|
686
669
|
}
|
|
687
670
|
|
|
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
671
|
async delete_many(filter = {}, options = {}) {
|
|
696
672
|
return this.client.send_request('delete_many', {
|
|
697
673
|
database: this.database_name,
|
|
@@ -701,12 +677,6 @@ class Collection {
|
|
|
701
677
|
});
|
|
702
678
|
}
|
|
703
679
|
|
|
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
680
|
async bulk_write(operations, options = {}) {
|
|
711
681
|
return this.client.send_request('bulk_write', {
|
|
712
682
|
database: this.database_name,
|
|
@@ -716,12 +686,6 @@ class Collection {
|
|
|
716
686
|
});
|
|
717
687
|
}
|
|
718
688
|
|
|
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
689
|
async create_index(field, options = {}) {
|
|
726
690
|
return this.client.send_request('create_index', {
|
|
727
691
|
database: this.database_name,
|
|
@@ -731,12 +695,6 @@ class Collection {
|
|
|
731
695
|
});
|
|
732
696
|
}
|
|
733
697
|
|
|
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
698
|
async upsert_index(field, options = {}) {
|
|
741
699
|
return this.client.send_request('create_index', {
|
|
742
700
|
database: this.database_name,
|
|
@@ -746,11 +704,6 @@ class Collection {
|
|
|
746
704
|
});
|
|
747
705
|
}
|
|
748
706
|
|
|
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
707
|
async drop_index(field) {
|
|
755
708
|
return this.client.send_request('drop_index', {
|
|
756
709
|
database: this.database_name,
|
|
@@ -759,10 +712,6 @@ class Collection {
|
|
|
759
712
|
});
|
|
760
713
|
}
|
|
761
714
|
|
|
762
|
-
/**
|
|
763
|
-
* Gets all indexes for the collection.
|
|
764
|
-
* @returns {Promise<Object>} Indexes information
|
|
765
|
-
*/
|
|
766
715
|
async get_indexes() {
|
|
767
716
|
return this.client.send_request('get_indexes', {
|
|
768
717
|
database: this.database_name,
|
|
@@ -771,19 +720,9 @@ class Collection {
|
|
|
771
720
|
}
|
|
772
721
|
}
|
|
773
722
|
|
|
774
|
-
// NOTE: Add Collection class as static property for Database class access.
|
|
775
723
|
JoystickDBClient.Collection = Collection;
|
|
776
724
|
|
|
777
|
-
/**
|
|
778
|
-
* JoystickDB client factory and utilities.
|
|
779
|
-
* @namespace
|
|
780
|
-
*/
|
|
781
725
|
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
726
|
client: (options) => new JoystickDBClient(options)
|
|
788
727
|
};
|
|
789
728
|
|