@ember-data/store 5.3.11 → 5.3.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 (58) hide show
  1. package/LICENSE.md +19 -7
  2. package/README.md +2 -2
  3. package/dist/-private.js +1 -1
  4. package/dist/index.js +1 -1
  5. package/dist/{handler-DX830Swd.js → many-array-BwVo-2vv.js} +1397 -544
  6. package/dist/many-array-BwVo-2vv.js.map +1 -0
  7. package/logos/NCC-1701-a-blue.svg +4 -0
  8. package/logos/NCC-1701-a-gold.svg +4 -0
  9. package/logos/NCC-1701-a-gold_100.svg +1 -0
  10. package/logos/NCC-1701-a-gold_base-64.txt +1 -0
  11. package/logos/NCC-1701-a.svg +4 -0
  12. package/logos/README.md +4 -0
  13. package/logos/docs-badge.svg +2 -0
  14. package/logos/github-header.svg +444 -0
  15. package/logos/social1.png +0 -0
  16. package/logos/social2.png +0 -0
  17. package/logos/warp-drive-logo-dark.svg +4 -0
  18. package/logos/warp-drive-logo-gold.svg +4 -0
  19. package/package.json +26 -45
  20. package/unstable-preview-types/-private/cache-handler/handler.d.ts.map +1 -1
  21. package/unstable-preview-types/-private/cache-handler/utils.d.ts +0 -5
  22. package/unstable-preview-types/-private/cache-handler/utils.d.ts.map +1 -1
  23. package/unstable-preview-types/-private/caches/identifier-cache.d.ts +7 -3
  24. package/unstable-preview-types/-private/caches/identifier-cache.d.ts.map +1 -1
  25. package/unstable-preview-types/-private/caches/instance-cache.d.ts +4 -1
  26. package/unstable-preview-types/-private/caches/instance-cache.d.ts.map +1 -1
  27. package/unstable-preview-types/-private/debug/utils.d.ts +9 -0
  28. package/unstable-preview-types/-private/debug/utils.d.ts.map +1 -0
  29. package/unstable-preview-types/-private/document.d.ts +23 -14
  30. package/unstable-preview-types/-private/document.d.ts.map +1 -1
  31. package/unstable-preview-types/-private/legacy-model-support/shim-model-class.d.ts +3 -3
  32. package/unstable-preview-types/-private/legacy-model-support/shim-model-class.d.ts.map +1 -1
  33. package/unstable-preview-types/-private/managers/cache-capabilities-manager.d.ts +3 -3
  34. package/unstable-preview-types/-private/managers/cache-capabilities-manager.d.ts.map +1 -1
  35. package/unstable-preview-types/-private/managers/cache-manager.d.ts +22 -0
  36. package/unstable-preview-types/-private/managers/cache-manager.d.ts.map +1 -1
  37. package/unstable-preview-types/-private/managers/notification-manager.d.ts +4 -4
  38. package/unstable-preview-types/-private/managers/notification-manager.d.ts.map +1 -1
  39. package/unstable-preview-types/-private/managers/record-array-manager.d.ts +9 -5
  40. package/unstable-preview-types/-private/managers/record-array-manager.d.ts.map +1 -1
  41. package/unstable-preview-types/-private/record-arrays/identifier-array.d.ts +14 -2
  42. package/unstable-preview-types/-private/record-arrays/identifier-array.d.ts.map +1 -1
  43. package/unstable-preview-types/-private/record-arrays/many-array.d.ts +199 -0
  44. package/unstable-preview-types/-private/record-arrays/many-array.d.ts.map +1 -0
  45. package/unstable-preview-types/-private/store-service.d.ts +62 -6
  46. package/unstable-preview-types/-private/store-service.d.ts.map +1 -1
  47. package/unstable-preview-types/-private.d.ts +4 -2
  48. package/unstable-preview-types/-private.d.ts.map +1 -1
  49. package/unstable-preview-types/-types/q/cache-capabilities-manager.d.ts +4 -4
  50. package/unstable-preview-types/-types/q/cache-capabilities-manager.d.ts.map +1 -1
  51. package/unstable-preview-types/-types/q/ds-model.d.ts +3 -3
  52. package/unstable-preview-types/-types/q/ds-model.d.ts.map +1 -1
  53. package/unstable-preview-types/-types/q/schema-service.d.ts +13 -5
  54. package/unstable-preview-types/-types/q/schema-service.d.ts.map +1 -1
  55. package/unstable-preview-types/index.d.ts +22 -20
  56. package/dist/handler-DX830Swd.js.map +0 -1
  57. /package/{ember-data-logo-dark.svg → logos/ember-data-logo-dark.svg} +0 -0
  58. /package/{ember-data-logo-light.svg → logos/ember-data-logo-light.svg} +0 -0
@@ -1,11 +1,12 @@
1
1
  import { deprecate, warn } from '@ember/debug';
2
2
  import { macroCondition, getGlobalConfig, dependencySatisfies, importSync } from '@embroider/macros';
3
3
  import { EnableHydration, SkipCache } from '@warp-drive/core-types/request';
4
- import { getOrSetGlobal, setTransient, peekTransient } from '@warp-drive/core-types/-private';
5
- import { CACHE_OWNER, DEBUG_STALE_CACHE_OWNER, DEBUG_CLIENT_ORIGINATED, DEBUG_IDENTIFIER_BUCKET } from '@warp-drive/core-types/identifier';
6
- import { dasherize } from '@ember-data/request-utils/string';
7
- import { defineSignal, createSignal, subscribe, createArrayTags, addToTransaction, addTransactionCB } from '@ember-data/tracking/-private';
4
+ import { setLogging, getRuntimeConfig } from '@warp-drive/core-types/runtime';
5
+ import { getOrSetGlobal, peekTransient, setTransient } from '@warp-drive/core-types/-private';
8
6
  import { _backburner } from '@ember/runloop';
7
+ import { defineSubscription, notifySignal, defineSignal, createSignal, subscribe, createArrayTags, addToTransaction, addTransactionCB } from '@ember-data/tracking/-private';
8
+ import { CACHE_OWNER, DEBUG_STALE_CACHE_OWNER, DEBUG_IDENTIFIER_BUCKET, DEBUG_CLIENT_ORIGINATED } from '@warp-drive/core-types/identifier';
9
+ import { dasherize } from '@ember-data/request-utils/string';
9
10
  import { compat } from '@ember-data/tracking';
10
11
 
11
12
  /**
@@ -129,10 +130,9 @@ function hasType(resource) {
129
130
  /**
130
131
  @module @ember-data/store
131
132
  */
132
- const IDENTIFIERS = getOrSetGlobal('IDENTIFIERS', new Set());
133
133
  const DOCUMENTS = getOrSetGlobal('DOCUMENTS', new Set());
134
134
  function isStableIdentifier(identifier) {
135
- return identifier[CACHE_OWNER] !== undefined || IDENTIFIERS.has(identifier);
135
+ return identifier[CACHE_OWNER] !== undefined;
136
136
  }
137
137
  function isDocumentIdentifier(identifier) {
138
138
  return DOCUMENTS.has(identifier);
@@ -310,9 +310,11 @@ class IdentifierCache {
310
310
  */
311
311
 
312
312
  _getRecordIdentifier(resource, shouldGenerate) {
313
- if (macroCondition(getGlobalConfig().WarpDrive.debug.LOG_IDENTIFIERS)) {
314
- // eslint-disable-next-line no-console
315
- console.groupCollapsed(`Identifiers: ${shouldGenerate ? 'Generating' : 'Peeking'} Identifier`, resource);
313
+ if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_IDENTIFIERS)) {
314
+ if (getGlobalConfig().WarpDrive.debug.LOG_IDENTIFIERS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_IDENTIFIERS) {
315
+ // eslint-disable-next-line no-console
316
+ console.groupCollapsed(`Identifiers: ${shouldGenerate ? 'Generating' : 'Peeking'} Identifier`, resource);
317
+ }
316
318
  }
317
319
  // short circuit if we're already the stable version
318
320
  if (isStableIdentifier(resource)) {
@@ -322,33 +324,41 @@ class IdentifierCache {
322
324
  throw new Error(`The supplied identifier ${JSON.stringify(resource)} does not belong to this store instance`);
323
325
  }
324
326
  }
325
- if (macroCondition(getGlobalConfig().WarpDrive.debug.LOG_IDENTIFIERS)) {
326
- // eslint-disable-next-line no-console
327
- console.log(`Identifiers: cache HIT - Stable ${resource.lid}`);
328
- // eslint-disable-next-line no-console
329
- console.groupEnd();
327
+ if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_IDENTIFIERS)) {
328
+ if (getGlobalConfig().WarpDrive.debug.LOG_IDENTIFIERS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_IDENTIFIERS) {
329
+ // eslint-disable-next-line no-console
330
+ console.log(`Identifiers: cache HIT - Stable ${resource.lid}`);
331
+ // eslint-disable-next-line no-console
332
+ console.groupEnd();
333
+ }
330
334
  }
331
335
  return resource;
332
336
  }
333
337
 
334
338
  // the resource is unknown, ask the application to identify this data for us
335
339
  const lid = this._generate(resource, 'record');
336
- if (macroCondition(getGlobalConfig().WarpDrive.debug.LOG_IDENTIFIERS)) {
337
- // eslint-disable-next-line no-console
338
- console.log(`Identifiers: ${lid ? 'no ' : ''}lid ${lid ? lid + ' ' : ''}determined for resource`, resource);
340
+ if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_IDENTIFIERS)) {
341
+ if (getGlobalConfig().WarpDrive.debug.LOG_IDENTIFIERS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_IDENTIFIERS) {
342
+ // eslint-disable-next-line no-console
343
+ console.log(`Identifiers: ${lid ? 'no ' : ''}lid ${lid ? lid + ' ' : ''}determined for resource`, resource);
344
+ }
339
345
  }
340
346
  let identifier = /*#__NOINLINE__*/getIdentifierFromLid(this._cache, lid, resource);
341
347
  if (identifier !== null) {
342
- if (macroCondition(getGlobalConfig().WarpDrive.debug.LOG_IDENTIFIERS)) {
343
- // eslint-disable-next-line no-console
344
- console.groupEnd();
348
+ if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_IDENTIFIERS)) {
349
+ if (getGlobalConfig().WarpDrive.debug.LOG_IDENTIFIERS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_IDENTIFIERS) {
350
+ // eslint-disable-next-line no-console
351
+ console.groupEnd();
352
+ }
345
353
  }
346
354
  return identifier;
347
355
  }
348
356
  if (shouldGenerate === 0) {
349
- if (macroCondition(getGlobalConfig().WarpDrive.debug.LOG_IDENTIFIERS)) {
350
- // eslint-disable-next-line no-console
351
- console.groupEnd();
357
+ if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_IDENTIFIERS)) {
358
+ if (getGlobalConfig().WarpDrive.debug.LOG_IDENTIFIERS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_IDENTIFIERS) {
359
+ // eslint-disable-next-line no-console
360
+ console.groupEnd();
361
+ }
352
362
  }
353
363
  return;
354
364
  }
@@ -366,9 +376,11 @@ class IdentifierCache {
366
376
  identifier = /*#__NOINLINE__*/makeStableRecordIdentifier(keyInfo, 'record', false);
367
377
  }
368
378
  addResourceToCache(this._cache, identifier);
369
- if (macroCondition(getGlobalConfig().WarpDrive.debug.LOG_IDENTIFIERS)) {
370
- // eslint-disable-next-line no-console
371
- console.groupEnd();
379
+ if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_IDENTIFIERS)) {
380
+ if (getGlobalConfig().WarpDrive.debug.LOG_IDENTIFIERS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_IDENTIFIERS) {
381
+ // eslint-disable-next-line no-console
382
+ console.groupEnd();
383
+ }
372
384
  }
373
385
  return identifier;
374
386
  }
@@ -462,9 +474,11 @@ class IdentifierCache {
462
474
 
463
475
  /*#__NOINLINE__*/
464
476
  addResourceToCache(this._cache, identifier);
465
- if (macroCondition(getGlobalConfig().WarpDrive.debug.LOG_IDENTIFIERS)) {
466
- // eslint-disable-next-line no-console
467
- console.log(`Identifiers: created identifier ${String(identifier)} for newly generated resource`, data);
477
+ if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_IDENTIFIERS)) {
478
+ if (getGlobalConfig().WarpDrive.debug.LOG_IDENTIFIERS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_IDENTIFIERS) {
479
+ // eslint-disable-next-line no-console
480
+ console.log(`Identifiers: created identifier ${String(identifier)} for newly generated resource`, data);
481
+ }
468
482
  }
469
483
  return identifier;
470
484
  }
@@ -511,9 +525,11 @@ class IdentifierCache {
511
525
  if (hadLid) {
512
526
  data.lid = identifier.lid;
513
527
  }
514
- if (macroCondition(getGlobalConfig().WarpDrive.debug.LOG_IDENTIFIERS)) {
515
- // eslint-disable-next-line no-console
516
- console.log(`Identifiers: merged identifiers ${generatedIdentifier.lid} and ${existingIdentifier.lid} for resource into ${identifier.lid}`, data);
528
+ if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_IDENTIFIERS)) {
529
+ if (getGlobalConfig().WarpDrive.debug.LOG_IDENTIFIERS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_IDENTIFIERS) {
530
+ // eslint-disable-next-line no-console
531
+ console.log(`Identifiers: merged identifiers ${generatedIdentifier.lid} and ${existingIdentifier.lid} for resource into ${identifier.lid}`, data);
532
+ }
517
533
  }
518
534
  }
519
535
  const id = identifier.id;
@@ -523,9 +539,11 @@ class IdentifierCache {
523
539
 
524
540
  // add to our own secondary lookup table
525
541
  if (id !== newId && newId !== null) {
526
- if (macroCondition(getGlobalConfig().WarpDrive.debug.LOG_IDENTIFIERS)) {
527
- // eslint-disable-next-line no-console
528
- console.log(`Identifiers: updated id for identifier ${identifier.lid} from '${String(id)}' to '${String(newId)}' for resource`, data);
542
+ if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_IDENTIFIERS)) {
543
+ if (getGlobalConfig().WarpDrive.debug.LOG_IDENTIFIERS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_IDENTIFIERS) {
544
+ // eslint-disable-next-line no-console
545
+ console.log(`Identifiers: updated id for identifier ${identifier.lid} from '${String(id)}' to '${String(newId)}' for resource`, data);
546
+ }
529
547
  }
530
548
  const typeSet = this._cache.resourcesByType[identifier.type];
531
549
  macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
@@ -537,9 +555,11 @@ class IdentifierCache {
537
555
  if (id !== null) {
538
556
  typeSet.id.delete(id);
539
557
  }
540
- } else if (macroCondition(getGlobalConfig().WarpDrive.debug.LOG_IDENTIFIERS)) {
541
- // eslint-disable-next-line no-console
542
- console.log(`Identifiers: updated identifier ${identifier.lid} resource`, data);
558
+ } else if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_IDENTIFIERS)) {
559
+ if (getGlobalConfig().WarpDrive.debug.LOG_IDENTIFIERS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_IDENTIFIERS) {
560
+ // eslint-disable-next-line no-console
561
+ console.log(`Identifiers: updated identifier ${identifier.lid} resource`, data);
562
+ }
543
563
  }
