@nymphjs/nymph 1.0.0-beta.4 → 1.0.0-beta.41

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 (48) hide show
  1. package/CHANGELOG.md +195 -0
  2. package/README.md +12 -12
  3. package/dist/Entity.d.ts +24 -3
  4. package/dist/Entity.js +194 -86
  5. package/dist/Entity.js.map +1 -1
  6. package/dist/Entity.test.js +34 -17
  7. package/dist/Entity.test.js.map +1 -1
  8. package/dist/Entity.types.d.ts +3 -1
  9. package/dist/Nymph.d.ts +22 -7
  10. package/dist/Nymph.js +214 -68
  11. package/dist/Nymph.js.map +1 -1
  12. package/dist/Nymph.types.d.ts +18 -5
  13. package/dist/Nymph.types.js +1 -1
  14. package/dist/Nymph.types.js.map +1 -1
  15. package/dist/driver/NymphDriver.d.ts +1 -22
  16. package/dist/driver/NymphDriver.js +2 -16
  17. package/dist/driver/NymphDriver.js.map +1 -1
  18. package/dist/driver/testArtifacts.js +208 -159
  19. package/dist/driver/testArtifacts.js.map +1 -1
  20. package/dist/errors/InvalidStateError.d.ts +3 -0
  21. package/dist/errors/InvalidStateError.js +11 -0
  22. package/dist/errors/InvalidStateError.js.map +1 -0
  23. package/dist/errors/index.d.ts +1 -0
  24. package/dist/errors/index.js +1 -0
  25. package/dist/errors/index.js.map +1 -1
  26. package/dist/testArtifacts.d.ts +10 -3
  27. package/dist/testArtifacts.js +20 -11
  28. package/dist/testArtifacts.js.map +1 -1
  29. package/dist/testMocks.d.ts +10 -7
  30. package/dist/testMocks.js +22 -10
  31. package/dist/testMocks.js.map +1 -1
  32. package/dist/utils.d.ts +1 -1
  33. package/dist/utils.js +11 -5
  34. package/dist/utils.js.map +1 -1
  35. package/package.json +13 -13
  36. package/src/Entity.test.ts +31 -9
  37. package/src/Entity.ts +289 -113
  38. package/src/Entity.types.ts +13 -1
  39. package/src/Nymph.ts +386 -82
  40. package/src/Nymph.types.ts +76 -5
  41. package/src/driver/NymphDriver.ts +19 -85
  42. package/src/driver/testArtifacts.ts +125 -44
  43. package/src/errors/InvalidStateError.ts +6 -0
  44. package/src/errors/index.ts +2 -0
  45. package/src/testArtifacts.ts +28 -10
  46. package/src/testMocks.ts +38 -21
  47. package/src/utils.ts +21 -7
  48. package/typedoc.json +4 -0
@@ -9,18 +9,31 @@ export type NymphEventType =
9
9
  | 'beforeGetEntities'
10
10
  | 'beforeSaveEntity'
11
11
  | 'afterSaveEntity'
12
+ | 'failedSaveEntity'
12
13
  | 'beforeDeleteEntity'
13
14
  | 'afterDeleteEntity'
15
+ | 'failedDeleteEntity'
14
16
  | 'beforeDeleteEntityByID'
15
17
  | 'afterDeleteEntityByID'
18
+ | 'failedDeleteEntityByID'
16
19
  | 'beforeNewUID'
17
20
  | 'afterNewUID'
21
+ | 'failedNewUID'
18
22
  | 'beforeSetUID'
19
23
  | 'afterSetUID'
24
+ | 'failedSetUID'
20
25
  | 'beforeRenameUID'
21
26
  | 'afterRenameUID'
27
+ | 'failedRenameUID'
22
28
  | 'beforeDeleteUID'
