@nymphjs/nymph 1.0.0-beta.1 → 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.
Files changed (96) hide show
  1. package/CHANGELOG.md +535 -0
  2. package/README.md +14 -22
  3. package/dist/Entity.d.ts +293 -10
  4. package/dist/Entity.js +571 -149
  5. package/dist/Entity.js.map +1 -1
  6. package/dist/Entity.test.js +67 -26
  7. package/dist/Entity.test.js.map +1 -1
  8. package/dist/Entity.types.d.ts +256 -23
  9. package/dist/Entity.types.js +1 -2
  10. package/dist/Nymph.d.ts +497 -13
  11. package/dist/Nymph.js +761 -188
  12. package/dist/Nymph.js.map +1 -1
  13. package/dist/Nymph.types.d.ts +95 -26
  14. package/dist/Nymph.types.js +3 -5
  15. package/dist/Nymph.types.js.map +1 -1
  16. package/dist/conf/d.d.ts +43 -0
  17. package/dist/conf/d.js +1 -2
  18. package/dist/conf/defaults.d.ts +1 -1
  19. package/dist/conf/defaults.js +26 -3
  20. package/dist/conf/defaults.js.map +1 -1
  21. package/dist/conf/index.d.ts +2 -2
  22. package/dist/conf/index.js +2 -8
  23. package/dist/conf/index.js.map +1 -1
  24. package/dist/driver/NymphDriver.d.ts +181 -43
  25. package/dist/driver/NymphDriver.js +532 -285
  26. package/dist/driver/NymphDriver.js.map +1 -1
  27. package/dist/driver/index.d.ts +2 -2
  28. package/dist/driver/index.js +3 -23
  29. package/dist/driver/index.js.map +1 -1
  30. package/dist/driver/testArtifacts.d.ts +2 -2
  31. package/dist/driver/testArtifacts.js +939 -219
  32. package/dist/driver/testArtifacts.js.map +1 -1
  33. package/dist/errors/ClassNotAvailableError.js +1 -5
  34. package/dist/errors/ClassNotAvailableError.js.map +1 -1
  35. package/dist/errors/EntityConflictError.js +1 -5
  36. package/dist/errors/EntityConflictError.js.map +1 -1
  37. package/dist/errors/EntityInvalidDataError.d.ts +6 -0
  38. package/dist/errors/EntityInvalidDataError.js +8 -6
  39. package/dist/errors/EntityInvalidDataError.js.map +1 -1
  40. package/dist/errors/EntityIsSleepingReferenceError.js +1 -5
  41. package/dist/errors/EntityIsSleepingReferenceError.js.map +1 -1
  42. package/dist/errors/EntityUniqueConstraintError.d.ts +3 -0
  43. package/dist/errors/EntityUniqueConstraintError.js +7 -0
  44. package/dist/errors/EntityUniqueConstraintError.js.map +1 -0
  45. package/dist/errors/InvalidParametersError.js +1 -5
  46. package/dist/errors/InvalidParametersError.js.map +1 -1
  47. package/dist/errors/InvalidStateError.d.ts +3 -0
  48. package/dist/errors/InvalidStateError.js +7 -0
  49. package/dist/errors/InvalidStateError.js.map +1 -0
  50. package/dist/errors/NotConfiguredError.js +1 -5
  51. package/dist/errors/NotConfiguredError.js.map +1 -1
  52. package/dist/errors/QueryFailedError.js +2 -5
  53. package/dist/errors/QueryFailedError.js.map +1 -1
  54. package/dist/errors/UnableToConnectError.js +1 -5
  55. package/dist/errors/UnableToConnectError.js.map +1 -1
  56. package/dist/errors/index.d.ts +10 -8
  57. package/dist/errors/index.js +10 -24
  58. package/dist/errors/index.js.map +1 -1
  59. package/dist/index.d.ts +11 -11
  60. package/dist/index.js +14 -34
  61. package/dist/index.js.map +1 -1
  62. package/dist/nymphJoiProps.js +10 -15
  63. package/dist/nymphJoiProps.js.map +1 -1
  64. package/dist/testArtifacts.d.ts +19 -6
  65. package/dist/testArtifacts.js +32 -37
  66. package/dist/testArtifacts.js.map +1 -1
  67. package/dist/testMocks.d.ts +28 -11
  68. package/dist/testMocks.js +44 -28
  69. package/dist/testMocks.js.map +1 -1
  70. package/dist/utils.d.ts +4 -4
  71. package/dist/utils.js +40 -29
  72. package/dist/utils.js.map +1 -1
  73. package/jest.config.js +11 -2
  74. package/package.json +24 -20
  75. package/src/Entity.test.ts +63 -13
  76. package/src/Entity.ts +498 -156
  77. package/src/Entity.types.ts +72 -74
  78. package/src/Nymph.ts +904 -336
  79. package/src/Nymph.types.ts +179 -44
  80. package/src/conf/d.ts +25 -0
  81. package/src/conf/defaults.ts +34 -1
  82. package/src/conf/index.ts +2 -2
  83. package/src/driver/NymphDriver.ts +650 -460
  84. package/src/driver/index.ts +2 -2
  85. package/src/driver/testArtifacts.ts +966 -196
  86. package/src/errors/EntityUniqueConstraintError.ts +6 -0
  87. package/src/errors/InvalidStateError.ts +6 -0
  88. package/src/errors/index.ts +12 -8
  89. package/src/index.ts +11 -11
  90. package/src/nymphJoiProps.ts +2 -2
  91. package/src/testArtifacts.ts +20 -20
  92. package/src/testMocks.ts +85 -37
  93. package/src/type/locutus.d.ts +3 -0
  94. package/src/utils.ts +39 -18
  95. package/tsconfig.json +5 -3
  96. package/typedoc.json +4 -0