544
564
  return identifier;
545
565
  }
@@ -621,11 +641,12 @@ class IdentifierCache {
621
641
  identifier[DEBUG_STALE_CACHE_OWNER] = identifier[CACHE_OWNER];
622
642
  }
623
643
  identifier[CACHE_OWNER] = undefined;
624
- IDENTIFIERS.delete(identifier);
625
644
  this._forget(identifier, 'record');
626
- if (macroCondition(getGlobalConfig().WarpDrive.debug.LOG_IDENTIFIERS)) {
627
- // eslint-disable-next-line no-console
628
- console.log(`Identifiers: released identifier ${identifierObject.lid}`);
645
+ if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_IDENTIFIERS)) {
646
+ if (getGlobalConfig().WarpDrive.debug.LOG_IDENTIFIERS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_IDENTIFIERS) {
647
+ // eslint-disable-next-line no-console
648
+ console.log(`Identifiers: released identifier ${identifierObject.lid}`);
649
+ }
629
650
  }
630
651
  }
631
652
  destroy() {
@@ -637,20 +658,17 @@ class IdentifierCache {
637
658
  }
638
659
  }
639
660
  function makeStableRecordIdentifier(recordIdentifier, bucket, clientOriginated) {
640
- IDENTIFIERS.add(recordIdentifier);
641
661
  if (macroCondition(getGlobalConfig().WarpDrive.env.DEBUG)) {
642
662
  // we enforce immutability in dev
643
663
  // but preserve our ability to do controlled updates to the reference
644
664
  let wrapper = {
645
- get lid() {
646
- return recordIdentifier.lid;
647
- },
665
+ type: recordIdentifier.type,
666
+ lid: recordIdentifier.lid,
648
667
  get id() {
649
668
  return recordIdentifier.id;
650
- },
651
- get type() {
652
- return recordIdentifier.type;
653
- },
669
+ }
670
+ };
671
+ const proto = {
654
672
  get [CACHE_OWNER]() {
655
673
  return recordIdentifier[CACHE_OWNER];
656
674
  },
@@ -662,9 +680,15 @@ function makeStableRecordIdentifier(recordIdentifier, bucket, clientOriginated)
662
680
  },
663
681
  set [DEBUG_STALE_CACHE_OWNER](value) {
664
682
  recordIdentifier[DEBUG_STALE_CACHE_OWNER] = value;
683
+ },
684
+ get [DEBUG_CLIENT_ORIGINATED]() {
685
+ return clientOriginated;
686
+ },
687
+ get [DEBUG_IDENTIFIER_BUCKET]() {
688
+ return bucket;
665
689
  }
666
690
  };
