@nymphjs/nymph 1.0.0-beta.6 → 1.0.0-beta.61

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 (64) hide show
  1. package/CHANGELOG.md +277 -0
  2. package/README.md +13 -21
  3. package/dist/Entity.d.ts +269 -7
  4. package/dist/Entity.js +431 -89
  5. package/dist/Entity.js.map +1 -1
  6. package/dist/Entity.test.js +38 -16
  7. package/dist/Entity.test.js.map +1 -1
  8. package/dist/Entity.types.d.ts +225 -19
  9. package/dist/Nymph.d.ts +423 -11
  10. package/dist/Nymph.js +649 -156
  11. package/dist/Nymph.js.map +1 -1
  12. package/dist/Nymph.types.d.ts +31 -7
  13. package/dist/Nymph.types.js +2 -1
  14. package/dist/Nymph.types.js.map +1 -1
  15. package/dist/conf/d.d.ts +26 -0
  16. package/dist/conf/defaults.js +22 -0
  17. package/dist/conf/defaults.js.map +1 -1
  18. package/dist/driver/NymphDriver.d.ts +111 -36
  19. package/dist/driver/NymphDriver.js +169 -68
  20. package/dist/driver/NymphDriver.js.map +1 -1
  21. package/dist/driver/testArtifacts.d.ts +1 -1
  22. package/dist/driver/testArtifacts.js +285 -18
  23. package/dist/driver/testArtifacts.js.map +1 -1
  24. package/dist/errors/EntityInvalidDataError.d.ts +6 -0
  25. package/dist/errors/EntityInvalidDataError.js +6 -0
  26. package/dist/errors/EntityInvalidDataError.js.map +1 -1
  27. package/dist/errors/EntityUniqueConstraintError.d.ts +3 -0
  28. package/dist/errors/EntityUniqueConstraintError.js +11 -0
  29. package/dist/errors/EntityUniqueConstraintError.js.map +1 -0
  30. package/dist/errors/InvalidStateError.d.ts +3 -0
  31. package/dist/errors/InvalidStateError.js +11 -0
  32. package/dist/errors/InvalidStateError.js.map +1 -0
  33. package/dist/errors/QueryFailedError.js.map +1 -1
  34. package/dist/errors/index.d.ts +2 -0
  35. package/dist/errors/index.js +2 -0
  36. package/dist/errors/index.js.map +1 -1
  37. package/dist/testArtifacts.d.ts +17 -5
  38. package/dist/testArtifacts.js +23 -19
  39. package/dist/testArtifacts.js.map +1 -1
  40. package/dist/testMocks.d.ts +15 -10
  41. package/dist/testMocks.js +34 -11
  42. package/dist/testMocks.js.map +1 -1
  43. package/dist/utils.d.ts +1 -1
  44. package/dist/utils.js +21 -5
  45. package/dist/utils.js.map +1 -1
  46. package/package.json +16 -14
  47. package/src/Entity.test.ts +30 -10
  48. package/src/Entity.ts +406 -135
  49. package/src/Entity.types.ts +32 -66
  50. package/src/Nymph.ts +626 -196
  51. package/src/Nymph.types.ts +97 -26
  52. package/src/conf/d.ts +8 -0
  53. package/src/conf/defaults.ts +24 -0
  54. package/src/driver/NymphDriver.ts +187 -243
  55. package/src/driver/testArtifacts.ts +327 -146
  56. package/src/errors/EntityUniqueConstraintError.ts +6 -0
  57. package/src/errors/InvalidStateError.ts +6 -0
  58. package/src/errors/index.ts +4 -0
  59. package/src/nymphJoiProps.ts +2 -2
  60. package/src/testArtifacts.ts +18 -19
  61. package/src/testMocks.ts +64 -32
  62. package/src/utils.ts +22 -8
  63. package/tsconfig.json +1 -1
  64. 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<ReturnType<T['factorySync']>[]>;
101
+ ): Promise<EntityInstanceType<T>[]>;
85
102
  abstract getEntities<T extends EntityConstructor = EntityConstructor>(
86
103
  options?: Options<T>,
87
104
  ...selectors: Selector[]
88
- ): Promise<ReturnType<T['factorySync']>[] | string[] | number>;
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
- const formattedSelectors = this.formatSelectors(selectors).selectors;
279
+ try {
280
+ const formattedSelectors = this.formatSelectors(selectors).selectors;
287
281
 
288
- for (const curSelector of formattedSelectors) {
289
- const type = curSelector.type;
290
- const typeIsNot = type === '!&' || type === '!|';
291
- const typeIsOr = type === '|' || type === '!|';
292
- let pass = !typeIsOr;
293
- for (const k in curSelector) {
294
- const key = k as keyof Selector;
295
- const value = curSelector[key];
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
- if (key === 'type' || value == null) {
298
- continue;
299
- }
291
+ if (key === 'type' || value == null) {
292
+ continue;
293
+ }
300
294
 
301
- const clauseNot = key.substr(0, 1) === '!';
302
- if (key === 'selector' || key === '!selector') {
303
- const tmpArr = (Array.isArray(value) ? value : [value]) as Selector[];
304
- pass = xor(
305
- this.checkData(data, sdata, tmpArr, guid, tags),
306
- xor(typeIsNot, clauseNot)
307
- );
308
- } else {
309
- if (key === 'qref' || key === '!qref') {
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
- // Check if it doesn't pass any for &, check if it
313
- // passes any for |.
314
- for (const curValue of value) {
315
- if (curValue == null) {
316
- continue;
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
- if (!xor(typeIsOr, pass)) {
482
- break;
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 (!xor(typeIsOr, pass)) {
488
- break;
479
+ if (!pass) {
480
+ return false;
489
481
  }
490
482
  }
491
- if (!pass) {
492
- return false;
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
- typeof qrefOptions.class === 'string'
614
- ? this.nymph.getEntityClass(qrefOptions.class)
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
- formattedSelectors: FormattedSelector[],
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 getEntitesRowLike<T extends EntityConstructor = EntityConstructor>(
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
- formattedSelectors: FormattedSelector[],
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 getEntitesRowLike<T extends EntityConstructor = EntityConstructor>(
748
+ protected getEntitiesRowLike<T extends EntityConstructor = EntityConstructor>(
812
749
  options: Options<T>,
813
750
  selectors: Selector[],
814
- performQueryCallback: (
815
- options: Options<T>,
816
- formattedSelectors: FormattedSelector[],
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
- ): { result: any; process: () => ReturnType<T['factorySync']>[] | Error };
835
- protected getEntitesRowLike<T extends EntityConstructor = EntityConstructor>(
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
- formattedSelectors: FormattedSelector[],
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: () => ReturnType<T['factorySync']>[] | string[] | number | Error;
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: ReturnType<T['factorySync']>[] | string[] = [];
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<ReturnType<T['factorySync']>>(
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 ReturnType<
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
- cdate: number,
1030
- etype: string
1031
- ) => Promise<boolean>,
1032
- saveExistingEntityCallback: (
1033
- entity: EntityInterface,
1034
- guid: string,
1035
- tags: string[],
1036
- data: EntityData,
1037
- sdata: SerializedEntityData,
1038
- mdate: number,
1039
- etype: string
1040
- ) => Promise<boolean>,
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 EntityInterface = EntityInterface>(
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(([guidA, countA], [guidB, countB]) => countA - countB)
1135
+ .sort(([_guidA, countA], [_guidB, countB]) => countA - countB)
1192
1136
  .pop()?.[0] ?? null;
1193
1137
  if (least == null) {
1194
1138
  // This should never happen.