@@ -1,34 +1,50 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- const fs_1 = __importDefault(require("fs"));
7
- const lodash_1 = require("lodash");
8
- const n_readlines_1 = __importDefault(require("n-readlines"));
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
- class NymphDriver {
17
- constructor() {
18
- this.nymph = new Proxy({}, {
19
- get() {
20
- throw new Error('Attempted access of Nymph instance before driver initialization!');
21
- },
22
- });
23
- this.entityCache = {};
24
- this.entityCount = {};
25
- this.putDataCounter = 0;
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
- '[0-9]',
54
- '[\x21-\x7E]',
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 = fs_1.default.openSync(filename, 'w');
92
+ const fhandle = fs.openSync(filename, 'w');
73
93
  if (!fhandle) {
74
- throw new errors_1.InvalidParametersError('Provided filename is not writeable.');
94
+ throw new InvalidParametersError('Provided filename is not writeable.');
75
95
  }
76
- await this.exportEntities((line) => {
77
- fs_1.default.writeSync(fhandle, `${line}\n`);
78
- });
79
- fs_1.default.closeSync(fhandle);
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.exportEntities((line) => {
88
- console.log(line);
89
- });
107
+ for await (let entry of this.exportDataIterator()) {
108
+ console.log(entry.content);
109
+ }
90
110
  return true;
91
111
  }
92
- async importFromFile(filename, saveEntityCallback, saveUIDCallback, startTransactionCallback = null, commitTransactionCallback = null) {
93
- let rl;
94
- try {
95
- rl = new n_readlines_1.default(filename);
96
- }
97
- catch (e) {
98
- throw new errors_1.InvalidParametersError('Provided filename is unreadable.');
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.');
99
116
  }
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
- while ((line = rl.next())) {
109
- const text = line.toString('utf8');
110
- if (text.match(/^\s*#/)) {
111
- continue;
112
- }
113
- const entityMatch = text.match(/^\s*{([0-9A-Fa-f]+)}<([-\w_]+)>\[([^\]]*)\]\s*$/);
114
- const propMatch = text.match(/^\s*([^=]+)\s*=\s*(.*\S)\s*$/);
115
- const uidMatch = text.match(/^\s*<([^>]+)>\[(\d+)\]\s*$/);
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
- guid = entityMatch[1];
128
- etype = entityMatch[2];
129
- tags = entityMatch[3].split(',');
130
- }
131
- else if (propMatch) {
132
- if (guid) {
133
- sdata[propMatch[1]] = propMatch[2];
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
- if (commitTransactionCallback) {
142
- await commitTransactionCallback();
178
+ catch (e) {
179
+ await this.rollback('nymph-import');
180
+ throw e;
143
181
  }
144
182
  return true;
145
183
  }
146
- checkData(data, sdata, selectors, guid = null, tags = null) {
147
- const formattedSelectors = this.formatSelectors(selectors);
148
- for (const curSelector of formattedSelectors) {
149
- const type = curSelector.type;
150
- const typeIsNot = type === '!&' || type === '!|';
151
- const typeIsOr = type === '|' || type === '!|';
152
- let pass = !typeIsOr;
153
- for (const k in curSelector) {
154
- const key = k;
155
- const value = curSelector[key];
156
- if (key === 'type' || value == null) {
157
- continue;
158
- }
159
- const clauseNot = key.substr(0, 1) === '!';
160
- if (key === 'selector' || key === '!selector') {
161
- const tmpArr = (Array.isArray(value) ? value : [value]);
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
- else {
165
- if (key === 'qref' || key === '!qref') {
166
- throw new Error("Can't use checkData on qref clauses.");
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
- for (const curValue of value) {
170
- if (curValue == null) {
171
- continue;
172
- }
173
- if (((key === 'guid' || key === '!guid') && guid == null) ||
174
- ((key === 'tag' || key === '!tag') && tags == null)) {
175
- pass = true;
176
- }
177
- else {
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 = (0, utils_1.xor)(guid == propName, (0, utils_1.xor)(typeIsNot, clauseNot));
244
+ pass = xor(guid == propName, xor(typeIsNot, clauseNot));
187
245
  break;
188
246
  case 'tag':
189
247
  case '!tag':
190
- pass = (0, utils_1.xor)((tags ?? []).indexOf(propName) !== -1, (0, utils_1.xor)(typeIsNot, clauseNot));
248
+ pass = xor((tags ?? []).indexOf(propName) !== -1, xor(typeIsNot, clauseNot));
191
249
  break;
192
250
  case 'defined':
193
251
  case '!defined':
194
- pass = (0, utils_1.xor)(propName in data, (0, utils_1.xor)(typeIsNot, clauseNot));
252
+ pass = xor(propName in data, xor(typeIsNot, clauseNot));
195
253
  break;
196
254
  case 'truthy':
197
255
  case '!truthy':
198
- pass = (0, utils_1.xor)(propName in data && data[propName], (0, utils_1.xor)(typeIsNot, clauseNot));
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 = (0, utils_1.xor)(propName in data &&
204
- this.entityReferenceSearch(data[propName], testRefValue), (0, utils_1.xor)(typeIsNot, clauseNot));
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 = (0, utils_1.xor)(propName in data &&
267
+ pass = xor(propName in data &&
210
268
  JSON.stringify(data[propName]) ===
211
- JSON.stringify(testEqualValue), (0, utils_1.xor)(typeIsNot, clauseNot));
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 = (0, utils_1.xor)(propName in data &&
217
- JSON.stringify(data[propName]).indexOf(JSON.stringify(testContainValue)) !== -1, (0, utils_1.xor)(typeIsNot, clauseNot));
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 = (0, utils_1.xor)(propName in data &&
280
+ pass = xor(propName in data &&
223
281
  new RegExp('^' +
224
282
  escapeRegExp(testLikeValue)
225
283
  .replace('%', () => '.*')
226
284
  .replace('_', () => '.') +
227
- '$').test(data[propName]), (0, utils_1.xor)(typeIsNot, clauseNot));
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
- pass = (0, utils_1.xor)(propName in data &&
233
- this.posixRegexMatch(testMatchValue, data[propName]), (0, utils_1.xor)(typeIsNot, clauseNot));
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
- pass = (0, utils_1.xor)(propName in data &&
239
- this.posixRegexMatch(testIMatchValue, data[propName], true), (0, utils_1.xor)(typeIsNot, clauseNot));
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 = (0, utils_1.xor)(propName in data && data[propName] > testGTValue, (0, utils_1.xor)(typeIsNot, clauseNot));
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 = (0, utils_1.xor)(propName in data && data[propName] >= testGTEValue, (0, utils_1.xor)(typeIsNot, clauseNot));
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 = (0, utils_1.xor)(propName in data && data[propName] < testLTValue, (0, utils_1.xor)(typeIsNot, clauseNot));
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 = (0, utils_1.xor)(propName in data && data[propName] <= testLTEValue, (0, utils_1.xor)(typeIsNot, clauseNot));
339
+ pass = xor(propName in data && data[propName] <= testLTEValue, xor(typeIsNot, clauseNot));
260
340
  break;
261
341
  }
262
- }
263
- if (!(0, utils_1.xor)(typeIsOr, pass)) {
264
- break;
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 (!(0, utils_1.xor)(typeIsOr, pass)) {
270
- break;
352
+ if (!pass) {
353
+ return false;
271
354
  }
272
355
  }
273
- if (!pass) {
274
- return false;
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
- if (Array.isArray(value) || typeof value === 'object') {
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;
@@ -334,6 +432,7 @@ class NymphDriver {
334
432
  }
335
433
  formatSelectors(selectors, options = {}) {
336
434
  const newSelectors = [];
435
+ const qrefs = [];
337
436
  for (const curSelector of selectors) {
338
437
  const newSelector = {
339
438
  type: curSelector.type,
@@ -355,43 +454,49 @@ class NymphDriver {
355
454
  for (let i = 0; i < tmpArr.length; i++) {
356
455
  const name = tmpArr[i][0];
357
456
  const [qrefOptions, ...qrefSelectors] = tmpArr[i][1];
358
- const QrefEntityClass = typeof qrefOptions.class === 'string'
457
+ const QrefEntityClass = qrefOptions.class
359
458
  ? this.nymph.getEntityClass(qrefOptions.class)
360
- : qrefOptions.class ?? this.nymph.getEntityClass('Entity');
459
+ : this.nymph.getEntityClass('Entity');
361
460
  const newOptions = {
362
461
  ...qrefOptions,
363
462
  class: QrefEntityClass,
364
463
  source: options.source,
365
464
  };
366
465
  const newSelectors = this.formatSelectors(qrefSelectors, options);
367
- this.nymph.runQueryCallbacks(newOptions, newSelectors);
368
- formatArr[i] = [name, [newOptions, ...newSelectors]];
466
+ qrefs.push([newOptions, ...newSelectors.selectors], ...newSelectors.qrefs);
467
+ formatArr[i] = [name, [newOptions, ...newSelectors.selectors]];
369
468
  }
370
469
  newSelector[key] = formatArr;
371
470
  }
372
471
  else if (key === 'selector' || key === '!selector') {
373
472
  const tmpArr = (Array.isArray(value) ? value : [value]);
374
- newSelector[key] = this.formatSelectors(tmpArr, options);
473
+ const newSelectors = this.formatSelectors(tmpArr, options);
474
+ newSelector[key] = newSelectors.selectors;
475
+ qrefs.push(...newSelectors.qrefs);
375
476
  }
376
477
  else if (!Array.isArray(value)) {
478
+ // @ts-ignore: ts doesn't know what value is here.
377
479
  newSelector[key] = [[value]];
378
480
  }
379
481
  else if (!Array.isArray(value[0])) {
380
482
  if (value.length === 3 &&
381
483
  value[1] == null &&
382
484
  typeof value[2] === 'string') {
383
- newSelector[key] = [[value[0], (0, strtotime_1.default)(value[2]) * 1000]];
485
+ // @ts-ignore: ts doesn't know what value is here.
486
+ newSelector[key] = [[value[0], strtotime(value[2]) * 1000]];
384
487
  }
385
488
  else {
489
+ // @ts-ignore: ts doesn't know what value is here.
386
490
  newSelector[key] = [value];
387
491
  }
388
492
  }
389
493
  else {
494
+ // @ts-ignore: ts doesn't know what value is here.
390
495
  newSelector[key] = value.map((curValue) => {
391
496
  if (curValue.length === 3 &&
392
497
  curValue[1] == null &&
393
498
  typeof curValue[2] === 'string') {
394
- return [curValue[0], (0, strtotime_1.default)(curValue[2]) * 1000];
499
+ return [curValue[0], strtotime(curValue[2]) * 1000];
395
500
  }
396
501
  return curValue;
397
502
  });
@@ -399,7 +504,7 @@ class NymphDriver {
399
504
  }
400
505
  newSelectors.push(newSelector);
401
506
  }
402
- return newSelectors;
507
+ return { selectors: newSelectors, qrefs };
403
508
  }
404
509
  iterateSelectorsForQuery(selectors, callback) {
405
510
  const queryParts = [];
@@ -413,7 +518,7 @@ class NymphDriver {
413
518
  if (key === 'type') {
414
519
  continue;
415
520
  }
416
- let curQuery = callback(key, value, typeIsOr, typeIsNot);
521
+ let curQuery = callback({ key, value, typeIsOr, typeIsNot });
417
522
  if (curQuery) {
418
523
  if (curSelectorQuery) {
419
524
  curSelectorQuery += typeIsOr ? ' OR ' : ' AND ';
@@ -427,214 +532,357 @@ class NymphDriver {
427
532
  }
428
533
  return queryParts;
429
534
  }
430
- getEntitySync(options = {}, ...selectors) {
431
- if (typeof selectors[0] === 'string') {
432
- selectors = [{ type: '&', guid: selectors[0] }];
433
- }
434
- options.limit = 1;
435
- const entities = this.getEntitiesSync(options, ...selectors);
436
- if (options.return === 'count') {
437
- return entities;
438
- }
439
- if (!entities || !entities.length) {
440
- return null;
441
- }
442
- return entities[0];
443
- }
444
- getEntitesRowLike(options, selectors, performQueryCallback, rowFetchCallback, freeResultCallback, getCountCallback, getGUIDCallback, getTagsAndDatesCallback, getDataNameAndSValueCallback) {
535
+ getEntitiesRowLike(options, selectors, performQueryCallback, rowFetchCallback, freeResultCallback, getCountCallback, getGUIDCallback, getTagsAndDatesCallback, getDataNameAndSValueCallback) {
445
536
  if (!this.isConnected()) {
446
- throw new errors_1.UnableToConnectError('not connected to DB');
537
+ throw new UnableToConnectError('not connected to DB');
447
538
  }
448
539
  for (const selector of selectors) {
449
540
  if (!selector ||
450
541
  Object.keys(selector).length === 1 ||
451
542
  !('type' in selector) ||
452
543
  ['&', '!&', '|', '!|'].indexOf(selector.type) === -1) {
453
- throw new errors_1.InvalidParametersError('Invalid query selector passed: ' + JSON.stringify(selector));
544
+ throw new InvalidParametersError('Invalid query selector passed: ' + JSON.stringify(selector));
454
545
  }
455
546
  }
456
547
  let entities = [];
457
548
  let count = 0;
458
549
  const EntityClass = options.class ?? this.nymph.getEntityClass('Entity');
459
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.
460
555
  if (this.nymph.config.cache &&
461
556
  !options.skipCache &&
557
+ selectors.length &&
462
558
  'guid' in selectors[0] &&
463
559
  typeof selectors[0].guid === 'number') {
560
+ // Only safe to use the cache option with no other selectors than a GUID
561
+ // and tags.
464
562
  if (selectors.length === 1 &&
465
563
  selectors[0].type === '&' &&
466
564
  (Object.keys(selectors[0]).length === 2 ||
467
565
  (Object.keys(selectors[0]).length === 3 && 'tag' in selectors[0]))) {
468
- const entity = this.pullCache(selectors[0]['guid'], EntityClass.class, !!options.skipAc);
469
- if (entity != null &&
470
- entity.guid != null &&
566
+ const cacheEntity = this.pullCache(selectors[0]['guid']);
567
+ if (cacheEntity != null &&
568
+ cacheEntity.guid != null &&
471
569
  (!('tag' in selectors[0]) ||
472
- entity.hasTag(...(Array.isArray(selectors[0].tag)
570
+ !(Array.isArray(selectors[0].tag)
473
571
  ? selectors[0].tag
474
- : [selectors[0].tag])))) {
475
- const guid = entity.guid;
476
- return {
477
- result: Promise.resolve(null),
478
- process: () => options.return === 'count'
479
- ? guid
480
- ? 1
481
- : 0
482
- : options.return === 'guid'
483
- ? [guid]
484
- : [entity],
485
- };
486
- }
487
- }
488
- }
489
- const formattedSelectors = this.formatSelectors(selectors, options);
490
- this.nymph.runQueryCallbacks(options, formattedSelectors);
491
- const { result } = performQueryCallback(options, formattedSelectors, etype);
492
- return {
493
- result,
494
- process: () => {
495
- let row = rowFetchCallback();
496
- if (options.return === 'count') {
497
- while (row != null) {
498
- count += getCountCallback(row);
499
- row = rowFetchCallback();
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
+ };
500
580
  }
501
- }
502
- else if (options.return === 'guid') {
503
- while (row != null) {
504
- entities.push(getGUIDCallback(row));
505
- row = rowFetchCallback();
581
+ else if (options.return === 'guid') {
582
+ return {
583
+ result: Promise.resolve(null),
584
+ process: () => [guid],
585
+ };
506
586
  }
507
- }
508
- else {
509
- while (row != null) {
510
- const guid = getGUIDCallback(row);
511
- const tagsAndDates = getTagsAndDatesCallback(row);
512
- const tags = tagsAndDates.tags;
513
- const cdate = tagsAndDates.cdate;
514
- const mdate = tagsAndDates.mdate;
515
- let dataNameAndSValue = getDataNameAndSValueCallback(row);
516
- const data = {};
517
- const sdata = {};
518
- if (dataNameAndSValue.name !== '') {
519
- do {
520
- dataNameAndSValue = getDataNameAndSValueCallback(row);
521
- sdata[dataNameAndSValue.name] = dataNameAndSValue.svalue;
522
- row = rowFetchCallback();
523
- } while (row != null && getGUIDCallback(row) === guid);
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
+ };
524
623
  }
525
- else {
624
+ return {
625
+ result: Promise.resolve(null),
626
+ process: () => [],
627
+ };
628
+ }
629
+ }
630
+ }
631
+ }
632
+ try {
633
+ const formattedSelectors = this.formatSelectors(selectors, options);
634
+ this.nymph.runQueryCallbacks(options, formattedSelectors.selectors);
635
+ for (let i = 0; i < formattedSelectors.qrefs.length; i++) {
636
+ const [options, ...selectors] = formattedSelectors.qrefs[i];
637
+ this.nymph.runQueryCallbacks(options, selectors);
638
+ formattedSelectors.qrefs[i] = [options, ...selectors];
639
+ }
640
+ const { result } = performQueryCallback({
641
+ options,
642
+ selectors: formattedSelectors.selectors,
643
+ etype,
644
+ });
645
+ return {
646
+ result,
647
+ process: () => {
648
+ let row = rowFetchCallback();
649
+ if (options.return === 'count') {
650
+ while (row != null) {
651
+ count += getCountCallback(row);
526
652
  row = rowFetchCallback();
527
653
  }
528
- const entity = EntityClass.factorySync();
529
- entity.$nymph = this.nymph;
530
- if (options.skipAc != null) {
531
- entity.$useSkipAc(!!options.skipAc);
532
- }
533
- entity.guid = guid;
534
- entity.cdate = cdate;
535
- entity.mdate = mdate;
536
- entity.tags = tags;
537
- this.putDataCounter++;
538
- if (this.putDataCounter == 100) {
539
- throw new Error('Infinite loop detected in Entity loading.');
654
+ }
655
+ else if (options.return === 'guid') {
656
+ while (row != null) {
657
+ entities.push(getGUIDCallback(row));
658
+ row = rowFetchCallback();
540
659
  }
541
- entity.$putData(data, sdata);
542
- this.putDataCounter--;
543
- if (this.nymph.config.cache) {
544
- this.pushCache(guid, cdate, mdate, tags, data, sdata);
660
+ }
661
+ else {
662
+ while (row != null) {
663
+ const guid = getGUIDCallback(row);
664
+ const tagsAndDates = getTagsAndDatesCallback(row);
665
+ const tags = tagsAndDates.tags;
666
+ const cdate = tagsAndDates.cdate;
667
+ const mdate = tagsAndDates.mdate;
668
+ let dataNameAndSValue = getDataNameAndSValueCallback(row);
669
+ // Data.
670
+ const data = {};
671
+ // Serialized data.
672
+ const sdata = {};
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.
676
+ do {
677
+ dataNameAndSValue = getDataNameAndSValueCallback(row);
678
+ sdata[dataNameAndSValue.name] = dataNameAndSValue.svalue;
679
+ row = rowFetchCallback();
680
+ } while (row != null && getGUIDCallback(row) === guid);
681
+ }
682
+ else {
683
+ // Make sure that $row is incremented :)
684
+ row = rowFetchCallback();
685
+ }
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
+ });
698
+ }
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);
716
+ }
717
+ if (this.nymph.config.cache) {
718
+ this.pushCache(guid, cdate, mdate, tags, data, sdata);
719
+ }
545
720
  }
546
- entities.push(entity);
547
721
  }
548
- }
549
- freeResultCallback();
550
- return options.return === 'count' ? count : entities;
551
- },
552
- };
722
+ freeResultCallback();
723
+ return options.return === 'count' ? count : entities;
724
+ },
725
+ };
726
+ }
727
+ catch (e) {
728
+ return {
729
+ result: Promise.resolve(e),
730
+ process: () => {
731
+ return e;
732
+ },
733
+ };
734
+ }
553
735
  }
554
736
  async saveEntityRowLike(entity, saveNewEntityCallback, saveExistingEntityCallback, startTransactionCallback = null, commitTransactionCallback = null) {
555
- const mdate = Date.now();
556
- const tags = (0, lodash_1.difference)(entity.tags, ['']);
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, ['']);
557
751
  const data = entity.$getData();
558
752
  const sdata = entity.$getSData();
753
+ const uniques = await entity.$getUniques();
559
754
  const EntityClass = entity.constructor;
560
755
  const etype = EntityClass.ETYPE;
561
756
  if (startTransactionCallback) {
562
757
  await startTransactionCallback();
563
758
  }
564
759
  let success = false;
565
- if (entity.guid == null) {
566
- const cdate = mdate;
567
- const newId = (0, guid_1.guid)();
568
- success = await saveNewEntityCallback(entity, newId, tags, data, sdata, cdate, etype);
569
- if (success) {
570
- entity.guid = newId;
571
- entity.cdate = cdate;
572
- entity.mdate = mdate;
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
+ }
573
779
  }
574
- }
575
- else {
576
- if (this.nymph.config.cache) {
577
- this.cleanCache(entity.guid);
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
+ }
798
+ }
799
+ if (commitTransactionCallback) {
800
+ success = await commitTransactionCallback(success);
578
801
  }
579
- success = await saveExistingEntityCallback(entity, entity.guid, tags, data, sdata, mdate, etype);
580
- if (success) {
581
- entity.mdate = mdate;
802
+ // Cache the entity.
803
+ if (success && this.nymph.config.cache) {
804
+ this.pushCache(entity.guid, entity.cdate, entity.mdate, entity.tags, data, sdata);
582
805
  }
583
806
  }
584
- if (commitTransactionCallback) {
585
- success = await commitTransactionCallback(success);
807
+ catch (e) {
808
+ entity.guid = originalGuid;
809
+ entity.cdate = originalCdate;
810
+ entity.mdate = originalMdate;
811
+ throw e;
586
812
  }
587
- if (success && this.nymph.config.cache) {
588
- this.pushCache(entity.guid, entity.cdate, entity.mdate, entity.tags, data, sdata);
813
+ if (!success) {
814
+ entity.guid = originalGuid;
815
+ entity.cdate = originalCdate;
816
+ entity.mdate = originalMdate;
589
817
  }
590
818
  return success;
591
819
  }
592
- pullCache(guid, className, useSkipAc = false) {
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.
593
828
  if (!(guid in this.entityCount)) {
594
829
  this.entityCount[guid] = 0;
595
830
  }
596
831
  this.entityCount[guid]++;
597
832
  if (guid in this.entityCache) {
598
- const entity = this.nymph.getEntityClass(className).factorySync();
599
- if (entity) {
600
- entity.$nymph = this.nymph;
601
- entity.$useSkipAc(!!useSkipAc);
602
- entity.guid = guid;
603
- entity.cdate = this.entityCache[guid]['cdate'];
604
- entity.mdate = this.entityCache[guid]['mdate'];
605
- entity.tags = this.entityCache[guid]['tags'];
606
- entity.$putData(this.entityCache[guid]['data'], this.entityCache[guid]['sdata']);
607
- return entity;
608
- }
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
+ };
609
841
  }
610
842
  return null;
611
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
+ */
612
854
  pushCache(guid, cdate, mdate, tags, data, sdata) {
613
855
  if (guid == null) {
614
856
  return;
615
857
  }
858
+ // Increment the entity access count.
616
859
  if (!(guid in this.entityCount)) {
617
860
  this.entityCount[guid] = 0;
618
861
  }
619
862
  this.entityCount[guid]++;
863
+ // Check the threshold.
620
864
  if (this.entityCount[guid] < this.nymph.config.cacheThreshold) {
621
865
  return;
622
866
  }
867
+ // Cache the entity.
623
868
  this.entityCache[guid] = {
624
- cdate: cdate,
625
- mdate: mdate,
626
- tags: tags,
627
- data: data,
628
- sdata: sdata,
869
+ cdate,
870
+ mdate,
871
+ tags,
872
+ data,
873
+ sdata,
629
874
  };
630
875
  while (this.nymph.config.cacheLimit &&
631
876
  Object.keys(this.entityCache).length >= this.nymph.config.cacheLimit) {
877
+ // Find which entity has been accessed the least.
632
878
  const least = Object.entries(this.entityCount)
633
- .sort(([guidA, countA], [guidB, countB]) => countA - countB)
879
+ .sort(([_guidA, countA], [_guidB, countB]) => countA - countB)
634
880
  .pop()?.[0] ?? null;
635
881
  if (least == null) {
882
+ // This should never happen.
636
883
  return;
637
884
  }
885
+ // Remove it.
638
886
  delete this.entityCache[least];
639
887
  delete this.entityCount[least];
640
888
  }
@@ -648,5 +896,4 @@ class NymphDriver {
648
896
  return matches.map((match) => match.replace(re, '$1'));
649
897
  }
650
898
  }
651
- exports.default = NymphDriver;
652
899
  //# sourceMappingURL=NymphDriver.js.map