@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.
- package/CHANGELOG.md +535 -0
- package/README.md +14 -22
- package/dist/Entity.d.ts +293 -10
- package/dist/Entity.js +571 -149
- package/dist/Entity.js.map +1 -1
- package/dist/Entity.test.js +67 -26
- 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 +497 -13
- package/dist/Nymph.js +761 -188
- package/dist/Nymph.js.map +1 -1
- package/dist/Nymph.types.d.ts +95 -26
- 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 +181 -43
- package/dist/driver/NymphDriver.js +532 -285
- 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 +939 -219
- 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 +28 -11
- package/dist/testMocks.js +44 -28
- 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 +24 -20
- package/src/Entity.test.ts +63 -13
- package/src/Entity.ts +498 -156
- package/src/Entity.types.ts +72 -74
- package/src/Nymph.ts +904 -336
- package/src/Nymph.types.ts +179 -44
- 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 +650 -460
- package/src/driver/index.ts +2 -2
- package/src/driver/testArtifacts.ts +966 -196
- 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 +85 -37
- 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,20 +1,24 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import { difference } from 'lodash';
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import { difference } from 'lodash-es';
|
|
3
3
|
import ReadLines from 'n-readlines';
|
|
4
|
-
import strtotime from 'locutus/php/datetime/strtotime';
|
|
5
|
-
import {
|
|
4
|
+
import strtotime from 'locutus/php/datetime/strtotime.js';
|
|
5
|
+
import { Tokenizer } from '@sciactive/tokenizer';
|
|
6
6
|
|
|
7
|
-
import type Nymph from '../Nymph';
|
|
8
|
-
import {
|
|
7
|
+
import type Nymph from '../Nymph.js';
|
|
8
|
+
import type { Selector, Options, FormattedSelector } from '../Nymph.types.js';
|
|
9
|
+
import type { EntityInstanceType, EntityObjectType } from '../Entity.js';
|
|
10
|
+
import type {
|
|
9
11
|
EntityConstructor,
|
|
10
12
|
EntityData,
|
|
11
13
|
EntityInterface,
|
|
12
14
|
EntityReference,
|
|
13
15
|
SerializedEntityData,
|
|
14
|
-
} from '../Entity.types';
|
|
15
|
-
import {
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
} from '../Entity.types.js';
|
|
17
|
+
import {
|
|
18
|
+
InvalidParametersError,
|
|
19
|
+
UnableToConnectError,
|
|
20
|
+
} from '../errors/index.js';
|
|
21
|
+
import { xor } from '../utils.js';
|
|
18
22
|
|
|
19
23
|
// from: https://stackoverflow.com/a/6969486/664915
|
|
20
24
|
function escapeRegExp(string: string): string {
|
|
@@ -28,11 +32,13 @@ export default abstract class NymphDriver {
|
|
|
28
32
|
protected nymph: Nymph = new Proxy({} as Nymph, {
|
|
29
33
|
get() {
|
|
30
34
|
throw new Error(
|
|
31
|
-
'Attempted access of Nymph instance before driver initialization!'
|
|
35
|
+
'Attempted access of Nymph instance before driver initialization!',
|
|
32
36
|
);
|
|
33
37
|
},
|
|
34
38
|
});
|
|
35
39
|
|
|
40
|
+
protected tokenizer = new Tokenizer();
|
|
41
|
+
|
|
36
42
|
/**
|
|
37
43
|
* A cache to make entity retrieval faster.
|
|
38
44
|
*/
|
|
@@ -54,22 +60,49 @@ export default abstract class NymphDriver {
|
|
|
54
60
|
*/
|
|
55
61
|
private putDataCounter = 0;
|
|
56
62
|
|
|
63
|
+
/**
|
|
64
|
+
* This is used internally by Nymph. Don't call it yourself.
|
|
65
|
+
*
|
|
66
|
+
* @returns A clone of this instance.
|
|
67
|
+
*/
|
|
68
|
+
abstract clone(): NymphDriver;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Connect to the data store.
|
|
72
|
+
*/
|
|
57
73
|
abstract connect(): Promise<boolean>;
|
|
74
|
+
|
|
58
75
|
abstract isConnected(): boolean;
|
|
76
|
+
protected abstract internalTransaction(name: string): Promise<any>;
|
|
59
77
|
abstract startTransaction(name: string): Promise<Nymph>;
|
|
60
78
|
abstract commit(name: string): Promise<boolean>;
|
|
61
79
|
abstract rollback(name: string): Promise<boolean>;
|
|
62
80
|
abstract inTransaction(): Promise<boolean>;
|
|
63
81
|
abstract deleteEntityByID(
|
|
64
82
|
guid: string,
|
|
65
|
-
classConstructor: EntityConstructor
|
|
83
|
+
classConstructor: EntityConstructor,
|
|
66
84
|
): Promise<boolean>;
|
|
67
85
|
abstract deleteEntityByID(guid: string, className?: string): Promise<boolean>;
|
|
68
86
|
abstract deleteUID(name: string): Promise<boolean>;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Disconnect from the data store.
|
|
90
|
+
*/
|
|
69
91
|
abstract disconnect(): Promise<boolean>;
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
): Promise<void>;
|
|
92
|
+
|
|
93
|
+
abstract needsMigration(): Promise<'json' | 'tokens' | false>;
|
|
94
|
+
abstract liveMigration(migrationType: 'tokenTables'): Promise<void>;
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Returns a list of the etypes stored in the DB.
|
|
98
|
+
*/
|
|
99
|
+
abstract getEtypes(): Promise<string[]>;
|
|
100
|
+
|
|
101
|
+
abstract exportDataIterator(): AsyncGenerator<
|
|
102
|
+
{ type: 'comment' | 'uid' | 'entity'; content: string },
|
|
103
|
+
void,
|
|
104
|
+
undefined | false
|
|
105
|
+
>;
|
|
73
106
|
abstract getEntities<T extends EntityConstructor = EntityConstructor>(
|
|
74
107
|
options: Options<T> & { return: 'count' },
|
|
75
108
|
...selectors: Selector[]
|
|
@@ -79,39 +112,37 @@ export default abstract class NymphDriver {
|
|
|
79
112
|
...selectors: Selector[]
|
|
80
113
|
): Promise<string[]>;
|
|
81
114
|
abstract getEntities<T extends EntityConstructor = EntityConstructor>(
|
|
82
|
-
options
|
|
115
|
+
options: Options<T> & { return: 'object' },
|
|
83
116
|
...selectors: Selector[]
|
|
84
|
-
): Promise<
|
|
117
|
+
): Promise<EntityObjectType<T>[]>;
|
|
85
118
|
abstract getEntities<T extends EntityConstructor = EntityConstructor>(
|
|
86
119
|
options?: Options<T>,
|
|
87
120
|
...selectors: Selector[]
|
|
88
|
-
): Promise<
|
|
89
|
-
|
|
90
|
-
T extends EntityConstructor = EntityConstructor
|
|
91
|
-
>(
|
|
92
|
-
options: Options<T> & { return: 'count' },
|
|
93
|
-
...selectors: Selector[]
|
|
94
|
-
): number;
|
|
95
|
-
protected abstract getEntitiesSync<
|
|
96
|
-
T extends EntityConstructor = EntityConstructor
|
|
97
|
-
>(
|
|
98
|
-
options: Options<T> & { return: 'guid' },
|
|
99
|
-
...selectors: Selector[]
|
|
100
|
-
): string[];
|
|
101
|
-
protected abstract getEntitiesSync<
|
|
102
|
-
T extends EntityConstructor = EntityConstructor
|
|
103
|
-
>(
|
|
104
|
-
options?: Options<T>,
|
|
105
|
-
...selectors: Selector[]
|
|
106
|
-
): ReturnType<T['factorySync']>[];
|
|
107
|
-
protected abstract getEntitiesSync<
|
|
108
|
-
T extends EntityConstructor = EntityConstructor
|
|
109
|
-
>(
|
|
121
|
+
): Promise<EntityInstanceType<T>[]>;
|
|
122
|
+
abstract getEntities<T extends EntityConstructor = EntityConstructor>(
|
|
110
123
|
options?: Options<T>,
|
|
111
124
|
...selectors: Selector[]
|
|
112
|
-
):
|
|
125
|
+
): Promise<
|
|
126
|
+
EntityInstanceType<T>[] | EntityObjectType<T>[] | string[] | number
|
|
127
|
+
>;
|
|
113
128
|
abstract getUID(name: string): Promise<number | null>;
|
|
114
|
-
abstract
|
|
129
|
+
abstract importEntity(entity: {
|
|
130
|
+
guid: string;
|
|
131
|
+
cdate: number;
|
|
132
|
+
mdate: number;
|
|
133
|
+
tags: string[];
|
|
134
|
+
sdata: SerializedEntityData;
|
|
135
|
+
etype: string;
|
|
136
|
+
}): Promise<void>;
|
|
137
|
+
abstract importEntityTokens(entity: {
|
|
138
|
+
guid: string;
|
|
139
|
+
cdate: number;
|
|
140
|
+
mdate: number;
|
|
141
|
+
tags: string[];
|
|
142
|
+
sdata: SerializedEntityData;
|
|
143
|
+
etype: string;
|
|
144
|
+
}): Promise<void>;
|
|
145
|
+
abstract importUID(uid: { name: string; value: number }): Promise<void>;
|
|
115
146
|
abstract newUID(name: string): Promise<number | null>;
|
|
116
147
|
abstract renameUID(oldName: string, newName: string): Promise<boolean>;
|
|
117
148
|
abstract saveEntity(entity: EntityInterface): Promise<boolean>;
|
|
@@ -131,7 +162,7 @@ export default abstract class NymphDriver {
|
|
|
131
162
|
protected posixRegexMatch(
|
|
132
163
|
pattern: string,
|
|
133
164
|
subject: string,
|
|
134
|
-
caseInsensitive = false
|
|
165
|
+
caseInsensitive = false,
|
|
135
166
|
) {
|
|
136
167
|
const posixClasses = [
|
|
137
168
|
// '[[:<:]]',
|
|
@@ -188,9 +219,9 @@ export default abstract class NymphDriver {
|
|
|
188
219
|
if (!fhandle) {
|
|
189
220
|
throw new InvalidParametersError('Provided filename is not writeable.');
|
|
190
221
|
}
|
|
191
|
-
await this.
|
|
192
|
-
fs.writeSync(fhandle,
|
|
193
|
-
}
|
|
222
|
+
for await (let entry of this.exportDataIterator()) {
|
|
223
|
+
fs.writeSync(fhandle, entry.content);
|
|
224
|
+
}
|
|
194
225
|
fs.closeSync(fhandle);
|
|
195
226
|
} catch (e: any) {
|
|
196
227
|
return false;
|
|
@@ -199,130 +230,157 @@ export default abstract class NymphDriver {
|
|
|
199
230
|
}
|
|
200
231
|
|
|
201
232
|
public async exportPrint() {
|
|
202
|
-
await this.
|
|
203
|
-
console.log(
|
|
204
|
-
}
|
|
233
|
+
for await (let entry of this.exportDataIterator()) {
|
|
234
|
+
console.log(entry.content);
|
|
235
|
+
}
|
|
205
236
|
return true;
|
|
206
237
|
}
|
|
207
238
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
guid: string,
|
|
212
|
-
tags: string[],
|
|
213
|
-
sdata: SerializedEntityData,
|
|
214
|
-
etype: string
|
|
215
|
-
) => Promise<void>,
|
|
216
|
-
saveUIDCallback: (name: string, value: number) => Promise<void>,
|
|
217
|
-
startTransactionCallback: (() => Promise<void>) | null = null,
|
|
218
|
-
commitTransactionCallback: (() => Promise<void>) | null = null
|
|
239
|
+
public async importDataIterator(
|
|
240
|
+
lines: Iterable<string>,
|
|
241
|
+
transaction?: boolean,
|
|
219
242
|
) {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
throw new InvalidParametersError('Provided filename is unreadable.');
|
|
243
|
+
const first = lines[Symbol.iterator]().next();
|
|
244
|
+
|
|
245
|
+
if (first.done || first.value !== '#nex2') {
|
|
246
|
+
throw new Error('Tried to import a file that is not a NEX v2 file.');
|
|
225
247
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
let tags: string[] = [];
|
|
230
|
-
let etype = '__undefined';
|
|
231
|
-
if (startTransactionCallback) {
|
|
232
|
-
await startTransactionCallback();
|
|
248
|
+
|
|
249
|
+
if (transaction) {
|
|
250
|
+
await this.internalTransaction('nymph-import');
|
|
233
251
|
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
/^\s
|
|
241
|
-
|
|
242
|
-
const propMatch = text.match(/^\s*([^=]+)\s*=\s*(.*\S)\s*$/);
|
|
243
|
-
const uidMatch = text.match(/^\s*<([^>]+)>\[(\d+)\]\s*$/);
|
|
244
|
-
if (uidMatch) {
|
|
245
|
-
// Add the UID.
|
|
246
|
-
await saveUIDCallback(uidMatch[1], Number(uidMatch[2]));
|
|
247
|
-
} else if (entityMatch) {
|
|
248
|
-
// Save the current entity.
|
|
249
|
-
if (guid) {
|
|
250
|
-
await saveEntityCallback(guid, tags, sdata, etype);
|
|
251
|
-
guid = null;
|
|
252
|
-
tags = [];
|
|
253
|
-
sdata = {};
|
|
254
|
-
etype = '__undefined';
|
|
252
|
+
try {
|
|
253
|
+
let guid: string | null = null;
|
|
254
|
+
let sdata: SerializedEntityData = {};
|
|
255
|
+
let tags: string[] = [];
|
|
256
|
+
let etype = '__undefined';
|
|
257
|
+
for (let line of lines) {
|
|
258
|
+
if (line.match(/^\s*#/)) {
|
|
259
|
+
continue;
|
|
255
260
|
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
261
|
+
const entityMatch = line.match(
|
|
262
|
+
/^\s*{([0-9A-Fa-f]+)}<([-\w_]+)>\[([^\]]*)\]\s*$/,
|
|
263
|
+
);
|
|
264
|
+
const propMatch = line.match(/^\s*([^=]+)\s*=\s*(.*\S)\s*$/);
|
|
265
|
+
const uidMatch = line.match(/^\s*<([^>]+)>\[(\d+)\]\s*$/);
|
|
266
|
+
if (uidMatch) {
|
|
267
|
+
// Add the UID.
|
|
268
|
+
await this.importUID({
|
|
269
|
+
name: uidMatch[1],
|
|
270
|
+
value: Number(uidMatch[2]),
|
|
271
|
+
});
|
|
272
|
+
} else if (entityMatch) {
|
|
273
|
+
// Save the current entity.
|
|
274
|
+
if (guid) {
|
|
275
|
+
const cdate = Number(JSON.parse(sdata.cdate));
|
|
276
|
+
delete sdata.cdate;
|
|
277
|
+
const mdate = Number(JSON.parse(sdata.mdate));
|
|
278
|
+
delete sdata.mdate;
|
|
279
|
+
await this.importEntity({ guid, cdate, mdate, tags, sdata, etype });
|
|
280
|
+
guid = null;
|
|
281
|
+
tags = [];
|
|
282
|
+
sdata = {};
|
|
283
|
+
etype = '__undefined';
|
|
284
|
+
}
|
|
285
|
+
// Record the new entity's info.
|
|
286
|
+
guid = entityMatch[1];
|
|
287
|
+
etype = entityMatch[2];
|
|
288
|
+
tags = entityMatch[3].split(',');
|
|
289
|
+
} else if (propMatch) {
|
|
290
|
+
// Add the variable to the new entity.
|
|
291
|
+
if (guid) {
|
|
292
|
+
sdata[propMatch[1]] = propMatch[2];
|
|
293
|
+
}
|
|
264
294
|
}
|
|
295
|
+
// Clear the entity cache.
|
|
296
|
+
this.entityCache = {};
|
|
265
297
|
}
|
|
266
|
-
//
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
298
|
+
// Save the last entity.
|
|
299
|
+
if (guid) {
|
|
300
|
+
const cdate = Number(JSON.parse(sdata.cdate));
|
|
301
|
+
delete sdata.cdate;
|
|
302
|
+
const mdate = Number(JSON.parse(sdata.mdate));
|
|
303
|
+
delete sdata.mdate;
|
|
304
|
+
await this.importEntity({ guid, cdate, mdate, tags, sdata, etype });
|
|
305
|
+
}
|
|
306
|
+
if (transaction) {
|
|
307
|
+
await this.commit('nymph-import');
|
|
308
|
+
}
|
|
309
|
+
} catch (e: any) {
|
|
310
|
+
await this.rollback('nymph-import');
|
|
311
|
+
throw e;
|
|
275
312
|
}
|
|
276
313
|
return true;
|
|
277
314
|
}
|
|
278
315
|
|
|
316
|
+
public async importData(text: string, transaction?: boolean) {
|
|
317
|
+
return await this.importDataIterator(text.split('\n'), transaction);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
public async import(filename: string, transaction?: boolean) {
|
|
321
|
+
let rl: ReadLines;
|
|
322
|
+
try {
|
|
323
|
+
rl = new ReadLines(filename);
|
|
324
|
+
} catch (e: any) {
|
|
325
|
+
throw new InvalidParametersError('Provided filename is unreadable.');
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const lines = {
|
|
329
|
+
*[Symbol.iterator]() {
|
|
330
|
+
let line: false | Buffer;
|
|
331
|
+
while ((line = rl.next())) {
|
|
332
|
+
yield line.toString('utf8');
|
|
333
|
+
}
|
|
334
|
+
},
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
return await this.importDataIterator(lines, transaction);
|
|
338
|
+
}
|
|
339
|
+
|
|
279
340
|
public checkData(
|
|
341
|
+
etype: string,
|
|
280
342
|
data: EntityData,
|
|
281
343
|
sdata: SerializedEntityData,
|
|
282
344
|
selectors: Selector[],
|
|
283
345
|
guid: string | null = null,
|
|
284
|
-
tags: string[] | null = null
|
|
346
|
+
tags: string[] | null = null,
|
|
285
347
|
) {
|
|
286
|
-
|
|
348
|
+
try {
|
|
349
|
+
const EntityClass = this.nymph.getEntityClassByEtype(etype);
|
|
350
|
+
const formattedSelectors = this.formatSelectors(selectors).selectors;
|
|
287
351
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
352
|
+
for (const curSelector of formattedSelectors) {
|
|
353
|
+
const type = curSelector.type;
|
|
354
|
+
const typeIsNot = type === '!&' || type === '!|';
|
|
355
|
+
const typeIsOr = type === '|' || type === '!|';
|
|
356
|
+
let pass = !typeIsOr;
|
|
357
|
+
for (const k in curSelector) {
|
|
358
|
+
const key = k as keyof Selector;
|
|
359
|
+
const value = curSelector[key];
|
|
296
360
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
361
|
+
if (key === 'type' || value == null) {
|
|
362
|
+
continue;
|
|
363
|
+
}
|
|
300
364
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
xor(
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
throw new Error("Can't use checkData on qref clauses.");
|
|
365
|
+
const clauseNot = key.substring(0, 1) === '!';
|
|
366
|
+
if (key === 'selector' || key === '!selector') {
|
|
367
|
+
const tmpArr = (
|
|
368
|
+
Array.isArray(value) ? value : [value]
|
|
369
|
+
) as Selector[];
|
|
370
|
+
pass = xor(
|
|
371
|
+
this.checkData(etype, data, sdata, tmpArr, guid, tags),
|
|
372
|
+
xor(typeIsNot, clauseNot),
|
|
373
|
+
);
|
|
311
374
|
} else {
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
if
|
|
316
|
-
|
|
317
|
-
|
|
375
|
+
if (key === 'qref' || key === '!qref') {
|
|
376
|
+
throw new Error("Can't use checkData on qref clauses.");
|
|
377
|
+
} else {
|
|
378
|
+
// Check if it doesn't pass any for &, check if it passes any for |.
|
|
379
|
+
for (const curValue of value) {
|
|
380
|
+
if (curValue == null) {
|
|
381
|
+
continue;
|
|
382
|
+
}
|
|
318
383
|
|
|
319
|
-
if (
|
|
320
|
-
((key === 'guid' || key === '!guid') && guid == null) ||
|
|
321
|
-
((key === 'tag' || key === '!tag') && tags == null)
|
|
322
|
-
) {
|
|
323
|
-
// Skip because it has already been checked (by the query).
|
|
324
|
-
pass = true;
|
|
325
|
-
} else {
|
|
326
384
|
const propName = (curValue as string[])[0];
|
|
327
385
|
|
|
328
386
|
// Unserialize the data for this variable.
|
|
@@ -340,7 +398,7 @@ export default abstract class NymphDriver {
|
|
|
340
398
|
case '!tag':
|
|
341
399
|
pass = xor(
|
|
342
400
|
(tags ?? []).indexOf(propName) !== -1,
|
|
343
|
-
xor(typeIsNot, clauseNot)
|
|
401
|
+
xor(typeIsNot, clauseNot),
|
|
344
402
|
);
|
|
345
403
|
break;
|
|
346
404
|
case 'defined':
|
|
@@ -351,7 +409,7 @@ export default abstract class NymphDriver {
|
|
|
351
409
|
case '!truthy':
|
|
352
410
|
pass = xor(
|
|
353
411
|
propName in data && data[propName],
|
|
354
|
-
xor(typeIsNot, clauseNot)
|
|
412
|
+
xor(typeIsNot, clauseNot),
|
|
355
413
|
);
|
|
356
414
|
break;
|
|
357
415
|
case 'ref':
|
|
@@ -361,9 +419,9 @@ export default abstract class NymphDriver {
|
|
|
361
419
|
propName in data &&
|
|
362
420
|
this.entityReferenceSearch(
|
|
363
421
|
data[propName],
|
|
364
|
-
testRefValue
|
|
422
|
+
testRefValue,
|
|
365
423
|
),
|
|
366
|
-
xor(typeIsNot, clauseNot)
|
|
424
|
+
xor(typeIsNot, clauseNot),
|
|
367
425
|
);
|
|
368
426
|
break;
|
|
369
427
|
case 'equal':
|
|
@@ -375,7 +433,7 @@ export default abstract class NymphDriver {
|
|
|
375
433
|
propName in data &&
|
|
376
434
|
JSON.stringify(data[propName]) ===
|
|
377
435
|
JSON.stringify(testEqualValue),
|
|
378
|
-
xor(typeIsNot, clauseNot)
|
|
436
|
+
xor(typeIsNot, clauseNot),
|
|
379
437
|
);
|
|
380
438
|
break;
|
|
381
439
|
case 'contain':
|
|
@@ -386,9 +444,9 @@ export default abstract class NymphDriver {
|
|
|
386
444
|
pass = xor(
|
|
387
445
|
propName in data &&
|
|
388
446
|
JSON.stringify(data[propName]).indexOf(
|
|
389
|
-
JSON.stringify(testContainValue)
|
|
447
|
+
JSON.stringify(testContainValue),
|
|
390
448
|
) !== -1,
|
|
391
|
-
xor(typeIsNot, clauseNot)
|
|
449
|
+
xor(typeIsNot, clauseNot),
|
|
392
450
|
);
|
|
393
451
|
break;
|
|
394
452
|
case 'like':
|
|
@@ -403,9 +461,27 @@ export default abstract class NymphDriver {
|
|
|
403
461
|
escapeRegExp(testLikeValue)
|
|
404
462
|
.replace('%', () => '.*')
|
|
405
463
|
.replace('_', () => '.') +
|
|
406
|
-
'$'
|
|
464
|
+
'$',
|
|
465
|
+
).test(data[propName]),
|
|
466
|
+
xor(typeIsNot, clauseNot),
|
|
467
|
+
);
|
|
468
|
+
break;
|
|
469
|
+
case 'ilike':
|
|
470
|
+
case '!ilike':
|
|
471
|
+
const testILikeValue = (
|
|
472
|
+
curValue as [string, string]
|
|
473
|
+
)[1] as string;
|
|
474
|
+
pass = xor(
|
|
475
|
+
propName in data &&
|
|
476
|
+
new RegExp(
|
|
477
|
+
'^' +
|
|
478
|
+
escapeRegExp(testILikeValue)
|
|
479
|
+
.replace('%', () => '.*')
|
|
480
|
+
.replace('_', () => '.') +
|
|
481
|
+
'$',
|
|
482
|
+
'i',
|
|
407
483
|
).test(data[propName]),
|
|
408
|
-
xor(typeIsNot, clauseNot)
|
|
484
|
+
xor(typeIsNot, clauseNot),
|
|
409
485
|
);
|
|
410
486
|
break;
|
|
411
487
|
case 'match':
|
|
@@ -417,7 +493,7 @@ export default abstract class NymphDriver {
|
|
|
417
493
|
pass = xor(
|
|
418
494
|
propName in data &&
|
|
419
495
|
this.posixRegexMatch(testMatchValue, data[propName]),
|
|
420
|
-
xor(typeIsNot, clauseNot)
|
|
496
|
+
xor(typeIsNot, clauseNot),
|
|
421
497
|
);
|
|
422
498
|
break;
|
|
423
499
|
case 'imatch':
|
|
@@ -431,9 +507,28 @@ export default abstract class NymphDriver {
|
|
|
431
507
|
this.posixRegexMatch(
|
|
432
508
|
testIMatchValue,
|
|
433
509
|
data[propName],
|
|
434
|
-
true
|
|
510
|
+
true,
|
|
511
|
+
),
|
|
512
|
+
xor(typeIsNot, clauseNot),
|
|
513
|
+
);
|
|
514
|
+
break;
|
|
515
|
+
case 'search':
|
|
516
|
+
case '!search':
|
|
517
|
+
const testSearchValue = (
|
|
518
|
+
curValue as [string, string]
|
|
519
|
+
)[1] as string;
|
|
520
|
+
const tokenString =
|
|
521
|
+
propName in data
|
|
522
|
+
? (EntityClass.getFTSText(propName, data[propName]) ??
|
|
523
|
+
'')
|
|
524
|
+
: '';
|
|
525
|
+
pass = xor(
|
|
526
|
+
propName in data &&
|
|
527
|
+
this.tokenizer.searchString(
|
|
528
|
+
testSearchValue,
|
|
529
|
+
tokenString,
|
|
435
530
|
),
|
|
436
|
-
xor(typeIsNot, clauseNot)
|
|
531
|
+
xor(typeIsNot, clauseNot),
|
|
437
532
|
);
|
|
438
533
|
break;
|
|
439
534
|
case 'gt':
|
|
@@ -443,7 +538,7 @@ export default abstract class NymphDriver {
|
|
|
443
538
|
)[1] as number;
|
|
444
539
|
pass = xor(
|
|
445
540
|
propName in data && data[propName] > testGTValue,
|
|
446
|
-
xor(typeIsNot, clauseNot)
|
|
541
|
+
xor(typeIsNot, clauseNot),
|
|
447
542
|
);
|
|
448
543
|
break;
|
|
449
544
|
case 'gte':
|
|
@@ -453,7 +548,7 @@ export default abstract class NymphDriver {
|
|
|
453
548
|
)[1] as number;
|
|
454
549
|
pass = xor(
|
|
455
550
|
propName in data && data[propName] >= testGTEValue,
|
|
456
|
-
xor(typeIsNot, clauseNot)
|
|
551
|
+
xor(typeIsNot, clauseNot),
|
|
457
552
|
);
|
|
458
553
|
break;
|
|
459
554
|
case 'lt':
|
|
@@ -463,7 +558,7 @@ export default abstract class NymphDriver {
|
|
|
463
558
|
)[1] as number;
|
|
464
559
|
pass = xor(
|
|
465
560
|
propName in data && data[propName] < testLTValue,
|
|
466
|
-
xor(typeIsNot, clauseNot)
|
|
561
|
+
xor(typeIsNot, clauseNot),
|
|
467
562
|
);
|
|
468
563
|
break;
|
|
469
564
|
case 'lte':
|
|
@@ -473,26 +568,33 @@ export default abstract class NymphDriver {
|
|
|
473
568
|
)[1] as number;
|
|
474
569
|
pass = xor(
|
|
475
570
|
propName in data && data[propName] <= testLTEValue,
|
|
476
|
-
xor(typeIsNot, clauseNot)
|
|
571
|
+
xor(typeIsNot, clauseNot),
|
|
477
572
|
);
|
|
478
573
|
break;
|
|
479
574
|
}
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
575
|
+
|
|
576
|
+
if (!xor(typeIsOr, pass)) {
|
|
577
|
+
break;
|
|
578
|
+
}
|
|
483
579
|
}
|
|
484
580
|
}
|
|
485
581
|
}
|
|
582
|
+
if (!xor(typeIsOr, pass)) {
|
|
583
|
+
break;
|
|
584
|
+
}
|
|
486
585
|
}
|
|
487
|
-
if (!
|
|
488
|
-
|
|
586
|
+
if (!pass) {
|
|
587
|
+
return false;
|
|
489
588
|
}
|
|
490
589
|
}
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
590
|
+
return true;
|
|
591
|
+
} catch (e: any) {
|
|
592
|
+
this.nymph.config.debugError(
|
|
593
|
+
'nymph',
|
|
594
|
+
`Failed to check entity data: ${e}`,
|
|
595
|
+
);
|
|
596
|
+
throw e;
|
|
494
597
|
}
|
|
495
|
-
return true;
|
|
496
598
|
}
|
|
497
599
|
|
|
498
600
|
/**
|
|
@@ -531,7 +633,7 @@ export default abstract class NymphDriver {
|
|
|
531
633
|
| EntityInterface
|
|
532
634
|
| EntityReference
|
|
533
635
|
| string
|
|
534
|
-
| (EntityInterface | EntityReference | string)[]
|
|
636
|
+
| (EntityInterface | EntityReference | string)[],
|
|
535
637
|
) {
|
|
536
638
|
// Get the GUID, if the passed $entity is an object.
|
|
537
639
|
const guids: string[] = [];
|
|
@@ -562,7 +664,7 @@ export default abstract class NymphDriver {
|
|
|
562
664
|
return true;
|
|
563
665
|
}
|
|
564
666
|
// Search through arrays and objects looking for the reference.
|
|
565
|
-
if (Array.isArray(value) || typeof value === 'object') {
|
|
667
|
+
if (value != null && (Array.isArray(value) || typeof value === 'object')) {
|
|
566
668
|
for (const key in value) {
|
|
567
669
|
if (this.entityReferenceSearch(value[key], guids)) {
|
|
568
670
|
return true;
|
|
@@ -574,9 +676,13 @@ export default abstract class NymphDriver {
|
|
|
574
676
|
|
|
575
677
|
public formatSelectors(
|
|
576
678
|
selectors: Selector[],
|
|
577
|
-
options: Options = {}
|
|
578
|
-
):
|
|
679
|
+
options: Options = {},
|
|
680
|
+
): {
|
|
681
|
+
selectors: FormattedSelector[];
|
|
682
|
+
qrefs: [Options, ...FormattedSelector[]][];
|
|
683
|
+
} {
|
|
579
684
|
const newSelectors: FormattedSelector[] = [];
|
|
685
|
+
const qrefs: [Options, ...FormattedSelector[]][] = [];
|
|
580
686
|
|
|
581
687
|
for (const curSelector of selectors) {
|
|
582
688
|
const newSelector: FormattedSelector = {
|
|
@@ -605,23 +711,27 @@ export default abstract class NymphDriver {
|
|
|
605
711
|
for (let i = 0; i < tmpArr.length; i++) {
|
|
606
712
|
const name = tmpArr[i][0];
|
|
607
713
|
const [qrefOptions, ...qrefSelectors] = tmpArr[i][1];
|
|
608
|
-
const QrefEntityClass =
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
: qrefOptions.class ?? this.nymph.getEntityClass('Entity');
|
|
714
|
+
const QrefEntityClass = qrefOptions.class
|
|
715
|
+
? this.nymph.getEntityClass(qrefOptions.class)
|
|
716
|
+
: this.nymph.getEntityClass('Entity');
|
|
612
717
|
const newOptions = {
|
|
613
718
|
...qrefOptions,
|
|
614
719
|
class: QrefEntityClass,
|
|
615
720
|
source: options.source,
|
|
616
721
|
};
|
|
617
722
|
const newSelectors = this.formatSelectors(qrefSelectors, options);
|
|
618
|
-
|
|
619
|
-
|
|
723
|
+
qrefs.push(
|
|
724
|
+
[newOptions, ...newSelectors.selectors],
|
|
725
|
+
...newSelectors.qrefs,
|
|
726
|
+
);
|
|
727
|
+
formatArr[i] = [name, [newOptions, ...newSelectors.selectors]];
|
|
620
728
|
}
|
|
621
729
|
newSelector[key] = formatArr;
|
|
622
730
|
} else if (key === 'selector' || key === '!selector') {
|
|
623
731
|
const tmpArr = (Array.isArray(value) ? value : [value]) as Selector[];
|
|
624
|
-
|
|
732
|
+
const newSelectors = this.formatSelectors(tmpArr, options);
|
|
733
|
+
newSelector[key] = newSelectors.selectors;
|
|
734
|
+
qrefs.push(...newSelectors.qrefs);
|
|
625
735
|
} else if (!Array.isArray(value)) {
|
|
626
736
|
// @ts-ignore: ts doesn't know what value is here.
|
|
627
737
|
newSelector[key] = [[value]];
|
|
@@ -655,17 +765,17 @@ export default abstract class NymphDriver {
|
|
|
655
765
|
newSelectors.push(newSelector);
|
|
656
766
|
}
|
|
657
767
|
|
|
658
|
-
return newSelectors;
|
|
768
|
+
return { selectors: newSelectors, qrefs };
|
|
659
769
|
}
|
|
660
770
|
|
|
661
771
|
protected iterateSelectorsForQuery(
|
|
662
772
|
selectors: FormattedSelector[],
|
|
663
|
-
callback: (
|
|
664
|
-
key: string
|
|
665
|
-
value: any
|
|
666
|
-
typeIsOr: boolean
|
|
667
|
-
typeIsNot: boolean
|
|
668
|
-
) => string
|
|
773
|
+
callback: (data: {
|
|
774
|
+
key: string;
|
|
775
|
+
value: any;
|
|
776
|
+
typeIsOr: boolean;
|
|
777
|
+
typeIsNot: boolean;
|
|
778
|
+
}) => string,
|
|
669
779
|
) {
|
|
670
780
|
const queryParts = [];
|
|
671
781
|
for (const curSelector of selectors) {
|
|
@@ -678,7 +788,7 @@ export default abstract class NymphDriver {
|
|
|
678
788
|
if (key === 'type') {
|
|
679
789
|
continue;
|
|
680
790
|
}
|
|
681
|
-
let curQuery = callback(key, value, typeIsOr, typeIsNot);
|
|
791
|
+
let curQuery = callback({ key, value, typeIsOr, typeIsNot });
|
|
682
792
|
if (curQuery) {
|
|
683
793
|
if (curSelectorQuery) {
|
|
684
794
|
curSelectorQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
@@ -694,71 +804,14 @@ export default abstract class NymphDriver {
|
|
|
694
804
|
return queryParts;
|
|
695
805
|
}
|
|
696
806
|
|
|
697
|
-
|
|
698
|
-
* Get the first entity to match all options/selectors, synchronously.
|
|
699
|
-
*
|
|
700
|
-
* This should really only be used internally by the Entity class. It's a bad
|
|
701
|
-
* idea to get entities synchronously. The Entity class uses this to load
|
|
702
|
-
* sleeping references.
|
|
703
|
-
*
|
|
704
|
-
* @param options The options.
|
|
705
|
-
* @param selectors Unlimited optional selectors to search for, or a single GUID. If none are given, all entities are searched for the given options.
|
|
706
|
-
* @returns An entity, or null on failure or nothing found.
|
|
707
|
-
*/
|
|
708
|
-
public getEntitySync<T extends EntityConstructor = EntityConstructor>(
|
|
709
|
-
options: Options<T> & { return: 'count' },
|
|
710
|
-
...selectors: Selector[]
|
|
711
|
-
): number;
|
|
712
|
-
public getEntitySync<T extends EntityConstructor = EntityConstructor>(
|
|
713
|
-
options: Options<T> & { return: 'guid' },
|
|
714
|
-
...selectors: Selector[]
|
|
715
|
-
): string | null;
|
|
716
|
-
public getEntitySync<T extends EntityConstructor = EntityConstructor>(
|
|
717
|
-
options: Options<T>,
|
|
718
|
-
...selectors: Selector[]
|
|
719
|
-
): ReturnType<T['factorySync']> | null;
|
|
720
|
-
public getEntitySync<T extends EntityConstructor = EntityConstructor>(
|
|
721
|
-
options: Options<T> & { return: 'count' },
|
|
722
|
-
guid: string
|
|
723
|
-
): number;
|
|
724
|
-
public getEntitySync<T extends EntityConstructor = EntityConstructor>(
|
|
725
|
-
options: Options<T> & { return: 'guid' },
|
|
726
|
-
guid: string
|
|
727
|
-
): string | null;
|
|
728
|
-
public getEntitySync<T extends EntityConstructor = EntityConstructor>(
|
|
729
|
-
options: Options<T>,
|
|
730
|
-
guid: string
|
|
731
|
-
): ReturnType<T['factorySync']> | null;
|
|
732
|
-
public getEntitySync<T extends EntityConstructor = EntityConstructor>(
|
|
733
|
-
options: Options<T> = {},
|
|
734
|
-
...selectors: Selector[] | string[]
|
|
735
|
-
): ReturnType<T['factorySync']> | string | number | null {
|
|
736
|
-
// Set up options and selectors.
|
|
737
|
-
if (typeof selectors[0] === 'string') {
|
|
738
|
-
selectors = [{ type: '&', guid: selectors[0] }];
|
|
739
|
-
}
|
|
740
|
-
options.limit = 1;
|
|
741
|
-
const entities = this.getEntitiesSync(
|
|
742
|
-
options,
|
|
743
|
-
...(selectors as Selector[])
|
|
744
|
-
);
|
|
745
|
-
if (options.return === 'count') {
|
|
746
|
-
return entities as unknown as number;
|
|
747
|
-
}
|
|
748
|
-
if (!entities || !entities.length) {
|
|
749
|
-
return null;
|
|
750
|
-
}
|
|
751
|
-
return entities[0];
|
|
752
|
-
}
|
|
753
|
-
|
|
754
|
-
protected getEntitesRowLike<T extends EntityConstructor = EntityConstructor>(
|
|
807
|
+
protected getEntitiesRowLike<T extends EntityConstructor = EntityConstructor>(
|
|
755
808
|
options: Options<T> & { return: 'count' },
|
|
756
809
|
selectors: Selector[],
|
|
757
|
-
performQueryCallback: (
|
|
758
|
-
options: Options<T
|
|
759
|
-
|
|
760
|
-
etype: string
|
|
761
|
-
) => {
|
|
810
|
+
performQueryCallback: (data: {
|
|
811
|
+
options: Options<T>;
|
|
812
|
+
selectors: FormattedSelector[];
|
|
813
|
+
etype: string;
|
|
814
|
+
}) => {
|
|
762
815
|
result: any;
|
|
763
816
|
},
|
|
764
817
|
rowFetchCallback: () => any,
|
|
@@ -773,16 +826,16 @@ export default abstract class NymphDriver {
|
|
|
773
826
|
getDataNameAndSValueCallback: (row: any) => {
|
|
774
827
|
name: string;
|
|
775
828
|
svalue: string;
|
|
776
|
-
}
|
|
777
|
-
): { result: any; process: () => number };
|
|
778
|
-
protected
|
|
829
|
+
},
|
|
830
|
+
): { result: any; process: () => number | Error };
|
|
831
|
+
protected getEntitiesRowLike<T extends EntityConstructor = EntityConstructor>(
|
|
779
832
|
options: Options<T> & { return: 'guid' },
|
|
780
833
|
selectors: Selector[],
|
|
781
|
-
performQueryCallback: (
|
|
782
|
-
options: Options<T
|
|
783
|
-
|
|
784
|
-
etype: string
|
|
785
|
-
) => {
|
|
834
|
+
performQueryCallback: (data: {
|
|
835
|
+
options: Options<T>;
|
|
836
|
+
selectors: FormattedSelector[];
|
|
837
|
+
etype: string;
|
|
838
|
+
}) => {
|
|
786
839
|
result: any;
|
|
787
840
|
},
|
|
788
841
|
rowFetchCallback: () => any,
|
|
@@ -797,16 +850,40 @@ export default abstract class NymphDriver {
|
|
|
797
850
|
getDataNameAndSValueCallback: (row: any) => {
|
|
798
851
|
name: string;
|
|
799
852
|
svalue: string;
|
|
800
|
-
}
|
|
801
|
-
): { result: any; process: () => string[] };
|
|
802
|
-
protected
|
|
853
|
+
},
|
|
854
|
+
): { result: any; process: () => string[] | Error };
|
|
855
|
+
protected getEntitiesRowLike<T extends EntityConstructor = EntityConstructor>(
|
|
856
|
+
options: Options<T> & { return: 'object' },
|
|
857
|
+
selectors: Selector[],
|
|
858
|
+
performQueryCallback: (data: {
|
|
859
|
+
options: Options<T>;
|
|
860
|
+
selectors: FormattedSelector[];
|
|
861
|
+
etype: string;
|
|
862
|
+
}) => {
|
|
863
|
+
result: any;
|
|
864
|
+
},
|
|
865
|
+
rowFetchCallback: () => any,
|
|
866
|
+
freeResultCallback: () => void,
|
|
867
|
+
getCountCallback: (row: any) => number,
|
|
868
|
+
getGUIDCallback: (row: any) => string,
|
|
869
|
+
getTagsAndDatesCallback: (row: any) => {
|
|
870
|
+
tags: string[];
|
|
871
|
+
cdate: number;
|
|
872
|
+
mdate: number;
|
|
873
|
+
},
|
|
874
|
+
getDataNameAndSValueCallback: (row: any) => {
|
|
875
|
+
name: string;
|
|
876
|
+
svalue: string;
|
|
877
|
+
},
|
|
878
|
+
): { result: any; process: () => EntityObjectType<T>[] | Error };
|
|
879
|
+
protected getEntitiesRowLike<T extends EntityConstructor = EntityConstructor>(
|
|
803
880
|
options: Options<T>,
|
|
804
881
|
selectors: Selector[],
|
|
805
|
-
performQueryCallback: (
|
|
806
|
-
options: Options<T
|
|
807
|
-
|
|
808
|
-
etype: string
|
|
809
|
-
) => {
|
|
882
|
+
performQueryCallback: (data: {
|
|
883
|
+
options: Options<T>;
|
|
884
|
+
selectors: FormattedSelector[];
|
|
885
|
+
etype: string;
|
|
886
|
+
}) => {
|
|
810
887
|
result: any;
|
|
811
888
|
},
|
|
812
889
|
rowFetchCallback: () => any,
|
|
@@ -821,16 +898,19 @@ export default abstract class NymphDriver {
|
|
|
821
898
|
getDataNameAndSValueCallback: (row: any) => {
|
|
822
899
|
name: string;
|
|
823
900
|
svalue: string;
|
|
824
|
-
}
|
|
825
|
-
): {
|
|
826
|
-
|
|
901
|
+
},
|
|
902
|
+
): {
|
|
903
|
+
result: any;
|
|
904
|
+
process: () => EntityInstanceType<T>[] | Error;
|
|
905
|
+
};
|
|
906
|
+
protected getEntitiesRowLike<T extends EntityConstructor = EntityConstructor>(
|
|
827
907
|
options: Options<T>,
|
|
828
908
|
selectors: Selector[],
|
|
829
|
-
performQueryCallback: (
|
|
830
|
-
options: Options<T
|
|
831
|
-
|
|
832
|
-
etype: string
|
|
833
|
-
) => {
|
|
909
|
+
performQueryCallback: (data: {
|
|
910
|
+
options: Options<T>;
|
|
911
|
+
selectors: FormattedSelector[];
|
|
912
|
+
etype: string;
|
|
913
|
+
}) => {
|
|
834
914
|
result: any;
|
|
835
915
|
},
|
|
836
916
|
rowFetchCallback: () => any,
|
|
@@ -845,10 +925,15 @@ export default abstract class NymphDriver {
|
|
|
845
925
|
getDataNameAndSValueCallback: (row: any) => {
|
|
846
926
|
name: string;
|
|
847
927
|
svalue: string;
|
|
848
|
-
}
|
|
928
|
+
},
|
|
849
929
|
): {
|
|
850
930
|
result: any;
|
|
851
|
-
process: () =>
|
|
931
|
+
process: () =>
|
|
932
|
+
| EntityInstanceType<T>[]
|
|
933
|
+
| EntityObjectType<T>[]
|
|
934
|
+
| string[]
|
|
935
|
+
| number
|
|
936
|
+
| Error;
|
|
852
937
|
} {
|
|
853
938
|
if (!this.isConnected()) {
|
|
854
939
|
throw new UnableToConnectError('not connected to DB');
|
|
@@ -861,21 +946,29 @@ export default abstract class NymphDriver {
|
|
|
861
946
|
['&', '!&', '|', '!|'].indexOf(selector.type) === -1
|
|
862
947
|
) {
|
|
863
948
|
throw new InvalidParametersError(
|
|
864
|
-
'Invalid query selector passed: ' + JSON.stringify(selector)
|
|
949
|
+
'Invalid query selector passed: ' + JSON.stringify(selector),
|
|
865
950
|
);
|
|
866
951
|
}
|
|
867
952
|
}
|
|
868
953
|
|
|
869
|
-
let entities:
|
|
954
|
+
let entities: EntityInstanceType<T>[] | EntityObjectType<T>[] | string[] =
|
|
955
|
+
[];
|
|
870
956
|
let count = 0;
|
|
871
957
|
const EntityClass =
|
|
872
958
|
options.class ?? (this.nymph.getEntityClass('Entity') as T);
|
|
873
959
|
const etype = EntityClass.ETYPE;
|
|
874
960
|
|
|
961
|
+
if (options.source === 'client' && options.return === 'object') {
|
|
962
|
+
throw new InvalidParametersError(
|
|
963
|
+
'Object return type not allowed from client.',
|
|
964
|
+
);
|
|
965
|
+
}
|
|
966
|
+
|
|
875
967
|
// Check if the requested entity is cached.
|
|
876
968
|
if (
|
|
877
969
|
this.nymph.config.cache &&
|
|
878
970
|
!options.skipCache &&
|
|
971
|
+
selectors.length &&
|
|
879
972
|
'guid' in selectors[0] &&
|
|
880
973
|
typeof selectors[0].guid === 'number'
|
|
881
974
|
) {
|
|
@@ -887,195 +980,297 @@ export default abstract class NymphDriver {
|
|
|
887
980
|
(Object.keys(selectors[0]).length === 2 ||
|
|
888
981
|
(Object.keys(selectors[0]).length === 3 && 'tag' in selectors[0]))
|
|
889
982
|
) {
|
|
890
|
-
const
|
|
891
|
-
|
|
892
|
-
EntityClass.class,
|
|
893
|
-
!!options.skipAc
|
|
894
|
-
);
|
|
983
|
+
const cacheEntity = this.pullCache(selectors[0]['guid']);
|
|
984
|
+
|
|
895
985
|
if (
|
|
896
|
-
|
|
897
|
-
|
|
986
|
+
cacheEntity != null &&
|
|
987
|
+
cacheEntity.guid != null &&
|
|
898
988
|
(!('tag' in selectors[0]) ||
|
|
899
|
-
|
|
900
|
-
|
|
989
|
+
!(
|
|
990
|
+
Array.isArray(selectors[0].tag)
|
|
901
991
|
? selectors[0].tag
|
|
902
|
-
: [selectors[0].tag]
|
|
903
|
-
))
|
|
992
|
+
: [selectors[0].tag]
|
|
993
|
+
).find((tag) => tag == null || !cacheEntity.tags.includes(tag)))
|
|
904
994
|
) {
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
995
|
+
// Return the value from the cache.
|
|
996
|
+
const { guid, cdate, mdate, tags, data, sdata } = cacheEntity;
|
|
997
|
+
|
|
998
|
+
if (options.return === 'count') {
|
|
999
|
+
return {
|
|
1000
|
+
result: Promise.resolve(null),
|
|
1001
|
+
process: () => 1,
|
|
1002
|
+
};
|
|
1003
|
+
} else if (options.return === 'guid') {
|
|
1004
|
+
return {
|
|
1005
|
+
result: Promise.resolve(null),
|
|
1006
|
+
process: () => [guid],
|
|
1007
|
+
};
|
|
1008
|
+
} else if (options.return === 'object') {
|
|
1009
|
+
return {
|
|
1010
|
+
result: Promise.resolve(null),
|
|
1011
|
+
process: () => [
|
|
1012
|
+
{
|
|
1013
|
+
guid,
|
|
1014
|
+
cdate,
|
|
1015
|
+
mdate,
|
|
1016
|
+
tags,
|
|
1017
|
+
...data,
|
|
1018
|
+
...Object.fromEntries(
|
|
1019
|
+
Object.entries(sdata).map(([name, value]) => [
|
|
1020
|
+
name,
|
|
1021
|
+
JSON.parse(value),
|
|
1022
|
+
]),
|
|
1023
|
+
),
|
|
1024
|
+
} as EntityObjectType<T>,
|
|
1025
|
+
],
|
|
1026
|
+
};
|
|
1027
|
+
} else {
|
|
1028
|
+
const entity = this.nymph
|
|
1029
|
+
.getEntityClass(EntityClass.class)
|
|
1030
|
+
.factorySync() as EntityInstanceType<T>;
|
|
1031
|
+
if (entity) {
|
|
1032
|
+
entity.$nymph = this.nymph;
|
|
1033
|
+
if (options.skipAc != null) {
|
|
1034
|
+
entity.$useSkipAc(!!options.skipAc);
|
|
1035
|
+
}
|
|
1036
|
+
entity.guid = guid;
|
|
1037
|
+
entity.cdate = cdate;
|
|
1038
|
+
entity.mdate = mdate;
|
|
1039
|
+
entity.tags = tags;
|
|
1040
|
+
entity.$putData(data, sdata, 'server');
|
|
1041
|
+
return {
|
|
1042
|
+
result: Promise.resolve(null),
|
|
1043
|
+
process: () => [entity],
|
|
1044
|
+
};
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
return {
|
|
1048
|
+
result: Promise.resolve(null),
|
|
1049
|
+
process: () => [],
|
|
1050
|
+
};
|
|
1051
|
+
}
|
|
917
1052
|
}
|
|
918
1053
|
}
|
|
919
1054
|
}
|
|
920
1055
|
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
1056
|
+
try {
|
|
1057
|
+
const formattedSelectors = this.formatSelectors(selectors, options);
|
|
1058
|
+
this.nymph.runQueryCallbacks(options, formattedSelectors.selectors);
|
|
1059
|
+
for (let i = 0; i < formattedSelectors.qrefs.length; i++) {
|
|
1060
|
+
const [options, ...selectors] = formattedSelectors.qrefs[i];
|
|
1061
|
+
this.nymph.runQueryCallbacks(options, selectors);
|
|
1062
|
+
formattedSelectors.qrefs[i] = [options, ...selectors];
|
|
1063
|
+
}
|
|
1064
|
+
const { result } = performQueryCallback({
|
|
1065
|
+
options,
|
|
1066
|
+
selectors: formattedSelectors.selectors,
|
|
1067
|
+
etype,
|
|
1068
|
+
});
|
|
924
1069
|
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
row = rowFetchCallback();
|
|
933
|
-
}
|
|
934
|
-
} else if (options.return === 'guid') {
|
|
935
|
-
while (row != null) {
|
|
936
|
-
(entities as string[]).push(getGUIDCallback(row));
|
|
937
|
-
row = rowFetchCallback();
|
|
938
|
-
}
|
|
939
|
-
} else {
|
|
940
|
-
while (row != null) {
|
|
941
|
-
const guid = getGUIDCallback(row);
|
|
942
|
-
const tagsAndDates = getTagsAndDatesCallback(row);
|
|
943
|
-
const tags = tagsAndDates.tags;
|
|
944
|
-
const cdate = tagsAndDates.cdate;
|
|
945
|
-
const mdate = tagsAndDates.mdate;
|
|
946
|
-
let dataNameAndSValue = getDataNameAndSValueCallback(row);
|
|
947
|
-
// Data.
|
|
948
|
-
const data: EntityData = {};
|
|
949
|
-
// Serialized data.
|
|
950
|
-
const sdata: SerializedEntityData = {};
|
|
951
|
-
if (dataNameAndSValue.name !== '') {
|
|
952
|
-
// This do will keep going and adding the data until the
|
|
953
|
-
// next entity is reached. $row will end on the next entity.
|
|
954
|
-
do {
|
|
955
|
-
dataNameAndSValue = getDataNameAndSValueCallback(row);
|
|
956
|
-
sdata[dataNameAndSValue.name] = dataNameAndSValue.svalue;
|
|
957
|
-
row = rowFetchCallback();
|
|
958
|
-
} while (row != null && getGUIDCallback(row) === guid);
|
|
959
|
-
} else {
|
|
960
|
-
// Make sure that $row is incremented :)
|
|
1070
|
+
return {
|
|
1071
|
+
result,
|
|
1072
|
+
process: () => {
|
|
1073
|
+
let row = rowFetchCallback();
|
|
1074
|
+
if (options.return === 'count') {
|
|
1075
|
+
while (row != null) {
|
|
1076
|
+
count += getCountCallback(row);
|
|
961
1077
|
row = rowFetchCallback();
|
|
962
1078
|
}
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
if (options.skipAc != null) {
|
|
968
|
-
entity.$useSkipAc(!!options.skipAc);
|
|
969
|
-
}
|
|
970
|
-
entity.guid = guid;
|
|
971
|
-
entity.cdate = cdate;
|
|
972
|
-
entity.mdate = mdate;
|
|
973
|
-
entity.tags = tags;
|
|
974
|
-
this.putDataCounter++;
|
|
975
|
-
if (this.putDataCounter == 100) {
|
|
976
|
-
throw new Error('Infinite loop detected in Entity loading.');
|
|
1079
|
+
} else if (options.return === 'guid') {
|
|
1080
|
+
while (row != null) {
|
|
1081
|
+
(entities as string[]).push(getGUIDCallback(row));
|
|
1082
|
+
row = rowFetchCallback();
|
|
977
1083
|
}
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
1084
|
+
} else {
|
|
1085
|
+
while (row != null) {
|
|
1086
|
+
const guid = getGUIDCallback(row);
|
|
1087
|
+
const tagsAndDates = getTagsAndDatesCallback(row);
|
|
1088
|
+
const tags = tagsAndDates.tags;
|
|
1089
|
+
const cdate = tagsAndDates.cdate;
|
|
1090
|
+
const mdate = tagsAndDates.mdate;
|
|
1091
|
+
let dataNameAndSValue = getDataNameAndSValueCallback(row);
|
|
1092
|
+
// Data.
|
|
1093
|
+
const data: EntityData = {};
|
|
1094
|
+
// Serialized data.
|
|
1095
|
+
const sdata: SerializedEntityData = {};
|
|
1096
|
+
if (dataNameAndSValue.name !== '') {
|
|
1097
|
+
// This do will keep going and adding the data until the
|
|
1098
|
+
// next entity is reached. $row will end on the next entity.
|
|
1099
|
+
do {
|
|
1100
|
+
dataNameAndSValue = getDataNameAndSValueCallback(row);
|
|
1101
|
+
sdata[dataNameAndSValue.name] = dataNameAndSValue.svalue;
|
|
1102
|
+
row = rowFetchCallback();
|
|
1103
|
+
} while (row != null && getGUIDCallback(row) === guid);
|
|
1104
|
+
} else {
|
|
1105
|
+
// Make sure that $row is incremented :)
|
|
1106
|
+
row = rowFetchCallback();
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
if (options.return === 'object') {
|
|
1110
|
+
(entities as EntityObjectType<T>[]).push({
|
|
1111
|
+
guid,
|
|
1112
|
+
cdate,
|
|
1113
|
+
mdate,
|
|
1114
|
+
tags,
|
|
1115
|
+
...data,
|
|
1116
|
+
...Object.fromEntries(
|
|
1117
|
+
Object.entries(sdata).map(([name, value]) => [
|
|
1118
|
+
name,
|
|
1119
|
+
JSON.parse(value),
|
|
1120
|
+
]),
|
|
1121
|
+
),
|
|
1122
|
+
} as EntityObjectType<T>);
|
|
1123
|
+
} else {
|
|
1124
|
+
const entity =
|
|
1125
|
+
EntityClass.factorySync() as EntityInstanceType<T>;
|
|
1126
|
+
entity.$nymph = this.nymph;
|
|
1127
|
+
if (options.skipAc != null) {
|
|
1128
|
+
entity.$useSkipAc(!!options.skipAc);
|
|
1129
|
+
}
|
|
1130
|
+
entity.guid = guid;
|
|
1131
|
+
entity.cdate = cdate;
|
|
1132
|
+
entity.mdate = mdate;
|
|
1133
|
+
entity.tags = tags;
|
|
1134
|
+
this.putDataCounter++;
|
|
1135
|
+
if (this.putDataCounter == 100) {
|
|
1136
|
+
throw new Error('Infinite loop detected in Entity loading.');
|
|
1137
|
+
}
|
|
1138
|
+
entity.$putData(data, sdata, 'server');
|
|
1139
|
+
this.putDataCounter--;
|
|
1140
|
+
entities.push(entity);
|
|
1141
|
+
}
|
|
1142
|
+
if (this.nymph.config.cache) {
|
|
1143
|
+
this.pushCache(guid, cdate, mdate, tags, data, sdata);
|
|
1144
|
+
}
|
|
982
1145
|
}
|
|
983
|
-
// @ts-ignore: ts doesn't know the return type here.
|
|
984
|
-
entities.push(entity);
|
|
985
1146
|
}
|
|
986
|
-
}
|
|
987
1147
|
|
|
988
|
-
|
|
1148
|
+
freeResultCallback();
|
|
989
1149
|
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
1150
|
+
return options.return === 'count' ? count : entities;
|
|
1151
|
+
},
|
|
1152
|
+
};
|
|
1153
|
+
} catch (e: any) {
|
|
1154
|
+
return {
|
|
1155
|
+
result: Promise.resolve(e),
|
|
1156
|
+
process: () => {
|
|
1157
|
+
return e;
|
|
1158
|
+
},
|
|
1159
|
+
};
|
|
1160
|
+
}
|
|
993
1161
|
}
|
|
994
1162
|
|
|
995
1163
|
protected async saveEntityRowLike(
|
|
996
1164
|
entity: EntityInterface,
|
|
997
|
-
saveNewEntityCallback: (
|
|
998
|
-
entity: EntityInterface
|
|
999
|
-
guid: string
|
|
1000
|
-
tags: string[]
|
|
1001
|
-
data: EntityData
|
|
1002
|
-
sdata: SerializedEntityData
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1165
|
+
saveNewEntityCallback: (data: {
|
|
1166
|
+
entity: EntityInterface;
|
|
1167
|
+
guid: string;
|
|
1168
|
+
tags: string[];
|
|
1169
|
+
data: EntityData;
|
|
1170
|
+
sdata: SerializedEntityData;
|
|
1171
|
+
uniques: string[];
|
|
1172
|
+
cdate: number;
|
|
1173
|
+
etype: string;
|
|
1174
|
+
}) => Promise<boolean>,
|
|
1175
|
+
saveExistingEntityCallback: (data: {
|
|
1176
|
+
entity: EntityInterface;
|
|
1177
|
+
guid: string;
|
|
1178
|
+
tags: string[];
|
|
1179
|
+
data: EntityData;
|
|
1180
|
+
sdata: SerializedEntityData;
|
|
1181
|
+
uniques: string[];
|
|
1182
|
+
mdate: number;
|
|
1183
|
+
etype: string;
|
|
1184
|
+
}) => Promise<boolean>,
|
|
1015
1185
|
startTransactionCallback: (() => Promise<void>) | null = null,
|
|
1016
1186
|
commitTransactionCallback:
|
|
1017
1187
|
| ((success: boolean) => Promise<boolean>)
|
|
1018
|
-
| null = null
|
|
1188
|
+
| null = null,
|
|
1019
1189
|
) {
|
|
1190
|
+
const originalGuid = entity.guid;
|
|
1191
|
+
const originalCdate = entity.cdate;
|
|
1192
|
+
const originalMdate = entity.mdate;
|
|
1020
1193
|
// Get a modified date.
|
|
1021
|
-
|
|
1194
|
+
let mdate = Date.now();
|
|
1195
|
+
if (entity.mdate != null) {
|
|
1196
|
+
if (this.nymph.config.updateMDate === false) {
|
|
1197
|
+
mdate = entity.mdate;
|
|
1198
|
+
} else if (typeof this.nymph.config.updateMDate === 'number') {
|
|
1199
|
+
mdate = entity.mdate + this.nymph.config.updateMDate;
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1022
1202
|
const tags = difference(entity.tags, ['']);
|
|
1023
1203
|
const data = entity.$getData();
|
|
1024
1204
|
const sdata = entity.$getSData();
|
|
1205
|
+
const uniques = await entity.$getUniques();
|
|
1025
1206
|
const EntityClass = entity.constructor as EntityConstructor;
|
|
1026
1207
|
const etype = EntityClass.ETYPE;
|
|
1027
1208
|
if (startTransactionCallback) {
|
|
1028
1209
|
await startTransactionCallback();
|
|
1029
1210
|
}
|
|
1030
1211
|
let success = false;
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1212
|
+
try {
|
|
1213
|
+
if (entity.guid == null) {
|
|
1214
|
+
const cdate = mdate;
|
|
1215
|
+
const newId = entity.$getGuaranteedGUID();
|
|
1216
|
+
success = await saveNewEntityCallback({
|
|
1217
|
+
entity,
|
|
1218
|
+
guid: newId,
|
|
1219
|
+
tags,
|
|
1220
|
+
data,
|
|
1221
|
+
sdata,
|
|
1222
|
+
uniques,
|
|
1223
|
+
cdate,
|
|
1224
|
+
etype,
|
|
1225
|
+
});
|
|
1226
|
+
if (success) {
|
|
1227
|
+
entity.guid = newId;
|
|
1228
|
+
entity.cdate = cdate;
|
|
1229
|
+
entity.mdate = mdate;
|
|
1230
|
+
}
|
|
1231
|
+
} else {
|
|
1232
|
+
// Removed any cached versions of this entity.
|
|
1233
|
+
if (this.nymph.config.cache) {
|
|
1234
|
+
this.cleanCache(entity.guid);
|
|
1235
|
+
}
|
|
1236
|
+
success = await saveExistingEntityCallback({
|
|
1237
|
+
entity,
|
|
1238
|
+
guid: entity.guid,
|
|
1239
|
+
tags,
|
|
1240
|
+
data,
|
|
1241
|
+
sdata,
|
|
1242
|
+
uniques,
|
|
1243
|
+
mdate,
|
|
1244
|
+
etype,
|
|
1245
|
+
});
|
|
1246
|
+
if (success) {
|
|
1247
|
+
entity.mdate = mdate;
|
|
1248
|
+
}
|
|
1047
1249
|
}
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
if (this.nymph.config.cache) {
|
|
1051
|
-
this.cleanCache(entity.guid);
|
|
1250
|
+
if (commitTransactionCallback) {
|
|
1251
|
+
success = await commitTransactionCallback(success);
|
|
1052
1252
|
}
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
entity.mdate = mdate;
|
|
1253
|
+
// Cache the entity.
|
|
1254
|
+
if (success && this.nymph.config.cache) {
|
|
1255
|
+
this.pushCache(
|
|
1256
|
+
entity.guid as string,
|
|
1257
|
+
entity.cdate as number,
|
|
1258
|
+
entity.mdate as number,
|
|
1259
|
+
entity.tags,
|
|
1260
|
+
data,
|
|
1261
|
+
sdata,
|
|
1262
|
+
);
|
|
1064
1263
|
}
|
|
1264
|
+
} catch (e: any) {
|
|
1265
|
+
entity.guid = originalGuid;
|
|
1266
|
+
entity.cdate = originalCdate;
|
|
1267
|
+
entity.mdate = originalMdate;
|
|
1268
|
+
throw e;
|
|
1065
1269
|
}
|
|
1066
|
-
if (
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
if (success && this.nymph.config.cache) {
|
|
1071
|
-
this.pushCache(
|
|
1072
|
-
entity.guid as string,
|
|
1073
|
-
entity.cdate as number,
|
|
1074
|
-
entity.mdate as number,
|
|
1075
|
-
entity.tags,
|
|
1076
|
-
data,
|
|
1077
|
-
sdata
|
|
1078
|
-
);
|
|
1270
|
+
if (!success) {
|
|
1271
|
+
entity.guid = originalGuid;
|
|
1272
|
+
entity.cdate = originalCdate;
|
|
1273
|
+
entity.mdate = originalMdate;
|
|
1079
1274
|
}
|
|
1080
1275
|
return success;
|
|
1081
1276
|
}
|
|
@@ -1084,35 +1279,30 @@ export default abstract class NymphDriver {
|
|
|
1084
1279
|
* Pull an entity from the cache.
|
|
1085
1280
|
*
|
|
1086
1281
|
* @param guid The entity's GUID.
|
|
1087
|
-
* @
|
|
1088
|
-
* @param useSkipAc Whether to tell the entity to use skip_ac.
|
|
1089
|
-
* @returns The entity or null if it's not cached.
|
|
1282
|
+
* @returns The entity's data or null if it's not cached.
|
|
1090
1283
|
*/
|
|
1091
|
-
protected pullCache
|
|
1092
|
-
guid: string
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1284
|
+
protected pullCache(guid: string): {
|
|
1285
|
+
guid: string;
|
|
1286
|
+
cdate: number;
|
|
1287
|
+
mdate: number;
|
|
1288
|
+
tags: string[];
|
|
1289
|
+
data: EntityData;
|
|
1290
|
+
sdata: SerializedEntityData;
|
|
1291
|
+
} | null {
|
|
1096
1292
|
// Increment the entity access count.
|
|
1097
1293
|
if (!(guid in this.entityCount)) {
|
|
1098
1294
|
this.entityCount[guid] = 0;
|
|
1099
1295
|
}
|
|
1100
1296
|
this.entityCount[guid]++;
|
|
1101
1297
|
if (guid in this.entityCache) {
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
entity.$putData(
|
|
1111
|
-
this.entityCache[guid]['data'],
|
|
1112
|
-
this.entityCache[guid]['sdata']
|
|
1113
|
-
);
|
|
1114
|
-
return entity;
|
|
1115
|
-
}
|
|
1298
|
+
return {
|
|
1299
|
+
guid,
|
|
1300
|
+
cdate: this.entityCache[guid]['cdate'],
|
|
1301
|
+
mdate: this.entityCache[guid]['mdate'],
|
|
1302
|
+
tags: this.entityCache[guid]['tags'],
|
|
1303
|
+
data: this.entityCache[guid]['data'],
|
|
1304
|
+
sdata: this.entityCache[guid]['sdata'],
|
|
1305
|
+
};
|
|
1116
1306
|
}
|
|
1117
1307
|
return null;
|
|
1118
1308
|
}
|
|
@@ -1133,7 +1323,7 @@ export default abstract class NymphDriver {
|
|
|
1133
1323
|
mdate: number,
|
|
1134
1324
|
tags: string[],
|
|
1135
1325
|
data: EntityData,
|
|
1136
|
-
sdata: SerializedEntityData
|
|
1326
|
+
sdata: SerializedEntityData,
|
|
1137
1327
|
) {
|
|
1138
1328
|
if (guid == null) {
|
|
1139
1329
|
return;
|
|
@@ -1149,11 +1339,11 @@ export default abstract class NymphDriver {
|
|
|
1149
1339
|
}
|
|
1150
1340
|
// Cache the entity.
|
|
1151
1341
|
this.entityCache[guid] = {
|
|
1152
|
-
cdate
|
|
1153
|
-
mdate
|
|
1154
|
-
tags
|
|
1155
|
-
data
|
|
1156
|
-
sdata
|
|
1342
|
+
cdate,
|
|
1343
|
+
mdate,
|
|
1344
|
+
tags,
|
|
1345
|
+
data,
|
|
1346
|
+
sdata,
|
|
1157
1347
|
};
|
|
1158
1348
|
while (
|
|
1159
1349
|
this.nymph.config.cacheLimit &&
|
|
@@ -1162,7 +1352,7 @@ export default abstract class NymphDriver {
|
|
|
1162
1352
|
// Find which entity has been accessed the least.
|
|
1163
1353
|
const least =
|
|
1164
1354
|
Object.entries(this.entityCount)
|
|
1165
|
-
.sort(([
|
|
1355
|
+
.sort(([_guidA, countA], [_guidB, countB]) => countA - countB)
|
|
1166
1356
|
.pop()?.[0] ?? null;
|
|
1167
1357
|
if (least == null) {
|
|
1168
1358
|
// This should never happen.
|