667
- Object.defineProperty(wrapper, 'toString', {
691
+ Object.defineProperty(proto, 'toString', {
668
692
  enumerable: false,
669
693
  value: () => {
670
694
  const {
@@ -675,7 +699,7 @@ function makeStableRecordIdentifier(recordIdentifier, bucket, clientOriginated)
675
699
  return `${clientOriginated ? '[CLIENT_ORIGINATED] ' : ''}${String(type)}:${String(id)} (${lid})`;
676
700
  }
677
701
  });
678
- Object.defineProperty(wrapper, 'toJSON', {
702
+ Object.defineProperty(proto, 'toJSON', {
679
703
  enumerable: false,
680
704
  value: () => {
681
705
  const {
@@ -690,9 +714,7 @@ function makeStableRecordIdentifier(recordIdentifier, bucket, clientOriginated)
690
714
  };
691
715
  }
692
716
  });
693
- wrapper[DEBUG_CLIENT_ORIGINATED] = clientOriginated;
694
- wrapper[DEBUG_IDENTIFIER_BUCKET] = bucket;
695
- IDENTIFIERS.add(wrapper);
717
+ Object.setPrototypeOf(wrapper, proto);
696
718
  DEBUG_MAP.set(wrapper, recordIdentifier);
697
719
  wrapper = freeze(wrapper);
698
720
  return wrapper;
@@ -775,9 +797,11 @@ function detectMerge(cache, keyInfo, identifier, data) {
775
797
  }
776
798
  function getIdentifierFromLid(cache, lid, resource) {
777
799
  const identifier = cache.resources.get(lid);
778
- if (macroCondition(getGlobalConfig().WarpDrive.debug.LOG_IDENTIFIERS)) {
779
- // eslint-disable-next-line no-console
780
- console.log(`Identifiers: cache ${identifier ? 'HIT' : 'MISS'} - Non-Stable ${lid}`, resource);
800
+ if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_IDENTIFIERS)) {
801
+ if (getGlobalConfig().WarpDrive.debug.LOG_IDENTIFIERS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_IDENTIFIERS) {
802
+ // eslint-disable-next-line no-console
803
+ console.log(`Identifiers: cache ${identifier ? 'HIT' : 'MISS'} - Non-Stable ${lid}`, resource);
804
+ }
781
805
  }
782
806
  return identifier || null;
783
807
  }
@@ -796,77 +820,461 @@ function addResourceToCache(cache, identifier) {
796
820
  typeSet.id.set(identifier.id, identifier);
797
821
  }
798
822
  }
823
+ const TEXT_COLORS = {
824
+ TEXT: 'inherit',
825
+ notify: ['white', 'white', 'inherit', 'magenta', 'inherit'],
826
+ 'reactive-ui': ['white', 'white', 'inherit', 'magenta', 'inherit'],
827
+ graph: ['white', 'white', 'inherit', 'magenta', 'inherit'],
828
+ request: ['white', 'white', 'inherit', 'magenta', 'inherit'],
829
+ cache: ['white', 'white', 'inherit', 'magenta', 'inherit']
830
+ };
831
+ const BG_COLORS = {
832
+ TEXT: 'transparent',
833
+ notify: ['dimgray', 'cadetblue', 'transparent', 'transparent', 'transparent'],
834
+ 'reactive-ui': ['dimgray', 'cadetblue', 'transparent', 'transparent', 'transparent'],
835
+ graph: ['dimgray', 'cadetblue', 'transparent', 'transparent', 'transparent'],
836
+ request: ['dimgray', 'cadetblue', 'transparent', 'transparent', 'transparent'],
837
+ cache: ['dimgray', 'cadetblue', 'transparent', 'transparent', 'transparent']
838
+ };
839
+ const NOTIFY_BORDER = {
840
+ TEXT: 0,
841
+ notify: [3, 2, 0, 0, 0],
842
+ 'reactive-ui': [3, 2, 0, 0, 0],
843
+ graph: [3, 2, 0, 0, 0],
844
+ request: [3, 2, 0, 0, 0],
845
+ cache: [3, 2, 0, 0, 0]
846
+ };
847
+ const LIGHT_DARK_ALT = {
848
+ lightgreen: 'green',
849
+ green: 'lightgreen'
850
+ };
851
+ function badge(isLight, color, bgColor, border) {
852
+ return [`color: ${correctColor(isLight, color)}; background-color: ${correctColor(isLight, bgColor)}; padding: ${border}px ${2 * border}px; border-radius: ${border}px;`, `color: ${TEXT_COLORS.TEXT}; background-color: ${BG_COLORS.TEXT};`];
853
+ }
854
+ function colorForBucket(isLight, scope, bucket) {
855
+ if (scope === 'notify') {
856
+ return bucket === 'added' ? badge(isLight, 'lightgreen', 'transparent', 0) : bucket === 'removed' ? badge(isLight, 'red', 'transparent', 0) : badge(isLight, TEXT_COLORS[scope][2], BG_COLORS[scope][2], NOTIFY_BORDER[scope][2]);
857
+ }
858
+ if (scope === 'reactive-ui') {
859
+ return bucket === 'created' ? badge(isLight, 'lightgreen', 'transparent', 0) : bucket === 'disconnected' ? badge(isLight, 'red', 'transparent', 0) : badge(isLight, TEXT_COLORS[scope][2], BG_COLORS[scope][2], NOTIFY_BORDER[scope][2]);
860
+ }
861
+ if (scope === 'cache') {
862
+ return bucket === 'inserted' ? badge(isLight, 'lightgreen', 'transparent', 0) : bucket === 'removed' ? badge(isLight, 'red', 'transparent', 0) : badge(isLight, TEXT_COLORS[scope][2], BG_COLORS[scope][2], NOTIFY_BORDER[scope][2]);
863
+ }
864
+ return badge(isLight, TEXT_COLORS[scope][3], BG_COLORS[scope][3], NOTIFY_BORDER[scope][3]);
865
+ }
866
+ function logGroup(scope, prefix, subScop1, subScop2, subScop3, subScop4) {
867
+ // eslint-disable-next-line no-console
868
+ console.groupCollapsed(..._log(scope, prefix, subScop1, subScop2, subScop3, subScop4));
869
+ }
870
+ function log(scope, prefix, subScop1, subScop2, subScop3, subScop4) {
871
+ // eslint-disable-next-line no-console
872
+ console.log(..._log(scope, prefix, subScop1, subScop2, subScop3, subScop4));
873
+ }
874
+ function correctColor(isLight, color) {
875
+ if (!isLight) {
876
+ return color;
877
+ }
878
+ return color in LIGHT_DARK_ALT ? LIGHT_DARK_ALT[color] : color;
879
+ }
880
+ function isLightMode() {
881
+ if (window?.matchMedia?.('(prefers-color-scheme: light)').matches) {
882
+ return true;
883
+ }
884
+ return false;
885
+ }
886
+ function _log(scope, prefix, subScop1, subScop2, subScop3, subScop4) {
887
+ const isLight = isLightMode();
888
+ switch (scope) {
889
+ case 'reactive-ui':
890
+ case 'notify':
891
+ {
892
+ const scopePath = prefix ? `[${prefix}] ${scope}` : scope;
893
+ const path = subScop4 ? `${subScop3}.${subScop4}` : subScop3;
894
+ return [`%c@warp%c-%cdrive%c %c${scopePath}%c %c${subScop1}%c %c${subScop2}%c %c${path}%c`, ...badge(isLight, 'lightgreen', 'transparent', 0), ...badge(isLight, 'magenta', 'transparent', 0), ...badge(isLight, TEXT_COLORS[scope][0], BG_COLORS[scope][0], NOTIFY_BORDER[scope][0]), ...badge(isLight, TEXT_COLORS[scope][1], BG_COLORS[scope][1], NOTIFY_BORDER[scope][1]), ...badge(isLight, TEXT_COLORS[scope][2], BG_COLORS[scope][2], NOTIFY_BORDER[scope][2]), ...colorForBucket(isLight, scope, path)];
895
+ }
896
+ case 'cache':
897
+ {
898
+ const scopePath = prefix ? `${scope} (${prefix})` : scope;
899
+ return [`%c@warp%c-%cdrive%c %c${scopePath}%c %c${subScop1}%c %c${subScop2}%c %c${subScop3}%c %c${subScop4}%c`, ...badge(isLight, 'lightgreen', 'transparent', 0), ...badge(isLight, 'magenta', 'transparent', 0), ...badge(isLight, TEXT_COLORS[scope][0], BG_COLORS[scope][0], NOTIFY_BORDER[scope][0]), ...badge(isLight, TEXT_COLORS[scope][1], BG_COLORS[scope][1], NOTIFY_BORDER[scope][1]), ...badge(isLight, TEXT_COLORS[scope][2], BG_COLORS[scope][2], NOTIFY_BORDER[scope][2]), ...colorForBucket(isLight, scope, subScop3), ...badge(isLight, TEXT_COLORS[scope][4], BG_COLORS[scope][4], NOTIFY_BORDER[scope][4])];
900
+ }
901
+ }
902
+ return [];
903
+ }
799
904
 
800
905
  /**
801
- @module @ember-data/store
802
- */
906
+ * @module @ember-data/store
907
+ */
908
+ function urlFromLink(link) {
909
+ if (typeof link === 'string') return link;
910
+ return link.href;
911
+ }
803
912
 
804
913
  /**
805
- A `RecordReference` is a low-level API that allows users and
806
- addon authors to perform meta-operations on a record.
914
+ * A Document is a class that wraps the response content from a request to the API
915
+ * returned by `Cache.put` or `Cache.peek`, converting resource-identifiers into
916
+ * record instances.
917
+ *
918
+ * It is not directly instantiated by the user, and its properties should not
919
+ * be directly modified. Whether individual properties are mutable or not is
920
+ * determined by the record instance itself.
921
+ *
922
+ * @public
923
+ * @class ReactiveDocument
924
+ */
925
+ class ReactiveDocument {
926
+ /**
927
+ * The links object for this document, if any
928
+ *
929
+ * e.g.
930
+ *
931
+ * ```
932
+ * {
933
+ * self: '/articles?page[number]=3',
934
+ * }
935
+ * ```
936
+ *
937
+ * @property links
938
+ * @type {object|undefined} - a links object
939
+ * @public
940
+ */
807
941
 
808
- @class RecordReference
809
- @public
810
- */
811
- class RecordReference {
812
- // unsubscribe token given to us by the notification manager
813
- ___token;
814
- ___identifier;
815
- constructor(store, identifier) {
816
- this.store = store;
817
- this.___identifier = identifier;
818
- this.___token = store.notifications.subscribe(identifier, (_, bucket, notifiedKey) => {
819
- if (bucket === 'identity' || bucket === 'attributes' && notifiedKey === 'id') {
820
- this._ref++;
821
- }
822
- });
942
+ /**
943
+ * The primary data for this document, if any.
944
+ *
945
+ * If this document has no primary data (e.g. because it is an error document)
946
+ * this property will be `undefined`.
947
+ *
948
+ * For collections this will be an array of record instances,
949
+ * for single resource requests it will be a single record instance or null.
950
+ *
951
+ * @property data
952
+ * @public
953
+ * @type {object|Array<object>|null|undefined} - a data object
954
+ */
955
+
956
+ /**
957
+ * The errors returned by the API for this request, if any
958
+ *
959
+ * @property errors
960
+ * @public
961
+ * @type {object|undefined} - an errors object
962
+ */
963
+
964
+ /**
965
+ * The meta object for this document, if any
966
+ *
967
+ * @property meta
968
+ * @public
969
+ * @type {object|undefined} - a meta object
970
+ */
971
+
972
+ /**
973
+ * The identifier associated with this document, if any
974
+ *
975
+ * @property identifier
976
+ * @public
977
+ * @type {StableDocumentIdentifier|null}
978
+ */
979
+
980
+ constructor(store, identifier, localCache) {
981
+ this._store = store;
982
+ this._localCache = localCache;
983
+ this.identifier = identifier;
984
+
985
+ // TODO if we ever enable auto-cleanup of the cache, we will need to tear this down
986
+ // in a destroy method
987
+ if (identifier) {
988
+ store.notifications.subscribe(identifier, (_identifier, type) => {
989
+ switch (type) {
990
+ case 'updated':
991
+ // FIXME in the case of a collection we need to notify it's length
992
+ // and have it recalc
993
+ notifySignal(this, 'data');
994
+ notifySignal(this, 'links');
995
+ notifySignal(this, 'meta');
996
+ notifySignal(this, 'errors');
997
+ break;
998
+ }
999
+ });
1000
+ }
823
1001
  }
824
- destroy() {
825
- this.store.notifications.unsubscribe(this.___token);
1002
+ async #request(link, options) {
1003
+ const href = this.links?.[link];
1004
+ if (!href) {
1005
+ return null;
1006
+ }
1007
+ options.method = options.method || 'GET';
1008
+ Object.assign(options, {
1009
+ url: urlFromLink(href)
1010
+ });
1011
+ const response = await this._store.request(options);
1012
+ return response.content;
826
1013
  }
827
- get type() {
828
- return this.identifier().type;
1014
+
1015
+ /**
1016
+ * Fetches the related link for this document, returning a promise that resolves
1017
+ * with the document when the request completes. If no related link is present,
1018
+ * will fallback to the self link if present
1019
+ *
1020
+ * @method fetch
1021
+ * @public
1022
+ * @param {object} options
1023
+ * @return Promise<Document>
1024
+ */
1025
+ fetch(options = {}) {
1026
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
1027
+ if (!test) {
1028
+ throw new Error(`No self or related link`);
1029
+ }
1030
+ })(this.links?.related || this.links?.self) : {};
1031
+ options.cacheOptions = options.cacheOptions || {};
1032
+ options.cacheOptions.key = this.identifier?.lid;
1033
+ return this.#request(this.links.related ? 'related' : 'self', options);
829
1034
  }
830
1035
 
831
1036
  /**
832
- The `id` of the record that this reference refers to.
833
- Together, the `type` and `id` properties form a composite key for
834
- the identity map.
835
- Example
836
- ```javascript
837
- let userRef = store.getReference('user', 1);
838
- userRef.id(); // '1'
839
- ```
840
- @method id
841
- @public
842
- @return {String} The id of the record.
843
- */
844
- id() {
845
- // eslint-disable-next-line @typescript-eslint/no-unused-expressions
846
- this._ref; // consume the tracked prop
847
- return this.___identifier.id;
1037
+ * Fetches the next link for this document, returning a promise that resolves
1038
+ * with the new document when the request completes, or null if there is no
1039
+ * next link.
1040
+ *
1041
+ * @method next
1042
+ * @public
1043
+ * @param {object} options
1044
+ * @return Promise<Document | null>
1045
+ */
1046
+ next(options = {}) {
1047
+ return this.#request('next', options);
848
1048
  }
849
1049
 
850
1050
  /**
851
- The `identifier` of the record that this reference refers to.
852
- Together, the `type` and `id` properties form a composite key for
853
- the identity map.
854
- Example
855
- ```javascript
856
- let userRef = store.getReference('user', 1);
857
- userRef.identifier(); // '1'
858
- ```
859
- @method identifier
860
- @public
861
- @return {String} The identifier of the record.
862
- */
863
- identifier() {
864
- return this.___identifier;
1051
+ * Fetches the prev link for this document, returning a promise that resolves
1052
+ * with the new document when the request completes, or null if there is no
1053
+ * prev link.
1054
+ *
1055
+ * @method prev
1056
+ * @public
1057
+ * @param {object} options
1058
+ * @return Promise<Document | null>
1059
+ */
1060
+ prev(options = {}) {
1061
+ return this.#request('prev', options);
865
1062
  }
866
1063
 
867
1064
  /**
868
- How the reference will be looked up when it is loaded. Currently
869
- this always returns `identity` to signify that a record will be
1065
+ * Fetches the first link for this document, returning a promise that resolves
1066
+ * with the new document when the request completes, or null if there is no
1067
+ * first link.
1068
+ *
1069
+ * @method first
1070
+ * @public
1071
+ * @param {object} options
1072
+ * @return Promise<Document | null>
1073
+ */
1074
+ first(options = {}) {
1075
+ return this.#request('first', options);
1076
+ }
1077
+
1078
+ /**
1079
+ * Fetches the last link for this document, returning a promise that resolves
1080
+ * with the new document when the request completes, or null if there is no
1081
+ * last link.
1082
+ *
1083
+ * @method last
1084
+ * @public
1085
+ * @param {object} options
1086
+ * @return Promise<Document | null>
1087
+ */
1088
+ last(options = {}) {
1089
+ return this.#request('last', options);
1090
+ }
1091
+
1092
+ /**
1093
+ * Implemented for `JSON.stringify` support.
1094
+ *
1095
+ * Returns the JSON representation of the document wrapper.
1096
+ *
1097
+ * This is a shallow serialization, it does not deeply serialize
1098
+ * the document's contents, leaving that to the individual record
1099
+ * instances to determine how to do, if at all.
1100
+ *
1101
+ * @method toJSON
1102
+ * @public
1103
+ * @return
1104
+ */
1105
+ toJSON() {
1106
+ const data = {};
1107
+ data.identifier = this.identifier;
1108
+ if (this.data !== undefined) {
1109
+ data.data = this.data;
1110
+ }
1111
+ if (this.links !== undefined) {
1112
+ data.links = this.links;
1113
+ }
1114
+ if (this.errors !== undefined) {
1115
+ data.errors = this.errors;
1116
+ }
1117
+ if (this.meta !== undefined) {
1118
+ data.meta = this.meta;
1119
+ }
1120
+ return data;
1121
+ }
1122
+ }
1123
+ defineSubscription(ReactiveDocument.prototype, 'errors', {
1124
+ get() {
1125
+ const {
1126
+ identifier
1127
+ } = this;
1128
+ if (!identifier) {
1129
+ const {
1130
+ document
1131
+ } = this._localCache;
1132
+ if ('errors' in document) {
1133
+ return document.errors;
1134
+ }
1135
+ return;
1136
+ }
1137
+ const doc = this._store.cache.peek(identifier);
1138
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
1139
+ if (!test) {
1140
+ throw new Error(`No cache data was found for the document '${identifier.lid}'`);
1141
+ }
1142
+ })(doc) : {};
1143
+ return 'errors' in doc ? doc.errors : undefined;
1144
+ }
1145
+ });
1146
+ defineSubscription(ReactiveDocument.prototype, 'data', {
1147
+ get() {
1148
+ const {
1149
+ identifier,
1150
+ _localCache
1151
+ } = this;
1152
+ const doc = identifier ? this._store.cache.peek(identifier) : _localCache.document;
1153
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
1154
+ if (!test) {
1155
+ throw new Error(`No cache data was found for the document '${identifier?.lid ?? '<uncached document>'}'`);
1156
+ }
1157
+ })(doc) : {};
1158
+ const data = 'data' in doc ? doc.data : undefined;
1159
+ if (Array.isArray(data)) {
1160
+ return this._store.recordArrayManager.getCollection({
1161
+ type: identifier ? identifier.lid : _localCache.request.url,
1162
+ identifiers: data.slice(),
1163
+ doc: identifier ? undefined : doc,
1164
+ identifier: identifier ?? null
1165
+ });
1166
+ } else if (data) {
1167
+ return this._store.peekRecord(data);
1168
+ } else {
1169
+ return data;
1170
+ }
1171
+ }
1172
+ });
1173
+ defineSubscription(ReactiveDocument.prototype, 'links', {
1174
+ get() {
1175
+ const {
1176
+ identifier
1177
+ } = this;
1178
+ if (!identifier) {
1179
+ return this._localCache.document.links;
1180
+ }
1181
+ const data = this._store.cache.peek(identifier);
1182
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
1183
+ if (!test) {
1184
+ throw new Error(`No cache data was found for the document '${identifier.lid}'`);
1185
+ }
1186
+ })(data) : {};
1187
+ return data.links;
1188
+ }
1189
+ });
1190
+ defineSubscription(ReactiveDocument.prototype, 'meta', {
1191
+ get() {
1192
+ const {
1193
+ identifier
1194
+ } = this;
1195
+ if (!identifier) {
1196
+ return this._localCache.document.meta;
1197
+ }
1198
+ const data = this._store.cache.peek(identifier);
1199
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
1200
+ if (!test) {
1201
+ throw new Error(`No cache data was found for the document '${identifier.lid}'`);
1202
+ }
1203
+ })(data) : {};
1204
+ return data.meta;
1205
+ }
1206
+ });
1207
+
1208
+ /**
1209
+ @module @ember-data/store
1210
+ */
1211
+
1212
+ /**
1213
+ A `RecordReference` is a low-level API that allows users and
1214
+ addon authors to perform meta-operations on a record.
1215
+
1216
+ @class RecordReference
1217
+ @public
1218
+ */
1219
+ class RecordReference {
1220
+ // unsubscribe token given to us by the notification manager
1221
+ ___token;
1222
+ ___identifier;
1223
+ constructor(store, identifier) {
1224
+ this.store = store;
1225
+ this.___identifier = identifier;
1226
+ this.___token = store.notifications.subscribe(identifier, (_, bucket, notifiedKey) => {
1227
+ if (bucket === 'identity' || bucket === 'attributes' && notifiedKey === 'id') {
1228
+ this._ref++;
1229
+ }
1230
+ });
1231
+ }
1232
+ destroy() {
1233
+ this.store.notifications.unsubscribe(this.___token);
1234
+ }
1235
+ get type() {
1236
+ return this.identifier().type;
1237
+ }
1238
+
1239
+ /**
1240
+ The `id` of the record that this reference refers to.
1241
+ Together, the `type` and `id` properties form a composite key for
1242
+ the identity map.
1243
+ Example
1244
+ ```javascript
1245
+ let userRef = store.getReference('user', 1);
1246
+ userRef.id(); // '1'
1247
+ ```
1248
+ @method id
1249
+ @public
1250
+ @return {String} The id of the record.
1251
+ */
1252
+ id() {
1253
+ // eslint-disable-next-line @typescript-eslint/no-unused-expressions
1254
+ this._ref; // consume the tracked prop
1255
+ return this.___identifier.id;
1256
+ }
1257
+
1258
+ /**
1259
+ The `identifier` of the record that this reference refers to.
1260
+ Together, the `type` and `id` properties form a composite key for
1261
+ the identity map.
1262
+ Example
1263
+ ```javascript
1264
+ let userRef = store.getReference('user', 1);
1265
+ userRef.identifier(); // '1'
1266
+ ```
1267
+ @method identifier
1268
+ @public
1269
+ @return {String} The identifier of the record.
1270
+ */
1271
+ identifier() {
1272
+ return this.___identifier;
1273
+ }
1274
+
1275
+ /**
1276
+ How the reference will be looked up when it is loaded. Currently
1277
+ this always returns `identity` to signify that a record will be
870
1278
  loaded by its `type` and `id`.
871
1279
  Example
872
1280
  ```javascript
@@ -1177,12 +1585,13 @@ function storeFor(record) {
1177
1585
  return store;
1178
1586
  }
1179
1587
  class InstanceCache {
1180
- __instances = {
1181
- record: new Map(),
1182
- reference: new WeakMap()
1183
- };
1184
1588
  constructor(store) {
1185
1589
  this.store = store;
1590
+ this.__instances = {
1591
+ record: new Map(),
1592
+ reference: new WeakMap(),
1593
+ document: new Map()
1594
+ };
1186
1595
  this._storeWrapper = new CacheCapabilitiesManager(this.store);
1187
1596
  store.identifierCache.__configureMerge((identifier, matchedIdentifier, resourceData) => {
1188
1597
  let keptIdentifier = identifier;
@@ -1237,6 +1646,14 @@ class InstanceCache {
1237
1646
  peek(identifier) {
1238
1647
  return this.__instances.record.get(identifier);
1239
1648
  }
1649
+ getDocument(identifier) {
1650
+ let doc = this.__instances.document.get(identifier);
1651
+ if (!doc) {
1652
+ doc = new ReactiveDocument(this.store, identifier, null);
1653
+ this.__instances.document.set(identifier, doc);
1654
+ }
1655
+ return doc;
1656
+ }
1240
1657
  getRecord(identifier, properties) {
1241
1658
  let record = this.__instances.record.get(identifier);
1242
1659
  if (!record) {
@@ -1252,9 +1669,16 @@ class InstanceCache {
1252
1669
  setCacheFor(record, cache);
1253
1670
  StoreMap.set(record, this.store);
1254
1671
  this.__instances.record.set(identifier, record);
1255
- if (macroCondition(getGlobalConfig().WarpDrive.debug.LOG_INSTANCE_CACHE)) {
1256
- // eslint-disable-next-line no-console
1257
- console.log(`InstanceCache: created Record for ${String(identifier)}`, properties);
1672
+ if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_INSTANCE_CACHE)) {
1673
+ if (getGlobalConfig().WarpDrive.debug.LOG_INSTANCE_CACHE || globalThis.getWarpDriveRuntimeConfig().debug.LOG_INSTANCE_CACHE) {
1674
+ logGroup('reactive-ui', '', identifier.type, identifier.lid, 'created', '');
1675
+ // eslint-disable-next-line no-console
1676
+ console.log({
1677
+ properties
1678
+ });
1679
+ // eslint-disable-next-line no-console
1680
+ console.groupEnd();
1681
+ }
1258
1682
  }
1259
1683
  }
1260
1684
  return record;
@@ -1300,9 +1724,10 @@ class InstanceCache {
1300
1724
  this.store.identifierCache.forgetRecordIdentifier(identifier);
1301
1725
  removeRecordDataFor(identifier);
1302
1726
  this.store._requestCache._clearEntries(identifier);
1303
- if (macroCondition(getGlobalConfig().WarpDrive.debug.LOG_INSTANCE_CACHE)) {
1304
- // eslint-disable-next-line no-console
1305
- console.log(`InstanceCache: disconnected ${String(identifier)}`);
1727
+ if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_INSTANCE_CACHE)) {
1728
+ if (getGlobalConfig().WarpDrive.debug.LOG_INSTANCE_CACHE || globalThis.getWarpDriveRuntimeConfig().debug.LOG_INSTANCE_CACHE) {
1729
+ log('reactive-ui', '', identifier.type, identifier.lid, 'disconnected', '');
1730
+ }
1306
1731
  }
1307
1732
  }
1308
1733
  unloadRecord(identifier) {
@@ -1318,9 +1743,11 @@ class InstanceCache {
1318
1743
  })() : {};
1319
1744
  }
1320
1745
  }
1321
- if (macroCondition(getGlobalConfig().WarpDrive.debug.LOG_INSTANCE_CACHE)) {
1322
- // eslint-disable-next-line no-console
1323
- console.groupCollapsed(`InstanceCache: unloading record for ${String(identifier)}`);
1746
+ if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_INSTANCE_CACHE)) {
1747
+ if (getGlobalConfig().WarpDrive.debug.LOG_INSTANCE_CACHE || globalThis.getWarpDriveRuntimeConfig().debug.LOG_INSTANCE_CACHE) {
1748
+ // eslint-disable-next-line no-console
1749
+ console.groupCollapsed(`InstanceCache: unloading record for ${String(identifier)}`);
1750
+ }
1324
1751
  }
1325
1752
 
1326
1753
  // TODO is this join still necessary?
@@ -1333,27 +1760,33 @@ class InstanceCache {
1333
1760
  StoreMap.delete(record);
1334
1761
  RecordCache.delete(record);
1335
1762
  removeRecordDataFor(record);
1336
- if (macroCondition(getGlobalConfig().WarpDrive.debug.LOG_INSTANCE_CACHE)) {
1337
- // eslint-disable-next-line no-console
1338
- console.log(`InstanceCache: destroyed record for ${String(identifier)}`);
1763
+ if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_INSTANCE_CACHE)) {
1764
+ if (getGlobalConfig().WarpDrive.debug.LOG_INSTANCE_CACHE || globalThis.getWarpDriveRuntimeConfig().debug.LOG_INSTANCE_CACHE) {
1765
+ // eslint-disable-next-line no-console
1766
+ console.log(`InstanceCache: destroyed record for ${String(identifier)}`);
1767
+ }
1339
1768
  }
1340
1769
  }
1341
1770
  if (cache) {
1342
1771
  cache.unloadRecord(identifier);
1343
1772
  removeRecordDataFor(identifier);
1344
- if (macroCondition(getGlobalConfig().WarpDrive.debug.LOG_INSTANCE_CACHE)) {
1345
- // eslint-disable-next-line no-console
1346
- console.log(`InstanceCache: destroyed cache for ${String(identifier)}`);
1773
+ if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_INSTANCE_CACHE)) {
1774
+ if (getGlobalConfig().WarpDrive.debug.LOG_INSTANCE_CACHE || globalThis.getWarpDriveRuntimeConfig().debug.LOG_INSTANCE_CACHE) {
1775
+ // eslint-disable-next-line no-console
1776
+ console.log(`InstanceCache: destroyed cache for ${String(identifier)}`);
1777
+ }
1347
1778
  }
1348
1779
  } else {
1349
1780
  this.disconnect(identifier);
1350
1781
  }
1351
1782
  this.store._requestCache._clearEntries(identifier);
1352
- if (macroCondition(getGlobalConfig().WarpDrive.debug.LOG_INSTANCE_CACHE)) {
1353
- // eslint-disable-next-line no-console
1354
- console.log(`InstanceCache: unloaded RecordData for ${String(identifier)}`);
1355
- // eslint-disable-next-line no-console
1356
- console.groupEnd();
1783
+ if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_INSTANCE_CACHE)) {
1784
+ if (getGlobalConfig().WarpDrive.debug.LOG_INSTANCE_CACHE || globalThis.getWarpDriveRuntimeConfig().debug.LOG_INSTANCE_CACHE) {
1785
+ // eslint-disable-next-line no-console
1786
+ console.log(`InstanceCache: unloaded RecordData for ${String(identifier)}`);
1787
+ // eslint-disable-next-line no-console
1788
+ console.groupEnd();
1789
+ }
1357
1790
  }
1358
1791
  });
1359
1792
  }
@@ -1408,9 +1841,11 @@ class InstanceCache {
1408
1841
  warn(`Your ${type} record was saved to the server, but the response does not have an id.`, !(oldId !== null && id === null));
1409
1842
  return;
1410
1843
  }
1411
- if (macroCondition(getGlobalConfig().WarpDrive.debug.LOG_INSTANCE_CACHE)) {
1412
- // eslint-disable-next-line no-console
1413
- console.log(`InstanceCache: updating id to '${id}' for record ${String(identifier)}`);
1844
+ if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_INSTANCE_CACHE)) {
1845
+ if (getGlobalConfig().WarpDrive.debug.LOG_INSTANCE_CACHE || globalThis.getWarpDriveRuntimeConfig().debug.LOG_INSTANCE_CACHE) {
1846
+ // eslint-disable-next-line no-console
1847
+ console.log(`InstanceCache: updating id to '${id}' for record ${String(identifier)}`);
1848
+ }
1414
1849
  }
1415
1850
  const existingIdentifier = this.store.identifierCache.peekRecordIdentifier({
1416
1851
  type,
@@ -1727,7 +2162,9 @@ class CacheManager {
1727
2162
  peek(identifier) {
1728
2163
  return this.#cache.peek(identifier);
1729
2164
  }
1730
-
2165
+ peekRemoteState(identifier) {
2166
+ return this.#cache.peekRemoteState(identifier);
2167
+ }
1731
2168
  /**
1732
2169
  * Peek the Cache for the existing request data associated with
1733
2170
  * a cacheable request
@@ -1950,6 +2387,19 @@ class CacheManager {
1950
2387
  return this.#cache.getAttr(identifier, propertyName);
1951
2388
  }
1952
2389
 
2390
+ /**
2391
+ * Retrieve the remote state for an attribute from the cache
2392
+ *
2393
+ * @method getRemoteAttr
2394
+ * @public
2395
+ * @param identifier
2396
+ * @param propertyName
2397
+ * @return {unknown}
2398
+ */
2399
+ getRemoteAttr(identifier, propertyName) {
2400
+ return this.#cache.getRemoteAttr(identifier, propertyName);
2401
+ }
2402
+
1953
2403
  /**
1954
2404
  * Mutate the data for an attribute in the cache
1955
2405
  *
@@ -2074,6 +2524,19 @@ class CacheManager {
2074
2524
  return this.#cache.getRelationship(identifier, propertyName);
2075
2525
  }
2076
2526
 
2527
+ /**
2528
+ * Query the cache for the remote state of a relationship property
2529
+ *
2530
+ * @method getRelationship
2531
+ * @public
2532
+ * @param identifier
2533
+ * @param propertyName
2534
+ * @return resource relationship object
2535
+ */
2536
+ getRemoteRelationship(identifier, propertyName) {
2537
+ return this.#cache.getRemoteRelationship(identifier, propertyName);
2538
+ }
2539
+
2077
2540
  // Resource State
2078
2541
  // ===============
2079
2542
 
@@ -2158,27 +2621,51 @@ class CacheManager {
2158
2621
  * @module @ember-data/store
2159
2622
  */
2160
2623
 
2161
- let tokenId = 0;
2162
- const CacheOperations = new Set(['added', 'removed', 'state', 'updated', 'invalidated']);
2163
2624
  function isCacheOperationValue(value) {
2164
- return CacheOperations.has(value);
2625
+ return value === 'added' || value === 'state' || value === 'updated' || value === 'removed' || value === 'invalidated';
2165
2626
  }
2166
2627
  function runLoopIsFlushing() {
2167
2628
  //@ts-expect-error
2168
2629
  return !!_backburner.currentInstance && _backburner._autorun !== true;
2169
2630
  }
2170
- function _unsubscribe(tokens, token, cache) {
2171
- const identifier = tokens.get(token);
2172
- if (macroCondition(getGlobalConfig().WarpDrive.debug.LOG_NOTIFICATIONS)) {
2173
- if (!identifier) {
2174
- // eslint-disable-next-line no-console
2175
- console.log('Passed unknown unsubscribe token to unsubscribe', identifier);
2631
+ function count(label) {
2632
+ // @ts-expect-error
2633
+ // eslint-disable-next-line
2634
+ globalThis.__WarpDriveMetricCountData[label] = (globalThis.__WarpDriveMetricCountData[label] || 0) + 1;
2635
+ }
2636
+ function asInternalToken(token) {
2637
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
2638
+ if (!test) {
2639
+ throw new Error(`Expected a token with a 'for' property`);
2640
+ }
2641
+ })(token && typeof token === 'function' && 'for' in token) : {};
2642
+ }
2643
+ function _unsubscribe(token, cache) {
2644
+ asInternalToken(token);
2645
+ const identifier = token.for;
2646
+ if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_NOTIFICATIONS)) {
2647
+ if (getGlobalConfig().WarpDrive.debug.LOG_NOTIFICATIONS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_NOTIFICATIONS) {
2648
+ if (!identifier) {
2649
+ // eslint-disable-next-line no-console
2650
+ console.log('Passed unknown unsubscribe token to unsubscribe', identifier);
2651
+ }
2176
2652
  }
2177
2653
  }
2178
2654
  if (identifier) {
2179
- tokens.delete(token);
2180
- const map = cache.get(identifier);
2181
- map?.delete(token);
2655
+ const callbacks = cache.get(identifier);
2656
+ if (!callbacks) {
2657
+ return;
2658
+ }
2659
+ const index = callbacks.indexOf(token);
2660
+ if (index === -1) {
2661
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
2662
+ if (!test) {
2663
+ throw new Error(`Cannot unsubscribe a token that is not subscribed`);
2664
+ }
2665
+ })(index !== -1) : {};
2666
+ return;
2667
+ }
2668
+ callbacks.splice(index, 1);
2182
2669
  }
