ueberdb2 5.0.39 → 5.0.40
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/databases/cassandra_db.js +190 -0
- package/dist/databases/couch_db.js +133 -0
- package/dist/databases/dirty_db.js +62 -0
- package/dist/databases/dirty_git_db.js +68 -0
- package/dist/databases/elasticsearch_db.js +269 -0
- package/dist/databases/memory_db.js +37 -0
- package/dist/databases/mock_db.js +40 -0
- package/dist/databases/mongodb_db.js +95 -0
- package/dist/databases/mssql_db.js +132 -0
- package/dist/databases/mysql_db.js +149 -0
- package/dist/databases/postgres_db.js +125 -0
- package/dist/databases/postgrespool_db.js +10 -0
- package/dist/databases/redis_db.js +93 -0
- package/dist/databases/rethink_db.js +92 -0
- package/dist/databases/rusty_db.js +49 -0
- package/dist/databases/sqlite_db.js +75 -0
- package/dist/databases/surrealdb_db.js +135 -0
- package/dist/index.d.ts +1 -17
- package/dist/index.js +32 -2280
- package/dist/lib/AbstractDatabase.js +36 -0
- package/dist/lib/CacheAndBufferLayer.js +484 -0
- package/dist/lib/logging.js +20 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3,2260 +3,10 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
__esModule: { value: true },
|
|
4
4
|
[Symbol.toStringTag]: { value: "Module" }
|
|
5
5
|
});
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
11
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
12
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
13
|
-
var __copyProps = (to, from, except, desc) => {
|
|
14
|
-
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
15
|
-
key = keys[i];
|
|
16
|
-
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
17
|
-
get: ((k) => from[k]).bind(null, key),
|
|
18
|
-
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
19
|
-
});
|
|
20
|
-
}
|
|
21
|
-
return to;
|
|
22
|
-
};
|
|
23
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
24
|
-
value: mod,
|
|
25
|
-
enumerable: true
|
|
26
|
-
}) : target, mod));
|
|
27
|
-
//#endregion
|
|
28
|
-
let util = require("util");
|
|
29
|
-
util = __toESM(util);
|
|
30
|
-
let cassandra_driver = require("cassandra-driver");
|
|
31
|
-
let http = require("http");
|
|
32
|
-
http = __toESM(http);
|
|
33
|
-
let nano = require("nano");
|
|
34
|
-
nano = __toESM(nano);
|
|
35
|
-
let dirty_ts = require("dirty-ts");
|
|
36
|
-
dirty_ts = __toESM(dirty_ts);
|
|
37
|
-
let node_path = require("node:path");
|
|
38
|
-
let simple_git = require("simple-git");
|
|
39
|
-
let assert = require("assert");
|
|
40
|
-
assert = __toESM(assert);
|
|
41
|
-
let buffer = require("buffer");
|
|
42
|
-
let crypto = require("crypto");
|
|
43
|
-
let _elastic_elasticsearch = require("@elastic/elasticsearch");
|
|
44
|
-
let events = require("events");
|
|
45
|
-
events = __toESM(events);
|
|
46
|
-
let mongodb = require("mongodb");
|
|
47
|
-
let async = require("async");
|
|
48
|
-
async = __toESM(async);
|
|
49
|
-
let mssql = require("mssql");
|
|
50
|
-
mssql = __toESM(mssql);
|
|
51
|
-
let mysql2 = require("mysql2");
|
|
52
|
-
let pg = require("pg");
|
|
53
|
-
pg = __toESM(pg);
|
|
54
|
-
let redis = require("redis");
|
|
55
|
-
let rethinkdb = require("rethinkdb");
|
|
56
|
-
rethinkdb = __toESM(rethinkdb);
|
|
57
|
-
let rusty_store_kv = require("rusty-store-kv");
|
|
58
|
-
let surrealdb = require("surrealdb");
|
|
59
|
-
//#region lib/CacheAndBufferLayer.ts
|
|
60
|
-
/**
|
|
61
|
-
* 2011 Peter 'Pita' Martischka
|
|
62
|
-
*
|
|
63
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
64
|
-
* you may not use this file except in compliance with the License.
|
|
65
|
-
* You may obtain a copy of the License at
|
|
66
|
-
*
|
|
67
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
68
|
-
*
|
|
69
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
70
|
-
* distributed under the License is distributed on an "AS-IS" BASIS,
|
|
71
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
72
|
-
* See the License for the specific language governing permissions and
|
|
73
|
-
* limitations under the License.
|
|
74
|
-
*/
|
|
75
|
-
/**
|
|
76
|
-
* This module is made for the case, you want to use a SQL-Based Databse or a KeyValue Database that
|
|
77
|
-
* can only save strings(and no objects), as a JSON KeyValue Store.
|
|
78
|
-
*
|
|
79
|
-
* The idea of the dbWrapper is to provide following features:
|
|
80
|
-
*
|
|
81
|
-
* * automatic JSON serialize/deserialize to abstract that away from the database driver and the
|
|
82
|
-
* module user.
|
|
83
|
-
* * cache reads. A amount of KeyValues are hold in the memory, so that reading is faster.
|
|
84
|
-
* * Buffer DB Writings. Sets and deletes should be buffered to make them in a setted interval
|
|
85
|
-
* with a bulk. This reduces the overhead of database transactions and makes the database
|
|
86
|
-
* faster. But there is also a danger to loose data integrity, to keep that, we should provide a
|
|
87
|
-
* flush function.
|
|
88
|
-
*
|
|
89
|
-
* All Features can be disabled or configured. The Wrapper provides default settings that can be
|
|
90
|
-
* overwriden by the driver and by the module user.
|
|
91
|
-
*/
|
|
92
|
-
/**
|
|
93
|
-
* Cache with Least Recently Used eviction policy.
|
|
94
|
-
*/
|
|
95
|
-
var LRU = class {
|
|
96
|
-
/**
|
|
97
|
-
* @param evictable Optional predicate that dictates whether it is permissable to evict the entry
|
|
98
|
-
* if it is old and the cache is over capacity. The predicate is passed two arguments (key,
|
|
99
|
-
* value). If no predicate is provided, all entries are evictable. Warning: Non-evictable
|
|
100
|
-
* entries can cause the cache to go over capacity. If the number of non-evictable entries is
|
|
101
|
-
* greater than or equal to the capacity, all new evictable entries will be evicted
|
|
102
|
-
* immediately.
|
|
103
|
-
*/
|
|
104
|
-
constructor(capacity, evictable = (k, v) => true) {
|
|
105
|
-
this._capacity = capacity;
|
|
106
|
-
this._evictable = evictable;
|
|
107
|
-
this._cache = /* @__PURE__ */ new Map();
|
|
108
|
-
}
|
|
109
|
-
/**
|
|
110
|
-
* The entries accessed via this iterator are not considered to have been "used" (for purposes of
|
|
111
|
-
* determining least recently used).
|
|
112
|
-
*/
|
|
113
|
-
[Symbol.iterator]() {
|
|
114
|
-
return this._cache.entries();
|
|
115
|
-
}
|
|
116
|
-
/**
|
|
117
|
-
* @param isUse Optional boolean indicating whether this get() should be considered a "use" of the
|
|
118
|
-
* entry (for determining least recently used). Defaults to true.
|
|
119
|
-
* @returns undefined if there is no entry matching the given key.
|
|
120
|
-
*/
|
|
121
|
-
get(k, isUse = true) {
|
|
122
|
-
if (!this._cache.has(k)) return;
|
|
123
|
-
const v = this._cache.get(k);
|
|
124
|
-
if (isUse) {
|
|
125
|
-
this._cache.delete(k);
|
|
126
|
-
this._cache.set(k, v);
|
|
127
|
-
}
|
|
128
|
-
return v;
|
|
129
|
-
}
|
|
130
|
-
/**
|
|
131
|
-
* Adds or updates an entry in the cache. This marks the entry as the most recently used entry.
|
|
132
|
-
*/
|
|
133
|
-
set(k, v) {
|
|
134
|
-
this._cache.delete(k);
|
|
135
|
-
this._cache.set(k, v);
|
|
136
|
-
this.evictOld();
|
|
137
|
-
}
|
|
138
|
-
/**
|
|
139
|
-
* Evicts the oldest evictable entries until the number of entries is equal to or less than the
|
|
140
|
-
* cache's capacity. This method is automatically called by set(). Call this if you need to evict
|
|
141
|
-
* newly evictable entries before the next call to set().
|
|
142
|
-
*/
|
|
143
|
-
evictOld() {
|
|
144
|
-
for (const [k, v] of this._cache.entries()) {
|
|
145
|
-
if (this._cache.size <= this._capacity) break;
|
|
146
|
-
if (!this._evictable(k, v)) continue;
|
|
147
|
-
this._cache.delete(k);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
};
|
|
151
|
-
var SelfContainedPromise = class extends Promise {
|
|
152
|
-
constructor(executor = null) {
|
|
153
|
-
let done;
|
|
154
|
-
super((resolve, reject) => {
|
|
155
|
-
done = (err, val) => err != null ? reject(err) : resolve(val);
|
|
156
|
-
if (executor != null) executor(resolve, reject);
|
|
157
|
-
});
|
|
158
|
-
this.done = done;
|
|
159
|
-
}
|
|
160
|
-
};
|
|
161
|
-
const defaultSettings = {
|
|
162
|
-
bulkLimit: 0,
|
|
163
|
-
cache: 1e4,
|
|
164
|
-
writeInterval: 100,
|
|
165
|
-
json: true,
|
|
166
|
-
charset: "utf8mb4"
|
|
167
|
-
};
|
|
168
|
-
const Database$1 = class {
|
|
169
|
-
/**
|
|
170
|
-
* @param wrappedDB The Database that should be wrapped
|
|
171
|
-
* @param settings (optional) The settings that should be applied to the wrapper
|
|
172
|
-
*/
|
|
173
|
-
constructor(wrappedDB, settings, logger) {
|
|
174
|
-
if (wrappedDB.isAsync) this.wrappedDB = wrappedDB;
|
|
175
|
-
else {
|
|
176
|
-
this.wrappedDB = {};
|
|
177
|
-
for (const fn of [
|
|
178
|
-
"close",
|
|
179
|
-
"doBulk",
|
|
180
|
-
"findKeys",
|
|
181
|
-
"get",
|
|
182
|
-
"init",
|
|
183
|
-
"remove",
|
|
184
|
-
"set"
|
|
185
|
-
]) {
|
|
186
|
-
const f = wrappedDB[fn];
|
|
187
|
-
if (typeof f !== "function") continue;
|
|
188
|
-
this.wrappedDB[fn] = util.default.promisify(f.bind(wrappedDB));
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
this.logger = logger;
|
|
192
|
-
this.settings = Object.freeze({
|
|
193
|
-
...defaultSettings,
|
|
194
|
-
...wrappedDB.settings || {},
|
|
195
|
-
...settings || {}
|
|
196
|
-
});
|
|
197
|
-
this.buffer = new LRU(this.settings.cache, (k, v) => !v.dirty && !v.writingInProgress);
|
|
198
|
-
this._flushPaused = null;
|
|
199
|
-
this._locks = /* @__PURE__ */ new Map();
|
|
200
|
-
this.metrics = {
|
|
201
|
-
lockAwaits: 0,
|
|
202
|
-
lockAcquires: 0,
|
|
203
|
-
lockReleases: 0,
|
|
204
|
-
reads: 0,
|
|
205
|
-
readsFailed: 0,
|
|
206
|
-
readsFinished: 0,
|
|
207
|
-
readsFromCache: 0,
|
|
208
|
-
readsFromDb: 0,
|
|
209
|
-
readsFromDbFailed: 0,
|
|
210
|
-
readsFromDbFinished: 0,
|
|
211
|
-
writes: 0,
|
|
212
|
-
writesFailed: 0,
|
|
213
|
-
writesFinished: 0,
|
|
214
|
-
writesObsoleted: 0,
|
|
215
|
-
writesToDb: 0,
|
|
216
|
-
writesToDbFailed: 0,
|
|
217
|
-
writesToDbFinished: 0,
|
|
218
|
-
writesToDbRetried: 0
|
|
219
|
-
};
|
|
220
|
-
this.flushInterval = this.settings.writeInterval > 0 ? setInterval(() => this.flush(), this.settings.writeInterval) : null;
|
|
221
|
-
}
|
|
222
|
-
async _lock(key) {
|
|
223
|
-
while (true) {
|
|
224
|
-
const l = this._locks.get(key);
|
|
225
|
-
if (l == null) break;
|
|
226
|
-
++this.metrics.lockAwaits;
|
|
227
|
-
await l;
|
|
228
|
-
}
|
|
229
|
-
++this.metrics.lockAcquires;
|
|
230
|
-
this._locks.set(key, new SelfContainedPromise());
|
|
231
|
-
}
|
|
232
|
-
async _unlock(key) {
|
|
233
|
-
++this.metrics.lockReleases;
|
|
234
|
-
this._locks.get(key).done();
|
|
235
|
-
this._locks.delete(key);
|
|
236
|
-
}
|
|
237
|
-
_pauseFlush() {
|
|
238
|
-
if (this._flushPaused == null) {
|
|
239
|
-
this._flushPaused = new SelfContainedPromise();
|
|
240
|
-
this._flushPaused.count = 0;
|
|
241
|
-
}
|
|
242
|
-
++this._flushPaused.count;
|
|
243
|
-
}
|
|
244
|
-
_resumeFlush() {
|
|
245
|
-
if (--this._flushPaused.count > 0) return;
|
|
246
|
-
this._flushPaused.done();
|
|
247
|
-
this._flushPaused = null;
|
|
248
|
-
}
|
|
249
|
-
/**
|
|
250
|
-
* wraps the init function of the original DB
|
|
251
|
-
*/
|
|
252
|
-
async init() {
|
|
253
|
-
await this.wrappedDB.init();
|
|
254
|
-
}
|
|
255
|
-
/**
|
|
256
|
-
* wraps the close function of the original DB
|
|
257
|
-
*/
|
|
258
|
-
async close() {
|
|
259
|
-
clearInterval(this.flushInterval);
|
|
260
|
-
await this.flush();
|
|
261
|
-
await this.wrappedDB.close();
|
|
262
|
-
this.wrappedDB = null;
|
|
263
|
-
}
|
|
264
|
-
/**
|
|
265
|
-
* Gets the value trough the wrapper.
|
|
266
|
-
*/
|
|
267
|
-
async get(key) {
|
|
268
|
-
let v;
|
|
269
|
-
await this._lock(key);
|
|
270
|
-
try {
|
|
271
|
-
v = await this._getLocked(key);
|
|
272
|
-
} finally {
|
|
273
|
-
this._unlock(key);
|
|
274
|
-
}
|
|
275
|
-
return clone(v);
|
|
276
|
-
}
|
|
277
|
-
async _getLocked(key) {
|
|
278
|
-
++this.metrics.reads;
|
|
279
|
-
try {
|
|
280
|
-
const entry = this.buffer.get(key);
|
|
281
|
-
if (entry != null) {
|
|
282
|
-
++this.metrics.readsFromCache;
|
|
283
|
-
if (this.logger.isDebugEnabled()) this.logger.debug(`GET - ${key} - ${JSON.stringify(entry.value)} - from ${entry.dirty ? "dirty buffer" : "cache"}`);
|
|
284
|
-
return entry.value;
|
|
285
|
-
}
|
|
286
|
-
let value;
|
|
287
|
-
++this.metrics.readsFromDb;
|
|
288
|
-
try {
|
|
289
|
-
value = await this.wrappedDB.get(key);
|
|
290
|
-
} catch (err) {
|
|
291
|
-
++this.metrics.readsFromDbFailed;
|
|
292
|
-
throw err;
|
|
293
|
-
} finally {
|
|
294
|
-
++this.metrics.readsFromDbFinished;
|
|
295
|
-
}
|
|
296
|
-
if (this.settings.json) try {
|
|
297
|
-
value = JSON.parse(value);
|
|
298
|
-
} catch (err) {
|
|
299
|
-
this.logger.error(`JSON-PROBLEM:${value}`);
|
|
300
|
-
throw err;
|
|
301
|
-
}
|
|
302
|
-
if (this.settings.cache > 0) this.buffer.set(key, {
|
|
303
|
-
value,
|
|
304
|
-
dirty: null,
|
|
305
|
-
writingInProgress: false
|
|
306
|
-
});
|
|
307
|
-
if (this.logger.isDebugEnabled()) this.logger.debug(`GET - ${key} - ${JSON.stringify(value)} - from database `);
|
|
308
|
-
return value;
|
|
309
|
-
} catch (err) {
|
|
310
|
-
++this.metrics.readsFailed;
|
|
311
|
-
throw err;
|
|
312
|
-
} finally {
|
|
313
|
-
++this.metrics.readsFinished;
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
/**
|
|
317
|
-
* Find keys function searches the db sets for matching entries and
|
|
318
|
-
* returns the key entries via callback.
|
|
319
|
-
*/
|
|
320
|
-
async findKeys(key, notKey) {
|
|
321
|
-
await this.flush();
|
|
322
|
-
const keyValues = await this.wrappedDB.findKeys(key, notKey);
|
|
323
|
-
if (this.logger.isDebugEnabled()) this.logger.debug(`GET - ${key}-${notKey} - ${JSON.stringify(keyValues)} - from database `);
|
|
324
|
-
return clone(keyValues);
|
|
325
|
-
}
|
|
326
|
-
/**
|
|
327
|
-
* Remove a record from the database
|
|
328
|
-
*/
|
|
329
|
-
async remove(key) {
|
|
330
|
-
if (this.logger.isDebugEnabled()) this.logger.debug(`DELETE - ${key} - from database `);
|
|
331
|
-
await this.set(key, null);
|
|
332
|
-
}
|
|
333
|
-
/**
|
|
334
|
-
* Sets the value trough the wrapper
|
|
335
|
-
*/
|
|
336
|
-
async set(key, value) {
|
|
337
|
-
value = clone(value);
|
|
338
|
-
let p;
|
|
339
|
-
this._pauseFlush();
|
|
340
|
-
try {
|
|
341
|
-
await this._lock(key);
|
|
342
|
-
try {
|
|
343
|
-
p = this._setLocked(key, value);
|
|
344
|
-
} finally {
|
|
345
|
-
this._unlock(key);
|
|
346
|
-
}
|
|
347
|
-
} finally {
|
|
348
|
-
this._resumeFlush();
|
|
349
|
-
}
|
|
350
|
-
await p;
|
|
351
|
-
}
|
|
352
|
-
async _setLocked(key, value) {
|
|
353
|
-
++this.metrics.writes;
|
|
354
|
-
try {
|
|
355
|
-
let entry = this.buffer.get(key);
|
|
356
|
-
if (!entry || entry.writingInProgress) entry = {};
|
|
357
|
-
else if (entry.dirty) ++this.metrics.writesObsoleted;
|
|
358
|
-
entry.value = value;
|
|
359
|
-
if (!entry.dirty) entry.dirty = new SelfContainedPromise();
|
|
360
|
-
this.buffer.set(key, entry);
|
|
361
|
-
const buffered = this.settings.writeInterval > 0;
|
|
362
|
-
if (this.logger.isDebugEnabled()) this.logger.debug(`SET - ${key} - ${JSON.stringify(value)} - to ${buffered ? "buffer" : "database"}`);
|
|
363
|
-
if (!buffered) this._write([[key, entry]]);
|
|
364
|
-
await entry.dirty;
|
|
365
|
-
} catch (err) {
|
|
366
|
-
++this.metrics.writesFailed;
|
|
367
|
-
throw err;
|
|
368
|
-
} finally {
|
|
369
|
-
++this.metrics.writesFinished;
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
/**
|
|
373
|
-
* Sets a subvalue
|
|
374
|
-
*/
|
|
375
|
-
async setSub(key, sub, value) {
|
|
376
|
-
value = clone(value);
|
|
377
|
-
if (this.logger.isDebugEnabled()) this.logger.debug(`SETSUB - ${key}${JSON.stringify(sub)} - ${JSON.stringify(value)}`);
|
|
378
|
-
let p;
|
|
379
|
-
this._pauseFlush();
|
|
380
|
-
try {
|
|
381
|
-
await this._lock(key);
|
|
382
|
-
try {
|
|
383
|
-
let base;
|
|
384
|
-
try {
|
|
385
|
-
const fullValue = await this._getLocked(key);
|
|
386
|
-
base = { fullValue };
|
|
387
|
-
const ptr = {
|
|
388
|
-
obj: base,
|
|
389
|
-
prop: "fullValue"
|
|
390
|
-
};
|
|
391
|
-
for (let i = 0; i < sub.length; i++) {
|
|
392
|
-
if (sub[i] === "__proto__") throw new Error("Modifying object prototype is not supported for security reasons");
|
|
393
|
-
let o = ptr.obj[ptr.prop];
|
|
394
|
-
if (o == null) ptr.obj[ptr.prop] = o = {};
|
|
395
|
-
if (typeof o !== "object") throw new TypeError(`Cannot set property ${JSON.stringify(sub[i])} on non-object ${JSON.stringify(o)} (key: ${JSON.stringify(key)} value in db: ${JSON.stringify(fullValue)} sub: ${JSON.stringify(sub.slice(0, i + 1))})`);
|
|
396
|
-
ptr.obj = ptr.obj[ptr.prop];
|
|
397
|
-
ptr.prop = sub[i];
|
|
398
|
-
}
|
|
399
|
-
if (value == null) delete ptr.obj[ptr.prop];
|
|
400
|
-
else ptr.obj[ptr.prop] = value;
|
|
401
|
-
} catch (err) {
|
|
402
|
-
++this.metrics.writes;
|
|
403
|
-
++this.metrics.writesFailed;
|
|
404
|
-
++this.metrics.writesFinished;
|
|
405
|
-
throw err;
|
|
406
|
-
}
|
|
407
|
-
p = this._setLocked(key, base.fullValue);
|
|
408
|
-
} finally {
|
|
409
|
-
await this._unlock(key);
|
|
410
|
-
}
|
|
411
|
-
} finally {
|
|
412
|
-
this._resumeFlush();
|
|
413
|
-
}
|
|
414
|
-
await p;
|
|
415
|
-
}
|
|
416
|
-
/**
|
|
417
|
-
* Returns a sub value of the object
|
|
418
|
-
* @param sub is a array, for example if you want to access object.test.bla, the array is ["test",
|
|
419
|
-
* "bla"]
|
|
420
|
-
*/
|
|
421
|
-
async getSub(key, sub) {
|
|
422
|
-
await this._lock(key);
|
|
423
|
-
try {
|
|
424
|
-
let v = await this._getLocked(key);
|
|
425
|
-
for (const k of sub) {
|
|
426
|
-
if (typeof v !== "object" || v != null && !Object.prototype.hasOwnProperty.call(v, k) || k === "__proto__") v = null;
|
|
427
|
-
if (v == null) break;
|
|
428
|
-
v = v[k];
|
|
429
|
-
}
|
|
430
|
-
if (this.logger.isDebugEnabled()) this.logger.debug(`GETSUB - ${key}${JSON.stringify(sub)} - ${JSON.stringify(v)}`);
|
|
431
|
-
return clone(v);
|
|
432
|
-
} finally {
|
|
433
|
-
this._unlock(key);
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
/**
|
|
437
|
-
* Writes all dirty values to the database
|
|
438
|
-
*/
|
|
439
|
-
async flush() {
|
|
440
|
-
if (this._flushDone == null) this._flushDone = (async () => {
|
|
441
|
-
while (true) {
|
|
442
|
-
while (this._flushPaused != null) await this._flushPaused;
|
|
443
|
-
const dirtyEntries = [];
|
|
444
|
-
for (const entry of this.buffer) if (entry[1].dirty && !entry[1].writingInProgress) {
|
|
445
|
-
dirtyEntries.push(entry);
|
|
446
|
-
if (this.settings.bulkLimit && dirtyEntries.length >= this.settings.bulkLimit) break;
|
|
447
|
-
}
|
|
448
|
-
if (dirtyEntries.length === 0) return;
|
|
449
|
-
await this._write(dirtyEntries);
|
|
450
|
-
}
|
|
451
|
-
})();
|
|
452
|
-
await this._flushDone;
|
|
453
|
-
this._flushDone = null;
|
|
454
|
-
}
|
|
455
|
-
async _write(dirtyEntries) {
|
|
456
|
-
const markDone = (entry, err) => {
|
|
457
|
-
if (entry.writingInProgress) {
|
|
458
|
-
entry.writingInProgress = false;
|
|
459
|
-
if (err != null) ++this.metrics.writesToDbFailed;
|
|
460
|
-
++this.metrics.writesToDbFinished;
|
|
461
|
-
}
|
|
462
|
-
entry.dirty.done(err);
|
|
463
|
-
entry.dirty = null;
|
|
464
|
-
};
|
|
465
|
-
const ops = [];
|
|
466
|
-
const entries = [];
|
|
467
|
-
for (const [key, entry] of dirtyEntries) {
|
|
468
|
-
let value = entry.value;
|
|
469
|
-
try {
|
|
470
|
-
value = this.settings.json && value != null ? JSON.stringify(value) : clone(value);
|
|
471
|
-
} catch (err) {
|
|
472
|
-
markDone(entry, err);
|
|
473
|
-
continue;
|
|
474
|
-
}
|
|
475
|
-
entry.writingInProgress = true;
|
|
476
|
-
ops.push({
|
|
477
|
-
type: value == null ? "remove" : "set",
|
|
478
|
-
key,
|
|
479
|
-
value
|
|
480
|
-
});
|
|
481
|
-
entries.push(entry);
|
|
482
|
-
}
|
|
483
|
-
if (ops.length === 0) return;
|
|
484
|
-
this.metrics.writesToDb += ops.length;
|
|
485
|
-
const writeOneOp = async (op, entry) => {
|
|
486
|
-
let writeErr = null;
|
|
487
|
-
try {
|
|
488
|
-
switch (op.type) {
|
|
489
|
-
case "remove":
|
|
490
|
-
await this.wrappedDB.remove(op.key);
|
|
491
|
-
break;
|
|
492
|
-
case "set":
|
|
493
|
-
await this.wrappedDB.set(op.key, op.value);
|
|
494
|
-
break;
|
|
495
|
-
default: throw new Error(`unsupported operation type: ${op.type}`);
|
|
496
|
-
}
|
|
497
|
-
} catch (err) {
|
|
498
|
-
writeErr = err || new Error(err);
|
|
499
|
-
}
|
|
500
|
-
markDone(entry, writeErr);
|
|
501
|
-
};
|
|
502
|
-
if (ops.length === 1) await writeOneOp(ops[0], entries[0]);
|
|
503
|
-
else {
|
|
504
|
-
let success = false;
|
|
505
|
-
try {
|
|
506
|
-
await this.wrappedDB.doBulk(ops);
|
|
507
|
-
success = true;
|
|
508
|
-
} catch (err) {
|
|
509
|
-
this.logger.error(`Bulk write of ${ops.length} ops failed, retrying individually: ${err.stack || err}`);
|
|
510
|
-
this.metrics.writesToDbRetried += ops.length;
|
|
511
|
-
await Promise.all(ops.map(async (op, i) => await writeOneOp(op, entries[i])));
|
|
512
|
-
}
|
|
513
|
-
if (success) entries.forEach((entry) => markDone(entry, null));
|
|
514
|
-
}
|
|
515
|
-
this.buffer.evictOld();
|
|
516
|
-
}
|
|
517
|
-
};
|
|
518
|
-
const clone = (obj, key = "") => {
|
|
519
|
-
if (null == obj || "object" !== typeof obj) return obj;
|
|
520
|
-
if (typeof obj.toJSON === "function") return clone(obj.toJSON(key));
|
|
521
|
-
if (obj instanceof Date) {
|
|
522
|
-
const copy = /* @__PURE__ */ new Date();
|
|
523
|
-
copy.setTime(obj.getTime());
|
|
524
|
-
return copy;
|
|
525
|
-
}
|
|
526
|
-
if (obj instanceof Array) {
|
|
527
|
-
const copy = [];
|
|
528
|
-
for (let i = 0, len = obj.length; i < len; ++i) copy[i] = clone(obj[i], String(i));
|
|
529
|
-
return copy;
|
|
530
|
-
}
|
|
531
|
-
if (obj instanceof Object) {
|
|
532
|
-
const copy = {};
|
|
533
|
-
for (const attr in obj) if (Object.prototype.hasOwnProperty.call(obj, attr)) copy[attr] = clone(obj[attr], attr);
|
|
534
|
-
return copy;
|
|
535
|
-
}
|
|
536
|
-
throw new Error("Unable to copy obj! Its type isn't supported.");
|
|
537
|
-
};
|
|
538
|
-
//#endregion
|
|
539
|
-
//#region lib/logging.ts
|
|
540
|
-
const normalizeLogger = (logger) => {
|
|
541
|
-
const logLevelsUsed = [
|
|
542
|
-
"debug",
|
|
543
|
-
"info",
|
|
544
|
-
"warn",
|
|
545
|
-
"error"
|
|
546
|
-
];
|
|
547
|
-
logger = Object.create(logger || {});
|
|
548
|
-
for (const level of logLevelsUsed) {
|
|
549
|
-
const enabledFnName = `is${level.charAt(0).toUpperCase() + level.slice(1)}Enabled`;
|
|
550
|
-
if (typeof logger[level] !== "function") {
|
|
551
|
-
logger[level] = () => {};
|
|
552
|
-
logger[enabledFnName] = () => false;
|
|
553
|
-
} else if (typeof logger[enabledFnName] !== "function") logger[enabledFnName] = () => true;
|
|
554
|
-
}
|
|
555
|
-
return logger;
|
|
556
|
-
};
|
|
557
|
-
//#endregion
|
|
558
|
-
//#region lib/AbstractDatabase.ts
|
|
559
|
-
const nullLogger = normalizeLogger(null);
|
|
560
|
-
const simpleGlobToRegExp = (s) => s.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
|
|
561
|
-
var AbstractDatabase = class {
|
|
562
|
-
logger;
|
|
563
|
-
settings;
|
|
564
|
-
constructor(settings) {
|
|
565
|
-
if (new.target === module.exports) throw new TypeError("cannot instantiate Abstract Database directly");
|
|
566
|
-
for (const fn of [
|
|
567
|
-
"init",
|
|
568
|
-
"close",
|
|
569
|
-
"get",
|
|
570
|
-
"findKeys",
|
|
571
|
-
"remove",
|
|
572
|
-
"set"
|
|
573
|
-
]) if (typeof this[fn] !== "function") throw new TypeError(`method ${fn} not defined`);
|
|
574
|
-
this.logger = nullLogger;
|
|
575
|
-
this.settings = settings;
|
|
576
|
-
}
|
|
577
|
-
/**
|
|
578
|
-
* For findKey regex. Used by document dbs like mongodb or dirty.
|
|
579
|
-
*/
|
|
580
|
-
createFindRegex(key, notKey) {
|
|
581
|
-
let regex = `^(?=${simpleGlobToRegExp(key)}$)`;
|
|
582
|
-
if (notKey != null) regex += `(?!${simpleGlobToRegExp(notKey)}$)`;
|
|
583
|
-
return new RegExp(regex);
|
|
584
|
-
}
|
|
585
|
-
doBulk(operations, cb) {
|
|
586
|
-
throw new Error("the doBulk method must be implemented if write caching is enabled");
|
|
587
|
-
}
|
|
588
|
-
get isAsync() {
|
|
589
|
-
return false;
|
|
590
|
-
}
|
|
591
|
-
};
|
|
592
|
-
//#endregion
|
|
593
|
-
//#region databases/cassandra_db.ts
|
|
594
|
-
/**
|
|
595
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
596
|
-
* you may not use this file except in compliance with the License.
|
|
597
|
-
* You may obtain a copy of the License at
|
|
598
|
-
*
|
|
599
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
600
|
-
*
|
|
601
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
602
|
-
* distributed under the License is distributed on an "AS-IS" BASIS,
|
|
603
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
604
|
-
* See the License for the specific language governing permissions and
|
|
605
|
-
* limitations under the License.
|
|
606
|
-
*/
|
|
607
|
-
var Cassandra_db = class extends AbstractDatabase {
|
|
608
|
-
client;
|
|
609
|
-
pool;
|
|
610
|
-
/**
|
|
611
|
-
* @param {Object} settings The required settings object to initiate the Cassandra database
|
|
612
|
-
* @param {String[]} settings.clientOptions See
|
|
613
|
-
* http://www.datastax.com/drivers/nodejs/2.0/global.html#ClientOptions for a full set of
|
|
614
|
-
* options that can be used
|
|
615
|
-
* @param {String} settings.columnFamily The column family that should be used to store data. The
|
|
616
|
-
* column family will be created if it doesn't exist
|
|
617
|
-
* @param {Function} [settings.logger] Function that will be used to pass on log events emitted by
|
|
618
|
-
* the Cassandra driver. See https://github.com/datastax/nodejs-driver#logging for more
|
|
619
|
-
* information
|
|
620
|
-
*/
|
|
621
|
-
constructor(settings) {
|
|
622
|
-
super(settings);
|
|
623
|
-
if (!settings.clientOptions) throw new Error("The Cassandra client options should be defined");
|
|
624
|
-
if (!settings.columnFamily) throw new Error("The Cassandra column family should be defined");
|
|
625
|
-
this.settings = { database: settings.database };
|
|
626
|
-
this.settings.clientOptions = settings.clientOptions;
|
|
627
|
-
this.settings.columnFamily = settings.columnFamily;
|
|
628
|
-
this.settings.logger = settings.logger;
|
|
629
|
-
}
|
|
630
|
-
/**
|
|
631
|
-
* Initializes the Cassandra client, connects to Cassandra and creates the CF if it didn't exist
|
|
632
|
-
* already
|
|
633
|
-
*
|
|
634
|
-
* @param {Function} callback Standard callback method.
|
|
635
|
-
* @param {Error} callback.err An error object (if any.)
|
|
636
|
-
*/
|
|
637
|
-
init(callback) {
|
|
638
|
-
this.client = new cassandra_driver.Client(this.settings.clientOptions);
|
|
639
|
-
if (this.settings.logger) this.client.on("log", this.settings.logger);
|
|
640
|
-
this.client.execute("SELECT columnfamily_name FROM system.schema_columnfamilies WHERE keyspace_name = ?", [this.settings.clientOptions.keyspace], (err, result) => {
|
|
641
|
-
if (err) return callback(err);
|
|
642
|
-
let isDefined = false;
|
|
643
|
-
const length = result.rows.length;
|
|
644
|
-
for (let i = 0; i < length; i++) if (result.rows[i].columnfamily_name === this.settings.columnFamily) {
|
|
645
|
-
isDefined = true;
|
|
646
|
-
break;
|
|
647
|
-
}
|
|
648
|
-
if (isDefined) return callback(null);
|
|
649
|
-
else {
|
|
650
|
-
const cql = `CREATE COLUMNFAMILY "${this.settings.columnFamily}" (key text PRIMARY KEY, data text)`;
|
|
651
|
-
this.client && this.client.execute(cql, callback);
|
|
652
|
-
}
|
|
653
|
-
});
|
|
654
|
-
}
|
|
655
|
-
/**
|
|
656
|
-
* Gets a value from Cassandra
|
|
657
|
-
*
|
|
658
|
-
* @param {String} key The key for which the value should be retrieved
|
|
659
|
-
* @param {Function} callback Standard callback method
|
|
660
|
-
* @param {Error} callback.err An error object, if any
|
|
661
|
-
* @param {String} callback.value The value for the given key (if any)
|
|
662
|
-
*/
|
|
663
|
-
get(key, callback) {
|
|
664
|
-
const cql = `SELECT data FROM "${this.settings.columnFamily}" WHERE key = ?`;
|
|
665
|
-
this.client && this.client.execute(cql, [key], (err, result) => {
|
|
666
|
-
if (err) return callback(err);
|
|
667
|
-
if (!result.rows || result.rows.length === 0) return callback(null, null);
|
|
668
|
-
return callback(null, result.rows[0].data);
|
|
669
|
-
});
|
|
670
|
-
}
|
|
671
|
-
/**
|
|
672
|
-
* Cassandra has no native `findKeys` method. This function implements a naive filter by
|
|
673
|
-
* retrieving *all* the keys and filtering those. This should obviously be used with the utmost
|
|
674
|
-
* care and is probably not something you want to run in production.
|
|
675
|
-
*
|
|
676
|
-
* @param {String} key The filter for keys that should match
|
|
677
|
-
* @param {String} [notKey] The filter for keys that shouldn't match
|
|
678
|
-
* @param {Function} callback Standard callback method
|
|
679
|
-
* @param {Error} callback.err An error object, if any
|
|
680
|
-
* @param {String[]} callback.keys An array of keys that match the specified filters
|
|
681
|
-
*/
|
|
682
|
-
findKeys(key, notKey, callback) {
|
|
683
|
-
let cql = null;
|
|
684
|
-
if (!notKey) {
|
|
685
|
-
cql = `SELECT key FROM "${this.settings.columnFamily}"`;
|
|
686
|
-
this.client && this.client.execute(cql, (err, result) => {
|
|
687
|
-
if (err) return callback(err);
|
|
688
|
-
const regex = new RegExp(`^${key.replace(/\*/g, ".*")}$`);
|
|
689
|
-
const keys = [];
|
|
690
|
-
result.rows.forEach((row) => {
|
|
691
|
-
if (regex.test(row.key)) keys.push(row.key);
|
|
692
|
-
});
|
|
693
|
-
return callback(null, keys);
|
|
694
|
-
});
|
|
695
|
-
} else if (notKey === "*:*:*") {
|
|
696
|
-
const matches = /^([^:]+):\*$/.exec(key);
|
|
697
|
-
if (matches) {
|
|
698
|
-
cql = `SELECT * from "${this.settings.columnFamily}" WHERE key = ?`;
|
|
699
|
-
this.client && this.client.execute(cql, [`ueberdb:keys:${matches[1]}`], (err, result) => {
|
|
700
|
-
if (err) return callback(err);
|
|
701
|
-
if (!result.rows || result.rows.length === 0) return callback(null, []);
|
|
702
|
-
return callback(null, result.rows.map((row) => row.data));
|
|
703
|
-
});
|
|
704
|
-
} else return callback(/* @__PURE__ */ new Error("Cassandra db only supports key patterns like pad:* when notKey is set to *:*:*"), null);
|
|
705
|
-
} else return callback(/* @__PURE__ */ new Error("Cassandra db currently only supports *:*:* as notKey"), null);
|
|
706
|
-
}
|
|
707
|
-
/**
|
|
708
|
-
* Sets a value for a key
|
|
709
|
-
*
|
|
710
|
-
* @param {String} key The key to set
|
|
711
|
-
* @param {String} value The value associated to this key
|
|
712
|
-
* @param {Function} callback Standard callback method
|
|
713
|
-
* @param {Error} callback.err An error object, if any
|
|
714
|
-
*/
|
|
715
|
-
set(key, value, callback) {
|
|
716
|
-
this.doBulk([{
|
|
717
|
-
type: "set",
|
|
718
|
-
key,
|
|
719
|
-
value
|
|
720
|
-
}], callback);
|
|
721
|
-
}
|
|
722
|
-
/**
|
|
723
|
-
* Removes a key and it's value from the column family
|
|
724
|
-
*
|
|
725
|
-
* @param {String} key The key to remove
|
|
726
|
-
* @param {Function} callback Standard callback method
|
|
727
|
-
* @param {Error} callback.err An error object, if any
|
|
728
|
-
*/
|
|
729
|
-
remove(key, callback) {
|
|
730
|
-
this.doBulk([{
|
|
731
|
-
type: "remove",
|
|
732
|
-
key
|
|
733
|
-
}], callback);
|
|
734
|
-
}
|
|
735
|
-
/**
|
|
736
|
-
* Performs multiple operations in one action
|
|
737
|
-
*
|
|
738
|
-
* @param {Object[]} bulk The set of operations that should be performed
|
|
739
|
-
* @param {Function} callback Standard callback method
|
|
740
|
-
* @param {Error} callback.err An error object, if any
|
|
741
|
-
*/
|
|
742
|
-
doBulk(bulk, callback) {
|
|
743
|
-
const queries = [];
|
|
744
|
-
bulk.forEach((operation) => {
|
|
745
|
-
const matches = /^([^:]+):([^:]+)$/.exec(operation.key);
|
|
746
|
-
if (operation.type === "set") {
|
|
747
|
-
queries.push({
|
|
748
|
-
query: `UPDATE "${this.settings.columnFamily}" SET data = ? WHERE key = ?`,
|
|
749
|
-
params: [operation.value, operation.key]
|
|
750
|
-
});
|
|
751
|
-
if (matches) queries.push({
|
|
752
|
-
query: `UPDATE "${this.settings.columnFamily}" SET data = ? WHERE key = ?`,
|
|
753
|
-
params: ["1", `ueberdb:keys:${matches[1]}`]
|
|
754
|
-
});
|
|
755
|
-
} else if (operation.type === "remove") {
|
|
756
|
-
queries.push({
|
|
757
|
-
query: `DELETE FROM "${this.settings.columnFamily}" WHERE key=?`,
|
|
758
|
-
params: [operation.key]
|
|
759
|
-
});
|
|
760
|
-
if (matches) queries.push({
|
|
761
|
-
query: `DELETE FROM "${this.settings.columnFamily}" WHERE key = ?`,
|
|
762
|
-
params: [`ueberdb:keys:${matches[1]}`]
|
|
763
|
-
});
|
|
764
|
-
}
|
|
765
|
-
});
|
|
766
|
-
this.client && this.client.batch(queries, { prepare: true }, callback);
|
|
767
|
-
}
|
|
768
|
-
/**
|
|
769
|
-
* Closes the Cassandra connection
|
|
770
|
-
*
|
|
771
|
-
* @param {Function} callback Standard callback method
|
|
772
|
-
* @param {Error} callback.err Error object in case something goes wrong
|
|
773
|
-
*/
|
|
774
|
-
close(callback) {
|
|
775
|
-
this.pool.shutdown(callback);
|
|
776
|
-
}
|
|
777
|
-
};
|
|
778
|
-
//#endregion
|
|
779
|
-
//#region databases/couch_db.ts
|
|
780
|
-
/**
|
|
781
|
-
* 2012 Max 'Azul' Wiehle
|
|
782
|
-
*
|
|
783
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
784
|
-
* you may not use this file except in compliance with the License.
|
|
785
|
-
* You may obtain a copy of the License at
|
|
786
|
-
*
|
|
787
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
788
|
-
*
|
|
789
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
790
|
-
* distributed under the License is distributed on an "AS-IS" BASIS,
|
|
791
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
792
|
-
* See the License for the specific language governing permissions and
|
|
793
|
-
* limitations under the License.
|
|
794
|
-
*/
|
|
795
|
-
var Couch_db = class extends AbstractDatabase {
|
|
796
|
-
agent;
|
|
797
|
-
db;
|
|
798
|
-
constructor(settings) {
|
|
799
|
-
super(settings);
|
|
800
|
-
this.agent = null;
|
|
801
|
-
this.db = null;
|
|
802
|
-
this.settings = settings;
|
|
803
|
-
this.settings.cache = 1e3;
|
|
804
|
-
this.settings.writeInterval = 100;
|
|
805
|
-
this.settings.json = false;
|
|
806
|
-
}
|
|
807
|
-
get isAsync() {
|
|
808
|
-
return true;
|
|
809
|
-
}
|
|
810
|
-
async init() {
|
|
811
|
-
this.agent = new http.default.Agent({
|
|
812
|
-
keepAlive: true,
|
|
813
|
-
maxSockets: this.settings.maxListeners || 1
|
|
814
|
-
});
|
|
815
|
-
const client = (0, nano.default)({
|
|
816
|
-
url: `http://${this.settings.host}:${this.settings.port}`,
|
|
817
|
-
requestDefaults: { agent: this.agent }
|
|
818
|
-
});
|
|
819
|
-
if (this.settings.user && this.settings.password) await client.auth(this.settings.user, this.settings.password);
|
|
820
|
-
try {
|
|
821
|
-
await client.db.get(this.settings.database);
|
|
822
|
-
} catch (err) {
|
|
823
|
-
if (err.statusCode !== 404) throw err;
|
|
824
|
-
await client.db.create(this.settings.database);
|
|
825
|
-
}
|
|
826
|
-
this.db = client.use(this.settings.database);
|
|
827
|
-
}
|
|
828
|
-
async get(key) {
|
|
829
|
-
let doc;
|
|
830
|
-
try {
|
|
831
|
-
if (this.db) doc = await this.db.get(key);
|
|
832
|
-
} catch (err) {
|
|
833
|
-
if (err.statusCode === 404) return null;
|
|
834
|
-
throw err;
|
|
835
|
-
}
|
|
836
|
-
if (doc && "value" in doc) return doc.value;
|
|
837
|
-
return "";
|
|
838
|
-
}
|
|
839
|
-
async findKeys(key, notKey) {
|
|
840
|
-
const pfxLen = key.indexOf("*");
|
|
841
|
-
if (!this.db) return;
|
|
842
|
-
const pfx = pfxLen < 0 ? key : key.slice(0, pfxLen);
|
|
843
|
-
return (await this.db.find({
|
|
844
|
-
selector: { _id: pfxLen < 0 ? pfx : {
|
|
845
|
-
$gte: pfx,
|
|
846
|
-
$lte: `${pfx}\ufff0`,
|
|
847
|
-
$regex: this.createFindRegex(key, notKey).source
|
|
848
|
-
} },
|
|
849
|
-
fields: ["_id"]
|
|
850
|
-
})).docs.map((doc) => doc._id);
|
|
851
|
-
}
|
|
852
|
-
async set(key, value) {
|
|
853
|
-
let doc;
|
|
854
|
-
if (!this.db) return;
|
|
855
|
-
try {
|
|
856
|
-
doc = await this.db.get(key);
|
|
857
|
-
} catch (err) {
|
|
858
|
-
if (err.statusCode !== 404) throw err;
|
|
859
|
-
}
|
|
860
|
-
await this.db.insert({
|
|
861
|
-
_id: key,
|
|
862
|
-
value,
|
|
863
|
-
...doc == null ? {} : { _rev: doc._rev }
|
|
864
|
-
});
|
|
865
|
-
}
|
|
866
|
-
async remove(key) {
|
|
867
|
-
let header;
|
|
868
|
-
if (!this.db) return;
|
|
869
|
-
try {
|
|
870
|
-
header = await this.db.head(key);
|
|
871
|
-
} catch (err) {
|
|
872
|
-
if (err.statusCode === 404) return;
|
|
873
|
-
throw err;
|
|
874
|
-
}
|
|
875
|
-
const etag = JSON.parse(header.etag);
|
|
876
|
-
await this.db.destroy(key, etag);
|
|
877
|
-
}
|
|
878
|
-
async doBulk(bulk) {
|
|
879
|
-
if (!this.db) return;
|
|
880
|
-
const keys = bulk.map((op) => op.key);
|
|
881
|
-
const revs = {};
|
|
882
|
-
for (const { key, value } of (await this.db.fetchRevs({ keys })).rows) if (value != null) revs[key] = value.rev;
|
|
883
|
-
const setters = [];
|
|
884
|
-
for (const item of bulk) {
|
|
885
|
-
const set = {
|
|
886
|
-
_id: item.key,
|
|
887
|
-
_rev: void 0,
|
|
888
|
-
_deleted: false,
|
|
889
|
-
value: ""
|
|
890
|
-
};
|
|
891
|
-
if (revs[item.key] != null) set._rev = revs[item.key];
|
|
892
|
-
if (item.type === "set") set.value = item.value;
|
|
893
|
-
if (item.type === "remove") set._deleted = true;
|
|
894
|
-
setters.push(set);
|
|
895
|
-
}
|
|
896
|
-
await this.db.bulk({ docs: setters });
|
|
897
|
-
}
|
|
898
|
-
async close() {
|
|
899
|
-
this.db = null;
|
|
900
|
-
if (this.agent) this.agent.destroy();
|
|
901
|
-
this.agent = null;
|
|
902
|
-
}
|
|
903
|
-
};
|
|
904
|
-
//#endregion
|
|
905
|
-
//#region databases/dirty_db.ts
|
|
906
|
-
/**
|
|
907
|
-
* 2011 Peter 'Pita' Martischka
|
|
908
|
-
*
|
|
909
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
910
|
-
* you may not use this file except in compliance with the License.
|
|
911
|
-
* You may obtain a copy of the License at
|
|
912
|
-
*
|
|
913
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
914
|
-
*
|
|
915
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
916
|
-
* distributed under the License is distributed on an "AS-IS" BASIS,
|
|
917
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
918
|
-
* See the License for the specific language governing permissions and
|
|
919
|
-
* limitations under the License.
|
|
920
|
-
*/
|
|
921
|
-
var dirty_db_default = class extends AbstractDatabase {
|
|
922
|
-
db;
|
|
923
|
-
constructor(settings) {
|
|
924
|
-
super(settings);
|
|
925
|
-
this.db = null;
|
|
926
|
-
if (!settings || !settings.filename) settings = { filename: null };
|
|
927
|
-
this.settings = settings;
|
|
928
|
-
this.settings.cache = 0;
|
|
929
|
-
this.settings.writeInterval = 0;
|
|
930
|
-
this.settings.json = false;
|
|
931
|
-
}
|
|
932
|
-
init(callback) {
|
|
933
|
-
this.db = new dirty_ts.default(this.settings.filename);
|
|
934
|
-
this.db.on("load", () => {
|
|
935
|
-
callback();
|
|
936
|
-
});
|
|
937
|
-
}
|
|
938
|
-
get(key, callback) {
|
|
939
|
-
callback(null, this.db.get(key));
|
|
940
|
-
}
|
|
941
|
-
findKeys(key, notKey, callback) {
|
|
942
|
-
const keys = [];
|
|
943
|
-
const regex = this.createFindRegex(key, notKey);
|
|
944
|
-
this.db.forEach((key) => {
|
|
945
|
-
if (key.search(regex) !== -1) keys.push(key);
|
|
946
|
-
});
|
|
947
|
-
callback(null, keys);
|
|
948
|
-
}
|
|
949
|
-
set(key, value, callback) {
|
|
950
|
-
this.db.set(key, value, callback);
|
|
951
|
-
}
|
|
952
|
-
remove(key, callback) {
|
|
953
|
-
this.db.rm(key, callback);
|
|
954
|
-
}
|
|
955
|
-
close(callback) {
|
|
956
|
-
this.db.close();
|
|
957
|
-
this.db = null;
|
|
958
|
-
if (callback) callback();
|
|
959
|
-
}
|
|
960
|
-
};
|
|
961
|
-
//#endregion
|
|
962
|
-
//#region databases/dirty_git_db.ts
|
|
963
|
-
/**
|
|
964
|
-
* 2011 Peter 'Pita' Martischka
|
|
965
|
-
*
|
|
966
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
967
|
-
* you may not use this file except in compliance with the License.
|
|
968
|
-
* You may obtain a copy of the License at
|
|
969
|
-
*
|
|
970
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
971
|
-
*
|
|
972
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
973
|
-
* distributed under the License is distributed on an "AS-IS" BASIS,
|
|
974
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
975
|
-
* See the License for the specific language governing permissions and
|
|
976
|
-
* limitations under the License.
|
|
977
|
-
*/
|
|
978
|
-
var dirty_git_db_default = class extends AbstractDatabase {
|
|
979
|
-
db;
|
|
980
|
-
constructor(settings) {
|
|
981
|
-
super(settings);
|
|
982
|
-
this.db = null;
|
|
983
|
-
if (!settings || !settings.filename) settings = {};
|
|
984
|
-
this.settings = settings;
|
|
985
|
-
this.settings.cache = 0;
|
|
986
|
-
this.settings.writeInterval = 0;
|
|
987
|
-
this.settings.json = false;
|
|
988
|
-
}
|
|
989
|
-
init(callback) {
|
|
990
|
-
this.db = new dirty_ts.default(this.settings.filename);
|
|
991
|
-
this.db.on("load", (err) => {
|
|
992
|
-
callback();
|
|
993
|
-
});
|
|
994
|
-
}
|
|
995
|
-
get(key, callback) {
|
|
996
|
-
callback(null, this.db.get(key));
|
|
997
|
-
}
|
|
998
|
-
findKeys(key, notKey, callback) {
|
|
999
|
-
const keys = [];
|
|
1000
|
-
const regex = this.createFindRegex(key, notKey);
|
|
1001
|
-
this.db.forEach((key, val) => {
|
|
1002
|
-
if (key.search(regex) !== -1) keys.push(key);
|
|
1003
|
-
});
|
|
1004
|
-
callback(null, keys);
|
|
1005
|
-
}
|
|
1006
|
-
set(key, value, callback) {
|
|
1007
|
-
this.db.set(key, value, callback);
|
|
1008
|
-
(0, simple_git.simpleGit)((0, node_path.dirname)(this.settings.filename)).silent(true).add("./*.db").commit("Automated commit...").push([
|
|
1009
|
-
"-u",
|
|
1010
|
-
"origin",
|
|
1011
|
-
"master"
|
|
1012
|
-
], () => console.debug("Stored git commit"));
|
|
1013
|
-
}
|
|
1014
|
-
remove(key, callback) {
|
|
1015
|
-
this.db.rm(key, callback);
|
|
1016
|
-
}
|
|
1017
|
-
close(callback) {
|
|
1018
|
-
this.db.close();
|
|
1019
|
-
if (callback) callback();
|
|
1020
|
-
}
|
|
1021
|
-
};
|
|
1022
|
-
//#endregion
|
|
1023
|
-
//#region databases/elasticsearch_db.ts
|
|
1024
|
-
/**
|
|
1025
|
-
* 2015 Visionist, Inc.
|
|
1026
|
-
*
|
|
1027
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
1028
|
-
* you may not use this file except in compliance with the License.
|
|
1029
|
-
* You may obtain a copy of the License at
|
|
1030
|
-
*
|
|
1031
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
1032
|
-
*
|
|
1033
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
1034
|
-
* distributed under the License is distributed on an "AS-IS" BASIS,
|
|
1035
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
1036
|
-
* See the License for the specific language governing permissions and
|
|
1037
|
-
* limitations under the License.
|
|
1038
|
-
*/
|
|
1039
|
-
const schema = "2";
|
|
1040
|
-
const keyToId = (key) => {
|
|
1041
|
-
const keyBuf = buffer.Buffer.from(key);
|
|
1042
|
-
return keyBuf.length > 512 ? (0, crypto.createHash)("sha512").update(keyBuf).digest("hex") : key;
|
|
1043
|
-
};
|
|
1044
|
-
const mappings = { properties: {
|
|
1045
|
-
key: { type: "wildcard" },
|
|
1046
|
-
value: {
|
|
1047
|
-
type: "object",
|
|
1048
|
-
enabled: false
|
|
1049
|
-
}
|
|
1050
|
-
} };
|
|
1051
|
-
const legacyDocToSchema2Key = (index, id, type, v1BaseIndex) => {
|
|
1052
|
-
const legacyType = typeof type === "string" && type !== "" && type !== "_doc" ? type : null;
|
|
1053
|
-
if (v1BaseIndex && index !== v1BaseIndex) {
|
|
1054
|
-
const parts = index.slice(v1BaseIndex.length + 1).split("-");
|
|
1055
|
-
if (parts.length !== 2) throw new Error(`unable to migrate records from index ${index} due to data ambiguity`);
|
|
1056
|
-
if (legacyType != null) return `${parts[0]}:${decodeURIComponent(legacyType)}:${parts[1]}:${id}`;
|
|
1057
|
-
const idParts = id.split(":");
|
|
1058
|
-
if (idParts.length !== 2) throw new Error(`unable to migrate records from index ${index} due to data ambiguity`);
|
|
1059
|
-
return `${parts[0]}:${idParts[0]}:${parts[1]}:${idParts[1]}`;
|
|
1060
|
-
}
|
|
1061
|
-
if (legacyType != null) return `${legacyType}:${id}`;
|
|
1062
|
-
const idParts = id.split(":");
|
|
1063
|
-
if (idParts.length !== 2) throw new Error(`unable to migrate records from index ${index} due to missing legacy type metadata`);
|
|
1064
|
-
return `${idParts[0]}:${idParts[1]}`;
|
|
1065
|
-
};
|
|
1066
|
-
const migrateToSchema2 = async (client, v1BaseIndex, v2Index, logger) => {
|
|
1067
|
-
let recordsMigratedLastLogged = 0;
|
|
1068
|
-
let recordsMigrated = 0;
|
|
1069
|
-
const totals = /* @__PURE__ */ new Map();
|
|
1070
|
-
logger.info(`Attempting elasticsearch record migration from schema v1 at base index ${v1BaseIndex} to schema v2 at index ${v2Index}...`);
|
|
1071
|
-
const indices = await client.indices.get({ index: [v1BaseIndex, `${v1BaseIndex}-*-*`] });
|
|
1072
|
-
const scrollIds = /* @__PURE__ */ new Map();
|
|
1073
|
-
const q = [];
|
|
1074
|
-
try {
|
|
1075
|
-
for (const index of Object.keys(indices)) {
|
|
1076
|
-
const res = await client.search({
|
|
1077
|
-
index,
|
|
1078
|
-
scroll: "10m"
|
|
1079
|
-
});
|
|
1080
|
-
scrollIds.set(index, res._scroll_id);
|
|
1081
|
-
q.push({
|
|
1082
|
-
index,
|
|
1083
|
-
res
|
|
1084
|
-
});
|
|
1085
|
-
}
|
|
1086
|
-
while (q.length) {
|
|
1087
|
-
const { index, res: { hits: { hits, total: { value: total } } } } = q.shift();
|
|
1088
|
-
if (hits.length === 0) continue;
|
|
1089
|
-
totals.set(index, total);
|
|
1090
|
-
const body = [];
|
|
1091
|
-
for (const { _id, _type, _source: { val } } of hits) {
|
|
1092
|
-
const key = legacyDocToSchema2Key(index, _id, _type, v1BaseIndex);
|
|
1093
|
-
body.push({ index: { _id: keyToId(key) } }, {
|
|
1094
|
-
key,
|
|
1095
|
-
value: JSON.parse(val)
|
|
1096
|
-
});
|
|
1097
|
-
}
|
|
1098
|
-
await client.bulk({
|
|
1099
|
-
index: v2Index,
|
|
1100
|
-
body
|
|
1101
|
-
});
|
|
1102
|
-
recordsMigrated += hits.length;
|
|
1103
|
-
if (Math.floor(recordsMigrated / 100) > Math.floor(recordsMigratedLastLogged / 100)) {
|
|
1104
|
-
const total = [...totals.values()].reduce((a, b) => a + b, 0);
|
|
1105
|
-
logger.info(`Migrated ${recordsMigrated} records out of ${total}`);
|
|
1106
|
-
recordsMigratedLastLogged = recordsMigrated;
|
|
1107
|
-
}
|
|
1108
|
-
q.push({
|
|
1109
|
-
index,
|
|
1110
|
-
res: await client.scroll({
|
|
1111
|
-
scroll: "5m",
|
|
1112
|
-
scroll_id: scrollIds.get(index)
|
|
1113
|
-
})
|
|
1114
|
-
});
|
|
1115
|
-
}
|
|
1116
|
-
logger.info(`Finished migrating ${recordsMigrated} records`);
|
|
1117
|
-
} finally {
|
|
1118
|
-
await Promise.all([...scrollIds.values()].map((scrollId) => client.clearScroll({ scroll_id: scrollId })));
|
|
1119
|
-
}
|
|
1120
|
-
};
|
|
1121
|
-
var elasticsearch_db_default = class extends AbstractDatabase {
|
|
1122
|
-
_client;
|
|
1123
|
-
_index;
|
|
1124
|
-
_indexClean;
|
|
1125
|
-
_q;
|
|
1126
|
-
constructor(settings) {
|
|
1127
|
-
super(settings);
|
|
1128
|
-
this._client = null;
|
|
1129
|
-
this.settings = {
|
|
1130
|
-
host: "127.0.0.1",
|
|
1131
|
-
port: "9200",
|
|
1132
|
-
base_index: "ueberes",
|
|
1133
|
-
migrate_to_newer_schema: false,
|
|
1134
|
-
api: "7.6",
|
|
1135
|
-
...settings || {},
|
|
1136
|
-
json: false
|
|
1137
|
-
};
|
|
1138
|
-
this._index = `${this.settings.base_index}_s${schema}`;
|
|
1139
|
-
this._q = { index: this._index };
|
|
1140
|
-
this._indexClean = true;
|
|
1141
|
-
}
|
|
1142
|
-
get isAsync() {
|
|
1143
|
-
return true;
|
|
1144
|
-
}
|
|
1145
|
-
async _refreshIndex() {
|
|
1146
|
-
if (this._indexClean) return;
|
|
1147
|
-
this._indexClean = true;
|
|
1148
|
-
await this._client.indices.refresh(this._q);
|
|
1149
|
-
}
|
|
1150
|
-
/**
|
|
1151
|
-
* Initialize the elasticsearch client, then ping the server to ensure that a
|
|
1152
|
-
* connection was made.
|
|
1153
|
-
*/
|
|
1154
|
-
async init() {
|
|
1155
|
-
const client = new _elastic_elasticsearch.Client({ node: `http://${this.settings.host}:${this.settings.port}` });
|
|
1156
|
-
await client.ping();
|
|
1157
|
-
if (!await client.indices.exists({ index: this._index })) {
|
|
1158
|
-
let tmpIndex;
|
|
1159
|
-
const exists = await client.indices.exists({ index: this.settings.base_index });
|
|
1160
|
-
if (exists && !this.settings.migrate_to_newer_schema) throw new Error(`Data exists under the legacy index (schema) named ${this.settings.base_index}. Set migrate_to_newer_schema to true to copy the existing data to a new index named ${this._index}.`);
|
|
1161
|
-
let attempt = 0;
|
|
1162
|
-
while (true) {
|
|
1163
|
-
tmpIndex = `${this._index}_${exists ? "migrate_attempt_" : "i"}${attempt++}`;
|
|
1164
|
-
if (!await client.indices.exists({ index: tmpIndex })) break;
|
|
1165
|
-
}
|
|
1166
|
-
await client.indices.create({
|
|
1167
|
-
index: tmpIndex,
|
|
1168
|
-
mappings
|
|
1169
|
-
});
|
|
1170
|
-
if (exists) await migrateToSchema2(client, this.settings.base_index, tmpIndex, this.logger);
|
|
1171
|
-
await client.indices.putAlias({
|
|
1172
|
-
index: tmpIndex,
|
|
1173
|
-
name: this._index
|
|
1174
|
-
});
|
|
1175
|
-
}
|
|
1176
|
-
const indices = Object.values(await client.indices.get({ index: this._index }));
|
|
1177
|
-
(0, assert.equal)(indices.length, 1);
|
|
1178
|
-
try {
|
|
1179
|
-
assert.default.deepEqual(indices[0].mappings, mappings);
|
|
1180
|
-
} catch (err) {
|
|
1181
|
-
this.logger.warn(`Index ${this._index} mappings does not match expected; attempting to use index anyway. Details: ${err}`);
|
|
1182
|
-
}
|
|
1183
|
-
this._client = client;
|
|
1184
|
-
}
|
|
1185
|
-
/**
|
|
1186
|
-
* This function provides read functionality to the database.
|
|
1187
|
-
*
|
|
1188
|
-
* @param {String} key Key
|
|
1189
|
-
*/
|
|
1190
|
-
async get(key) {
|
|
1191
|
-
const res = await this._client.get({
|
|
1192
|
-
...this._q,
|
|
1193
|
-
id: keyToId(key)
|
|
1194
|
-
}, { ignore: [404] });
|
|
1195
|
-
if (!res.found) return null;
|
|
1196
|
-
return res._source.value;
|
|
1197
|
-
}
|
|
1198
|
-
/**
|
|
1199
|
-
* @param key Search key, which uses an asterisk (*) as the wild card.
|
|
1200
|
-
* @param notKey Used to filter the result set
|
|
1201
|
-
*/
|
|
1202
|
-
async findKeys(key, notKey) {
|
|
1203
|
-
await this._refreshIndex();
|
|
1204
|
-
const q = {
|
|
1205
|
-
...this._q,
|
|
1206
|
-
body: { query: { bool: {
|
|
1207
|
-
filter: { wildcard: { key: { value: key } } },
|
|
1208
|
-
...notKey == null ? {} : { must_not: { wildcard: { key: { value: notKey } } } }
|
|
1209
|
-
} } }
|
|
1210
|
-
};
|
|
1211
|
-
const { hits } = await this._client.search(q);
|
|
1212
|
-
return hits.hits.map((h) => h._source.key);
|
|
1213
|
-
}
|
|
1214
|
-
/**
|
|
1215
|
-
* This function provides write functionality to the database.
|
|
1216
|
-
*
|
|
1217
|
-
* @param {String} key Record identifier.
|
|
1218
|
-
* @param {JSON|String} value The value to store in the database.
|
|
1219
|
-
*/
|
|
1220
|
-
async set(key, value) {
|
|
1221
|
-
this._indexClean = false;
|
|
1222
|
-
await this._client.index({
|
|
1223
|
-
...this._q,
|
|
1224
|
-
id: keyToId(key),
|
|
1225
|
-
body: {
|
|
1226
|
-
key,
|
|
1227
|
-
value
|
|
1228
|
-
}
|
|
1229
|
-
});
|
|
1230
|
-
}
|
|
1231
|
-
/**
|
|
1232
|
-
* This function provides delete functionality to the database.
|
|
1233
|
-
*
|
|
1234
|
-
* The index, type, and ID will be parsed from the key, and this document will
|
|
1235
|
-
* be deleted from the database.
|
|
1236
|
-
*
|
|
1237
|
-
* @param {String} key Record identifier.
|
|
1238
|
-
*/
|
|
1239
|
-
async remove(key) {
|
|
1240
|
-
this._indexClean = false;
|
|
1241
|
-
await this._client.delete({
|
|
1242
|
-
...this._q,
|
|
1243
|
-
id: keyToId(key)
|
|
1244
|
-
}, { ignore: [404] });
|
|
1245
|
-
}
|
|
1246
|
-
/**
|
|
1247
|
-
* This uses the bulk upload functionality of elasticsearch (url:port/_bulk).
|
|
1248
|
-
*
|
|
1249
|
-
* The CacheAndBufferLayer will periodically (every this.settings.writeInterval)
|
|
1250
|
-
* flush writes that have already been done in the local cache out to the database.
|
|
1251
|
-
*
|
|
1252
|
-
* @param {Array} bulk An array of JSON data in the format:
|
|
1253
|
-
* {"type":type, "key":key, "value":value}
|
|
1254
|
-
*/
|
|
1255
|
-
async doBulk(bulk) {
|
|
1256
|
-
const operations = [];
|
|
1257
|
-
for (const { type, key, value } of bulk) {
|
|
1258
|
-
this._indexClean = false;
|
|
1259
|
-
switch (type) {
|
|
1260
|
-
case "set":
|
|
1261
|
-
operations.push({ index: { _id: keyToId(key) } });
|
|
1262
|
-
operations.push({
|
|
1263
|
-
key,
|
|
1264
|
-
value
|
|
1265
|
-
});
|
|
1266
|
-
break;
|
|
1267
|
-
case "remove":
|
|
1268
|
-
operations.push({ delete: { _id: keyToId(key) } });
|
|
1269
|
-
break;
|
|
1270
|
-
default:
|
|
1271
|
-
}
|
|
1272
|
-
}
|
|
1273
|
-
await this._client.bulk({
|
|
1274
|
-
...this._q,
|
|
1275
|
-
body: operations
|
|
1276
|
-
});
|
|
1277
|
-
}
|
|
1278
|
-
async close() {
|
|
1279
|
-
if (this._client != null) this._client.close();
|
|
1280
|
-
this._client = null;
|
|
1281
|
-
}
|
|
1282
|
-
};
|
|
1283
|
-
//#endregion
|
|
1284
|
-
//#region databases/memory_db.ts
|
|
1285
|
-
var MemoryDB = class extends AbstractDatabase {
|
|
1286
|
-
_data;
|
|
1287
|
-
constructor(settings) {
|
|
1288
|
-
super(settings);
|
|
1289
|
-
this.settings = settings;
|
|
1290
|
-
settings.json = false;
|
|
1291
|
-
settings.cache = 0;
|
|
1292
|
-
settings.writeInterval = 0;
|
|
1293
|
-
this._data = null;
|
|
1294
|
-
}
|
|
1295
|
-
get isAsync() {
|
|
1296
|
-
return true;
|
|
1297
|
-
}
|
|
1298
|
-
close() {
|
|
1299
|
-
this._data = null;
|
|
1300
|
-
}
|
|
1301
|
-
findKeys(key, notKey) {
|
|
1302
|
-
const regex = this.createFindRegex(key, notKey);
|
|
1303
|
-
return [...this._data.keys()].filter((k) => regex.test(k));
|
|
1304
|
-
}
|
|
1305
|
-
get(key) {
|
|
1306
|
-
return this._data.get(key);
|
|
1307
|
-
}
|
|
1308
|
-
init() {
|
|
1309
|
-
this._data = this.settings.data || /* @__PURE__ */ new Map();
|
|
1310
|
-
}
|
|
1311
|
-
remove(key) {
|
|
1312
|
-
this._data.delete(key);
|
|
1313
|
-
}
|
|
1314
|
-
set(key, value) {
|
|
1315
|
-
this._data.set(key, value);
|
|
1316
|
-
}
|
|
1317
|
-
};
|
|
1318
|
-
//#endregion
|
|
1319
|
-
//#region databases/mock_db.ts
|
|
1320
|
-
var mock_db_default = class extends events.default.EventEmitter {
|
|
1321
|
-
settings;
|
|
1322
|
-
mock;
|
|
1323
|
-
constructor(settings) {
|
|
1324
|
-
super();
|
|
1325
|
-
this.settings = {
|
|
1326
|
-
writeInterval: 1,
|
|
1327
|
-
...settings
|
|
1328
|
-
};
|
|
1329
|
-
settings.mock = this;
|
|
1330
|
-
this.settings = settings;
|
|
1331
|
-
}
|
|
1332
|
-
close(cb) {
|
|
1333
|
-
this.emit("close", cb);
|
|
1334
|
-
}
|
|
1335
|
-
doBulk(ops, cb) {
|
|
1336
|
-
this.emit("doBulk", ops, cb);
|
|
1337
|
-
}
|
|
1338
|
-
findKeys(key, notKey, cb) {
|
|
1339
|
-
this.emit("findKeys", key, notKey, cb);
|
|
1340
|
-
}
|
|
1341
|
-
get(key, cb) {
|
|
1342
|
-
this.emit("get", key, cb);
|
|
1343
|
-
}
|
|
1344
|
-
async init(cb) {
|
|
1345
|
-
this.emit("init", cb());
|
|
1346
|
-
}
|
|
1347
|
-
remove(key, cb) {
|
|
1348
|
-
this.emit("remove", key, cb);
|
|
1349
|
-
}
|
|
1350
|
-
set(key, value, cb) {
|
|
1351
|
-
this.emit("set", key, value, cb);
|
|
1352
|
-
}
|
|
1353
|
-
};
|
|
1354
|
-
//#endregion
|
|
1355
|
-
//#region databases/mongodb_db.ts
|
|
1356
|
-
/**
|
|
1357
|
-
* 2020 Sylchauf
|
|
1358
|
-
*
|
|
1359
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
1360
|
-
* you may not use this file except in compliance with the License.
|
|
1361
|
-
* You may obtain a copy of the License at
|
|
1362
|
-
*
|
|
1363
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
1364
|
-
*
|
|
1365
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
1366
|
-
* distributed under the License is distributed on an "AS-IS" BASIS,
|
|
1367
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
1368
|
-
* See the License for the specific language governing permissions and
|
|
1369
|
-
* limitations under the License.
|
|
1370
|
-
*/
|
|
1371
|
-
var mongodb_db_default = class extends AbstractDatabase {
|
|
1372
|
-
interval;
|
|
1373
|
-
database;
|
|
1374
|
-
client;
|
|
1375
|
-
collection;
|
|
1376
|
-
constructor(settings) {
|
|
1377
|
-
super(settings);
|
|
1378
|
-
this.settings = settings;
|
|
1379
|
-
if (!this.settings.url) throw new Error("You must specify a mongodb url");
|
|
1380
|
-
if (this.settings.database == null) this.settings.database = this.settings.dbName;
|
|
1381
|
-
if (!this.settings.collection) this.settings.collection = "ueberdb";
|
|
1382
|
-
}
|
|
1383
|
-
clearPing() {
|
|
1384
|
-
if (this.interval) clearInterval(this.interval[Symbol.toPrimitive]());
|
|
1385
|
-
}
|
|
1386
|
-
schedulePing() {
|
|
1387
|
-
this.clearPing();
|
|
1388
|
-
this.interval = setInterval(() => {
|
|
1389
|
-
this.database.command({ ping: 1 });
|
|
1390
|
-
}, 1e4);
|
|
1391
|
-
}
|
|
1392
|
-
init(callback) {
|
|
1393
|
-
mongodb.MongoClient.connect(this.settings.url).then((v) => {
|
|
1394
|
-
this.client = v;
|
|
1395
|
-
this.database = v.db(this.settings.database);
|
|
1396
|
-
this.schedulePing();
|
|
1397
|
-
this.collection = this.database.collection(this.settings.collection);
|
|
1398
|
-
callback(null);
|
|
1399
|
-
}).catch((v) => {
|
|
1400
|
-
callback(v);
|
|
1401
|
-
});
|
|
1402
|
-
}
|
|
1403
|
-
get(key, callback) {
|
|
1404
|
-
this.collection.findOne({ _id: key }).then((v) => {
|
|
1405
|
-
callback(null, v && v.value);
|
|
1406
|
-
}).catch((v) => {
|
|
1407
|
-
console.log(v);
|
|
1408
|
-
callback(v);
|
|
1409
|
-
});
|
|
1410
|
-
this.schedulePing();
|
|
1411
|
-
}
|
|
1412
|
-
findKeys(key, notKey, callback) {
|
|
1413
|
-
const selector = { $and: [{ _id: { $regex: `${key.replace(/\*/g, "")}` } }] };
|
|
1414
|
-
if (notKey) selector.$and.push({ _id: { $not: { $regex: `${notKey.replace(/\*/g, "")}` } } });
|
|
1415
|
-
this.collection.find(selector).map((i) => i._id).toArray().then((r) => {
|
|
1416
|
-
callback(null, r);
|
|
1417
|
-
}).catch((v) => callback(v));
|
|
1418
|
-
this.schedulePing();
|
|
1419
|
-
}
|
|
1420
|
-
set(key, value, callback) {
|
|
1421
|
-
if (key.length > 100) callback("Your Key can only be 100 chars");
|
|
1422
|
-
else this.collection.updateMany({ _id: key }, { $set: { value } }, { upsert: true }).then(() => callback(null)).catch((v) => callback(v));
|
|
1423
|
-
this.schedulePing();
|
|
1424
|
-
}
|
|
1425
|
-
remove(key, callback) {
|
|
1426
|
-
this.collection.deleteOne({ _id: key }).then((r) => callback(null, r)).catch((v) => callback(v));
|
|
1427
|
-
this.schedulePing();
|
|
1428
|
-
}
|
|
1429
|
-
doBulk(bulk, callback) {
|
|
1430
|
-
const bulkMongo = this.collection.initializeOrderedBulkOp();
|
|
1431
|
-
for (const i in bulk) if (bulk[i].type === "set") bulkMongo.find({ _id: bulk[i].key }).upsert().updateOne({ $set: { value: bulk[i].value } });
|
|
1432
|
-
else if (bulk[i].type === "remove") bulkMongo.find({ _id: bulk[i].key }).deleteOne();
|
|
1433
|
-
bulkMongo.execute().then((res) => {
|
|
1434
|
-
callback(null, res);
|
|
1435
|
-
}).catch((error) => {
|
|
1436
|
-
callback(error);
|
|
1437
|
-
});
|
|
1438
|
-
this.schedulePing();
|
|
1439
|
-
}
|
|
1440
|
-
close(callback) {
|
|
1441
|
-
this.clearPing();
|
|
1442
|
-
this.client.close().then((r) => callback(r));
|
|
1443
|
-
}
|
|
1444
|
-
};
|
|
1445
|
-
//#endregion
|
|
1446
|
-
//#region databases/mssql_db.ts
|
|
1447
|
-
/**
|
|
1448
|
-
* 2019 - exspecto@gmail.com
|
|
1449
|
-
*
|
|
1450
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
1451
|
-
* you may not use this file except in compliance with the License.
|
|
1452
|
-
* You may obtain a copy of the License at
|
|
1453
|
-
*
|
|
1454
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
1455
|
-
*
|
|
1456
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
1457
|
-
* distributed under the License is distributed on an "AS-IS" BASIS,
|
|
1458
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
1459
|
-
* See the License for the specific language governing permissions and
|
|
1460
|
-
* limitations under the License.
|
|
1461
|
-
*
|
|
1462
|
-
*
|
|
1463
|
-
* Note: This requires MS SQL Server >= 2008 due to the usage of the MERGE statement
|
|
1464
|
-
*
|
|
1465
|
-
*/
|
|
1466
|
-
var MSSQL = class extends AbstractDatabase {
|
|
1467
|
-
db;
|
|
1468
|
-
constructor(settings) {
|
|
1469
|
-
super(settings);
|
|
1470
|
-
settings = settings || {};
|
|
1471
|
-
if (settings.json != null) settings.parseJSON = settings.json;
|
|
1472
|
-
settings.requestTimeout = 3e5;
|
|
1473
|
-
settings.server = settings.host;
|
|
1474
|
-
this.settings = settings;
|
|
1475
|
-
this.settings.cache = 0;
|
|
1476
|
-
this.settings.writeInterval = 0;
|
|
1477
|
-
}
|
|
1478
|
-
init(callback) {
|
|
1479
|
-
const sqlCreate = "IF OBJECT_ID(N'dbo.store', N'U') IS NULL BEGIN CREATE TABLE [store] ( [key] NVARCHAR(100) PRIMARY KEY, [value] NTEXT NOT NULL ); END";
|
|
1480
|
-
new mssql.default.ConnectionPool(this.settings).connect().then((pool) => {
|
|
1481
|
-
this.db = pool;
|
|
1482
|
-
new mssql.default.Request(this.db).query(sqlCreate, (err) => {
|
|
1483
|
-
callback(err);
|
|
1484
|
-
});
|
|
1485
|
-
this.db.on("error", (err) => {
|
|
1486
|
-
console.log(err);
|
|
1487
|
-
});
|
|
1488
|
-
});
|
|
1489
|
-
}
|
|
1490
|
-
get(key, callback) {
|
|
1491
|
-
const request = new mssql.default.Request(this.db);
|
|
1492
|
-
request.input("key", mssql.default.NVarChar(100), key);
|
|
1493
|
-
request.query("SELECT [value] FROM [store] WHERE [key] = @key", (err, results) => {
|
|
1494
|
-
let value = null;
|
|
1495
|
-
if (!err && results && results.rowsAffected[0] === 1) value = results.recordset[0].value;
|
|
1496
|
-
callback(err, value);
|
|
1497
|
-
});
|
|
1498
|
-
}
|
|
1499
|
-
findKeys(key, notKey, callback) {
|
|
1500
|
-
const request = new mssql.default.Request(this.db);
|
|
1501
|
-
let query = "SELECT [key] FROM [store] WHERE [key] LIKE @key";
|
|
1502
|
-
key = key.replace(/\*/g, "%");
|
|
1503
|
-
request.input("key", mssql.default.NVarChar(100), key);
|
|
1504
|
-
if (notKey != null) {
|
|
1505
|
-
notKey = notKey.replace(/\*/g, "%");
|
|
1506
|
-
request.input("notkey", mssql.default.NVarChar(100), notKey);
|
|
1507
|
-
query += " AND [key] NOT LIKE @notkey";
|
|
1508
|
-
}
|
|
1509
|
-
request.query(query, (err, results) => {
|
|
1510
|
-
const value = [];
|
|
1511
|
-
if (!err && results && results.rowsAffected[0] > 0) for (let i = 0; i < results.recordset.length; i++) value.push(results.recordset[i].key);
|
|
1512
|
-
callback(err, value);
|
|
1513
|
-
});
|
|
1514
|
-
}
|
|
1515
|
-
set(key, value, callback) {
|
|
1516
|
-
const request = new mssql.default.Request(this.db);
|
|
1517
|
-
if (key.length > 100) callback("Your Key can only be 100 chars");
|
|
1518
|
-
else {
|
|
1519
|
-
const query = "MERGE [store] t USING (SELECT @key [key], @value [value]) s ON t.[key] = s.[key] WHEN MATCHED AND s.[value] IS NOT NULL THEN UPDATE SET t.[value] = s.[value] WHEN NOT MATCHED THEN INSERT ([key], [value]) VALUES (s.[key], s.[value]);";
|
|
1520
|
-
request.input("key", mssql.default.NVarChar(100), key);
|
|
1521
|
-
request.input("value", mssql.default.NText, value);
|
|
1522
|
-
request.query(query, (err, info) => {
|
|
1523
|
-
callback(err ? err.toString() : "");
|
|
1524
|
-
});
|
|
1525
|
-
}
|
|
1526
|
-
}
|
|
1527
|
-
remove(key, callback) {
|
|
1528
|
-
const request = new mssql.default.Request(this.db);
|
|
1529
|
-
request.input("key", mssql.default.NVarChar(100), key);
|
|
1530
|
-
request.query("DELETE FROM [store] WHERE [key] = @key", callback);
|
|
1531
|
-
}
|
|
1532
|
-
doBulk(bulk, callback) {
|
|
1533
|
-
const maxInserts = 100;
|
|
1534
|
-
const request = new mssql.default.Request(this.db);
|
|
1535
|
-
let firstReplace = true;
|
|
1536
|
-
let firstRemove = true;
|
|
1537
|
-
const replacements = [];
|
|
1538
|
-
let removeSQL = "DELETE FROM [store] WHERE [key] IN (";
|
|
1539
|
-
for (const i in bulk) if (bulk[i].type === "set") {
|
|
1540
|
-
if (firstReplace) {
|
|
1541
|
-
replacements.push("BEGIN TRANSACTION;");
|
|
1542
|
-
firstReplace = false;
|
|
1543
|
-
} else if (Number(i) % maxInserts === 0) replacements.push("\nCOMMIT TRANSACTION;\nBEGIN TRANSACTION;\n");
|
|
1544
|
-
replacements.push(`MERGE [store] t USING (SELECT '${bulk[i].key}' [key], '${bulk[i].value}' [value]) s`, "ON t.[key] = s.[key]", "WHEN MATCHED AND s.[value] IS NOT NULL THEN UPDATE SET t.[value] = s.[value]", "WHEN NOT MATCHED THEN INSERT ([key], [value]) VALUES (s.[key], s.[value]);");
|
|
1545
|
-
} else if (bulk[i].type === "remove") {
|
|
1546
|
-
if (!firstRemove) removeSQL += ",";
|
|
1547
|
-
firstRemove = false;
|
|
1548
|
-
removeSQL += `'${bulk[i].key}'`;
|
|
1549
|
-
}
|
|
1550
|
-
removeSQL += ");";
|
|
1551
|
-
replacements.push("COMMIT TRANSACTION;");
|
|
1552
|
-
async.default.parallel([(callback) => {
|
|
1553
|
-
if (!firstReplace) request.batch(replacements.join("\n"), (err, results) => {
|
|
1554
|
-
if (err) callback(err);
|
|
1555
|
-
callback(err, results);
|
|
1556
|
-
});
|
|
1557
|
-
else callback();
|
|
1558
|
-
}, (callback) => {
|
|
1559
|
-
if (!firstRemove) request.query(removeSQL, callback);
|
|
1560
|
-
else callback();
|
|
1561
|
-
}], (err, results) => {
|
|
1562
|
-
if (err) callback(err);
|
|
1563
|
-
callback(err, results);
|
|
1564
|
-
});
|
|
1565
|
-
}
|
|
1566
|
-
close(callback) {
|
|
1567
|
-
this.db && this.db.close(callback);
|
|
1568
|
-
}
|
|
1569
|
-
};
|
|
1570
|
-
//#endregion
|
|
1571
|
-
//#region databases/mysql_db.ts
|
|
1572
|
-
/**
|
|
1573
|
-
* 2011 Peter 'Pita' Martischka
|
|
1574
|
-
*
|
|
1575
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
1576
|
-
* you may not use this file except in compliance with the License.
|
|
1577
|
-
* You may obtain a copy of the License at
|
|
1578
|
-
*
|
|
1579
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
1580
|
-
*
|
|
1581
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
1582
|
-
* distributed under the License is distributed on an "AS-IS" BASIS,
|
|
1583
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
1584
|
-
* See the License for the specific language governing permissions and
|
|
1585
|
-
* limitations under the License.
|
|
1586
|
-
*/
|
|
1587
|
-
var mysql_db_default = class extends AbstractDatabase {
|
|
1588
|
-
_mysqlSettings;
|
|
1589
|
-
_pool;
|
|
1590
|
-
constructor(settings) {
|
|
1591
|
-
super(settings);
|
|
1592
|
-
this.logger = console;
|
|
1593
|
-
this._mysqlSettings = {
|
|
1594
|
-
charset: "utf8mb4",
|
|
1595
|
-
...settings
|
|
1596
|
-
};
|
|
1597
|
-
this.settings = {
|
|
1598
|
-
engine: "InnoDB",
|
|
1599
|
-
bulkLimit: 100,
|
|
1600
|
-
json: true,
|
|
1601
|
-
queryTimeout: 6e4
|
|
1602
|
-
};
|
|
1603
|
-
this._pool = null;
|
|
1604
|
-
}
|
|
1605
|
-
get isAsync() {
|
|
1606
|
-
return true;
|
|
1607
|
-
}
|
|
1608
|
-
async _query(options) {
|
|
1609
|
-
try {
|
|
1610
|
-
return await new Promise((resolve, reject) => {
|
|
1611
|
-
options = {
|
|
1612
|
-
timeout: this.settings.queryTimeout,
|
|
1613
|
-
...options
|
|
1614
|
-
};
|
|
1615
|
-
this._pool && this._pool.query(options, (err, ...args) => err != null ? reject(err) : resolve(args));
|
|
1616
|
-
});
|
|
1617
|
-
} catch (err) {
|
|
1618
|
-
this.logger.error(`${err.fatal ? "Fatal " : ""}MySQL error: ${err.stack || err}`);
|
|
1619
|
-
throw err;
|
|
1620
|
-
}
|
|
1621
|
-
}
|
|
1622
|
-
async init() {
|
|
1623
|
-
if ("speeds" in this._mysqlSettings) delete this._mysqlSettings.speeds;
|
|
1624
|
-
if ("filename" in this._mysqlSettings) delete this._mysqlSettings.filename;
|
|
1625
|
-
this._pool = (0, mysql2.createPool)(this._mysqlSettings);
|
|
1626
|
-
const { database, charset } = this._mysqlSettings;
|
|
1627
|
-
const sqlCreate = `CREATE TABLE IF NOT EXISTS \`store\` ( \`key\` VARCHAR( 100 ) NOT NULL COLLATE utf8mb4_bin, \`value\` LONGTEXT COLLATE utf8mb4_bin NOT NULL , PRIMARY KEY ( \`key\` ) ) ENGINE=${this.settings.engine} CHARSET=utf8mb4 COLLATE=utf8mb4_bin;`;
|
|
1628
|
-
const sqlAlter = "ALTER TABLE store MODIFY `key` VARCHAR(100) COLLATE utf8mb4_bin;";
|
|
1629
|
-
await this._query({ sql: sqlCreate });
|
|
1630
|
-
const dbCharSet = `SELECT DEFAULT_CHARACTER_SET_NAME, DEFAULT_COLLATION_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = '${database}'`;
|
|
1631
|
-
let [result] = await this._query({ sql: dbCharSet });
|
|
1632
|
-
result = JSON.parse(JSON.stringify(result));
|
|
1633
|
-
if (result[0].DEFAULT_CHARACTER_SET_NAME !== charset) {
|
|
1634
|
-
this.logger.error(`Database is not configured with charset ${charset} -- This may lead to crashes when certain characters are pasted in pads`);
|
|
1635
|
-
this.logger.warn(result[0], charset);
|
|
1636
|
-
}
|
|
1637
|
-
if (result[0].DEFAULT_COLLATION_NAME.indexOf(charset) === -1) {
|
|
1638
|
-
this.logger.error(`Database is not configured with collation name that includes ${charset} -- This may lead to crashes when certain characters are pasted in pads`);
|
|
1639
|
-
this.logger.warn(result[0], charset, result[0].DEFAULT_COLLATION_NAME);
|
|
1640
|
-
}
|
|
1641
|
-
const tableCharSet = `SELECT CCSA.character_set_name AS character_set_name FROM information_schema.\`TABLES\` T,information_schema.\`COLLATION_CHARACTER_SET_APPLICABILITY\` CCSA WHERE CCSA.collation_name = T.table_collation AND T.table_schema = '${database}' AND T.table_name = 'store'`;
|
|
1642
|
-
[result] = await this._query({ sql: tableCharSet });
|
|
1643
|
-
if (!result[0]) this.logger.warn("Data has no character_set_name value -- This may lead to crashes when certain characters are pasted in pads");
|
|
1644
|
-
if (result[0] && result[0].character_set_name !== charset) {
|
|
1645
|
-
this.logger.error(`table is not configured with charset ${charset} -- This may lead to crashes when certain characters are pasted in pads`);
|
|
1646
|
-
this.logger.warn(result[0], charset);
|
|
1647
|
-
}
|
|
1648
|
-
if (await this.get("MYSQL_MIGRATION_LEVEL") !== "1") {
|
|
1649
|
-
await this._query({ sql: sqlAlter });
|
|
1650
|
-
await this.set("MYSQL_MIGRATION_LEVEL", "1");
|
|
1651
|
-
}
|
|
1652
|
-
}
|
|
1653
|
-
async get(key) {
|
|
1654
|
-
const [results] = await this._query({
|
|
1655
|
-
sql: "SELECT `value` FROM `store` WHERE `key` = ? AND BINARY `key` = ?",
|
|
1656
|
-
values: [key, key]
|
|
1657
|
-
});
|
|
1658
|
-
return results.length === 1 ? results[0].value : null;
|
|
1659
|
-
}
|
|
1660
|
-
async findKeys(key, notKey) {
|
|
1661
|
-
let query = "SELECT `key` FROM `store` WHERE `key` LIKE ?";
|
|
1662
|
-
const params = [];
|
|
1663
|
-
key = key.replace(/\*/g, "%");
|
|
1664
|
-
params.push(key);
|
|
1665
|
-
if (notKey != null) {
|
|
1666
|
-
notKey = notKey.replace(/\*/g, "%");
|
|
1667
|
-
query += " AND `key` NOT LIKE ?";
|
|
1668
|
-
params.push(notKey);
|
|
1669
|
-
}
|
|
1670
|
-
const [results] = await this._query({
|
|
1671
|
-
sql: query,
|
|
1672
|
-
values: params
|
|
1673
|
-
});
|
|
1674
|
-
return results.map((val) => val.key);
|
|
1675
|
-
}
|
|
1676
|
-
async set(key, value) {
|
|
1677
|
-
if (key.length > 100) throw new Error("Your Key can only be 100 chars");
|
|
1678
|
-
await this._query({
|
|
1679
|
-
sql: "REPLACE INTO `store` VALUES (?,?)",
|
|
1680
|
-
values: [key, value]
|
|
1681
|
-
});
|
|
1682
|
-
}
|
|
1683
|
-
async remove(key) {
|
|
1684
|
-
await this._query({
|
|
1685
|
-
sql: "DELETE FROM `store` WHERE `key` = ? AND BINARY `key` = ?",
|
|
1686
|
-
values: [key, key]
|
|
1687
|
-
});
|
|
1688
|
-
}
|
|
1689
|
-
async doBulk(bulk) {
|
|
1690
|
-
const replaces = [];
|
|
1691
|
-
const deletes = [];
|
|
1692
|
-
for (const op of bulk) switch (op.type) {
|
|
1693
|
-
case "set":
|
|
1694
|
-
replaces.push([op.key, op.value]);
|
|
1695
|
-
break;
|
|
1696
|
-
case "remove":
|
|
1697
|
-
deletes.push(op.key);
|
|
1698
|
-
break;
|
|
1699
|
-
default: throw new Error(`unknown op type: ${op.type}`);
|
|
1700
|
-
}
|
|
1701
|
-
await Promise.all([replaces.length ? this._query({
|
|
1702
|
-
sql: "REPLACE INTO `store` VALUES ?;",
|
|
1703
|
-
values: [replaces]
|
|
1704
|
-
}) : null, deletes.length ? this._query({
|
|
1705
|
-
sql: "DELETE FROM `store` WHERE `key` IN (?) AND BINARY `key` IN (?);",
|
|
1706
|
-
values: [deletes, deletes]
|
|
1707
|
-
}) : null]);
|
|
1708
|
-
}
|
|
1709
|
-
async close() {
|
|
1710
|
-
await util.default.promisify(this._pool.end.bind(this._pool))();
|
|
1711
|
-
}
|
|
1712
|
-
};
|
|
1713
|
-
//#endregion
|
|
1714
|
-
//#region databases/postgres_db.ts
|
|
1715
|
-
/**
|
|
1716
|
-
* 2011 Peter 'Pita' Martischka
|
|
1717
|
-
*
|
|
1718
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
1719
|
-
* you may not use this file except in compliance with the License.
|
|
1720
|
-
* You may obtain a copy of the License at
|
|
1721
|
-
*
|
|
1722
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
1723
|
-
*
|
|
1724
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
1725
|
-
* distributed under the License is distributed on an "AS-IS" BASIS,
|
|
1726
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
1727
|
-
* See the License for the specific language governing permissions and
|
|
1728
|
-
* limitations under the License.
|
|
1729
|
-
*/
|
|
1730
|
-
var postgres_db_default = class extends AbstractDatabase {
|
|
1731
|
-
db;
|
|
1732
|
-
upsertStatement;
|
|
1733
|
-
constructor(settings) {
|
|
1734
|
-
super(settings);
|
|
1735
|
-
if (typeof settings === "string") settings = { connectionString: settings };
|
|
1736
|
-
this.settings = settings;
|
|
1737
|
-
this.settings.cache = settings.cache || 1e3;
|
|
1738
|
-
this.settings.writeInterval = 100;
|
|
1739
|
-
this.settings.json = true;
|
|
1740
|
-
this.settings.max = this.settings.max || 20;
|
|
1741
|
-
this.settings.min = this.settings.min || 4;
|
|
1742
|
-
this.settings.idleTimeoutMillis = this.settings.idleTimeoutMillis || 1e3;
|
|
1743
|
-
this.db = new pg.Pool(this.settings);
|
|
1744
|
-
}
|
|
1745
|
-
init(callback) {
|
|
1746
|
-
const testTableExists = "SELECT 1 as exists FROM pg_tables WHERE tablename = 'store'";
|
|
1747
|
-
const createTable = "CREATE TABLE IF NOT EXISTS store (\"key\" character varying(100) NOT NULL, \"value\" text NOT NULL, CONSTRAINT store_pkey PRIMARY KEY (key))";
|
|
1748
|
-
this.upsertStatement = null;
|
|
1749
|
-
const detectUpsertMethod = (callback) => {
|
|
1750
|
-
const upsertViaFunction = "SELECT ueberdb_insert_or_update($1,$2)";
|
|
1751
|
-
const upsertNatively = "INSERT INTO store(key, value) VALUES ($1, $2) ON CONFLICT (key) DO UPDATE SET value = excluded.value";
|
|
1752
|
-
const createFunc = "CREATE OR REPLACE FUNCTION ueberdb_insert_or_update(character varying, text) RETURNS void AS $$ BEGIN IF EXISTS( SELECT * FROM store WHERE key = $1 ) THEN UPDATE store SET value = $2 WHERE key = $1; ELSE INSERT INTO store(key,value) VALUES( $1, $2 ); END IF; RETURN; END; $$ LANGUAGE plpgsql;";
|
|
1753
|
-
const testNativeUpsert = `EXPLAIN ${upsertNatively}`;
|
|
1754
|
-
this.db.query(testNativeUpsert, ["test-key", "test-value"], (err) => {
|
|
1755
|
-
if (err) {
|
|
1756
|
-
this.upsertStatement = upsertViaFunction;
|
|
1757
|
-
this.db.query(createFunc, [], callback);
|
|
1758
|
-
return;
|
|
1759
|
-
}
|
|
1760
|
-
this.upsertStatement = upsertNatively;
|
|
1761
|
-
callback();
|
|
1762
|
-
});
|
|
1763
|
-
};
|
|
1764
|
-
this.db.query(testTableExists, (err, result) => {
|
|
1765
|
-
if (err != null) return callback(err);
|
|
1766
|
-
if (result.rows.length === 0) this.db.query(createTable, (err) => {
|
|
1767
|
-
if (err != null) return callback(err);
|
|
1768
|
-
detectUpsertMethod(callback);
|
|
1769
|
-
});
|
|
1770
|
-
else detectUpsertMethod(callback);
|
|
1771
|
-
});
|
|
1772
|
-
}
|
|
1773
|
-
get(key, callback) {
|
|
1774
|
-
this.db.query("SELECT value FROM store WHERE key=$1", [key], (err, results) => {
|
|
1775
|
-
let value = null;
|
|
1776
|
-
if (!err && results.rows.length === 1) value = results.rows[0].value;
|
|
1777
|
-
callback(err, value);
|
|
1778
|
-
});
|
|
1779
|
-
}
|
|
1780
|
-
findKeys(key, notKey, callback) {
|
|
1781
|
-
let query = "SELECT key FROM store WHERE key LIKE $1";
|
|
1782
|
-
const params = [];
|
|
1783
|
-
key = key.replace(/\*/g, "%");
|
|
1784
|
-
params.push(key);
|
|
1785
|
-
if (notKey != null) {
|
|
1786
|
-
notKey = notKey.replace(/\*/g, "%");
|
|
1787
|
-
query += " AND key NOT LIKE $2";
|
|
1788
|
-
params.push(notKey);
|
|
1789
|
-
}
|
|
1790
|
-
this.db.query(query, params, (err, results) => {
|
|
1791
|
-
const value = [];
|
|
1792
|
-
if (!err && results.rows.length > 0) results.rows.forEach((val) => {
|
|
1793
|
-
value.push(val.key);
|
|
1794
|
-
});
|
|
1795
|
-
callback(err, value);
|
|
1796
|
-
});
|
|
1797
|
-
}
|
|
1798
|
-
set(key, value, callback) {
|
|
1799
|
-
if (key.length > 100) callback(Error("Your Key can only be 100 chars"), "");
|
|
1800
|
-
else if (this.upsertStatement != null) this.db.query(this.upsertStatement, [key, value], callback);
|
|
1801
|
-
}
|
|
1802
|
-
remove(key, callback) {
|
|
1803
|
-
this.db.query("DELETE FROM store WHERE key=$1", [key], callback);
|
|
1804
|
-
}
|
|
1805
|
-
doBulk(bulk, callback) {
|
|
1806
|
-
const replaceVALs = [];
|
|
1807
|
-
let removeSQL = "DELETE FROM store WHERE key IN (";
|
|
1808
|
-
const removeVALs = [];
|
|
1809
|
-
let removeCount = 0;
|
|
1810
|
-
for (const i in bulk) if (bulk[i].type === "set") replaceVALs.push([bulk[i].key, bulk[i].value]);
|
|
1811
|
-
else if (bulk[i].type === "remove") {
|
|
1812
|
-
if (removeCount !== 0) removeSQL += ",";
|
|
1813
|
-
removeCount += 1;
|
|
1814
|
-
removeSQL += `$${removeCount}`;
|
|
1815
|
-
removeVALs.push(bulk[i].key);
|
|
1816
|
-
}
|
|
1817
|
-
removeSQL += ");";
|
|
1818
|
-
if (!this.upsertStatement) return;
|
|
1819
|
-
const functions = replaceVALs.map((v) => (cb) => this.db.query(this.upsertStatement, v, cb));
|
|
1820
|
-
const removeFunction = (callback) => {
|
|
1821
|
-
if (!(removeVALs.length < 1)) this.db.query(removeSQL, removeVALs, callback);
|
|
1822
|
-
else callback();
|
|
1823
|
-
};
|
|
1824
|
-
functions.push(removeFunction);
|
|
1825
|
-
async.default.parallel(functions, callback);
|
|
1826
|
-
}
|
|
1827
|
-
close(callback) {
|
|
1828
|
-
this.db.end(callback);
|
|
1829
|
-
}
|
|
1830
|
-
};
|
|
1831
|
-
//#endregion
|
|
1832
|
-
//#region databases/postgrespool_db.ts
|
|
1833
|
-
var PostgresDB = class extends postgres_db_default {
|
|
1834
|
-
constructor(settings) {
|
|
1835
|
-
console.warn("ueberdb: The postgrespool database driver is deprecated and will be removed in a future version. Use postgres instead.");
|
|
1836
|
-
super(settings);
|
|
1837
|
-
}
|
|
1838
|
-
};
|
|
1839
|
-
//#endregion
|
|
1840
|
-
//#region databases/redis_db.ts
|
|
1841
|
-
/**
|
|
1842
|
-
* 2011 Peter 'Pita' Martischka
|
|
1843
|
-
*
|
|
1844
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
1845
|
-
* you may not use this file except in compliance with the License.
|
|
1846
|
-
* You may obtain a copy of the License at
|
|
1847
|
-
*
|
|
1848
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
1849
|
-
*
|
|
1850
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
1851
|
-
* distributed under the License is distributed on an "AS-IS" BASIS,
|
|
1852
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
1853
|
-
* See the License for the specific language governing permissions and
|
|
1854
|
-
* limitations under the License.
|
|
1855
|
-
*/
|
|
1856
|
-
var RedisDB = class extends AbstractDatabase {
|
|
1857
|
-
_client;
|
|
1858
|
-
constructor(settings) {
|
|
1859
|
-
super(settings);
|
|
1860
|
-
this._client = null;
|
|
1861
|
-
this.settings = settings || {};
|
|
1862
|
-
}
|
|
1863
|
-
get isAsync() {
|
|
1864
|
-
return true;
|
|
1865
|
-
}
|
|
1866
|
-
async init() {
|
|
1867
|
-
if (this.settings.url) this._client = (0, redis.createClient)({ url: this.settings.url });
|
|
1868
|
-
else if (this.settings.host) {
|
|
1869
|
-
const options = { socket: {
|
|
1870
|
-
host: this.settings.host,
|
|
1871
|
-
port: Number(this.settings.port)
|
|
1872
|
-
} };
|
|
1873
|
-
if (this.settings.password) options.password = this.settings.password;
|
|
1874
|
-
if (this.settings.user) options.username = this.settings.user;
|
|
1875
|
-
this._client = (0, redis.createClient)(options);
|
|
1876
|
-
}
|
|
1877
|
-
if (this._client) {
|
|
1878
|
-
await this._client.connect();
|
|
1879
|
-
await this._client.ping();
|
|
1880
|
-
}
|
|
1881
|
-
}
|
|
1882
|
-
async get(key) {
|
|
1883
|
-
if (this._client == null) return null;
|
|
1884
|
-
return await this._client.get(key);
|
|
1885
|
-
}
|
|
1886
|
-
async findKeys(key, notKey) {
|
|
1887
|
-
if (this._client == null) return null;
|
|
1888
|
-
const [_, type] = /^([^:*]+):\*$/.exec(key) || [];
|
|
1889
|
-
if (type != null && ["*:*:*", `${key}:*`].includes(notKey)) return await this._client.sMembers(`ueberDB:keys:${type}`);
|
|
1890
|
-
let keys = await this._client.keys(key.replace(/[?[\]\\]/g, "\\$&"));
|
|
1891
|
-
if (notKey != null) {
|
|
1892
|
-
const regex = this.createFindRegex(key, notKey);
|
|
1893
|
-
keys = keys.filter((k) => regex.test(k));
|
|
1894
|
-
}
|
|
1895
|
-
return keys;
|
|
1896
|
-
}
|
|
1897
|
-
async set(key, value) {
|
|
1898
|
-
if (this._client == null) return null;
|
|
1899
|
-
const matches = /^([^:]+):([^:]+)$/.exec(key);
|
|
1900
|
-
await Promise.all([matches && this._client.sAdd(`ueberDB:keys:${matches[1]}`, matches[0]), this._client.set(key, value)]);
|
|
1901
|
-
}
|
|
1902
|
-
async remove(key) {
|
|
1903
|
-
if (this._client == null) return null;
|
|
1904
|
-
const matches = /^([^:]+):([^:]+)$/.exec(key);
|
|
1905
|
-
await Promise.all([matches && this._client.sRem(`ueberDB:keys:${matches[1]}`, matches[0]), this._client.del(key)]);
|
|
1906
|
-
}
|
|
1907
|
-
async doBulk(bulk) {
|
|
1908
|
-
if (this._client == null) return null;
|
|
1909
|
-
const multi = this._client.multi();
|
|
1910
|
-
for (const { key, type, value } of bulk) {
|
|
1911
|
-
const matches = /^([^:]+):([^:]+)$/.exec(key);
|
|
1912
|
-
if (type === "set") {
|
|
1913
|
-
if (matches) multi.sAdd(`ueberDB:keys:${matches[1]}`, matches[0]);
|
|
1914
|
-
multi.set(key, value);
|
|
1915
|
-
} else if (type === "remove") {
|
|
1916
|
-
if (matches) multi.sRem(`ueberDB:keys:${matches[1]}`, matches[0]);
|
|
1917
|
-
multi.del(key);
|
|
1918
|
-
}
|
|
1919
|
-
}
|
|
1920
|
-
await multi.exec();
|
|
1921
|
-
}
|
|
1922
|
-
async close() {
|
|
1923
|
-
if (this._client == null) return null;
|
|
1924
|
-
await this._client.quit();
|
|
1925
|
-
this._client = null;
|
|
1926
|
-
}
|
|
1927
|
-
};
|
|
1928
|
-
//#endregion
|
|
1929
|
-
//#region databases/rethink_db.ts
|
|
1930
|
-
/**
|
|
1931
|
-
* 2016 Remi Arnaud
|
|
1932
|
-
*
|
|
1933
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
1934
|
-
* you may not use this file except in compliance with the License.
|
|
1935
|
-
* You may obtain a copy of the License at
|
|
1936
|
-
*
|
|
1937
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
1938
|
-
*
|
|
1939
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
1940
|
-
* distributed under the License is distributed on an "AS-IS" BASIS,
|
|
1941
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
1942
|
-
* See the License for the specific language governing permissions and
|
|
1943
|
-
* limitations under the License.
|
|
1944
|
-
*/
|
|
1945
|
-
var Rethink_db = class extends AbstractDatabase {
|
|
1946
|
-
host;
|
|
1947
|
-
db;
|
|
1948
|
-
port;
|
|
1949
|
-
table;
|
|
1950
|
-
connection;
|
|
1951
|
-
constructor(settings) {
|
|
1952
|
-
super(settings);
|
|
1953
|
-
if (!settings) settings = {};
|
|
1954
|
-
if (!settings.host) settings.host = "localhost";
|
|
1955
|
-
if (!settings.port) settings.port = 28015;
|
|
1956
|
-
if (!settings.db) settings.db = "test";
|
|
1957
|
-
if (!settings.table) settings.table = "test";
|
|
1958
|
-
this.host = settings.host;
|
|
1959
|
-
this.db = settings.db;
|
|
1960
|
-
this.port = settings.port;
|
|
1961
|
-
this.table = settings.table;
|
|
1962
|
-
this.connection = null;
|
|
1963
|
-
}
|
|
1964
|
-
init(callback) {
|
|
1965
|
-
rethinkdb.default.connect(this, (err, conn) => {
|
|
1966
|
-
if (err) throw err;
|
|
1967
|
-
this.connection = conn;
|
|
1968
|
-
rethinkdb.default.table(this.table).run(this.connection, (err, cursor) => {
|
|
1969
|
-
if (err) rethinkdb.default.tableCreate(this.table).run(this.connection, callback);
|
|
1970
|
-
else if (callback) callback(null, cursor);
|
|
1971
|
-
});
|
|
1972
|
-
});
|
|
1973
|
-
}
|
|
1974
|
-
get(key, callback) {
|
|
1975
|
-
rethinkdb.default.table(this.table).get(key).run(this.connection, (err, item) => {
|
|
1976
|
-
callback(err, item ? item.content : item);
|
|
1977
|
-
});
|
|
1978
|
-
}
|
|
1979
|
-
findKeys(key, notKey, callback) {
|
|
1980
|
-
const keys = [];
|
|
1981
|
-
const regex = this.createFindRegex(key, notKey);
|
|
1982
|
-
rethinkdb.default.filter((item) => {
|
|
1983
|
-
if (item.id.search(regex) !== -1) keys.push(item.id);
|
|
1984
|
-
}).run(this.connection, callback);
|
|
1985
|
-
}
|
|
1986
|
-
set(key, value, callback) {
|
|
1987
|
-
rethinkdb.default.table(this.table).insert({
|
|
1988
|
-
id: key,
|
|
1989
|
-
content: value
|
|
1990
|
-
}, { conflict: "replace" }).run(this.connection, callback);
|
|
1991
|
-
}
|
|
1992
|
-
doBulk(bulk, callback) {
|
|
1993
|
-
const _in = [];
|
|
1994
|
-
const _out = [];
|
|
1995
|
-
for (const i in bulk) if (bulk[i].type === "set") _in.push({
|
|
1996
|
-
id: bulk[i].key,
|
|
1997
|
-
content: bulk[i].value
|
|
1998
|
-
});
|
|
1999
|
-
else if (bulk[i].type === "remove") _out.push(bulk[i].key);
|
|
2000
|
-
async.default.parallel([(cb) => {
|
|
2001
|
-
rethinkdb.default.table(this.table).insert(_in, { conflict: "replace" }).run(this.connection, cb);
|
|
2002
|
-
}, (cb) => {
|
|
2003
|
-
rethinkdb.default.table(this.table).getAll(_out).delete().run(this.connection, cb);
|
|
2004
|
-
}], callback);
|
|
2005
|
-
}
|
|
2006
|
-
remove(key, callback) {
|
|
2007
|
-
rethinkdb.default.table(this.table).get(key).delete().run(this.connection, callback);
|
|
2008
|
-
}
|
|
2009
|
-
close(callback) {
|
|
2010
|
-
if (this.connection) this.connection.close(callback);
|
|
2011
|
-
}
|
|
2012
|
-
};
|
|
2013
|
-
//#endregion
|
|
2014
|
-
//#region databases/sqlite_db.ts
|
|
2015
|
-
/**
|
|
2016
|
-
* 2011 Peter 'Pita' Martischka
|
|
2017
|
-
*
|
|
2018
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
2019
|
-
* you may not use this file except in compliance with the License.
|
|
2020
|
-
* You may obtain a copy of the License at
|
|
2021
|
-
*
|
|
2022
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
2023
|
-
*
|
|
2024
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
2025
|
-
* distributed under the License is distributed on an "AS-IS" BASIS,
|
|
2026
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
2027
|
-
* See the License for the specific language governing permissions and
|
|
2028
|
-
* limitations under the License.
|
|
2029
|
-
*/
|
|
2030
|
-
var SQLiteDB = class extends AbstractDatabase {
|
|
2031
|
-
db;
|
|
2032
|
-
constructor(settings) {
|
|
2033
|
-
super(settings);
|
|
2034
|
-
this.db = null;
|
|
2035
|
-
if (!settings || !settings.filename) settings = { filename: ":memory:" };
|
|
2036
|
-
this.settings = settings;
|
|
2037
|
-
if (settings.filename === ":memory:") {
|
|
2038
|
-
this.settings.cache = 0;
|
|
2039
|
-
this.settings.writeInterval = 0;
|
|
2040
|
-
this.settings.json = true;
|
|
2041
|
-
} else {
|
|
2042
|
-
this.settings.cache = 1e3;
|
|
2043
|
-
this.settings.writeInterval = 100;
|
|
2044
|
-
this.settings.json = true;
|
|
2045
|
-
}
|
|
2046
|
-
}
|
|
2047
|
-
init(callback) {
|
|
2048
|
-
this.db = new rusty_store_kv.SQLite(this.settings.filename);
|
|
2049
|
-
callback();
|
|
2050
|
-
}
|
|
2051
|
-
get(key, callback) {
|
|
2052
|
-
const res = this.db.get(key);
|
|
2053
|
-
callback(null, res ? res : null);
|
|
2054
|
-
}
|
|
2055
|
-
findKeys(key, notKey, callback) {
|
|
2056
|
-
const res = this.db?.findKeys(key, notKey);
|
|
2057
|
-
callback(null, res);
|
|
2058
|
-
}
|
|
2059
|
-
set(key, value, callback) {
|
|
2060
|
-
const res = this.db.set(key, value);
|
|
2061
|
-
res ? callback(null, null) : callback(null, res);
|
|
2062
|
-
}
|
|
2063
|
-
remove(key, callback) {
|
|
2064
|
-
this.db.remove(key);
|
|
2065
|
-
callback(null, null);
|
|
2066
|
-
}
|
|
2067
|
-
doBulk(bulk, callback) {
|
|
2068
|
-
const convertedBulk = bulk.map((b) => {
|
|
2069
|
-
if (b.value === null) return {
|
|
2070
|
-
key: b.key,
|
|
2071
|
-
type: b.type
|
|
2072
|
-
};
|
|
2073
|
-
else return b;
|
|
2074
|
-
});
|
|
2075
|
-
this.db.doBulk(convertedBulk);
|
|
2076
|
-
callback();
|
|
2077
|
-
}
|
|
2078
|
-
close(callback) {
|
|
2079
|
-
callback();
|
|
2080
|
-
this.db.close();
|
|
2081
|
-
}
|
|
2082
|
-
};
|
|
2083
|
-
//#endregion
|
|
2084
|
-
//#region databases/surrealdb_db.ts
|
|
2085
|
-
/**
|
|
2086
|
-
* 2023 Samuel Schwanzer
|
|
2087
|
-
*
|
|
2088
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
2089
|
-
* you may not use this file except in compliance with the License.
|
|
2090
|
-
* You may obtain a copy of the License at
|
|
2091
|
-
*
|
|
2092
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
2093
|
-
*
|
|
2094
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
2095
|
-
* distributed under the License is distributed on an "AS-IS" BASIS,
|
|
2096
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
2097
|
-
* See the License for the specific language governing permissions and
|
|
2098
|
-
* limitations under the License.
|
|
2099
|
-
*/
|
|
2100
|
-
const DATABASE = "ueberdb";
|
|
2101
|
-
const WILDCARD = "*";
|
|
2102
|
-
const replaceAt = function(index, replacement, original) {
|
|
2103
|
-
return original.substring(0, index) + replacement + original.substring(index + replacement.length);
|
|
2104
|
-
};
|
|
2105
|
-
const escapeKey = (key) => {
|
|
2106
|
-
const index = key.indexOf(":");
|
|
2107
|
-
if (index > -1) return replaceAt(index, "_", key);
|
|
2108
|
-
return key;
|
|
2109
|
-
};
|
|
2110
|
-
const unescapeKey = (key, originalKey) => {
|
|
2111
|
-
const index = originalKey.indexOf(":");
|
|
2112
|
-
if (index > -1) return replaceAt(index, ":", key);
|
|
2113
|
-
return key;
|
|
2114
|
-
};
|
|
2115
|
-
var SurrealDB = class extends AbstractDatabase {
|
|
2116
|
-
_client;
|
|
2117
|
-
constructor(settings) {
|
|
2118
|
-
super(settings);
|
|
2119
|
-
this._client = null;
|
|
2120
|
-
}
|
|
2121
|
-
get isAsync() {
|
|
2122
|
-
return true;
|
|
2123
|
-
}
|
|
2124
|
-
async init() {
|
|
2125
|
-
if (this.settings.url) {
|
|
2126
|
-
this._client = new surrealdb.Surreal();
|
|
2127
|
-
await this._client.connect(this.settings.url);
|
|
2128
|
-
} else if (this.settings.host) {
|
|
2129
|
-
const port = this.settings.port || 8e3;
|
|
2130
|
-
const protocol = this.settings.clientOptions?.protocol || "http://";
|
|
2131
|
-
const path = this.settings.clientOptions?.path || "/rpc";
|
|
2132
|
-
const host = this.settings.host;
|
|
2133
|
-
this._client = new surrealdb.Surreal();
|
|
2134
|
-
await this._client.connect(`${protocol}${host}:${port}${path}`);
|
|
2135
|
-
}
|
|
2136
|
-
if (this.settings.user && this.settings.password) await this._client.signin({
|
|
2137
|
-
username: this.settings.user,
|
|
2138
|
-
password: this.settings.password
|
|
2139
|
-
});
|
|
2140
|
-
await this._client.use({
|
|
2141
|
-
namespace: DATABASE,
|
|
2142
|
-
database: DATABASE
|
|
2143
|
-
});
|
|
2144
|
-
}
|
|
2145
|
-
async get(key) {
|
|
2146
|
-
if (this._client == null) return null;
|
|
2147
|
-
key = escapeKey(key);
|
|
2148
|
-
const rows = (await this._client.query("SELECT key, value FROM store WHERE key = $key", { key }))[0] || [];
|
|
2149
|
-
if (rows.length === 0) return null;
|
|
2150
|
-
const row = rows[0];
|
|
2151
|
-
if (typeof row === "string") return row;
|
|
2152
|
-
return unescapeKey(row.value, key);
|
|
2153
|
-
}
|
|
2154
|
-
async findKeys(key, notKey) {
|
|
2155
|
-
if (this._client == null) return null;
|
|
2156
|
-
const queryString = notKey != null ? `SELECT key FROM store WHERE ${this.transformWildcard(key, "key")} AND ${this.transformWildcardNegative(notKey, "notKey")}` : `SELECT key FROM store WHERE ${this.transformWildcard(key, "key")}`;
|
|
2157
|
-
const cleanKey = key.replace(WILDCARD, "");
|
|
2158
|
-
const cleanNotKey = (notKey || "").replace(WILDCARD, "");
|
|
2159
|
-
const bindings = { key: cleanKey };
|
|
2160
|
-
if (notKey != null) bindings.notKey = cleanNotKey;
|
|
2161
|
-
const res = await this._client.query(queryString, bindings);
|
|
2162
|
-
return this.transformResult(res[0] || [], cleanKey);
|
|
2163
|
-
}
|
|
2164
|
-
transformWildcard(key, keyExpr) {
|
|
2165
|
-
if (key.startsWith(WILDCARD) && key.endsWith(WILDCARD)) return `${keyExpr} CONTAINS $${keyExpr}`;
|
|
2166
|
-
else if (key.startsWith(WILDCARD)) return `string::ends_with(${keyExpr}, $${keyExpr})`;
|
|
2167
|
-
else if (key.endsWith(WILDCARD)) return `string::starts_with(${keyExpr}, $${keyExpr})`;
|
|
2168
|
-
else return `${keyExpr} = $${keyExpr}`;
|
|
2169
|
-
}
|
|
2170
|
-
transformWildcardNegative(key, keyExpr) {
|
|
2171
|
-
if (key.startsWith(WILDCARD) && key.endsWith(WILDCARD)) return `key CONTAINSNOT $${keyExpr}`;
|
|
2172
|
-
else if (key.startsWith(WILDCARD)) return `string::ends_with(key, $${keyExpr}) == false`;
|
|
2173
|
-
else if (key.endsWith(WILDCARD)) return `string::starts_with(key, $${keyExpr}) == false`;
|
|
2174
|
-
else return `key != $${keyExpr}`;
|
|
2175
|
-
}
|
|
2176
|
-
transformResult(rows, originalKey) {
|
|
2177
|
-
const value = [];
|
|
2178
|
-
if (typeof rows === "string") {
|
|
2179
|
-
value.push(unescapeKey(rows, originalKey));
|
|
2180
|
-
return value;
|
|
2181
|
-
}
|
|
2182
|
-
for (const row of rows) value.push(unescapeKey(row.key, originalKey));
|
|
2183
|
-
return value;
|
|
2184
|
-
}
|
|
2185
|
-
async set(key, value) {
|
|
2186
|
-
if (this._client == null) return null;
|
|
2187
|
-
const exists = await this.get(key);
|
|
2188
|
-
const escapedKey = escapeKey(key);
|
|
2189
|
-
if (exists) await this._client.query("UPDATE store SET value = $value WHERE key = $key", {
|
|
2190
|
-
key: escapedKey,
|
|
2191
|
-
value
|
|
2192
|
-
});
|
|
2193
|
-
else await this._client.query("INSERT INTO store (key, value) VALUES ($key, $value)", {
|
|
2194
|
-
key: escapedKey,
|
|
2195
|
-
value
|
|
2196
|
-
});
|
|
2197
|
-
}
|
|
2198
|
-
async remove(key) {
|
|
2199
|
-
if (this._client == null) return null;
|
|
2200
|
-
key = escapeKey(key);
|
|
2201
|
-
return await this._client.query("DELETE FROM store WHERE key = $key", { key });
|
|
2202
|
-
}
|
|
2203
|
-
async doBulk(bulk) {
|
|
2204
|
-
if (this._client == null) return null;
|
|
2205
|
-
for (const b of bulk) if (b.type === "set") await this.set(b.key, b.value);
|
|
2206
|
-
else if (b.type === "remove") await this.remove(b.key);
|
|
2207
|
-
}
|
|
2208
|
-
async close() {
|
|
2209
|
-
if (this._client == null) return null;
|
|
2210
|
-
await this._client.close();
|
|
2211
|
-
this._client = null;
|
|
2212
|
-
}
|
|
2213
|
-
};
|
|
2214
|
-
//#endregion
|
|
2215
|
-
//#region databases/rusty_db.ts
|
|
2216
|
-
var Rusty_db = class extends AbstractDatabase {
|
|
2217
|
-
db;
|
|
2218
|
-
constructor(settings) {
|
|
2219
|
-
super(settings);
|
|
2220
|
-
this.settings.cache = 0;
|
|
2221
|
-
this.settings.writeInterval = 0;
|
|
2222
|
-
this.settings.json = false;
|
|
2223
|
-
}
|
|
2224
|
-
get isAsync() {
|
|
2225
|
-
return true;
|
|
2226
|
-
}
|
|
2227
|
-
findKeys(key, notKey) {
|
|
2228
|
-
return this.db.findKeys(key, notKey);
|
|
2229
|
-
}
|
|
2230
|
-
get(key) {
|
|
2231
|
-
const val = this.db.get(key);
|
|
2232
|
-
if (!val) return val;
|
|
2233
|
-
try {
|
|
2234
|
-
return JSON.parse(val);
|
|
2235
|
-
} catch (e) {
|
|
2236
|
-
return val;
|
|
2237
|
-
}
|
|
2238
|
-
}
|
|
2239
|
-
async init() {
|
|
2240
|
-
this.db = new rusty_store_kv.KeyValueDB(this.settings.filename);
|
|
2241
|
-
}
|
|
2242
|
-
close() {
|
|
2243
|
-
this.db?.close();
|
|
2244
|
-
this.db = null;
|
|
2245
|
-
}
|
|
2246
|
-
remove(key) {
|
|
2247
|
-
this.db.remove(key);
|
|
2248
|
-
}
|
|
2249
|
-
set(key, value) {
|
|
2250
|
-
if (typeof value === "object") {
|
|
2251
|
-
const valStr = JSON.stringify(value);
|
|
2252
|
-
this.db.set(key, valStr);
|
|
2253
|
-
} else this.db.set(key, value.toString());
|
|
2254
|
-
}
|
|
2255
|
-
destroy() {
|
|
2256
|
-
this.db.destroy();
|
|
2257
|
-
}
|
|
2258
|
-
};
|
|
2259
|
-
//#endregion
|
|
6
|
+
require("./_virtual/_rolldown/runtime.js");
|
|
7
|
+
const require_CacheAndBufferLayer = require("./lib/CacheAndBufferLayer.js");
|
|
8
|
+
const require_logging = require("./lib/logging.js");
|
|
9
|
+
let util = require("util");
|
|
2260
10
|
//#region index.ts
|
|
2261
11
|
/**
|
|
2262
12
|
* 2011 Peter 'Pita' Martischka
|
|
@@ -2295,7 +45,7 @@ for (const fn of [
|
|
|
2295
45
|
"remove",
|
|
2296
46
|
"set",
|
|
2297
47
|
"setSub"
|
|
2298
|
-
]) if (fn in cbDb) cbDb[fn] = (0, util.callbackify)(Database
|
|
48
|
+
]) if (fn in cbDb) cbDb[fn] = (0, util.callbackify)(require_CacheAndBufferLayer.Database.prototype[fn]);
|
|
2299
49
|
const makeDoneCallback = (callback, deprecated) => (err) => {
|
|
2300
50
|
if (callback) callback(err);
|
|
2301
51
|
if (deprecated) deprecated(err);
|
|
@@ -2326,38 +76,40 @@ var Database = class {
|
|
|
2326
76
|
this.type = type;
|
|
2327
77
|
this.dbSettings = dbSettings;
|
|
2328
78
|
this.wrapperSettings = wrapperSettings;
|
|
2329
|
-
this.logger = normalizeLogger(logger);
|
|
79
|
+
this.logger = require_logging.normalizeLogger(logger);
|
|
2330
80
|
}
|
|
2331
81
|
/**
|
|
2332
82
|
* @param callback - Deprecated. Node-style callback. If null, a Promise is returned.
|
|
2333
83
|
*/
|
|
2334
84
|
init(callback = null) {
|
|
2335
|
-
const
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
85
|
+
const p = this.initDB().then((db) => {
|
|
86
|
+
db.logger = this.logger;
|
|
87
|
+
this.db = new require_CacheAndBufferLayer.Database(db, this.wrapperSettings, this.logger);
|
|
88
|
+
this.metrics = this.db.metrics;
|
|
89
|
+
return this.db.init();
|
|
90
|
+
});
|
|
91
|
+
if (callback != null) return cbDb.init.call({ init: () => p });
|
|
92
|
+
return p;
|
|
2341
93
|
}
|
|
2342
|
-
initDB() {
|
|
94
|
+
async initDB() {
|
|
2343
95
|
switch (this.type) {
|
|
2344
|
-
case "mysql": return new
|
|
2345
|
-
case "postgres": return new
|
|
2346
|
-
case "sqlite": return new
|
|
2347
|
-
case "rustydb": return new
|
|
2348
|
-
case "mongodb": return new
|
|
2349
|
-
case "redis": return new
|
|
2350
|
-
case "cassandra": return new
|
|
2351
|
-
case "dirty": return new
|
|
2352
|
-
case "dirtygit": return new
|
|
2353
|
-
case "elasticsearch": return new
|
|
2354
|
-
case "memory": return new
|
|
2355
|
-
case "mock": return new
|
|
2356
|
-
case "mssql": return new
|
|
2357
|
-
case "postgrespool": return new
|
|
2358
|
-
case "rethink": return new
|
|
2359
|
-
case "couch": return new
|
|
2360
|
-
case "surrealdb": return new
|
|
96
|
+
case "mysql": return new (await (Promise.resolve().then(() => require("./databases/mysql_db.js")))).default(this.dbSettings);
|
|
97
|
+
case "postgres": return new (await (Promise.resolve().then(() => require("./databases/postgres_db.js")))).default(this.dbSettings);
|
|
98
|
+
case "sqlite": return new (await (Promise.resolve().then(() => require("./databases/sqlite_db.js")))).default(this.dbSettings);
|
|
99
|
+
case "rustydb": return new (await (Promise.resolve().then(() => require("./databases/rusty_db.js")))).default(this.dbSettings);
|
|
100
|
+
case "mongodb": return new (await (Promise.resolve().then(() => require("./databases/mongodb_db.js")))).default(this.dbSettings);
|
|
101
|
+
case "redis": return new (await (Promise.resolve().then(() => require("./databases/redis_db.js")))).default(this.dbSettings);
|
|
102
|
+
case "cassandra": return new (await (Promise.resolve().then(() => require("./databases/cassandra_db.js")))).default(this.dbSettings);
|
|
103
|
+
case "dirty": return new (await (Promise.resolve().then(() => require("./databases/dirty_db.js")))).default(this.dbSettings);
|
|
104
|
+
case "dirtygit": return new (await (Promise.resolve().then(() => require("./databases/dirty_git_db.js")))).default(this.dbSettings);
|
|
105
|
+
case "elasticsearch": return new (await (Promise.resolve().then(() => require("./databases/elasticsearch_db.js")))).default(this.dbSettings);
|
|
106
|
+
case "memory": return new (await (Promise.resolve().then(() => require("./databases/memory_db.js")))).default(this.dbSettings);
|
|
107
|
+
case "mock": return new (await (Promise.resolve().then(() => require("./databases/mock_db.js")))).default(this.dbSettings);
|
|
108
|
+
case "mssql": return new (await (Promise.resolve().then(() => require("./databases/mssql_db.js")))).default(this.dbSettings);
|
|
109
|
+
case "postgrespool": return new (await (Promise.resolve().then(() => require("./databases/postgrespool_db.js")))).default(this.dbSettings);
|
|
110
|
+
case "rethink": return new (await (Promise.resolve().then(() => require("./databases/rethink_db.js")))).default(this.dbSettings);
|
|
111
|
+
case "couch": return new (await (Promise.resolve().then(() => require("./databases/couch_db.js")))).default(this.dbSettings);
|
|
112
|
+
case "surrealdb": return new (await (Promise.resolve().then(() => require("./databases/surrealdb_db.js")))).default(this.dbSettings);
|
|
2361
113
|
default: throw new Error("Invalid database type");
|
|
2362
114
|
}
|
|
2363
115
|
}
|