@nymphjs/nymph 1.0.0-beta.55 → 1.0.0-beta.57
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/CHANGELOG.md +12 -0
- package/dist/Entity.d.ts +191 -0
- package/dist/Entity.js +179 -0
- package/dist/Entity.js.map +1 -1
- package/dist/Entity.test.js +6 -0
- package/dist/Entity.test.js.map +1 -1
- package/dist/Entity.types.d.ts +258 -0
- package/dist/Nymph.d.ts +389 -0
- package/dist/Nymph.js +501 -179
- package/dist/Nymph.js.map +1 -1
- package/dist/Nymph.types.d.ts +12 -1
- package/dist/Nymph.types.js +1 -0
- package/dist/Nymph.types.js.map +1 -1
- package/dist/conf/d.d.ts +26 -0
- package/dist/conf/defaults.js +22 -0
- package/dist/conf/defaults.js.map +1 -1
- package/dist/driver/NymphDriver.d.ts +61 -1
- package/dist/driver/NymphDriver.js +220 -123
- package/dist/driver/NymphDriver.js.map +1 -1
- package/dist/driver/testArtifacts.js +142 -0
- package/dist/driver/testArtifacts.js.map +1 -1
- package/dist/errors/EntityInvalidDataError.d.ts +6 -0
- package/dist/errors/EntityInvalidDataError.js +6 -0
- package/dist/errors/EntityInvalidDataError.js.map +1 -1
- package/dist/testArtifacts.d.ts +9 -0
- package/dist/testArtifacts.js +9 -0
- package/dist/testArtifacts.js.map +1 -1
- package/dist/testMocks.js +2 -0
- package/dist/testMocks.js.map +1 -1
- package/dist/utils.js +10 -0
- package/dist/utils.js.map +1 -1
- package/package.json +5 -3
- package/src/Nymph.ts +281 -177
- package/src/Nymph.types.ts +1 -1
- package/src/conf/d.ts +8 -0
- package/src/conf/defaults.ts +24 -0
- package/src/driver/NymphDriver.ts +198 -183
- package/tsconfig.json +1 -1
|
@@ -10,9 +10,13 @@ const strtotime_1 = __importDefault(require("locutus/php/datetime/strtotime"));
|
|
|
10
10
|
const guid_1 = require("@nymphjs/guid");
|
|
11
11
|
const errors_1 = require("../errors");
|
|
12
12
|
const utils_1 = require("../utils");
|
|
13
|
+
// from: https://stackoverflow.com/a/6969486/664915
|
|
13
14
|
function escapeRegExp(string) {
|
|
14
|
-
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
15
|
+
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
|
|
15
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* A Nymph database driver.
|
|
19
|
+
*/
|
|
16
20
|
class NymphDriver {
|
|
17
21
|
constructor() {
|
|
18
22
|
this.nymph = new Proxy({}, {
|
|
@@ -20,15 +24,33 @@ class NymphDriver {
|
|
|
20
24
|
throw new Error('Attempted access of Nymph instance before driver initialization!');
|
|
21
25
|
},
|
|
22
26
|
});
|
|
27
|
+
/**
|
|
28
|
+
* A cache to make entity retrieval faster.
|
|
29
|
+
*/
|
|
23
30
|
this.entityCache = {};
|
|
31
|
+
/**
|
|
32
|
+
* A counter for the entity cache to determine the most accessed entities.
|
|
33
|
+
*/
|
|
24
34
|
this.entityCount = {};
|
|
35
|
+
/**
|
|
36
|
+
* Protect against infinite loops.
|
|
37
|
+
*/
|
|
25
38
|
this.putDataCounter = 0;
|
|
26
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* Initialize the Nymph driver.
|
|
42
|
+
*
|
|
43
|
+
* This is meant to be called internally by Nymph. Don't call this directly.
|
|
44
|
+
*
|
|
45
|
+
* @param nymph The Nymph instance.
|
|
46
|
+
*/
|
|
27
47
|
init(nymph) {
|
|
28
48
|
this.nymph = nymph;
|
|
29
49
|
}
|
|
30
50
|
posixRegexMatch(pattern, subject, caseInsensitive = false) {
|
|
31
51
|
const posixClasses = [
|
|
52
|
+
// '[[:<:]]',
|
|
53
|
+
// '[[:>:]]',
|
|
32
54
|
'[:alnum:]',
|
|
33
55
|
'[:alpha:]',
|
|
34
56
|
'[:ascii:]',
|
|
@@ -45,17 +67,21 @@ class NymphDriver {
|
|
|
45
67
|
'[:xdigit:]',
|
|
46
68
|
];
|
|
47
69
|
const jsClasses = [
|
|
70
|
+
// '\b(?=\w)',
|
|
71
|
+
// '(?<=\w)\b',
|
|
48
72
|
'[A-Za-z0-9]',
|
|
49
73
|
'[A-Za-z]',
|
|
50
74
|
'[\x00-\x7F]',
|
|
51
|
-
'[ \t]',
|
|
52
|
-
'[\x00-\x1F\x7F]',
|
|
53
|
-
'
|
|
54
|
-
'
|
|
75
|
+
'[ \t]', // '\s',
|
|
76
|
+
'[\x00-\x1F\x7F]', // '[\000\001\002\003\004\005\006\007\008\009\010\011\012\013\014'.
|
|
77
|
+
// '\015\016\017\018\019\020\021\022\023\024\025\026\027\028\029'.
|
|
78
|
+
// '\030\031\032\033\034\035\036\037\177]',
|
|
79
|
+
'[0-9]', // '\d',
|
|
80
|
+
'[\x21-\x7E]', // '[A-Za-z0-9!"#$%&\'()*+,\-./:;<=>?@[\\\]^_`{|}\~]',
|
|
55
81
|
'[a-z]',
|
|
56
|
-
'[\x20-\x7E]',
|
|
57
|
-
'[!"#$%&\'()*+,-./:;<=>?@[\\\\\\]^_‘{|}~]',
|
|
58
|
-
'[ \t\r\n\v\f]',
|
|
82
|
+
'[\x20-\x7E]', // '[A-Za-z0-9!"#$%&\'()*+,\-./:;<=>?@[\\\]^_`{|}\~]',
|
|
83
|
+
'[!"#$%&\'()*+,-./:;<=>?@[\\\\\\]^_‘{|}~]', // '[!"#$%&\'()*+,\-./:;<=>?@[\\\]^_`{|}\~]',
|
|
84
|
+
'[ \t\r\n\v\f]', // '[\t\n\x0B\f\r ]',
|
|
59
85
|
'[A-Z]',
|
|
60
86
|
'[A-Za-z0-9_]',
|
|
61
87
|
'[0-9A-Fa-f]',
|
|
@@ -114,9 +140,11 @@ class NymphDriver {
|
|
|
114
140
|
const propMatch = text.match(/^\s*([^=]+)\s*=\s*(.*\S)\s*$/);
|
|
115
141
|
const uidMatch = text.match(/^\s*<([^>]+)>\[(\d+)\]\s*$/);
|
|
116
142
|
if (uidMatch) {
|
|
143
|
+
// Add the UID.
|
|
117
144
|
await saveUIDCallback(uidMatch[1], Number(uidMatch[2]));
|
|
118
145
|
}
|
|
119
146
|
else if (entityMatch) {
|
|
147
|
+
// Save the current entity.
|
|
120
148
|
if (guid) {
|
|
121
149
|
await saveEntityCallback(guid, tags, sdata, etype);
|
|
122
150
|
guid = null;
|
|
@@ -124,17 +152,21 @@ class NymphDriver {
|
|
|
124
152
|
sdata = {};
|
|
125
153
|
etype = '__undefined';
|
|
126
154
|
}
|
|
155
|
+
// Record the new entity's info.
|
|
127
156
|
guid = entityMatch[1];
|
|
128
157
|
etype = entityMatch[2];
|
|
129
158
|
tags = entityMatch[3].split(',');
|
|
130
159
|
}
|
|
131
160
|
else if (propMatch) {
|
|
161
|
+
// Add the variable to the new entity.
|
|
132
162
|
if (guid) {
|
|
133
163
|
sdata[propMatch[1]] = propMatch[2];
|
|
134
164
|
}
|
|
135
165
|
}
|
|
166
|
+
// Clear the entity cache.
|
|
136
167
|
this.entityCache = {};
|
|
137
168
|
}
|
|
169
|
+
// Save the last entity.
|
|
138
170
|
if (guid) {
|
|
139
171
|
await saveEntityCallback(guid, tags, sdata, etype);
|
|
140
172
|
}
|
|
@@ -144,132 +176,147 @@ class NymphDriver {
|
|
|
144
176
|
return true;
|
|
145
177
|
}
|
|
146
178
|
checkData(data, sdata, selectors, guid = null, tags = null) {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
else {
|
|
165
|
-
if (key === 'qref' || key === '!qref') {
|
|
166
|
-
throw new Error("Can't use checkData on qref clauses.");
|
|
179
|
+
try {
|
|
180
|
+
const formattedSelectors = this.formatSelectors(selectors).selectors;
|
|
181
|
+
for (const curSelector of formattedSelectors) {
|
|
182
|
+
const type = curSelector.type;
|
|
183
|
+
const typeIsNot = type === '!&' || type === '!|';
|
|
184
|
+
const typeIsOr = type === '|' || type === '!|';
|
|
185
|
+
let pass = !typeIsOr;
|
|
186
|
+
for (const k in curSelector) {
|
|
187
|
+
const key = k;
|
|
188
|
+
const value = curSelector[key];
|
|
189
|
+
if (key === 'type' || value == null) {
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
const clauseNot = key.substring(0, 1) === '!';
|
|
193
|
+
if (key === 'selector' || key === '!selector') {
|
|
194
|
+
const tmpArr = (Array.isArray(value) ? value : [value]);
|
|
195
|
+
pass = (0, utils_1.xor)(this.checkData(data, sdata, tmpArr, guid, tags), (0, utils_1.xor)(typeIsNot, clauseNot));
|
|
167
196
|
}
|
|
168
197
|
else {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
198
|
+
if (key === 'qref' || key === '!qref') {
|
|
199
|
+
throw new Error("Can't use checkData on qref clauses.");
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
// Check if it doesn't pass any for &, check if it passes any for |.
|
|
203
|
+
for (const curValue of value) {
|
|
204
|
+
if (curValue == null) {
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
const propName = curValue[0];
|
|
208
|
+
// Unserialize the data for this variable.
|
|
209
|
+
if (propName in sdata) {
|
|
210
|
+
data[propName] = JSON.parse(sdata[propName]);
|
|
211
|
+
delete sdata[propName];
|
|
212
|
+
}
|
|
213
|
+
switch (key) {
|
|
214
|
+
case 'guid':
|
|
215
|
+
case '!guid':
|
|
216
|
+
pass = (0, utils_1.xor)(guid == propName, (0, utils_1.xor)(typeIsNot, clauseNot));
|
|
217
|
+
break;
|
|
218
|
+
case 'tag':
|
|
219
|
+
case '!tag':
|
|
220
|
+
pass = (0, utils_1.xor)((tags ?? []).indexOf(propName) !== -1, (0, utils_1.xor)(typeIsNot, clauseNot));
|
|
221
|
+
break;
|
|
222
|
+
case 'defined':
|
|
223
|
+
case '!defined':
|
|
224
|
+
pass = (0, utils_1.xor)(propName in data, (0, utils_1.xor)(typeIsNot, clauseNot));
|
|
225
|
+
break;
|
|
226
|
+
case 'truthy':
|
|
227
|
+
case '!truthy':
|
|
228
|
+
pass = (0, utils_1.xor)(propName in data && data[propName], (0, utils_1.xor)(typeIsNot, clauseNot));
|
|
229
|
+
break;
|
|
230
|
+
case 'ref':
|
|
231
|
+
case '!ref':
|
|
232
|
+
const testRefValue = curValue[1];
|
|
233
|
+
pass = (0, utils_1.xor)(propName in data &&
|
|
234
|
+
this.entityReferenceSearch(data[propName], testRefValue), (0, utils_1.xor)(typeIsNot, clauseNot));
|
|
235
|
+
break;
|
|
236
|
+
case 'equal':
|
|
237
|
+
case '!equal':
|
|
238
|
+
const testEqualValue = curValue[1];
|
|
239
|
+
pass = (0, utils_1.xor)(propName in data &&
|
|
240
|
+
JSON.stringify(data[propName]) ===
|
|
241
|
+
JSON.stringify(testEqualValue), (0, utils_1.xor)(typeIsNot, clauseNot));
|
|
242
|
+
break;
|
|
243
|
+
case 'contain':
|
|
244
|
+
case '!contain':
|
|
245
|
+
const testContainValue = curValue[1];
|
|
246
|
+
pass = (0, utils_1.xor)(propName in data &&
|
|
247
|
+
JSON.stringify(data[propName]).indexOf(JSON.stringify(testContainValue)) !== -1, (0, utils_1.xor)(typeIsNot, clauseNot));
|
|
248
|
+
break;
|
|
249
|
+
case 'like':
|
|
250
|
+
case '!like':
|
|
251
|
+
const testLikeValue = curValue[1];
|
|
252
|
+
pass = (0, utils_1.xor)(propName in data &&
|
|
253
|
+
new RegExp('^' +
|
|
254
|
+
escapeRegExp(testLikeValue)
|
|
255
|
+
.replace('%', () => '.*')
|
|
256
|
+
.replace('_', () => '.') +
|
|
257
|
+
'$').test(data[propName]), (0, utils_1.xor)(typeIsNot, clauseNot));
|
|
258
|
+
break;
|
|
259
|
+
case 'match':
|
|
260
|
+
case '!match':
|
|
261
|
+
const testMatchValue = curValue[1];
|
|
262
|
+
// Convert a POSIX regex to a JS regex.
|
|
263
|
+
pass = (0, utils_1.xor)(propName in data &&
|
|
264
|
+
this.posixRegexMatch(testMatchValue, data[propName]), (0, utils_1.xor)(typeIsNot, clauseNot));
|
|
265
|
+
break;
|
|
266
|
+
case 'imatch':
|
|
267
|
+
case '!imatch':
|
|
268
|
+
const testIMatchValue = curValue[1];
|
|
269
|
+
// Convert a POSIX regex to a JS regex.
|
|
270
|
+
pass = (0, utils_1.xor)(propName in data &&
|
|
271
|
+
this.posixRegexMatch(testIMatchValue, data[propName], true), (0, utils_1.xor)(typeIsNot, clauseNot));
|
|
272
|
+
break;
|
|
273
|
+
case 'gt':
|
|
274
|
+
case '!gt':
|
|
275
|
+
const testGTValue = curValue[1];
|
|
276
|
+
pass = (0, utils_1.xor)(propName in data && data[propName] > testGTValue, (0, utils_1.xor)(typeIsNot, clauseNot));
|
|
277
|
+
break;
|
|
278
|
+
case 'gte':
|
|
279
|
+
case '!gte':
|
|
280
|
+
const testGTEValue = curValue[1];
|
|
281
|
+
pass = (0, utils_1.xor)(propName in data && data[propName] >= testGTEValue, (0, utils_1.xor)(typeIsNot, clauseNot));
|
|
282
|
+
break;
|
|
283
|
+
case 'lt':
|
|
284
|
+
case '!lt':
|
|
285
|
+
const testLTValue = curValue[1];
|
|
286
|
+
pass = (0, utils_1.xor)(propName in data && data[propName] < testLTValue, (0, utils_1.xor)(typeIsNot, clauseNot));
|
|
287
|
+
break;
|
|
288
|
+
case 'lte':
|
|
289
|
+
case '!lte':
|
|
290
|
+
const testLTEValue = curValue[1];
|
|
291
|
+
pass = (0, utils_1.xor)(propName in data && data[propName] <= testLTEValue, (0, utils_1.xor)(typeIsNot, clauseNot));
|
|
292
|
+
break;
|
|
293
|
+
}
|
|
294
|
+
if (!(0, utils_1.xor)(typeIsOr, pass)) {
|
|
255
295
|
break;
|
|
256
|
-
|
|
257
|
-
if (!(0, utils_1.xor)(typeIsOr, pass)) {
|
|
258
|
-
break;
|
|
296
|
+
}
|
|
259
297
|
}
|
|
260
298
|
}
|
|
261
299
|
}
|
|
300
|
+
if (!(0, utils_1.xor)(typeIsOr, pass)) {
|
|
301
|
+
break;
|
|
302
|
+
}
|
|
262
303
|
}
|
|
263
|
-
if (!
|
|
264
|
-
|
|
304
|
+
if (!pass) {
|
|
305
|
+
return false;
|
|
265
306
|
}
|
|
266
307
|
}
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
308
|
+
return true;
|
|
309
|
+
}
|
|
310
|
+
catch (e) {
|
|
311
|
+
this.nymph.config.debugError('nymph', `Failed to check entity data: ${e}`);
|
|
312
|
+
throw e;
|
|
270
313
|
}
|
|
271
|
-
return true;
|
|
272
314
|
}
|
|
315
|
+
/**
|
|
316
|
+
* Remove all copies of an entity from the cache.
|
|
317
|
+
*
|
|
318
|
+
* @param guid The GUID of the entity to remove.
|
|
319
|
+
*/
|
|
273
320
|
cleanCache(guid) {
|
|
274
321
|
delete this.entityCache[guid];
|
|
275
322
|
}
|
|
@@ -286,7 +333,15 @@ class NymphDriver {
|
|
|
286
333
|
}
|
|
287
334
|
return ret;
|
|
288
335
|
}
|
|
336
|
+
/**
|
|
337
|
+
* Search through a value for an entity reference.
|
|
338
|
+
*
|
|
339
|
+
* @param value Any value to search.
|
|
340
|
+
* @param entity An entity, GUID, or array of either to search for.
|
|
341
|
+
* @returns True if the reference is found, false otherwise.
|
|
342
|
+
*/
|
|
289
343
|
entityReferenceSearch(value, entity) {
|
|
344
|
+
// Get the GUID, if the passed $entity is an object.
|
|
290
345
|
const guids = [];
|
|
291
346
|
if (Array.isArray(entity)) {
|
|
292
347
|
if (entity[0] === 'nymph_entity_reference') {
|
|
@@ -317,6 +372,7 @@ class NymphDriver {
|
|
|
317
372
|
guids.indexOf(value[1]) !== -1) {
|
|
318
373
|
return true;
|
|
319
374
|
}
|
|
375
|
+
// Search through arrays and objects looking for the reference.
|
|
320
376
|
if (Array.isArray(value) || typeof value === 'object') {
|
|
321
377
|
for (const key in value) {
|
|
322
378
|
if (this.entityReferenceSearch(value[key], guids)) {
|
|
@@ -371,19 +427,23 @@ class NymphDriver {
|
|
|
371
427
|
qrefs.push(...newSelectors.qrefs);
|
|
372
428
|
}
|
|
373
429
|
else if (!Array.isArray(value)) {
|
|
430
|
+
// @ts-ignore: ts doesn't know what value is here.
|
|
374
431
|
newSelector[key] = [[value]];
|
|
375
432
|
}
|
|
376
433
|
else if (!Array.isArray(value[0])) {
|
|
377
434
|
if (value.length === 3 &&
|
|
378
435
|
value[1] == null &&
|
|
379
436
|
typeof value[2] === 'string') {
|
|
437
|
+
// @ts-ignore: ts doesn't know what value is here.
|
|
380
438
|
newSelector[key] = [[value[0], (0, strtotime_1.default)(value[2]) * 1000]];
|
|
381
439
|
}
|
|
382
440
|
else {
|
|
441
|
+
// @ts-ignore: ts doesn't know what value is here.
|
|
383
442
|
newSelector[key] = [value];
|
|
384
443
|
}
|
|
385
444
|
}
|
|
386
445
|
else {
|
|
446
|
+
// @ts-ignore: ts doesn't know what value is here.
|
|
387
447
|
newSelector[key] = value.map((curValue) => {
|
|
388
448
|
if (curValue.length === 3 &&
|
|
389
449
|
curValue[1] == null &&
|
|
@@ -440,11 +500,14 @@ class NymphDriver {
|
|
|
440
500
|
let count = 0;
|
|
441
501
|
const EntityClass = options.class ?? this.nymph.getEntityClass('Entity');
|
|
442
502
|
const etype = EntityClass.ETYPE;
|
|
503
|
+
// Check if the requested entity is cached.
|
|
443
504
|
if (this.nymph.config.cache &&
|
|
444
505
|
!options.skipCache &&
|
|
445
506
|
selectors.length &&
|
|
446
507
|
'guid' in selectors[0] &&
|
|
447
508
|
typeof selectors[0].guid === 'number') {
|
|
509
|
+
// Only safe to use the cache option with no other selectors than a GUID
|
|
510
|
+
// and tags.
|
|
448
511
|
if (selectors.length === 1 &&
|
|
449
512
|
selectors[0].type === '&' &&
|
|
450
513
|
(Object.keys(selectors[0]).length === 2 ||
|
|
@@ -502,9 +565,13 @@ class NymphDriver {
|
|
|
502
565
|
const cdate = tagsAndDates.cdate;
|
|
503
566
|
const mdate = tagsAndDates.mdate;
|
|
504
567
|
let dataNameAndSValue = getDataNameAndSValueCallback(row);
|
|
568
|
+
// Data.
|
|
505
569
|
const data = {};
|
|
570
|
+
// Serialized data.
|
|
506
571
|
const sdata = {};
|
|
507
572
|
if (dataNameAndSValue.name !== '') {
|
|
573
|
+
// This do will keep going and adding the data until the
|
|
574
|
+
// next entity is reached. $row will end on the next entity.
|
|
508
575
|
do {
|
|
509
576
|
dataNameAndSValue = getDataNameAndSValueCallback(row);
|
|
510
577
|
sdata[dataNameAndSValue.name] = dataNameAndSValue.svalue;
|
|
@@ -512,6 +579,7 @@ class NymphDriver {
|
|
|
512
579
|
} while (row != null && getGUIDCallback(row) === guid);
|
|
513
580
|
}
|
|
514
581
|
else {
|
|
582
|
+
// Make sure that $row is incremented :)
|
|
515
583
|
row = rowFetchCallback();
|
|
516
584
|
}
|
|
517
585
|
const entity = EntityClass.factorySync();
|
|
@@ -532,6 +600,7 @@ class NymphDriver {
|
|
|
532
600
|
if (this.nymph.config.cache) {
|
|
533
601
|
this.pushCache(guid, cdate, mdate, tags, data, sdata);
|
|
534
602
|
}
|
|
603
|
+
// @ts-ignore: ts doesn't know the return type here.
|
|
535
604
|
entities.push(entity);
|
|
536
605
|
}
|
|
537
606
|
}
|
|
@@ -550,6 +619,7 @@ class NymphDriver {
|
|
|
550
619
|
}
|
|
551
620
|
}
|
|
552
621
|
async saveEntityRowLike(entity, saveNewEntityCallback, saveExistingEntityCallback, startTransactionCallback = null, commitTransactionCallback = null) {
|
|
622
|
+
// Get a modified date.
|
|
553
623
|
const mdate = Date.now();
|
|
554
624
|
const tags = (0, lodash_1.difference)(entity.tags, ['']);
|
|
555
625
|
const data = entity.$getData();
|
|
@@ -571,6 +641,7 @@ class NymphDriver {
|
|
|
571
641
|
}
|
|
572
642
|
}
|
|
573
643
|
else {
|
|
644
|
+
// Removed any cached versions of this entity.
|
|
574
645
|
if (this.nymph.config.cache) {
|
|
575
646
|
this.cleanCache(entity.guid);
|
|
576
647
|
}
|
|
@@ -582,12 +653,22 @@ class NymphDriver {
|
|
|
582
653
|
if (commitTransactionCallback) {
|
|
583
654
|
success = await commitTransactionCallback(success);
|
|
584
655
|
}
|
|
656
|
+
// Cache the entity.
|
|
585
657
|
if (success && this.nymph.config.cache) {
|
|
586
658
|
this.pushCache(entity.guid, entity.cdate, entity.mdate, entity.tags, data, sdata);
|
|
587
659
|
}
|
|
588
660
|
return success;
|
|
589
661
|
}
|
|
662
|
+
/**
|
|
663
|
+
* Pull an entity from the cache.
|
|
664
|
+
*
|
|
665
|
+
* @param guid The entity's GUID.
|
|
666
|
+
* @param className The entity's class.
|
|
667
|
+
* @param useSkipAc Whether to tell the entity to use skip_ac.
|
|
668
|
+
* @returns The entity or null if it's not cached.
|
|
669
|
+
*/
|
|
590
670
|
pullCache(guid, className, useSkipAc = false) {
|
|
671
|
+
// Increment the entity access count.
|
|
591
672
|
if (!(guid in this.entityCount)) {
|
|
592
673
|
this.entityCount[guid] = 0;
|
|
593
674
|
}
|
|
@@ -607,17 +688,30 @@ class NymphDriver {
|
|
|
607
688
|
}
|
|
608
689
|
return null;
|
|
609
690
|
}
|
|
691
|
+
/**
|
|
692
|
+
* Push an entity onto the cache.
|
|
693
|
+
*
|
|
694
|
+
* @param guid The entity's GUID.
|
|
695
|
+
* @param cdate The entity's cdate.
|
|
696
|
+
* @param mdate The entity's mdate.
|
|
697
|
+
* @param tags The entity's tags.
|
|
698
|
+
* @param data The entity's data.
|
|
699
|
+
* @param sdata The entity's sdata.
|
|
700
|
+
*/
|
|
610
701
|
pushCache(guid, cdate, mdate, tags, data, sdata) {
|
|
611
702
|
if (guid == null) {
|
|
612
703
|
return;
|
|
613
704
|
}
|
|
705
|
+
// Increment the entity access count.
|
|
614
706
|
if (!(guid in this.entityCount)) {
|
|
615
707
|
this.entityCount[guid] = 0;
|
|
616
708
|
}
|
|
617
709
|
this.entityCount[guid]++;
|
|
710
|
+
// Check the threshold.
|
|
618
711
|
if (this.entityCount[guid] < this.nymph.config.cacheThreshold) {
|
|
619
712
|
return;
|
|
620
713
|
}
|
|
714
|
+
// Cache the entity.
|
|
621
715
|
this.entityCache[guid] = {
|
|
622
716
|
cdate: cdate,
|
|
623
717
|
mdate: mdate,
|
|
@@ -627,12 +721,15 @@ class NymphDriver {
|
|
|
627
721
|
};
|
|
628
722
|
while (this.nymph.config.cacheLimit &&
|
|
629
723
|
Object.keys(this.entityCache).length >= this.nymph.config.cacheLimit) {
|
|
724
|
+
// Find which entity has been accessed the least.
|
|
630
725
|
const least = Object.entries(this.entityCount)
|
|
631
726
|
.sort(([guidA, countA], [guidB, countB]) => countA - countB)
|
|
632
727
|
.pop()?.[0] ?? null;
|
|
633
728
|
if (least == null) {
|
|
729
|
+
// This should never happen.
|
|
634
730
|
return;
|
|
635
731
|
}
|
|
732
|
+
// Remove it.
|
|
636
733
|
delete this.entityCache[least];
|
|
637
734
|
delete this.entityCount[least];
|
|
638
735
|
}
|