@nymphjs/nymph 1.0.0-beta.0 → 1.0.0-beta.10

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/Entity.ts CHANGED
@@ -45,9 +45,9 @@ import {
45
45
  *
46
46
  * Some notes about $equals() and $is(), the replacements for "==":
47
47
  *
48
- * The == operator will likely not give you the result you want, since any yet
49
- * to be unserialized data causes == to return false when you probably want it
50
- * to return true.
48
+ * The == operator will likely not give you the result you want, since two
49
+ * instances of the same entity will fail that check, even though they represent
50
+ * the same data in the database.
51
51
  *
52
52
  * $equals() performs a more strict comparison of the entity to another. Use
53
53
  * $equals() instead of the == operator when you want to check both the entities
@@ -56,8 +56,7 @@ import {
56
56
  *
57
57
  * - They must be entities.
58
58
  * - They must have equal GUIDs, or both must have no GUID.
59
- * - They must be instances of the same class.
60
- * - Their data must be equal.
59
+ * - Their data and tags must be equal.
61
60
  *
62
61
  * $is() performs a less strict comparison of the entity to another. Use $is()
63
62
  * instead of the == operator when the entity's data may have been changed, but
@@ -66,7 +65,7 @@ import {
66
65
  *
67
66
  * - They must be entities.
68
67
  * - They must have equal GUIDs, or both must have no GUID.
69
- * - If they have no GUIDs, their data must be equal.
68
+ * - If they have no GUIDs, their data and tags must be equal.
70
69
  *
71
70
  * Some notes about saving entities in other entity's properties:
72
71
  *
@@ -513,15 +512,17 @@ export default class Entity<T extends EntityData = EntityData>
513
512
  return false;
514
513
  }
515
514
  }
516
- if (object.constructor !== this.constructor) {
517
- return false;
518
- }
519
515
  if (object.cdate !== this.cdate) {
520
516
  return false;
521
517
  }
522
518
  if (object.mdate !== this.mdate) {
523
519
  return false;
524
520
  }
521
+ const obTags = [...object.tags].sort();
522
+ const myTags = [...this.tags].sort();
523
+ if (!isEqual(obTags, myTags)) {
524
+ return false;
525
+ }
525
526
  const obData = object.$getData(true);
526
527
  const myData = this.$getData(true);
527
528
  return isEqual(obData, myData);
@@ -615,6 +616,11 @@ export default class Entity<T extends EntityData = EntityData>
615
616
  if (typeof object.$getData !== 'function') {
616
617
  return false;
617
618
  } else {
619
+ const obTags = [...object.tags].sort();
620
+ const myTags = [...this.tags].sort();
621
+ if (!isEqual(obTags, myTags)) {
622
+ return false;
623
+ }
618
624
  const obData = object.$getData(true);
619
625
  const myData = this.$getData(true);
620
626
  return isEqual(obData, myData);
@@ -271,7 +271,7 @@ export interface EntityInterface extends DataObjectInterface {
271
271
  $useSkipAc(useSkipAc: boolean): void;
272
272
  }
273
273
 
274
- export type EntityConstructor = (new (guid?: string) => EntityInterface) & {
274
+ export type EntityConstructor = (new (...args: any[]) => EntityInterface) & {
275
275
  /**
276
276
  * The instance of Nymph to use for queries.
277
277
  */
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;