@vsaas/loopback-datasource-juggler 10.0.0
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/LICENSE +25 -0
- package/NOTICE +23 -0
- package/README.md +74 -0
- package/dist/_virtual/_rolldown/runtime.js +4 -0
- package/dist/index.js +26 -0
- package/dist/lib/browser.depd.js +13 -0
- package/dist/lib/case-utils.js +21 -0
- package/dist/lib/connectors/kv-memory.js +158 -0
- package/dist/lib/connectors/memory.js +810 -0
- package/dist/lib/connectors/transient.js +126 -0
- package/dist/lib/dao.js +2445 -0
- package/dist/lib/datasource.js +2215 -0
- package/dist/lib/date-string.js +87 -0
- package/dist/lib/geo.js +244 -0
- package/dist/lib/globalize.js +33 -0
- package/dist/lib/hooks.js +79 -0
- package/dist/lib/id-utils.js +66 -0
- package/dist/lib/include.js +795 -0
- package/dist/lib/include_utils.js +104 -0
- package/dist/lib/introspection.js +37 -0
- package/dist/lib/jutil.js +65 -0
- package/dist/lib/kvao/delete-all.js +57 -0
- package/dist/lib/kvao/delete.js +43 -0
- package/dist/lib/kvao/expire.js +35 -0
- package/dist/lib/kvao/get.js +34 -0
- package/dist/lib/kvao/index.js +28 -0
- package/dist/lib/kvao/iterate-keys.js +38 -0
- package/dist/lib/kvao/keys.js +55 -0
- package/dist/lib/kvao/set.js +39 -0
- package/dist/lib/kvao/ttl.js +35 -0
- package/dist/lib/list.js +101 -0
- package/dist/lib/mixins.js +58 -0
- package/dist/lib/model-builder.js +608 -0
- package/dist/lib/model-definition.js +231 -0
- package/dist/lib/model-utils.js +368 -0
- package/dist/lib/model.js +586 -0
- package/dist/lib/observer.js +235 -0
- package/dist/lib/relation-definition.js +2604 -0
- package/dist/lib/relations.js +587 -0
- package/dist/lib/scope.js +392 -0
- package/dist/lib/transaction.js +183 -0
- package/dist/lib/types.js +58 -0
- package/dist/lib/utils.js +625 -0
- package/dist/lib/validations.js +742 -0
- package/dist/package.js +93 -0
- package/package.json +85 -0
- package/types/common.d.ts +28 -0
- package/types/connector.d.ts +52 -0
- package/types/datasource.d.ts +324 -0
- package/types/date-string.d.ts +21 -0
- package/types/inclusion-mixin.d.ts +44 -0
- package/types/index.d.ts +36 -0
- package/types/kv-model.d.ts +201 -0
- package/types/model.d.ts +368 -0
- package/types/observer-mixin.d.ts +174 -0
- package/types/persisted-model.d.ts +505 -0
- package/types/query.d.ts +108 -0
- package/types/relation-mixin.d.ts +577 -0
- package/types/relation.d.ts +301 -0
- package/types/scope.d.ts +92 -0
- package/types/transaction-mixin.d.ts +47 -0
- package/types/types.d.ts +65 -0
- package/types/validation-mixin.d.ts +287 -0
|
@@ -0,0 +1,810 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const require_runtime = require("../../_virtual/_rolldown/runtime.js");
|
|
3
|
+
const require_lib_globalize = require("../globalize.js");
|
|
4
|
+
const require_lib_geo = require("../geo.js");
|
|
5
|
+
const require_lib_utils = require("../utils.js");
|
|
6
|
+
//#region src/lib/connectors/memory.ts
|
|
7
|
+
var require_memory = /* @__PURE__ */ require_runtime.__commonJSMin(((exports) => {
|
|
8
|
+
const g = require_lib_globalize();
|
|
9
|
+
const util = require("util");
|
|
10
|
+
const Connector = require("@vsaas/loopback-connector").Connector;
|
|
11
|
+
const geo = require_lib_geo;
|
|
12
|
+
const utils = require_lib_utils;
|
|
13
|
+
const fs = require("fs");
|
|
14
|
+
const debug = require("debug")("loopback:connector:memory");
|
|
15
|
+
const hasOwnProperty = Object.prototype.hasOwnProperty;
|
|
16
|
+
function callbackToPromise(fn) {
|
|
17
|
+
return new Promise(function(resolve, reject) {
|
|
18
|
+
fn(function(err, result) {
|
|
19
|
+
if (err) return reject(err);
|
|
20
|
+
resolve(result);
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
async function eachSeries(items, iterator) {
|
|
25
|
+
for (let i = 0; i < items.length; i++) await callbackToPromise(function(done) {
|
|
26
|
+
iterator(items[i], done);
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
async function eachParallel(items, iterator) {
|
|
30
|
+
await Promise.all(items.map(function(item) {
|
|
31
|
+
return callbackToPromise(function(done) {
|
|
32
|
+
iterator(item, done);
|
|
33
|
+
});
|
|
34
|
+
}));
|
|
35
|
+
}
|
|
36
|
+
function createSerialQueue(worker) {
|
|
37
|
+
const tasks = [];
|
|
38
|
+
let running = false;
|
|
39
|
+
function runNext() {
|
|
40
|
+
if (running || tasks.length === 0) return;
|
|
41
|
+
running = true;
|
|
42
|
+
const task = tasks.shift();
|
|
43
|
+
let completed = false;
|
|
44
|
+
function done() {
|
|
45
|
+
if (completed) return;
|
|
46
|
+
completed = true;
|
|
47
|
+
running = false;
|
|
48
|
+
runNext();
|
|
49
|
+
}
|
|
50
|
+
try {
|
|
51
|
+
worker(task, done);
|
|
52
|
+
} catch (err) {
|
|
53
|
+
running = false;
|
|
54
|
+
(task.callback || function() {})(err);
|
|
55
|
+
runNext();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return { push: function(task) {
|
|
59
|
+
tasks.push(task);
|
|
60
|
+
runNext();
|
|
61
|
+
} };
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Initialize the Memory connector against the given data source
|
|
65
|
+
*
|
|
66
|
+
* @param {DataSource} dataSource The loopback-datasource-juggler dataSource
|
|
67
|
+
* @param {Function} [callback] The callback function
|
|
68
|
+
*/
|
|
69
|
+
exports.initialize = function initializeDataSource(dataSource, callback) {
|
|
70
|
+
dataSource.connector = new Memory(null, dataSource.settings);
|
|
71
|
+
dataSource.connect(callback);
|
|
72
|
+
};
|
|
73
|
+
exports.Memory = Memory;
|
|
74
|
+
exports.applyFilter = applyFilter;
|
|
75
|
+
function Memory(m, settings) {
|
|
76
|
+
if (m instanceof Memory) {
|
|
77
|
+
this.isTransaction = true;
|
|
78
|
+
this.cache = m.cache;
|
|
79
|
+
this.ids = m.ids;
|
|
80
|
+
this.constructor.super_.call(this, "memory", settings);
|
|
81
|
+
this._models = m._models;
|
|
82
|
+
} else {
|
|
83
|
+
this.isTransaction = false;
|
|
84
|
+
this.cache = {};
|
|
85
|
+
this.ids = {};
|
|
86
|
+
this.constructor.super_.call(this, "memory", settings);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
util.inherits(Memory, Connector);
|
|
90
|
+
Memory.prototype.multiInsertSupported = true;
|
|
91
|
+
Memory.prototype.getDefaultIdType = function() {
|
|
92
|
+
return Number;
|
|
93
|
+
};
|
|
94
|
+
Memory.prototype.getTypes = function() {
|
|
95
|
+
return [
|
|
96
|
+
"db",
|
|
97
|
+
"nosql",
|
|
98
|
+
"memory"
|
|
99
|
+
];
|
|
100
|
+
};
|
|
101
|
+
Memory.prototype.connect = function(callback) {
|
|
102
|
+
if (this.isTransaction) this.onTransactionExec = callback;
|
|
103
|
+
else this.loadFromFile(callback);
|
|
104
|
+
};
|
|
105
|
+
function serialize(obj) {
|
|
106
|
+
if (obj === null || obj === void 0) return obj;
|
|
107
|
+
return JSON.stringify(obj);
|
|
108
|
+
}
|
|
109
|
+
function deserialize(dbObj) {
|
|
110
|
+
if (dbObj === null || dbObj === void 0) return dbObj;
|
|
111
|
+
if (typeof dbObj === "string") return JSON.parse(dbObj);
|
|
112
|
+
else return dbObj;
|
|
113
|
+
}
|
|
114
|
+
Memory.prototype.getCollection = function(model) {
|
|
115
|
+
const modelClass = this._models[model];
|
|
116
|
+
if (modelClass && modelClass.settings.memory) model = modelClass.settings.memory.collection || model;
|
|
117
|
+
return model;
|
|
118
|
+
};
|
|
119
|
+
Memory.prototype.initCollection = function(model) {
|
|
120
|
+
this.collection(model, {});
|
|
121
|
+
this.collectionSeq(model, 1);
|
|
122
|
+
};
|
|
123
|
+
Memory.prototype.collection = function(model, val) {
|
|
124
|
+
model = this.getCollection(model);
|
|
125
|
+
if (arguments.length > 1) this.cache[model] = val;
|
|
126
|
+
return this.cache[model];
|
|
127
|
+
};
|
|
128
|
+
Memory.prototype.collectionSeq = function(model, val) {
|
|
129
|
+
model = this.getCollection(model);
|
|
130
|
+
if (arguments.length > 1) this.ids[model] = val;
|
|
131
|
+
return this.ids[model];
|
|
132
|
+
};
|
|
133
|
+
/**
|
|
134
|
+
* Create a queue to serialize file read/write operations
|
|
135
|
+
* @returns {*} The file operation queue
|
|
136
|
+
*/
|
|
137
|
+
Memory.prototype.setupFileQueue = function() {
|
|
138
|
+
const self = this;
|
|
139
|
+
if (!this.fileQueue) this.fileQueue = createSerialQueue(function(task, done) {
|
|
140
|
+
const callback = task.callback || function() {};
|
|
141
|
+
const file = self.settings.file;
|
|
142
|
+
if (task.operation === "write") {
|
|
143
|
+
const data = JSON.stringify({
|
|
144
|
+
ids: self.ids,
|
|
145
|
+
models: self.cache
|
|
146
|
+
}, null, " ");
|
|
147
|
+
debug("Writing cache to %s: %s", file, data);
|
|
148
|
+
fs.writeFile(file, data, function(err) {
|
|
149
|
+
debug("Cache has been written to %s", file);
|
|
150
|
+
done();
|
|
151
|
+
callback(err, task.data);
|
|
152
|
+
});
|
|
153
|
+
} else if (task.operation === "read") {
|
|
154
|
+
debug("Reading cache from %s", file);
|
|
155
|
+
fs.readFile(file, {
|
|
156
|
+
encoding: "utf8",
|
|
157
|
+
flag: "r"
|
|
158
|
+
}, function(err, data) {
|
|
159
|
+
if (err && err.code !== "ENOENT") {
|
|
160
|
+
done();
|
|
161
|
+
callback(err);
|
|
162
|
+
} else {
|
|
163
|
+
debug("Cache has been read from %s: %s", file, data);
|
|
164
|
+
self.parseAndLoad(data, function(err) {
|
|
165
|
+
done();
|
|
166
|
+
callback(err);
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
} else {
|
|
171
|
+
const err = /* @__PURE__ */ new Error("Unknown type of task");
|
|
172
|
+
done();
|
|
173
|
+
callback(err);
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
return this.fileQueue;
|
|
177
|
+
};
|
|
178
|
+
Memory.prototype.parseAndLoad = function(data, callback) {
|
|
179
|
+
if (data) {
|
|
180
|
+
try {
|
|
181
|
+
data = JSON.parse(data.toString());
|
|
182
|
+
} catch (e) {
|
|
183
|
+
if (callback) return callback(e);
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
this.ids = data.ids || {};
|
|
187
|
+
this.cache = data.models || {};
|
|
188
|
+
} else if (!this.cache) {
|
|
189
|
+
this.ids = {};
|
|
190
|
+
this.cache = {};
|
|
191
|
+
}
|
|
192
|
+
if (callback) callback();
|
|
193
|
+
};
|
|
194
|
+
Memory.prototype.loadFromFile = function(callback) {
|
|
195
|
+
const localStorage = typeof window !== "undefined" && window.localStorage && this.settings.localStorage;
|
|
196
|
+
if (this.settings.file) {
|
|
197
|
+
debug("Queueing read %s", this.settings.file);
|
|
198
|
+
this.setupFileQueue().push({
|
|
199
|
+
operation: "read",
|
|
200
|
+
callback
|
|
201
|
+
});
|
|
202
|
+
} else if (localStorage) {
|
|
203
|
+
let data = window.localStorage.getItem(localStorage);
|
|
204
|
+
data = data || "{}";
|
|
205
|
+
this.parseAndLoad(data, callback);
|
|
206
|
+
} else process.nextTick(callback);
|
|
207
|
+
};
|
|
208
|
+
/*!
|
|
209
|
+
* Flush the cache into the json file if necessary
|
|
210
|
+
* @param {Function} callback
|
|
211
|
+
*/
|
|
212
|
+
Memory.prototype.saveToFile = function(result, callback) {
|
|
213
|
+
const file = this.settings.file;
|
|
214
|
+
const localStorage = typeof window !== "undefined" && window.localStorage && this.settings.localStorage;
|
|
215
|
+
if (file) {
|
|
216
|
+
debug("Queueing write %s", this.settings.file);
|
|
217
|
+
this.setupFileQueue().push({
|
|
218
|
+
operation: "write",
|
|
219
|
+
data: result,
|
|
220
|
+
callback
|
|
221
|
+
});
|
|
222
|
+
} else if (localStorage) {
|
|
223
|
+
const data = JSON.stringify({
|
|
224
|
+
ids: this.ids,
|
|
225
|
+
models: this.cache
|
|
226
|
+
}, null, " ");
|
|
227
|
+
window.localStorage.setItem(localStorage, data);
|
|
228
|
+
process.nextTick(function() {
|
|
229
|
+
if (callback) callback(null, result);
|
|
230
|
+
});
|
|
231
|
+
} else process.nextTick(function() {
|
|
232
|
+
if (callback) callback(null, result);
|
|
233
|
+
});
|
|
234
|
+
};
|
|
235
|
+
Memory.prototype.define = function defineModel(definition) {
|
|
236
|
+
this.constructor.super_.prototype.define.apply(this, [].slice.call(arguments));
|
|
237
|
+
const m = definition.model.modelName;
|
|
238
|
+
if (!this.collection(m)) this.initCollection(m);
|
|
239
|
+
};
|
|
240
|
+
Memory.prototype._createSync = function(model, data, fn) {
|
|
241
|
+
let currentId = this.collectionSeq(model);
|
|
242
|
+
if (currentId === void 0) currentId = this.collectionSeq(model, 1);
|
|
243
|
+
let id = this.getIdValue(model, data) || currentId;
|
|
244
|
+
if (id > currentId) currentId = id;
|
|
245
|
+
this.collectionSeq(model, Number(currentId) + 1);
|
|
246
|
+
const props = this._models[model].properties;
|
|
247
|
+
const idName = this.idName(model);
|
|
248
|
+
id = props[idName] && props[idName].type && props[idName].type(id) || id;
|
|
249
|
+
this.setIdValue(model, data, id);
|
|
250
|
+
if (!this.collection(model)) this.collection(model, {});
|
|
251
|
+
if (this.collection(model)[id]) {
|
|
252
|
+
const error = new Error(g.f("Duplicate entry for %s.%s", model, idName));
|
|
253
|
+
error.statusCode = error.status = 409;
|
|
254
|
+
return fn(error);
|
|
255
|
+
}
|
|
256
|
+
this.collection(model)[id] = serialize(data);
|
|
257
|
+
fn(null, id);
|
|
258
|
+
};
|
|
259
|
+
Memory.prototype.create = function create(model, data, options, callback) {
|
|
260
|
+
const self = this;
|
|
261
|
+
this._createSync(model, data, function(err, id) {
|
|
262
|
+
if (err) return process.nextTick(function() {
|
|
263
|
+
callback(err);
|
|
264
|
+
});
|
|
265
|
+
self.saveToFile(id, callback);
|
|
266
|
+
});
|
|
267
|
+
};
|
|
268
|
+
Memory.prototype.createAll = function create(model, dataArray, options, callback) {
|
|
269
|
+
const returnArr = [];
|
|
270
|
+
eachSeries(dataArray, (data, cb) => {
|
|
271
|
+
this._createSync(model, data, (err, id) => {
|
|
272
|
+
if (err) return process.nextTick(function() {
|
|
273
|
+
cb(err);
|
|
274
|
+
});
|
|
275
|
+
const returnData = Object.assign({}, data);
|
|
276
|
+
this.setIdValue(model, returnData, id);
|
|
277
|
+
returnArr.push(returnData);
|
|
278
|
+
this.saveToFile(id, cb);
|
|
279
|
+
});
|
|
280
|
+
}).then(function() {
|
|
281
|
+
callback(null, returnArr);
|
|
282
|
+
}, function(err) {
|
|
283
|
+
callback(err, returnArr);
|
|
284
|
+
});
|
|
285
|
+
};
|
|
286
|
+
Memory.prototype.updateOrCreate = function(model, data, options, callback) {
|
|
287
|
+
const self = this;
|
|
288
|
+
this.exists(model, self.getIdValue(model, data), options, function(err, exists) {
|
|
289
|
+
if (exists) self.save(model, data, options, function(err, data) {
|
|
290
|
+
callback(err, data, { isNewInstance: false });
|
|
291
|
+
});
|
|
292
|
+
else self.create(model, data, options, function(err, id) {
|
|
293
|
+
self.setIdValue(model, data, id);
|
|
294
|
+
callback(err, data, { isNewInstance: true });
|
|
295
|
+
});
|
|
296
|
+
});
|
|
297
|
+
};
|
|
298
|
+
Memory.prototype.patchOrCreateWithWhere = Memory.prototype.upsertWithWhere = function(model, where, data, options, callback) {
|
|
299
|
+
const self = this;
|
|
300
|
+
const primaryKey = this.idName(model);
|
|
301
|
+
const filter = { where };
|
|
302
|
+
const nodes = self._findAllSkippingIncludes(model, filter);
|
|
303
|
+
if (nodes.length === 0) return self._createSync(model, data, function(err, id) {
|
|
304
|
+
if (err) return process.nextTick(function() {
|
|
305
|
+
callback(err);
|
|
306
|
+
});
|
|
307
|
+
self.saveToFile(id, function(err, id) {
|
|
308
|
+
self.setIdValue(model, data, id);
|
|
309
|
+
callback(err, self.fromDb(model, data), { isNewInstance: true });
|
|
310
|
+
});
|
|
311
|
+
});
|
|
312
|
+
if (nodes.length === 1) {
|
|
313
|
+
const primaryKeyValue = nodes[0][primaryKey];
|
|
314
|
+
self.updateAttributes(model, primaryKeyValue, data, options, function(err, data) {
|
|
315
|
+
callback(err, data, { isNewInstance: false });
|
|
316
|
+
});
|
|
317
|
+
} else process.nextTick(function() {
|
|
318
|
+
const error = /* @__PURE__ */ new Error("There are multiple instances found.Upsert Operation will not be performed!");
|
|
319
|
+
error.statusCode = 400;
|
|
320
|
+
callback(error);
|
|
321
|
+
});
|
|
322
|
+
};
|
|
323
|
+
Memory.prototype.findOrCreate = function(model, filter, data, options, callback) {
|
|
324
|
+
const self = this;
|
|
325
|
+
const nodes = self._findAllSkippingIncludes(model, filter);
|
|
326
|
+
const found = nodes[0];
|
|
327
|
+
if (!found) return self._createSync(model, data, function(err, id) {
|
|
328
|
+
if (err) return callback(err);
|
|
329
|
+
self.saveToFile(id, function(err, id) {
|
|
330
|
+
self.setIdValue(model, data, id);
|
|
331
|
+
callback(err, data, true);
|
|
332
|
+
});
|
|
333
|
+
});
|
|
334
|
+
if (!filter || !filter.include) return process.nextTick(function() {
|
|
335
|
+
callback(null, found, false);
|
|
336
|
+
});
|
|
337
|
+
self._models[model].model.include(nodes[0], filter.include, options, function(err, nodes) {
|
|
338
|
+
process.nextTick(function() {
|
|
339
|
+
if (err) return callback(err);
|
|
340
|
+
callback(null, nodes[0], false);
|
|
341
|
+
});
|
|
342
|
+
});
|
|
343
|
+
};
|
|
344
|
+
Memory.prototype.save = function save(model, data, options, callback) {
|
|
345
|
+
const self = this;
|
|
346
|
+
const id = this.getIdValue(model, data);
|
|
347
|
+
let modelData = this.collection(model) && this.collection(model)[id];
|
|
348
|
+
modelData = modelData && deserialize(modelData);
|
|
349
|
+
if (modelData) data = merge(modelData, data);
|
|
350
|
+
this.collection(model)[id] = serialize(data);
|
|
351
|
+
this.saveToFile(data, function(err) {
|
|
352
|
+
callback(err, self.fromDb(model, data), { isNewInstance: !modelData });
|
|
353
|
+
});
|
|
354
|
+
};
|
|
355
|
+
Memory.prototype.exists = function exists(model, id, options, callback) {
|
|
356
|
+
process.nextTick(function() {
|
|
357
|
+
callback(null, this.collection(model) && this.collection(model).hasOwnProperty(id));
|
|
358
|
+
}.bind(this));
|
|
359
|
+
};
|
|
360
|
+
Memory.prototype.find = function find(model, id, options, callback) {
|
|
361
|
+
process.nextTick(function() {
|
|
362
|
+
callback(null, id in this.collection(model) && this.fromDb(model, this.collection(model)[id]));
|
|
363
|
+
}.bind(this));
|
|
364
|
+
};
|
|
365
|
+
Memory.prototype.destroy = function destroy(model, id, options, callback) {
|
|
366
|
+
const exists = this.collection(model)[id];
|
|
367
|
+
delete this.collection(model)[id];
|
|
368
|
+
this.saveToFile({ count: exists ? 1 : 0 }, callback);
|
|
369
|
+
};
|
|
370
|
+
Memory.prototype.fromDb = function(model, data) {
|
|
371
|
+
if (!data) return null;
|
|
372
|
+
data = deserialize(data);
|
|
373
|
+
const props = this._models[model].properties;
|
|
374
|
+
for (const key in data) {
|
|
375
|
+
let val = data[key];
|
|
376
|
+
if (val === void 0 || val === null) continue;
|
|
377
|
+
if (props[key]) switch (props[key].type.name) {
|
|
378
|
+
case "Date":
|
|
379
|
+
val = new Date(val.toString().replace(/GMT.*$/, "GMT"));
|
|
380
|
+
break;
|
|
381
|
+
case "Boolean":
|
|
382
|
+
val = Boolean(val);
|
|
383
|
+
break;
|
|
384
|
+
case "Number":
|
|
385
|
+
val = Number(val);
|
|
386
|
+
break;
|
|
387
|
+
}
|
|
388
|
+
data[key] = val;
|
|
389
|
+
}
|
|
390
|
+
return data;
|
|
391
|
+
};
|
|
392
|
+
function getValue(obj, path, keys) {
|
|
393
|
+
if (obj == null) return;
|
|
394
|
+
if (!keys) return obj[path];
|
|
395
|
+
let val = obj;
|
|
396
|
+
for (let i = 0, n = keys.length; i < n; i++) {
|
|
397
|
+
val = val[keys[i]];
|
|
398
|
+
if (val == null) return val;
|
|
399
|
+
}
|
|
400
|
+
return val;
|
|
401
|
+
}
|
|
402
|
+
function normalizeSortOrders(order) {
|
|
403
|
+
const rawOrders = typeof order === "string" ? [order] : order;
|
|
404
|
+
const orders = new Array(rawOrders.length);
|
|
405
|
+
for (let i = 0; i < rawOrders.length; i++) {
|
|
406
|
+
let key = rawOrders[i];
|
|
407
|
+
let reverse = 1;
|
|
408
|
+
const match = key.match(/\s+(A|DE)SC$/i);
|
|
409
|
+
if (match) {
|
|
410
|
+
key = key.replace(/\s+(A|DE)SC/i, "");
|
|
411
|
+
if (match[1].toLowerCase() === "de") reverse = -1;
|
|
412
|
+
}
|
|
413
|
+
orders[i] = {
|
|
414
|
+
key,
|
|
415
|
+
keys: key.indexOf(".") === -1 ? null : key.split("."),
|
|
416
|
+
reverse
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
return orders;
|
|
420
|
+
}
|
|
421
|
+
function createSorter(orders) {
|
|
422
|
+
return function sorting(a, b) {
|
|
423
|
+
let undefinedA;
|
|
424
|
+
let undefinedB;
|
|
425
|
+
for (let i = 0, l = orders.length; i < l; i++) {
|
|
426
|
+
const order = orders[i];
|
|
427
|
+
const aVal = getValue(a, order.key, order.keys);
|
|
428
|
+
const bVal = getValue(b, order.key, order.keys);
|
|
429
|
+
undefinedB = bVal === void 0 && aVal !== void 0;
|
|
430
|
+
undefinedA = aVal === void 0 && bVal !== void 0;
|
|
431
|
+
if (undefinedB || aVal > bVal) return order.reverse;
|
|
432
|
+
else if (undefinedA || aVal < bVal) return -order.reverse;
|
|
433
|
+
}
|
|
434
|
+
return 0;
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
Memory.prototype._findAllSkippingIncludes = function(model, filter) {
|
|
438
|
+
const collection = this.collection(model);
|
|
439
|
+
let nodes = [];
|
|
440
|
+
for (const key in collection) if (hasOwnProperty.call(collection, key)) nodes.push(this.fromDb(model, collection[key]));
|
|
441
|
+
if (filter) {
|
|
442
|
+
let order = filter.order;
|
|
443
|
+
if (!order) {
|
|
444
|
+
const idNames = this.idNames(model);
|
|
445
|
+
if (idNames && idNames.length) order = idNames;
|
|
446
|
+
}
|
|
447
|
+
if (order) nodes = nodes.sort(createSorter(normalizeSortOrders(order)));
|
|
448
|
+
const nearFilter = geo.nearFilter(filter.where);
|
|
449
|
+
if (nearFilter) nodes = geo.filter(nodes, nearFilter);
|
|
450
|
+
if (filter.where && nodes.length > 0) {
|
|
451
|
+
const predicate = applyFilter(filter);
|
|
452
|
+
const filteredNodes = [];
|
|
453
|
+
for (let i = 0; i < nodes.length; i++) if (predicate(nodes[i])) filteredNodes.push(nodes[i]);
|
|
454
|
+
nodes = filteredNodes;
|
|
455
|
+
}
|
|
456
|
+
if (filter.fields) {
|
|
457
|
+
const selectFields = utils.selectFields(filter.fields);
|
|
458
|
+
const selectedNodes = new Array(nodes.length);
|
|
459
|
+
for (let i = 0; i < nodes.length; i++) selectedNodes[i] = selectFields(nodes[i]);
|
|
460
|
+
nodes = selectedNodes;
|
|
461
|
+
}
|
|
462
|
+
const skip = filter.skip || filter.offset || 0;
|
|
463
|
+
const limit = filter.limit || nodes.length;
|
|
464
|
+
if (skip !== 0 || limit < nodes.length) nodes = nodes.slice(skip, skip + limit);
|
|
465
|
+
}
|
|
466
|
+
return nodes;
|
|
467
|
+
};
|
|
468
|
+
Memory.prototype.all = function all(model, filter, options, callback) {
|
|
469
|
+
const self = this;
|
|
470
|
+
const nodes = self._findAllSkippingIncludes(model, filter);
|
|
471
|
+
process.nextTick(function() {
|
|
472
|
+
if (filter && filter.include) self._models[model].model.include(nodes, filter.include, options, callback);
|
|
473
|
+
else callback(null, nodes);
|
|
474
|
+
});
|
|
475
|
+
};
|
|
476
|
+
function applyFilter(filter) {
|
|
477
|
+
const where = filter.where;
|
|
478
|
+
if (typeof where === "function") return where;
|
|
479
|
+
return createWherePredicate(where);
|
|
480
|
+
}
|
|
481
|
+
function createWherePredicate(where) {
|
|
482
|
+
const keys = Object.keys(where);
|
|
483
|
+
const predicates = new Array(keys.length);
|
|
484
|
+
for (let i = 0; i < keys.length; i++) {
|
|
485
|
+
const key = keys[i];
|
|
486
|
+
const value = where[key];
|
|
487
|
+
if ((key === "and" || key === "or") && Array.isArray(value)) {
|
|
488
|
+
const nestedPredicates = new Array(value.length);
|
|
489
|
+
for (let j = 0; j < value.length; j++) nestedPredicates[j] = createWherePredicate(value[j]);
|
|
490
|
+
predicates[i] = key === "and" ? function(obj) {
|
|
491
|
+
for (let j = 0; j < nestedPredicates.length; j++) if (!nestedPredicates[j](obj)) return false;
|
|
492
|
+
return true;
|
|
493
|
+
} : function(obj) {
|
|
494
|
+
for (let j = 0; j < nestedPredicates.length; j++) if (nestedPredicates[j](obj)) return true;
|
|
495
|
+
return false;
|
|
496
|
+
};
|
|
497
|
+
continue;
|
|
498
|
+
}
|
|
499
|
+
predicates[i] = createPropertyPredicate(key, value);
|
|
500
|
+
}
|
|
501
|
+
if (predicates.length === 1) return predicates[0];
|
|
502
|
+
return function(obj) {
|
|
503
|
+
for (let i = 0; i < predicates.length; i++) if (!predicates[i](obj)) return false;
|
|
504
|
+
return true;
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
function createPropertyPredicate(key, matcher) {
|
|
508
|
+
const dotIndex = key.indexOf(".");
|
|
509
|
+
const keyPath = dotIndex === -1 ? null : key.split(".");
|
|
510
|
+
const matchValue = createValueMatcher(matcher);
|
|
511
|
+
let subPredicate;
|
|
512
|
+
let subKey;
|
|
513
|
+
if (dotIndex !== -1) {
|
|
514
|
+
subKey = key.substring(0, dotIndex);
|
|
515
|
+
const nestedWhere = {};
|
|
516
|
+
nestedWhere[key.substring(dotIndex + 1)] = matcher;
|
|
517
|
+
subPredicate = createWherePredicate(nestedWhere);
|
|
518
|
+
}
|
|
519
|
+
return function(obj) {
|
|
520
|
+
const value = getValue(obj, key, keyPath);
|
|
521
|
+
if (Array.isArray(value)) {
|
|
522
|
+
if (matcher && typeof matcher === "object" && matcher !== null && matcher.neq !== void 0) {
|
|
523
|
+
if (value.length <= 0) return true;
|
|
524
|
+
}
|
|
525
|
+
for (let i = 0; i < value.length; i++) if (matchValue(value[i])) return true;
|
|
526
|
+
return false;
|
|
527
|
+
}
|
|
528
|
+
if (matchValue(value)) return true;
|
|
529
|
+
if (!subPredicate) return false;
|
|
530
|
+
const subValue = obj[subKey];
|
|
531
|
+
if (Array.isArray(subValue)) {
|
|
532
|
+
for (let i = 0; i < subValue.length; i++) if (subPredicate(subValue[i])) return true;
|
|
533
|
+
return false;
|
|
534
|
+
}
|
|
535
|
+
if (typeof subValue === "object" && subValue !== null) return subPredicate(subValue);
|
|
536
|
+
return false;
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
function likeToRegExp(pattern) {
|
|
540
|
+
if (pattern instanceof RegExp) return pattern;
|
|
541
|
+
let regex = "";
|
|
542
|
+
pattern = pattern.replace(/([.*+?^=!:${}()|[\]/\\])/g, "\\$1");
|
|
543
|
+
for (let i = 0, n = pattern.length; i < n; i++) {
|
|
544
|
+
const char = pattern.charAt(i);
|
|
545
|
+
if (char === "\\") {
|
|
546
|
+
i++;
|
|
547
|
+
if (i < n) regex += pattern.charAt(i);
|
|
548
|
+
continue;
|
|
549
|
+
} else if (char === "%") regex += ".*";
|
|
550
|
+
else if (char === "_") regex += ".";
|
|
551
|
+
else if (char === ".") regex += "\\.";
|
|
552
|
+
else if (char === "*") regex += "\\*";
|
|
553
|
+
else regex += char;
|
|
554
|
+
}
|
|
555
|
+
return regex;
|
|
556
|
+
}
|
|
557
|
+
function createValueMatcher(example) {
|
|
558
|
+
if (example instanceof RegExp) return function(value) {
|
|
559
|
+
return typeof value === "string" ? value.match(example) : false;
|
|
560
|
+
};
|
|
561
|
+
if (example == null) return function(value) {
|
|
562
|
+
return value == null;
|
|
563
|
+
};
|
|
564
|
+
if (typeof example === "object" && example !== null) {
|
|
565
|
+
if (example.regexp) {
|
|
566
|
+
const regexp = example.regexp;
|
|
567
|
+
return function(value) {
|
|
568
|
+
return value ? value.match(regexp) : false;
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
if (example.near) return function() {
|
|
572
|
+
return true;
|
|
573
|
+
};
|
|
574
|
+
if (example.inq) {
|
|
575
|
+
const inq = example.inq;
|
|
576
|
+
return function(value) {
|
|
577
|
+
for (let i = 0; i < inq.length; i++) if (inq[i] == value) return true;
|
|
578
|
+
return false;
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
if (example.nin) {
|
|
582
|
+
const nin = example.nin;
|
|
583
|
+
return function(value) {
|
|
584
|
+
for (let i = 0; i < nin.length; i++) if (nin[i] == value) return false;
|
|
585
|
+
return true;
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
if ("neq" in example) {
|
|
589
|
+
const neq = example.neq;
|
|
590
|
+
return function(value) {
|
|
591
|
+
return compare(neq, value) !== 0;
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
if ("between" in example) {
|
|
595
|
+
const lower = example.between[0];
|
|
596
|
+
const upper = example.between[1];
|
|
597
|
+
return function(value) {
|
|
598
|
+
return compare(value, lower) >= 0 && compare(value, upper) <= 0;
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
if (example.like || example.nlike || example.ilike || example.nilike) {
|
|
602
|
+
let like = example.like || example.nlike || example.ilike || example.nilike;
|
|
603
|
+
if (typeof like === "string") like = likeToRegExp(like);
|
|
604
|
+
const regexp = example.ilike || example.nilike ? new RegExp(like, "i") : new RegExp(like);
|
|
605
|
+
const negate = !!(example.nlike || example.nilike);
|
|
606
|
+
return function(value) {
|
|
607
|
+
regexp.lastIndex = 0;
|
|
608
|
+
const matches = regexp.test(value);
|
|
609
|
+
return negate ? !matches : matches;
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
const inequalityMatcher = createInequalityMatcher(example);
|
|
613
|
+
if (inequalityMatcher) return inequalityMatcher;
|
|
614
|
+
}
|
|
615
|
+
if (example instanceof Date) return function(value) {
|
|
616
|
+
if (value instanceof Date) return example.getTime() === value.getTime();
|
|
617
|
+
return example == value;
|
|
618
|
+
};
|
|
619
|
+
return function(value) {
|
|
620
|
+
return example == value;
|
|
621
|
+
};
|
|
622
|
+
}
|
|
623
|
+
function createInequalityMatcher(example) {
|
|
624
|
+
if ("gt" in example) {
|
|
625
|
+
const gt = example.gt;
|
|
626
|
+
return function(value) {
|
|
627
|
+
return compare(value, gt) > 0;
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
if ("gte" in example) {
|
|
631
|
+
const gte = example.gte;
|
|
632
|
+
return function(value) {
|
|
633
|
+
return compare(value, gte) >= 0;
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
if ("lt" in example) {
|
|
637
|
+
const lt = example.lt;
|
|
638
|
+
return function(value) {
|
|
639
|
+
return compare(value, lt) < 0;
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
if ("lte" in example) {
|
|
643
|
+
const lte = example.lte;
|
|
644
|
+
return function(value) {
|
|
645
|
+
return compare(value, lte) <= 0;
|
|
646
|
+
};
|
|
647
|
+
}
|
|
648
|
+
return null;
|
|
649
|
+
}
|
|
650
|
+
function compare(val1, val2) {
|
|
651
|
+
if (val1 == null || val2 == null) return val1 == val2 ? 0 : NaN;
|
|
652
|
+
if (typeof val1 === "number") return val1 - val2;
|
|
653
|
+
if (typeof val1 === "string") return val1 > val2 ? 1 : val1 < val2 ? -1 : val1 == val2 ? 0 : NaN;
|
|
654
|
+
if (typeof val1 === "boolean") return val1 - val2;
|
|
655
|
+
if (val1 instanceof Date) return val1 - val2;
|
|
656
|
+
return val1 == val2 ? 0 : NaN;
|
|
657
|
+
}
|
|
658
|
+
Memory.prototype.destroyAll = function destroyAll(model, where, options, callback) {
|
|
659
|
+
const cache = this.collection(model);
|
|
660
|
+
let count = 0;
|
|
661
|
+
if (where) {
|
|
662
|
+
const filter = applyFilter({ where });
|
|
663
|
+
for (const id in cache) if (hasOwnProperty.call(cache, id) && filter(this.fromDb(model, cache[id]))) {
|
|
664
|
+
count++;
|
|
665
|
+
delete cache[id];
|
|
666
|
+
}
|
|
667
|
+
} else {
|
|
668
|
+
for (const id in cache) if (hasOwnProperty.call(cache, id)) count++;
|
|
669
|
+
this.collection(model, {});
|
|
670
|
+
}
|
|
671
|
+
this.saveToFile({ count }, callback);
|
|
672
|
+
};
|
|
673
|
+
Memory.prototype.count = function count(model, where, options, callback) {
|
|
674
|
+
const cache = this.collection(model);
|
|
675
|
+
let count = 0;
|
|
676
|
+
if (where) {
|
|
677
|
+
const filter = applyFilter({ where });
|
|
678
|
+
for (const id in cache) if (hasOwnProperty.call(cache, id) && filter(this.fromDb(model, cache[id]))) count++;
|
|
679
|
+
} else for (const id in cache) if (hasOwnProperty.call(cache, id)) count++;
|
|
680
|
+
process.nextTick(function() {
|
|
681
|
+
callback(null, count);
|
|
682
|
+
});
|
|
683
|
+
};
|
|
684
|
+
Memory.prototype.update = Memory.prototype.updateAll = function updateAll(model, where, data, options, cb) {
|
|
685
|
+
const self = this;
|
|
686
|
+
const cache = this.collection(model);
|
|
687
|
+
let filter = null;
|
|
688
|
+
where = where || {};
|
|
689
|
+
filter = applyFilter({ where });
|
|
690
|
+
const ids = Object.keys(cache);
|
|
691
|
+
let count = 0;
|
|
692
|
+
eachParallel(ids, function(id, done) {
|
|
693
|
+
const inst = self.fromDb(model, cache[id]);
|
|
694
|
+
if (!filter || filter(inst)) {
|
|
695
|
+
count++;
|
|
696
|
+
id = self.getIdValue(model, inst);
|
|
697
|
+
self.updateAttributes(model, id, data, options, done);
|
|
698
|
+
} else process.nextTick(done);
|
|
699
|
+
}).then(function() {
|
|
700
|
+
self.saveToFile({ count }, cb);
|
|
701
|
+
}, function(err) {
|
|
702
|
+
if (err) return cb(err);
|
|
703
|
+
});
|
|
704
|
+
};
|
|
705
|
+
Memory.prototype.updateAttributes = function updateAttributes(model, id, data, options, cb) {
|
|
706
|
+
if (!id) {
|
|
707
|
+
const err = new Error(g.f("You must provide an {{id}} when updating attributes!"));
|
|
708
|
+
if (cb) return cb(err);
|
|
709
|
+
else throw err;
|
|
710
|
+
}
|
|
711
|
+
data = Object.create(data);
|
|
712
|
+
this.setIdValue(model, data, id);
|
|
713
|
+
if (this.collection(model) && this.collection(model)[id]) this.save(model, data, options, cb);
|
|
714
|
+
else {
|
|
715
|
+
const msg = g.f("Could not update attributes. {{Object}} with {{id}} %s does not exist!", id);
|
|
716
|
+
const error = new Error(msg);
|
|
717
|
+
error.statusCode = error.status = 404;
|
|
718
|
+
cb(error);
|
|
719
|
+
}
|
|
720
|
+
};
|
|
721
|
+
Memory.prototype.replaceById = function(model, id, data, options, cb) {
|
|
722
|
+
const self = this;
|
|
723
|
+
if (!id) {
|
|
724
|
+
const err = new Error(g.f("You must provide an {{id}} when replacing!"));
|
|
725
|
+
return process.nextTick(function() {
|
|
726
|
+
cb(err);
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
data = Object.create(data);
|
|
730
|
+
this.setIdValue(model, data, id);
|
|
731
|
+
if (!(this.collection(model) && this.collection(model)[id])) {
|
|
732
|
+
const msg = "Could not replace. Object with id " + id + " does not exist!";
|
|
733
|
+
const error = new Error(msg);
|
|
734
|
+
error.statusCode = error.status = 404;
|
|
735
|
+
return process.nextTick(function() {
|
|
736
|
+
cb(error);
|
|
737
|
+
});
|
|
738
|
+
}
|
|
739
|
+
const newModelData = {};
|
|
740
|
+
for (const key in data) {
|
|
741
|
+
const val = data[key];
|
|
742
|
+
if (typeof val === "function") continue;
|
|
743
|
+
newModelData[key] = val;
|
|
744
|
+
}
|
|
745
|
+
this.collection(model)[id] = serialize(newModelData);
|
|
746
|
+
this.saveToFile(newModelData, function(err) {
|
|
747
|
+
cb(err, self.fromDb(model, newModelData));
|
|
748
|
+
});
|
|
749
|
+
};
|
|
750
|
+
Memory.prototype.replaceOrCreate = function(model, data, options, callback) {
|
|
751
|
+
const self = this;
|
|
752
|
+
const idName = self.idNames(model)[0];
|
|
753
|
+
const idValue = self.getIdValue(model, data);
|
|
754
|
+
const filter = { where: {} };
|
|
755
|
+
filter.where[idName] = idValue;
|
|
756
|
+
if (!self._findAllSkippingIncludes(model, filter)[0]) return self._createSync(model, data, function(err, id) {
|
|
757
|
+
if (err) return process.nextTick(function() {
|
|
758
|
+
callback(err);
|
|
759
|
+
});
|
|
760
|
+
self.saveToFile(id, function(err, id) {
|
|
761
|
+
self.setIdValue(model, data, id);
|
|
762
|
+
callback(err, self.fromDb(model, data), { isNewInstance: true });
|
|
763
|
+
});
|
|
764
|
+
});
|
|
765
|
+
const id = self.getIdValue(model, data);
|
|
766
|
+
self.collection(model)[id] = serialize(data);
|
|
767
|
+
self.saveToFile(data, function(err) {
|
|
768
|
+
callback(err, self.fromDb(model, data), { isNewInstance: false });
|
|
769
|
+
});
|
|
770
|
+
};
|
|
771
|
+
Memory.prototype.transaction = function() {
|
|
772
|
+
return new Memory(this);
|
|
773
|
+
};
|
|
774
|
+
Memory.prototype.exec = function(callback) {
|
|
775
|
+
this.onTransactionExec();
|
|
776
|
+
setTimeout(callback, 50);
|
|
777
|
+
};
|
|
778
|
+
Memory.prototype.buildNearFilter = function(_filter) {};
|
|
779
|
+
Memory.prototype.automigrate = function(models, cb) {
|
|
780
|
+
const self = this;
|
|
781
|
+
if (!cb && "function" === typeof models) {
|
|
782
|
+
cb = models;
|
|
783
|
+
models = void 0;
|
|
784
|
+
}
|
|
785
|
+
if ("string" === typeof models) models = [models];
|
|
786
|
+
models = models || Object.keys(self._models);
|
|
787
|
+
if (models.length === 0) return process.nextTick(cb);
|
|
788
|
+
const invalidModels = models.filter(function(m) {
|
|
789
|
+
return !(m in self._models);
|
|
790
|
+
});
|
|
791
|
+
if (invalidModels.length) return process.nextTick(function() {
|
|
792
|
+
cb(new Error(g.f("Cannot migrate models not attached to this datasource: %s", invalidModels.join(" "))));
|
|
793
|
+
});
|
|
794
|
+
models.forEach(function(m) {
|
|
795
|
+
self.initCollection(m);
|
|
796
|
+
});
|
|
797
|
+
if (cb) process.nextTick(cb);
|
|
798
|
+
};
|
|
799
|
+
function merge(base, update) {
|
|
800
|
+
if (!base) return update;
|
|
801
|
+
for (const key in update) {
|
|
802
|
+
const val = update[key];
|
|
803
|
+
if (typeof val === "function") continue;
|
|
804
|
+
base[key] = val;
|
|
805
|
+
}
|
|
806
|
+
return base;
|
|
807
|
+
}
|
|
808
|
+
}));
|
|
809
|
+
//#endregion
|
|
810
|
+
module.exports = require_memory();
|