2183
2670
  }
2184
2671
 
@@ -2199,7 +2686,6 @@ class NotificationManager {
2199
2686
  this._buffered = new Map();
2200
2687
  this._hasFlush = false;
2201
2688
  this._cache = new Map();
2202
- this._tokens = new Map();
2203
2689
  }
2204
2690
 
2205
2691
  /**
@@ -2236,17 +2722,26 @@ class NotificationManager {
2236
2722
  throw new Error(`Expected to receive a stable Identifier to subscribe to`);
2237
2723
  }
2238
2724
  })(identifier === 'resource' || identifier === 'document' || isStableIdentifier(identifier) || isDocumentIdentifier(identifier)) : {};
2239
- let map = this._cache.get(identifier);
2240
- if (!map) {
2241
- map = new Map();
2242
- this._cache.set(identifier, map);
2725
+ let callbacks = this._cache.get(identifier);
2726
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
2727
+ if (!test) {
2728
+ throw new Error(`expected to receive a valid callback`);
2729
+ }
2730
+ })(typeof callback === 'function') : {};
2731
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
2732
+ if (!test) {
2733
+ throw new Error(`cannot subscribe with the same callback twice`);
2734
+ }
2735
+ })(!callbacks || !callbacks.includes(callback)) : {};
2736
+ // we use the callback as the cancellation token
2737
+ //@ts-expect-error
2738
+ callback.for = identifier;
2739
+ if (!callbacks) {
2740
+ callbacks = [];
2741
+ this._cache.set(identifier, callbacks);
2243
2742
  }
2244
- const unsubToken = macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? {
2245
- _tokenRef: tokenId++
2246
- } : {};
2247
- map.set(unsubToken, callback);
2248
- this._tokens.set(unsubToken, identifier);
2249
- return unsubToken;
2743
+ callbacks.push(callback);
2744
+ return callback;
2250
2745
  }
2251
2746
 
2252
2747
  /**
@@ -2258,7 +2753,7 @@ class NotificationManager {
2258
2753
  */
2259
2754
  unsubscribe(token) {
2260
2755
  if (!this.isDestroyed) {
2261
- _unsubscribe(this._tokens, token, this._cache);
2756
+ _unsubscribe(token, this._cache);
2262
2757
  }
2263
2758
  }
2264
2759
 
@@ -2280,17 +2775,15 @@ class NotificationManager {
2280
2775
  }
2281
2776
  })(!key || value === 'attributes' || value === 'relationships') : {};
2282
2777
  if (!isStableIdentifier(identifier) && !isDocumentIdentifier(identifier)) {
2283
- if (macroCondition(getGlobalConfig().WarpDrive.debug.LOG_NOTIFICATIONS)) {
2284
- // eslint-disable-next-line no-console
2285
- console.log(`Notifying: Expected to receive a stable Identifier to notify '${value}' '${key || ''}' with, but ${String(identifier)} is not in the cache`, identifier);
2778
+ if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_NOTIFICATIONS)) {
2779
+ if (getGlobalConfig().WarpDrive.debug.LOG_NOTIFICATIONS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_NOTIFICATIONS) {
2780
+ // eslint-disable-next-line no-console
2781
+ console.log(`Notifying: Expected to receive a stable Identifier to notify '${value}' '${key || ''}' with, but ${String(identifier)} is not in the cache`, identifier);
2782
+ }
2286
2783
  }
2287
2784
  return false;
2288
2785
  }
2289
- if (macroCondition(getGlobalConfig().WarpDrive.debug.LOG_NOTIFICATIONS)) {
2290
- // eslint-disable-next-line no-console
2291
- console.log(`Buffering Notify: ${String(identifier.lid)}\t${value}\t${key || ''}`);
2292
- }
2293
- const hasSubscribers = Boolean(this._cache.get(identifier)?.size);
2786
+ const hasSubscribers = Boolean(this._cache.get(identifier)?.length);
2294
2787
  if (isCacheOperationValue(value) || hasSubscribers) {
2295
2788
  let buffer = this._buffered.get(identifier);
2296
2789
  if (!buffer) {
@@ -2298,7 +2791,29 @@ class NotificationManager {
2298
2791
  this._buffered.set(identifier, buffer);
2299
2792
  }
2300
2793
  buffer.push([value, key]);
2301
- this._scheduleNotify();
2794
+ if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_METRIC_COUNTS)) {
2795
+ if (getGlobalConfig().WarpDrive.debug.LOG_METRIC_COUNTS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_METRIC_COUNTS) {
2796
+ count(`notify ${'type' in identifier ? identifier.type : '<document>'} ${value} ${key}`);
2797
+ }
2798
+ }
2799
+ if (!this._scheduleNotify()) {
2800
+ if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_NOTIFICATIONS)) {
2801
+ if (getGlobalConfig().WarpDrive.debug.LOG_NOTIFICATIONS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_NOTIFICATIONS) {
2802
+ log('notify', 'buffered', `${'type' in identifier ? identifier.type : 'document'}`, identifier.lid, `${value}`, key || '');
2803
+ }
2804
+ }
2805
+ }
2806
+ } else {
2807
+ if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_NOTIFICATIONS)) {
2808
+ if (getGlobalConfig().WarpDrive.debug.LOG_NOTIFICATIONS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_NOTIFICATIONS) {
2809
+ log('notify', 'discarded', `${'type' in identifier ? identifier.type : 'document'}`, identifier.lid, `${value}`, key || '');
2810
+ }
2811
+ }
2812
+ if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_METRIC_COUNTS)) {
2813
+ if (getGlobalConfig().WarpDrive.debug.LOG_METRIC_COUNTS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_METRIC_COUNTS) {
2814
+ count(`DISCARDED notify ${'type' in identifier ? identifier.type : '<document>'} ${value} ${key}`);
2815
+ }
2816
+ }
2302
2817
  }
