@nymphjs/nymph 1.0.0-alpha.1 → 1.0.0-alpha.13

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 (47) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/README.md +17 -7
  3. package/dist/Entity.d.ts +3 -0
  4. package/dist/Entity.js +15 -17
  5. package/dist/Entity.js.map +1 -1
  6. package/dist/Entity.test.js +6 -3
  7. package/dist/Entity.test.js.map +1 -1
  8. package/dist/Entity.types.d.ts +4 -1
  9. package/dist/Nymph.d.ts +56 -53
  10. package/dist/Nymph.js +138 -67
  11. package/dist/Nymph.js.map +1 -1
  12. package/dist/Nymph.types.d.ts +49 -28
  13. package/dist/Nymph.types.js +8 -0
  14. package/dist/Nymph.types.js.map +1 -1
  15. package/dist/driver/NymphDriver.d.ts +5 -2
  16. package/dist/driver/NymphDriver.js +68 -39
  17. package/dist/driver/NymphDriver.js.map +1 -1
  18. package/dist/driver/testArtifacts.d.ts +4 -3
  19. package/dist/driver/testArtifacts.js +265 -167
  20. package/dist/driver/testArtifacts.js.map +1 -1
  21. package/dist/index.d.ts +2 -1
  22. package/dist/index.js +3 -2
  23. package/dist/index.js.map +1 -1
  24. package/dist/nymphJoiProps.d.ts +8 -0
  25. package/dist/nymphJoiProps.js +24 -0
  26. package/dist/nymphJoiProps.js.map +1 -0
  27. package/dist/testArtifacts.js +0 -3
  28. package/dist/testArtifacts.js.map +1 -1
  29. package/dist/testMocks.d.ts +14 -13
  30. package/dist/testMocks.js +21 -11
  31. package/dist/testMocks.js.map +1 -1
  32. package/dist/utils.d.ts +2 -1
  33. package/dist/utils.js +9 -10
  34. package/dist/utils.js.map +1 -1
  35. package/package.json +3 -2
  36. package/src/Entity.test.ts +7 -4
  37. package/src/Entity.ts +22 -12
  38. package/src/Entity.types.ts +10 -1
  39. package/src/Nymph.ts +203 -119
  40. package/src/Nymph.types.ts +91 -22
  41. package/src/driver/NymphDriver.ts +93 -46
  42. package/src/driver/testArtifacts.ts +307 -161
  43. package/src/index.ts +3 -1
  44. package/src/nymphJoiProps.ts +24 -0
  45. package/src/testArtifacts.ts +0 -5
  46. package/src/testMocks.ts +34 -29
  47. package/src/utils.ts +12 -10
@@ -1,8 +1,10 @@
1
1
  import { EntityConstructor, EntityInterface } from './Entity.types';
2
+ import Nymph from './Nymph';
2
3
 
3
4
  export type NymphEventType =
4
5
  | 'connect'
5
6
  | 'disconnect'
7
+ | 'query'
6
8
  | 'beforeGetEntity'
7
9
  | 'beforeGetEntities'
8
10
  | 'beforeSaveEntity'
@@ -19,38 +21,83 @@ export type NymphEventType =
19
21
  | 'afterRenameUID'
20
22
  | 'beforeDeleteUID'
21
23
  | 'afterDeleteUID';
22
- export type NymphConnectCallback = (result: Promise<boolean>) => void;
23
- export type NymphDisconnectCallback = (result: Promise<boolean>) => void;
24
+ export type NymphConnectCallback = (
25
+ nymph: Nymph,
26
+ result: Promise<boolean>
27
+ ) => void;
28
+ export type NymphDisconnectCallback = (
29
+ nymph: Nymph,
30
+ result: Promise<boolean>
31
+ ) => void;
32
+ /** The NymphQueryCallback will be called on both top level and qref queries. */
33
+ export type NymphQueryCallback = (
34
+ nymph: Nymph,
35
+ options: Options,
36
+ selectors: FormattedSelector[]
37
+ ) => void;
24
38
  export type NymphBeforeGetEntityCallback = (
39
+ nymph: Nymph,
25
40
  options: Options,
26
41
  selectors: Selector[]
27
42
  ) => void;
28
43
  export type NymphBeforeGetEntitiesCallback = (
44
+ nymph: Nymph,
29
45
  options: Options,
30
46
  selectors: Selector[]
31
47
  ) => void;
