@nymphjs/client 1.0.0-beta.11 → 1.0.0-beta.110

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 (69) hide show
  1. package/CHANGELOG.md +452 -0
  2. package/README.md +6 -6
  3. package/asyncitertest.js +53 -0
  4. package/dist/Entity.d.ts +156 -0
  5. package/{lib → dist}/Entity.js +202 -104
  6. package/dist/Entity.js.map +1 -0
  7. package/dist/Entity.types.d.ts +218 -0
  8. package/dist/Entity.types.js +2 -0
  9. package/{lib → dist}/EntityWeakCache.d.ts +1 -1
  10. package/{lib → dist}/EntityWeakCache.js +5 -9
  11. package/dist/EntityWeakCache.js.map +1 -0
  12. package/{lib → dist}/HttpRequester.d.ts +18 -4
  13. package/dist/HttpRequester.js +365 -0
  14. package/dist/HttpRequester.js.map +1 -0
  15. package/{lib → dist}/Nymph.d.ts +48 -11
  16. package/{lib → dist}/Nymph.js +167 -51
  17. package/dist/Nymph.js.map +1 -0
  18. package/{lib → dist}/Nymph.types.d.ts +82 -2
  19. package/dist/Nymph.types.js +2 -0
  20. package/{lib → dist}/PubSub.d.ts +16 -10
  21. package/{lib → dist}/PubSub.js +172 -108
  22. package/dist/PubSub.js.map +1 -0
  23. package/{lib → dist}/PubSub.types.d.ts +8 -3
  24. package/dist/PubSub.types.js +2 -0
  25. package/{lib → dist}/entityRefresh.d.ts +1 -1
  26. package/{lib → dist}/entityRefresh.js +21 -13
  27. package/dist/entityRefresh.js.map +1 -0
  28. package/dist/index.d.ts +13 -0
  29. package/dist/index.js +13 -2
  30. package/dist/index.js.map +1 -1
  31. package/{lib → dist}/utils.d.ts +1 -1
  32. package/{lib → dist}/utils.js +28 -21
  33. package/dist/utils.js.map +1 -0
  34. package/jest.config.js +11 -2
  35. package/package.json +23 -27
  36. package/src/Entity.ts +173 -107
  37. package/src/Entity.types.ts +29 -47
  38. package/src/EntityWeakCache.ts +8 -6
  39. package/src/HttpRequester.ts +268 -31
  40. package/src/Nymph.ts +191 -88
  41. package/src/Nymph.types.ts +51 -2
  42. package/src/PubSub.ts +214 -141
  43. package/src/PubSub.types.ts +10 -5
  44. package/src/entityRefresh.ts +6 -6
  45. package/src/index.ts +10 -10
  46. package/src/utils.ts +12 -5
  47. package/tsconfig.json +6 -4
  48. package/typedoc.json +4 -0
  49. package/dist/index.js.LICENSE.txt +0 -8
  50. package/lib/Entity.d.ts +0 -51
  51. package/lib/Entity.js.map +0 -1
  52. package/lib/Entity.types.d.ts +0 -65
  53. package/lib/Entity.types.js +0 -3
  54. package/lib/EntityWeakCache.js.map +0 -1
  55. package/lib/HttpRequester.js +0 -190
  56. package/lib/HttpRequester.js.map +0 -1
  57. package/lib/Nymph.js.map +0 -1
  58. package/lib/Nymph.types.js +0 -3
  59. package/lib/PubSub.js.map +0 -1
  60. package/lib/PubSub.types.js +0 -3
  61. package/lib/entityRefresh.js.map +0 -1
  62. package/lib/index.d.ts +0 -13
  63. package/lib/index.js +0 -34
  64. package/lib/index.js.map +0 -1
  65. package/lib/utils.js.map +0 -1
  66. package/webpack.config.js +0 -28
  67. /package/{lib → dist}/Entity.types.js.map +0 -0
  68. /package/{lib → dist}/Nymph.types.js.map +0 -0
  69. /package/{lib → dist}/PubSub.types.js.map +0 -0