2303
2818
  return hasSubscribers;
2304
2819
  }
@@ -2309,14 +2824,15 @@ class NotificationManager {
2309
2824
  const asyncFlush = this.store._enableAsyncFlush;
2310
2825
  if (this._hasFlush) {
2311
2826
  if (asyncFlush !== false && !runLoopIsFlushing()) {
2312
- return;
2827
+ return false;
2313
2828
  }
2314
2829
  }
2315
2830
  if (asyncFlush && !runLoopIsFlushing()) {
2316
2831
  this._hasFlush = true;
2317
- return;
2832
+ return false;
2318
2833
  }
2319
2834
  this._flush();
2835
+ return true;
2320
2836
  }
2321
2837
  _flush() {
2322
2838
  const buffered = this._buffered;
@@ -2334,9 +2850,10 @@ class NotificationManager {
2334
2850
  this._onFlushCB = undefined;
2335
2851
  }
2336
2852
  _flushNotification(identifier, value, key) {
2337
- if (macroCondition(getGlobalConfig().WarpDrive.debug.LOG_NOTIFICATIONS)) {
2338
- // eslint-disable-next-line no-console
2339
- console.log(`Notifying: ${String(identifier)}\t${value}\t${key || ''}`);
2853
+ if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_NOTIFICATIONS)) {
2854
+ if (getGlobalConfig().WarpDrive.debug.LOG_NOTIFICATIONS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_NOTIFICATIONS) {
2855
+ log('notify', '', `${'type' in identifier ? identifier.type : 'document'}`, identifier.lid, `${value}`, key || '');
2856
+ }
2340
2857
  }
2341
2858
 
2342
2859
  // TODO for documents this will need to switch based on Identifier kind
@@ -2348,11 +2865,11 @@ class NotificationManager {
2348
2865
  });
2349
2866
  }
2350
2867
  }
