@nymphjs/nymph 1.0.0-beta.6 → 1.0.0-beta.60
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 +273 -0
- package/README.md +13 -21
- package/dist/Entity.d.ts +269 -7
- package/dist/Entity.js +431 -89
- package/dist/Entity.js.map +1 -1
- package/dist/Entity.test.js +38 -16
- package/dist/Entity.test.js.map +1 -1
- package/dist/Entity.types.d.ts +225 -19
- package/dist/Nymph.d.ts +423 -11
- package/dist/Nymph.js +649 -156
- package/dist/Nymph.js.map +1 -1
- package/dist/Nymph.types.d.ts +31 -7
- package/dist/Nymph.types.js +2 -1
- package/dist/Nymph.types.js.map +1 -1
- package/dist/conf/d.d.ts +26 -0
- package/dist/conf/defaults.js +22 -0
- package/dist/conf/defaults.js.map +1 -1
- package/dist/driver/NymphDriver.d.ts +111 -36
- package/dist/driver/NymphDriver.js +169 -68
- package/dist/driver/NymphDriver.js.map +1 -1
- package/dist/driver/testArtifacts.d.ts +1 -1
- package/dist/driver/testArtifacts.js +285 -18
- package/dist/driver/testArtifacts.js.map +1 -1
- package/dist/errors/EntityInvalidDataError.d.ts +6 -0
- package/dist/errors/EntityInvalidDataError.js +6 -0
- package/dist/errors/EntityInvalidDataError.js.map +1 -1
- package/dist/errors/EntityUniqueConstraintError.d.ts +3 -0
- package/dist/errors/EntityUniqueConstraintError.js +11 -0
- package/dist/errors/EntityUniqueConstraintError.js.map +1 -0
- package/dist/errors/InvalidStateError.d.ts +3 -0
- package/dist/errors/InvalidStateError.js +11 -0
- package/dist/errors/InvalidStateError.js.map +1 -0
- package/dist/errors/QueryFailedError.js.map +1 -1
- package/dist/errors/index.d.ts +2 -0
- package/dist/errors/index.js +2 -0
- package/dist/errors/index.js.map +1 -1
- package/dist/testArtifacts.d.ts +17 -5
- package/dist/testArtifacts.js +23 -19
- package/dist/testArtifacts.js.map +1 -1
- package/dist/testMocks.d.ts +15 -10
- package/dist/testMocks.js +34 -11
- package/dist/testMocks.js.map +1 -1
- package/dist/utils.d.ts +1 -1
- package/dist/utils.js +21 -5
- package/dist/utils.js.map +1 -1
- package/package.json +16 -14
- package/src/Entity.test.ts +30 -10
- package/src/Entity.ts +406 -135
- package/src/Entity.types.ts +32 -66
- package/src/Nymph.ts +626 -196
- package/src/Nymph.types.ts +97 -26
- package/src/conf/d.ts +8 -0
- package/src/conf/defaults.ts +24 -0
- package/src/driver/NymphDriver.ts +187 -243
- package/src/driver/testArtifacts.ts +327 -146
- package/src/errors/EntityUniqueConstraintError.ts +6 -0
- package/src/errors/InvalidStateError.ts +6 -0
- package/src/errors/index.ts +4 -0
- package/src/nymphJoiProps.ts +2 -2
- package/src/testArtifacts.ts +18 -19
- package/src/testMocks.ts +64 -32
- package/src/utils.ts +22 -8
- package/tsconfig.json +1 -1
- package/typedoc.json +4 -0
|
@@ -5,7 +5,9 @@ import strtotime from 'locutus/php/datetime/strtotime';
|
|
|
5
5
|
import { guid } from '@nymphjs/guid';
|
|
6
6
|
|
|
7
7
|
import type Nymph from '../Nymph';
|
|
8
|
-
import {
|
|
8
|
+
import type { Selector, Options, FormattedSelector } from '../Nymph.types';
|
|
9
|
+
import Entity, { type EntityInstanceType } from '../Entity';
|
|
10
|
+
import type {
|
|
9
11
|
EntityConstructor,
|
|
10
12
|
EntityData,
|
|
11
13
|
EntityInterface,
|
|
@@ -13,7 +15,6 @@ import {
|
|
|
13
15
|
SerializedEntityData,
|
|
14
16
|
} from '../Entity.types';
|
|
15
17
|
import { InvalidParametersError, UnableToConnectError } from '../errors';
|
|
16
|
-
import { Selector, Options, FormattedSelector } from '../Nymph.types';
|
|
17
18
|
import { xor } from '../utils';
|
|
18
19
|
|
|
19
20
|
// from: https://stackoverflow.com/a/6969486/664915
|
|
@@ -28,7 +29,7 @@ export default abstract class NymphDriver {
|
|
|
28
29
|
protected nymph: Nymph = new Proxy({} as Nymph, {
|
|
29
30
|
get() {
|
|
30
31
|
throw new Error(
|
|
31
|
-
'Attempted access of Nymph instance before driver initialization!'
|
|
32
|
+
'Attempted access of Nymph instance before driver initialization!',
|
|
32
33
|
);
|
|
33
34
|
},
|
|
34
35
|
});
|
|
@@ -54,7 +55,18 @@ export default abstract class NymphDriver {
|
|
|
54
55
|
*/
|
|
55
56
|
private putDataCounter = 0;
|
|
56
57
|
|
|
58
|
+
/**
|
|
59
|
+
* This is used internally by Nymph. Don't call it yourself.
|
|
60
|
+
*
|
|
61
|
+
* @returns A clone of this instance.
|
|
62
|
+
*/
|
|
63
|
+
abstract clone(): NymphDriver;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Connect to the data store.
|
|
67
|
+
*/
|
|
57
68
|
abstract connect(): Promise<boolean>;
|
|
69
|
+
|
|
58
70
|
abstract isConnected(): boolean;
|
|
59
71
|
abstract startTransaction(name: string): Promise<Nymph>;
|
|
60
72
|
abstract commit(name: string): Promise<boolean>;
|
|
@@ -62,13 +74,18 @@ export default abstract class NymphDriver {
|
|
|
62
74
|
abstract inTransaction(): Promise<boolean>;
|
|
63
75
|
abstract deleteEntityByID(
|
|
64
76
|
guid: string,
|
|
65
|
-
classConstructor: EntityConstructor
|
|
77
|
+
classConstructor: EntityConstructor,
|
|
66
78
|
): Promise<boolean>;
|
|
67
79
|
abstract deleteEntityByID(guid: string, className?: string): Promise<boolean>;
|
|
68
80
|
abstract deleteUID(name: string): Promise<boolean>;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Disconnect from the data store.
|
|
84
|
+
*/
|
|
69
85
|
abstract disconnect(): Promise<boolean>;
|
|
86
|
+
|
|
70
87
|
protected abstract exportEntities(
|
|
71
|
-
writeLine: (line: string) => void
|
|
88
|
+
writeLine: (line: string) => void,
|
|
72
89
|
): Promise<void>;
|
|
73
90
|
abstract getEntities<T extends EntityConstructor = EntityConstructor>(
|
|
74
91
|
options: Options<T> & { return: 'count' },
|
|
@@ -81,37 +98,13 @@ export default abstract class NymphDriver {
|
|
|
81
98
|
abstract getEntities<T extends EntityConstructor = EntityConstructor>(
|
|
82
99
|
options?: Options<T>,
|
|
83
100
|
...selectors: Selector[]
|
|
84
|
-
): Promise<
|
|
101
|
+
): Promise<EntityInstanceType<T>[]>;
|
|
85
102
|
abstract getEntities<T extends EntityConstructor = EntityConstructor>(
|
|
86
103
|
options?: Options<T>,
|
|
87
104
|
...selectors: Selector[]
|
|
88
|
-
): Promise<
|
|
89
|
-
protected abstract getEntitiesSync<
|
|
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
|
-
>(
|
|
110
|
-
options?: Options<T>,
|
|
111
|
-
...selectors: Selector[]
|
|
112
|
-
): ReturnType<T['factorySync']>[] | string[] | number;
|
|
105
|
+
): Promise<EntityInstanceType<T>[] | string[] | number>;
|
|
113
106
|
abstract getUID(name: string): Promise<number | null>;
|
|
114
|
-
abstract import(filename: string): Promise<boolean>;
|
|
107
|
+
abstract import(filename: string, transaction?: boolean): Promise<boolean>;
|
|
115
108
|
abstract newUID(name: string): Promise<number | null>;
|
|
116
109
|
abstract renameUID(oldName: string, newName: string): Promise<boolean>;
|
|
117
110
|
abstract saveEntity(entity: EntityInterface): Promise<boolean>;
|
|
@@ -131,7 +124,7 @@ export default abstract class NymphDriver {
|
|
|
131
124
|
protected posixRegexMatch(
|
|
132
125
|
pattern: string,
|
|
133
126
|
subject: string,
|
|
134
|
-
caseInsensitive = false
|
|
127
|
+
caseInsensitive = false,
|
|
135
128
|
) {
|
|
136
129
|
const posixClasses = [
|
|
137
130
|
// '[[:<:]]',
|
|
@@ -211,11 +204,11 @@ export default abstract class NymphDriver {
|
|
|
211
204
|
guid: string,
|
|
212
205
|
tags: string[],
|
|
213
206
|
sdata: SerializedEntityData,
|
|
214
|
-
etype: string
|
|
207
|
+
etype: string,
|
|
215
208
|
) => Promise<void>,
|
|
216
209
|
saveUIDCallback: (name: string, value: number) => Promise<void>,
|
|
217
210
|
startTransactionCallback: (() => Promise<void>) | null = null,
|
|
218
|
-
commitTransactionCallback: (() => Promise<void>) | null = null
|
|
211
|
+
commitTransactionCallback: (() => Promise<void>) | null = null,
|
|
219
212
|
) {
|
|
220
213
|
let rl: ReadLines;
|
|
221
214
|
try {
|
|
@@ -237,7 +230,7 @@ export default abstract class NymphDriver {
|
|
|
237
230
|
continue;
|
|
238
231
|
}
|
|
239
232
|
const entityMatch = text.match(
|
|
240
|
-
/^\s*{([0-9A-Fa-f]+)}<([-\w_]+)>\[([^\]]*)\]\s
|
|
233
|
+
/^\s*{([0-9A-Fa-f]+)}<([-\w_]+)>\[([^\]]*)\]\s*$/,
|
|
241
234
|
);
|
|
242
235
|
const propMatch = text.match(/^\s*([^=]+)\s*=\s*(.*\S)\s*$/);
|
|
243
236
|
const uidMatch = text.match(/^\s*<([^>]+)>\[(\d+)\]\s*$/);
|
|
@@ -281,48 +274,43 @@ export default abstract class NymphDriver {
|
|
|
281
274
|
sdata: SerializedEntityData,
|
|
282
275
|
selectors: Selector[],
|
|
283
276
|
guid: string | null = null,
|
|
284
|
-
tags: string[] | null = null
|
|
277
|
+
tags: string[] | null = null,
|
|
285
278
|
) {
|
|
286
|
-
|
|
279
|
+
try {
|
|
280
|
+
const formattedSelectors = this.formatSelectors(selectors).selectors;
|
|
287
281
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
282
|
+
for (const curSelector of formattedSelectors) {
|
|
283
|
+
const type = curSelector.type;
|
|
284
|
+
const typeIsNot = type === '!&' || type === '!|';
|
|
285
|
+
const typeIsOr = type === '|' || type === '!|';
|
|
286
|
+
let pass = !typeIsOr;
|
|
287
|
+
for (const k in curSelector) {
|
|
288
|
+
const key = k as keyof Selector;
|
|
289
|
+
const value = curSelector[key];
|
|
296
290
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
291
|
+
if (key === 'type' || value == null) {
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
300
294
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
xor(
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
throw new Error("Can't use checkData on qref clauses.");
|
|
295
|
+
const clauseNot = key.substring(0, 1) === '!';
|
|
296
|
+
if (key === 'selector' || key === '!selector') {
|
|
297
|
+
const tmpArr = (
|
|
298
|
+
Array.isArray(value) ? value : [value]
|
|
299
|
+
) as Selector[];
|
|
300
|
+
pass = xor(
|
|
301
|
+
this.checkData(data, sdata, tmpArr, guid, tags),
|
|
302
|
+
xor(typeIsNot, clauseNot),
|
|
303
|
+
);
|
|
311
304
|
} else {
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
if
|
|
316
|
-
|
|
317
|
-
|
|
305
|
+
if (key === 'qref' || key === '!qref') {
|
|
306
|
+
throw new Error("Can't use checkData on qref clauses.");
|
|
307
|
+
} else {
|
|
308
|
+
// Check if it doesn't pass any for &, check if it passes any for |.
|
|
309
|
+
for (const curValue of value) {
|
|
310
|
+
if (curValue == null) {
|
|
311
|
+
continue;
|
|
312
|
+
}
|
|
318
313
|
|
|
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
314
|
const propName = (curValue as string[])[0];
|
|
327
315
|
|
|
328
316
|
// Unserialize the data for this variable.
|
|
@@ -340,7 +328,7 @@ export default abstract class NymphDriver {
|
|
|
340
328
|
case '!tag':
|
|
341
329
|
pass = xor(
|
|
342
330
|
(tags ?? []).indexOf(propName) !== -1,
|
|
343
|
-
xor(typeIsNot, clauseNot)
|
|
331
|
+
xor(typeIsNot, clauseNot),
|
|
344
332
|
);
|
|
345
333
|
break;
|
|
346
334
|
case 'defined':
|
|
@@ -351,7 +339,7 @@ export default abstract class NymphDriver {
|
|
|
351
339
|
case '!truthy':
|
|
352
340
|
pass = xor(
|
|
353
341
|
propName in data && data[propName],
|
|
354
|
-
xor(typeIsNot, clauseNot)
|
|
342
|
+
xor(typeIsNot, clauseNot),
|
|
355
343
|
);
|
|
356
344
|
break;
|
|
357
345
|
case 'ref':
|
|
@@ -361,9 +349,9 @@ export default abstract class NymphDriver {
|
|
|
361
349
|
propName in data &&
|
|
362
350
|
this.entityReferenceSearch(
|
|
363
351
|
data[propName],
|
|
364
|
-
testRefValue
|
|
352
|
+
testRefValue,
|
|
365
353
|
),
|
|
366
|
-
xor(typeIsNot, clauseNot)
|
|
354
|
+
xor(typeIsNot, clauseNot),
|
|
367
355
|
);
|
|
368
356
|
break;
|
|
369
357
|
case 'equal':
|
|
@@ -375,7 +363,7 @@ export default abstract class NymphDriver {
|
|
|
375
363
|
propName in data &&
|
|
376
364
|
JSON.stringify(data[propName]) ===
|
|
377
365
|
JSON.stringify(testEqualValue),
|
|
378
|
-
xor(typeIsNot, clauseNot)
|
|
366
|
+
xor(typeIsNot, clauseNot),
|
|
379
367
|
);
|
|
380
368
|
break;
|
|
381
369
|
case 'contain':
|
|
@@ -386,9 +374,9 @@ export default abstract class NymphDriver {
|
|
|
386
374
|
pass = xor(
|
|
387
375
|
propName in data &&
|
|
388
376
|
JSON.stringify(data[propName]).indexOf(
|
|
389
|
-
JSON.stringify(testContainValue)
|
|
377
|
+
JSON.stringify(testContainValue),
|
|
390
378
|
) !== -1,
|
|
391
|
-
xor(typeIsNot, clauseNot)
|
|
379
|
+
xor(typeIsNot, clauseNot),
|
|
392
380
|
);
|
|
393
381
|
break;
|
|
394
382
|
case 'like':
|
|
@@ -403,9 +391,9 @@ export default abstract class NymphDriver {
|
|
|
403
391
|
escapeRegExp(testLikeValue)
|
|
404
392
|
.replace('%', () => '.*')
|
|
405
393
|
.replace('_', () => '.') +
|
|
406
|
-
'$'
|
|
394
|
+
'$',
|
|
407
395
|
).test(data[propName]),
|
|
408
|
-
xor(typeIsNot, clauseNot)
|
|
396
|
+
xor(typeIsNot, clauseNot),
|
|
409
397
|
);
|
|
410
398
|
break;
|
|
411
399
|
case 'match':
|
|
@@ -417,7 +405,7 @@ export default abstract class NymphDriver {
|
|
|
417
405
|
pass = xor(
|
|
418
406
|
propName in data &&
|
|
419
407
|
this.posixRegexMatch(testMatchValue, data[propName]),
|
|
420
|
-
xor(typeIsNot, clauseNot)
|
|
408
|
+
xor(typeIsNot, clauseNot),
|
|
421
409
|
);
|
|
422
410
|
break;
|
|
423
411
|
case 'imatch':
|
|
@@ -431,9 +419,9 @@ export default abstract class NymphDriver {
|
|
|
431
419
|
this.posixRegexMatch(
|
|
432
420
|
testIMatchValue,
|
|
433
421
|
data[propName],
|
|
434
|
-
true
|
|
422
|
+
true,
|
|
435
423
|
),
|
|
436
|
-
xor(typeIsNot, clauseNot)
|
|
424
|
+
xor(typeIsNot, clauseNot),
|
|
437
425
|
);
|
|
438
426
|
break;
|
|
439
427
|
case 'gt':
|
|
@@ -443,7 +431,7 @@ export default abstract class NymphDriver {
|
|
|
443
431
|
)[1] as number;
|
|
444
432
|
pass = xor(
|
|
445
433
|
propName in data && data[propName] > testGTValue,
|
|
446
|
-
xor(typeIsNot, clauseNot)
|
|
434
|
+
xor(typeIsNot, clauseNot),
|
|
447
435
|
);
|
|
448
436
|
break;
|
|
449
437
|
case 'gte':
|
|
@@ -453,7 +441,7 @@ export default abstract class NymphDriver {
|
|
|
453
441
|
)[1] as number;
|
|
454
442
|
pass = xor(
|
|
455
443
|
propName in data && data[propName] >= testGTEValue,
|
|
456
|
-
xor(typeIsNot, clauseNot)
|
|
444
|
+
xor(typeIsNot, clauseNot),
|
|
457
445
|
);
|
|
458
446
|
break;
|
|
459
447
|
case 'lt':
|
|
@@ -463,7 +451,7 @@ export default abstract class NymphDriver {
|
|
|
463
451
|
)[1] as number;
|
|
464
452
|
pass = xor(
|
|
465
453
|
propName in data && data[propName] < testLTValue,
|
|
466
|
-
xor(typeIsNot, clauseNot)
|
|
454
|
+
xor(typeIsNot, clauseNot),
|
|
467
455
|
);
|
|
468
456
|
break;
|
|
469
457
|
case 'lte':
|
|
@@ -473,26 +461,33 @@ export default abstract class NymphDriver {
|
|
|
473
461
|
)[1] as number;
|
|
474
462
|
pass = xor(
|
|
475
463
|
propName in data && data[propName] <= testLTEValue,
|
|
476
|
-
xor(typeIsNot, clauseNot)
|
|
464
|
+
xor(typeIsNot, clauseNot),
|
|
477
465
|
);
|
|
478
466
|
break;
|
|
479
467
|
}
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
468
|
+
|
|
469
|
+
if (!xor(typeIsOr, pass)) {
|
|
470
|
+
break;
|
|
471
|
+
}
|
|
483
472
|
}
|
|
484
473
|
}
|
|
485
474
|
}
|
|
475
|
+
if (!xor(typeIsOr, pass)) {
|
|
476
|
+
break;
|
|
477
|
+
}
|
|
486
478
|
}
|
|
487
|
-
if (!
|
|
488
|
-
|
|
479
|
+
if (!pass) {
|
|
480
|
+
return false;
|
|
489
481
|
}
|
|
490
482
|
}
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
483
|
+
return true;
|
|
484
|
+
} catch (e: any) {
|
|
485
|
+
this.nymph.config.debugError(
|
|
486
|
+
'nymph',
|
|
487
|
+
`Failed to check entity data: ${e}`,
|
|
488
|
+
);
|
|
489
|
+
throw e;
|
|
494
490
|
}
|
|
495
|
-
return true;
|
|
496
491
|
}
|
|
497
492
|
|
|
498
493
|
/**
|
|
@@ -531,7 +526,7 @@ export default abstract class NymphDriver {
|
|
|
531
526
|
| EntityInterface
|
|
532
527
|
| EntityReference
|
|
533
528
|
| string
|
|
534
|
-
| (EntityInterface | EntityReference | string)[]
|
|
529
|
+
| (EntityInterface | EntityReference | string)[],
|
|
535
530
|
) {
|
|
536
531
|
// Get the GUID, if the passed $entity is an object.
|
|
537
532
|
const guids: string[] = [];
|
|
@@ -574,7 +569,7 @@ export default abstract class NymphDriver {
|
|
|
574
569
|
|
|
575
570
|
public formatSelectors(
|
|
576
571
|
selectors: Selector[],
|
|
577
|
-
options: Options = {}
|
|
572
|
+
options: Options = {},
|
|
578
573
|
): {
|
|
579
574
|
selectors: FormattedSelector[];
|
|
580
575
|
qrefs: [Options, ...FormattedSelector[]][];
|
|
@@ -609,10 +604,9 @@ export default abstract class NymphDriver {
|
|
|
609
604
|
for (let i = 0; i < tmpArr.length; i++) {
|
|
610
605
|
const name = tmpArr[i][0];
|
|
611
606
|
const [qrefOptions, ...qrefSelectors] = tmpArr[i][1];
|
|
612
|
-
const QrefEntityClass =
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
: qrefOptions.class ?? this.nymph.getEntityClass('Entity');
|
|
607
|
+
const QrefEntityClass = qrefOptions.class
|
|
608
|
+
? this.nymph.getEntityClass(qrefOptions.class)
|
|
609
|
+
: this.nymph.getEntityClass('Entity');
|
|
616
610
|
const newOptions = {
|
|
617
611
|
...qrefOptions,
|
|
618
612
|
class: QrefEntityClass,
|
|
@@ -621,7 +615,7 @@ export default abstract class NymphDriver {
|
|
|
621
615
|
const newSelectors = this.formatSelectors(qrefSelectors, options);
|
|
622
616
|
qrefs.push(
|
|
623
617
|
[newOptions, ...newSelectors.selectors],
|
|
624
|
-
...newSelectors.qrefs
|
|
618
|
+
...newSelectors.qrefs,
|
|
625
619
|
);
|
|
626
620
|
formatArr[i] = [name, [newOptions, ...newSelectors.selectors]];
|
|
627
621
|
}
|
|
@@ -669,12 +663,12 @@ export default abstract class NymphDriver {
|
|
|
669
663
|
|
|
670
664
|
protected iterateSelectorsForQuery(
|
|
671
665
|
selectors: FormattedSelector[],
|
|
672
|
-
callback: (
|
|
673
|
-
key: string
|
|
674
|
-
value: any
|
|
675
|
-
typeIsOr: boolean
|
|
676
|
-
typeIsNot: boolean
|
|
677
|
-
) => string
|
|
666
|
+
callback: (data: {
|
|
667
|
+
key: string;
|
|
668
|
+
value: any;
|
|
669
|
+
typeIsOr: boolean;
|
|
670
|
+
typeIsNot: boolean;
|
|
671
|
+
}) => string,
|
|
678
672
|
) {
|
|
679
673
|
const queryParts = [];
|
|
680
674
|
for (const curSelector of selectors) {
|
|
@@ -687,7 +681,7 @@ export default abstract class NymphDriver {
|
|
|
687
681
|
if (key === 'type') {
|
|
688
682
|
continue;
|
|
689
683
|
}
|
|
690
|
-
let curQuery = callback(key, value, typeIsOr, typeIsNot);
|
|
684
|
+
let curQuery = callback({ key, value, typeIsOr, typeIsNot });
|
|
691
685
|
if (curQuery) {
|
|
692
686
|
if (curSelectorQuery) {
|
|
693
687
|
curSelectorQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
@@ -703,71 +697,14 @@ export default abstract class NymphDriver {
|
|
|
703
697
|
return queryParts;
|
|
704
698
|
}
|
|
705
699
|
|
|
706
|
-
|
|
707
|
-
* Get the first entity to match all options/selectors, synchronously.
|
|
708
|
-
*
|
|
709
|
-
* This should really only be used internally by the Entity class. It's a bad
|
|
710
|
-
* idea to get entities synchronously. The Entity class uses this to load
|
|
711
|
-
* sleeping references.
|
|
712
|
-
*
|
|
713
|
-
* @param options The options.
|
|
714
|
-
* @param selectors Unlimited optional selectors to search for, or a single GUID. If none are given, all entities are searched for the given options.
|
|
715
|
-
* @returns An entity, or null on failure or nothing found.
|
|
716
|
-
*/
|
|
717
|
-
public getEntitySync<T extends EntityConstructor = EntityConstructor>(
|
|
718
|
-
options: Options<T> & { return: 'count' },
|
|
719
|
-
...selectors: Selector[]
|
|
720
|
-
): number;
|
|
721
|
-
public getEntitySync<T extends EntityConstructor = EntityConstructor>(
|
|
722
|
-
options: Options<T> & { return: 'guid' },
|
|
723
|
-
...selectors: Selector[]
|
|
724
|
-
): string | null;
|
|
725
|
-
public getEntitySync<T extends EntityConstructor = EntityConstructor>(
|
|
726
|
-
options: Options<T>,
|
|
727
|
-
...selectors: Selector[]
|
|
728
|
-
): ReturnType<T['factorySync']> | null;
|
|
729
|
-
public getEntitySync<T extends EntityConstructor = EntityConstructor>(
|
|
730
|
-
options: Options<T> & { return: 'count' },
|
|
731
|
-
guid: string
|
|
732
|
-
): number;
|
|
733
|
-
public getEntitySync<T extends EntityConstructor = EntityConstructor>(
|
|
734
|
-
options: Options<T> & { return: 'guid' },
|
|
735
|
-
guid: string
|
|
736
|
-
): string | null;
|
|
737
|
-
public getEntitySync<T extends EntityConstructor = EntityConstructor>(
|
|
738
|
-
options: Options<T>,
|
|
739
|
-
guid: string
|
|
740
|
-
): ReturnType<T['factorySync']> | null;
|
|
741
|
-
public getEntitySync<T extends EntityConstructor = EntityConstructor>(
|
|
742
|
-
options: Options<T> = {},
|
|
743
|
-
...selectors: Selector[] | string[]
|
|
744
|
-
): ReturnType<T['factorySync']> | string | number | null {
|
|
745
|
-
// Set up options and selectors.
|
|
746
|
-
if (typeof selectors[0] === 'string') {
|
|
747
|
-
selectors = [{ type: '&', guid: selectors[0] }];
|
|
748
|
-
}
|
|
749
|
-
options.limit = 1;
|
|
750
|
-
const entities = this.getEntitiesSync(
|
|
751
|
-
options,
|
|
752
|
-
...(selectors as Selector[])
|
|
753
|
-
);
|
|
754
|
-
if (options.return === 'count') {
|
|
755
|
-
return entities as unknown as number;
|
|
756
|
-
}
|
|
757
|
-
if (!entities || !entities.length) {
|
|
758
|
-
return null;
|
|
759
|
-
}
|
|
760
|
-
return entities[0];
|
|
761
|
-
}
|
|
762
|
-
|
|
763
|
-
protected getEntitesRowLike<T extends EntityConstructor = EntityConstructor>(
|
|
700
|
+
protected getEntitiesRowLike<T extends EntityConstructor = EntityConstructor>(
|
|
764
701
|
options: Options<T> & { return: 'count' },
|
|
765
702
|
selectors: Selector[],
|
|
766
|
-
performQueryCallback: (
|
|
767
|
-
options: Options<T
|
|
768
|
-
|
|
769
|
-
etype: string
|
|
770
|
-
) => {
|
|
703
|
+
performQueryCallback: (data: {
|
|
704
|
+
options: Options<T>;
|
|
705
|
+
selectors: FormattedSelector[];
|
|
706
|
+
etype: string;
|
|
707
|
+
}) => {
|
|
771
708
|
result: any;
|
|
772
709
|
},
|
|
773
710
|
rowFetchCallback: () => any,
|
|
@@ -782,16 +719,16 @@ export default abstract class NymphDriver {
|
|
|
782
719
|
getDataNameAndSValueCallback: (row: any) => {
|
|
783
720
|
name: string;
|
|
784
721
|
svalue: string;
|
|
785
|
-
}
|
|
722
|
+
},
|
|
786
723
|
): { result: any; process: () => number | Error };
|
|
787
|
-
protected
|
|
724
|
+
protected getEntitiesRowLike<T extends EntityConstructor = EntityConstructor>(
|
|
788
725
|
options: Options<T> & { return: 'guid' },
|
|
789
726
|
selectors: Selector[],
|
|
790
|
-
performQueryCallback: (
|
|
791
|
-
options: Options<T
|
|
792
|
-
|
|
793
|
-
etype: string
|
|
794
|
-
) => {
|
|
727
|
+
performQueryCallback: (data: {
|
|
728
|
+
options: Options<T>;
|
|
729
|
+
selectors: FormattedSelector[];
|
|
730
|
+
etype: string;
|
|
731
|
+
}) => {
|
|
795
732
|
result: any;
|
|
796
733
|
},
|
|
797
734
|
rowFetchCallback: () => any,
|
|
@@ -806,16 +743,16 @@ export default abstract class NymphDriver {
|
|
|
806
743
|
getDataNameAndSValueCallback: (row: any) => {
|
|
807
744
|
name: string;
|
|
808
745
|
svalue: string;
|
|
809
|
-
}
|
|
746
|
+
},
|
|
810
747
|
): { result: any; process: () => string[] | Error };
|
|
811
|
-
protected
|
|
748
|
+
protected getEntitiesRowLike<T extends EntityConstructor = EntityConstructor>(
|
|
812
749
|
options: Options<T>,
|
|
813
750
|
selectors: Selector[],
|
|
814
|
-
performQueryCallback: (
|
|
815
|
-
options: Options<T
|
|
816
|
-
|
|
817
|
-
etype: string
|
|
818
|
-
) => {
|
|
751
|
+
performQueryCallback: (data: {
|
|
752
|
+
options: Options<T>;
|
|
753
|
+
selectors: FormattedSelector[];
|
|
754
|
+
etype: string;
|
|
755
|
+
}) => {
|
|
819
756
|
result: any;
|
|
820
757
|
},
|
|
821
758
|
rowFetchCallback: () => any,
|
|
@@ -830,16 +767,19 @@ export default abstract class NymphDriver {
|
|
|
830
767
|
getDataNameAndSValueCallback: (row: any) => {
|
|
831
768
|
name: string;
|
|
832
769
|
svalue: string;
|
|
833
|
-
}
|
|
834
|
-
): {
|
|
835
|
-
|
|
770
|
+
},
|
|
771
|
+
): {
|
|
772
|
+
result: any;
|
|
773
|
+
process: () => EntityInstanceType<T>[] | Error;
|
|
774
|
+
};
|
|
775
|
+
protected getEntitiesRowLike<T extends EntityConstructor = EntityConstructor>(
|
|
836
776
|
options: Options<T>,
|
|
837
777
|
selectors: Selector[],
|
|
838
|
-
performQueryCallback: (
|
|
839
|
-
options: Options<T
|
|
840
|
-
|
|
841
|
-
etype: string
|
|
842
|
-
) => {
|
|
778
|
+
performQueryCallback: (data: {
|
|
779
|
+
options: Options<T>;
|
|
780
|
+
selectors: FormattedSelector[];
|
|
781
|
+
etype: string;
|
|
782
|
+
}) => {
|
|
843
783
|
result: any;
|
|
844
784
|
},
|
|
845
785
|
rowFetchCallback: () => any,
|
|
@@ -854,10 +794,10 @@ export default abstract class NymphDriver {
|
|
|
854
794
|
getDataNameAndSValueCallback: (row: any) => {
|
|
855
795
|
name: string;
|
|
856
796
|
svalue: string;
|
|
857
|
-
}
|
|
797
|
+
},
|
|
858
798
|
): {
|
|
859
799
|
result: any;
|
|
860
|
-
process: () =>
|
|
800
|
+
process: () => EntityInstanceType<T>[] | string[] | number | Error;
|
|
861
801
|
} {
|
|
862
802
|
if (!this.isConnected()) {
|
|
863
803
|
throw new UnableToConnectError('not connected to DB');
|
|
@@ -870,12 +810,12 @@ export default abstract class NymphDriver {
|
|
|
870
810
|
['&', '!&', '|', '!|'].indexOf(selector.type) === -1
|
|
871
811
|
) {
|
|
872
812
|
throw new InvalidParametersError(
|
|
873
|
-
'Invalid query selector passed: ' + JSON.stringify(selector)
|
|
813
|
+
'Invalid query selector passed: ' + JSON.stringify(selector),
|
|
874
814
|
);
|
|
875
815
|
}
|
|
876
816
|
}
|
|
877
817
|
|
|
878
|
-
let entities:
|
|
818
|
+
let entities: EntityInstanceType<T>[] | string[] = [];
|
|
879
819
|
let count = 0;
|
|
880
820
|
const EntityClass =
|
|
881
821
|
options.class ?? (this.nymph.getEntityClass('Entity') as T);
|
|
@@ -885,6 +825,7 @@ export default abstract class NymphDriver {
|
|
|
885
825
|
if (
|
|
886
826
|
this.nymph.config.cache &&
|
|
887
827
|
!options.skipCache &&
|
|
828
|
+
selectors.length &&
|
|
888
829
|
'guid' in selectors[0] &&
|
|
889
830
|
typeof selectors[0].guid === 'number'
|
|
890
831
|
) {
|
|
@@ -896,10 +837,10 @@ export default abstract class NymphDriver {
|
|
|
896
837
|
(Object.keys(selectors[0]).length === 2 ||
|
|
897
838
|
(Object.keys(selectors[0]).length === 3 && 'tag' in selectors[0]))
|
|
898
839
|
) {
|
|
899
|
-
const entity = this.pullCache<
|
|
840
|
+
const entity = this.pullCache<EntityInstanceType<T>>(
|
|
900
841
|
selectors[0]['guid'],
|
|
901
842
|
EntityClass.class,
|
|
902
|
-
!!options.skipAc
|
|
843
|
+
!!options.skipAc,
|
|
903
844
|
);
|
|
904
845
|
if (
|
|
905
846
|
entity != null &&
|
|
@@ -908,7 +849,7 @@ export default abstract class NymphDriver {
|
|
|
908
849
|
entity.hasTag(
|
|
909
850
|
...(Array.isArray(selectors[0].tag)
|
|
910
851
|
? selectors[0].tag
|
|
911
|
-
: [selectors[0].tag])
|
|
852
|
+
: [selectors[0].tag]),
|
|
912
853
|
))
|
|
913
854
|
) {
|
|
914
855
|
const guid = entity.guid;
|
|
@@ -934,11 +875,11 @@ export default abstract class NymphDriver {
|
|
|
934
875
|
const [options, ...selectors] = qref;
|
|
935
876
|
this.nymph.runQueryCallbacks(options, selectors);
|
|
936
877
|
}
|
|
937
|
-
const { result } = performQueryCallback(
|
|
878
|
+
const { result } = performQueryCallback({
|
|
938
879
|
options,
|
|
939
|
-
formattedSelectors.selectors,
|
|
940
|
-
etype
|
|
941
|
-
);
|
|
880
|
+
selectors: formattedSelectors.selectors,
|
|
881
|
+
etype,
|
|
882
|
+
});
|
|
942
883
|
|
|
943
884
|
return {
|
|
944
885
|
result,
|
|
@@ -978,9 +919,7 @@ export default abstract class NymphDriver {
|
|
|
978
919
|
// Make sure that $row is incremented :)
|
|
979
920
|
row = rowFetchCallback();
|
|
980
921
|
}
|
|
981
|
-
const entity = EntityClass.factorySync() as
|
|
982
|
-
T['factorySync']
|
|
983
|
-
>;
|
|
922
|
+
const entity = EntityClass.factorySync() as EntityInstanceType<T>;
|
|
984
923
|
entity.$nymph = this.nymph;
|
|
985
924
|
if (options.skipAc != null) {
|
|
986
925
|
entity.$useSkipAc(!!options.skipAc);
|
|
@@ -1020,34 +959,37 @@ export default abstract class NymphDriver {
|
|
|
1020
959
|
|
|
1021
960
|
protected async saveEntityRowLike(
|
|
1022
961
|
entity: EntityInterface,
|
|
1023
|
-
saveNewEntityCallback: (
|
|
1024
|
-
entity: EntityInterface
|
|
1025
|
-
guid: string
|
|
1026
|
-
tags: string[]
|
|
1027
|
-
data: EntityData
|
|
1028
|
-
sdata: SerializedEntityData
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
962
|
+
saveNewEntityCallback: (data: {
|
|
963
|
+
entity: EntityInterface;
|
|
964
|
+
guid: string;
|
|
965
|
+
tags: string[];
|
|
966
|
+
data: EntityData;
|
|
967
|
+
sdata: SerializedEntityData;
|
|
968
|
+
uniques: string[];
|
|
969
|
+
cdate: number;
|
|
970
|
+
etype: string;
|
|
971
|
+
}) => Promise<boolean>,
|
|
972
|
+
saveExistingEntityCallback: (data: {
|
|
973
|
+
entity: EntityInterface;
|
|
974
|
+
guid: string;
|
|
975
|
+
tags: string[];
|
|
976
|
+
data: EntityData;
|
|
977
|
+
sdata: SerializedEntityData;
|
|
978
|
+
uniques: string[];
|
|
979
|
+
mdate: number;
|
|
980
|
+
etype: string;
|
|
981
|
+
}) => Promise<boolean>,
|
|
1041
982
|
startTransactionCallback: (() => Promise<void>) | null = null,
|
|
1042
983
|
commitTransactionCallback:
|
|
1043
984
|
| ((success: boolean) => Promise<boolean>)
|
|
1044
|
-
| null = null
|
|
985
|
+
| null = null,
|
|
1045
986
|
) {
|
|
1046
987
|
// Get a modified date.
|
|
1047
988
|
const mdate = Date.now();
|
|
1048
989
|
const tags = difference(entity.tags, ['']);
|
|
1049
990
|
const data = entity.$getData();
|
|
1050
991
|
const sdata = entity.$getSData();
|
|
992
|
+
const uniques = await entity.$getUniques();
|
|
1051
993
|
const EntityClass = entity.constructor as EntityConstructor;
|
|
1052
994
|
const etype = EntityClass.ETYPE;
|
|
1053
995
|
if (startTransactionCallback) {
|
|
@@ -1057,15 +999,16 @@ export default abstract class NymphDriver {
|
|
|
1057
999
|
if (entity.guid == null) {
|
|
1058
1000
|
const cdate = mdate;
|
|
1059
1001
|
const newId = guid();
|
|
1060
|
-
success = await saveNewEntityCallback(
|
|
1002
|
+
success = await saveNewEntityCallback({
|
|
1061
1003
|
entity,
|
|
1062
|
-
newId,
|
|
1004
|
+
guid: newId,
|
|
1063
1005
|
tags,
|
|
1064
1006
|
data,
|
|
1065
1007
|
sdata,
|
|
1008
|
+
uniques,
|
|
1066
1009
|
cdate,
|
|
1067
|
-
etype
|
|
1068
|
-
);
|
|
1010
|
+
etype,
|
|
1011
|
+
});
|
|
1069
1012
|
if (success) {
|
|
1070
1013
|
entity.guid = newId;
|
|
1071
1014
|
entity.cdate = cdate;
|
|
@@ -1076,15 +1019,16 @@ export default abstract class NymphDriver {
|
|
|
1076
1019
|
if (this.nymph.config.cache) {
|
|
1077
1020
|
this.cleanCache(entity.guid);
|
|
1078
1021
|
}
|
|
1079
|
-
success = await saveExistingEntityCallback(
|
|
1022
|
+
success = await saveExistingEntityCallback({
|
|
1080
1023
|
entity,
|
|
1081
|
-
entity.guid,
|
|
1024
|
+
guid: entity.guid,
|
|
1082
1025
|
tags,
|
|
1083
1026
|
data,
|
|
1084
1027
|
sdata,
|
|
1028
|
+
uniques,
|
|
1085
1029
|
mdate,
|
|
1086
|
-
etype
|
|
1087
|
-
);
|
|
1030
|
+
etype,
|
|
1031
|
+
});
|
|
1088
1032
|
if (success) {
|
|
1089
1033
|
entity.mdate = mdate;
|
|
1090
1034
|
}
|
|
@@ -1100,7 +1044,7 @@ export default abstract class NymphDriver {
|
|
|
1100
1044
|
entity.mdate as number,
|
|
1101
1045
|
entity.tags,
|
|
1102
1046
|
data,
|
|
1103
|
-
sdata
|
|
1047
|
+
sdata,
|
|
1104
1048
|
);
|
|
1105
1049
|
}
|
|
1106
1050
|
return success;
|
|
@@ -1114,10 +1058,10 @@ export default abstract class NymphDriver {
|
|
|
1114
1058
|
* @param useSkipAc Whether to tell the entity to use skip_ac.
|
|
1115
1059
|
* @returns The entity or null if it's not cached.
|
|
1116
1060
|
*/
|
|
1117
|
-
protected pullCache<T extends
|
|
1061
|
+
protected pullCache<T extends Entity>(
|
|
1118
1062
|
guid: string,
|
|
1119
1063
|
className: string,
|
|
1120
|
-
useSkipAc = false
|
|
1064
|
+
useSkipAc = false,
|
|
1121
1065
|
): T | null {
|
|
1122
1066
|
// Increment the entity access count.
|
|
1123
1067
|
if (!(guid in this.entityCount)) {
|
|
@@ -1135,7 +1079,7 @@ export default abstract class NymphDriver {
|
|
|
1135
1079
|
entity.tags = this.entityCache[guid]['tags'];
|
|
1136
1080
|
entity.$putData(
|
|
1137
1081
|
this.entityCache[guid]['data'],
|
|
1138
|
-
this.entityCache[guid]['sdata']
|
|
1082
|
+
this.entityCache[guid]['sdata'],
|
|
1139
1083
|
);
|
|
1140
1084
|
return entity;
|
|
1141
1085
|
}
|
|
@@ -1159,7 +1103,7 @@ export default abstract class NymphDriver {
|
|
|
1159
1103
|
mdate: number,
|
|
1160
1104
|
tags: string[],
|
|
1161
1105
|
data: EntityData,
|
|
1162
|
-
sdata: SerializedEntityData
|
|
1106
|
+
sdata: SerializedEntityData,
|
|
1163
1107
|
) {
|
|
1164
1108
|
if (guid == null) {
|
|
1165
1109
|
return;
|
|
@@ -1188,7 +1132,7 @@ export default abstract class NymphDriver {
|
|
|
1188
1132
|
// Find which entity has been accessed the least.
|
|
1189
1133
|
const least =
|
|
1190
1134
|
Object.entries(this.entityCount)
|
|
1191
|
-
.sort(([
|
|
1135
|
+
.sort(([_guidA, countA], [_guidB, countB]) => countA - countB)
|
|
1192
1136
|
.pop()?.[0] ?? null;
|
|
1193
1137
|
if (least == null) {
|
|
1194
1138
|
// This should never happen.
|