@nymphjs/nymph 1.0.0-beta.9 → 1.0.0-beta.90

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