package/src/Entity.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { difference, isEqual } from 'lodash';
1
+ import { difference, isEqual } from 'lodash-es';
2
2
 
3
- import type Nymph from './Nymph';
3
+ import type Nymph from './Nymph.js';
4
4
  import {
5
5
  EntityConstructor,
6
6
  EntityData,
@@ -8,21 +8,23 @@ import {
8
8
  EntityJson,
9
9
  EntityPatch,
10
10
  EntityReference,
11
- } from './Entity.types';
11
+ } from './Entity.types.js';
12
12
  import {
13
13
  uniqueStrings,
14
14
  entitiesToReferences,
15
15
  referencesToEntities,
16
16
  sortObj,
17
- } from './utils';
17
+ } from './utils.js';
18
18
 
19
- const sleepErr =
20
- 'This entity is in a sleeping reference state. ' +
21
- 'You must use .$ready() to wake it.';
19
+ export type EntityDataType<T> =
20
+ T extends Entity<infer DataType> ? DataType : never;
22
21
 
23
- export default class Entity<T extends EntityData = EntityData>
24
- implements EntityInterface
25
- {
22
+ export type EntityInstanceType<T extends EntityConstructor> =
23
+ T extends new () => infer E ? E & EntityDataType<E> : never;
24
+
25
+ export default class Entity<
26
+ T extends EntityData = EntityData,
27
+ > implements EntityInterface {
26
28
  /**
27
29
  * The instance of Nymph to use for queries.
28
30
  */
@@ -84,15 +86,14 @@ export default class Entity<T extends EntityData = EntityData>
84
86
  */
85
87
  protected $sleepingReference: EntityReference | null = null;
86
88
  /**
87
- * A promise that resolved when the entity's data is ready.
89
+ * A promise that resolved when the entity's data is wake.
88
90
  */
89
- protected $readyPromise: Promise<Entity<T>> | null = null;
91
+ protected $wakePromise: Promise<Entity<T>> | null = null;
90
92
 
91
93
  /**
92
- * Load an entity.
93
- * @param guid The ID of the entity to load, undefined for a new entity.
94
+ * Initialize an entity.
94
95
  */
95
- public constructor(guid?: string) {
96
+ public constructor(..._rest: any[]) {
96
97
  this.$nymph = (this.constructor as EntityConstructor).nymph;
97
98
  this.$dataHandler = {
98
99
  has: (data: EntityData, name: string) => {
@@ -129,7 +130,7 @@ export default class Entity<T extends EntityData = EntityData>
129
130
  deleteProperty: (data: EntityData, name: string) => {
130
131
  if (typeof name !== 'symbol' && this.$isASleepingReference) {
131
132
  console.error(
132
- `Tried to delete data on a sleeping reference: ${name}`
133
+ `Tried to delete data on a sleeping reference: ${name}`,
133
134
  );
134
135
  return false;
135
136
  }
@@ -143,11 +144,11 @@ export default class Entity<T extends EntityData = EntityData>
143
144
  defineProperty: (
144
145
  data: EntityData,
145
146
  name: string,
146
- descriptor: PropertyDescriptor
147
+ descriptor: PropertyDescriptor,
147
148
  ) => {
148
149
  if (typeof name !== 'symbol' && this.$isASleepingReference) {
149
150
  console.error(
150
- `Tried to define data on a sleeping reference: ${name}`
151
+ `Tried to define data on a sleeping reference: ${name}`,
151
152
  );
152
153
  return false;
153
154
  }
@@ -161,7 +162,7 @@ export default class Entity<T extends EntityData = EntityData>
161
162
  getOwnPropertyDescriptor: (data: EntityData, name: string) => {
162
163
  if (typeof name !== 'symbol' && this.$isASleepingReference) {
163
164
  console.error(
164
- `Tried to get property descriptor on a sleeping reference: ${name}`
165
+ `Tried to get property descriptor on a sleeping reference: ${name}`,
165
166
  );
166
167
  return undefined;
167
168
  }
@@ -179,17 +180,6 @@ export default class Entity<T extends EntityData = EntityData>
179
180
  this.$dataStore = {} as T;
180
181
  this.$data = new Proxy(this.$dataStore, this.$dataHandler);
181
182
 
182
- if (guid != null) {
183
- this.guid = guid;
184
- this.$isASleepingReference = true;
185
- this.$sleepingReference = [
186
- 'nymph_entity_reference',
187
- this.guid,
188
- (this.constructor as EntityConstructor).class,
189
- ];
190
- this.$ready();
191
- }
192
-
193
183
  return new Proxy(this, {
194
184
  has(entity: Entity, name: string) {
195
185
  if (
@@ -245,7 +235,7 @@ export default class Entity<T extends EntityData = EntityData>
245
235
  defineProperty(
246
236
  entity: Entity,
247
237
  name: string,
248
- descriptor: PropertyDescriptor
238
+ descriptor: PropertyDescriptor,
249
239
  ) {
250
240
  if (
251
241
  typeof name !== 'string' ||
@@ -273,57 +263,130 @@ export default class Entity<T extends EntityData = EntityData>
273
263
 
274
264
  ownKeys(entity: Entity) {
275
265
  return Object.getOwnPropertyNames(entity).concat(
276
- Object.getOwnPropertyNames(entity.$data)
266
+ Object.getOwnPropertyNames(entity.$data),
277
267
  );
278
268
  },
279
269
  }) as Entity<T>;
280
270
  }
281
271
 
282
- public static async factory(guid?: string) {
272
+ /**
273
+ * Create or retrieve a new entity instance.
274
+ *
275
+ * Note that this will always return an entity, even if the GUID is not found.
276
+ *
277
+ * @param guid An optional GUID to retrieve.
278
+ */
279
+ public static async factory<E extends Entity>(
280
+ this: {
281
+ new (): E;
282
+ },
283
+ guid?: string,
284
+ ): Promise<E & EntityDataType<E>> {
283
285
  const cacheEntity = (
284
- guid ? this.nymph.getEntityFromCache(this, guid) : null
286
+ guid
287
+ ? (this as unknown as EntityConstructor).nymph.getEntityFromCache(
288
+ this as unknown as EntityConstructor,
289
+ guid,
290
+ )
291
+ : null
285
292
  ) as Entity | null;
286
- const entity = cacheEntity || new this(guid);
293
+ if (cacheEntity) {
294
+ return cacheEntity as E & EntityDataType<E>;
295
+ }
296
+ const entity = new this();
287
297
  if (guid != null) {
288
- await entity.$ready();
298
+ entity.guid = guid;
299
+ entity.$isASleepingReference = true;
300
+ entity.$sleepingReference = [
301
+ 'nymph_entity_reference',
302
+ guid,
303
+ (this as unknown as EntityConstructor).class,
304
+ ];
305
+ await entity.$wake();
289
306
  }
290
- return entity;
307
+ return entity as E & EntityDataType<E>;
291
308
  }
292
309
 
293
- public static factorySync(guid?: string) {
294
- const cacheEntity = (
295
- guid ? this.nymph.getEntityFromCache(this, guid) : null
296
- ) as Entity | null;
297
- return cacheEntity || new this(guid);
310
+ /**
311
+ * Create a new entity instance.
312
+ */
313
+ public static factorySync<E extends Entity>(this: {
314
+ new (): E;
315
+ }): E & EntityDataType<E> {
316
+ return new this() as E & EntityDataType<E>;
298
317
  }
299
318
 
300
- public static factoryReference(reference: EntityReference) {
319
+ /**
320
+ * Create a new sleeping reference instance.
321
+ *
322
+ * Sleeping references won't retrieve their data from the server until they
323
+ * are readied with `$wake()` or a parent's `$wakeAll()`.
324
+ *
325
+ * @param reference The Nymph Entity Reference to use to wake.
326
+ * @returns The new instance.
327
+ */
328
+ public static factoryReference<E extends Entity>(
329
+ this: {
330
+ new (): E;
331
+ },
332
+ reference: EntityReference,
333
+ ): E & EntityDataType<E> {
301
334
  const cacheEntity = (
302
- reference[1] ? this.nymph.getEntityFromCache(this, reference[1]) : null
335
+ reference[1]
336
+ ? (this as unknown as EntityConstructor).nymph.getEntityFromCache(
337
+ this as unknown as EntityConstructor,
338
+ reference[1],
339
+ )
340
+ : null
303
341
  ) as Entity | null;
304
342
 
305
343
  const entity = cacheEntity || new this();
306
344
  if (!cacheEntity) {
307
345
  entity.$referenceSleep(reference);
308
346
  }
309
- return entity;
347
+ return entity as E & EntityDataType<E>;
310
348
  }
311
349
 
350
+ /**
351
+ * Call a static method on the server version of this entity.
352
+ *
353
+ * @param method The name of the method.
354
+ * @param params The parameters to call the method with.
355
+ * @returns The value that the method on the server returned.
356
+ */
312
357
  public static async serverCallStatic(method: string, params: Iterable<any>) {
313
358
  const data = await this.nymph.serverCallStatic(
314
359
  this.class,
315
360
  method,
316
361
  // Turn the params into a real array, in case an arguments object was
317
362
  // passed.
318
- Array.prototype.slice.call(params)
363
+ Array.prototype.slice.call(params),
319
364
  );
320
365
  return data.return;
321
366
  }
322
367
 
368
+ /**
369
+ * Call a static iterator method on the server version of this entity.
370
+ *
371
+ * @param method The name of the method.
372
+ * @param params The parameters to call the method with.
373
+ * @returns An iterator that iterates over values that the method on the server yields.
374
+ */
375
+ public static async serverCallStaticIterator(
376
+ method: string,
377
+ params: Iterable<any>,
378
+ ) {
379
+ return await this.nymph.serverCallStaticIterator(
380
+ this.class,
381
+ method,
382
+ // Turn the params into a real array, in case an arguments object was
383
+ // passed.
384
+ Array.prototype.slice.call(params),
385
+ );
386
+ }
387
+
323
388
  public toJSON() {
324
- if (this.$isASleepingReference) {
325
- throw new EntityIsSleepingReferenceError(sleepErr);
326
- }
389
+ this.$check();
327
390
  const obj: EntityJson = {
328
391
  class: (this.constructor as any).class as string,
329
392
  guid: this.guid,
@@ -359,7 +422,7 @@ export default class Entity<T extends EntityData = EntityData>
359
422
  })
360
423
  .reduce(
361
424
  (obj, { key, value }) => Object.assign(obj, { [key]: value }),
362
- {}
425
+ {},
363
426
  ) as T;
364
427
  this.$data = new Proxy(this.$dataStore, this.$dataHandler);
365
428
 
@@ -369,9 +432,7 @@ export default class Entity<T extends EntityData = EntityData>
369
432
  }
370
433
 
371
434
  public $addTag(...tags: string[]) {
372
- if (this.$isASleepingReference) {
373
- throw new EntityIsSleepingReferenceError(sleepErr);
374
- }
435
+ this.$check();
375
436
 
376
437
  if (tags.length < 1) {
377
438
  return;
@@ -380,9 +441,7 @@ export default class Entity<T extends EntityData = EntityData>
380
441
  }
381
442
 
382
443
  public $arraySearch(array: any[], strict = false) {
383
- if (this.$isASleepingReference) {
384
- throw new EntityIsSleepingReferenceError(sleepErr);
385
- }
444
+ this.$check();
386
445
 
387
446
  if (!Array.isArray(array)) {
388
447
  return -1;
@@ -397,9 +456,7 @@ export default class Entity<T extends EntityData = EntityData>
397
456
  }
398
457
 
399
458
  public async $delete(): Promise<boolean> {
400
- if (this.$isASleepingReference) {
401
- throw new EntityIsSleepingReferenceError(sleepErr);
402
- }
459
+ this.$check();
403
460
 
404
461
  const guid = this.guid;
405
462
 
@@ -407,9 +464,7 @@ export default class Entity<T extends EntityData = EntityData>
407
464
  }
408
465
 
409
466
  public $equals(object: any) {
410
- if (this.$isASleepingReference) {
411
- throw new EntityIsSleepingReferenceError(sleepErr);
412
- }
467
+ this.$check();
413
468
 
414
469
  if (!(object instanceof Entity)) {
415
470
  return false;
@@ -435,12 +490,10 @@ export default class Entity<T extends EntityData = EntityData>
435
490
  }
436
491
 
437
492
  public $getPatch(): EntityPatch {
438
- if (this.$isASleepingReference) {
439
- throw new EntityIsSleepingReferenceError(sleepErr);
440
- }
493
+ this.$check();
441
494
  if (this.guid == null) {
442
495
  throw new InvalidStateError(
443
- "You can't make a patch from an unsaved entity."
496
+ "You can't make a patch from an unsaved entity.",
444
497
  );
445
498
  }
446
499
  const patch: EntityPatch = {
@@ -448,10 +501,10 @@ export default class Entity<T extends EntityData = EntityData>
448
501
  mdate: this.mdate,
449
502
  class: (this.constructor as EntityConstructor).class,
450
503
  addTags: this.tags.filter(
451
- (tag) => this.$originalTags.indexOf(tag) === -1
504
+ (tag) => this.$originalTags.indexOf(tag) === -1,
452
505
  ),
453
506
  removeTags: this.$originalTags.filter(
454
- (tag) => this.tags.indexOf(tag) === -1
507
+ (tag) => this.tags.indexOf(tag) === -1,
455
508
  ),
456
509
  unset: [],
457
510
  set: {},
@@ -471,9 +524,7 @@ export default class Entity<T extends EntityData = EntityData>
471
524
  }
472
525
 
473
526
  public $hasTag(...tags: string[]) {
474
- if (this.$isASleepingReference) {
475
- throw new EntityIsSleepingReferenceError(sleepErr);
476
- }
527
+ this.$check();
477
528
 
478
529
  if (!tags.length) {
479
530
  return false;
@@ -486,14 +537,16 @@ export default class Entity<T extends EntityData = EntityData>
486
537
  return true;
487
538
  }
488
539
 
540
+ public $isDirty(property: string) {
541
+ return property in this.$dirty ? this.$dirty[property] : null;
542
+ }
543
+
489
544
  public $inArray(array: any[], strict = false) {
490
545
  return this.$arraySearch(array, strict) !== -1;
491
546
  }
492
547
 
493
548
  public $is(object: any) {
494
- if (this.$isASleepingReference) {
495
- throw new EntityIsSleepingReferenceError(sleepErr);
496
- }
549
+ this.$check();
497
550
 
498
551
  if (!(object instanceof Entity)) {
499
552
  return false;
@@ -514,9 +567,7 @@ export default class Entity<T extends EntityData = EntityData>
514
567
  }
515
568
 
516
569
  public async $patch() {
517
- if (this.$isASleepingReference) {
518
- throw new EntityIsSleepingReferenceError(sleepErr);
519
- }
570
+ this.$check();
520
571
 
521
572
  const mdate = this.mdate;
522
573
 
@@ -524,21 +575,42 @@ export default class Entity<T extends EntityData = EntityData>
524
575
  return mdate !== this.mdate;
525
576
  }
526
577
 
527
- public $ready() {
578
+ /**
579
+ * Check if this is a sleeping reference and throw an error if so.
580
+ */
581
+ protected $check() {
582
+ if (this.$isASleepingReference || this.$sleepingReference != null) {
583
+ throw new EntityIsSleepingReferenceError(
584
+ 'This entity is in a sleeping reference state. You must use .$wake() to wake it.',
585
+ );
586
+ }
587
+ }
588
+
589
+ /**
590
+ * Check if this is a sleeping reference.
591
+ */
592
+ public $asleep() {
593
+ return this.$isASleepingReference || this.$sleepingReference != null;
594
+ }
595
+
596
+ /**
597
+ * Wake from a sleeping reference.
598
+ */
599
+ public $wake() {
528
600
  if (!this.$isASleepingReference) {
529
- this.$readyPromise = null;
601
+ this.$wakePromise = null;
530
602
  return Promise.resolve(this);
531
603
  }
532
604
  if (this.$sleepingReference?.[1] == null) {
533
605
  throw new InvalidStateError(
534
- 'Tried to ready a sleeping reference with no GUID.'
606
+ 'Tried to wake a sleeping reference with no GUID.',
535
607
  );
536
608
  }
537
- if (!this.$readyPromise) {
538
- this.$readyPromise = this.$nymph
609
+ if (!this.$wakePromise) {
610
+ this.$wakePromise = this.$nymph
539
611
  .getEntityData(
540
612
  { class: this.constructor as EntityConstructor },
541
- { type: '&', guid: this.$sleepingReference[1] }
613
+ { type: '&', guid: this.$sleepingReference[1] },
542
614
  )
543
615
  .then((data) => {
544
616
  if (data == null) {
@@ -548,16 +620,16 @@ export default class Entity<T extends EntityData = EntityData>
548
620
  return this.$init(data);
549
621
  })
550
622
  .finally(() => {
551
- this.$readyPromise = null;
623
+ this.$wakePromise = null;
552
624
  });
553
625
  }
554
- return this.$readyPromise;
626
+ return this.$wakePromise;
555
627
  }
556
628
 
557
- public $readyAll(level?: number) {
629
+ public $wakeAll(level?: number) {
558
630
  return new Promise((resolve, reject) => {
559
- // Run this once this entity is ready.
560
- const readyProps = () => {
631
+ // Run this once this entity is awake.
632
+ const wakeProps = () => {
561
633
  let newLevel;
562
634
  // If level is undefined, keep going forever, otherwise, stop once we've
563
635
  // gone deep enough.
@@ -569,17 +641,17 @@ export default class Entity<T extends EntityData = EntityData>
569
641
  return;
570
642
  }
571
643
  const promises = [];
572
- // Go through data looking for entities to ready.
644
+ // Go through data looking for entities to wake.
573
645
  for (let [key, value] of Object.entries(this.$data)) {
574
646
  if (value instanceof Entity && value.$isASleepingReference) {
575
- promises.push(value.$readyAll(newLevel));
647
+ promises.push(value.$wakeAll(newLevel));
576
648
  } else if (Array.isArray(value)) {
577
649
  for (let i = 0; i < value.length; i++) {
578
650
  if (
579
651
  value[i] instanceof Entity &&
580
652
  value[i].$isASleepingReference
581
653
  ) {
582
- promises.push(value[i].$readyAll(newLevel));
654
+ promises.push(value[i].$wakeAll(newLevel));
583
655
  }
584
656
  }
585
657
  }
@@ -587,7 +659,7 @@ export default class Entity<T extends EntityData = EntityData>
587
659
  if (promises.length) {
588
660
  Promise.all(promises).then(
589
661
  () => resolve(this),
590
- (errObj) => reject(errObj)
662
+ (errObj) => reject(errObj),
591
663
  );
592
664
  } else {
593
665
  resolve(this);
@@ -595,9 +667,9 @@ export default class Entity<T extends EntityData = EntityData>
595
667
  };
596
668
 
597
669
  if (this.$isASleepingReference) {
598
- this.$ready().then(readyProps, (errObj) => reject(errObj));
670
+ this.$wake().then(wakeProps, (errObj) => reject(errObj));
599
671
  } else {
600
- readyProps();
672
+ wakeProps();
601
673
  }
602
674
  }) as Promise<Entity<T>>;
603
675
  }
@@ -610,7 +682,7 @@ export default class Entity<T extends EntityData = EntityData>
610
682
 
611
683
  public async $refresh() {
612
684
  if (this.$isASleepingReference) {
613
- await this.$ready();
685
+ await this.$wake();
614
686
  return true;
615
687
  }
616
688
 
@@ -624,24 +696,20 @@ export default class Entity<T extends EntityData = EntityData>
624
696
  {
625
697
  type: '&',
626
698
  guid: this.guid,
627
- }
699
+ },
628
700
  );
629
701
  this.$init(data);
630
702
  return this.guid == null ? 0 : true;
631
703
  }
632
704
 
633
705
  public $removeTag(...tags: string[]) {
634
- if (this.$isASleepingReference) {
635
- throw new EntityIsSleepingReferenceError(sleepErr);
636
- }
706
+ this.$check();
637
707
 
638
708
  this.tags = difference(this.tags, tags);
639
709
  }
640
710
 
641
711
  public async $save() {
642
- if (this.$isASleepingReference) {
643
- throw new EntityIsSleepingReferenceError(sleepErr);
644
- }
712
+ this.$check();
645
713
 
646
714
  await this.$nymph.saveEntity(this);
647
715
  return !!this.guid;
@@ -650,11 +718,9 @@ export default class Entity<T extends EntityData = EntityData>
650
718
  public async $serverCall(
651
719
  method: string,
652
720
  params: Iterable<any>,
653
- stateless = false
721
+ stateless = false,
654
722
  ) {
655
- if (this.$isASleepingReference) {
656
- throw new EntityIsSleepingReferenceError(sleepErr);
657
- }
723
+ this.$check();
658
724
  // Turn the params into a real array, in case an arguments object was
659
725
  // passed.
660
726
  const paramArray = Array.prototype.slice.call(params);
@@ -662,7 +728,7 @@ export default class Entity<T extends EntityData = EntityData>
662
728
  this,
663
729
  method,
664
730
  paramArray,
665
- stateless
731
+ stateless,
666
732
  );
667
733
  if (!stateless && data.entity) {
668
734
  this.$init(data.entity);
@@ -1,4 +1,5 @@
1
- import type Nymph from './Nymph';
1
+ import type Nymph from './Nymph.js';
2
+ import type Entity from './Entity.js';
2
3
 
3
4
  export type ServerCallResponse = {
4
5
  return: any;
@@ -155,6 +156,24 @@ export interface EntityInterface extends DataObjectInterface {
155
156
  * @returns True or false.
156
157
  */
157
158
  $hasTag(...tags: string[]): boolean;
159
+ /**
160
+ * Check whether a property is dirty.
161
+ *
162
+ * To be a dirty property, it must have been set or deleted since the entity
163
+ * was initialized. A clean property existed on initialization and hasn't been
164
+ * set or deleted. An untracked property didn't exist on initialization and
165
+ * hasn't been set or deleted.
166
+ *
167
+ * Note that this doesn't necessarily mean the property has changed. It could
168
+ * have been set to the same value, or created and then deleted.
169
+ *
170
+ * Entities are initialized when they are pulled from the server or saved.
171
+ * This is done with the `$init` method.
172
+ *
173
+ * @param property The name of a property.
174
+ * @returns True if it's dirty, false if not, and null if it's not tracked.
175
+ */
176
+ $isDirty(property: string): boolean | null;
158
177
  /**
159
178
  * Initialize this entity from a JSON representation.
160
179
  *
@@ -166,14 +185,14 @@ export interface EntityInterface extends DataObjectInterface {
166
185
  *
167
186
  * @returns The entity.
168
187
  */
169
- $ready(): Promise<EntityInterface>;
188
+ $wake(): Promise<EntityInterface>;
170
189
  /**
171
190
  * Ready this entity's data, and the data of entity's within this one's.
172
191
  *
173
- * @param level The number of levels deep to ready. If undefined, it will keep going until there are no more entities. (Careful of infinite loops.)
192
+ * @param level The number of levels deep to wake. If undefined, it will keep going until there are no more entities. (Careful of infinite loops.)
174
193
  * @returns The entity.
175
194
  */
176
- $readyAll(level?: number): Promise<EntityInterface>;
195
+ $wakeAll(level?: number): Promise<EntityInterface>;
177
196
  /**
178
197
  * Remove one or more tags.
179
198
  *
@@ -195,7 +214,7 @@ export interface EntityInterface extends DataObjectInterface {
195
214
  $serverCall(
196
215
  method: string,
197
216
  params: Iterable<any>,
198
- stateless: boolean
217
+ stateless: boolean,
199
218
  ): Promise<any>;
200
219
  /**
201
220
  * Return a Nymph Entity Reference for this entity.
@@ -208,46 +227,9 @@ export interface EntityInterface extends DataObjectInterface {
208
227
  $toReference(): EntityReference | EntityInterface;
209
228
  }
210
229
 
211
- export type EntityConstructor = (new (...args: any[]) => EntityInterface) & {
212
- /**
213
- * The instance of Nymph to use for queries.
214
- */
215
- nymph: Nymph;
216
- /**
217
- * The lookup name for this entity.
218
- *
219
- * This is used for reference arrays (and sleeping references) and client
220
- * requests.
221
- */
222
- class: string;
223
- /**
224
- * Create a new entity instance.
225
- *
226
- * @param guid An optional GUID to retrieve.
227
- */
228
- factory(guid?: string): Promise<EntityInterface>;
229
- /**
230
- * Create a new entity instance.
231
- *
232
- * @param guid An optional GUID to retrieve.
233
- */
234
- factorySync(guid?: string): EntityInterface;
235
- /**
236
- * Create a new sleeping reference instance.
237
- *
238
- * Sleeping references won't retrieve their data from the server until they
239
- * are readied with `ready()` or a parent's `readyAll()`.
240
- *
241
- * @param reference The Nymph Entity Reference to use to wake.
242
- * @returns The new instance.
243
- */
244
- factoryReference(reference: EntityReference): EntityInterface;
245
- /**
246
- * Call a static method on the server version of this entity.
247
- *
248
- * @param method The name of the method.
249
- * @param params The parameters to call the method with.
250
- * @returns The value that the method on the server returned.
251
- */
252
- serverCallStatic(method: string, params: Iterable<any>): Promise<any>;
230
+ export type EntityConstructor<
231
+ D extends EntityData = EntityData,
232
+ E extends Entity<D> = Entity<D>,
233
+ > = (new (...args: any[]) => E) & {
234
+ [k in keyof typeof Entity]: (typeof Entity)[k];
253
235
  };
@@ -1,15 +1,18 @@
1
- import { EntityConstructor, EntityInterface } from './Entity.types';
1
+ import { EntityConstructor, EntityInterface } from './Entity.types.js';
2
2
 
3
3
  export default class EntityWeakCache {
4
- private references: WeakMap<EntityConstructor, { [k: string]: any }> =
5
- new WeakMap();
4
+ private references: WeakMap<
5
+ EntityConstructor,
6
+ { [k: string]: WeakRef<EntityInterface> }
7
+ > = new WeakMap();
6
8
 
7
9
  get(EntityClass: EntityConstructor, guid: string): EntityInterface | null {
8
10
  const classMap = this.references.get(EntityClass);
9
11
  if (classMap && guid in classMap) {
10
12
  const weakRef = classMap[guid];
11
- if (weakRef && weakRef.deref() != null) {
12
- return weakRef.deref();
13
+ const deref = weakRef && weakRef.deref();
14
+ if (deref != null) {
15
+ return deref;
13
16
  } else {
14
17
  delete classMap[guid];
15
18
  }
@@ -22,7 +25,6 @@ export default class EntityWeakCache {
22
25
  return;
23
26
  }
24
27
 
25
- // @ts-ignore TS doesn't know about WeakRef.
26
28
  const weakRef = new WeakRef(entity);
27
29
 
28
30
  const classMap = this.references.get(EntityClass) || {};