2351
- const callbackMap = this._cache.get(identifier);
2352
- if (!callbackMap || !callbackMap.size) {
2868
+ const callbacks = this._cache.get(identifier);
2869
+ if (!callbacks || !callbacks.length) {
2353
2870
  return false;
2354
2871
  }
2355
- callbackMap.forEach(cb => {
2872
+ callbacks.forEach(cb => {
2356
2873
  // @ts-expect-error overload doesn't narrow within body
2357
2874
  cb(identifier, value, key);
2358
2875
  });
@@ -2360,7 +2877,6 @@ class NotificationManager {
2360
2877
  }
2361
2878
  destroy() {
2362
2879
  this.isDestroyed = true;
2363
- this._tokens.clear();
2364
2880
  this._cache.clear();
2365
2881
  }
2366
2882
  }
@@ -2474,6 +2990,7 @@ class IdentifierArray {
2474
2990
  isDestroying = false;
2475
2991
  isDestroyed = false;
2476
2992
  _updatingPromise = null;
2993
+ identifier;
2477
2994
  [IS_COLLECTION] = true;
2478
2995
  [SOURCE];
2479
2996
  [NOTIFY]() {
@@ -2512,6 +3029,7 @@ class IdentifierArray {
2512
3029
  this.modelName = options.type;
2513
3030
  this.store = options.store;
2514
3031
  this._manager = options.manager;
3032
+ this.identifier = options.identifier || null;
2515
3033
  this[SOURCE] = options.identifiers;
2516
3034
  this[ARRAY_SIGNAL] = createSignal(this, 'length');
2517
3035
  const store = options.store;
@@ -2682,7 +3200,7 @@ class IdentifierArray {
2682
3200
  return false;
2683
3201
  }
2684
3202
  const original = target[index];
2685
- const newIdentifier = extractIdentifierFromRecord$1(value);
3203
+ const newIdentifier = extractIdentifierFromRecord$2(value);
2686
3204
  macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
2687
3205
  if (!test) {
2688
3206
  throw new Error(`Expected a record`);
@@ -2723,9 +3241,18 @@ class IdentifierArray {
2723
3241
  return Reflect.deleteProperty(target, prop);
2724
3242
  },
2725
3243
  getPrototypeOf() {
2726
- return IdentifierArray.prototype;
3244
+ return Array.prototype;
2727
3245
  }
2728
3246
  });
3247
+ if (macroCondition(getGlobalConfig().WarpDrive.env.DEBUG)) {
3248
+ Object.defineProperty(this, '__SHOW_ME_THE_DATA_(debug mode only)__', {
3249
+ enumerable: false,
3250
+ configurable: true,
3251
+ get() {
3252
+ return proxy.slice();
3253
+ }
3254
+ });
3255
+ }
2729
3256
  createArrayTags(proxy, _SIGNAL);
2730
3257
  this[NOTIFY] = this[NOTIFY].bind(proxy);
2731
3258
  return proxy;
@@ -2866,7 +3393,7 @@ Collection.prototype.query = null;
2866
3393
  // Ensure instanceof works correctly
2867
3394
  // Object.setPrototypeOf(IdentifierArray.prototype, Array.prototype);
2868
3395
 
2869
- function assertRecordPassedToHasMany(record) {
3396
+ function assertRecordPassedToHasMany$1(record) {
2870
3397
  macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
2871
3398
  if (!test) {
2872
3399
  throw new Error(`All elements of a hasMany relationship must be instances of Model, you passed $${typeof record}`);
@@ -2880,11 +3407,11 @@ function assertRecordPassedToHasMany(record) {
2880
3407
  }
2881
3408
  }()) : {};
2882
3409
  }
2883
- function extractIdentifierFromRecord$1(record) {
3410
+ function extractIdentifierFromRecord$2(record) {
2884
3411
  if (!record) {
2885
3412
  return null;
2886
3413
  }
2887
- assertRecordPassedToHasMany(record);
3414
+ assertRecordPassedToHasMany$1(record);
2888
3415
  return recordIdentifierFor(record);
2889
3416
  }
2890
3417
 
@@ -2959,6 +3486,12 @@ class RecordArrayManager {
2959
3486
  this._identifiers = new Map();
2960
3487
  this._set = new Map();
2961
3488
  this._visibilitySet = new Map();
3489
+ this._subscription = this.store.notifications.subscribe('document', (identifier, type) => {
3490
+ if (type === 'updated' && this._keyedArrays.has(identifier.lid)) {
3491
+ const array = this._keyedArrays.get(identifier.lid);
3492
+ this.dirtyArray(array, 0, true);
3493
+ }
3494
+ });
2962
3495
  this._subscription = this.store.notifications.subscribe('resource', (identifier, type) => {
2963
3496
  if (type === 'added') {
2964
3497
  this._visibilitySet.set(identifier, true);
@@ -2973,11 +3506,38 @@ class RecordArrayManager {
2973
3506
  }
2974
3507
  _syncArray(array) {
2975
3508
  const pending = this._pending.get(array);
2976
- if (!pending || this.isDestroying || this.isDestroyed) {
3509
+ const isRequestArray = isCollection(array);
3510
+ if (!isRequestArray && !pending || this.isDestroying || this.isDestroyed) {
2977
3511
  return;
2978
3512
  }
2979
- sync(array, pending, this._set.get(array));
2980
- this._pending.delete(array);
3513
+
3514
+ // first flush any staged changes
3515
+ if (pending) {
3516
+ sync(array, pending, this._set.get(array));
3517
+ this._pending.delete(array);
3518
+ }
3519
+
3520
+ // then pull new state if required
3521
+ if (isRequestArray) {
3522
+ const tag = array[ARRAY_SIGNAL];
3523
+ if (tag.reason === 'cache-sync') {
3524
+ tag.reason = null;
3525
+ const doc = this.store.cache.peek(array.identifier);
3526
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
3527
+ if (!test) {
3528
+ throw new Error(`Expected to find a document for ${array.identifier.lid} but found none`);
3529
+ }
3530
+ })(doc) : {};
3531
+ const data = !('data' in doc) || !Array.isArray(doc.data) ? [] : doc.data;
3532
+ // TODO technically we should destroy here if
3533
+ // !('data' in doc) || !Array.isArray(doc.data)
3534
+ // is true.
3535
+ this.populateManagedArray(array, data, null);
3536
+ }
3537
+ }
3538
+ }
3539
+ mutate(mutation) {
3540
+ this.store.cache.mutate(mutation);
2981
3541
  }
2982
3542
 
2983
3543
  /**
@@ -3013,9 +3573,13 @@ class RecordArrayManager {
3013
3573
  }
3014
3574
  return array;
3015
3575
  }
3016
- createArray(config) {
3576
+ getCollection(config) {
3577
+ if (config.identifier && this._keyedArrays.has(config.identifier.lid)) {
3578
+ return this._keyedArrays.get(config.identifier.lid);
3579
+ }
3017
3580
  const options = {
3018
3581
  type: config.type,
3582
+ identifier: config.identifier || null,
3019
3583
  links: config.doc?.links || null,
3020
3584
  meta: config.doc?.meta || null,
3021
3585
  query: config.query || null,
@@ -3028,16 +3592,22 @@ class RecordArrayManager {
3028
3592
  const array = new Collection(options);
3029
3593
  this._managed.add(array);
3030
3594
  this._set.set(array, new Set(options.identifiers || []));
3595
+ if (config.identifier) {
3596
+ this._keyedArrays.set(config.identifier.lid, array);
3597
+ }
3031
3598
  if (config.identifiers) {
3032
3599
  associate(this._identifiers, array, config.identifiers);
3033
3600
  }
3034
3601
  return array;
3035
3602
  }
3036
- dirtyArray(array, delta) {
3603
+ dirtyArray(array, delta, shouldSyncFromCache) {
3037
3604
  if (array === FAKE_ARR) {
3038
3605
  return;
3039
3606
  }
3040
3607
  const tag = array[ARRAY_SIGNAL];
3608
+ if (shouldSyncFromCache) {
3609
+ tag.reason = 'cache-sync';
3610
+ }
3041
3611
  if (!tag.shouldReset) {
3042
3612
  tag.shouldReset = true;
3043
3613
  addTransactionCB(array[NOTIFY]);
@@ -3096,13 +3666,18 @@ class RecordArrayManager {
3096
3666
  populateManagedArray(array, identifiers, payload) {
3097
3667
  this._pending.delete(array);
3098
3668
  const source = array[SOURCE];
3669
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
3670
+ if (!test) {
3671
+ throw new Error(`The new state of the collection should not be using the same array reference as the original state.`);
3672
+ }
3673
+ })(source !== identifiers) : {};
3099
3674
  const old = source.slice();
3100
3675
  source.length = 0;
3101
3676
  fastPush(source, identifiers);
3102
3677
  this._set.set(array, new Set(identifiers));
3103
3678
  notifyArray(array);
3104
- array.meta = payload.meta || null;
3105
- array.links = payload.links || null;
3679
+ array.meta = payload?.meta || null;
3680
+ array.links = payload?.links || null;
3106
3681
  array.isLoaded = true;
3107
3682
  disassociate(this._identifiers, array, old);
3108
3683
  associate(this._identifiers, array, identifiers);
@@ -3116,7 +3691,7 @@ class RecordArrayManager {
3116
3691
  changes.delete(identifier);
3117
3692
  } else {
3118
3693
  changes.set(identifier, 'add');
3119
- this.dirtyArray(array, changes.size);
3694
+ this.dirtyArray(array, changes.size, false);
3120
3695
  }
3121
3696
  });
3122
3697
  }
@@ -3130,7 +3705,7 @@ class RecordArrayManager {
3130
3705
  changes.delete(identifier);
3131
3706
  } else {
3132
3707
  changes.set(identifier, 'del');
3133
- this.dirtyArray(array, changes.size);
3708
+ this.dirtyArray(array, changes.size, false);
3134
3709
  }
3135
3710
  });
3136
3711
  }
@@ -3235,6 +3810,9 @@ function sync(array, changes, arraySet) {
3235
3810
  */
3236
3811
  }
3237
3812
  }
3813
+ function isCollection(array) {
3814
+ return array.identifier !== null;
3815
+ }
3238
3816
 
3239
3817
  /**
3240
3818
  * @module @ember-data/store
@@ -3495,6 +4073,118 @@ function constructResource(type, id, lid) {
3495
4073
  */
3496
4074
  // this import location is deprecated but breaks in 4.8 and older
3497
4075
 
4076
+ // @ts-expect-error adding to globalThis
4077
+ globalThis.setWarpDriveLogging = setLogging;
4078
+
4079
+ // @ts-expect-error adding to globalThis
4080
+ globalThis.getWarpDriveRuntimeConfig = getRuntimeConfig;
4081
+ if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_METRIC_COUNTS)) {
4082
+ if (getGlobalConfig().WarpDrive.debug.LOG_METRIC_COUNTS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_METRIC_COUNTS) {
4083
+ // @ts-expect-error adding to globalThis
4084
+ // eslint-disable-next-line
4085
+ globalThis.__WarpDriveMetricCountData = globalThis.__WarpDriveMetricCountData || {};
4086
+
4087
+ // @ts-expect-error adding to globalThis
4088
+ globalThis.getWarpDriveMetricCounts = () => {
4089
+ // @ts-expect-error
4090
+ // eslint-disable-next-line
4091
+ return structuredClone(globalThis.__WarpDriveMetricCountData);
4092
+ };
4093
+
4094
+ // @ts-expect-error adding to globalThis
4095
+ globalThis.resetWarpDriveMetricCounts = () => {
4096
+ // @ts-expect-error
4097
+ globalThis.__WarpDriveMetricCountData = {};
4098
+ };
4099
+ if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.__INTERNAL_LOG_NATIVE_MAP_SET_COUNTS)) {
4100
+ if (getGlobalConfig().WarpDrive.debug.__INTERNAL_LOG_NATIVE_MAP_SET_COUNTS || globalThis.getWarpDriveRuntimeConfig().debug.__INTERNAL_LOG_NATIVE_MAP_SET_COUNTS) {
4101
+ // @ts-expect-error adding to globalThis
4102
+ globalThis.__primitiveInstanceId = 0;
4103
+ function interceptAndLog(klassName, methodName) {
4104
+ const klass = globalThis[klassName];
4105
+ if (methodName === 'constructor') {
4106
+ const instantiationLabel = `new ${klassName}()`;
4107
+ // @ts-expect-error
4108
+ globalThis[klassName] = class extends klass {
4109
+ // @ts-expect-error
4110
+ constructor(...args) {
4111
+ // eslint-disable-next-line
4112
+ super(...args);
4113
+ // @ts-expect-error
4114
+
4115
+ const instanceId = globalThis.__primitiveInstanceId++;
4116
+ // @ts-expect-error
4117
+ // eslint-disable-next-line
4118
+ globalThis.__WarpDriveMetricCountData[instantiationLabel] =
4119
+ // @ts-expect-error
4120
+ // eslint-disable-next-line
4121
+ (globalThis.__WarpDriveMetricCountData[instantiationLabel] || 0) + 1;
4122
+ // @ts-expect-error
4123
+ this.instanceName = `${klassName}:${instanceId} - ${new Error().stack?.split('\n')[2]}`;
4124
+ }
4125
+ };
4126
+ } else {
4127
+ // @ts-expect-error
4128
+ // eslint-disable-next-line
4129
+ const original = klass.prototype[methodName];
4130
+ const logName = `${klassName}.${methodName}`;
4131
+
4132
+ // @ts-expect-error
4133
+ klass.prototype[methodName] = function (...args) {
4134
+ // @ts-expect-error
4135
+ // eslint-disable-next-line
4136
+ globalThis.__WarpDriveMetricCountData[logName] = (globalThis.__WarpDriveMetricCountData[logName] || 0) + 1;
4137
+ // @ts-expect-error
4138
+ const {
4139
+ instanceName
4140
+ } = this;
4141
+ if (!instanceName) {
4142
+ // @ts-expect-error
4143
+ const instanceId = globalThis.__primitiveInstanceId++;
4144
+ // @ts-expect-error
4145
+ this.instanceName = `${klassName}.${methodName}:${instanceId} - ${new Error().stack?.split('\n')[2]}`;
4146
+ }
4147
+ const instanceLogName = `${logName} (${instanceName})`;
4148
+ // @ts-expect-error
4149
+ // eslint-disable-next-line
4150
+ globalThis.__WarpDriveMetricCountData[instanceLogName] =
4151
+ // @ts-expect-error
4152
+ // eslint-disable-next-line
4153
+ (globalThis.__WarpDriveMetricCountData[instanceLogName] || 0) + 1;
4154
+ // eslint-disable-next-line
4155
+ return original.apply(this, args);
4156
+ };
4157
+ }
4158
+ }
4159
+ interceptAndLog('Set', 'constructor');
4160
+ interceptAndLog('Set', 'add');
4161
+ interceptAndLog('Set', 'delete');
4162
+ interceptAndLog('Set', 'has');
4163
+ interceptAndLog('Set', 'set');
4164
+ interceptAndLog('Set', 'get');
4165
+ interceptAndLog('Map', 'constructor');
4166
+ interceptAndLog('Map', 'set');
4167
+ interceptAndLog('Map', 'delete');
4168
+ interceptAndLog('Map', 'has');
4169
+ interceptAndLog('Map', 'add');
4170
+ interceptAndLog('Map', 'get');
4171
+ interceptAndLog('WeakSet', 'constructor');
4172
+ interceptAndLog('WeakSet', 'add');
4173
+ interceptAndLog('WeakSet', 'delete');
4174
+ interceptAndLog('WeakSet', 'has');
4175
+ interceptAndLog('WeakSet', 'set');
4176
+ interceptAndLog('WeakSet', 'get');
4177
+ interceptAndLog('WeakMap', 'constructor');
4178
+ interceptAndLog('WeakMap', 'set');
4179
+ interceptAndLog('WeakMap', 'delete');
4180
+ interceptAndLog('WeakMap', 'has');
4181
+ interceptAndLog('WeakMap', 'add');
4182
+ interceptAndLog('WeakMap', 'get');
4183
+ }
4184
+ }
4185
+ }
4186
+ }
4187
+
3498
4188
  // `AwaitedKeys` is needed here to resolve any promise types like `PromiseBelongsTo`.
3499
4189
 
3500
4190
  /**
@@ -3561,6 +4251,7 @@ const app = new EmberApp(defaults, {
3561
4251
  id: 'ember-data:deprecate-store-extends-ember-object',
3562
4252
  until: '6.0',
3563
4253
  for: 'ember-data',
4254
+ url: 'https://deprecations.emberjs.com/id/ember-data-deprecate-store-extends-ember-object',
3564
4255
  since: {
3565
4256
  available: '4.13',
3566
4257
  enabled: '5.4'
@@ -3708,7 +4399,6 @@ class Store extends BaseClass {
3708
4399
  // private
3709
4400
  this._requestCache = new RequestStateService(this);
3710
4401
  this._instanceCache = new InstanceCache(this);
3711
- this._documentCache = new Map();
3712
4402
  this.isDestroying = false;
3713
4403
  this.isDestroyed = false;
3714
4404
  }
@@ -3877,22 +4567,26 @@ class Store extends BaseClass {
3877
4567
  opts.disableTestWaiter = typeof requestConfig.disableTestWaiter === 'boolean' ? requestConfig.disableTestWaiter : true;
3878
4568
  }
3879
4569
  }
3880
- if (macroCondition(getGlobalConfig().WarpDrive.debug.LOG_REQUESTS)) {
3881
- let options;
3882
- try {
3883
- options = JSON.parse(JSON.stringify(requestConfig));
3884
- } catch {
3885
- options = requestConfig;
4570
+ if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_REQUESTS)) {
4571
+ if (getGlobalConfig().WarpDrive.debug.LOG_REQUESTS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_REQUESTS) {
4572
+ let options;
4573
+ try {
4574
+ options = JSON.parse(JSON.stringify(requestConfig));
4575
+ } catch {
4576
+ options = requestConfig;
4577
+ }
4578
+ // eslint-disable-next-line no-console
4579
+ console.log(`request: [[START]] ${requestConfig.op && !requestConfig.url ? '(LEGACY) ' : ''}${requestConfig.op || '<unknown operation>'} ${requestConfig.url || '<empty url>'} ${requestConfig.method || '<empty method>'}`, options);
3886
4580
  }
3887
- // eslint-disable-next-line no-console
3888
- console.log(`request: [[START]] ${requestConfig.op && !requestConfig.url ? '(LEGACY) ' : ''}${requestConfig.op || '<unknown operation>'} ${requestConfig.url || '<empty url>'} ${requestConfig.method || '<empty method>'}`, options);
3889
4581
  }
3890
4582
  const request = Object.assign({}, requestConfig, opts);
3891
4583
  const future = this.requestManager.request(request);
3892
4584
  future.onFinalize(() => {
3893
- if (macroCondition(getGlobalConfig().WarpDrive.debug.LOG_REQUESTS)) {
3894
- // eslint-disable-next-line no-console
3895
- console.log(`request: [[FINALIZE]] ${requestConfig.op && !requestConfig.url ? '(LEGACY) ' : ''}${requestConfig.op || '<unknown operation>'} ${requestConfig.url || '<empty url>'} ${requestConfig.method || '<empty method>'}`);
4585
+ if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_REQUESTS)) {
4586
+ if (getGlobalConfig().WarpDrive.debug.LOG_REQUESTS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_REQUESTS) {
4587
+ // eslint-disable-next-line no-console
4588
+ console.log(`request: [[FINALIZE]] ${requestConfig.op && !requestConfig.url ? '(LEGACY) ' : ''}${requestConfig.op || '<unknown operation>'} ${requestConfig.url || '<empty url>'} ${requestConfig.method || '<empty method>'}`);
4589
+ }
3896
4590
  }
3897
4591
  // skip flush for legacy belongsTo
3898
4592
  if (requestConfig.op === 'findBelongsTo' && !requestConfig.url) {
@@ -4098,9 +4792,8 @@ class Store extends BaseClass {
4098
4792
  This will cause the record to be destroyed and freed up for garbage collection.
4099
4793
  Example
4100
4794
  ```javascript
4101
- store.findRecord('post', '1').then(function(post) {
4102
- store.unloadRecord(post);
4103
- });
4795
+ const { content: { data: post } } = await store.request(findRecord({ type: 'post', id: '1' }));
4796
+ store.unloadRecord(post);
4104
4797
  ```
4105
4798
  @method unloadRecord
4106
4799
  @public
@@ -5152,16 +5845,6 @@ class Store extends BaseClass {
5152
5845
  if (macroCondition(getGlobalConfig().WarpDrive.env.DEBUG)) {
5153
5846
  assertDestroyingStore(this, '_push');
5154
5847
  }
5155
- if (macroCondition(getGlobalConfig().WarpDrive.debug.LOG_PAYLOADS)) {
5156
- try {
5157
- const data = JSON.parse(JSON.stringify(jsonApiDoc));
5158
- // eslint-disable-next-line no-console
5159
- console.log('EmberData | Payload - push', data);
5160
- } catch {
5161
- // eslint-disable-next-line no-console
5162
- console.log('EmberData | Payload - push', jsonApiDoc);
5163
- }
5164
- }
5165
5848
  if (asyncFlush) {
5166
5849
  this._enableAsyncFlush = true;
5167
5850
  }
@@ -5382,9 +6065,9 @@ function normalizeProperties(store, identifier, properties) {
5382
6065
  if (macroCondition(getGlobalConfig().WarpDrive.env.DEBUG)) {
5383
6066
  assertRecordsPassedToHasMany(properties[prop]);
5384
6067
  }
5385
- properties[prop] = extractIdentifiersFromRecords(properties[prop]);
6068
+ properties[prop] = extractIdentifiersFromRecords$1(properties[prop]);
5386
6069
  } else if (field.kind === 'belongsTo') {
5387
- properties[prop] = extractIdentifierFromRecord(properties[prop]);
6070
+ properties[prop] = extractIdentifierFromRecord$1(properties[prop]);
5388
6071
  }
5389
6072
  }
5390
6073
  }
@@ -5412,222 +6095,16 @@ function assertRecordsPassedToHasMany(records) {
5412
6095
  });
5413
6096
  }()) : {};
5414
6097
  }
5415
- function extractIdentifiersFromRecords(records) {
5416
- return records.map(record => extractIdentifierFromRecord(record));
6098
+ function extractIdentifiersFromRecords$1(records) {
6099
+ return records.map(record => extractIdentifierFromRecord$1(record));
5417
6100
  }
5418
- function extractIdentifierFromRecord(recordOrPromiseRecord) {
6101
+ function extractIdentifierFromRecord$1(recordOrPromiseRecord) {
5419
6102
  if (!recordOrPromiseRecord) {
5420
6103
  return null;
5421
6104
  }
5422
6105
  const extract = recordIdentifierFor;
5423
6106
  return extract(recordOrPromiseRecord);
5424
6107
  }
5425
-
5426
- /**
5427
- * @module @ember-data/store
5428
- */
5429
- function urlFromLink(link) {
5430
- if (typeof link === 'string') return link;
5431
- return link.href;
5432
- }
5433
-
5434
- /**
5435
- * A Document is a class that wraps the response content from a request to the API
5436
- * returned by `Cache.put` or `Cache.peek`, converting resource-identifiers into
5437
- * record instances.
5438
- *
5439
- * It is not directly instantiated by the user, and its properties should not
5440
- * be directly modified. Whether individual properties are mutable or not is
5441
- * determined by the record instance itself.
5442
- *
5443
- * @public
5444
- * @class Document
5445
- */
5446
- class Document {
5447
- /**
5448
- * The links object for this document, if any
5449
- *
5450
- * e.g.
5451
- *
5452
- * ```
5453
- * {
5454
- * self: '/articles?page[number]=3',
5455
- * }
5456
- * ```
5457
- *
5458
- * @property links
5459
- * @type {object|undefined} - a links object
5460
- * @public
5461
- */
5462
-
5463
- /**
5464
- * The primary data for this document, if any.
5465
- *
5466
- * If this document has no primary data (e.g. because it is an error document)
5467
- * this property will be `undefined`.
5468
- *
5469
- * For collections this will be an array of record instances,
5470
- * for single resource requests it will be a single record instance or null.
5471
- *
5472
- * @property data
5473
- * @public
5474
- * @type {object|Array<object>|null|undefined} - a data object
5475
- */
5476
-
5477
- /**
5478
- * The errors returned by the API for this request, if any
5479
- *
5480
- * @property errors
5481
- * @public
5482
- * @type {object|undefined} - an errors object
5483
- */
5484
-
5485
- /**
5486
- * The meta object for this document, if any
5487
- *
5488
- * @property meta
5489
- * @public
5490
- * @type {object|undefined} - a meta object
5491
- */
5492
-
5493
- /**
5494
- * The identifier associated with this document, if any
5495
- *
5496
- * @property identifier
5497
- * @public
5498
- * @type {StableDocumentIdentifier|null}
5499
- */
5500
-
5501
- #store;
5502
- constructor(store, identifier) {
5503
- this.#store = store;
5504
- this.identifier = identifier;
5505
- }
5506
- async #request(link, options) {
5507
- const href = this.links?.[link];
5508
- if (!href) {
5509
- return null;
5510
- }
5511
- options.method = options.method || 'GET';
5512
- Object.assign(options, {
5513
- url: urlFromLink(href)
5514
- });
5515
- const response = await this.#store.request(options);
5516
- return response.content;
5517
- }
5518
-
5519
- /**
5520
- * Fetches the related link for this document, returning a promise that resolves
5521
- * with the document when the request completes. If no related link is present,
5522
- * will fallback to the self link if present
5523
- *
5524
- * @method fetch
5525
- * @public
5526
- * @param {object} options
5527
- * @return Promise<Document>
5528
- */
5529
- fetch(options = {}) {
5530
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5531
- if (!test) {
5532
- throw new Error(`No self or related link`);
5533
- }
5534
- })(this.links?.related || this.links?.self) : {};
5535
- options.cacheOptions = options.cacheOptions || {};
5536
- options.cacheOptions.key = this.identifier?.lid;
5537
- return this.#request(this.links.related ? 'related' : 'self', options);
5538
- }
5539
-
5540
- /**
5541
- * Fetches the next link for this document, returning a promise that resolves
5542
- * with the new document when the request completes, or null if there is no
5543
- * next link.
5544
- *
5545
- * @method next
5546
- * @public
5547
- * @param {object} options
5548
- * @return Promise<Document | null>
5549
- */
5550
- next(options = {}) {
5551
- return this.#request('next', options);
5552
- }
5553
-
5554
- /**
5555
- * Fetches the prev link for this document, returning a promise that resolves
5556
- * with the new document when the request completes, or null if there is no
5557
- * prev link.
5558
- *
5559
- * @method prev
5560
- * @public
5561
- * @param {object} options
5562
- * @return Promise<Document | null>
5563
- */
5564
- prev(options = {}) {
5565
- return this.#request('prev', options);
5566
- }
5567
-
5568
- /**
5569
- * Fetches the first link for this document, returning a promise that resolves
5570
- * with the new document when the request completes, or null if there is no
5571
- * first link.
5572
- *
5573
- * @method first
5574
- * @public
5575
- * @param {object} options
5576
- * @return Promise<Document | null>
5577
- */
5578
- first(options = {}) {
5579
- return this.#request('first', options);
5580
- }
5581
-
5582
- /**
5583
- * Fetches the last link for this document, returning a promise that resolves
5584
- * with the new document when the request completes, or null if there is no
5585
- * last link.
5586
- *
5587
- * @method last
5588
- * @public
5589
- * @param {object} options
5590
- * @return Promise<Document | null>
5591
- */
5592
- last(options = {}) {
5593
- return this.#request('last', options);
5594
- }
5595
-
5596
- /**
5597
- * Implemented for `JSON.stringify` support.
5598
- *
5599
- * Returns the JSON representation of the document wrapper.
5600
- *
5601
- * This is a shallow serialization, it does not deeply serialize
5602
- * the document's contents, leaving that to the individual record
5603
- * instances to determine how to do, if at all.
5604
- *
5605
- * @method toJSON
5606
- * @public
5607
- * @return
5608
- */
5609
- toJSON() {
5610
- const data = {};
5611
- data.identifier = this.identifier;
5612
- if (this.data !== undefined) {
5613
- data.data = this.data;
5614
- }
5615
- if (this.links !== undefined) {
5616
- data.links = this.links;
5617
- }
5618
- if (this.errors !== undefined) {
5619
- data.errors = this.errors;
5620
- }
5621
- if (this.meta !== undefined) {
5622
- data.meta = this.meta;
5623
- }
5624
- return data;
5625
- }
5626
- }
5627
- defineSignal(Document.prototype, 'data');
5628
- defineSignal(Document.prototype, 'links');
5629
- defineSignal(Document.prototype, 'errors');
5630
- defineSignal(Document.prototype, 'meta');
5631
6108
  const MUTATION_OPS = new Set(['createRecord', 'updateRecord', 'deleteRecord']);