32
- export type NymphBeforeSaveEntityCallback = (entity: EntityInterface) => void;
33
- export type NymphAfterSaveEntityCallback = (result: Promise<boolean>) => void;
34
- export type NymphBeforeDeleteEntityCallback = (entity: EntityInterface) => void;
35
- export type NymphAfterDeleteEntityCallback = (result: Promise<boolean>) => void;
48
+ export type NymphBeforeSaveEntityCallback = (
49
+ nymph: Nymph,
50
+ entity: EntityInterface
51
+ ) => void;
52
+ export type NymphAfterSaveEntityCallback = (
53
+ nymph: Nymph,
54
+ result: Promise<boolean>
55
+ ) => void;
56
+ export type NymphBeforeDeleteEntityCallback = (
57
+ nymph: Nymph,
58
+ entity: EntityInterface
59
+ ) => void;
60
+ export type NymphAfterDeleteEntityCallback = (
61
+ nymph: Nymph,
62
+ result: Promise<boolean>
63
+ ) => void;
36
64
  export type NymphBeforeDeleteEntityByIDCallback = (
65
+ nymph: Nymph,
37
66
  guid: string,
38
67
  className?: string
39
68
  ) => void;
40
69
  export type NymphAfterDeleteEntityByIDCallback = (
70
+ nymph: Nymph,
71
+ result: Promise<boolean>
72
+ ) => void;
73
+ export type NymphBeforeNewUIDCallback = (nymph: Nymph, name: string) => void;
74
+ export type NymphAfterNewUIDCallback = (
75
+ nymph: Nymph,
76
+ result: Promise<number | null>
77
+ ) => void;
78
+ export type NymphBeforeSetUIDCallback = (
79
+ nymph: Nymph,
80
+ name: string,
81
+ value: number
82
+ ) => void;
83
+ export type NymphAfterSetUIDCallback = (
84
+ nymph: Nymph,
41
85
  result: Promise<boolean>
42
86
  ) => void;
43
- export type NymphBeforeNewUIDCallback = (name: string) => void;
44
- export type NymphAfterNewUIDCallback = (result: Promise<number | null>) => void;
45
- export type NymphBeforeSetUIDCallback = (name: string, value: number) => void;
46
- export type NymphAfterSetUIDCallback = (result: Promise<boolean>) => void;
47
87
  export type NymphBeforeRenameUIDCallback = (
88
+ nymph: Nymph,
48
89
  oldName: string,
49
90
  newName: string
50
91
  ) => void;
51
- export type NymphAfterRenameUIDCallback = (result: Promise<boolean>) => void;
52
- export type NymphBeforeDeleteUIDCallback = (name: string) => void;
53
- export type NymphAfterDeleteUIDCallback = (result: Promise<boolean>) => void;
92
+ export type NymphAfterRenameUIDCallback = (
93
+ nymph: Nymph,
94
+ result: Promise<boolean>
95
+ ) => void;
96
+ export type NymphBeforeDeleteUIDCallback = (nymph: Nymph, name: string) => void;
97
+ export type NymphAfterDeleteUIDCallback = (
98
+ nymph: Nymph,
99
+ result: Promise<boolean>
100
+ ) => void;
54
101
 
