@nymphjs/nymph 1.0.0-beta.10 → 1.0.0-beta.100
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 +483 -0
- package/README.md +10 -20
- package/dist/Entity.d.ts +293 -10
- package/dist/Entity.js +563 -148
- package/dist/Entity.js.map +1 -1
- package/dist/Entity.test.js +52 -13
- package/dist/Entity.test.js.map +1 -1
- package/dist/Entity.types.d.ts +256 -23
- package/dist/Entity.types.js +1 -2
- package/dist/Nymph.d.ts +496 -12
- package/dist/Nymph.js +752 -179
- package/dist/Nymph.js.map +1 -1
- package/dist/Nymph.types.d.ts +77 -8
- package/dist/Nymph.types.js +3 -5
- package/dist/Nymph.types.js.map +1 -1
- package/dist/conf/d.d.ts +43 -0
- package/dist/conf/d.js +1 -2
- package/dist/conf/defaults.d.ts +1 -1
- package/dist/conf/defaults.js +26 -3
- package/dist/conf/defaults.js.map +1 -1
- package/dist/conf/index.d.ts +2 -2
- package/dist/conf/index.js +2 -8
- package/dist/conf/index.js.map +1 -1
- package/dist/driver/NymphDriver.d.ts +174 -40
- package/dist/driver/NymphDriver.js +469 -239
- package/dist/driver/NymphDriver.js.map +1 -1
- package/dist/driver/index.d.ts +2 -2
- package/dist/driver/index.js +3 -23
- package/dist/driver/index.js.map +1 -1
- package/dist/driver/testArtifacts.d.ts +2 -2
- package/dist/driver/testArtifacts.js +814 -92
- package/dist/driver/testArtifacts.js.map +1 -1
- package/dist/errors/ClassNotAvailableError.js +1 -5
- package/dist/errors/ClassNotAvailableError.js.map +1 -1
- package/dist/errors/EntityConflictError.js +1 -5
- package/dist/errors/EntityConflictError.js.map +1 -1
- package/dist/errors/EntityInvalidDataError.d.ts +6 -0
- package/dist/errors/EntityInvalidDataError.js +8 -6
- package/dist/errors/EntityInvalidDataError.js.map +1 -1
- package/dist/errors/EntityIsSleepingReferenceError.js +1 -5
- package/dist/errors/EntityIsSleepingReferenceError.js.map +1 -1
- package/dist/errors/EntityUniqueConstraintError.d.ts +3 -0
- package/dist/errors/EntityUniqueConstraintError.js +7 -0
- package/dist/errors/EntityUniqueConstraintError.js.map +1 -0
- package/dist/errors/InvalidParametersError.js +1 -5
- package/dist/errors/InvalidParametersError.js.map +1 -1
- package/dist/errors/InvalidStateError.d.ts +3 -0
- package/dist/errors/InvalidStateError.js +7 -0
- package/dist/errors/InvalidStateError.js.map +1 -0
- package/dist/errors/NotConfiguredError.js +1 -5
- package/dist/errors/NotConfiguredError.js.map +1 -1
- package/dist/errors/QueryFailedError.js +2 -5
- package/dist/errors/QueryFailedError.js.map +1 -1
- package/dist/errors/UnableToConnectError.js +1 -5
- package/dist/errors/UnableToConnectError.js.map +1 -1
- package/dist/errors/index.d.ts +10 -8
- package/dist/errors/index.js +10 -24
- package/dist/errors/index.js.map +1 -1
- package/dist/index.d.ts +11 -11
- package/dist/index.js +14 -34
- package/dist/index.js.map +1 -1
- package/dist/nymphJoiProps.js +10 -15
- package/dist/nymphJoiProps.js.map +1 -1
- package/dist/testArtifacts.d.ts +19 -6
- package/dist/testArtifacts.js +32 -37
- package/dist/testArtifacts.js.map +1 -1
- package/dist/testMocks.d.ts +27 -12
- package/dist/testMocks.js +40 -29
- package/dist/testMocks.js.map +1 -1
- package/dist/utils.d.ts +4 -4
- package/dist/utils.js +40 -29
- package/dist/utils.js.map +1 -1
- package/jest.config.js +11 -2
- package/package.json +23 -19
- package/src/Entity.test.ts +50 -7
- package/src/Entity.ts +483 -147
- package/src/Entity.types.ts +72 -74
- package/src/Nymph.ts +868 -322
- package/src/Nymph.types.ts +146 -27
- package/src/conf/d.ts +25 -0
- package/src/conf/defaults.ts +34 -1
- package/src/conf/index.ts +2 -2
- package/src/driver/NymphDriver.ts +562 -414
- package/src/driver/index.ts +2 -2
- package/src/driver/testArtifacts.ts +954 -185
- package/src/errors/EntityUniqueConstraintError.ts +6 -0
- package/src/errors/InvalidStateError.ts +6 -0
- package/src/errors/index.ts +12 -8
- package/src/index.ts +11 -11
- package/src/nymphJoiProps.ts +2 -2
- package/src/testArtifacts.ts +20 -20
- package/src/testMocks.ts +76 -35
- package/src/type/locutus.d.ts +3 -0
- package/src/utils.ts +39 -18
- package/tsconfig.json +5 -3
- package/typedoc.json +4 -0
|
@@ -1,34 +1,50 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const strtotime_1 = __importDefault(require("locutus/php/datetime/strtotime"));
|
|
10
|
-
const guid_1 = require("@nymphjs/guid");
|
|
11
|
-
const errors_1 = require("../errors");
|
|
12
|
-
const utils_1 = require("../utils");
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import { difference } from 'lodash-es';
|
|
3
|
+
import ReadLines from 'n-readlines';
|
|
4
|
+
import strtotime from 'locutus/php/datetime/strtotime.js';
|
|
5
|
+
import { Tokenizer } from '@sciactive/tokenizer';
|
|
6
|
+
import { InvalidParametersError, UnableToConnectError, } from '../errors/index.js';
|
|
7
|
+
import { xor } from '../utils.js';
|
|
8
|
+
// from: https://stackoverflow.com/a/6969486/664915
|
|
13
9
|
function escapeRegExp(string) {
|
|
14
|
-
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
10
|
+
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
|
|
15
11
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
12
|
+
/**
|
|
13
|
+
* A Nymph database driver.
|
|
14
|
+
*/
|
|
15
|
+
export default class NymphDriver {
|
|
16
|
+
nymph = new Proxy({}, {
|
|
17
|
+
get() {
|
|
18
|
+
throw new Error('Attempted access of Nymph instance before driver initialization!');
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
tokenizer = new Tokenizer();
|
|
22
|
+
/**
|
|
23
|
+
* A cache to make entity retrieval faster.
|
|
24
|
+
*/
|
|
25
|
+
entityCache = {};
|
|
26
|
+
/**
|
|
27
|
+
* A counter for the entity cache to determine the most accessed entities.
|
|
28
|
+
*/
|
|
29
|
+
entityCount = {};
|
|
30
|
+
/**
|
|
31
|
+
* Protect against infinite loops.
|
|
32
|
+
*/
|
|
33
|
+
putDataCounter = 0;
|
|
34
|
+
/**
|
|
35
|
+
* Initialize the Nymph driver.
|
|
36
|
+
*
|
|
37
|
+
* This is meant to be called internally by Nymph. Don't call this directly.
|
|
38
|
+
*
|
|
39
|
+
* @param nymph The Nymph instance.
|
|
40
|
+
*/
|
|
27
41
|
init(nymph) {
|
|
28
42
|
this.nymph = nymph;
|
|
29
43
|
}
|
|
30
44
|
posixRegexMatch(pattern, subject, caseInsensitive = false) {
|
|
31
45
|
const posixClasses = [
|
|
46
|
+
// '[[:<:]]',
|
|
47
|
+
// '[[:>:]]',
|
|
32
48
|
'[:alnum:]',
|
|
33
49
|
'[:alpha:]',
|
|
34
50
|
'[:ascii:]',
|
|
@@ -45,17 +61,21 @@ class NymphDriver {
|
|
|
45
61
|
'[:xdigit:]',
|
|
46
62
|
];
|
|
47
63
|
const jsClasses = [
|
|
64
|
+
// '\b(?=\w)',
|
|
65
|
+
// '(?<=\w)\b',
|
|
48
66
|
'[A-Za-z0-9]',
|
|
49
67
|
'[A-Za-z]',
|
|
50
68
|
'[\x00-\x7F]',
|
|
51
|
-
'[ \t]',
|
|
52
|
-
'[\x00-\x1F\x7F]',
|
|
53
|
-
'
|
|
54
|
-
'
|
|
69
|
+
'[ \t]', // '\s',
|
|
70
|
+
'[\x00-\x1F\x7F]', // '[\000\001\002\003\004\005\006\007\008\009\010\011\012\013\014'.
|
|
71
|
+
// '\015\016\017\018\019\020\021\022\023\024\025\026\027\028\029'.
|
|
72
|
+
// '\030\031\032\033\034\035\036\037\177]',
|
|
73
|
+
'[0-9]', // '\d',
|
|
74
|
+
'[\x21-\x7E]', // '[A-Za-z0-9!"#$%&\'()*+,\-./:;<=>?@[\\\]^_`{|}\~]',
|
|
55
75
|
'[a-z]',
|
|
56
|
-
'[\x20-\x7E]',
|
|
57
|
-
'[!"#$%&\'()*+,-./:;<=>?@[\\\\\\]^_‘{|}~]',
|
|
58
|
-
'[ \t\r\n\v\f]',
|
|
76
|
+
'[\x20-\x7E]', // '[A-Za-z0-9!"#$%&\'()*+,\-./:;<=>?@[\\\]^_`{|}\~]',
|
|
77
|
+
'[!"#$%&\'()*+,-./:;<=>?@[\\\\\\]^_‘{|}~]', // '[!"#$%&\'()*+,\-./:;<=>?@[\\\]^_`{|}\~]',
|
|
78
|
+
'[ \t\r\n\v\f]', // '[\t\n\x0B\f\r ]',
|
|
59
79
|
'[A-Z]',
|
|
60
80
|
'[A-Za-z0-9_]',
|
|
61
81
|
'[0-9A-Fa-f]',
|
|
@@ -69,14 +89,14 @@ class NymphDriver {
|
|
|
69
89
|
}
|
|
70
90
|
async export(filename) {
|
|
71
91
|
try {
|
|
72
|
-
const fhandle =
|
|
92
|
+
const fhandle = fs.openSync(filename, 'w');
|
|
73
93
|
if (!fhandle) {
|
|
74
|
-
throw new
|
|
94
|
+
throw new InvalidParametersError('Provided filename is not writeable.');
|
|
75
95
|
}
|
|
76
|
-
await this.
|
|
77
|
-
|
|
78
|
-
}
|
|
79
|
-
|
|
96
|
+
for await (let entry of this.exportDataIterator()) {
|
|
97
|
+
fs.writeSync(fhandle, entry.content);
|
|
98
|
+
}
|
|
99
|
+
fs.closeSync(fhandle);
|
|
80
100
|
}
|
|
81
101
|
catch (e) {
|
|
82
102
|
return false;
|
|
@@ -84,98 +104,136 @@ class NymphDriver {
|
|
|
84
104
|
return true;
|
|
85
105
|
}
|
|
86
106
|
async exportPrint() {
|
|
87
|
-
await this.
|
|
88
|
-
console.log(
|
|
89
|
-
}
|
|
107
|
+
for await (let entry of this.exportDataIterator()) {
|
|
108
|
+
console.log(entry.content);
|
|
109
|
+
}
|
|
90
110
|
return true;
|
|
91
111
|
}
|
|
92
|
-
async
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
112
|
+
async importDataIterator(lines, transaction) {
|
|
113
|
+
const first = lines[Symbol.iterator]().next();
|
|
114
|
+
if (first.done || first.value !== '#nex2') {
|
|
115
|
+
throw new Error('Tried to import a file that is not a NEX v2 file.');
|
|
96
116
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}
|
|
100
|
-
let guid = null;
|
|
101
|
-
let line = false;
|
|
102
|
-
let sdata = {};
|
|
103
|
-
let tags = [];
|
|
104
|
-
let etype = '__undefined';
|
|
105
|
-
if (startTransactionCallback) {
|
|
106
|
-
await startTransactionCallback();
|
|
117
|
+
if (transaction) {
|
|
118
|
+
await this.internalTransaction('nymph-import');
|
|
107
119
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
if (uidMatch) {
|
|
117
|
-
await saveUIDCallback(uidMatch[1], Number(uidMatch[2]));
|
|
118
|
-
}
|
|
119
|
-
else if (entityMatch) {
|
|
120
|
-
if (guid) {
|
|
121
|
-
await saveEntityCallback(guid, tags, sdata, etype);
|
|
122
|
-
guid = null;
|
|
123
|
-
tags = [];
|
|
124
|
-
sdata = {};
|
|
125
|
-
etype = '__undefined';
|
|
120
|
+
try {
|
|
121
|
+
let guid = null;
|
|
122
|
+
let sdata = {};
|
|
123
|
+
let tags = [];
|
|
124
|
+
let etype = '__undefined';
|
|
125
|
+
for (let line of lines) {
|
|
126
|
+
if (line.match(/^\s*#/)) {
|
|
127
|
+
continue;
|
|
126
128
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
129
|
+
const entityMatch = line.match(/^\s*{([0-9A-Fa-f]+)}<([-\w_]+)>\[([^\]]*)\]\s*$/);
|
|
130
|
+
const propMatch = line.match(/^\s*([^=]+)\s*=\s*(.*\S)\s*$/);
|
|
131
|
+
const uidMatch = line.match(/^\s*<([^>]+)>\[(\d+)\]\s*$/);
|
|
132
|
+
if (uidMatch) {
|
|
133
|
+
// Add the UID.
|
|
134
|
+
await this.importUID({
|
|
135
|
+
name: uidMatch[1],
|
|
136
|
+
value: Number(uidMatch[2]),
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
else if (entityMatch) {
|
|
140
|
+
// Save the current entity.
|
|
141
|
+
if (guid) {
|
|
142
|
+
const cdate = Number(JSON.parse(sdata.cdate));
|
|
143
|
+
delete sdata.cdate;
|
|
144
|
+
const mdate = Number(JSON.parse(sdata.mdate));
|
|
145
|
+
delete sdata.mdate;
|
|
146
|
+
await this.importEntity({ guid, cdate, mdate, tags, sdata, etype });
|
|
147
|
+
guid = null;
|
|
148
|
+
tags = [];
|
|
149
|
+
sdata = {};
|
|
150
|
+
etype = '__undefined';
|
|
151
|
+
}
|
|
152
|
+
// Record the new entity's info.
|
|
153
|
+
guid = entityMatch[1];
|
|
154
|
+
etype = entityMatch[2];
|
|
155
|
+
tags = entityMatch[3].split(',');
|
|
156
|
+
}
|
|
157
|
+
else if (propMatch) {
|
|
158
|
+
// Add the variable to the new entity.
|
|
159
|
+
if (guid) {
|
|
160
|
+
sdata[propMatch[1]] = propMatch[2];
|
|
161
|
+
}
|
|
134
162
|
}
|
|
163
|
+
// Clear the entity cache.
|
|
164
|
+
this.entityCache = {};
|
|
165
|
+
}
|
|
166
|
+
// Save the last entity.
|
|
167
|
+
if (guid) {
|
|
168
|
+
const cdate = Number(JSON.parse(sdata.cdate));
|
|
169
|
+
delete sdata.cdate;
|
|
170
|
+
const mdate = Number(JSON.parse(sdata.mdate));
|
|
171
|
+
delete sdata.mdate;
|
|
172
|
+
await this.importEntity({ guid, cdate, mdate, tags, sdata, etype });
|
|
173
|
+
}
|
|
174
|
+
if (transaction) {
|
|
175
|
+
await this.commit('nymph-import');
|
|
135
176
|
}
|
|
136
|
-
this.entityCache = {};
|
|
137
|
-
}
|
|
138
|
-
if (guid) {
|
|
139
|
-
await saveEntityCallback(guid, tags, sdata, etype);
|
|
140
177
|
}
|
|
141
|
-
|
|
142
|
-
await
|
|
178
|
+
catch (e) {
|
|
179
|
+
await this.rollback('nymph-import');
|
|
180
|
+
throw e;
|
|
143
181
|
}
|
|
144
182
|
return true;
|
|
145
183
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
pass = (0, utils_1.xor)(this.checkData(data, sdata, tmpArr, guid, tags), (0, utils_1.xor)(typeIsNot, clauseNot));
|
|
184
|
+
async importData(text, transaction) {
|
|
185
|
+
return await this.importDataIterator(text.split('\n'), transaction);
|
|
186
|
+
}
|
|
187
|
+
async import(filename, transaction) {
|
|
188
|
+
let rl;
|
|
189
|
+
try {
|
|
190
|
+
rl = new ReadLines(filename);
|
|
191
|
+
}
|
|
192
|
+
catch (e) {
|
|
193
|
+
throw new InvalidParametersError('Provided filename is unreadable.');
|
|
194
|
+
}
|
|
195
|
+
const lines = {
|
|
196
|
+
*[Symbol.iterator]() {
|
|
197
|
+
let line;
|
|
198
|
+
while ((line = rl.next())) {
|
|
199
|
+
yield line.toString('utf8');
|
|
163
200
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
201
|
+
},
|
|
202
|
+
};
|
|
203
|
+
return await this.importDataIterator(lines, transaction);
|
|
204
|
+
}
|
|
205
|
+
checkData(etype, data, sdata, selectors, guid = null, tags = null) {
|
|
206
|
+
try {
|
|
207
|
+
const EntityClass = this.nymph.getEntityClassByEtype(etype);
|
|
208
|
+
const formattedSelectors = this.formatSelectors(selectors).selectors;
|
|
209
|
+
for (const curSelector of formattedSelectors) {
|
|
210
|
+
const type = curSelector.type;
|
|
211
|
+
const typeIsNot = type === '!&' || type === '!|';
|
|
212
|
+
const typeIsOr = type === '|' || type === '!|';
|
|
213
|
+
let pass = !typeIsOr;
|
|
214
|
+
for (const k in curSelector) {
|
|
215
|
+
const key = k;
|
|
216
|
+
const value = curSelector[key];
|
|
217
|
+
if (key === 'type' || value == null) {
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
const clauseNot = key.substring(0, 1) === '!';
|
|
221
|
+
if (key === 'selector' || key === '!selector') {
|
|
222
|
+
const tmpArr = (Array.isArray(value) ? value : [value]);
|
|
223
|
+
pass = xor(this.checkData(etype, data, sdata, tmpArr, guid, tags), xor(typeIsNot, clauseNot));
|
|
167
224
|
}
|
|
168
225
|
else {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
if
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
226
|
+
if (key === 'qref' || key === '!qref') {
|
|
227
|
+
throw new Error("Can't use checkData on qref clauses.");
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
// Check if it doesn't pass any for &, check if it passes any for |.
|
|
231
|
+
for (const curValue of value) {
|
|
232
|
+
if (curValue == null) {
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
178
235
|
const propName = curValue[0];
|
|
236
|
+
// Unserialize the data for this variable.
|
|
179
237
|
if (propName in sdata) {
|
|
180
238
|
data[propName] = JSON.parse(sdata[propName]);
|
|
181
239
|
delete sdata[propName];
|
|
@@ -183,99 +241,130 @@ class NymphDriver {
|
|
|
183
241
|
switch (key) {
|
|
184
242
|
case 'guid':
|
|
185
243
|
case '!guid':
|
|
186
|
-
pass =
|
|
244
|
+
pass = xor(guid == propName, xor(typeIsNot, clauseNot));
|
|
187
245
|
break;
|
|
188
246
|
case 'tag':
|
|
189
247
|
case '!tag':
|
|
190
|
-
pass =
|
|
248
|
+
pass = xor((tags ?? []).indexOf(propName) !== -1, xor(typeIsNot, clauseNot));
|
|
191
249
|
break;
|
|
192
250
|
case 'defined':
|
|
193
251
|
case '!defined':
|
|
194
|
-
pass =
|
|
252
|
+
pass = xor(propName in data, xor(typeIsNot, clauseNot));
|
|
195
253
|
break;
|
|
196
254
|
case 'truthy':
|
|
197
255
|
case '!truthy':
|
|
198
|
-
pass =
|
|
256
|
+
pass = xor(propName in data && data[propName], xor(typeIsNot, clauseNot));
|
|
199
257
|
break;
|
|
200
258
|
case 'ref':
|
|
201
259
|
case '!ref':
|
|
202
260
|
const testRefValue = curValue[1];
|
|
203
|
-
pass =
|
|
204
|
-
this.entityReferenceSearch(data[propName], testRefValue),
|
|
261
|
+
pass = xor(propName in data &&
|
|
262
|
+
this.entityReferenceSearch(data[propName], testRefValue), xor(typeIsNot, clauseNot));
|
|
205
263
|
break;
|
|
206
264
|
case 'equal':
|
|
207
265
|
case '!equal':
|
|
208
266
|
const testEqualValue = curValue[1];
|
|
209
|
-
pass =
|
|
267
|
+
pass = xor(propName in data &&
|
|
210
268
|
JSON.stringify(data[propName]) ===
|
|
211
|
-
JSON.stringify(testEqualValue),
|
|
269
|
+
JSON.stringify(testEqualValue), xor(typeIsNot, clauseNot));
|
|
212
270
|
break;
|
|
213
271
|
case 'contain':
|
|
214
272
|
case '!contain':
|
|
215
273
|
const testContainValue = curValue[1];
|
|
216
|
-
pass =
|
|
217
|
-
JSON.stringify(data[propName]).indexOf(JSON.stringify(testContainValue)) !== -1,
|
|
274
|
+
pass = xor(propName in data &&
|
|
275
|
+
JSON.stringify(data[propName]).indexOf(JSON.stringify(testContainValue)) !== -1, xor(typeIsNot, clauseNot));
|
|
218
276
|
break;
|
|
219
277
|
case 'like':
|
|
220
278
|
case '!like':
|
|
221
279
|
const testLikeValue = curValue[1];
|
|
222
|
-
pass =
|
|
280
|
+
pass = xor(propName in data &&
|
|
223
281
|
new RegExp('^' +
|
|
224
282
|
escapeRegExp(testLikeValue)
|
|
225
283
|
.replace('%', () => '.*')
|
|
226
284
|
.replace('_', () => '.') +
|
|
227
|
-
'$').test(data[propName]),
|
|
285
|
+
'$').test(data[propName]), xor(typeIsNot, clauseNot));
|
|
286
|
+
break;
|
|
287
|
+
case 'ilike':
|
|
288
|
+
case '!ilike':
|
|
289
|
+
const testILikeValue = curValue[1];
|
|
290
|
+
pass = xor(propName in data &&
|
|
291
|
+
new RegExp('^' +
|
|
292
|
+
escapeRegExp(testILikeValue)
|
|
293
|
+
.replace('%', () => '.*')
|
|
294
|
+
.replace('_', () => '.') +
|
|
295
|
+
'$', 'i').test(data[propName]), xor(typeIsNot, clauseNot));
|
|
228
296
|
break;
|
|
229
297
|
case 'match':
|
|
230
298
|
case '!match':
|
|
231
299
|
const testMatchValue = curValue[1];
|
|
232
|
-
|
|
233
|
-
|
|
300
|
+
// Convert a POSIX regex to a JS regex.
|
|
301
|
+
pass = xor(propName in data &&
|
|
302
|
+
this.posixRegexMatch(testMatchValue, data[propName]), xor(typeIsNot, clauseNot));
|
|
234
303
|
break;
|
|
235
304
|
case 'imatch':
|
|
236
305
|
case '!imatch':
|
|
237
306
|
const testIMatchValue = curValue[1];
|
|
238
|
-
|
|
239
|
-
|
|
307
|
+
// Convert a POSIX regex to a JS regex.
|
|
308
|
+
pass = xor(propName in data &&
|
|
309
|
+
this.posixRegexMatch(testIMatchValue, data[propName], true), xor(typeIsNot, clauseNot));
|
|
310
|
+
break;
|
|
311
|
+
case 'search':
|
|
312
|
+
case '!search':
|
|
313
|
+
const testSearchValue = curValue[1];
|
|
314
|
+
const tokenString = propName in data
|
|
315
|
+
? (EntityClass.getFTSText(propName, data[propName]) ??
|
|
316
|
+
'')
|
|
317
|
+
: '';
|
|
318
|
+
pass = xor(propName in data &&
|
|
319
|
+
this.tokenizer.searchString(testSearchValue, tokenString), xor(typeIsNot, clauseNot));
|
|
240
320
|
break;
|
|
241
321
|
case 'gt':
|
|
242
322
|
case '!gt':
|
|
243
323
|
const testGTValue = curValue[1];
|
|
244
|
-
pass =
|
|
324
|
+
pass = xor(propName in data && data[propName] > testGTValue, xor(typeIsNot, clauseNot));
|
|
245
325
|
break;
|
|
246
326
|
case 'gte':
|
|
247
327
|
case '!gte':
|
|
248
328
|
const testGTEValue = curValue[1];
|
|
249
|
-
pass =
|
|
329
|
+
pass = xor(propName in data && data[propName] >= testGTEValue, xor(typeIsNot, clauseNot));
|
|
250
330
|
break;
|
|
251
331
|
case 'lt':
|
|
252
332
|
case '!lt':
|
|
253
333
|
const testLTValue = curValue[1];
|
|
254
|
-
pass =
|
|
334
|
+
pass = xor(propName in data && data[propName] < testLTValue, xor(typeIsNot, clauseNot));
|
|
255
335
|
break;
|
|
256
336
|
case 'lte':
|
|
257
337
|
case '!lte':
|
|
258
338
|
const testLTEValue = curValue[1];
|
|
259
|
-
pass =
|
|
339
|
+
pass = xor(propName in data && data[propName] <= testLTEValue, xor(typeIsNot, clauseNot));
|
|
260
340
|
break;
|
|
261
341
|
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
342
|
+
if (!xor(typeIsOr, pass)) {
|
|
343
|
+
break;
|
|
344
|
+
}
|
|
265
345
|
}
|
|
266
346
|
}
|
|
267
347
|
}
|
|
348
|
+
if (!xor(typeIsOr, pass)) {
|
|
349
|
+
break;
|
|
350
|
+
}
|
|
268
351
|
}
|
|
269
|
-
if (!
|
|
270
|
-
|
|
352
|
+
if (!pass) {
|
|
353
|
+
return false;
|
|
271
354
|
}
|
|
272
355
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
356
|
+
return true;
|
|
357
|
+
}
|
|
358
|
+
catch (e) {
|
|
359
|
+
this.nymph.config.debugError('nymph', `Failed to check entity data: ${e}`);
|
|
360
|
+
throw e;
|
|
276
361
|
}
|
|
277
|
-
return true;
|
|
278
362
|
}
|
|
363
|
+
/**
|
|
364
|
+
* Remove all copies of an entity from the cache.
|
|
365
|
+
*
|
|
366
|
+
* @param guid The GUID of the entity to remove.
|
|
367
|
+
*/
|
|
279
368
|
cleanCache(guid) {
|
|
280
369
|
delete this.entityCache[guid];
|
|
281
370
|
}
|
|
@@ -292,7 +381,15 @@ class NymphDriver {
|
|
|
292
381
|
}
|
|
293
382
|
return ret;
|
|
294
383
|
}
|
|
384
|
+
/**
|
|
385
|
+
* Search through a value for an entity reference.
|
|
386
|
+
*
|
|
387
|
+
* @param value Any value to search.
|
|
388
|
+
* @param entity An entity, GUID, or array of either to search for.
|
|
389
|
+
* @returns True if the reference is found, false otherwise.
|
|
390
|
+
*/
|
|
295
391
|
entityReferenceSearch(value, entity) {
|
|
392
|
+
// Get the GUID, if the passed $entity is an object.
|
|
296
393
|
const guids = [];
|
|
297
394
|
if (Array.isArray(entity)) {
|
|
298
395
|
if (entity[0] === 'nymph_entity_reference') {
|
|
@@ -323,7 +420,8 @@ class NymphDriver {
|
|
|
323
420
|
guids.indexOf(value[1]) !== -1) {
|
|
324
421
|
return true;
|
|
325
422
|
}
|
|
326
|
-
|
|
423
|
+
// Search through arrays and objects looking for the reference.
|
|
424
|
+
if (value != null && (Array.isArray(value) || typeof value === 'object')) {
|
|
327
425
|
for (const key in value) {
|
|
328
426
|
if (this.entityReferenceSearch(value[key], guids)) {
|
|
329
427
|
return true;
|
|
@@ -356,9 +454,9 @@ class NymphDriver {
|
|
|
356
454
|
for (let i = 0; i < tmpArr.length; i++) {
|
|
357
455
|
const name = tmpArr[i][0];
|
|
358
456
|
const [qrefOptions, ...qrefSelectors] = tmpArr[i][1];
|
|
359
|
-
const QrefEntityClass =
|
|
457
|
+
const QrefEntityClass = qrefOptions.class
|
|
360
458
|
? this.nymph.getEntityClass(qrefOptions.class)
|
|
361
|
-
:
|
|
459
|
+
: this.nymph.getEntityClass('Entity');
|
|
362
460
|
const newOptions = {
|
|
363
461
|
...qrefOptions,
|
|
364
462
|
class: QrefEntityClass,
|
|
@@ -377,24 +475,28 @@ class NymphDriver {
|
|
|
377
475
|
qrefs.push(...newSelectors.qrefs);
|
|
378
476
|
}
|
|
379
477
|
else if (!Array.isArray(value)) {
|
|
478
|
+
// @ts-ignore: ts doesn't know what value is here.
|
|
380
479
|
newSelector[key] = [[value]];
|
|
381
480
|
}
|
|
382
481
|
else if (!Array.isArray(value[0])) {
|
|
383
482
|
if (value.length === 3 &&
|
|
384
483
|
value[1] == null &&
|
|
385
484
|
typeof value[2] === 'string') {
|
|
386
|
-
|
|
485
|
+
// @ts-ignore: ts doesn't know what value is here.
|
|
486
|
+
newSelector[key] = [[value[0], strtotime(value[2]) * 1000]];
|
|
387
487
|
}
|
|
388
488
|
else {
|
|
489
|
+
// @ts-ignore: ts doesn't know what value is here.
|
|
389
490
|
newSelector[key] = [value];
|
|
390
491
|
}
|
|
391
492
|
}
|
|
392
493
|
else {
|
|
494
|
+
// @ts-ignore: ts doesn't know what value is here.
|
|
393
495
|
newSelector[key] = value.map((curValue) => {
|
|
394
496
|
if (curValue.length === 3 &&
|
|
395
497
|
curValue[1] == null &&
|
|
396
498
|
typeof curValue[2] === 'string') {
|
|
397
|
-
return [curValue[0], (
|
|
499
|
+
return [curValue[0], strtotime(curValue[2]) * 1000];
|
|
398
500
|
}
|
|
399
501
|
return curValue;
|
|
400
502
|
});
|
|
@@ -416,7 +518,7 @@ class NymphDriver {
|
|
|
416
518
|
if (key === 'type') {
|
|
417
519
|
continue;
|
|
418
520
|
}
|
|
419
|
-
let curQuery = callback(key, value, typeIsOr, typeIsNot);
|
|
521
|
+
let curQuery = callback({ key, value, typeIsOr, typeIsNot });
|
|
420
522
|
if (curQuery) {
|
|
421
523
|
if (curSelectorQuery) {
|
|
422
524
|
curSelectorQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
@@ -430,73 +532,116 @@ class NymphDriver {
|
|
|
430
532
|
}
|
|
431
533
|
return queryParts;
|
|
432
534
|
}
|
|
433
|
-
|
|
434
|
-
if (typeof selectors[0] === 'string') {
|
|
435
|
-
selectors = [{ type: '&', guid: selectors[0] }];
|
|
436
|
-
}
|
|
437
|
-
options.limit = 1;
|
|
438
|
-
const entities = this.getEntitiesSync(options, ...selectors);
|
|
439
|
-
if (options.return === 'count') {
|
|
440
|
-
return entities;
|
|
441
|
-
}
|
|
442
|
-
if (!entities || !entities.length) {
|
|
443
|
-
return null;
|
|
444
|
-
}
|
|
445
|
-
return entities[0];
|
|
446
|
-
}
|
|
447
|
-
getEntitesRowLike(options, selectors, performQueryCallback, rowFetchCallback, freeResultCallback, getCountCallback, getGUIDCallback, getTagsAndDatesCallback, getDataNameAndSValueCallback) {
|
|
535
|
+
getEntitiesRowLike(options, selectors, performQueryCallback, rowFetchCallback, freeResultCallback, getCountCallback, getGUIDCallback, getTagsAndDatesCallback, getDataNameAndSValueCallback) {
|
|
448
536
|
if (!this.isConnected()) {
|
|
449
|
-
throw new
|
|
537
|
+
throw new UnableToConnectError('not connected to DB');
|
|
450
538
|
}
|
|
451
539
|
for (const selector of selectors) {
|
|
452
540
|
if (!selector ||
|
|
453
541
|
Object.keys(selector).length === 1 ||
|
|
454
542
|
!('type' in selector) ||
|
|
455
543
|
['&', '!&', '|', '!|'].indexOf(selector.type) === -1) {
|
|
456
|
-
throw new
|
|
544
|
+
throw new InvalidParametersError('Invalid query selector passed: ' + JSON.stringify(selector));
|
|
457
545
|
}
|
|
458
546
|
}
|
|
459
547
|
let entities = [];
|
|
460
548
|
let count = 0;
|
|
461
549
|
const EntityClass = options.class ?? this.nymph.getEntityClass('Entity');
|
|
462
550
|
const etype = EntityClass.ETYPE;
|
|
551
|
+
if (options.source === 'client' && options.return === 'object') {
|
|
552
|
+
throw new InvalidParametersError('Object return type not allowed from client.');
|
|
553
|
+
}
|
|
554
|
+
// Check if the requested entity is cached.
|
|
463
555
|
if (this.nymph.config.cache &&
|
|
464
556
|
!options.skipCache &&
|
|
557
|
+
selectors.length &&
|
|
465
558
|
'guid' in selectors[0] &&
|
|
466
559
|
typeof selectors[0].guid === 'number') {
|
|
560
|
+
// Only safe to use the cache option with no other selectors than a GUID
|
|
561
|
+
// and tags.
|
|
467
562
|
if (selectors.length === 1 &&
|
|
468
563
|
selectors[0].type === '&' &&
|
|
469
564
|
(Object.keys(selectors[0]).length === 2 ||
|
|
470
565
|
(Object.keys(selectors[0]).length === 3 && 'tag' in selectors[0]))) {
|
|
471
|
-
const
|
|
472
|
-
if (
|
|
473
|
-
|
|
566
|
+
const cacheEntity = this.pullCache(selectors[0]['guid']);
|
|
567
|
+
if (cacheEntity != null &&
|
|
568
|
+
cacheEntity.guid != null &&
|
|
474
569
|
(!('tag' in selectors[0]) ||
|
|
475
|
-
|
|
570
|
+
!(Array.isArray(selectors[0].tag)
|
|
476
571
|
? selectors[0].tag
|
|
477
|
-
: [selectors[0].tag])))) {
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
572
|
+
: [selectors[0].tag]).find((tag) => tag == null || !cacheEntity.tags.includes(tag)))) {
|
|
573
|
+
// Return the value from the cache.
|
|
574
|
+
const { guid, cdate, mdate, tags, data, sdata } = cacheEntity;
|
|
575
|
+
if (options.return === 'count') {
|
|
576
|
+
return {
|
|
577
|
+
result: Promise.resolve(null),
|
|
578
|
+
process: () => 1,
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
else if (options.return === 'guid') {
|
|
582
|
+
return {
|
|
583
|
+
result: Promise.resolve(null),
|
|
584
|
+
process: () => [guid],
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
else if (options.return === 'object') {
|
|
588
|
+
return {
|
|
589
|
+
result: Promise.resolve(null),
|
|
590
|
+
process: () => [
|
|
591
|
+
{
|
|
592
|
+
guid,
|
|
593
|
+
cdate,
|
|
594
|
+
mdate,
|
|
595
|
+
tags,
|
|
596
|
+
...data,
|
|
597
|
+
...Object.fromEntries(Object.entries(sdata).map(([name, value]) => [
|
|
598
|
+
name,
|
|
599
|
+
JSON.parse(value),
|
|
600
|
+
])),
|
|
601
|
+
},
|
|
602
|
+
],
|
|
603
|
+
};
|
|
604
|
+
}
|
|
605
|
+
else {
|
|
606
|
+
const entity = this.nymph
|
|
607
|
+
.getEntityClass(EntityClass.class)
|
|
608
|
+
.factorySync();
|
|
609
|
+
if (entity) {
|
|
610
|
+
entity.$nymph = this.nymph;
|
|
611
|
+
if (options.skipAc != null) {
|
|
612
|
+
entity.$useSkipAc(!!options.skipAc);
|
|
613
|
+
}
|
|
614
|
+
entity.guid = guid;
|
|
615
|
+
entity.cdate = cdate;
|
|
616
|
+
entity.mdate = mdate;
|
|
617
|
+
entity.tags = tags;
|
|
618
|
+
entity.$putData(data, sdata, 'server');
|
|
619
|
+
return {
|
|
620
|
+
result: Promise.resolve(null),
|
|
621
|
+
process: () => [entity],
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
return {
|
|
625
|
+
result: Promise.resolve(null),
|
|
626
|
+
process: () => [],
|
|
627
|
+
};
|
|
628
|
+
}
|
|
489
629
|
}
|
|
490
630
|
}
|
|
491
631
|
}
|
|
492
632
|
try {
|
|
493
633
|
const formattedSelectors = this.formatSelectors(selectors, options);
|
|
494
634
|
this.nymph.runQueryCallbacks(options, formattedSelectors.selectors);
|
|
495
|
-
for (let
|
|
496
|
-
const [options, ...selectors] =
|
|
635
|
+
for (let i = 0; i < formattedSelectors.qrefs.length; i++) {
|
|
636
|
+
const [options, ...selectors] = formattedSelectors.qrefs[i];
|
|
497
637
|
this.nymph.runQueryCallbacks(options, selectors);
|
|
638
|
+
formattedSelectors.qrefs[i] = [options, ...selectors];
|
|
498
639
|
}
|
|
499
|
-
const { result } = performQueryCallback(
|
|
640
|
+
const { result } = performQueryCallback({
|
|
641
|
+
options,
|
|
642
|
+
selectors: formattedSelectors.selectors,
|
|
643
|
+
etype,
|
|
644
|
+
});
|
|
500
645
|
return {
|
|
501
646
|
result,
|
|
502
647
|
process: () => {
|
|
@@ -521,9 +666,13 @@ class NymphDriver {
|
|
|
521
666
|
const cdate = tagsAndDates.cdate;
|
|
522
667
|
const mdate = tagsAndDates.mdate;
|
|
523
668
|
let dataNameAndSValue = getDataNameAndSValueCallback(row);
|
|
669
|
+
// Data.
|
|
524
670
|
const data = {};
|
|
671
|
+
// Serialized data.
|
|
525
672
|
const sdata = {};
|
|
526
673
|
if (dataNameAndSValue.name !== '') {
|
|
674
|
+
// This do will keep going and adding the data until the
|
|
675
|
+
// next entity is reached. $row will end on the next entity.
|
|
527
676
|
do {
|
|
528
677
|
dataNameAndSValue = getDataNameAndSValueCallback(row);
|
|
529
678
|
sdata[dataNameAndSValue.name] = dataNameAndSValue.svalue;
|
|
@@ -531,27 +680,43 @@ class NymphDriver {
|
|
|
531
680
|
} while (row != null && getGUIDCallback(row) === guid);
|
|
532
681
|
}
|
|
533
682
|
else {
|
|
683
|
+
// Make sure that $row is incremented :)
|
|
534
684
|
row = rowFetchCallback();
|
|
535
685
|
}
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
686
|
+
if (options.return === 'object') {
|
|
687
|
+
entities.push({
|
|
688
|
+
guid,
|
|
689
|
+
cdate,
|
|
690
|
+
mdate,
|
|
691
|
+
tags,
|
|
692
|
+
...data,
|
|
693
|
+
...Object.fromEntries(Object.entries(sdata).map(([name, value]) => [
|
|
694
|
+
name,
|
|
695
|
+
JSON.parse(value),
|
|
696
|
+
])),
|
|
697
|
+
});
|
|
540
698
|
}
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
699
|
+
else {
|
|
700
|
+
const entity = EntityClass.factorySync();
|
|
701
|
+
entity.$nymph = this.nymph;
|
|
702
|
+
if (options.skipAc != null) {
|
|
703
|
+
entity.$useSkipAc(!!options.skipAc);
|
|
704
|
+
}
|
|
705
|
+
entity.guid = guid;
|
|
706
|
+
entity.cdate = cdate;
|
|
707
|
+
entity.mdate = mdate;
|
|
708
|
+
entity.tags = tags;
|
|
709
|
+
this.putDataCounter++;
|
|
710
|
+
if (this.putDataCounter == 100) {
|
|
711
|
+
throw new Error('Infinite loop detected in Entity loading.');
|
|
712
|
+
}
|
|
713
|
+
entity.$putData(data, sdata, 'server');
|
|
714
|
+
this.putDataCounter--;
|
|
715
|
+
entities.push(entity);
|
|
548
716
|
}
|
|
549
|
-
entity.$putData(data, sdata);
|
|
550
|
-
this.putDataCounter--;
|
|
551
717
|
if (this.nymph.config.cache) {
|
|
552
718
|
this.pushCache(guid, cdate, mdate, tags, data, sdata);
|
|
553
719
|
}
|
|
554
|
-
entities.push(entity);
|
|
555
720
|
}
|
|
556
721
|
}
|
|
557
722
|
freeResultCallback();
|
|
@@ -569,89 +734,155 @@ class NymphDriver {
|
|
|
569
734
|
}
|
|
570
735
|
}
|
|
571
736
|
async saveEntityRowLike(entity, saveNewEntityCallback, saveExistingEntityCallback, startTransactionCallback = null, commitTransactionCallback = null) {
|
|
572
|
-
const
|
|
573
|
-
const
|
|
737
|
+
const originalGuid = entity.guid;
|
|
738
|
+
const originalCdate = entity.cdate;
|
|
739
|
+
const originalMdate = entity.mdate;
|
|
740
|
+
// Get a modified date.
|
|
741
|
+
let mdate = Date.now();
|
|
742
|
+
if (entity.mdate != null) {
|
|
743
|
+
if (this.nymph.config.updateMDate === false) {
|
|
744
|
+
mdate = entity.mdate;
|
|
745
|
+
}
|
|
746
|
+
else if (typeof this.nymph.config.updateMDate === 'number') {
|
|
747
|
+
mdate = entity.mdate + this.nymph.config.updateMDate;
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
const tags = difference(entity.tags, ['']);
|
|
574
751
|
const data = entity.$getData();
|
|
575
752
|
const sdata = entity.$getSData();
|
|
753
|
+
const uniques = await entity.$getUniques();
|
|
576
754
|
const EntityClass = entity.constructor;
|
|
577
755
|
const etype = EntityClass.ETYPE;
|
|
578
756
|
if (startTransactionCallback) {
|
|
579
757
|
await startTransactionCallback();
|
|
580
758
|
}
|
|
581
759
|
let success = false;
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
760
|
+
try {
|
|
761
|
+
if (entity.guid == null) {
|
|
762
|
+
const cdate = mdate;
|
|
763
|
+
const newId = entity.$getGuaranteedGUID();
|
|
764
|
+
success = await saveNewEntityCallback({
|
|
765
|
+
entity,
|
|
766
|
+
guid: newId,
|
|
767
|
+
tags,
|
|
768
|
+
data,
|
|
769
|
+
sdata,
|
|
770
|
+
uniques,
|
|
771
|
+
cdate,
|
|
772
|
+
etype,
|
|
773
|
+
});
|
|
774
|
+
if (success) {
|
|
775
|
+
entity.guid = newId;
|
|
776
|
+
entity.cdate = cdate;
|
|
777
|
+
entity.mdate = mdate;
|
|
778
|
+
}
|
|
590
779
|
}
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
780
|
+
else {
|
|
781
|
+
// Removed any cached versions of this entity.
|
|
782
|
+
if (this.nymph.config.cache) {
|
|
783
|
+
this.cleanCache(entity.guid);
|
|
784
|
+
}
|
|
785
|
+
success = await saveExistingEntityCallback({
|
|
786
|
+
entity,
|
|
787
|
+
guid: entity.guid,
|
|
788
|
+
tags,
|
|
789
|
+
data,
|
|
790
|
+
sdata,
|
|
791
|
+
uniques,
|
|
792
|
+
mdate,
|
|
793
|
+
etype,
|
|
794
|
+
});
|
|
795
|
+
if (success) {
|
|
796
|
+
entity.mdate = mdate;
|
|
797
|
+
}
|
|
595
798
|
}
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
799
|
+
if (commitTransactionCallback) {
|
|
800
|
+
success = await commitTransactionCallback(success);
|
|
801
|
+
}
|
|
802
|
+
// Cache the entity.
|
|
803
|
+
if (success && this.nymph.config.cache) {
|
|
804
|
+
this.pushCache(entity.guid, entity.cdate, entity.mdate, entity.tags, data, sdata);
|
|
599
805
|
}
|
|
600
806
|
}
|
|
601
|
-
|
|
602
|
-
|
|
807
|
+
catch (e) {
|
|
808
|
+
entity.guid = originalGuid;
|
|
809
|
+
entity.cdate = originalCdate;
|
|
810
|
+
entity.mdate = originalMdate;
|
|
811
|
+
throw e;
|
|
603
812
|
}
|
|
604
|
-
if (success
|
|
605
|
-
|
|
813
|
+
if (!success) {
|
|
814
|
+
entity.guid = originalGuid;
|
|
815
|
+
entity.cdate = originalCdate;
|
|
816
|
+
entity.mdate = originalMdate;
|
|
606
817
|
}
|
|
607
818
|
return success;
|
|
608
819
|
}
|
|
609
|
-
|
|
820
|
+
/**
|
|
821
|
+
* Pull an entity from the cache.
|
|
822
|
+
*
|
|
823
|
+
* @param guid The entity's GUID.
|
|
824
|
+
* @returns The entity's data or null if it's not cached.
|
|
825
|
+
*/
|
|
826
|
+
pullCache(guid) {
|
|
827
|
+
// Increment the entity access count.
|
|
610
828
|
if (!(guid in this.entityCount)) {
|
|
611
829
|
this.entityCount[guid] = 0;
|
|
612
830
|
}
|
|
613
831
|
this.entityCount[guid]++;
|
|
614
832
|
if (guid in this.entityCache) {
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
entity.$putData(this.entityCache[guid]['data'], this.entityCache[guid]['sdata']);
|
|
624
|
-
return entity;
|
|
625
|
-
}
|
|
833
|
+
return {
|
|
834
|
+
guid,
|
|
835
|
+
cdate: this.entityCache[guid]['cdate'],
|
|
836
|
+
mdate: this.entityCache[guid]['mdate'],
|
|
837
|
+
tags: this.entityCache[guid]['tags'],
|
|
838
|
+
data: this.entityCache[guid]['data'],
|
|
839
|
+
sdata: this.entityCache[guid]['sdata'],
|
|
840
|
+
};
|
|
626
841
|
}
|
|
627
842
|
return null;
|
|
628
843
|
}
|
|
844
|
+
/**
|
|
845
|
+
* Push an entity onto the cache.
|
|
846
|
+
*
|
|
847
|
+
* @param guid The entity's GUID.
|
|
848
|
+
* @param cdate The entity's cdate.
|
|
849
|
+
* @param mdate The entity's mdate.
|
|
850
|
+
* @param tags The entity's tags.
|
|
851
|
+
* @param data The entity's data.
|
|
852
|
+
* @param sdata The entity's sdata.
|
|
853
|
+
*/
|
|
629
854
|
pushCache(guid, cdate, mdate, tags, data, sdata) {
|
|
630
855
|
if (guid == null) {
|
|
631
856
|
return;
|
|
632
857
|
}
|
|
858
|
+
// Increment the entity access count.
|
|
633
859
|
if (!(guid in this.entityCount)) {
|
|
634
860
|
this.entityCount[guid] = 0;
|
|
635
861
|
}
|
|
636
862
|
this.entityCount[guid]++;
|
|
863
|
+
// Check the threshold.
|
|
637
864
|
if (this.entityCount[guid] < this.nymph.config.cacheThreshold) {
|
|
638
865
|
return;
|
|
639
866
|
}
|
|
867
|
+
// Cache the entity.
|
|
640
868
|
this.entityCache[guid] = {
|
|
641
|
-
cdate
|
|
642
|
-
mdate
|
|
643
|
-
tags
|
|
644
|
-
data
|
|
645
|
-
sdata
|
|
869
|
+
cdate,
|
|
870
|
+
mdate,
|
|
871
|
+
tags,
|
|
872
|
+
data,
|
|
873
|
+
sdata,
|
|
646
874
|
};
|
|
647
875
|
while (this.nymph.config.cacheLimit &&
|
|
648
876
|
Object.keys(this.entityCache).length >= this.nymph.config.cacheLimit) {
|
|
877
|
+
// Find which entity has been accessed the least.
|
|
649
878
|
const least = Object.entries(this.entityCount)
|
|
650
|
-
.sort(([
|
|
879
|
+
.sort(([_guidA, countA], [_guidB, countB]) => countA - countB)
|
|
651
880
|
.pop()?.[0] ?? null;
|
|
652
881
|
if (least == null) {
|
|
882
|
+
// This should never happen.
|
|
653
883
|
return;
|
|
654
884
|
}
|
|
885
|
+
// Remove it.
|
|
655
886
|
delete this.entityCache[least];
|
|
656
887
|
delete this.entityCount[least];
|
|
657
888
|
}
|
|
@@ -665,5 +896,4 @@ class NymphDriver {
|
|
|
665
896
|
return matches.map((match) => match.replace(re, '$1'));
|
|
666
897
|
}
|
|
667
898
|
}
|
|
668
|
-
exports.default = NymphDriver;
|
|
669
899
|
//# sourceMappingURL=NymphDriver.js.map
|