23
- | 'afterDeleteUID';
29
+ | 'afterDeleteUID'
30
+ | 'failedDeleteUID'
31
+ | 'beforeStartTransaction'
32
+ | 'afterStartTransaction'
33
+ | 'beforeCommitTransaction'
34
+ | 'afterCommitTransaction'
35
+ | 'beforeRollbackTransaction'
36
+ | 'afterRollbackTransaction';
24
37
  export type NymphConnectCallback = (
25
38
  nymph: Nymph,
26
39
  result: Promise<boolean>
@@ -63,6 +76,10 @@ export type NymphAfterSaveEntityCallback = (
63
76
  nymph: Nymph,
64
77
  result: Promise<boolean>
65
78
  ) => Promise<void>;
79
+ export type NymphFailedSaveEntityCallback = (
80
+ nymph: Nymph,
81
+ error: any
82
+ ) => Promise<void>;
66
83
  export type NymphBeforeDeleteEntityCallback = (
67
84
  nymph: Nymph,
68
85
  entity: EntityInterface
@@ -71,6 +88,10 @@ export type NymphAfterDeleteEntityCallback = (
71
88
  nymph: Nymph,
72
89
  result: Promise<boolean>
73
90
  ) => Promise<void>;
91
+ export type NymphFailedDeleteEntityCallback = (
92
+ nymph: Nymph,
93
+ error: any
94
+ ) => Promise<void>;
74
95
  export type NymphBeforeDeleteEntityByIDCallback = (
75
96
  nymph: Nymph,
76
97
  guid: string,
@@ -80,6 +101,10 @@ export type NymphAfterDeleteEntityByIDCallback = (
80
101
  nymph: Nymph,
81
102
  result: Promise<boolean>
82
103
  ) => Promise<void>;
104
+ export type NymphFailedDeleteEntityByIDCallback = (
105
+ nymph: Nymph,
106
+ error: any
107
+ ) => Promise<void>;
83
108
  export type NymphBeforeNewUIDCallback = (
84
109
  nymph: Nymph,
85
110
  name: string
@@ -88,6 +113,10 @@ export type NymphAfterNewUIDCallback = (
88
113
  nymph: Nymph,
89
114
  result: Promise<number | null>
90
115
  ) => Promise<void>;
116
+ export type NymphFailedNewUIDCallback = (
117
+ nymph: Nymph,
118
+ error: any
119
+ ) => Promise<void>;
91
120
  export type NymphBeforeSetUIDCallback = (
92
121
  nymph: Nymph,
93
122
  name: string,
@@ -97,6 +126,10 @@ export type NymphAfterSetUIDCallback = (
97
126
  nymph: Nymph,
98
127
  result: Promise<boolean>
99
128
  ) => Promise<void>;
129
+ export type NymphFailedSetUIDCallback = (
130
+ nymph: Nymph,
131
+ error: any
132
+ ) => Promise<void>;
100
133
  export type NymphBeforeRenameUIDCallback = (
101
134
  nymph: Nymph,
102
135
  oldName: string,
@@ -106,6 +139,10 @@ export type NymphAfterRenameUIDCallback = (
106
139
  nymph: Nymph,
107
140
  result: Promise<boolean>
108
141
  ) => Promise<void>;
142
+ export type NymphFailedRenameUIDCallback = (
143
+ nymph: Nymph,
144
+ error: any
145
+ ) => Promise<void>;
109
146
  export type NymphBeforeDeleteUIDCallback = (
110
147
  nymph: Nymph,
111
148
  name: string
@@ -114,13 +151,44 @@ export type NymphAfterDeleteUIDCallback = (
114
151
  nymph: Nymph,
115
152
  result: Promise<boolean>
116
153
  ) => Promise<void>;
154
+ export type NymphFailedDeleteUIDCallback = (
155
+ nymph: Nymph,
156
+ error: any
157
+ ) => Promise<void>;
158
+ export type NymphBeforeStartTransactionCallback = (
159
+ nymph: Nymph,
160
+ name: string
161
+ ) => Promise<void>;
162
+ export type NymphAfterStartTransactionCallback = (
163
+ nymph: Nymph,
164
+ name: string,
165
+ result: Nymph
166
+ ) => Promise<void>;
167
+ export type NymphBeforeCommitTransactionCallback = (
168
+ nymph: Nymph,
169
+ name: string
170
+ ) => Promise<void>;
171
+ export type NymphAfterCommitTransactionCallback = (
172
+ nymph: Nymph,
173
+ name: string,
174
+ result: boolean
175
+ ) => Promise<void>;
176
+ export type NymphBeforeRollbackTransactionCallback = (
177
+ nymph: Nymph,
178
+ name: string
179
+ ) => Promise<void>;
180
+ export type NymphAfterRollbackTransactionCallback = (
181
+ nymph: Nymph,
182
+ name: string,
183
+ result: boolean
184
+ ) => Promise<void>;
117
185
 
118
186
  export type Options<T extends EntityConstructor = EntityConstructor> = {
119
187
  class?: T;
120
188
  limit?: number;
121
189
  offset?: number;
122
190
  reverse?: boolean;
123
- sort?: 'cdate' | 'mdate';
191
+ sort?: 'cdate' | 'mdate' | string;
124
192
  return?: 'entity' | 'guid' | 'count';
125
193
  source?: string;
126
194
  skipCache?: boolean;
@@ -308,10 +376,13 @@ export interface TilmeldInterface {
308
376
  response: any;
309
377
  init(nymph: Nymph): void;
310
378
  clone(): TilmeldInterface;
311
- gatekeeper(ability?: string): boolean;
312
- authenticate(): void;
379
+ gatekeeper(ability?: string): Promise<boolean>;
380
+ authenticate(): Promise<boolean>;
313
381
  clearSession(): void;
314
382
  extractToken(token: string): Promise<any>;
315
383
  fillSession(user: any): void;
316
- checkClientUIDPermissions(name: string, type?: TilmeldAccessLevels): boolean;
384
+ checkClientUIDPermissions(
385
+ name: string,
386
+ type?: TilmeldAccessLevels
387
+ ): Promise<boolean>;
317
388
  }
@@ -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>;
@@ -86,30 +102,6 @@ export default abstract class NymphDriver {
86
102
  options?: Options<T>,
87
103
  ...selectors: Selector[]
88
104
  ): 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;
113
105
  abstract getUID(name: string): Promise<number | null>;
114
106
  abstract import(filename: string): Promise<boolean>;
115
107
  abstract newUID(name: string): Promise<number | null>;
@@ -609,10 +601,9 @@ export default abstract class NymphDriver {
609
601
  for (let i = 0; i < tmpArr.length; i++) {
610
602
  const name = tmpArr[i][0];
611
603
  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');
604
+ const QrefEntityClass = qrefOptions.class
605
+ ? this.nymph.getEntityClass(qrefOptions.class)
606
+ : this.nymph.getEntityClass('Entity');
616
607
  const newOptions = {
617
608
  ...qrefOptions,
618
609
  class: QrefEntityClass,
@@ -703,63 +694,6 @@ export default abstract class NymphDriver {
703
694
  return queryParts;
704
695
  }
705
696
 
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
697
  protected getEntitesRowLike<T extends EntityConstructor = EntityConstructor>(
764
698
  options: Options<T> & { return: 'count' },
765
699
  selectors: Selector[],
@@ -3,16 +3,22 @@ import strtotime from 'locutus/php/datetime/strtotime';
3
3
  import { guid } from '@nymphjs/guid';
4
4
 
5
5
  import type Nymph from '../Nymph';
6
- import { TestBModel, TestModel, TestModelData } from '../testArtifacts';
6
+ import {
7
+ TestBModel as TestBModelClass,
8
+ TestModel as TestModelClass,
9
+ TestModelData,
10
+ TestEmptyModel as TestEmptyModelClass,
11
+ } from '../testArtifacts';
7
12
 
8
13
  export function QueriesTest(
9
14
  nymph: Nymph,
10
15
  it: (name: string, fn: () => void) => void
11
16
  ) {
12
- nymph.addEntityClass(TestModel);
13
- nymph.addEntityClass(TestBModel);
17
+ const TestModel = nymph.addEntityClass(TestModelClass);
18
+ const TestBModel = nymph.addEntityClass(TestBModelClass);
19
+ const TestEmptyModel = nymph.addEntityClass(TestEmptyModelClass);
14
20
 
15
- let testEntity: TestModel & TestModelData;
21
+ let testEntity: TestModelClass & TestModelData;
16
22
  let testGuid: string;
17
23
  let refGuid: string;
18
24
  let createdMultiple = false;
@@ -35,7 +41,10 @@ export function QueriesTest(
35
41
 
36
42
  This one's email address is nice_hat-wednesday+newyork@im-a-hat.hat.
37
43
  This one's phone number is (555) 555-1818.
38
- This one's zip code is 92064.`;
44
+ This one's zip code is 92064.
45
+ This one's favorite emojis are 🔥❤️😊😂⭐🤔
46
+
47
+ These hats are absolutely fantastic.`;
39
48
  testEntity.number = 30;
40
49
  testEntity.numberString = '30';
41
50
  testEntity.timestamp = Date.now();
@@ -43,7 +52,7 @@ This one's zip code is 92064.`;
43
52
  expect(testEntity.guid).not.toBeNull();
44
53
  testGuid = testEntity.guid as string;
45
54
 
46
- const entityReferenceTest: TestModel & TestModelData = new TestModel();
55
+ const entityReferenceTest: TestModelClass & TestModelData = new TestModel();
47
56
  entityReferenceTest.string = 'wrong';
48
57
  entityReferenceTest.timestamp = strtotime('-2 days') * 1000;
49
58
  expect(await entityReferenceTest.$save()).toEqual(true);
@@ -55,19 +64,11 @@ This one's zip code is 92064.`;
55
64
  entityReferenceTest.test = 'good';
56
65
  expect(await entityReferenceTest.$save()).toEqual(true);
57
66
 
58
- // Test synchronous getEntity.
59
- testEntity = nymph.driver.getEntitySync(
60
- { class: TestModel },
61
- testGuid
62
- ) as TestModel & TestModelData;
63
- expect(testEntity).toBeInstanceOf(TestModel);
64
- expect(testEntity.guid).toEqual(testGuid);
65
-
66
67
  // Test asynchronous getEntity.
67
68
  testEntity = (await nymph.getEntity(
68
69
  { class: TestModel },
69
70
  testGuid
70
- )) as TestModel & TestModelData;
71
+ )) as TestModelClass & TestModelData;
71
72
  expect(testEntity).toBeInstanceOf(TestModel);
72
73
  expect(testEntity.guid).toEqual(testGuid);
73
74
  }
@@ -80,6 +81,8 @@ This one's zip code is 92064.`;
80
81
  for (let i = 0; i < 100; i++) {
81
82
  const testEntity = await TestModel.factory();
82
83
  testEntity.name = `Multi Test ${i}`;
84
+ // Reverse the number for sort testing.
85
+ testEntity.number = 100 - i;
83
86
  testEntity.$removeTag('test');
84
87
  testEntity.$addTag('multiTest');
85
88
  expect(await testEntity.$save()).toEqual(true);
@@ -104,6 +107,13 @@ This one's zip code is 92064.`;
104
107
  await createTestEntities();
105
108
  });
106
109
 
110
+ it("doesn't create empty entity", async () => {
111
+ const testEntity = await TestEmptyModel.factory();
112
+
113
+ expect(await testEntity.$save()).toBeFalsy();
114
+ expect(testEntity.guid).toBeNull();
115
+ });
116
+
107
117
  it('update mdate on save', async () => {
108
118
  await createTestEntities();
109
119
 
@@ -191,12 +201,12 @@ This one's zip code is 92064.`;
191
201
  testEntity = (await transaction.getEntity(
192
202
  { class: TestModel },
193
203
  testGuid
194
- )) as TestModel & TestModelData;
204
+ )) as TestModelClass & TestModelData;
195
205
 
196
206
  expect(testEntity.guid).toEqual(testGuid);
197
207
 
198
- // Start a new transaction.
199
- transaction = await nymph.startTransaction('test');
208
+ // Start a new transaction with a dot in the name.
209
+ transaction = await nymph.startTransaction('test.1');
200
210
  testEntity.$nymph = transaction;
201
211
 
202
212
  // Verify in a transaction.
@@ -207,7 +217,7 @@ This one's zip code is 92064.`;
207
217
  expect(await testEntity.$save()).toEqual(true);
208
218
 
209
219
  // Commit the transaction.
210
- await transaction.commit('test');
220
+ await transaction.commit('test.1');
211
221
  testEntity.$nymph = nymph;
212
222
 
213
223
  // Verify not in a transaction.
@@ -1161,9 +1171,11 @@ This one's zip code is 92064.`;
1161
1171
  await createTestEntities();
1162
1172
 
1163
1173
  // Testing referenced entities...
1174
+ await testEntity.reference?.$wake();
1164
1175
  expect(testEntity.reference?.test).toEqual('good');
1165
1176
 
1166
1177
  // Testing referenced entity arrays...
1178
+ await Promise.all(testEntity.refArray?.map((e) => e.$wake()) || []);
1167
1179
  expect(testEntity.refArray?.[0].test).toEqual('good');
1168
1180
  });
1169
1181
 
@@ -1633,26 +1645,23 @@ This one's zip code is 92064.`;
1633
1645
  expect(testEntity.$inArray(resultEntity)).toEqual(false);
1634
1646
  });
1635
1647
 
1636
- it('sort option', async () => {
1648
+ it('cdate and mdate sort option', async () => {
1637
1649
  await createMultipleTestEntities();
1638
1650
 
1639
- for (const sort of ['cdate', 'mdate']) {
1651
+ for (const sort of ['cdate', 'mdate'] as ('cdate' | 'mdate')[]) {
1640
1652
  // Retrieving entities sorted...
1641
- let resultEntities = await nymph.getEntities({
1642
- class: TestModel,
1643
- sort: sort as 'cdate' | 'mdate',
1644
- });
1653
+ let resultEntities = await nymph.getEntities({ class: TestModel, sort });
1645
1654
  expect(resultEntities.length).toBeGreaterThan(100);
1646
1655
  for (let i = 0; i < resultEntities.length - 1; i++) {
1647
- expect(
1648
- resultEntities[i + 1][sort as 'cdate' | 'mdate']
1649
- ).toBeGreaterThan(resultEntities[i][sort as 'cdate' | 'mdate'] ?? 0);
1656
+ expect(resultEntities[i + 1][sort]).toBeGreaterThan(
1657
+ resultEntities[i][sort] ?? 0
1658
+ );
1650
1659
  }
1651
1660
 
1652
1661
  // And test the same with guid return...
1653
1662
  let resultGuids = await nymph.getEntities({
1654
1663
  class: TestModel,
1655
- sort: sort as 'cdate' | 'mdate',
1664
+ sort,
1656
1665
  return: 'guid',
1657
1666
  });
1658
1667
  expect(resultGuids.length).toBeGreaterThan(100);
@@ -1663,48 +1672,121 @@ This one's zip code is 92064.`;
1663
1672
  // Retrieving entities reverse sorted...
1664
1673
  resultEntities = await nymph.getEntities({
1665
1674
  class: TestModel,
1666
- sort: sort as 'cdate' | 'mdate',
1675
+ sort,
1667
1676
  reverse: true,
1668
1677
  });
1669
1678
  expect(resultEntities.length).toBeGreaterThan(100);
1670
1679
  for (let i = 0; i < resultEntities.length - 1; i++) {
1671
- expect(resultEntities[i + 1][sort as 'cdate' | 'mdate']).toBeLessThan(
1672
- resultEntities[i][sort as 'cdate' | 'mdate'] ?? 0
1680
+ expect(resultEntities[i + 1][sort]).toBeLessThan(
1681
+ resultEntities[i][sort] ?? 0
1673
1682
  );
1674
1683
  }
1675
1684
 
1676
1685
  // And again with other selectors.
1677
1686
  // Retrieving entities sorted...
1678
1687
  resultEntities = await nymph.getEntities(
1679
- { class: TestModel, sort: sort as 'cdate' | 'mdate' },
1688
+ { class: TestModel, sort },
1680
1689
  { type: '&', match: ['name', '^Multi Test '] }
1681
1690
  );
1682
1691
  expect(resultEntities.length).toEqual(100);
1683
1692
  for (let i = 0; i < resultEntities.length - 1; i++) {
1684
- expect(
1685
- resultEntities[i + 1][sort as 'cdate' | 'mdate']
1686
- ).toBeGreaterThan(resultEntities[i][sort as 'cdate' | 'mdate'] ?? 0);
1693
+ expect(resultEntities[i + 1][sort]).toBeGreaterThan(
1694
+ resultEntities[i][sort] ?? 0
1695
+ );
1687
1696
  }
1688
1697
 
1689
1698
  // Retrieving entities reverse sorted...
1690
1699
  resultEntities = await nymph.getEntities(
1691
- { class: TestModel, sort: sort as 'cdate' | 'mdate', reverse: true },
1700
+ { class: TestModel, sort, reverse: true },
1692
1701
  { type: '&', match: ['name', '^Multi Test '] }
1693
1702
  );
1694
1703
  expect(resultEntities.length).toEqual(100);
1695
1704
  for (let i = 0; i < resultEntities.length - 1; i++) {
1696
- expect(resultEntities[i + 1][sort as 'cdate' | 'mdate']).toBeLessThan(
1697
- resultEntities[i][sort as 'cdate' | 'mdate'] ?? 0
1705
+ expect(resultEntities[i + 1][sort]).toBeLessThan(
1706
+ resultEntities[i][sort] ?? 0
1698
1707
  );
1699
1708
  }
1700
1709
  }
1701
1710
  });
1702
1711
 
1712
+ it('property sort option', async () => {
1713
+ await createMultipleTestEntities();
1714
+
1715
+ // Retrieving entities sorted...
1716
+ let resultEntities = await nymph.getEntities({
1717
+ class: TestModel,
1718
+ sort: 'number',
1719
+ });
1720
+ expect(resultEntities.length).toBeGreaterThan(100);
1721
+ for (let i = 0; i < resultEntities.length - 1; i++) {
1722
+ expect(resultEntities[i + 1].number ?? Infinity).toBeGreaterThanOrEqual(
1723
+ resultEntities[i].number ?? 0
1724
+ );
1725
+ }
1726
+
1727
+ // And test the same with guid return...
1728
+ let resultGuids = await nymph.getEntities({
1729
+ class: TestModel,
1730
+ sort: 'number',
1731
+ return: 'guid',
1732
+ });
1733
+ expect(resultGuids.length).toBeGreaterThan(100);
1734
+ for (let i = 0; i < resultGuids.length - 1; i++) {
1735
+ expect(resultGuids[i]).toEqual(resultEntities[i].guid);
1736
+ }
1737
+
1738
+ // Retrieving entities reverse sorted...
1739
+ resultEntities = await nymph.getEntities({
1740
+ class: TestModel,
1741
+ sort: 'number',
1742
+ reverse: true,
1743
+ });
1744
+ expect(resultEntities.length).toBeGreaterThan(100);
1745
+ for (let i = 0; i < resultEntities.length - 1; i++) {
1746
+ expect(resultEntities[i + 1].number ?? 0).toBeLessThanOrEqual(
1747
+ resultEntities[i].number ?? Infinity
1748
+ );
1749
+ }
1750
+
1751
+ // And again with other selectors.
1752
+ // Retrieving entities sorted...
1753
+ resultEntities = await nymph.getEntities(
1754
+ { class: TestModel, sort: 'number' },
1755
+ { type: '&', match: ['name', '^Multi Test '] }
1756
+ );
1757
+ expect(resultEntities.length).toEqual(100);
1758
+ for (let i = 0; i < resultEntities.length - 1; i++) {
1759
+ expect(resultEntities[i + 1].number ?? Infinity).toBeGreaterThan(
1760
+ resultEntities[i].number ?? 0
1761
+ );
1762
+ expect(resultEntities[i].name).toEqual(
1763
+ `Multi Test ${100 - (resultEntities[i].number ?? 0)}`
1764
+ );
1765
+ }
1766
+
1767
+ // Retrieving entities reverse sorted...
1768
+ resultEntities = await nymph.getEntities(
1769
+ { class: TestModel, sort: 'number', reverse: true },
1770
+ { type: '&', match: ['name', '^Multi Test '] }
1771
+ );
1772
+ expect(resultEntities.length).toEqual(100);
1773
+ for (let i = 0; i < resultEntities.length - 1; i++) {
1774
+ expect(resultEntities[i + 1].number ?? 0).toBeLessThan(
1775
+ resultEntities[i].number ?? Infinity
1776
+ );
1777
+ expect(resultEntities[i].name).toEqual(
1778
+ `Multi Test ${100 - (resultEntities[i].number ?? 0)}`
1779
+ );
1780
+ }
1781
+ });
1782
+
1703
1783
  it('delete reference', async () => {
1704
1784
  await createTestEntities();
1705
1785
 
1706
1786
  // Deleting referenced entities...
1787
+ await testEntity.reference?.$wake();
1707
1788
  expect(await testEntity.reference?.$delete()).toEqual(true);
1789
+ await Promise.all(testEntity.refArray?.map((e) => e.$wake()) || []);
1708
1790
  expect(testEntity.reference?.guid).toBeNull();
1709
1791
  });
1710
1792
 
@@ -1728,9 +1810,6 @@ export function UIDTest(
1728
1810
  nymph: Nymph,
1729
1811
  it: (name: string, fn: () => void) => void
1730
1812
  ) {
1731
- nymph.addEntityClass(TestModel);
1732
- nymph.addEntityClass(TestBModel);
1733
-
1734
1813
  it('delete old test data', async () => {
1735
1814
  expect(await nymph.deleteUID('TestUID')).toEqual(true);
1736
1815
  expect(await nymph.deleteUID('NewUID')).toEqual(true);
@@ -1772,8 +1851,8 @@ export function ExportImportTest(
1772
1851
  nymph: Nymph,
1773
1852
  it: (name: string, fn: () => void) => void
1774
1853
  ) {
1775
- nymph.addEntityClass(TestModel);
1776
- nymph.addEntityClass(TestBModel);
1854
+ const TestModel = nymph.addEntityClass(TestModelClass);
1855
+ const TestBModel = nymph.addEntityClass(TestBModelClass);
1777
1856
 
1778
1857
  async function deleteTestData() {
1779
1858
  let all = await nymph.getEntities({ class: TestModel });
@@ -1817,9 +1896,11 @@ export function ExportImportTest(
1817
1896
  expect(model.number).toEqual(30);
1818
1897
  expect(model.timestamp).toBeGreaterThanOrEqual(strtotime('-2 minutes'));
1819
1898
 
1899
+ await model.reference?.$wake();
1820
1900
  expect(model.reference?.guid).not.toBeNull();
1821
1901
  expect(model.reference?.string).toEqual('another');
1822
1902
  expect(model.reference?.index?.match(/^\d+b$/)).toBeTruthy();
1903
+ await Promise.all(model.refArray?.map((e) => e.$wake()) || []);
1823
1904
  expect(model.refArray?.[0].guid).not.toBeNull();
1824
1905
  expect(model.refArray?.[0].guid).toEqual(model.reference?.guid);
1825
1906
  }
@@ -0,0 +1,6 @@
1
+ export class InvalidStateError extends Error {
2
+ constructor(message: string) {
3
+ super(message);
4
+ this.name = 'InvalidStateError';
5
+ }
6
+ }
@@ -8,6 +8,8 @@ export * from './EntityIsSleepingReferenceError';
8
8
 
9
9
  export * from './InvalidParametersError';
10
10
 
11
+ export * from './InvalidStateError';
12
+
11
13
  export * from './NotConfiguredError';
12
14
 
13
15
  export * from './QueryFailedError';
@@ -38,17 +38,15 @@ export class TestModel extends Entity<TestModelData> {
38
38
  return (await super.factory(guid)) as TestModel & TestModelData;
39
39
  }
40
40
 
41
- static factorySync(guid?: string): TestModel & TestModelData {
42
- return super.factorySync(guid) as TestModel & TestModelData;
41
+ static factorySync(): TestModel & TestModelData {
42
+ return super.factorySync() as TestModel & TestModelData;
43
43
  }
44
44
 
45
- constructor(guid?: string) {
46
- super(guid);
45
+ constructor() {
46
+ super();
47
47
 
48
- if (this.guid == null) {
49
- this.$addTag('test');
50
- this.$data.boolean = true;
51
- }
48
+ this.$addTag('test');
49
+ this.$data.boolean = true;
52
50
  }
53
51
 
54
52
  public $useProtectedData() {
@@ -82,7 +80,27 @@ export class TestBModel extends TestModel {
82
80
  return (await super.factory(guid)) as TestBModel & TestModelData;
83
81
  }
84
82
 
85
- static factorySync(guid?: string): TestBModel & TestModelData {
86
- return super.factorySync(guid) as TestBModel & TestModelData;
83
+ static factorySync(): TestBModel & TestModelData {
84
+ return super.factorySync() as TestBModel & TestModelData;
85
+ }
86
+ }
87
+
88
+ export type TestEmptyModelData = {};
89
+
90
+ /**
91
+ * This class is a test class that extends the Entity class.
92
+ */
93
+ export class TestEmptyModel extends Entity<TestEmptyModelData> {
94
+ static ETYPE = 'test_empty_model';
95
+ static class = 'TestEmptyModel';
96
+
97
+ static async factory(
98
+ guid?: string
99
+ ): Promise<TestEmptyModel & TestEmptyModelData> {
100
+ return (await super.factory(guid)) as TestEmptyModel & TestEmptyModelData;
101
+ }
102
+
103
+ static factorySync(): TestEmptyModel & TestEmptyModelData {
104
+ return super.factorySync() as TestEmptyModel & TestEmptyModelData;
87
105
  }
88
106
  }