@scriptdb/client 1.1.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.d.mts +192 -0
- package/dist/index.d.ts +192 -0
- package/dist/index.js +290 -162
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +837 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +2 -5
package/dist/index.js
CHANGED
|
@@ -1,72 +1,116 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
1
30
|
// src/index.ts
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
ScriptDBClient: () => ScriptDBClient,
|
|
34
|
+
default: () => index_default
|
|
35
|
+
});
|
|
36
|
+
module.exports = __toCommonJS(index_exports);
|
|
37
|
+
var net = __toESM(require("net"));
|
|
38
|
+
var tls = __toESM(require("tls"));
|
|
39
|
+
var import_url = require("url");
|
|
40
|
+
var crypto = __toESM(require("crypto"));
|
|
6
41
|
var noopLogger = {
|
|
7
|
-
debug: () => {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
42
|
+
debug: () => {
|
|
43
|
+
},
|
|
44
|
+
info: () => {
|
|
45
|
+
},
|
|
46
|
+
warn: () => {
|
|
47
|
+
},
|
|
48
|
+
error: () => {
|
|
49
|
+
}
|
|
11
50
|
};
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
frame = "ndjson";
|
|
25
|
-
uri = "";
|
|
26
|
-
protocolName = "";
|
|
27
|
-
username = null;
|
|
28
|
-
password = null;
|
|
29
|
-
host = "";
|
|
30
|
-
port = 0;
|
|
31
|
-
database = null;
|
|
32
|
-
client = null;
|
|
33
|
-
buffer = Buffer.alloc(0);
|
|
34
|
-
_nextId = 1;
|
|
35
|
-
_pending = new Map;
|
|
36
|
-
_maxPending = 0;
|
|
37
|
-
_maxQueue = 0;
|
|
38
|
-
_pendingQueue = [];
|
|
39
|
-
_connected = false;
|
|
40
|
-
_authenticating = false;
|
|
41
|
-
token = null;
|
|
42
|
-
_currentRetries = 0;
|
|
43
|
-
tokenExpiry = null;
|
|
44
|
-
_destroyed = false;
|
|
45
|
-
_reconnectTimer = null;
|
|
46
|
-
signing = null;
|
|
47
|
-
_stringify = JSON.stringify;
|
|
48
|
-
ready = Promise.resolve();
|
|
49
|
-
_resolveReadyFn = null;
|
|
50
|
-
_rejectReadyFn = null;
|
|
51
|
-
_connecting = null;
|
|
52
|
-
_authPendingId = null;
|
|
51
|
+
var ScriptDBClient = class {
|
|
52
|
+
/**
|
|
53
|
+
* Create a new client. Do NOT auto-connect in constructor — call connect()
|
|
54
|
+
* @param uri - Connection URI
|
|
55
|
+
* @param options - Client options
|
|
56
|
+
* @param options.secure - use TLS
|
|
57
|
+
* @param options.logger - { debug, info, warn, error }
|
|
58
|
+
* @param options.requestTimeout - Request timeout in ms
|
|
59
|
+
* @param options.retries - Reconnection retries
|
|
60
|
+
* @param options.retryDelay - Initial retry delay in ms
|
|
61
|
+
* @param options.tlsOptions - Passed to tls.connect when secure
|
|
62
|
+
*/
|
|
53
63
|
constructor(uri, options = {}) {
|
|
54
|
-
|
|
55
|
-
|
|
64
|
+
this.socketTimeout = 0;
|
|
65
|
+
this.maxMessageSize = 0;
|
|
66
|
+
this._mask = () => {
|
|
67
|
+
};
|
|
68
|
+
this._maskArgs = () => [];
|
|
69
|
+
this.logger = {};
|
|
70
|
+
this.secure = true;
|
|
71
|
+
this.requestTimeout = 0;
|
|
72
|
+
this.retries = 0;
|
|
73
|
+
this.retryDelay = 0;
|
|
74
|
+
this.frame = "ndjson";
|
|
75
|
+
this.uri = "";
|
|
76
|
+
this.protocolName = "";
|
|
77
|
+
this.username = null;
|
|
78
|
+
this.password = null;
|
|
79
|
+
this.host = "";
|
|
80
|
+
this.port = 0;
|
|
81
|
+
this.database = null;
|
|
82
|
+
this.client = null;
|
|
83
|
+
this.buffer = Buffer.alloc(0);
|
|
84
|
+
this._nextId = 1;
|
|
85
|
+
this._pending = /* @__PURE__ */ new Map();
|
|
86
|
+
this._maxPending = 0;
|
|
87
|
+
this._maxQueue = 0;
|
|
88
|
+
this._pendingQueue = [];
|
|
89
|
+
this._connected = false;
|
|
90
|
+
this._authenticating = false;
|
|
91
|
+
this.token = null;
|
|
92
|
+
this._currentRetries = 0;
|
|
93
|
+
this.tokenExpiry = null;
|
|
94
|
+
this._destroyed = false;
|
|
95
|
+
this._reconnectTimer = null;
|
|
96
|
+
this.signing = null;
|
|
97
|
+
this._stringify = JSON.stringify;
|
|
98
|
+
this.ready = Promise.resolve();
|
|
99
|
+
this._resolveReadyFn = null;
|
|
100
|
+
this._rejectReadyFn = null;
|
|
101
|
+
this._connecting = null;
|
|
102
|
+
this._authPendingId = null;
|
|
103
|
+
if (!uri || typeof uri !== "string") throw new Error("uri required");
|
|
56
104
|
this.options = Object.assign({}, options);
|
|
57
105
|
this.socketTimeout = Number.isFinite(this.options.socketTimeout) ? this.options.socketTimeout : 0;
|
|
58
106
|
this.maxMessageSize = Number.isFinite(this.options.maxMessageSize) ? this.options.maxMessageSize : 5 * 1024 * 1024;
|
|
59
107
|
this._mask = (obj) => {
|
|
60
108
|
try {
|
|
61
|
-
if (!obj || typeof obj !== "object")
|
|
62
|
-
return obj;
|
|
109
|
+
if (!obj || typeof obj !== "object") return obj;
|
|
63
110
|
const copy = Array.isArray(obj) ? obj.slice() : Object.assign({}, obj);
|
|
64
|
-
if (copy.token)
|
|
65
|
-
|
|
66
|
-
if (copy.password)
|
|
67
|
-
copy.password = "****";
|
|
68
|
-
if (copy.data && copy.data.password)
|
|
69
|
-
copy.data.password = "****";
|
|
111
|
+
if (copy.token) copy.token = "****";
|
|
112
|
+
if (copy.password) copy.password = "****";
|
|
113
|
+
if (copy.data && copy.data.password) copy.data.password = "****";
|
|
70
114
|
return copy;
|
|
71
115
|
} catch (e) {
|
|
72
116
|
return obj;
|
|
@@ -75,10 +119,8 @@ class ScriptDBClient {
|
|
|
75
119
|
const rawLogger = this.options.logger && typeof this.options.logger === "object" ? this.options.logger : noopLogger;
|
|
76
120
|
this._maskArgs = (args) => {
|
|
77
121
|
return args.map((a) => {
|
|
78
|
-
if (!a)
|
|
79
|
-
|
|
80
|
-
if (typeof a === "string")
|
|
81
|
-
return a;
|
|
122
|
+
if (!a) return a;
|
|
123
|
+
if (typeof a === "string") return a;
|
|
82
124
|
return this._mask(a);
|
|
83
125
|
});
|
|
84
126
|
};
|
|
@@ -90,14 +132,16 @@ class ScriptDBClient {
|
|
|
90
132
|
};
|
|
91
133
|
this.secure = typeof this.options.secure === "boolean" ? !!this.options.secure : true;
|
|
92
134
|
if (!this.secure)
|
|
93
|
-
this.logger.warn?.(
|
|
135
|
+
this.logger.warn?.(
|
|
136
|
+
"Warning: connecting in insecure mode (secure=false). This is not recommended."
|
|
137
|
+
);
|
|
94
138
|
this.requestTimeout = Number.isFinite(this.options.requestTimeout) ? this.options.requestTimeout : 0;
|
|
95
139
|
this.retries = Number.isFinite(this.options.retries) ? this.options.retries : 3;
|
|
96
|
-
this.retryDelay = Number.isFinite(this.options.retryDelay) ? this.options.retryDelay :
|
|
140
|
+
this.retryDelay = Number.isFinite(this.options.retryDelay) ? this.options.retryDelay : 1e3;
|
|
97
141
|
this.frame = this.options.frame === "length-prefix" || this.options.preferLengthPrefix ? "length-prefix" : "ndjson";
|
|
98
142
|
let parsed;
|
|
99
143
|
try {
|
|
100
|
-
parsed = new URL(uri);
|
|
144
|
+
parsed = new import_url.URL(uri);
|
|
101
145
|
} catch (e) {
|
|
102
146
|
throw new Error("Invalid uri");
|
|
103
147
|
}
|
|
@@ -109,7 +153,9 @@ class ScriptDBClient {
|
|
|
109
153
|
this.username = (typeof this.options.username === "string" ? this.options.username : parsed.username) || null;
|
|
110
154
|
this.password = (typeof this.options.password === "string" ? this.options.password : parsed.password) || null;
|
|
111
155
|
if (parsed.username && !(typeof this.options.username === "string")) {
|
|
112
|
-
this.logger.warn?.(
|
|
156
|
+
this.logger.warn?.(
|
|
157
|
+
"Credentials found in URI \u2014 consider passing credentials via options instead of embedding in URI"
|
|
158
|
+
);
|
|
113
159
|
}
|
|
114
160
|
try {
|
|
115
161
|
parsed.username = "";
|
|
@@ -124,9 +170,9 @@ class ScriptDBClient {
|
|
|
124
170
|
this.client = null;
|
|
125
171
|
this.buffer = Buffer.alloc(0);
|
|
126
172
|
this._nextId = 1;
|
|
127
|
-
this._pending = new Map;
|
|
173
|
+
this._pending = /* @__PURE__ */ new Map();
|
|
128
174
|
this._maxPending = Number.isFinite(this.options.maxPending) ? this.options.maxPending : 100;
|
|
129
|
-
this._maxQueue = Number.isFinite(this.options.maxQueue) ? this.options.maxQueue :
|
|
175
|
+
this._maxQueue = Number.isFinite(this.options.maxQueue) ? this.options.maxQueue : 1e3;
|
|
130
176
|
this._pendingQueue = [];
|
|
131
177
|
this._connected = false;
|
|
132
178
|
this._authenticating = false;
|
|
@@ -149,7 +195,8 @@ class ScriptDBClient {
|
|
|
149
195
|
if (this._resolveReadyFn) {
|
|
150
196
|
try {
|
|
151
197
|
this._resolveReadyFn(value);
|
|
152
|
-
} catch (e) {
|
|
198
|
+
} catch (e) {
|
|
199
|
+
}
|
|
153
200
|
this._resolveReadyFn = null;
|
|
154
201
|
this._rejectReadyFn = null;
|
|
155
202
|
}
|
|
@@ -162,16 +209,22 @@ class ScriptDBClient {
|
|
|
162
209
|
process.nextTick(() => {
|
|
163
210
|
try {
|
|
164
211
|
rejectFn(err);
|
|
165
|
-
} catch (e) {
|
|
212
|
+
} catch (e) {
|
|
213
|
+
}
|
|
166
214
|
});
|
|
167
215
|
}
|
|
168
216
|
}
|
|
217
|
+
/**
|
|
218
|
+
* Check if connected
|
|
219
|
+
*/
|
|
169
220
|
get connected() {
|
|
170
221
|
return this._connected;
|
|
171
222
|
}
|
|
223
|
+
/**
|
|
224
|
+
* Connect to server and authenticate. Returns a Promise that resolves when authenticated.
|
|
225
|
+
*/
|
|
172
226
|
connect() {
|
|
173
|
-
if (this._connecting)
|
|
174
|
-
return this._connecting;
|
|
227
|
+
if (this._connecting) return this._connecting;
|
|
175
228
|
this._connecting = new Promise((resolve, reject) => {
|
|
176
229
|
const opts = { host: this.host, port: this.port };
|
|
177
230
|
const onConnect = () => {
|
|
@@ -192,7 +245,11 @@ class ScriptDBClient {
|
|
|
192
245
|
try {
|
|
193
246
|
if (this.secure) {
|
|
194
247
|
const connectionOpts = { host: opts.host, port: opts.port };
|
|
195
|
-
const tlsOptions = Object.assign(
|
|
248
|
+
const tlsOptions = Object.assign(
|
|
249
|
+
{},
|
|
250
|
+
this.options.tlsOptions || {},
|
|
251
|
+
connectionOpts
|
|
252
|
+
);
|
|
196
253
|
if (typeof tlsOptions.rejectUnauthorized === "undefined")
|
|
197
254
|
tlsOptions.rejectUnauthorized = true;
|
|
198
255
|
this.client = tls.connect(tlsOptions, onConnect);
|
|
@@ -214,7 +271,10 @@ class ScriptDBClient {
|
|
|
214
271
|
return reject(error);
|
|
215
272
|
}
|
|
216
273
|
const onError = (err) => {
|
|
217
|
-
this.logger?.error?.(
|
|
274
|
+
this.logger?.error?.(
|
|
275
|
+
"Client socket error:",
|
|
276
|
+
err && err.message ? err.message : err
|
|
277
|
+
);
|
|
218
278
|
this._handleDisconnect(err);
|
|
219
279
|
};
|
|
220
280
|
const onClose = (hadError) => {
|
|
@@ -229,7 +289,8 @@ class ScriptDBClient {
|
|
|
229
289
|
this.logger?.warn?.("Socket timeout, destroying connection");
|
|
230
290
|
try {
|
|
231
291
|
this.client?.destroy();
|
|
232
|
-
} catch (e) {
|
|
292
|
+
} catch (e) {
|
|
293
|
+
}
|
|
233
294
|
});
|
|
234
295
|
}
|
|
235
296
|
}).catch((err) => {
|
|
@@ -239,8 +300,7 @@ class ScriptDBClient {
|
|
|
239
300
|
return this._connecting;
|
|
240
301
|
}
|
|
241
302
|
_setupListeners() {
|
|
242
|
-
if (!this.client)
|
|
243
|
-
return;
|
|
303
|
+
if (!this.client) return;
|
|
244
304
|
console.log("ScriptDBClient _setupListeners: called, client exists:", !!this.client);
|
|
245
305
|
this.client.removeAllListeners("data");
|
|
246
306
|
this.buffer = Buffer.alloc(0);
|
|
@@ -258,28 +318,32 @@ class ScriptDBClient {
|
|
|
258
318
|
while (this.buffer.length >= 4) {
|
|
259
319
|
const len = this.buffer.readUInt32BE(0);
|
|
260
320
|
if (len > this.maxMessageSize) {
|
|
261
|
-
this.logger?.error?.(
|
|
321
|
+
this.logger?.error?.(
|
|
322
|
+
"Incoming length-prefixed frame exceeds maxMessageSize \u2014 closing connection"
|
|
323
|
+
);
|
|
262
324
|
try {
|
|
263
325
|
this.client?.destroy();
|
|
264
|
-
} catch (e) {
|
|
326
|
+
} catch (e) {
|
|
327
|
+
}
|
|
265
328
|
return;
|
|
266
329
|
}
|
|
267
|
-
if (this.buffer.length < 4 + len)
|
|
268
|
-
break;
|
|
330
|
+
if (this.buffer.length < 4 + len) break;
|
|
269
331
|
const payload = this.buffer.slice(4, 4 + len);
|
|
270
332
|
this.buffer = this.buffer.slice(4 + len);
|
|
271
333
|
let msg;
|
|
272
334
|
try {
|
|
273
335
|
msg = JSON.parse(payload.toString("utf8"));
|
|
274
336
|
} catch (e) {
|
|
275
|
-
this.logger?.error?.(
|
|
337
|
+
this.logger?.error?.(
|
|
338
|
+
"Invalid JSON frame",
|
|
339
|
+
e && e.message ? e.message : e
|
|
340
|
+
);
|
|
276
341
|
continue;
|
|
277
342
|
}
|
|
278
|
-
if (!msg || typeof msg !== "object")
|
|
279
|
-
continue;
|
|
343
|
+
if (!msg || typeof msg !== "object") continue;
|
|
280
344
|
const validSchema = (typeof msg.id === "undefined" || typeof msg.id === "number") && (typeof msg.action === "string" || typeof msg.action === "undefined") && (typeof msg.command === "string" || typeof msg.command === "undefined") && (typeof msg.message === "string" || typeof msg.message === "undefined") && (typeof msg.data === "object" || typeof msg.data === "undefined" || msg.data === null);
|
|
281
345
|
if (!validSchema) {
|
|
282
|
-
this.logger?.warn?.("Message failed schema validation
|
|
346
|
+
this.logger?.warn?.("Message failed schema validation \u2014 ignoring");
|
|
283
347
|
continue;
|
|
284
348
|
}
|
|
285
349
|
this._handleMessage(msg);
|
|
@@ -303,48 +367,53 @@ class ScriptDBClient {
|
|
|
303
367
|
while ((idx2 = chunk.indexOf(10, start)) !== -1) {
|
|
304
368
|
const lineBuf = chunk.slice(start, idx2);
|
|
305
369
|
start = idx2 + 1;
|
|
306
|
-
if (lineBuf.length === 0)
|
|
307
|
-
continue;
|
|
370
|
+
if (lineBuf.length === 0) continue;
|
|
308
371
|
let msg;
|
|
309
372
|
try {
|
|
310
373
|
msg = JSON.parse(lineBuf.toString("utf8"));
|
|
311
374
|
} catch (e) {
|
|
312
|
-
this.logger?.error?.(
|
|
375
|
+
this.logger?.error?.(
|
|
376
|
+
"Invalid JSON from server",
|
|
377
|
+
e && e.message ? e.message : e
|
|
378
|
+
);
|
|
313
379
|
continue;
|
|
314
380
|
}
|
|
315
|
-
if (!msg || typeof msg !== "object")
|
|
316
|
-
continue;
|
|
381
|
+
if (!msg || typeof msg !== "object") continue;
|
|
317
382
|
const validSchema = (typeof msg.id === "undefined" || typeof msg.id === "number") && (typeof msg.action === "string" || typeof msg.action === "undefined") && (typeof msg.command === "string" || typeof msg.command === "undefined") && (typeof msg.message === "string" || typeof msg.message === "undefined");
|
|
318
|
-
if (!validSchema)
|
|
319
|
-
continue;
|
|
383
|
+
if (!validSchema) continue;
|
|
320
384
|
this._handleMessage(msg);
|
|
321
385
|
}
|
|
322
386
|
return;
|
|
323
387
|
}
|
|
324
388
|
this.buffer = Buffer.concat([this.buffer, chunk]);
|
|
325
389
|
if (this.buffer.length > this.maxMessageSize) {
|
|
326
|
-
this.logger?.error?.(
|
|
390
|
+
this.logger?.error?.(
|
|
391
|
+
"Incoming message exceeds maxMessageSize \u2014 closing connection"
|
|
392
|
+
);
|
|
327
393
|
try {
|
|
328
394
|
this.client?.destroy();
|
|
329
|
-
} catch (e) {
|
|
395
|
+
} catch (e) {
|
|
396
|
+
}
|
|
330
397
|
return;
|
|
331
398
|
}
|
|
332
399
|
let idx;
|
|
333
400
|
while ((idx = this.buffer.indexOf(10)) !== -1) {
|
|
334
401
|
const lineBuf = this.buffer.slice(0, idx);
|
|
335
402
|
this.buffer = this.buffer.slice(idx + 1);
|
|
336
|
-
if (lineBuf.length === 0)
|
|
337
|
-
continue;
|
|
403
|
+
if (lineBuf.length === 0) continue;
|
|
338
404
|
let msg;
|
|
339
405
|
try {
|
|
340
406
|
msg = JSON.parse(lineBuf.toString("utf8"));
|
|
341
407
|
} catch (e) {
|
|
342
|
-
this.logger?.error?.(
|
|
408
|
+
this.logger?.error?.(
|
|
409
|
+
"Invalid JSON from server",
|
|
410
|
+
e && e.message ? e.message : e
|
|
411
|
+
);
|
|
343
412
|
continue;
|
|
344
413
|
}
|
|
345
414
|
const validSchema = (typeof msg.id === "undefined" || typeof msg.id === "number") && (typeof msg.action === "string" || typeof msg.action === "undefined") && (typeof msg.command === "string" || typeof msg.command === "undefined") && (typeof msg.message === "string" || typeof msg.message === "undefined");
|
|
346
415
|
if (!validSchema) {
|
|
347
|
-
this.logger?.warn?.("Message failed schema validation
|
|
416
|
+
this.logger?.warn?.("Message failed schema validation \u2014 ignoring");
|
|
348
417
|
continue;
|
|
349
418
|
}
|
|
350
419
|
this._handleMessage(msg);
|
|
@@ -352,13 +421,19 @@ class ScriptDBClient {
|
|
|
352
421
|
});
|
|
353
422
|
}
|
|
354
423
|
}
|
|
424
|
+
// build the final buffer for sending using current token and signing settings
|
|
355
425
|
_buildFinalBuffer(payloadBase, id) {
|
|
356
|
-
const payloadObj = Object.assign(
|
|
357
|
-
|
|
358
|
-
|
|
426
|
+
const payloadObj = Object.assign(
|
|
427
|
+
{ id, action: payloadBase.action },
|
|
428
|
+
payloadBase.data !== void 0 ? { data: payloadBase.data } : {}
|
|
429
|
+
);
|
|
430
|
+
if (this.token) payloadObj.token = this.token;
|
|
359
431
|
const payloadStr = this._stringify(payloadObj);
|
|
360
432
|
if (this.signing && this.signing.secret) {
|
|
361
|
-
const hmac = crypto.createHmac(
|
|
433
|
+
const hmac = crypto.createHmac(
|
|
434
|
+
this.signing.algorithm || "sha256",
|
|
435
|
+
this.signing.secret
|
|
436
|
+
);
|
|
362
437
|
hmac.update(payloadStr);
|
|
363
438
|
const sig = hmac.digest("hex");
|
|
364
439
|
const envelope = { id, signature: sig, payload: payloadObj };
|
|
@@ -370,8 +445,7 @@ class ScriptDBClient {
|
|
|
370
445
|
body.copy(buf, 4);
|
|
371
446
|
return buf;
|
|
372
447
|
}
|
|
373
|
-
return Buffer.from(envelopeStr +
|
|
374
|
-
`, "utf8");
|
|
448
|
+
return Buffer.from(envelopeStr + "\n", "utf8");
|
|
375
449
|
}
|
|
376
450
|
if (this.frame === "length-prefix") {
|
|
377
451
|
const body = Buffer.from(payloadStr, "utf8");
|
|
@@ -380,8 +454,7 @@ class ScriptDBClient {
|
|
|
380
454
|
body.copy(buf, 4);
|
|
381
455
|
return buf;
|
|
382
456
|
}
|
|
383
|
-
return Buffer.from(payloadStr +
|
|
384
|
-
`, "utf8");
|
|
457
|
+
return Buffer.from(payloadStr + "\n", "utf8");
|
|
385
458
|
}
|
|
386
459
|
_handleMessage(msg) {
|
|
387
460
|
console.log("ScriptDBClient _handleMessage:", JSON.stringify(msg));
|
|
@@ -395,8 +468,7 @@ class ScriptDBClient {
|
|
|
395
468
|
return;
|
|
396
469
|
}
|
|
397
470
|
const { resolve, reject, timer } = pending;
|
|
398
|
-
if (timer)
|
|
399
|
-
clearTimeout(timer);
|
|
471
|
+
if (timer) clearTimeout(timer);
|
|
400
472
|
this._pending.delete(msg.id);
|
|
401
473
|
this._processQueue();
|
|
402
474
|
if (msg.action === "login" || msg.command === "login") {
|
|
@@ -412,7 +484,8 @@ class ScriptDBClient {
|
|
|
412
484
|
const errorMsg = msg.data || "Authentication failed";
|
|
413
485
|
try {
|
|
414
486
|
this.client?.end();
|
|
415
|
-
} catch (e) {
|
|
487
|
+
} catch (e) {
|
|
488
|
+
}
|
|
416
489
|
return reject(new Error(typeof errorMsg === "string" ? errorMsg : "Authentication failed"));
|
|
417
490
|
}
|
|
418
491
|
}
|
|
@@ -456,27 +529,22 @@ class ScriptDBClient {
|
|
|
456
529
|
}
|
|
457
530
|
this._pending.set(id, {
|
|
458
531
|
resolve: (data) => {
|
|
459
|
-
if (timer)
|
|
460
|
-
clearTimeout(timer);
|
|
532
|
+
if (timer) clearTimeout(timer);
|
|
461
533
|
this._authenticating = false;
|
|
462
|
-
if (data && data.token)
|
|
463
|
-
this.token = data.token;
|
|
534
|
+
if (data && data.token) this.token = data.token;
|
|
464
535
|
resolve(data);
|
|
465
536
|
},
|
|
466
537
|
reject: (err) => {
|
|
467
|
-
if (timer)
|
|
468
|
-
clearTimeout(timer);
|
|
538
|
+
if (timer) clearTimeout(timer);
|
|
469
539
|
this._authenticating = false;
|
|
470
540
|
reject(err);
|
|
471
541
|
},
|
|
472
542
|
timer
|
|
473
543
|
});
|
|
474
544
|
try {
|
|
475
|
-
const buf = Buffer.from(JSON.stringify(payload) +
|
|
476
|
-
`, "utf8");
|
|
545
|
+
const buf = Buffer.from(JSON.stringify(payload) + "\n", "utf8");
|
|
477
546
|
this._write(buf).catch((err) => {
|
|
478
|
-
if (timer)
|
|
479
|
-
clearTimeout(timer);
|
|
547
|
+
if (timer) clearTimeout(timer);
|
|
480
548
|
this._pending.delete(id);
|
|
481
549
|
this._authenticating = false;
|
|
482
550
|
reject(err);
|
|
@@ -498,18 +566,18 @@ class ScriptDBClient {
|
|
|
498
566
|
throw err;
|
|
499
567
|
});
|
|
500
568
|
}
|
|
569
|
+
// internal write helper that respects backpressure
|
|
501
570
|
_write(buf) {
|
|
502
571
|
return new Promise((resolve, reject) => {
|
|
503
572
|
if (!this.client || !this._connected)
|
|
504
573
|
return reject(new Error("Not connected"));
|
|
505
574
|
try {
|
|
506
575
|
const ok = this.client.write(buf, (err) => {
|
|
507
|
-
if (err)
|
|
508
|
-
|
|
509
|
-
resolve(undefined);
|
|
576
|
+
if (err) return reject(err);
|
|
577
|
+
resolve(void 0);
|
|
510
578
|
});
|
|
511
579
|
if (!ok) {
|
|
512
|
-
this.client.once("drain", () => resolve(
|
|
580
|
+
this.client.once("drain", () => resolve(void 0));
|
|
513
581
|
}
|
|
514
582
|
} catch (e) {
|
|
515
583
|
reject(e);
|
|
@@ -526,7 +594,8 @@ class ScriptDBClient {
|
|
|
526
594
|
clearTimeout(timer);
|
|
527
595
|
try {
|
|
528
596
|
reject(new Error("Token expired and refresh failed"));
|
|
529
|
-
} catch (e) {
|
|
597
|
+
} catch (e) {
|
|
598
|
+
}
|
|
530
599
|
continue;
|
|
531
600
|
}
|
|
532
601
|
}
|
|
@@ -537,7 +606,8 @@ class ScriptDBClient {
|
|
|
537
606
|
clearTimeout(timer);
|
|
538
607
|
try {
|
|
539
608
|
reject(e);
|
|
540
|
-
} catch (er) {
|
|
609
|
+
} catch (er) {
|
|
610
|
+
}
|
|
541
611
|
continue;
|
|
542
612
|
}
|
|
543
613
|
this._pending.set(id, { resolve, reject, timer });
|
|
@@ -548,24 +618,29 @@ class ScriptDBClient {
|
|
|
548
618
|
this._pending.delete(id);
|
|
549
619
|
try {
|
|
550
620
|
reject(e);
|
|
551
|
-
} catch (er) {
|
|
621
|
+
} catch (er) {
|
|
622
|
+
}
|
|
552
623
|
}
|
|
553
624
|
}
|
|
554
625
|
}
|
|
626
|
+
/**
|
|
627
|
+
* Execute a command. Supports concurrent requests via request id mapping.
|
|
628
|
+
* Returns a Promise resolved with response.data or rejected on ERROR/timeout.
|
|
629
|
+
*/
|
|
555
630
|
async execute(payload) {
|
|
556
631
|
try {
|
|
557
632
|
await this.ready;
|
|
558
633
|
} catch (err) {
|
|
559
|
-
throw new Error(
|
|
634
|
+
throw new Error(
|
|
635
|
+
"Not authenticated: " + (err && err.message ? err.message : err)
|
|
636
|
+
);
|
|
560
637
|
}
|
|
561
|
-
if (!this.token)
|
|
562
|
-
throw new Error("Not authenticated");
|
|
638
|
+
if (!this.token) throw new Error("Not authenticated");
|
|
563
639
|
const id = this._nextId++;
|
|
564
640
|
const payloadBase = { action: payload.action, data: payload.data };
|
|
565
641
|
if (this.tokenExpiry && Date.now() >= this.tokenExpiry) {
|
|
566
642
|
const refreshed = await this._maybeRefreshToken();
|
|
567
|
-
if (!refreshed)
|
|
568
|
-
throw new Error("Token expired");
|
|
643
|
+
if (!refreshed) throw new Error("Token expired");
|
|
569
644
|
}
|
|
570
645
|
return new Promise((resolve, reject) => {
|
|
571
646
|
let timer = null;
|
|
@@ -579,12 +654,12 @@ class ScriptDBClient {
|
|
|
579
654
|
}
|
|
580
655
|
if (this._pending.size >= this._maxPending) {
|
|
581
656
|
if (this._pendingQueue.length >= this._maxQueue) {
|
|
582
|
-
if (timer)
|
|
583
|
-
clearTimeout(timer);
|
|
657
|
+
if (timer) clearTimeout(timer);
|
|
584
658
|
return reject(new Error("Pending queue full"));
|
|
585
659
|
}
|
|
586
660
|
this._pendingQueue.push({ payloadBase, id, resolve, reject, timer });
|
|
587
|
-
this._processQueue().catch(() => {
|
|
661
|
+
this._processQueue().catch(() => {
|
|
662
|
+
});
|
|
588
663
|
return;
|
|
589
664
|
}
|
|
590
665
|
let finalBuf;
|
|
@@ -596,13 +671,16 @@ class ScriptDBClient {
|
|
|
596
671
|
}
|
|
597
672
|
this._pending.set(id, { resolve, reject, timer });
|
|
598
673
|
this._write(finalBuf).catch((e) => {
|
|
599
|
-
if (timer)
|
|
600
|
-
clearTimeout(timer);
|
|
674
|
+
if (timer) clearTimeout(timer);
|
|
601
675
|
this._pending.delete(id);
|
|
602
676
|
reject(e);
|
|
603
677
|
});
|
|
604
678
|
});
|
|
605
679
|
}
|
|
680
|
+
/**
|
|
681
|
+
* Attempt to refresh token using provided tokenRefresh option if token expired.
|
|
682
|
+
* options.tokenRefresh should be async function that returns { token, expiresAt }
|
|
683
|
+
*/
|
|
606
684
|
async _maybeRefreshToken() {
|
|
607
685
|
if (!this.options.tokenRefresh || typeof this.options.tokenRefresh !== "function")
|
|
608
686
|
return false;
|
|
@@ -610,12 +688,14 @@ class ScriptDBClient {
|
|
|
610
688
|
const res = await this.options.tokenRefresh();
|
|
611
689
|
if (res && res.token) {
|
|
612
690
|
this.token = res.token;
|
|
613
|
-
if (res.expiresAt)
|
|
614
|
-
this.tokenExpiry = res.expiresAt;
|
|
691
|
+
if (res.expiresAt) this.tokenExpiry = res.expiresAt;
|
|
615
692
|
return true;
|
|
616
693
|
}
|
|
617
694
|
} catch (e) {
|
|
618
|
-
this.logger?.error?.(
|
|
695
|
+
this.logger?.error?.(
|
|
696
|
+
"Token refresh failed",
|
|
697
|
+
e && e.message ? e.message : String(e)
|
|
698
|
+
);
|
|
619
699
|
}
|
|
620
700
|
return false;
|
|
621
701
|
}
|
|
@@ -627,18 +707,18 @@ class ScriptDBClient {
|
|
|
627
707
|
}
|
|
628
708
|
this._rejectReady(new Error("Client destroyed"));
|
|
629
709
|
this._pending.forEach((pending, id) => {
|
|
630
|
-
if (pending.timer)
|
|
631
|
-
clearTimeout(pending.timer);
|
|
710
|
+
if (pending.timer) clearTimeout(pending.timer);
|
|
632
711
|
try {
|
|
633
712
|
pending.reject(new Error("Client destroyed"));
|
|
634
|
-
} catch (e) {
|
|
713
|
+
} catch (e) {
|
|
714
|
+
}
|
|
635
715
|
this._pending.delete(id);
|
|
636
716
|
});
|
|
637
717
|
this._pendingQueue = [];
|
|
638
718
|
try {
|
|
639
|
-
if (this.client)
|
|
640
|
-
|
|
641
|
-
}
|
|
719
|
+
if (this.client) this.client.destroy();
|
|
720
|
+
} catch (e) {
|
|
721
|
+
}
|
|
642
722
|
this.client = null;
|
|
643
723
|
}
|
|
644
724
|
_handleDisconnect(err) {
|
|
@@ -647,13 +727,13 @@ class ScriptDBClient {
|
|
|
647
727
|
}
|
|
648
728
|
const wasAuthenticating = this._authenticating;
|
|
649
729
|
this._pending.forEach((pending, id) => {
|
|
650
|
-
if (pending.timer)
|
|
651
|
-
clearTimeout(pending.timer);
|
|
730
|
+
if (pending.timer) clearTimeout(pending.timer);
|
|
652
731
|
const errorMsg = wasAuthenticating ? "Authentication failed - credentials may be required" : err && err.message ? err.message : "Disconnected";
|
|
653
732
|
process.nextTick(() => {
|
|
654
733
|
try {
|
|
655
734
|
pending.reject(new Error(errorMsg));
|
|
656
|
-
} catch (e) {
|
|
735
|
+
} catch (e) {
|
|
736
|
+
}
|
|
657
737
|
});
|
|
658
738
|
this._pending.delete(id);
|
|
659
739
|
});
|
|
@@ -664,16 +744,22 @@ class ScriptDBClient {
|
|
|
664
744
|
const rejectErr = err || new Error("Authentication failed and no retries configured");
|
|
665
745
|
try {
|
|
666
746
|
this._rejectReady(rejectErr);
|
|
667
|
-
} catch (e) {
|
|
747
|
+
} catch (e) {
|
|
748
|
+
}
|
|
668
749
|
return;
|
|
669
750
|
}
|
|
670
751
|
if (this._currentRetries < this.retries) {
|
|
671
752
|
this._createReady();
|
|
672
|
-
const base = Math.min(
|
|
673
|
-
|
|
753
|
+
const base = Math.min(
|
|
754
|
+
this.retryDelay * Math.pow(2, this._currentRetries),
|
|
755
|
+
3e4
|
|
756
|
+
);
|
|
757
|
+
const jitter = Math.floor(Math.random() * Math.min(1e3, base));
|
|
674
758
|
const delay = base + jitter;
|
|
675
759
|
this._currentRetries += 1;
|
|
676
|
-
this.logger?.info?.(
|
|
760
|
+
this.logger?.info?.(
|
|
761
|
+
"Attempting reconnect in " + delay + "ms (attempt " + this._currentRetries + ")"
|
|
762
|
+
);
|
|
677
763
|
this.token = null;
|
|
678
764
|
setTimeout(() => {
|
|
679
765
|
this.connect().catch((e) => {
|
|
@@ -683,62 +769,104 @@ class ScriptDBClient {
|
|
|
683
769
|
} else {
|
|
684
770
|
try {
|
|
685
771
|
this._rejectReady(err || new Error("Disconnected and no retries left"));
|
|
686
|
-
} catch (e) {
|
|
772
|
+
} catch (e) {
|
|
773
|
+
}
|
|
687
774
|
}
|
|
688
775
|
}
|
|
689
776
|
close() {
|
|
690
|
-
if (!this.client)
|
|
691
|
-
return;
|
|
777
|
+
if (!this.client) return;
|
|
692
778
|
try {
|
|
693
779
|
this.client.removeAllListeners("data");
|
|
694
780
|
this.client.removeAllListeners("error");
|
|
695
781
|
this.client.removeAllListeners("close");
|
|
696
782
|
this.client.end();
|
|
697
|
-
} catch (e) {
|
|
783
|
+
} catch (e) {
|
|
784
|
+
}
|
|
698
785
|
this.client = null;
|
|
699
786
|
}
|
|
787
|
+
/**
|
|
788
|
+
* Disconnect (alias for close)
|
|
789
|
+
*/
|
|
700
790
|
disconnect() {
|
|
701
791
|
this.close();
|
|
702
792
|
}
|
|
793
|
+
/**
|
|
794
|
+
* Send request helper (for public API methods)
|
|
795
|
+
*/
|
|
703
796
|
async sendRequest(action, data = {}) {
|
|
704
797
|
return this.execute({ action, data });
|
|
705
798
|
}
|
|
799
|
+
// ========== Public API Methods ==========
|
|
800
|
+
/**
|
|
801
|
+
* Login with username and password
|
|
802
|
+
*/
|
|
706
803
|
async login(username, password) {
|
|
707
804
|
return this.sendRequest("login", { username, password });
|
|
708
805
|
}
|
|
806
|
+
/**
|
|
807
|
+
* Logout from server
|
|
808
|
+
*/
|
|
709
809
|
async logout() {
|
|
710
810
|
return this.sendRequest("logout", {});
|
|
711
811
|
}
|
|
812
|
+
/**
|
|
813
|
+
* List all databases
|
|
814
|
+
*/
|
|
712
815
|
async listDatabases() {
|
|
713
816
|
return this.sendRequest("list-dbs", {});
|
|
714
817
|
}
|
|
818
|
+
/**
|
|
819
|
+
* Create a new database
|
|
820
|
+
*/
|
|
715
821
|
async createDatabase(name) {
|
|
716
822
|
return this.sendRequest("create-db", { databaseName: name });
|
|
717
823
|
}
|
|
824
|
+
/**
|
|
825
|
+
* Remove a database
|
|
826
|
+
*/
|
|
718
827
|
async removeDatabase(name) {
|
|
719
828
|
return this.sendRequest("remove-db", { databaseName: name });
|
|
720
829
|
}
|
|
830
|
+
/**
|
|
831
|
+
* Rename a database
|
|
832
|
+
*/
|
|
721
833
|
async renameDatabase(oldName, newName) {
|
|
722
834
|
return this.sendRequest("rename-db", { databaseName: oldName, newName });
|
|
723
835
|
}
|
|
836
|
+
/**
|
|
837
|
+
* Execute code in a database
|
|
838
|
+
*/
|
|
724
839
|
async run(code, databaseName) {
|
|
725
840
|
return this.sendRequest("script-code", { code, databaseName });
|
|
726
841
|
}
|
|
842
|
+
/**
|
|
843
|
+
* Save a database to disk
|
|
844
|
+
*/
|
|
727
845
|
async saveDatabase(databaseName) {
|
|
728
846
|
return this.sendRequest("save-db", { databaseName });
|
|
729
847
|
}
|
|
848
|
+
/**
|
|
849
|
+
* Update database metadata
|
|
850
|
+
*/
|
|
730
851
|
async updateDatabase(databaseName, data) {
|
|
731
852
|
return this.sendRequest("update-db", { databaseName, ...data });
|
|
732
853
|
}
|
|
854
|
+
/**
|
|
855
|
+
* Get server information
|
|
856
|
+
*/
|
|
733
857
|
async getInfo() {
|
|
734
858
|
return this.sendRequest("get-info", {});
|
|
735
859
|
}
|
|
860
|
+
/**
|
|
861
|
+
* Execute shell command on server
|
|
862
|
+
*/
|
|
736
863
|
async executeShell(command) {
|
|
737
864
|
return this.sendRequest("shell-command", { command });
|
|
738
865
|
}
|
|
739
|
-
}
|
|
740
|
-
var src_default = ScriptDBClient;
|
|
741
|
-
export {
|
|
742
|
-
src_default as default,
|
|
743
|
-
ScriptDBClient
|
|
744
866
|
};
|
|
867
|
+
var index_default = ScriptDBClient;
|
|
868
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
869
|
+
0 && (module.exports = {
|
|
870
|
+
ScriptDBClient
|
|
871
|
+
});
|
|
872
|
+
//# sourceMappingURL=index.js.map
|