5632
6109
  function calcShouldFetch(store, request, hasCachedValue, identifier) {
5633
6110
  const {
@@ -5644,17 +6121,6 @@ function calcShouldBackgroundFetch(store, request, willFetch, identifier) {
5644
6121
  function isMutation(request) {
5645
6122
  return Boolean(request.op && MUTATION_OPS.has(request.op));
5646
6123
  }
5647
- function copyDocumentProperties(target, source) {
5648
- if ('links' in source) {
5649
- target.links = source.links;
5650
- }
5651
- if ('meta' in source) {
5652
- target.meta = source.meta;
5653
- }
5654
- if ('errors' in source) {
5655
- target.errors = source.errors;
5656
- }
5657
- }
5658
6124
  function isCacheAffecting(document) {
5659
6125
  if (!isMutation(document.request)) {
5660
6126
  return true;
@@ -5682,9 +6148,6 @@ function cloneError(error) {
5682
6148
  Object.assign(cloned, error);
5683
6149
  return cloned;
5684
6150
  }
5685
- function isErrorDocument(document) {
5686
- return 'errors' in document;
5687
- }
5688
6151
  function getPriority(identifier, deduped, priority) {
5689
6152
  if (identifier) {
5690
6153
  const existing = deduped.get(identifier);
@@ -5814,10 +6277,10 @@ const CacheHandler = {
5814
6277
  const shouldHydrate = context.request[EnableHydration] || false;
5815
6278
  context.setResponse(peeked.response);
5816
6279
  if ('error' in peeked) {
5817
- const content = shouldHydrate ? maybeUpdateErrorUiObjects(store, {
6280
+ const content = shouldHydrate ? maybeUpdateUiObjects(store, context.request, {
5818
6281
  shouldHydrate,
5819
6282
  identifier
5820
- }, peeked.content, true) : peeked.content;
6283
+ }, peeked.content) : peeked.content;
5821
6284
  const newError = cloneError(peeked);
5822
6285
  newError.content = content;
5823
6286
  throw newError;
@@ -5825,15 +6288,15 @@ const CacheHandler = {
5825
6288
  const result = shouldHydrate ? maybeUpdateUiObjects(store, context.request, {
5826
6289
  shouldHydrate,
5827
6290
  identifier
5828
- }, peeked.content, true) : peeked.content;
6291
+ }, peeked.content) : peeked.content;
5829
6292
  return result;
5830
6293
  }
5831
6294
  };
5832
- function maybeUpdateUiObjects(store, request, options, document, isFromCache) {
6295
+ function maybeUpdateUiObjects(store, request, options, document) {
5833
6296
  const {
5834
6297
  identifier
5835
6298
  } = options;
5836
- if (!document) {
6299
+ if (!document || !options.shouldHydrate) {
5837
6300
  macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5838
6301
  if (!test) {
5839
6302
  throw new Error(`The CacheHandler expected response content but none was found`);
@@ -5841,97 +6304,16 @@ function maybeUpdateUiObjects(store, request, options, document, isFromCache) {
5841
6304
  })(!options.shouldHydrate) : {};
5842
6305
  return document;
5843
6306
  }
5844
- if (Array.isArray(document.data)) {
5845
- const {
5846
- recordArrayManager
5847
- } = store;
5848
- if (!identifier) {
5849
- if (!options.shouldHydrate) {
5850
- return document;
5851
- }
5852
- const data = recordArrayManager.createArray({
5853
- type: request.url,
5854
- identifiers: document.data,
5855
- doc: document,
5856
- query: request
5857
- });
5858
- const doc = new Document(store, null);
5859
- doc.data = data;
5860
- doc.meta = document.meta;
5861
- doc.links = document.links;
5862
- return doc;
5863
- }
5864
- let managed = recordArrayManager._keyedArrays.get(identifier.lid);
5865
- if (!managed) {
5866
- managed = recordArrayManager.createArray({
5867
- type: identifier.lid,
5868
- identifiers: document.data,
5869
- doc: document
5870
- });
5871
- recordArrayManager._keyedArrays.set(identifier.lid, managed);
5872
- const doc = new Document(store, identifier);
5873
- doc.data = managed;
5874
- doc.meta = document.meta;
5875
- doc.links = document.links;
5876
- store._documentCache.set(identifier, doc);
5877
- return options.shouldHydrate ? doc : document;
5878
- } else {
5879
- const doc = store._documentCache.get(identifier);
5880
- if (!isFromCache) {
5881
- recordArrayManager.populateManagedArray(managed, document.data, document);
5882
- doc.data = managed;
5883
- doc.meta = document.meta;
5884
- doc.links = document.links;
5885
- }
5886
- return options.shouldHydrate ? doc : document;
5887
- }
5888
- } else {
5889
- if (!identifier && !options.shouldHydrate) {
5890
- return document;
5891
- }
5892
- const data = document.data ? store.peekRecord(document.data) : null;
5893
- let doc;
5894
- if (identifier) {
5895
- doc = store._documentCache.get(identifier);
5896
- }
5897
- if (!doc) {
5898
- doc = new Document(store, identifier);
5899
- doc.data = data;
5900
- copyDocumentProperties(doc, document);
5901
- if (identifier) {
5902
- store._documentCache.set(identifier, doc);
5903
- }
5904
- } else if (!isFromCache) {
5905
- doc.data = data;
5906
- copyDocumentProperties(doc, document);
5907
- }
5908
- return options.shouldHydrate ? doc : document;
5909
- }
5910
- }
5911
- function maybeUpdateErrorUiObjects(store, options, document, isFromCache) {
5912
- const {
5913
- identifier
5914
- } = options;
5915
-
5916
- // TODO investigate why ResourceErrorDocument is insufficient for expressing all error types
5917
- if (!isErrorDocument(document) || !identifier && !options.shouldHydrate) {
5918
- return document;
5919
- }
5920
- let doc;
5921
6307
  if (identifier) {
5922
- doc = store._documentCache.get(identifier);
5923
- }
5924
- if (!doc) {
5925
- doc = new Document(store, identifier);
5926
- copyDocumentProperties(doc, document);
5927
- if (identifier) {
5928
- store._documentCache.set(identifier, doc);
5929
- }
5930
- } else if (!isFromCache) {
5931
- doc.data = undefined;
5932
- copyDocumentProperties(doc, document);
6308
+ return store._instanceCache.getDocument(identifier);
5933
6309
  }
5934
- return options.shouldHydrate ? doc : document;
6310
+
6311
+ // if we don't have an identifier, we give the document
6312
+ // its own local cache
6313
+ return new ReactiveDocument(store, null, {
6314
+ request,
6315
+ document
6316
+ });
5935
6317
  }
5936
6318
  function updateCacheForSuccess(store, request, options, document) {
5937
6319
  let response = null;
@@ -5949,7 +6331,7 @@ function updateCacheForSuccess(store, request, options, document) {
5949
6331
  } else {
5950
6332
  response = store.cache.put(document);
5951
6333
  }
5952
- return maybeUpdateUiObjects(store, request, options, response, false);
6334
+ return maybeUpdateUiObjects(store, request, options, response);
5953
6335
  }
5954
6336
  function handleFetchSuccess(store, context, options, document) {
5955
6337
  const {
@@ -5982,7 +6364,7 @@ function updateCacheForError(store, context, options, error) {
5982
6364
  store.cache.commitWasRejected(record, errors);
5983
6365
  } else {
5984
6366
  response = store.cache.put(error);
5985
- return maybeUpdateErrorUiObjects(store, options, response, false);
6367
+ return maybeUpdateUiObjects(store, context.request, options, response);
5986
6368
  }
5987
6369
  }
5988
6370
  function handleFetchError(store, context, options, error) {
@@ -6063,4 +6445,475 @@ function fetchContentAndHydrate(next, context, identifier, priority) {
6063
6445
  }]
6064
6446
  });
6065
6447
  }
6066
- export { ARRAY_SIGNAL as A, CacheHandler as C, IdentifierArray as I, MUTATE as M, RecordArrayManager as R, Store as S, _clearCaches as _, setIdentifierGenerationMethod as a, setIdentifierUpdateMethod as b, setIdentifierForgetMethod as c, setIdentifierResetMethod as d, setKeyInfoForResource as e, constructResource as f, coerceId as g, ensureStringId as h, isStableIdentifier as i, Collection as j, SOURCE as k, fastPush as l, removeRecordDataFor as m, notifyArray as n, setRecordIdentifier as o, peekCache as p, StoreMap as q, recordIdentifierFor as r, storeFor as s, setCacheFor as t, normalizeModelName as u };
6448
+
6449
+ /**
6450
+ @module @ember-data/store
6451
+ */
6452
+ /**
6453
+ A `ManyArray` is a `MutableArray` that represents the contents of a has-many
6454
+ relationship.
6455
+
6456
+ The `ManyArray` is instantiated lazily the first time the relationship is
6457
+ requested.
6458
+
6459
+ This class is not intended to be directly instantiated by consuming applications.
6460
+
6461
+ ### Inverses
6462
+
6463
+ Often, the relationships in Ember Data applications will have
6464
+ an inverse. For example, imagine the following models are
6465
+ defined:
6466
+
6467
+ ```app/models/post.js
6468
+ import Model, { hasMany } from '@ember-data/model';
6469
+
6470
+ export default class PostModel extends Model {
6471
+ @hasMany('comment') comments;
6472
+ }
6473
+ ```
6474
+
6475
+ ```app/models/comment.js
6476
+ import Model, { belongsTo } from '@ember-data/model';
6477
+
6478
+ export default class CommentModel extends Model {
6479
+ @belongsTo('post') post;
6480
+ }
6481
+ ```
6482
+
6483
+ If you created a new instance of `Post` and added
6484
+ a `Comment` record to its `comments` has-many
6485
+ relationship, you would expect the comment's `post`
6486
+ property to be set to the post that contained
6487
+ the has-many.
6488
+
6489
+ We call the record to which a relationship belongs-to the
6490
+ relationship's _owner_.
6491
+
6492
+ @class ManyArray
6493
+ @public
6494
+ */
6495
+ class RelatedCollection extends IdentifierArray {
6496
+ /**
6497
+ The loading state of this array
6498
+ @property {Boolean} isLoaded
6499
+ @public
6500
+ */
6501
+
6502
+ /**
6503
+ `true` if the relationship is polymorphic, `false` otherwise.
6504
+ @property {Boolean} isPolymorphic
6505
+ @private
6506
+ */
6507
+
6508
+ /**
6509
+ Metadata associated with the request for async hasMany relationships.
6510
+ Example
6511
+ Given that the server returns the following JSON payload when fetching a
6512
+ hasMany relationship:
6513
+ ```js
6514
+ {
6515
+ "comments": [{
6516
+ "id": 1,
6517
+ "comment": "This is the first comment",
6518
+ }, {
6519
+ // ...
6520
+ }],
6521
+ "meta": {
6522
+ "page": 1,
6523
+ "total": 5
6524
+ }
6525
+ }
6526
+ ```
6527
+ You can then access the meta data via the `meta` property:
6528
+ ```js
6529
+ let comments = await post.comments;
6530
+ let meta = comments.meta;
6531
+ // meta.page => 1
6532
+ // meta.total => 5
6533
+ ```
6534
+ @property {Object | null} meta
6535
+ @public
6536
+ */
6537
+
6538
+ /**
6539
+ * Retrieve the links for this relationship
6540
+ *
6541
+ @property {Object | null} links
6542
+ @public
6543
+ */
6544
+
6545
+ constructor(options) {
6546
+ super(options);
6547
+ this.isLoaded = options.isLoaded || false;
6548
+ this.isAsync = options.isAsync || false;
6549
+ this.isPolymorphic = options.isPolymorphic || false;
6550
+ this.identifier = options.identifier;
6551
+ this.key = options.key;
6552
+ }
6553
+ [MUTATE](target, receiver, prop, args, _SIGNAL) {
6554
+ switch (prop) {
6555
+ case 'length 0':
6556
+ {
6557
+ Reflect.set(target, 'length', 0);
6558
+ mutateReplaceRelatedRecords(this, [], _SIGNAL);
6559
+ return true;
6560
+ }
6561
+ case 'replace cell':
6562
+ {
6563
+ const [index, prior, value] = args;
6564
+ target[index] = value;
6565
+ mutateReplaceRelatedRecord(this, {
6566
+ value,
6567
+ prior,
6568
+ index
6569
+ }, _SIGNAL);
6570
+ return true;
6571
+ }
6572
+ case 'push':
6573
+ {
6574
+ const newValues = extractIdentifiersFromRecords(args);
6575
+ assertNoDuplicates(this, target, currentState => currentState.push(...newValues), `Cannot push duplicates to a hasMany's state.`);
6576
+ if (macroCondition(getGlobalConfig().WarpDrive.deprecations.DEPRECATE_MANY_ARRAY_DUPLICATES)) {
6577
+ // dedupe
6578
+ const seen = new Set(target);
6579
+ const unique = new Set();
6580
+ args.forEach(item => {
6581
+ const identifier = recordIdentifierFor(item);
6582
+ if (!seen.has(identifier)) {
6583
+ seen.add(identifier);
6584
+ unique.add(item);
6585
+ }
6586
+ });
6587
+ const newArgs = Array.from(unique);
6588
+ const result = Reflect.apply(target[prop], receiver, newArgs);
6589
+ if (newArgs.length) {
6590
+ mutateAddToRelatedRecords(this, {
6591
+ value: extractIdentifiersFromRecords(newArgs)
6592
+ }, _SIGNAL);
6593
+ }
6594
+ return result;
6595
+ }
6596
+
6597
+ // else, no dedupe, error on duplicates
6598
+ const result = Reflect.apply(target[prop], receiver, args);
6599
+ if (newValues.length) {
6600
+ mutateAddToRelatedRecords(this, {
6601
+ value: newValues
6602
+ }, _SIGNAL);
6603
+ }
6604
+ return result;
6605
+ }
6606
+ case 'pop':
6607
+ {
6608
+ const result = Reflect.apply(target[prop], receiver, args);
6609
+ if (result) {
6610
+ mutateRemoveFromRelatedRecords(this, {
6611
+ value: recordIdentifierFor(result)
6612
+ }, _SIGNAL);
6613
+ }
6614
+ return result;
6615
+ }
6616
+ case 'unshift':
6617
+ {
6618
+ const newValues = extractIdentifiersFromRecords(args);
6619
+ assertNoDuplicates(this, target, currentState => currentState.unshift(...newValues), `Cannot unshift duplicates to a hasMany's state.`);
6620
+ if (macroCondition(getGlobalConfig().WarpDrive.deprecations.DEPRECATE_MANY_ARRAY_DUPLICATES)) {
6621
+ // dedupe
6622
+ const seen = new Set(target);
6623
+ const unique = new Set();
6624
+ args.forEach(item => {
6625
+ const identifier = recordIdentifierFor(item);
6626
+ if (!seen.has(identifier)) {
6627
+ seen.add(identifier);
6628
+ unique.add(item);
6629
+ }
6630
+ });
6631
+ const newArgs = Array.from(unique);
6632
+ const result = Reflect.apply(target[prop], receiver, newArgs);
6633
+ if (newArgs.length) {
6634
+ mutateAddToRelatedRecords(this, {
6635
+ value: extractIdentifiersFromRecords(newArgs),
6636
+ index: 0
6637
+ }, _SIGNAL);
6638
+ }
6639
+ return result;
6640
+ }
6641
+
6642
+ // else, no dedupe, error on duplicates
6643
+ const result = Reflect.apply(target[prop], receiver, args);
6644
+ if (newValues.length) {
6645
+ mutateAddToRelatedRecords(this, {
6646
+ value: newValues,
6647
+ index: 0
6648
+ }, _SIGNAL);
6649
+ }
6650
+ return result;
6651
+ }
6652
+ case 'shift':
6653
+ {
6654
+ const result = Reflect.apply(target[prop], receiver, args);
6655
+ if (result) {
6656
+ mutateRemoveFromRelatedRecords(this, {
6657
+ value: recordIdentifierFor(result),
6658
+ index: 0
6659
+ }, _SIGNAL);
6660
+ }
6661
+ return result;
6662
+ }
6663
+ case 'sort':
6664
+ {
6665
+ const result = Reflect.apply(target[prop], receiver, args);
6666
+ mutateSortRelatedRecords(this, result.map(recordIdentifierFor), _SIGNAL);
6667
+ return result;
6668
+ }
6669
+ case 'splice':
6670
+ {
6671
+ const [start, deleteCount, ...adds] = args;
6672
+
6673
+ // detect a full replace
6674
+ if (start === 0 && deleteCount === this[SOURCE].length) {
6675
+ const newValues = extractIdentifiersFromRecords(adds);
6676
+ assertNoDuplicates(this, target, currentState => currentState.splice(start, deleteCount, ...newValues), `Cannot replace a hasMany's state with a new state that contains duplicates.`);
6677
+ if (macroCondition(getGlobalConfig().WarpDrive.deprecations.DEPRECATE_MANY_ARRAY_DUPLICATES)) {
6678
+ // dedupe
6679
+ const current = new Set(adds);
6680
+ const unique = Array.from(current);
6681
+ const newArgs = [start, deleteCount].concat(unique);
6682
+ const result = Reflect.apply(target[prop], receiver, newArgs);
6683
+ mutateReplaceRelatedRecords(this, extractIdentifiersFromRecords(unique), _SIGNAL);
6684
+ return result;
6685
+ }
6686
+
6687
+ // else, no dedupe, error on duplicates
6688
+ const result = Reflect.apply(target[prop], receiver, args);
6689
+ mutateReplaceRelatedRecords(this, newValues, _SIGNAL);
6690
+ return result;
6691
+ }
6692
+ const newValues = extractIdentifiersFromRecords(adds);
6693
+ assertNoDuplicates(this, target, currentState => currentState.splice(start, deleteCount, ...newValues), `Cannot splice a hasMany's state with a new state that contains duplicates.`);
6694
+ if (macroCondition(getGlobalConfig().WarpDrive.deprecations.DEPRECATE_MANY_ARRAY_DUPLICATES)) {
6695
+ // dedupe
6696
+ const currentState = target.slice();
6697
+ currentState.splice(start, deleteCount);
6698
+ const seen = new Set(currentState);
6699
+ const unique = [];
6700
+ adds.forEach(item => {
6701
+ const identifier = recordIdentifierFor(item);
6702
+ if (!seen.has(identifier)) {
6703
+ seen.add(identifier);
6704
+ unique.push(item);
6705
+ }
6706
+ });
6707
+ const newArgs = [start, deleteCount, ...unique];
6708
+ const result = Reflect.apply(target[prop], receiver, newArgs);
6709
+ if (deleteCount > 0) {
6710
+ mutateRemoveFromRelatedRecords(this, {
6711
+ value: result.map(recordIdentifierFor),
6712
+ index: start
6713
+ }, _SIGNAL);
6714
+ }
6715
+ if (unique.length > 0) {
6716
+ mutateAddToRelatedRecords(this, {
6717
+ value: extractIdentifiersFromRecords(unique),
6718
+ index: start
6719
+ }, _SIGNAL);
6720
+ }
6721
+ return result;
6722
+ }
6723
+
6724
+ // else, no dedupe, error on duplicates
6725
+ const result = Reflect.apply(target[prop], receiver, args);
6726
+ if (deleteCount > 0) {
6727
+ mutateRemoveFromRelatedRecords(this, {
6728
+ value: result.map(recordIdentifierFor),
6729
+ index: start
6730
+ }, _SIGNAL);
6731
+ }
6732
+ if (newValues.length > 0) {
6733
+ mutateAddToRelatedRecords(this, {
6734
+ value: newValues,
6735
+ index: start
6736
+ }, _SIGNAL);
6737
+ }
6738
+ return result;
6739
+ }
6740
+ default:
6741
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
6742
+ {
6743
+ throw new Error(`unable to convert ${prop} into a transaction that updates the cache state for this record array`);
6744
+ }
6745
+ })() : {};
6746
+ }
6747
+ }
6748
+ notify() {
6749
+ const signal = this[ARRAY_SIGNAL];
6750
+ signal.shouldReset = true;
6751
+ notifyArray(this);
6752
+ }
6753
+
6754
+ /**
6755
+ Reloads all of the records in the manyArray. If the manyArray
6756
+ holds a relationship that was originally fetched using a links url
6757
+ EmberData will revisit the original links url to repopulate the
6758
+ relationship.
6759
+ If the ManyArray holds the result of a `store.query()` reload will
6760
+ re-run the original query.
6761
+ Example
6762
+ ```javascript
6763
+ let user = store.peekRecord('user', '1')
6764
+ await login(user);
6765
+ let permissions = await user.permissions;
6766
+ await permissions.reload();
6767
+ ```
6768
+ @method reload
6769
+ @public
6770
+ */
6771
+ reload(options) {
6772
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
6773
+ if (!test) {
6774
+ throw new Error(`Expected the manager for ManyArray to implement reloadHasMany`);
6775
+ }
6776
+ })(typeof this._manager.reloadHasMany === 'function') : {};
6777
+ // TODO this is odd, we don't ask the store for anything else like this?
6778
+ return this._manager.reloadHasMany(this.key, options);
6779
+ }
6780
+
6781
+ /**
6782
+ Saves all of the records in the `ManyArray`.
6783
+ Note: this API can only be used in legacy mode with a configured Adapter.
6784
+ Example
6785
+ ```javascript
6786
+ const { content: { data: inbox } } = await store.request(findRecord({ type: 'inbox', id: '1' }));
6787
+ let messages = await inbox.messages;
6788
+ messages.forEach((message) => {
6789
+ message.isRead = true;
6790
+ });
6791
+ messages.save();
6792
+ ```
6793
+ @method save
6794
+ @public
6795
+ @return {PromiseArray} promise
6796
+ */
6797
+
6798
+ /**
6799
+ Create a child record within the owner
6800
+ @method createRecord
6801
+ @public
6802
+ @param {Object} hash
6803
+ @return {Model} record
6804
+ */
6805
+ createRecord(hash) {
6806
+ const {
6807
+ store
6808
+ } = this;
6809
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
6810
+ if (!test) {
6811
+ throw new Error(`Expected modelName to be set`);
6812
+ }
6813
+ })(this.modelName) : {};
6814
+ const record = store.createRecord(this.modelName, hash);
6815
+ this.push(record);
6816
+ return record;
6817
+ }
6818
+ destroy() {
6819
+ super.destroy(false);
6820
+ }
6821
+ }
6822
+ RelatedCollection.prototype.isAsync = false;
6823
+ RelatedCollection.prototype.isPolymorphic = false;
6824
+ RelatedCollection.prototype.identifier = null;
6825
+ RelatedCollection.prototype.cache = null;
6826
+ RelatedCollection.prototype._inverseIsAsync = false;
6827
+ RelatedCollection.prototype.key = '';
6828
+ RelatedCollection.prototype.DEPRECATED_CLASS_NAME = 'ManyArray';
6829
+ function assertRecordPassedToHasMany(record) {
6830
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
6831
+ if (!test) {
6832
+ throw new Error(`All elements of a hasMany relationship must be instances of Model, you passed ${typeof record}`);
6833
+ }
6834
+ })(function () {
6835
+ try {
6836
+ recordIdentifierFor(record);
6837
+ return true;
6838
+ } catch {
6839
+ return false;
6840
+ }
6841
+ }()) : {};
6842
+ }
6843
+ function extractIdentifiersFromRecords(records) {
6844
+ return records.map(extractIdentifierFromRecord);
6845
+ }
6846
+ function extractIdentifierFromRecord(recordOrPromiseRecord) {
6847
+ assertRecordPassedToHasMany(recordOrPromiseRecord);
6848
+ return recordIdentifierFor(recordOrPromiseRecord);
6849
+ }
6850
+ function assertNoDuplicates(collection, target, callback, reason) {
6851
+ const state = target.slice();
6852
+ callback(state);
6853
+ if (state.length !== new Set(state).size) {
6854
+ const duplicates = state.filter((currentValue, currentIndex) => state.indexOf(currentValue) !== currentIndex);
6855
+ if (macroCondition(getGlobalConfig().WarpDrive.deprecations.DEPRECATE_MANY_ARRAY_DUPLICATES)) {
6856
+ deprecate(`${reason} This behavior is deprecated. Found duplicates for the following records within the new state provided to \`<${collection.identifier.type}:${collection.identifier.id || collection.identifier.lid}>.${collection.key}\`\n\t- ${Array.from(new Set(duplicates)).map(r => isStableIdentifier(r) ? r.lid : recordIdentifierFor(r).lid).sort((a, b) => a.localeCompare(b)).join('\n\t- ')}`, false, {
6857
+ id: 'ember-data:deprecate-many-array-duplicates',
6858
+ for: 'ember-data',
6859
+ until: '6.0',
6860
+ since: {
6861
+ enabled: '5.3',
6862
+ available: '4.13'
6863
+ }
6864
+ });
6865
+ } else {
6866
+ throw new Error(`${reason} Found duplicates for the following records within the new state provided to \`<${collection.identifier.type}:${collection.identifier.id || collection.identifier.lid}>.${collection.key}\`\n\t- ${Array.from(new Set(duplicates)).map(r => isStableIdentifier(r) ? r.lid : recordIdentifierFor(r).lid).sort((a, b) => a.localeCompare(b)).join('\n\t- ')}`);
6867
+ }
6868
+ }
6869
+ }
6870
+ function mutateAddToRelatedRecords(collection, operationInfo, _SIGNAL) {
6871
+ mutate(collection, {
6872
+ op: 'add',
6873
+ record: collection.identifier,
6874
+ field: collection.key,
6875
+ ...operationInfo
6876
+ }, _SIGNAL);
6877
+ }
6878
+ function mutateRemoveFromRelatedRecords(collection, operationInfo, _SIGNAL) {
6879
+ mutate(collection, {
6880
+ op: 'remove',
6881
+ record: collection.identifier,
6882
+ field: collection.key,
6883
+ ...operationInfo
6884
+ }, _SIGNAL);
6885
+ }
6886
+ function mutateReplaceRelatedRecord(collection, operationInfo, _SIGNAL) {
6887
+ mutate(collection, {
6888
+ op: 'replaceRelatedRecord',
6889
+ record: collection.identifier,
6890
+ field: collection.key,
6891
+ ...operationInfo
6892
+ }, _SIGNAL);
6893
+ }
6894
+ function mutateReplaceRelatedRecords(collection, value, _SIGNAL) {
6895
+ mutate(collection, {
6896
+ op: 'replaceRelatedRecords',
6897
+ record: collection.identifier,
6898
+ field: collection.key,
6899
+ value
6900
+ }, _SIGNAL);
6901
+ }
6902
+ function mutateSortRelatedRecords(collection, value, _SIGNAL) {
6903
+ mutate(collection, {
6904
+ op: 'sortRelatedRecords',
6905
+ record: collection.identifier,
6906
+ field: collection.key,
6907
+ value
6908
+ }, _SIGNAL);
6909
+ }
6910
+ function mutate(collection, mutation, _SIGNAL) {
6911
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
6912
+ if (!test) {
6913
+ throw new Error(`Expected the manager for ManyArray to implement mutate`);
6914
+ }
6915
+ })(typeof collection._manager.mutate === 'function') : {};
6916
+ collection._manager.mutate(mutation);
6917
+ addToTransaction(_SIGNAL);
6918
+ }
6919
+ export { ARRAY_SIGNAL as A, CacheHandler as C, IdentifierArray as I, MUTATE as M, RecordArrayManager as R, Store as S, _clearCaches as _, setIdentifierGenerationMethod as a, setIdentifierUpdateMethod as b, setIdentifierForgetMethod as c, setIdentifierResetMethod as d, setKeyInfoForResource as e, isDocumentIdentifier as f, constructResource as g, coerceId as h, isStableIdentifier as i, ensureStringId as j, Collection as k, SOURCE as l, fastPush as m, notifyArray as n, removeRecordDataFor as o, peekCache as p, setRecordIdentifier as q, recordIdentifierFor as r, storeFor as s, StoreMap as t, setCacheFor as u, normalizeModelName as v, RelatedCollection as w, log as x, logGroup as y };