@nymphjs/nymph 1.0.0-beta.1 → 1.0.0-beta.11

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/src/Nymph.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { Config, ConfigDefaults as defaults } from './conf';
2
2
  import { NymphDriver } from './driver';
3
3
  import Entity from './Entity';
4
- import { EntityConstructor, EntityData, EntityInterface } from './Entity.types';
4
+ import { EntityConstructor, EntityInterface } from './Entity.types';
5
5
  import { ClassNotAvailableError } from './errors';
6
6
  import type {
7
7
  Selector,
@@ -63,7 +63,7 @@ export default class Nymph {
63
63
  /**
64
64
  * The entity class for this instance of Nymph.
65
65
  */
66
- public Entity: typeof Entity = Entity;
66
+ public Entity: typeof Entity;
67
67
 
68
68
  private connectCallbacks: NymphConnectCallback[] = [];
69
69
  private disconnectCallbacks: NymphDisconnectCallback[] = [];
@@ -87,20 +87,6 @@ export default class Nymph {
87
87
  private beforeDeleteUIDCallbacks: NymphBeforeDeleteUIDCallback[] = [];
88
88
  private afterDeleteUIDCallbacks: NymphAfterDeleteUIDCallback[] = [];
89
89
 
90
- public addEntityClass(entityClass: EntityConstructor) {
91
- this.entityClasses[entityClass.class] = entityClass;
92
- entityClass.nymph = this;
93
- }
94
-
95
- public getEntityClass(className: string): EntityConstructor {
96
- if (className in this.entityClasses) {
97
- const EntityClass = this.entityClasses[className];
98
- EntityClass.nymph = this;
99
- return EntityClass;
100
- }
101
- throw new ClassNotAvailableError('Tried to use class: ' + className);
102
- }
103
-
104
90
  /**
105
91
  * Initialize Nymph.
106
92
  *
@@ -115,28 +101,63 @@ export default class Nymph {
115
101
  ) {
116
102
  this.config = { ...defaults, ...config };
117
103
  this.driver = driver;
104
+
105
+ this.Entity = this.addEntityClass(Entity);
106
+
118
107
  if (typeof tilmeld !== 'undefined') {
119
108
  this.tilmeld = tilmeld;
120
109
  }
121
110
 
122
- class NymphEntity<T extends EntityData = EntityData> extends Entity<T> {}
123
- NymphEntity.nymph = this;
124
- this.Entity = NymphEntity;
125
- this.addEntityClass(NymphEntity);
126
-
127
111
  this.driver.init(this);
128
112
  if (this.tilmeld) {
129
113
  this.tilmeld.init(this);
130
114
  }
131
115
  }
132
116
 
117
+ /**
118
+ * Add your class to this instance.
119
+ *
120
+ * This will create a class that extends your class within this instance of
121
+ * Nymph and return it. You can then use this class's constructor and methods,
122
+ * which will use this instance of Nymph.
123
+ *
124
+ * Because this creates a subclass, don't use the class
125
+ * returned from `getEntityClass` to check with `instanceof`.
126
+ */
127
+ public addEntityClass<T extends EntityConstructor>(entityClass: T): T {
128
+ const nymph = this;
129
+ class NymphEntity extends entityClass {
130
+ static nymph: Nymph = nymph;
131
+
132
+ constructor(...args: any[]) {
133
+ super(...args);
134
+ }
135
+ }
136
+ this.entityClasses[entityClass.class] = NymphEntity;
137
+ return NymphEntity;
138
+ }
139
+
140
+ /**
141
+ * Get the class that uses the specified class name.
142
+ */
143
+ public getEntityClass(className: string): EntityConstructor {
144
+ if (className in this.entityClasses) {
145
+ return this.entityClasses[className];
146
+ }
147
+ throw new ClassNotAvailableError('Tried to use class: ' + className);
148
+ }
149
+
133
150
  /**
134
151
  * Get a clone of this instance with duplicate classes and event listeners.
135
152
  *
136
153
  * @returns A clone of this instance.
137
154
  */
138
155
  public clone() {
139
- const nymph = new Nymph(this.config, this.driver, this.tilmeld?.clone());
156
+ const nymph = new Nymph(
157
+ this.config,
158
+ this.driver.clone(),
159
+ this.tilmeld?.clone()
160
+ );
140
161
  for (const name in this.entityClasses) {
141
162
  if (name === 'Entity' || (this.entityClasses[name] as any).skipOnClone) {
142
163
  continue;
@@ -190,7 +211,7 @@ export default class Nymph {
190
211
  const result = this.driver.connect();
191
212
  for (let callback of this.connectCallbacks) {
192
213
  if (callback) {
193
- callback(this, result);
214
+ await callback(this, result);
194
215
  }
195
216
  }
196
217
  return await result;
@@ -205,7 +226,7 @@ export default class Nymph {
205
226
  const result = this.driver.disconnect();
206
227
  for (let callback of this.disconnectCallbacks) {
207
228
  if (callback) {
208
- callback(this, result);
229
+ await callback(this, result);
209
230
  }
210
231
  }
211
232
  return await result;
@@ -214,10 +235,7 @@ export default class Nymph {
214
235
  /**
215
236
  * Run all the query callbacks on a query.
216
237
  */
217
- public async runQueryCallbacks(
218
- options: Options,
219
- selectors: FormattedSelector[]
220
- ) {
238
+ public runQueryCallbacks(options: Options, selectors: FormattedSelector[]) {
221
239
  for (let callback of this.queryCallbacks) {
222
240
  if (callback) {
223
241
  callback(this, options, selectors);
@@ -235,7 +253,7 @@ export default class Nymph {
235
253
  * Transactions will nest as long as every name is unique. Internally, Nymph
236
254
  * uses names prefixed with "nymph-".
237
255
  *
238
- * @returns True on success, false on failure.
256
+ * @returns A new instance of Nymph that should be used for the transaction.
239
257
  */
240
258
  public async startTransaction(name: string): Promise<Nymph> {
241
259
  return await this.driver.startTransaction(name);
@@ -244,6 +262,8 @@ export default class Nymph {
244
262
  /**
245
263
  * Commit the named transaction.
246
264
  *
265
+ * After this is called, the transaction instance should be discarded.
266
+ *
247
267
  * @returns True on success, false on failure.
248
268
  */
249
269
  public async commit(name: string): Promise<boolean> {
@@ -253,6 +273,8 @@ export default class Nymph {
253
273
  /**
254
274
  * Rollback the named transaction.
255
275
  *
276
+ * After this is called, the transaction instance should be discarded.
277
+ *
256
278
  * @returns True on success, false on failure.
257
279
  */
258
280
  public async rollback(name: string): Promise<boolean> {
@@ -292,13 +314,13 @@ export default class Nymph {
292
314
  public async newUID(name: string): Promise<number | null> {
293
315
  for (let callback of this.beforeNewUIDCallbacks) {
294
316
  if (callback) {
295
- callback(this, name);
317
+ await callback(this, name);
296
318
  }
297
319
  }
298
320
  const result = this.driver.newUID(name);
299
321
  for (let callback of this.afterNewUIDCallbacks) {
300
322
  if (callback) {
301
- callback(this, result);
323
+ await callback(this, result);
302
324
  }
303
325
  }
304
326
  return await result;
@@ -323,13 +345,13 @@ export default class Nymph {
323
345
  public async setUID(name: string, value: number): Promise<boolean> {
324
346
  for (let callback of this.beforeSetUIDCallbacks) {
325
347
  if (callback) {
326
- callback(this, name, value);
348
+ await callback(this, name, value);
327
349
  }
328
350
  }
329
351
  const result = this.driver.setUID(name, value);
330
352
  for (let callback of this.afterSetUIDCallbacks) {
331
353
  if (callback) {
332
- callback(this, result);
354
+ await callback(this, result);
333
355
  }
334
356
  }
335
357
  return await result;
@@ -344,13 +366,13 @@ export default class Nymph {
344
366
  public async deleteUID(name: string): Promise<boolean> {
345
367
  for (let callback of this.beforeDeleteUIDCallbacks) {
346
368
  if (callback) {
347
- callback(this, name);
369
+ await callback(this, name);
348
370
  }
349
371
  }
350
372
  const result = this.driver.deleteUID(name);
351
373
  for (let callback of this.afterDeleteUIDCallbacks) {
352
374
  if (callback) {
353
- callback(this, result);
375
+ await callback(this, result);
354
376
  }
355
377
  }
356
378
  return await result;
@@ -366,13 +388,13 @@ export default class Nymph {
366
388
  public async renameUID(oldName: string, newName: string): Promise<boolean> {
367
389
  for (let callback of this.beforeRenameUIDCallbacks) {
368
390
  if (callback) {
369
- callback(this, oldName, newName);
391
+ await callback(this, oldName, newName);
370
392
  }
371
393
  }
372
394
  const result = this.driver.renameUID(oldName, newName);
373
395
  for (let callback of this.afterRenameUIDCallbacks) {
374
396
  if (callback) {
375
- callback(this, result);
397
+ await callback(this, result);
376
398
  }
377
399
  }
378
400
  return await result;
@@ -392,13 +414,13 @@ export default class Nymph {
392
414
  public async saveEntity(entity: EntityInterface): Promise<boolean> {
393
415
  for (let callback of this.beforeSaveEntityCallbacks) {
394
416
  if (callback) {
395
- callback(this, entity);
417
+ await callback(this, entity);
396
418
  }
397
419
  }
398
420
  const result = this.driver.saveEntity(entity);
399
421
  for (let callback of this.afterSaveEntityCallbacks) {
400
422
  if (callback) {
401
- callback(this, result);
423
+ await callback(this, result);
402
424
  }
403
425
  }
404
426
  return await result;
@@ -581,7 +603,7 @@ export default class Nymph {
581
603
  ): Promise<ReturnType<T['factorySync']>[] | string[] | number> {
582
604
  for (let callback of this.beforeGetEntitiesCallbacks) {
583
605
  if (callback) {
584
- callback(this, options, selectors);
606
+ await callback(this, options, selectors);
585
607
  }
586
608
  }
587
609
  return await this.driver.getEntities(options, ...selectors);
@@ -631,7 +653,7 @@ export default class Nymph {
631
653
  options.limit = 1;
632
654
  for (let callback of this.beforeGetEntityCallbacks) {
633
655
  if (callback) {
634
- callback(this, options, selectors as Selector[]);
656
+ await callback(this, options, selectors as Selector[]);
635
657
  }
636
658
  }
637
659
  const entities = await this.driver.getEntities(
@@ -656,13 +678,13 @@ export default class Nymph {
656
678
  public async deleteEntity(entity: EntityInterface): Promise<boolean> {
657
679
  for (let callback of this.beforeDeleteEntityCallbacks) {
658
680
  if (callback) {
659
- callback(this, entity);
681
+ await callback(this, entity);
660
682
  }
661
683
  }
662
684
  const result = this.driver.deleteEntity(entity);
663
685
  for (let callback of this.afterDeleteEntityCallbacks) {
664
686
  if (callback) {
665
- callback(this, result);
687
+ await callback(this, result);
666
688
  }
667
689
  }
668
690
  return await result;
@@ -681,13 +703,13 @@ export default class Nymph {
681
703
  ): Promise<boolean> {
682
704
  for (let callback of this.beforeDeleteEntityByIDCallbacks) {
683
705
  if (callback) {
684
- callback(this, guid, className);
706
+ await callback(this, guid, className);
685
707
  }
686
708
  }
687
709
  const result = this.driver.deleteEntityByID(guid, className);
688
710
  for (let callback of this.afterDeleteEntityByIDCallbacks) {
689
711
  if (callback) {
690
- callback(this, result);
712
+ await callback(this, result);
691
713
  }
692
714
  }
693
715
  return await result;
@@ -24,12 +24,22 @@ export type NymphEventType =
24
24
  export type NymphConnectCallback = (
25
25
  nymph: Nymph,
26
26
  result: Promise<boolean>
27
- ) => void;
27
+ ) => Promise<void>;
28
28
  export type NymphDisconnectCallback = (
29
29
  nymph: Nymph,
30
30
  result: Promise<boolean>
31
- ) => void;
32
- /** The NymphQueryCallback will be called on both top level and qref queries. */
31
+ ) => Promise<void>;
32
+ /**
33
+ * The NymphQueryCallback will be called on both top level and qref queries.
34
+ *
35
+ * This is the only callback that is not asynchronous.
36
+ *
37
+ * This also isn't necessarily run on every "query". It is run before a database
38
+ * query for an entity, but it is not run during PubSub entity change
39
+ * propagation. Therefore, it shouldn't do anything _entity_ specific, instead
40
+ * it should be _query_ specific. For example, throw an error if the user is not
41
+ * permitted to run the query at all.
42
+ */
33
43
  export type NymphQueryCallback = (
34
44
  nymph: Nymph,
35
45
  options: Options,
@@ -39,65 +49,71 @@ export type NymphBeforeGetEntityCallback = (
39
49
  nymph: Nymph,
40
50
  options: Options,
41
51
  selectors: Selector[]
42
- ) => void;
52
+ ) => Promise<void>;
43
53
  export type NymphBeforeGetEntitiesCallback = (
44
54
  nymph: Nymph,
45
55
  options: Options,
46
56
  selectors: Selector[]
47
- ) => void;
57
+ ) => Promise<void>;
48
58
  export type NymphBeforeSaveEntityCallback = (
49
59
  nymph: Nymph,
50
60
  entity: EntityInterface
51
- ) => void;
61
+ ) => Promise<void>;
52
62
  export type NymphAfterSaveEntityCallback = (
53
63
  nymph: Nymph,
54
64
  result: Promise<boolean>
55
- ) => void;
65
+ ) => Promise<void>;
56
66
  export type NymphBeforeDeleteEntityCallback = (
57
67
  nymph: Nymph,
58
68
  entity: EntityInterface
59
- ) => void;
69
+ ) => Promise<void>;
60
70
  export type NymphAfterDeleteEntityCallback = (
61
71
  nymph: Nymph,
62
72
  result: Promise<boolean>
63
- ) => void;
73
+ ) => Promise<void>;
64
74
  export type NymphBeforeDeleteEntityByIDCallback = (
65
75
  nymph: Nymph,
66
76
  guid: string,
67
77
  className?: string
68
- ) => void;
78
+ ) => Promise<void>;
69
79
  export type NymphAfterDeleteEntityByIDCallback = (
70
80
  nymph: Nymph,
71
81
  result: Promise<boolean>
72
- ) => void;
73
- export type NymphBeforeNewUIDCallback = (nymph: Nymph, name: string) => void;
82
+ ) => Promise<void>;
83
+ export type NymphBeforeNewUIDCallback = (
84
+ nymph: Nymph,
85
+ name: string
86
+ ) => Promise<void>;
74
87
  export type NymphAfterNewUIDCallback = (
75
88
  nymph: Nymph,
76
89
  result: Promise<number | null>
77
- ) => void;
90
+ ) => Promise<void>;
78
91
  export type NymphBeforeSetUIDCallback = (
79
92
  nymph: Nymph,
80
93
  name: string,
81
94
  value: number
82
- ) => void;
95
+ ) => Promise<void>;
83
96
  export type NymphAfterSetUIDCallback = (
84
97
  nymph: Nymph,
85
98
  result: Promise<boolean>
86
- ) => void;
99
+ ) => Promise<void>;
87
100
  export type NymphBeforeRenameUIDCallback = (
88
101
  nymph: Nymph,
89
102
  oldName: string,
90
103
  newName: string
91
- ) => void;
104
+ ) => Promise<void>;
92
105
  export type NymphAfterRenameUIDCallback = (
93
106
  nymph: Nymph,
94
107
  result: Promise<boolean>
95
- ) => void;
96
- export type NymphBeforeDeleteUIDCallback = (nymph: Nymph, name: string) => void;
108
+ ) => Promise<void>;
109
+ export type NymphBeforeDeleteUIDCallback = (
110
+ nymph: Nymph,
111
+ name: string
112
+ ) => Promise<void>;
97
113
  export type NymphAfterDeleteUIDCallback = (
98
114
  nymph: Nymph,
99
115
  result: Promise<boolean>
100
- ) => void;
116
+ ) => Promise<void>;
101
117
 
102
118
  export type Options<T extends EntityConstructor = EntityConstructor> = {
103
119
  class?: T;
@@ -54,7 +54,18 @@ export default abstract class NymphDriver {
54
54
  */
55
55
  private putDataCounter = 0;
56
56
 
57
+ /**
58
+ * This is used internally by Nymph. Don't call it yourself.
59
+ *
60
+ * @returns A clone of this instance.
61
+ */
62
+ abstract clone(): NymphDriver;
63
+
64
+ /**
65
+ * Connect to the data store.
66
+ */
57
67
  abstract connect(): Promise<boolean>;
68
+
58
69
  abstract isConnected(): boolean;
59
70
  abstract startTransaction(name: string): Promise<Nymph>;
60
71
  abstract commit(name: string): Promise<boolean>;
@@ -66,7 +77,12 @@ export default abstract class NymphDriver {
66
77
  ): Promise<boolean>;
67
78
  abstract deleteEntityByID(guid: string, className?: string): Promise<boolean>;
68
79
  abstract deleteUID(name: string): Promise<boolean>;
80
+
81
+ /**
82
+ * Disconnect from the data store.
83
+ */
69
84
  abstract disconnect(): Promise<boolean>;
85
+
70
86
  protected abstract exportEntities(
71
87
  writeLine: (line: string) => void
72
88
  ): Promise<void>;
@@ -283,7 +299,7 @@ export default abstract class NymphDriver {
283
299
  guid: string | null = null,
284
300
  tags: string[] | null = null
285
301
  ) {
286
- const formattedSelectors = this.formatSelectors(selectors);
302
+ const formattedSelectors = this.formatSelectors(selectors).selectors;
287
303
 
288
304
  for (const curSelector of formattedSelectors) {
289
305
  const type = curSelector.type;
@@ -575,8 +591,12 @@ export default abstract class NymphDriver {
575
591
  public formatSelectors(
576
592
  selectors: Selector[],
577
593
  options: Options = {}
578
- ): FormattedSelector[] {
594
+ ): {
595
+ selectors: FormattedSelector[];
596
+ qrefs: [Options, ...FormattedSelector[]][];
597
+ } {
579
598
  const newSelectors: FormattedSelector[] = [];
599
+ const qrefs: [Options, ...FormattedSelector[]][] = [];
580
600
 
581
601
  for (const curSelector of selectors) {
582
602
  const newSelector: FormattedSelector = {
@@ -615,13 +635,18 @@ export default abstract class NymphDriver {
615
635
  source: options.source,
616
636
  };
617
637
  const newSelectors = this.formatSelectors(qrefSelectors, options);
618
- this.nymph.runQueryCallbacks(newOptions, newSelectors);
619
- formatArr[i] = [name, [newOptions, ...newSelectors]];
638
+ qrefs.push(
639
+ [newOptions, ...newSelectors.selectors],
640
+ ...newSelectors.qrefs
641
+ );
642
+ formatArr[i] = [name, [newOptions, ...newSelectors.selectors]];
620
643
  }
621
644
  newSelector[key] = formatArr;
622
645
  } else if (key === 'selector' || key === '!selector') {
623
646
  const tmpArr = (Array.isArray(value) ? value : [value]) as Selector[];
624
- newSelector[key] = this.formatSelectors(tmpArr, options);
647
+ const newSelectors = this.formatSelectors(tmpArr, options);
648
+ newSelector[key] = newSelectors.selectors;
649
+ qrefs.push(...newSelectors.qrefs);
625
650
  } else if (!Array.isArray(value)) {
626
651
  // @ts-ignore: ts doesn't know what value is here.
627
652
  newSelector[key] = [[value]];
@@ -655,7 +680,7 @@ export default abstract class NymphDriver {
655
680
  newSelectors.push(newSelector);
656
681
  }
657
682
 
658
- return newSelectors;
683
+ return { selectors: newSelectors, qrefs };
659
684
  }
660
685
 
661
686
  protected iterateSelectorsForQuery(
@@ -774,7 +799,7 @@ export default abstract class NymphDriver {
774
799
  name: string;
775
800
  svalue: string;
776
801
  }
777
- ): { result: any; process: () => number };
802
+ ): { result: any; process: () => number | Error };
778
803
  protected getEntitesRowLike<T extends EntityConstructor = EntityConstructor>(
779
804
  options: Options<T> & { return: 'guid' },
780
805
  selectors: Selector[],
@@ -798,7 +823,7 @@ export default abstract class NymphDriver {
798
823
  name: string;
799
824
  svalue: string;
800
825
  }
801
- ): { result: any; process: () => string[] };
826
+ ): { result: any; process: () => string[] | Error };
802
827
  protected getEntitesRowLike<T extends EntityConstructor = EntityConstructor>(
803
828
  options: Options<T>,
804
829
  selectors: Selector[],
@@ -822,7 +847,7 @@ export default abstract class NymphDriver {
822
847
  name: string;
823
848
  svalue: string;
824
849
  }
825
- ): { result: any; process: () => ReturnType<T['factorySync']>[] };
850
+ ): { result: any; process: () => ReturnType<T['factorySync']>[] | Error };
826
851
  protected getEntitesRowLike<T extends EntityConstructor = EntityConstructor>(
827
852
  options: Options<T>,
828
853
  selectors: Selector[],
@@ -848,7 +873,7 @@ export default abstract class NymphDriver {
848
873
  }
849
874
  ): {
850
875
  result: any;
851
- process: () => ReturnType<T['factorySync']>[] | string[] | number;
876
+ process: () => ReturnType<T['factorySync']>[] | string[] | number | Error;
852
877
  } {
853
878
  if (!this.isConnected()) {
854
879
  throw new UnableToConnectError('not connected to DB');
@@ -918,78 +943,95 @@ export default abstract class NymphDriver {
918
943
  }
919
944
  }
920
945
 
921
- const formattedSelectors = this.formatSelectors(selectors, options);
922
- this.nymph.runQueryCallbacks(options, formattedSelectors);
923
- const { result } = performQueryCallback(options, formattedSelectors, etype);
946
+ try {
947
+ const formattedSelectors = this.formatSelectors(selectors, options);
948
+ this.nymph.runQueryCallbacks(options, formattedSelectors.selectors);
949
+ for (let qref of formattedSelectors.qrefs) {
950
+ const [options, ...selectors] = qref;
951
+ this.nymph.runQueryCallbacks(options, selectors);
952
+ }
953
+ const { result } = performQueryCallback(
954
+ options,
955
+ formattedSelectors.selectors,
956
+ etype
957
+ );
924
958
 
925
- return {
926
- result,
927
- process: () => {
928
- let row = rowFetchCallback();
929
- if (options.return === 'count') {
930
- while (row != null) {
931
- count += getCountCallback(row);
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 :)
959
+ return {
960
+ result,
961
+ process: () => {
962
+ let row = rowFetchCallback();
963
+ if (options.return === 'count') {
964
+ while (row != null) {
965
+ count += getCountCallback(row);
961
966
  row = rowFetchCallback();
962
967
  }
963
- const entity = EntityClass.factorySync() as ReturnType<
964
- T['factorySync']
965
- >;
966
- entity.$nymph = this.nymph;
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.');
968
+ } else if (options.return === 'guid') {
969
+ while (row != null) {
970
+ (entities as string[]).push(getGUIDCallback(row));
971
+ row = rowFetchCallback();
977
972
  }
978
- entity.$putData(data, sdata);
979
- this.putDataCounter--;
980
- if (this.nymph.config.cache) {
981
- this.pushCache(guid, cdate, mdate, tags, data, sdata);
973
+ } else {
974
+ while (row != null) {
975
+ const guid = getGUIDCallback(row);
976
+ const tagsAndDates = getTagsAndDatesCallback(row);
977
+ const tags = tagsAndDates.tags;
978
+ const cdate = tagsAndDates.cdate;
979
+ const mdate = tagsAndDates.mdate;
980
+ let dataNameAndSValue = getDataNameAndSValueCallback(row);
981
+ // Data.
982
+ const data: EntityData = {};
983
+ // Serialized data.
984
+ const sdata: SerializedEntityData = {};
985
+ if (dataNameAndSValue.name !== '') {
986
+ // This do will keep going and adding the data until the
987
+ // next entity is reached. $row will end on the next entity.
988
+ do {
989
+ dataNameAndSValue = getDataNameAndSValueCallback(row);
990
+ sdata[dataNameAndSValue.name] = dataNameAndSValue.svalue;
991
+ row = rowFetchCallback();
992
+ } while (row != null && getGUIDCallback(row) === guid);
993
+ } else {
994
+ // Make sure that $row is incremented :)
995
+ row = rowFetchCallback();
996
+ }
997
+ const entity = EntityClass.factorySync() as ReturnType<
998
+ T['factorySync']
999
+ >;
1000
+ entity.$nymph = this.nymph;
1001
+ if (options.skipAc != null) {
1002
+ entity.$useSkipAc(!!options.skipAc);
1003
+ }
1004
+ entity.guid = guid;
1005
+ entity.cdate = cdate;
1006
+ entity.mdate = mdate;
1007
+ entity.tags = tags;
1008
+ this.putDataCounter++;
1009
+ if (this.putDataCounter == 100) {
1010
+ throw new Error('Infinite loop detected in Entity loading.');
1011
+ }
1012
+ entity.$putData(data, sdata);
1013
+ this.putDataCounter--;
1014
+ if (this.nymph.config.cache) {
1015
+ this.pushCache(guid, cdate, mdate, tags, data, sdata);
1016
+ }
1017
+ // @ts-ignore: ts doesn't know the return type here.
1018
+ entities.push(entity);
982
1019
  }
983
- // @ts-ignore: ts doesn't know the return type here.
984
- entities.push(entity);
985
1020
  }
986
- }
987
1021
 
988
- freeResultCallback();
1022
+ freeResultCallback();
989
1023
 
990
- return options.return === 'count' ? count : entities;
991
- },
992
- };
1024
+ return options.return === 'count' ? count : entities;
1025
+ },
1026
+ };
1027
+ } catch (e: any) {
1028
+ return {
1029
+ result: Promise.resolve(e),
1030
+ process: () => {
1031
+ return e;
1032
+ },
1033
+ };
1034
+ }
993
1035
  }
994
1036
 
995
1037
  protected async saveEntityRowLike(