55
102
  export type Options<T extends EntityConstructor = EntityConstructor> = {
56
103
  class?: T;
@@ -169,7 +216,7 @@ export type Selector = {
169
216
  ref?: Clause<PrimitiveSelector['ref']>;
170
217
  '!ref'?: Clause<PrimitiveSelector['ref']>;
171
218
 
172
- qref?: [string, [Options, ...Selector[]]];
219
+ qref?: Clause<[string, [Options, ...Selector[]]]>;
173
220
  '!qref'?: Selector['qref'];
174
221
 
175
222
  selector?: Clause<Selector>;
@@ -179,17 +226,17 @@ export type Selector = {
179
226
  export type FormattedSelector = {
180
227
  type: '&' | '|' | '!&' | '!|';
181
228
 
182
- guid?: PrimitiveSelector['guid'][];
183
- '!guid'?: PrimitiveSelector['guid'][];
229
+ guid?: PrimitiveSelector['guid'][][];
230
+ '!guid'?: PrimitiveSelector['guid'][][];
184
231
 
185
- tag?: PrimitiveSelector['tag'][];
186
- '!tag'?: PrimitiveSelector['tag'][];
232
+ tag?: PrimitiveSelector['tag'][][];
233
+ '!tag'?: PrimitiveSelector['tag'][][];
187
234
 
188
- defined?: PrimitiveSelector['defined'][];
189
- '!defined'?: PrimitiveSelector['defined'][];
235
+ defined?: PrimitiveSelector['defined'][][];
236
+ '!defined'?: PrimitiveSelector['defined'][][];
190
237
 
191
- truthy?: PrimitiveSelector['truthy'][];
192
- '!truthy'?: PrimitiveSelector['truthy'][];
238
+ truthy?: PrimitiveSelector['truthy'][][];
239
+ '!truthy'?: PrimitiveSelector['truthy'][][];
193
240
 
194
241
  equal?: PrimitiveSelector['equal'][];
195
242
  '!equal'?: PrimitiveSelector['equal'][];
@@ -230,3 +277,25 @@ export type FormattedSelector = {
230
277
  selector?: FormattedSelector[];
231
278
  '!selector'?: FormattedSelector['selector'];
232
279
  };
280
+
281
+ export enum TilmeldAccessLevels {
282
+ NO_ACCESS = 0,
283
+ READ_ACCESS = 1,
284
+ WRITE_ACCESS = 2,
285
+ // Keeping 3 open in case we ever need one between write and full.
286
+ FULL_ACCESS = 4,
287
+ }
288
+
289
+ export interface TilmeldInterface {
290
+ nymph: Nymph;
291
+ request: any;
292
+ response: any;
293
+ init(nymph: Nymph): void;
294
+ clone(): TilmeldInterface;
295
+ gatekeeper(ability?: string): boolean;
296
+ authenticate(): void;
297
+ clearSession(): void;
298
+ extractToken(token: string): Promise<any>;
299
+ fillSession(user: any): void;
300
+ checkClientUIDPermissions(name: string, type?: TilmeldAccessLevels): boolean;
301
+ }
@@ -3,6 +3,7 @@ import { difference } from 'lodash';
3
3
  import ReadLines from 'n-readlines';
4
4
  import strtotime from 'locutus/php/datetime/strtotime';
5
5
 
6
+ import type Nymph from '../Nymph';
6
7
  import newGUID from '../newGUID';
7
8
  import {
8
9
  EntityConstructor,
@@ -11,7 +12,6 @@ import {
11
12
  SerializedEntityData,
12
13
  } from '../Entity.types';
13
14
  import { InvalidParametersError, UnableToConnectError } from '../errors';
14
- import Nymph from '../Nymph';
15
15
  import { Selector, Options, FormattedSelector } from '../Nymph.types';
16
16
  import { xor } from '../utils';
17
17
 
@@ -24,6 +24,14 @@ function escapeRegExp(string: string): string {
24
24
  * A Nymph database driver.
25
25
  */
26
26
  export default abstract class NymphDriver {
27
+ protected nymph: Nymph = new Proxy({} as Nymph, {
28
+ get() {
29
+ throw new Error(
30
+ 'Attempted access of Nymph instance before driver initialization!'
31
+ );
32
+ },
33
+ });
34
+
27
35
  /**
28
36
  * A cache to make entity retrieval faster.
29
37
  */
@@ -47,7 +55,7 @@ export default abstract class NymphDriver {
47
55
 
48
56
  abstract connect(): Promise<boolean>;
49
57
  abstract isConnected(): boolean;
50
- abstract startTransaction(name: string): Promise<boolean>;
58
+ abstract startTransaction(name: string): Promise<Nymph>;
51
59
  abstract commit(name: string): Promise<boolean>;
52
60
  abstract rollback(name: string): Promise<boolean>;
53
61
  abstract inTransaction(): Promise<boolean>;
@@ -98,6 +106,17 @@ export default abstract class NymphDriver {
98
106
  abstract saveEntity(entity: EntityInterface): Promise<boolean>;
99
107
  abstract setUID(name: string, value: number): Promise<boolean>;
100
108
 
109
+ /**
110
+ * Initialize the Nymph driver.
111
+ *
112
+ * This is meant to be called internally by Nymph. Don't call this directly.
113
+ *
114
+ * @param nymph The Nymph instance.
115
+ */
116
+ public init(nymph: Nymph) {
117
+ this.nymph = nymph;
118
+ }
119
+
101
120
  protected posixRegexMatch(
102
121
  pattern: string,
103
122
  subject: string,
@@ -277,8 +296,7 @@ export default abstract class NymphDriver {
277
296
  );
278
297
  } else {
279
298
  if (key === 'qref' || key === '!qref') {
280
- // TODO: implement qref clauses
281
- throw new Error('not implemented');
299
+ throw new Error("Can't use checkData on qref clauses.");
282
300
  } else {
283
301
  // Check if it doesn't pass any for &, check if it
284
302
  // passes any for |.
@@ -533,7 +551,10 @@ export default abstract class NymphDriver {
533
551
  return false;
534
552
  }
535
553
 
536
- public formatSelectors(selectors: Selector[]): FormattedSelector[] {
554
+ public formatSelectors(
555
+ selectors: Selector[],
556
+ options: Options = {}
557
+ ): FormattedSelector[] {
537
558
  const newSelectors: FormattedSelector[] = [];
538
559
 
539
560
  for (const curSelector of selectors) {
@@ -549,17 +570,37 @@ export default abstract class NymphDriver {
549
570
  continue;
550
571
  }
551
572
 
552
- if (key === 'qref' || key === '!qref') {
553
- throw new Error("qref isn't implemented yet.");
554
- }
555
-
556
573
  if (value === undefined) {
557
574
  continue;
558
575
  }
559
576
 
560
- if (key === 'selector' || key === '!selector') {
577
+ if (key === 'qref' || key === '!qref') {
578
+ const tmpArr = (
579
+ Array.isArray(((value as Selector['qref']) ?? [])[0])
580
+ ? value
581
+ : [value]
582
+ ) as [string, [Options, ...Selector[]]][];
583
+ const formatArr: [string, [Options, ...FormattedSelector[]]][] = [];
584
+ for (let i = 0; i < tmpArr.length; i++) {
585
+ const name = tmpArr[i][0];
586
+ const [qrefOptions, ...qrefSelectors] = tmpArr[i][1];
587
+ const QrefEntityClass =
588
+ typeof qrefOptions.class === 'string'
589
+ ? this.nymph.getEntityClass(qrefOptions.class)
590
+ : qrefOptions.class ?? this.nymph.getEntityClass('Entity');
591
+ const newOptions = {
592
+ ...qrefOptions,
593
+ class: QrefEntityClass,
594
+ source: options.source,
595
+ };
596
+ const newSelectors = this.formatSelectors(qrefSelectors, options);
597
+ this.nymph.runQueryCallbacks(newOptions, newSelectors);
598
+ formatArr[i] = [name, [newOptions, ...newSelectors]];
599
+ }
600
+ newSelector[key] = formatArr;
601
+ } else if (key === 'selector' || key === '!selector') {
561
602
  const tmpArr = (Array.isArray(value) ? value : [value]) as Selector[];
562
- newSelector[key] = this.formatSelectors(tmpArr);
603
+ newSelector[key] = this.formatSelectors(tmpArr, options);
563
604
  } else if (!Array.isArray(value)) {
564
605
  // @ts-ignore: ts doesn't know what value is here.
565
606
  newSelector[key] = [[value]];
@@ -764,12 +805,13 @@ export default abstract class NymphDriver {
764
805
  }
765
806
 
766
807
  let entities: ReturnType<T['factorySync']>[] | string[] = [];
767
- const EntityClass = options.class ?? (Nymph.getEntityClass('Entity') as T);
808
+ const EntityClass =
809
+ options.class ?? (this.nymph.getEntityClass('Entity') as T);
768
810
  const etype = EntityClass.ETYPE;
769
811
 
770
812
  // Check if the requested entity is cached.
771
813
  if (
772
- Nymph.config.cache &&
814
+ this.nymph.config.cache &&
773
815
  !options.skipCache &&
774
816
  'guid' in selectors[0] &&
775
817
  typeof selectors[0].guid === 'number'
@@ -806,43 +848,47 @@ export default abstract class NymphDriver {
806
848
  }
807
849
  }
808
850
 
809
- const formattedSelectors = this.formatSelectors(selectors);
851
+ const formattedSelectors = this.formatSelectors(selectors, options);
852
+ this.nymph.runQueryCallbacks(options, formattedSelectors);
810
853
  const { result } = performQueryCallback(options, formattedSelectors, etype);
811
854
 
812
855
  return {
813
856
  result,
814
857
  process: () => {
815
858
  let row = rowFetchCallback();
816
- while (row != null) {
817
- const guid = getGUIDCallback(row);
818
- const tagsAndDates = getTagsAndDatesCallback(row);
819
- const tags = tagsAndDates.tags;
820
- const cdate = tagsAndDates.cdate;
821
- const mdate = tagsAndDates.mdate;
822
- let dataNameAndSValue = getDataNameAndSValueCallback(row);
823
- // Data.
824
- const data: EntityData = {};
825
- // Serialized data.
826
- const sdata: SerializedEntityData = {};
827
- if (dataNameAndSValue.name !== '') {
828
- // This do will keep going and adding the data until the
829
- // next entity is reached. $row will end on the next entity.
830
- do {
831
- dataNameAndSValue = getDataNameAndSValueCallback(row);
832
- sdata[dataNameAndSValue.name] = dataNameAndSValue.svalue;
833
- row = rowFetchCallback();
834
- } while (row != null && getGUIDCallback(row) === guid);
835
- } else {
836
- // Make sure that $row is incremented :)
859
+ if (options.return === 'guid') {
860
+ while (row != null) {
861
+ (entities as string[]).push(getGUIDCallback(row));
837
862
  row = rowFetchCallback();
838
863
  }
839
- if (options.return === 'guid') {
840
- // @ts-ignore: ts doesn't know the return type here.
841
- entities.push(guid);
842
- } else {
864
+ } else {
865
+ while (row != null) {
866
+ const guid = getGUIDCallback(row);
867
+ const tagsAndDates = getTagsAndDatesCallback(row);
868
+ const tags = tagsAndDates.tags;
869
+ const cdate = tagsAndDates.cdate;
870
+ const mdate = tagsAndDates.mdate;
871
+ let dataNameAndSValue = getDataNameAndSValueCallback(row);
872
+ // Data.
873
+ const data: EntityData = {};
874
+ // Serialized data.
875
+ const sdata: SerializedEntityData = {};
876
+ if (dataNameAndSValue.name !== '') {
877
+ // This do will keep going and adding the data until the
878
+ // next entity is reached. $row will end on the next entity.
879
+ do {
880
+ dataNameAndSValue = getDataNameAndSValueCallback(row);
881
+ sdata[dataNameAndSValue.name] = dataNameAndSValue.svalue;
882
+ row = rowFetchCallback();
883
+ } while (row != null && getGUIDCallback(row) === guid);
884
+ } else {
885
+ // Make sure that $row is incremented :)
886
+ row = rowFetchCallback();
887
+ }
843
888
  const entity = EntityClass.factorySync() as ReturnType<
844
889
  T['factorySync']
845
890
  >;
891
+ entity.$nymph = this.nymph;
846
892
  if (options.skipAc != null) {
847
893
  entity.$useSkipAc(!!options.skipAc);
848
894
  }
@@ -856,7 +902,7 @@ export default abstract class NymphDriver {
856
902
  }
857
903
  entity.$putData(data, sdata);
858
904
  this.putDataCounter--;
859
- if (Nymph.config.cache) {
905
+ if (this.nymph.config.cache) {
860
906
  this.pushCache(guid, cdate, mdate, tags, data, sdata);
861
907
  }
862
908
  // @ts-ignore: ts doesn't know the return type here.
@@ -926,7 +972,7 @@ export default abstract class NymphDriver {
926
972
  }
927
973
  } else {
928
974
  // Removed any cached versions of this entity.
929
- if (Nymph.config.cache) {
975
+ if (this.nymph.config.cache) {
930
976
  this.cleanCache(entity.guid);
931
977
  }
932
978
  success = await saveExistingEntityCallback(
@@ -946,7 +992,7 @@ export default abstract class NymphDriver {
946
992
  success = await commitTransactionCallback(success);
947
993
  }
948
994
  // Cache the entity.
949
- if (success && Nymph.config.cache) {
995
+ if (success && this.nymph.config.cache) {
950
996
  this.pushCache(
951
997
  entity.guid as string,
952
998
  entity.cdate as number,
@@ -978,8 +1024,9 @@ export default abstract class NymphDriver {
978
1024
  }
979
1025
  this.entityCount[guid]++;
980
1026
  if (guid in this.entityCache) {
981
- const entity = Nymph.getEntityClass(className).factorySync() as T;
1027
+ const entity = this.nymph.getEntityClass(className).factorySync() as T;
982
1028
  if (entity) {
1029
+ entity.$nymph = this.nymph;
983
1030
  entity.$useSkipAc(!!useSkipAc);
984
1031
  entity.guid = guid;
985
1032
  entity.cdate = this.entityCache[guid]['cdate'];
@@ -1022,7 +1069,7 @@ export default abstract class NymphDriver {
1022
1069
  }
1023
1070
  this.entityCount[guid]++;
1024
1071
  // Check the threshold.
1025
- if (this.entityCount[guid] < Nymph.config.cacheThreshold) {
1072
+ if (this.entityCount[guid] < this.nymph.config.cacheThreshold) {
1026
1073
  return;
1027
1074
  }
1028
1075
  // Cache the entity.
@@ -1034,8 +1081,8 @@ export default abstract class NymphDriver {
1034
1081
  sdata: sdata,
1035
1082
  };
1036
1083
  while (
1037
- Nymph.config.cacheLimit &&
1038
- Object.keys(this.entityCache).length >= Nymph.config.cacheLimit
1084
+ this.nymph.config.cacheLimit &&
1085
+ Object.keys(this.entityCache).length >= this.nymph.config.cacheLimit
1039
1086
  ) {
1040
1087
  // Find which entity has been accessed the least.
1041
1088
  const least =