@nymphjs/client 1.0.0-beta.132 → 1.0.0-beta.134

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.
@@ -30,24 +30,43 @@ export type NymphOptions = {
30
30
  */
31
31
  noAutoconnect?: boolean;
32
32
  /**
33
- * Use a WeakRef based cache of entities.
33
+ * Use a WeakRef based cache of entities and queries.
34
34
  *
35
35
  * This ensures all entities returned are the same instance if they have the
36
36
  * same class and GUID. This also means that whenever an entity is returned
37
37
  * from the server, the single instance in memory will be refreshed. This
38
38
  * could have annoying results, like destroying dirty data (the dreaded
39
- * triple-D).
39
+ * triple-D). The dirty data problem can be partially mitigated with
40
+ * weakCacheKeepDirtyData.
40
41
  *
41
42
  * This could also be a potential source of memory leaks. Although the
42
43
  * entities themselves are referenced weakly so they get garbage collected,
43
44
  * the GUID used as a key and the WeakRef object itself are not weak
44
45
  * references, so not destroyed when the instance is garbage collected.
45
46
  *
47
+ * Queries are also cached with weak references. Practically, what this means
48
+ * is that if you make two identical queries (getEntity, getEntities, or
49
+ * getUID) at the same time, only one request will be sent to the server, and
50
+ * both queries will resolve with the same result. This is particularly useful
51
+ * if you tend to wake a lot of copies of the same entity (like a user) at the
52
+ * same time.
53
+ *
46
54
  * However, even with these caveats, this might help you if you have a big app
47
55
  * with the same entities stored in several different places in memory. This
48
56
  * can help to synchronize them correctly and avoid data conflicts.
49
57
  */
50
58
  weakCache?: boolean;
59
+ /**
60
+ * Keep dirty data after receiving unrelated updates from the server.
61
+ *
62
+ * When an entity in the weak cache is updated from the server due to an
63
+ * unrelated query (ie, not because it was just saved), all of the properties
64
+ * that are currently marked dirty will not be overwritten by the new data.
65
+ *
66
+ * This could potentially have negative consequences if your data is
67
+ * complicated and interrelated.
68
+ */
69
+ weakCacheKeepDirtyData?: boolean;
51
70
  /**
52
71
  * Whether to renew tokens when a request is made.
53
72
  *
package/src/PubSub.ts CHANGED
@@ -3,6 +3,7 @@ import type { NymphOptions, Options, Selector } from './Nymph.types.js';
3
3
  import type { EntityInstanceType } from './Entity.js';
4
4
  import type {
5
5
  EntityConstructor,
6
+ EntityData,
6
7
  EntityInterface,
7
8
  EntityJson,
8
9
  } from './Entity.types.js';
@@ -219,7 +220,7 @@ export default class PubSub {
219
220
  const newResolve = (args: any) => {
220
221
  if (Array.isArray(args)) {
221
222
  if (args.length) {
222
- entity.$init(args[0].toJSON());
223
+ entity.$init(args[0].toJSON(), false);
223
224
  } else {
224
225
  entity.guid = null;
225
226
  }
@@ -231,7 +232,7 @@ export default class PubSub {
231
232
  if ('removed' in update) {
232
233
  entity.guid = null;
233
234
  } else {
234
- entity.$init(update.data);
235
+ entity.$init(update.data, false);
235
236
  }
236
237
  }
237
238
 
@@ -387,7 +388,9 @@ export default class PubSub {
387
388
  if (typeof callback === 'function') {
388
389
  callback(
389
390
  set
390
- ? data.data.map((e: EntityJson) => this.nymph.initEntity(e))
391
+ ? data.data.map((e: EntityJson) =>
392
+ this.nymph.initEntity(e, false),
393
+ )
391
394
  : data,
392
395
  );
393
396
  }
@@ -629,8 +632,8 @@ export default class PubSub {
629
632
  }
630
633
 
631
634
  public updateArray(
632
- current: EntityInterface[],
633
- update: PubSubUpdate<EntityInterface[]>,
635
+ current: (EntityInterface & EntityData)[],
636
+ update: PubSubUpdate<(EntityInterface & EntityData)[]>,
634
637
  ) {
635
638
  if (Array.isArray(update)) {
636
639
  const newArr = [...update];
@@ -641,8 +644,8 @@ export default class PubSub {
641
644
  return;
642
645
  }
643
646
 
644
- const idMap: { [k: string]: EntityInterface } = {};
645
- const newEntities: EntityInterface[] = [];
647
+ const idMap: { [k: string]: EntityInterface & EntityData } = {};
648
+ const newEntities: (EntityInterface & EntityData)[] = [];
646
649
  while (current.length) {
647
650
  const first = current.shift();
648
651
  if (!first) {
@@ -668,7 +671,10 @@ export default class PubSub {
668
671
  current.push(entity);
669
672
  } else if (idMap[guid].mdate !== entity.mdate) {
670
673
  // It was modified.
671
- idMap[guid].$init(entity.toJSON());
674
+ const json = entity.toJSON();
675
+ if (!Array.isArray(json)) {
676
+ idMap[guid].$init(json, false);
677
+ }
672
678
  current.push(idMap[guid]);
673
679
  } else {
674
680
  // Item wasn't modified.
@@ -691,24 +697,24 @@ export default class PubSub {
691
697
  }
692
698
 
693
699
  // Get the entity.
694
- let entity: EntityInterface | null = null;
700
+ let entity: (EntityInterface & EntityData) | null = null;
695
701
  if ('added' in curUpdate) {
696
702
  // Check for it in the array already.
697
703
  for (let i = 0; i < current.length; i++) {
698
704
  if (current[i] != null && current[i].guid === curUpdate.added) {
699
- entity = current.splice(i, 1)[0].$init(curUpdate.data);
705
+ entity = current.splice(i, 1)[0].$init(curUpdate.data, false);
700
706
  }
701
707
  }
702
708
  if (entity == null) {
703
709
  // A new entity.
704
- entity = this.nymph.initEntity(curUpdate.data);
710
+ entity = this.nymph.initEntity(curUpdate.data, false);
705
711
  }
706
712
  }
707
713
  if ('updated' in curUpdate) {
708
714
  // Extract it from the array.
709
715
  for (let i = 0; i < current.length; i++) {
710
716
  if (current[i] != null && current[i].guid === curUpdate.updated) {
711
- entity = current.splice(i, 1)[0].$init(curUpdate.data);
717
+ entity = current.splice(i, 1)[0].$init(curUpdate.data, false);
712
718
  }
713
719
  }
714
720
  }
@@ -0,0 +1,35 @@
1
+ export class QueryWeakCache {
2
+ private queries: Map<string, WeakRef<Promise<any>>> = new Map();
3
+
4
+ get(queryJSON: string): Promise<any> | null {
5
+ const weakRef = this.queries.get(queryJSON);
6
+ const deref = weakRef && weakRef.deref();
7
+ if (deref != null) {
8
+ return deref;
9
+ } else {
10
+ this.queries.delete(queryJSON);
11
+ }
12
+ return null;
13
+ }
14
+
15
+ set(queryJSON: string, promise: Promise<any>) {
16
+ this.clean();
17
+
18
+ if (!(promise instanceof Promise)) {
19
+ return;
20
+ }
21
+
22
+ const weakRef = new WeakRef(promise);
23
+
24
+ this.queries.set(queryJSON, weakRef);
25
+ }
26
+
27
+ clean() {
28
+ this.queries.forEach((value, key) => {
29
+ const deref = value && value.deref();
30
+ if (deref == null) {
31
+ this.queries.delete(key);
32
+ }
33
+ });
34
+ }
35
+ }