@nymphjs/nymph 1.0.0-beta.54 → 1.0.0-beta.56
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 +14 -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 +221 -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 +199 -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,10 +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 &&
|
|
506
|
+
selectors.length &&
|
|
445
507
|
'guid' in selectors[0] &&
|
|
446
508
|
typeof selectors[0].guid === 'number') {
|
|
509
|
+
// Only safe to use the cache option with no other selectors than a GUID
|
|
510
|
+
// and tags.
|
|
447
511
|
if (selectors.length === 1 &&
|
|
448
512
|
selectors[0].type === '&' &&
|
|
449
513
|
(Object.keys(selectors[0]).length === 2 ||
|
|
@@ -501,9 +565,13 @@ class NymphDriver {
|
|
|
501
565
|
const cdate = tagsAndDates.cdate;
|
|
502
566
|
const mdate = tagsAndDates.mdate;
|
|
503
567
|
let dataNameAndSValue = getDataNameAndSValueCallback(row);
|
|
568
|
+
// Data.
|
|
504
569
|
const data = {};
|
|
570
|
+
// Serialized data.
|
|
505
571
|
const sdata = {};
|
|
506
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.
|
|
507
575
|
do {
|
|
508
576
|
dataNameAndSValue = getDataNameAndSValueCallback(row);
|
|
509
577
|
sdata[dataNameAndSValue.name] = dataNameAndSValue.svalue;
|
|
@@ -511,6 +579,7 @@ class NymphDriver {
|
|
|
511
579
|
} while (row != null && getGUIDCallback(row) === guid);
|
|
512
580
|
}
|
|
513
581
|
else {
|
|
582
|
+
// Make sure that $row is incremented :)
|
|
514
583
|
row = rowFetchCallback();
|
|
515
584
|
}
|
|
516
585
|
const entity = EntityClass.factorySync();
|
|
@@ -531,6 +600,7 @@ class NymphDriver {
|
|
|
531
600
|
if (this.nymph.config.cache) {
|
|
532
601
|
this.pushCache(guid, cdate, mdate, tags, data, sdata);
|
|
533
602
|
}
|
|
603
|
+
// @ts-ignore: ts doesn't know the return type here.
|
|
534
604
|
entities.push(entity);
|
|
535
605
|
}
|
|
536
606
|
}
|
|
@@ -549,6 +619,7 @@ class NymphDriver {
|
|
|
549
619
|
}
|
|
550
620
|
}
|
|
551
621
|
async saveEntityRowLike(entity, saveNewEntityCallback, saveExistingEntityCallback, startTransactionCallback = null, commitTransactionCallback = null) {
|
|
622
|
+
// Get a modified date.
|
|
552
623
|
const mdate = Date.now();
|
|
553
624
|
const tags = (0, lodash_1.difference)(entity.tags, ['']);
|
|
554
625
|
const data = entity.$getData();
|
|
@@ -570,6 +641,7 @@ class NymphDriver {
|
|
|
570
641
|
}
|
|
571
642
|
}
|
|
572
643
|
else {
|
|
644
|
+
// Removed any cached versions of this entity.
|
|
573
645
|
if (this.nymph.config.cache) {
|
|
574
646
|
this.cleanCache(entity.guid);
|
|
575
647
|
}
|
|
@@ -581,12 +653,22 @@ class NymphDriver {
|
|
|
581
653
|
if (commitTransactionCallback) {
|
|
582
654
|
success = await commitTransactionCallback(success);
|
|
583
655
|
}
|
|
656
|
+
// Cache the entity.
|
|
584
657
|
if (success && this.nymph.config.cache) {
|
|
585
658
|
this.pushCache(entity.guid, entity.cdate, entity.mdate, entity.tags, data, sdata);
|
|
586
659
|
}
|
|
587
660
|
return success;
|
|
588
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
|
+
*/
|
|
589
670
|
pullCache(guid, className, useSkipAc = false) {
|
|
671
|
+
// Increment the entity access count.
|
|
590
672
|
if (!(guid in this.entityCount)) {
|
|
591
673
|
this.entityCount[guid] = 0;
|
|
592
674
|
}
|
|
@@ -606,17 +688,30 @@ class NymphDriver {
|
|
|
606
688
|
}
|
|
607
689
|
return null;
|
|
608
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
|
+
*/
|
|
609
701
|
pushCache(guid, cdate, mdate, tags, data, sdata) {
|
|
610
702
|
if (guid == null) {
|
|
611
703
|
return;
|
|
612
704
|
}
|
|
705
|
+
// Increment the entity access count.
|
|
613
706
|
if (!(guid in this.entityCount)) {
|
|
614
707
|
this.entityCount[guid] = 0;
|
|
615
708
|
}
|
|
616
709
|
this.entityCount[guid]++;
|
|
710
|
+
// Check the threshold.
|
|
617
711
|
if (this.entityCount[guid] < this.nymph.config.cacheThreshold) {
|
|
618
712
|
return;
|
|
619
713
|
}
|
|
714
|
+
// Cache the entity.
|
|
620
715
|
this.entityCache[guid] = {
|
|
621
716
|
cdate: cdate,
|
|
622
717
|
mdate: mdate,
|
|
@@ -626,12 +721,15 @@ class NymphDriver {
|
|
|
626
721
|
};
|
|
627
722
|
while (this.nymph.config.cacheLimit &&
|
|
628
723
|
Object.keys(this.entityCache).length >= this.nymph.config.cacheLimit) {
|
|
724
|
+
// Find which entity has been accessed the least.
|
|
629
725
|
const least = Object.entries(this.entityCount)
|
|
630
726
|
.sort(([guidA, countA], [guidB, countB]) => countA - countB)
|
|
631
727
|
.pop()?.[0] ?? null;
|
|
632
728
|
if (least == null) {
|
|
729
|
+
// This should never happen.
|
|
633
730
|
return;
|
|
634
731
|
}
|
|
732
|
+
// Remove it.
|
|
635
733
|
delete this.entityCache[least];
|
|
636
734
|
delete this.entityCount[least];
|
|
637
735
|
}
|