ueberdb2 2.1.0 → 2.2.2
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/.github/workflows/npmpublish.yml +1 -0
- package/CHANGELOG.md +44 -0
- package/README.md +70 -92
- package/databases/couch_db.js +85 -144
- package/index.js +64 -84
- package/lib/AbstractDatabase.js +5 -0
- package/lib/CacheAndBufferLayer.js +95 -37
- package/lib/logging.js +29 -0
- package/package.json +9 -9
- package/test/lib/databases.js +3 -2
- package/test/test.js +92 -75
- package/test/test_bulk.js +1 -3
- package/test/test_findKeys.js +43 -0
- package/test/test_flush.js +61 -0
- package/test/test_metrics.js +1 -5
- package/test/test_postgres.js +3 -4
- package/test/test_setSub.js +13 -0
- package/test/test_tojson.js +1 -4
package/index.js
CHANGED
|
@@ -18,24 +18,12 @@
|
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
20
|
const cacheAndBufferLayer = require('./lib/CacheAndBufferLayer');
|
|
21
|
+
const logging = require('./lib/logging');
|
|
21
22
|
const util = require('util');
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
const logLevelsUsed = ['debug', 'error'];
|
|
27
|
-
logger = Object.create(logger || {});
|
|
28
|
-
for (const level of logLevelsUsed) {
|
|
29
|
-
const enabledFnName = `is${level.charAt(0).toUpperCase() + level.slice(1)}Enabled`;
|
|
30
|
-
if (typeof logger[level] !== 'function') {
|
|
31
|
-
logger[level] = () => {};
|
|
32
|
-
logger[enabledFnName] = () => false;
|
|
33
|
-
} else if (typeof logger[enabledFnName] !== 'function') {
|
|
34
|
-
logger[enabledFnName] = () => true;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
return logger;
|
|
38
|
-
};
|
|
24
|
+
const cbDb = {};
|
|
25
|
+
const fns = ['close', 'findKeys', 'flush', 'get', 'getSub', 'init', 'remove', 'set', 'setSub'];
|
|
26
|
+
for (const fn of fns) cbDb[fn] = util.callbackify(cacheAndBufferLayer.Database.prototype[fn]);
|
|
39
27
|
|
|
40
28
|
const makeDoneCallback = (callback, deprecated) => (err) => {
|
|
41
29
|
if (callback) callback(err);
|
|
@@ -62,8 +50,9 @@ exports.Database = class {
|
|
|
62
50
|
this.dbModule = require(`./databases/${type}_db`);
|
|
63
51
|
this.dbSettings = dbSettings;
|
|
64
52
|
this.wrapperSettings = wrapperSettings;
|
|
65
|
-
this.logger = normalizeLogger(logger);
|
|
53
|
+
this.logger = logging.normalizeLogger(logger);
|
|
66
54
|
const db = new this.dbModule.Database(this.dbSettings);
|
|
55
|
+
db.logger = this.logger;
|
|
67
56
|
this.db = new cacheAndBufferLayer.Database(db, this.wrapperSettings, this.logger);
|
|
68
57
|
|
|
69
58
|
// Expose the cache wrapper's metrics to the user. See lib/CacheAndBufferLayer.js for details.
|
|
@@ -73,12 +62,12 @@ exports.Database = class {
|
|
|
73
62
|
this.metrics = this.db.metrics;
|
|
74
63
|
}
|
|
75
64
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
65
|
+
/**
|
|
66
|
+
* @param callback - Deprecated. Node-style callback. If null, a Promise is returned.
|
|
67
|
+
*/
|
|
68
|
+
init(callback = null) {
|
|
69
|
+
if (callback != null) return cbDb.init.call(this.db, callback);
|
|
70
|
+
return this.db.init();
|
|
82
71
|
}
|
|
83
72
|
|
|
84
73
|
/**
|
|
@@ -87,104 +76,95 @@ exports.Database = class {
|
|
|
87
76
|
|
|
88
77
|
/**
|
|
89
78
|
* Deprecated synonym of flush().
|
|
79
|
+
*
|
|
80
|
+
* @param callback - Deprecated. Node-style callback. If null, a Promise is returned.
|
|
90
81
|
*/
|
|
91
|
-
doShutdown(callback) {
|
|
92
|
-
this.flush(callback);
|
|
82
|
+
doShutdown(callback = null) {
|
|
83
|
+
return this.flush(callback);
|
|
93
84
|
}
|
|
94
85
|
|
|
95
86
|
/**
|
|
96
87
|
* Writes any unsaved changes to the underlying database.
|
|
88
|
+
*
|
|
89
|
+
* @param callback - Deprecated. Node-style callback. If null, a Promise is returned.
|
|
97
90
|
*/
|
|
98
|
-
flush(callback) {
|
|
99
|
-
|
|
91
|
+
flush(callback = null) {
|
|
92
|
+
if (callback != null) return cbDb.flush.call(this.db, callback);
|
|
93
|
+
return this.db.flush();
|
|
100
94
|
}
|
|
101
95
|
|
|
102
|
-
|
|
103
|
-
|
|
96
|
+
/**
|
|
97
|
+
* @param callback - Deprecated. Node-style callback. If null, a Promise is returned.
|
|
98
|
+
*/
|
|
99
|
+
get(key, callback = null) {
|
|
100
|
+
if (callback != null) return cbDb.get.call(this.db, key, callback);
|
|
101
|
+
return this.db.get(key);
|
|
104
102
|
}
|
|
105
103
|
|
|
106
|
-
|
|
107
|
-
|
|
104
|
+
/**
|
|
105
|
+
* @param callback - Deprecated. Node-style callback. If null, a Promise is returned.
|
|
106
|
+
*/
|
|
107
|
+
findKeys(key, notKey, callback = null) {
|
|
108
|
+
if (callback != null) return cbDb.findKeys.call(this.db, key, notKey, callback);
|
|
109
|
+
return this.db.findKeys(key, notKey);
|
|
108
110
|
}
|
|
109
111
|
|
|
110
112
|
/**
|
|
111
113
|
* Removes an entry from the database if present.
|
|
112
114
|
*
|
|
113
|
-
* @param cb Called when the write has been committed to the
|
|
114
|
-
*
|
|
115
|
+
* @param cb Deprecated. Node-style callback. Called when the write has been committed to the
|
|
116
|
+
* underlying database driver. If null, a Promise is returned.
|
|
117
|
+
* @param deprecated Deprecated callback that is called just after cb. Ignored if cb is null.
|
|
115
118
|
*/
|
|
116
|
-
remove(key, cb, deprecated = null) {
|
|
117
|
-
|
|
119
|
+
remove(key, cb = null, deprecated = null) {
|
|
120
|
+
if (cb != null) return cbDb.remove.call(this.db, key, makeDoneCallback(cb, deprecated));
|
|
121
|
+
return this.db.remove(key);
|
|
118
122
|
}
|
|
119
123
|
|
|
120
124
|
/**
|
|
121
125
|
* Adds or changes the value of an entry.
|
|
122
126
|
*
|
|
123
|
-
* @param cb Called when the write has been committed to the
|
|
124
|
-
*
|
|
127
|
+
* @param cb Deprecated. Node-style callback. Called when the write has been committed to the
|
|
128
|
+
* underlying database driver. If null, a Promise is returned.
|
|
129
|
+
* @param deprecated Deprecated callback that is called just after cb. Ignored if cb is null.
|
|
125
130
|
*/
|
|
126
|
-
set(key, value, cb, deprecated = null) {
|
|
127
|
-
|
|
128
|
-
|
|
131
|
+
set(key, value, cb = null, deprecated = null) {
|
|
132
|
+
if (cb != null) return cbDb.set.call(this.db, key, value, makeDoneCallback(cb, deprecated));
|
|
133
|
+
return this.db.set(key, value);
|
|
129
134
|
}
|
|
130
135
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
136
|
+
/**
|
|
137
|
+
* @param callback - Deprecated. Node-style callback. If null, a Promise is returned.
|
|
138
|
+
*/
|
|
139
|
+
getSub(key, sub, callback = null) {
|
|
140
|
+
if (callback != null) return cbDb.getSub.call(this.db, key, sub, callback);
|
|
141
|
+
return this.db.getSub(key, sub);
|
|
134
142
|
}
|
|
135
143
|
|
|
136
144
|
/**
|
|
137
145
|
* Adds or changes a subvalue of an entry.
|
|
138
146
|
*
|
|
139
|
-
* @param cb Called when the write has been committed to the
|
|
140
|
-
*
|
|
147
|
+
* @param cb Deprecated. Node-style callback. Called when the write has been committed to the
|
|
148
|
+
* underlying database driver. If null, a Promise is returned.
|
|
149
|
+
* @param deprecated Deprecated callback that is called just after cb. Ignored if cb is null.
|
|
141
150
|
*/
|
|
142
|
-
setSub(key, sub, value, cb, deprecated = null) {
|
|
143
|
-
|
|
144
|
-
|
|
151
|
+
setSub(key, sub, value, cb = null, deprecated = null) {
|
|
152
|
+
if (cb != null) {
|
|
153
|
+
return cbDb.setSub.call(this.db, key, sub, value, makeDoneCallback(cb, deprecated));
|
|
154
|
+
}
|
|
155
|
+
return this.db.setSub(key, sub, value);
|
|
145
156
|
}
|
|
146
157
|
|
|
147
158
|
/**
|
|
148
159
|
* Flushes unwritten changes then closes the connection to the underlying database. After this
|
|
149
160
|
* returns, any future call to a method on this object may result in an error.
|
|
161
|
+
*
|
|
162
|
+
* @param callback - Deprecated. Node-style callback. If null, a Promise is returned.
|
|
150
163
|
*/
|
|
151
|
-
close(callback) {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
const clone = (obj, key = '') => {
|
|
157
|
-
// Handle the 3 simple types, and null or undefined
|
|
158
|
-
if (null == obj || 'object' !== typeof obj) return obj;
|
|
159
|
-
|
|
160
|
-
if (typeof obj.toJSON === 'function') return clone(obj.toJSON(key));
|
|
161
|
-
|
|
162
|
-
// Handle Date
|
|
163
|
-
if (obj instanceof Date) {
|
|
164
|
-
const copy = new Date();
|
|
165
|
-
copy.setTime(obj.getTime());
|
|
166
|
-
return copy;
|
|
164
|
+
close(callback = null) {
|
|
165
|
+
if (callback != null) return cbDb.close.call(this.db, callback);
|
|
166
|
+
return this.db.close();
|
|
167
167
|
}
|
|
168
|
-
|
|
169
|
-
// Handle Array
|
|
170
|
-
if (obj instanceof Array) {
|
|
171
|
-
const copy = [];
|
|
172
|
-
for (let i = 0, len = obj.length; i < len; ++i) {
|
|
173
|
-
copy[i] = clone(obj[i], String(i));
|
|
174
|
-
}
|
|
175
|
-
return copy;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// Handle Object
|
|
179
|
-
if (obj instanceof Object) {
|
|
180
|
-
const copy = {};
|
|
181
|
-
for (const attr in obj) {
|
|
182
|
-
if (Object.prototype.hasOwnProperty.call(obj, attr)) copy[attr] = clone(obj[attr], attr);
|
|
183
|
-
}
|
|
184
|
-
return copy;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
throw new Error("Unable to copy obj! Its type isn't supported.");
|
|
188
168
|
};
|
|
189
169
|
|
|
190
170
|
/**
|
package/lib/AbstractDatabase.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const logging = require('./logging');
|
|
4
|
+
|
|
5
|
+
const nullLogger = logging.normalizeLogger(null);
|
|
6
|
+
|
|
3
7
|
module.exports = class AbstractDatabase {
|
|
4
8
|
constructor() {
|
|
5
9
|
if (new.target === module.exports) {
|
|
@@ -8,6 +12,7 @@ module.exports = class AbstractDatabase {
|
|
|
8
12
|
for (const fn of ['init', 'close', 'get', 'findKeys', 'remove', 'set']) {
|
|
9
13
|
if (typeof this[fn] !== 'function') throw new TypeError(`method ${fn} not defined`);
|
|
10
14
|
}
|
|
15
|
+
this.logger = nullLogger;
|
|
11
16
|
}
|
|
12
17
|
|
|
13
18
|
/**
|
|
@@ -165,6 +165,11 @@ exports.Database = class {
|
|
|
165
165
|
// underlying database and we are awaiting commit.
|
|
166
166
|
this.buffer = new LRU(this.settings.cache, (k, v) => !v.dirty && !v.writingInProgress);
|
|
167
167
|
|
|
168
|
+
// Either null if flushing is currently allowed, or a Promise that will resolve when it is OK to
|
|
169
|
+
// start flushing. The Promise has a `count` property that tracks the number of operations that
|
|
170
|
+
// are currently preventing flush() from running.
|
|
171
|
+
this._flushPaused = null;
|
|
172
|
+
|
|
168
173
|
// Maps database key to a Promise that is resolved when the record is unlocked.
|
|
169
174
|
this._locks = new Map();
|
|
170
175
|
|
|
@@ -245,6 +250,33 @@ exports.Database = class {
|
|
|
245
250
|
this._locks.delete(key);
|
|
246
251
|
}
|
|
247
252
|
|
|
253
|
+
// Block flush() until _resumeFlush() is called. This is needed so that a call to flush() after a
|
|
254
|
+
// write (set(), setSub(), or remove() call) in the same ECMAScript macro- or microtask will see
|
|
255
|
+
// the enqueued write and flush it.
|
|
256
|
+
//
|
|
257
|
+
// An alternative would be to change flush() to schedule its actions in a future microtask after
|
|
258
|
+
// the write has been queued in the buffer, but:
|
|
259
|
+
//
|
|
260
|
+
// * That would be fragile: Every use of await moves the subsequent processing to a new
|
|
261
|
+
// microtask, so flush() would need to do a number of `await Promise.resolve();` calls equal
|
|
262
|
+
// to the number of awaits before a write is actually buffered.
|
|
263
|
+
//
|
|
264
|
+
// * It won't work for setSub() because it must wait for a read to complete before it buffers
|
|
265
|
+
// the write.
|
|
266
|
+
_pauseFlush() {
|
|
267
|
+
if (this._flushPaused == null) {
|
|
268
|
+
this._flushPaused = new SelfContainedPromise();
|
|
269
|
+
this._flushPaused.count = 0;
|
|
270
|
+
}
|
|
271
|
+
++this._flushPaused.count;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
_resumeFlush() {
|
|
275
|
+
if (--this._flushPaused.count > 0) return;
|
|
276
|
+
this._flushPaused.done();
|
|
277
|
+
this._flushPaused = null;
|
|
278
|
+
}
|
|
279
|
+
|
|
248
280
|
/**
|
|
249
281
|
* wraps the init function of the original DB
|
|
250
282
|
*/
|
|
@@ -266,12 +298,14 @@ exports.Database = class {
|
|
|
266
298
|
* Gets the value trough the wrapper.
|
|
267
299
|
*/
|
|
268
300
|
async get(key) {
|
|
301
|
+
let v;
|
|
269
302
|
await this._lock(key);
|
|
270
303
|
try {
|
|
271
|
-
|
|
304
|
+
v = await this._getLocked(key);
|
|
272
305
|
} finally {
|
|
273
306
|
this._unlock(key);
|
|
274
307
|
}
|
|
308
|
+
return clone(v);
|
|
275
309
|
}
|
|
276
310
|
|
|
277
311
|
async _getLocked(key) {
|
|
@@ -334,12 +368,13 @@ exports.Database = class {
|
|
|
334
368
|
* returns the key entries via callback.
|
|
335
369
|
*/
|
|
336
370
|
async findKeys(key, notKey) {
|
|
337
|
-
|
|
371
|
+
await this.flush();
|
|
338
372
|
const keyValues = await this.wrappedDB.findKeys(key, notKey);
|
|
339
373
|
if (this.logger.isDebugEnabled()) {
|
|
340
|
-
this.logger.debug(
|
|
374
|
+
this.logger.debug(
|
|
375
|
+
`GET - ${key}-${notKey} - ${JSON.stringify(keyValues)} - from database `);
|
|
341
376
|
}
|
|
342
|
-
return keyValues;
|
|
377
|
+
return clone(keyValues);
|
|
343
378
|
}
|
|
344
379
|
|
|
345
380
|
/**
|
|
@@ -354,9 +389,19 @@ exports.Database = class {
|
|
|
354
389
|
* Sets the value trough the wrapper
|
|
355
390
|
*/
|
|
356
391
|
async set(key, value) {
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
this.
|
|
392
|
+
value = clone(value);
|
|
393
|
+
let p;
|
|
394
|
+
this._pauseFlush();
|
|
395
|
+
try {
|
|
396
|
+
await this._lock(key);
|
|
397
|
+
try {
|
|
398
|
+
p = this._setLocked(key, value);
|
|
399
|
+
} finally {
|
|
400
|
+
this._unlock(key);
|
|
401
|
+
}
|
|
402
|
+
} finally {
|
|
403
|
+
this._resumeFlush();
|
|
404
|
+
}
|
|
360
405
|
await p;
|
|
361
406
|
}
|
|
362
407
|
|
|
@@ -406,44 +451,53 @@ exports.Database = class {
|
|
|
406
451
|
* Sets a subvalue
|
|
407
452
|
*/
|
|
408
453
|
async setSub(key, sub, value) {
|
|
454
|
+
value = clone(value);
|
|
409
455
|
if (this.logger.isDebugEnabled()) {
|
|
410
456
|
this.logger.debug(`SETSUB - ${key}${JSON.stringify(sub)} - ${JSON.stringify(value)}`);
|
|
411
457
|
}
|
|
412
458
|
let p;
|
|
413
|
-
|
|
459
|
+
this._pauseFlush();
|
|
414
460
|
try {
|
|
415
|
-
|
|
461
|
+
await this._lock(key);
|
|
416
462
|
try {
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
463
|
+
let base;
|
|
464
|
+
try {
|
|
465
|
+
const fullValue = await this._getLocked(key);
|
|
466
|
+
base = {fullValue};
|
|
467
|
+
// Emulate a pointer to the property that should be set to `value`.
|
|
468
|
+
const ptr = {obj: base, prop: 'fullValue'};
|
|
469
|
+
for (let i = 0; i < sub.length; i++) {
|
|
470
|
+
if (sub[i] === '__proto__') {
|
|
471
|
+
throw new Error('Modifying object prototype is not supported for security reasons');
|
|
472
|
+
}
|
|
473
|
+
let o = ptr.obj[ptr.prop];
|
|
474
|
+
if (o == null) ptr.obj[ptr.prop] = o = {};
|
|
475
|
+
// If o is a primitive (string, number, etc.), then setting `o.foo` has no effect
|
|
476
|
+
// because ECMAScript automatically wraps primitives in a temporary wrapper object.
|
|
477
|
+
if (typeof o !== 'object') {
|
|
478
|
+
throw new TypeError(
|
|
479
|
+
`Cannot set property ${JSON.stringify(sub[i])} on non-object ` +
|
|
429
480
|
`${JSON.stringify(o)} (key: ${JSON.stringify(key)} ` +
|
|
430
481
|
`value in db: ${JSON.stringify(fullValue)} ` +
|
|
431
482
|
`sub: ${JSON.stringify(sub.slice(0, i + 1))})`);
|
|
483
|
+
}
|
|
484
|
+
ptr.obj = ptr.obj[ptr.prop];
|
|
485
|
+
ptr.prop = sub[i];
|
|
432
486
|
}
|
|
433
|
-
ptr.obj
|
|
434
|
-
|
|
487
|
+
ptr.obj[ptr.prop] = value;
|
|
488
|
+
} catch (err) {
|
|
489
|
+
// this._setLocked() will not be called but it should still count as a write failure.
|
|
490
|
+
++this.metrics.writes;
|
|
491
|
+
++this.metrics.writesFailed;
|
|
492
|
+
++this.metrics.writesFinished;
|
|
493
|
+
throw err;
|
|
435
494
|
}
|
|
436
|
-
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
++this.metrics.writes;
|
|
440
|
-
++this.metrics.writesFailed;
|
|
441
|
-
++this.metrics.writesFinished;
|
|
442
|
-
throw err;
|
|
495
|
+
p = this._setLocked(key, base.fullValue);
|
|
496
|
+
} finally {
|
|
497
|
+
this._unlock(key);
|
|
443
498
|
}
|
|
444
|
-
p = this._setLocked(key, base.fullValue);
|
|
445
499
|
} finally {
|
|
446
|
-
this.
|
|
500
|
+
this._resumeFlush();
|
|
447
501
|
}
|
|
448
502
|
await p;
|
|
449
503
|
}
|
|
@@ -454,13 +508,14 @@ exports.Database = class {
|
|
|
454
508
|
* "bla"]
|
|
455
509
|
*/
|
|
456
510
|
async getSub(key, sub) {
|
|
511
|
+
let subvalue;
|
|
457
512
|
await this._lock(key);
|
|
458
513
|
try {
|
|
459
514
|
// get the full value
|
|
460
515
|
const value = await this._getLocked(key);
|
|
461
516
|
|
|
462
517
|
// everything is correct, navigate to the subvalue and return it
|
|
463
|
-
|
|
518
|
+
subvalue = value;
|
|
464
519
|
|
|
465
520
|
for (let i = 0; i < sub.length; i++) {
|
|
466
521
|
// test if the subvalue exist
|
|
@@ -476,10 +531,10 @@ exports.Database = class {
|
|
|
476
531
|
if (this.logger.isDebugEnabled()) {
|
|
477
532
|
this.logger.debug(`GETSUB - ${key}${JSON.stringify(sub)} - ${JSON.stringify(subvalue)}`);
|
|
478
533
|
}
|
|
479
|
-
return subvalue;
|
|
480
534
|
} finally {
|
|
481
535
|
this._unlock(key);
|
|
482
536
|
}
|
|
537
|
+
return clone(subvalue);
|
|
483
538
|
}
|
|
484
539
|
|
|
485
540
|
/**
|
|
@@ -489,6 +544,7 @@ exports.Database = class {
|
|
|
489
544
|
if (this._flushDone == null) {
|
|
490
545
|
this._flushDone = (async () => {
|
|
491
546
|
while (true) {
|
|
547
|
+
while (this._flushPaused != null) await this._flushPaused;
|
|
492
548
|
const dirtyEntries = [];
|
|
493
549
|
for (const entry of this.buffer) {
|
|
494
550
|
if (entry[1].dirty && !entry[1].writingInProgress) {
|
|
@@ -573,10 +629,12 @@ exports.Database = class {
|
|
|
573
629
|
}
|
|
574
630
|
};
|
|
575
631
|
|
|
576
|
-
const clone = (obj) => {
|
|
632
|
+
const clone = (obj, key = '') => {
|
|
577
633
|
// Handle the 3 simple types, and null or undefined
|
|
578
634
|
if (null == obj || 'object' !== typeof obj) return obj;
|
|
579
635
|
|
|
636
|
+
if (typeof obj.toJSON === 'function') return clone(obj.toJSON(key));
|
|
637
|
+
|
|
580
638
|
// Handle Date
|
|
581
639
|
if (obj instanceof Date) {
|
|
582
640
|
const copy = new Date();
|
|
@@ -588,7 +646,7 @@ const clone = (obj) => {
|
|
|
588
646
|
if (obj instanceof Array) {
|
|
589
647
|
const copy = [];
|
|
590
648
|
for (let i = 0, len = obj.length; i < len; ++i) {
|
|
591
|
-
copy[i] = clone(obj[i]);
|
|
649
|
+
copy[i] = clone(obj[i], String(i));
|
|
592
650
|
}
|
|
593
651
|
return copy;
|
|
594
652
|
}
|
|
@@ -597,7 +655,7 @@ const clone = (obj) => {
|
|
|
597
655
|
if (obj instanceof Object) {
|
|
598
656
|
const copy = {};
|
|
599
657
|
for (const attr in obj) {
|
|
600
|
-
if (Object.prototype.hasOwnProperty.call(obj, attr)) copy[attr] = clone(obj[attr]);
|
|
658
|
+
if (Object.prototype.hasOwnProperty.call(obj, attr)) copy[attr] = clone(obj[attr], attr);
|
|
601
659
|
}
|
|
602
660
|
return copy;
|
|
603
661
|
}
|
package/lib/logging.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const {Console} = require('console');
|
|
4
|
+
const {stdout, stderr} = require('process');
|
|
5
|
+
|
|
6
|
+
class ConsoleLogger extends Console {
|
|
7
|
+
constructor(opts = {}) { super({stdout, stderr, inspectOptions: {depth: Infinity}, ...opts}); }
|
|
8
|
+
isDebugEnabled() { return false; }
|
|
9
|
+
isInfoEnabled() { return true; }
|
|
10
|
+
isWarnEnabled() { return true; }
|
|
11
|
+
isErrorEnabled() { return true; }
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
exports.ConsoleLogger = ConsoleLogger;
|
|
15
|
+
|
|
16
|
+
exports.normalizeLogger = (logger) => {
|
|
17
|
+
const logLevelsUsed = ['debug', 'info', 'warn', 'error'];
|
|
18
|
+
logger = Object.create(logger || {});
|
|
19
|
+
for (const level of logLevelsUsed) {
|
|
20
|
+
const enabledFnName = `is${level.charAt(0).toUpperCase() + level.slice(1)}Enabled`;
|
|
21
|
+
if (typeof logger[level] !== 'function') {
|
|
22
|
+
logger[level] = () => {};
|
|
23
|
+
logger[enabledFnName] = () => false;
|
|
24
|
+
} else if (typeof logger[enabledFnName] !== 'function') {
|
|
25
|
+
logger[enabledFnName] = () => true;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return logger;
|
|
29
|
+
};
|
package/package.json
CHANGED
|
@@ -21,24 +21,24 @@
|
|
|
21
21
|
}
|
|
22
22
|
],
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"async": "^3.2.
|
|
24
|
+
"async": "^3.2.3",
|
|
25
25
|
"cassandra-driver": "^4.6.3",
|
|
26
26
|
"dirty": "^1.1.3",
|
|
27
|
-
"elasticsearch": "^16.7.
|
|
27
|
+
"elasticsearch": "^16.7.3",
|
|
28
28
|
"mongodb": "^3.7.3",
|
|
29
29
|
"mssql": "^8.1.0",
|
|
30
30
|
"mysql": "2.18.1",
|
|
31
|
-
"nano": "^
|
|
32
|
-
"pg": "^8.7.
|
|
31
|
+
"nano": "^10.0.0",
|
|
32
|
+
"pg": "^8.7.3",
|
|
33
33
|
"redis": "^3.1.2",
|
|
34
34
|
"rethinkdb": "^2.4.2",
|
|
35
|
-
"simple-git": "^3.
|
|
35
|
+
"simple-git": "^3.7.1"
|
|
36
36
|
},
|
|
37
37
|
"optionalDependencies": {
|
|
38
|
-
"sqlite3": "
|
|
38
|
+
"sqlite3": "^5.0.6"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
|
-
"cli-table": "^0.3.
|
|
41
|
+
"cli-table": "^0.3.11",
|
|
42
42
|
"eslint": "^7.32.0",
|
|
43
43
|
"eslint-config-etherpad": "^2.0.3",
|
|
44
44
|
"eslint-plugin-cypress": "^2.12.1",
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"url": "https://github.com/ether/ueberDB.git"
|
|
58
58
|
},
|
|
59
59
|
"main": "./index",
|
|
60
|
-
"version": "2.
|
|
60
|
+
"version": "2.2.2",
|
|
61
61
|
"bugs": {
|
|
62
62
|
"url": "https://github.com/ether/ueberDB/issues"
|
|
63
63
|
},
|
|
@@ -117,6 +117,6 @@
|
|
|
117
117
|
"root": true
|
|
118
118
|
},
|
|
119
119
|
"engines": {
|
|
120
|
-
"node": "
|
|
120
|
+
"node": ">=14.15.0"
|
|
121
121
|
}
|
|
122
122
|
}
|
package/test/lib/databases.js
CHANGED
|
@@ -51,13 +51,14 @@ exports.databases = {
|
|
|
51
51
|
removeMax: 0.3,
|
|
52
52
|
},
|
|
53
53
|
},
|
|
54
|
-
/* Disabled due to "Document update conflict" error in findKeys()
|
|
55
54
|
couch: {
|
|
56
55
|
host: '127.0.0.1',
|
|
57
56
|
port: 5984,
|
|
58
57
|
database: 'ueberdb',
|
|
59
58
|
user: 'ueberdb',
|
|
60
59
|
password: 'ueberdb',
|
|
60
|
+
speeds: {
|
|
61
|
+
findKeysMax: 30,
|
|
62
|
+
},
|
|
61
63
|
},
|
|
62
|
-
*/
|
|
63
64
|
};
|