@ember-data-mirror/store 5.4.0-beta.9 → 5.4.1-beta.0

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 (65) hide show
  1. package/LICENSE.md +19 -7
  2. package/README.md +5 -8
  3. package/dist/-private.js +1 -1
  4. package/dist/index.js +1 -1
  5. package/dist/{cache-handler-C5ilAUZ5.js → many-array-BwVo-2vv.js} +1828 -999
  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/ember-data-logo-dark.svg +12 -0
  15. package/logos/ember-data-logo-light.svg +12 -0
  16. package/logos/github-header.svg +444 -0
  17. package/logos/social1.png +0 -0
  18. package/logos/social2.png +0 -0
  19. package/logos/warp-drive-logo-dark.svg +4 -0
  20. package/logos/warp-drive-logo-gold.svg +4 -0
  21. package/package.json +40 -48
  22. package/unstable-preview-types/-private/cache-handler/handler.d.ts +62 -0
  23. package/unstable-preview-types/-private/cache-handler/handler.d.ts.map +1 -0
  24. package/unstable-preview-types/-private/{cache-handler.d.ts → cache-handler/types.d.ts} +25 -62
  25. package/unstable-preview-types/-private/cache-handler/types.d.ts.map +1 -0
  26. package/unstable-preview-types/-private/cache-handler/utils.d.ts +34 -0
  27. package/unstable-preview-types/-private/cache-handler/utils.d.ts.map +1 -0
  28. package/unstable-preview-types/-private/caches/identifier-cache.d.ts +7 -3
  29. package/unstable-preview-types/-private/caches/identifier-cache.d.ts.map +1 -1
  30. package/unstable-preview-types/-private/caches/instance-cache.d.ts +4 -1
  31. package/unstable-preview-types/-private/caches/instance-cache.d.ts.map +1 -1
  32. package/unstable-preview-types/-private/debug/utils.d.ts +9 -0
  33. package/unstable-preview-types/-private/debug/utils.d.ts.map +1 -0
  34. package/unstable-preview-types/-private/document.d.ts +23 -14
  35. package/unstable-preview-types/-private/document.d.ts.map +1 -1
  36. package/unstable-preview-types/-private/legacy-model-support/record-reference.d.ts.map +1 -1
  37. package/unstable-preview-types/-private/legacy-model-support/shim-model-class.d.ts +3 -3
  38. package/unstable-preview-types/-private/legacy-model-support/shim-model-class.d.ts.map +1 -1
  39. package/unstable-preview-types/-private/managers/cache-capabilities-manager.d.ts +3 -3
  40. package/unstable-preview-types/-private/managers/cache-capabilities-manager.d.ts.map +1 -1
  41. package/unstable-preview-types/-private/managers/cache-manager.d.ts +23 -1
  42. package/unstable-preview-types/-private/managers/cache-manager.d.ts.map +1 -1
  43. package/unstable-preview-types/-private/managers/notification-manager.d.ts +11 -6
  44. package/unstable-preview-types/-private/managers/notification-manager.d.ts.map +1 -1
  45. package/unstable-preview-types/-private/managers/record-array-manager.d.ts +9 -5
  46. package/unstable-preview-types/-private/managers/record-array-manager.d.ts.map +1 -1
  47. package/unstable-preview-types/-private/record-arrays/identifier-array.d.ts +14 -2
  48. package/unstable-preview-types/-private/record-arrays/identifier-array.d.ts.map +1 -1
  49. package/unstable-preview-types/-private/record-arrays/many-array.d.ts +199 -0
  50. package/unstable-preview-types/-private/record-arrays/many-array.d.ts.map +1 -0
  51. package/unstable-preview-types/-private/record-arrays/native-proxy-type-fix.d.ts +3 -3
  52. package/unstable-preview-types/-private/store-service.d.ts +92 -19
  53. package/unstable-preview-types/-private/store-service.d.ts.map +1 -1
  54. package/unstable-preview-types/-private.d.ts +7 -4
  55. package/unstable-preview-types/-private.d.ts.map +1 -1
  56. package/unstable-preview-types/-types/q/cache-capabilities-manager.d.ts +4 -4
  57. package/unstable-preview-types/-types/q/cache-capabilities-manager.d.ts.map +1 -1
  58. package/unstable-preview-types/-types/q/ds-model.d.ts +3 -3
  59. package/unstable-preview-types/-types/q/ds-model.d.ts.map +1 -1
  60. package/unstable-preview-types/-types/q/schema-service.d.ts +16 -8
  61. package/unstable-preview-types/-types/q/schema-service.d.ts.map +1 -1
  62. package/unstable-preview-types/index.d.ts +24 -23
  63. package/unstable-preview-types/index.d.ts.map +1 -1
  64. package/dist/cache-handler-C5ilAUZ5.js.map +0 -1
  65. package/unstable-preview-types/-private/cache-handler.d.ts.map +0 -1
@@ -1,11 +1,12 @@
1
1
  import { deprecate, warn } from '@ember/debug';
2
2
  import { macroCondition, getGlobalConfig, dependencySatisfies, importSync } from '@embroider/macros';
3
- import { SkipCache, EnableHydration } from '@warp-drive-mirror/core-types/request';
4
- import { getOrSetGlobal, setTransient, peekTransient } from '@warp-drive-mirror/core-types/-private';
5
- import { CACHE_OWNER, DEBUG_STALE_CACHE_OWNER, DEBUG_CLIENT_ORIGINATED, DEBUG_IDENTIFIER_BUCKET } from '@warp-drive-mirror/core-types/identifier';
6
- import { dasherize } from '@ember-data-mirror/request-utils/string';
7
- import { defineSignal, createSignal, subscribe, createArrayTags, addToTransaction, addTransactionCB } from '@ember-data-mirror/tracking/-private';
3
+ import { EnableHydration, SkipCache } from '@warp-drive-mirror/core-types/request';
4
+ import { setLogging, getRuntimeConfig } from '@warp-drive-mirror/core-types/runtime';
5
+ import { getOrSetGlobal, peekTransient, setTransient } from '@warp-drive-mirror/core-types/-private';
8
6
  import { _backburner } from '@ember/runloop';
7
+ import { defineSubscription, notifySignal, defineSignal, createSignal, subscribe, createArrayTags, addToTransaction, addTransactionCB } from '@ember-data-mirror/tracking/-private';
8
+ import { CACHE_OWNER, DEBUG_STALE_CACHE_OWNER, DEBUG_IDENTIFIER_BUCKET, DEBUG_CLIENT_ORIGINATED } from '@warp-drive-mirror/core-types/identifier';
9
+ import { dasherize } from '@ember-data-mirror/request-utils/string';
9
10
  import { compat } from '@ember-data-mirror/tracking';
10
11
 
11
12
  /**
@@ -13,7 +14,7 @@ import { compat } from '@ember-data-mirror/tracking';
13
14
  */
14
15
 
15
16
  function coerceId(id) {
16
- if (macroCondition(getGlobalConfig().WarpDrive.deprecations.DEPRECATE_NON_STRICT_ID)) {
17
+ if (macroCondition(getGlobalConfig().WarpDriveMirror.deprecations.DEPRECATE_NON_STRICT_ID)) {
17
18
  let normalized;
18
19
  if (id === null || id === undefined || id === '') {
19
20
  normalized = null;
@@ -25,13 +26,13 @@ function coerceId(id) {
25
26
  until: '6.0',
26
27
  for: 'ember-data-mirror',
27
28
  since: {
28
- available: '5.3',
29
+ available: '4.13',
29
30
  enabled: '5.3'
30
31
  }
31
32
  });
32
33
  return normalized;
33
34
  }
34
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
35
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
35
36
  if (!test) {
36
37
  throw new Error(`Resource IDs must be a non-empty string or null. Received '${String(id)}'.`);
37
38
  }
@@ -45,7 +46,7 @@ function ensureStringId(id) {
45
46
  } else if (typeof id === 'number' && !isNaN(id)) {
46
47
  normalized = String(id);
47
48
  }
48
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
49
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
49
50
  if (!test) {
50
51
  throw new Error(`Expected id to be a string or number, received ${String(id)}`);
51
52
  }
@@ -53,14 +54,14 @@ function ensureStringId(id) {
53
54
  return normalized;
54
55
  }
55
56
  function normalizeModelName(type) {
56
- if (macroCondition(getGlobalConfig().WarpDrive.deprecations.DEPRECATE_NON_STRICT_TYPES)) {
57
+ if (macroCondition(getGlobalConfig().WarpDriveMirror.deprecations.DEPRECATE_NON_STRICT_TYPES)) {
57
58
  const result = dasherize(type);
58
59
  deprecate(`The resource type '${type}' is not normalized. Update your application code to use '${result}' instead of '${type}'.`, result === type, {
59
60
  id: 'ember-data-mirror:deprecate-non-strict-types',
60
61
  until: '6.0',
61
62
  for: 'ember-data-mirror',
62
63
  since: {
63
- available: '5.3',
64
+ available: '4.13',
64
65
  enabled: '5.3'
65
66
  }
66
67
  });
@@ -75,7 +76,7 @@ function normalizeModelName(type) {
75
76
 
76
77
  function installPolyfill() {
77
78
  const isFastBoot = typeof FastBoot !== 'undefined';
78
- const CRYPTO = isFastBoot ? FastBoot.require('crypto') : window.crypto;
79
+ const CRYPTO = isFastBoot ? FastBoot.require('crypto') : globalThis.crypto;
79
80
  if (!CRYPTO.randomUUID) {
80
81
  // we might be able to optimize this by requesting more bytes than we need at a time
81
82
  const rng = function () {
@@ -129,21 +130,20 @@ function hasType(resource) {
129
130
  /**
130
131
  @module @ember-data-mirror/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);
139
139
  }
140
140
  const isFastBoot = typeof FastBoot !== 'undefined';
141
- const _crypto = isFastBoot ? FastBoot.require('crypto') : window.crypto;
142
- if (macroCondition(getGlobalConfig().WarpDrive.polyfillUUID)) {
141
+ const _crypto = isFastBoot ? FastBoot.require('crypto') : globalThis.crypto;
142
+ if (macroCondition(getGlobalConfig().WarpDriveMirror.polyfillUUID)) {
143
143
  installPolyfill();
144
144
  }
145
145
  function uuidv4() {
146
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
146
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
147
147
  if (!test) {
148
148
  throw new Error('crypto.randomUUID needs to be avaliable. Some browsers incorrectly disallow it in insecure contexts. You maybe want to enable the polyfill: https://github.com/emberjs/data#randomuuid-polyfill');
149
149
  }
@@ -192,7 +192,7 @@ function updateTypeIdMapping(typeMap, identifier, id) {
192
192
  }
193
193
  function defaultUpdateMethod(identifier, data, bucket) {
194
194
  if (bucket === 'record') {
195
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
195
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
196
196
  if (!test) {
197
197
  throw new Error(`Expected identifier to be a StableRecordIdentifier`);
198
198
  }
@@ -206,7 +206,7 @@ function defaultKeyInfoMethod(resource, known) {
206
206
  // TODO RFC something to make this configurable
207
207
  const id = hasId(resource) ? coerceId(resource.id) : null;
208
208
  const type = hasType(resource) ? normalizeModelName(resource.type) : known ? known.type : null;
209
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
209
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
210
210
  if (!test) {
211
211
  throw new Error(`Expected keyInfoForResource to provide a type for the resource`);
212
212
  }
@@ -221,7 +221,7 @@ function defaultGenerationMethod(data, bucket) {
221
221
  if (hasLid(data)) {
222
222
  return data.lid;
223
223
  }
224
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
224
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
225
225
  if (!test) {
226
226
  throw new Error(`Cannot generate an identifier for a resource without a type`);
227
227
  }
@@ -241,7 +241,7 @@ function defaultGenerationMethod(data, bucket) {
241
241
  }
242
242
  return null;
243
243
  }
244
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
244
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
245
245
  {
246
246
  throw new Error(`Unknown bucket ${bucket}`);
247
247
  }
@@ -252,7 +252,7 @@ function defaultMergeMethod(a, _b, _c) {
252
252
  return a;
253
253
  }
254
254
  let DEBUG_MAP;
255
- if (macroCondition(getGlobalConfig().WarpDrive.env.DEBUG)) {
255
+ if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
256
256
  DEBUG_MAP = getOrSetGlobal('DEBUG_MAP', new WeakMap());
257
257
  }
258
258
 
@@ -310,45 +310,55 @@ 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().WarpDriveMirror.activeLogging.LOG_IDENTIFIERS)) {
314
+ if (getGlobalConfig().WarpDriveMirror.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)) {
319
- if (macroCondition(getGlobalConfig().WarpDrive.env.DEBUG)) {
321
+ if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
320
322
  // TODO should we instead just treat this case as a new generation skipping the short circuit?
321
323
  if (!this._cache.resources.has(resource.lid) || this._cache.resources.get(resource.lid) !== resource) {
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().WarpDriveMirror.activeLogging.LOG_IDENTIFIERS)) {
328
+ if (getGlobalConfig().WarpDriveMirror.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().WarpDriveMirror.activeLogging.LOG_IDENTIFIERS)) {
341
+ if (getGlobalConfig().WarpDriveMirror.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().WarpDriveMirror.activeLogging.LOG_IDENTIFIERS)) {
349
+ if (getGlobalConfig().WarpDriveMirror.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().WarpDriveMirror.activeLogging.LOG_IDENTIFIERS)) {
358
+ if (getGlobalConfig().WarpDriveMirror.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().WarpDriveMirror.activeLogging.LOG_IDENTIFIERS)) {
380
+ if (getGlobalConfig().WarpDriveMirror.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
  }
@@ -408,7 +420,7 @@ class IdentifierCache {
408
420
  identifier = {
409
421
  lid: cacheKey
410
422
  };
411
- if (macroCondition(getGlobalConfig().WarpDrive.env.DEBUG)) {
423
+ if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
412
424
  Object.freeze(identifier);
413
425
  }
414
426
  DOCUMENTS.add(identifier);
@@ -454,7 +466,7 @@ class IdentifierCache {
454
466
  }, 'record', true);
455
467
 
456
468
  // populate our unique table
457
- if (macroCondition(getGlobalConfig().WarpDrive.env.DEBUG)) {
469
+ if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
458
470
  if (this._cache.resources.has(identifier.lid)) {
459
471
  throw new Error(`The lid generated for the new record is not unique as it matches an existing identifier`);
460
472
  }
@@ -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().WarpDriveMirror.activeLogging.LOG_IDENTIFIERS)) {
478
+ if (getGlobalConfig().WarpDriveMirror.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().WarpDriveMirror.activeLogging.LOG_IDENTIFIERS)) {
529
+ if (getGlobalConfig().WarpDriveMirror.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,12 +539,14 @@ 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().WarpDriveMirror.activeLogging.LOG_IDENTIFIERS)) {
543
+ if (getGlobalConfig().WarpDriveMirror.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
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
549
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
532
550
  if (!test) {
533
551
  throw new Error(`Expected to find a typeSet for ${identifier.type}`);
534
552
  }
@@ -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().WarpDriveMirror.activeLogging.LOG_IDENTIFIERS)) {
559
+ if (getGlobalConfig().WarpDriveMirror.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
  }
@@ -549,7 +569,7 @@ class IdentifierCache {
549
569
  * @private
550
570
  */
551
571
  _mergeRecordIdentifiers(keyInfo, identifier, existingIdentifier, data) {
552
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
572
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
553
573
  if (!test) {
554
574
  throw new Error(`Expected keyInfo to contain an id`);
555
575
  }
@@ -600,7 +620,7 @@ class IdentifierCache {
600
620
  forgetRecordIdentifier(identifierObject) {
601
621
  const identifier = this.getOrCreateRecordIdentifier(identifierObject);
602
622
  const typeSet = this._cache.resourcesByType[identifier.type];
603
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
623
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
604
624
  if (!test) {
605
625
  throw new Error(`Expected to find a typeSet for ${identifier.type}`);
606
626
  }
@@ -617,15 +637,16 @@ class IdentifierCache {
617
637
  });
618
638
  this._cache.polymorphicLidBackMap.delete(identifier.lid);
619
639
  }
620
- if (macroCondition(getGlobalConfig().WarpDrive.env.DEBUG)) {
640
+ if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
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().WarpDriveMirror.activeLogging.LOG_IDENTIFIERS)) {
646
+ if (getGlobalConfig().WarpDriveMirror.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
- if (macroCondition(getGlobalConfig().WarpDrive.env.DEBUG)) {
661
+ if (macroCondition(getGlobalConfig().WarpDriveMirror.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;
@@ -700,7 +722,7 @@ function makeStableRecordIdentifier(recordIdentifier, bucket, clientOriginated)
700
722
  return recordIdentifier;
701
723
  }
702
724
  function performRecordIdentifierUpdate(identifier, keyInfo, data, updateFn) {
703
- if (macroCondition(getGlobalConfig().WarpDrive.env.DEBUG)) {
725
+ if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
704
726
  const {
705
727
  id,
706
728
  type
@@ -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().WarpDriveMirror.activeLogging.LOG_IDENTIFIERS)) {
801
+ if (getGlobalConfig().WarpDriveMirror.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,92 +820,477 @@ 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-mirror/store
802
- */
906
+ * @module @ember-data-mirror/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
- });
823
- }
824
- destroy() {
825
- this.store.notifications.unsubscribe(this.___token);
826
- }
827
- get type() {
828
- return this.identifier().type;
829
- }
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
+ */
830
955
 
831
956
  /**
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
- this._ref; // consume the tracked prop
846
- return this.___identifier.id;
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
+ }
1001
+ }
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;
847
1013
  }
848
1014
 
849
1015
  /**
850
- The `identifier` of the record that this reference refers to.
851
- Together, the `type` and `id` properties form a composite key for
852
- the identity map.
853
- Example
854
- ```javascript
855
- let userRef = store.getReference('user', 1);
856
- userRef.identifier(); // '1'
857
- ```
858
- @method identifier
859
- @public
860
- @return {String} The identifier of the record.
861
- */
862
- identifier() {
863
- return this.___identifier;
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().WarpDriveMirror.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);
864
1034
  }
865
1035
 
866
1036
  /**
867
- How the reference will be looked up when it is loaded. Currently
868
- this always returns `identity` to signify that a record will be
869
- loaded by its `type` and `id`.
870
- Example
871
- ```javascript
872
- const userRef = store.getReference('user', 1);
873
- userRef.remoteType(); // 'identity'
874
- ```
875
- @method remoteType
876
- @public
877
- @return {String} 'identity'
878
- */
879
- remoteType() {
880
- return 'identity';
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);
881
1048
  }
882
1049
 
883
1050
  /**
884
- This API allows you to provide a reference with new data. The
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);
1062
+ }
1063
+
1064
+ /**
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().WarpDriveMirror.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().WarpDriveMirror.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().WarpDriveMirror.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().WarpDriveMirror.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-mirror/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
1278
+ loaded by its `type` and `id`.
1279
+ Example
1280
+ ```javascript
1281
+ const userRef = store.getReference('user', 1);
1282
+ userRef.remoteType(); // 'identity'
1283
+ ```
1284
+ @method remoteType
1285
+ @public
1286
+ @return {String} 'identity'
1287
+ */
1288
+ remoteType() {
1289
+ return 'identity';
1290
+ }
1291
+
1292
+ /**
1293
+ This API allows you to provide a reference with new data. The
885
1294
  simplest usage of this API is similar to `store.push`: you provide a
886
1295
  normalized hash of data and the object represented by the reference
887
1296
  will update.
@@ -955,7 +1364,7 @@ class RecordReference {
955
1364
  if (id !== null) {
956
1365
  return this.store.findRecord(this.type, id);
957
1366
  }
958
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
1367
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
959
1368
  {
960
1369
  throw new Error(`Unable to fetch record of type ${this.type} without an id`);
961
1370
  }
@@ -982,7 +1391,7 @@ class RecordReference {
982
1391
  reload: true
983
1392
  });
984
1393
  }
985
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
1394
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
986
1395
  {
987
1396
  throw new Error(`Unable to fetch record of type ${this.type} without an id`);
988
1397
  }
@@ -1038,7 +1447,7 @@ class CacheCapabilitiesManager {
1038
1447
  });
1039
1448
  }
1040
1449
  notifyChange(identifier, namespace, key) {
1041
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
1450
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
1042
1451
  if (!test) {
1043
1452
  throw new Error(`Expected a stable identifier`);
1044
1453
  }
@@ -1057,7 +1466,7 @@ class CacheCapabilitiesManager {
1057
1466
  return this._store.schema;
1058
1467
  }
1059
1468
  setRecordId(identifier, id) {
1060
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
1469
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
1061
1470
  if (!test) {
1062
1471
  throw new Error(`Expected a stable identifier`);
1063
1472
  }
@@ -1068,7 +1477,7 @@ class CacheCapabilitiesManager {
1068
1477
  return Boolean(this._store._instanceCache.peek(identifier));
1069
1478
  }
1070
1479
  disconnectRecord(identifier) {
1071
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
1480
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
1072
1481
  if (!test) {
1073
1482
  throw new Error(`Expected a stable identifier`);
1074
1483
  }
@@ -1077,7 +1486,7 @@ class CacheCapabilitiesManager {
1077
1486
  this._pendingNotifies.delete(identifier);
1078
1487
  }
1079
1488
  }
1080
- if (macroCondition(getGlobalConfig().WarpDrive.deprecations.ENABLE_LEGACY_SCHEMA_SERVICE)) {
1489
+ if (macroCondition(getGlobalConfig().WarpDriveMirror.deprecations.ENABLE_LEGACY_SCHEMA_SERVICE)) {
1081
1490
  CacheCapabilitiesManager.prototype.getSchemaDefinitionService = function () {
1082
1491
  // FIXME add deprecation for this
1083
1492
  return this._store.schema;
@@ -1091,7 +1500,7 @@ if (macroCondition(getGlobalConfig().WarpDrive.deprecations.ENABLE_LEGACY_SCHEMA
1091
1500
 
1092
1501
  const CacheForIdentifierCache = getOrSetGlobal('CacheForIdentifierCache', new Map());
1093
1502
  function setCacheFor(identifier, cache) {
1094
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
1503
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
1095
1504
  if (!test) {
1096
1505
  throw new Error(`Illegal set of identifier`);
1097
1506
  }
@@ -1141,7 +1550,7 @@ function peekRecordIdentifier(record) {
1141
1550
  */
1142
1551
 
1143
1552
  function recordIdentifierFor(record) {
1144
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
1553
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
1145
1554
  if (!test) {
1146
1555
  throw new Error(`${String(record)} is not a record instantiated by @ember-data-mirror/store`);
1147
1556
  }
@@ -1149,7 +1558,7 @@ function recordIdentifierFor(record) {
1149
1558
  return RecordCache.get(record);
1150
1559
  }
1151
1560
  function setRecordIdentifier(record, identifier) {
1152
- if (macroCondition(getGlobalConfig().WarpDrive.env.DEBUG)) {
1561
+ if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
1153
1562
  if (RecordCache.has(record) && RecordCache.get(record) !== identifier) {
1154
1563
  throw new Error(`${String(record)} was already assigned an identifier`);
1155
1564
  }
@@ -1168,7 +1577,7 @@ function setRecordIdentifier(record, identifier) {
1168
1577
  const StoreMap = getOrSetGlobal('StoreMap', new Map());
1169
1578
  function storeFor(record) {
1170
1579
  const store = StoreMap.get(record);
1171
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
1580
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
1172
1581
  if (!test) {
1173
1582
  throw new Error(`A record in a disconnected state cannot utilize the store. This typically means the record has been destroyed, most commonly by unloading it.`);
1174
1583
  }
@@ -1176,12 +1585,13 @@ function storeFor(record) {
1176
1585
  return store;
1177
1586
  }
1178
1587
  class InstanceCache {
1179
- __instances = {
1180
- record: new Map(),
1181
- reference: new WeakMap()
1182
- };
1183
1588
  constructor(store) {
1184
1589
  this.store = store;
1590
+ this.__instances = {
1591
+ record: new Map(),
1592
+ reference: new WeakMap(),
1593
+ document: new Map()
1594
+ };
1185
1595
  this._storeWrapper = new CacheCapabilitiesManager(this.store);
1186
1596
  store.identifierCache.__configureMerge((identifier, matchedIdentifier, resourceData) => {
1187
1597
  let keptIdentifier = identifier;
@@ -1210,7 +1620,7 @@ class InstanceCache {
1210
1620
  if ('id' in resourceData) {
1211
1621
  throw new Error(`Failed to update the 'id' for the RecordIdentifier '${identifier.type}:${String(identifier.id)} (${identifier.lid})' to '${String(resourceData.id)}', because that id is already in use by '${matchedIdentifier.type}:${String(matchedIdentifier.id)} (${matchedIdentifier.lid})'`);
1212
1622
  }
1213
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
1623
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
1214
1624
  {
1215
1625
  throw new Error(`Failed to update the RecordIdentifier '${identifier.type}:${String(identifier.id)} (${identifier.lid})' to merge with the detected duplicate identifier '${matchedIdentifier.type}:${String(matchedIdentifier.id)} (${String(matchedIdentifier.lid)})'`);
1216
1626
  }
@@ -1236,10 +1646,18 @@ class InstanceCache {
1236
1646
  peek(identifier) {
1237
1647
  return this.__instances.record.get(identifier);
1238
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
+ }
1239
1657
  getRecord(identifier, properties) {
1240
1658
  let record = this.__instances.record.get(identifier);
1241
1659
  if (!record) {
1242
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
1660
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
1243
1661
  if (!test) {
1244
1662
  throw new Error(`Cannot create a new record instance while the store is being destroyed`);
1245
1663
  }
@@ -1251,9 +1669,16 @@ class InstanceCache {
1251
1669
  setCacheFor(record, cache);
1252
1670
  StoreMap.set(record, this.store);
1253
1671
  this.__instances.record.set(identifier, record);
1254
- if (macroCondition(getGlobalConfig().WarpDrive.debug.LOG_INSTANCE_CACHE)) {
1255
- // eslint-disable-next-line no-console
1256
- console.log(`InstanceCache: created Record for ${String(identifier)}`, properties);
1672
+ if (macroCondition(getGlobalConfig().WarpDriveMirror.activeLogging.LOG_INSTANCE_CACHE)) {
1673
+ if (getGlobalConfig().WarpDriveMirror.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
+ }
1257
1682
  }
1258
1683
  }
1259
1684
  return record;
@@ -1290,7 +1715,7 @@ class InstanceCache {
1290
1715
  }
1291
1716
  disconnect(identifier) {
1292
1717
  const record = this.__instances.record.get(identifier);
1293
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
1718
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
1294
1719
  if (!test) {
1295
1720
  throw new Error('Cannot destroy record while it is still materialized');
1296
1721
  }
@@ -1299,27 +1724,30 @@ class InstanceCache {
1299
1724
  this.store.identifierCache.forgetRecordIdentifier(identifier);
1300
1725
  removeRecordDataFor(identifier);
1301
1726
  this.store._requestCache._clearEntries(identifier);
1302
- if (macroCondition(getGlobalConfig().WarpDrive.debug.LOG_INSTANCE_CACHE)) {
1303
- // eslint-disable-next-line no-console
1304
- console.log(`InstanceCache: disconnected ${String(identifier)}`);
1727
+ if (macroCondition(getGlobalConfig().WarpDriveMirror.activeLogging.LOG_INSTANCE_CACHE)) {
1728
+ if (getGlobalConfig().WarpDriveMirror.debug.LOG_INSTANCE_CACHE || globalThis.getWarpDriveRuntimeConfig().debug.LOG_INSTANCE_CACHE) {
1729
+ log('reactive-ui', '', identifier.type, identifier.lid, 'disconnected', '');
1730
+ }
1305
1731
  }
1306
1732
  }
1307
1733
  unloadRecord(identifier) {
1308
- if (macroCondition(getGlobalConfig().WarpDrive.env.DEBUG)) {
1734
+ if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
1309
1735
  const requests = this.store.getRequestStateService().getPendingRequestsForRecord(identifier);
1310
1736
  if (requests.some(req => {
1311
1737
  return req.type === 'mutation';
1312
1738
  })) {
1313
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
1739
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
1314
1740
  {
1315
1741
  throw new Error(`You can only unload a record which is not inFlight. '${String(identifier)}'`);
1316
1742
  }
1317
1743
  })() : {};
1318
1744
  }
1319
1745
  }
1320
- if (macroCondition(getGlobalConfig().WarpDrive.debug.LOG_INSTANCE_CACHE)) {
1321
- // eslint-disable-next-line no-console
1322
- console.groupCollapsed(`InstanceCache: unloading record for ${String(identifier)}`);
1746
+ if (macroCondition(getGlobalConfig().WarpDriveMirror.activeLogging.LOG_INSTANCE_CACHE)) {
1747
+ if (getGlobalConfig().WarpDriveMirror.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
+ }
1323
1751
  }
1324
1752
 
1325
1753
  // TODO is this join still necessary?
@@ -1332,27 +1760,33 @@ class InstanceCache {
1332
1760
  StoreMap.delete(record);
1333
1761
  RecordCache.delete(record);
1334
1762
  removeRecordDataFor(record);
1335
- if (macroCondition(getGlobalConfig().WarpDrive.debug.LOG_INSTANCE_CACHE)) {
1336
- // eslint-disable-next-line no-console
1337
- console.log(`InstanceCache: destroyed record for ${String(identifier)}`);
1763
+ if (macroCondition(getGlobalConfig().WarpDriveMirror.activeLogging.LOG_INSTANCE_CACHE)) {
1764
+ if (getGlobalConfig().WarpDriveMirror.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
+ }
1338
1768
  }
1339
1769
  }
1340
1770
  if (cache) {
1341
1771
  cache.unloadRecord(identifier);
1342
1772
  removeRecordDataFor(identifier);
1343
- if (macroCondition(getGlobalConfig().WarpDrive.debug.LOG_INSTANCE_CACHE)) {
1344
- // eslint-disable-next-line no-console
1345
- console.log(`InstanceCache: destroyed cache for ${String(identifier)}`);
1773
+ if (macroCondition(getGlobalConfig().WarpDriveMirror.activeLogging.LOG_INSTANCE_CACHE)) {
1774
+ if (getGlobalConfig().WarpDriveMirror.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
+ }
1346
1778
  }
1347
1779
  } else {
1348
1780
  this.disconnect(identifier);
1349
1781
  }
1350
1782
  this.store._requestCache._clearEntries(identifier);
1351
- if (macroCondition(getGlobalConfig().WarpDrive.debug.LOG_INSTANCE_CACHE)) {
1352
- // eslint-disable-next-line no-console
1353
- console.log(`InstanceCache: unloaded RecordData for ${String(identifier)}`);
1354
- // eslint-disable-next-line no-console
1355
- console.groupEnd();
1783
+ if (macroCondition(getGlobalConfig().WarpDriveMirror.activeLogging.LOG_INSTANCE_CACHE)) {
1784
+ if (getGlobalConfig().WarpDriveMirror.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
+ }
1356
1790
  }
1357
1791
  });
1358
1792
  }
@@ -1387,7 +1821,7 @@ class InstanceCache {
1387
1821
  const oldId = identifier.id;
1388
1822
 
1389
1823
  // ID absolutely can't be missing if the oldID is empty (missing Id in response for a new record)
1390
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
1824
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
1391
1825
  if (!test) {
1392
1826
  throw new Error(`'${type}' was saved to the server, but the response does not have an id and your record does not either.`);
1393
1827
  }
@@ -1395,7 +1829,7 @@ class InstanceCache {
1395
1829
 
1396
1830
  // ID absolutely can't be different than oldID if oldID is not null
1397
1831
  // TODO this assertion and restriction may not strictly be needed in the identifiers world
1398
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
1832
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
1399
1833
  if (!test) {
1400
1834
  throw new Error(`Cannot update the id for '${type}:${lid}' from '${String(oldId)}' to '${id}'.`);
1401
1835
  }
@@ -1407,15 +1841,17 @@ class InstanceCache {
1407
1841
  warn(`Your ${type} record was saved to the server, but the response does not have an id.`, !(oldId !== null && id === null));
1408
1842
  return;
1409
1843
  }
1410
- if (macroCondition(getGlobalConfig().WarpDrive.debug.LOG_INSTANCE_CACHE)) {
1411
- // eslint-disable-next-line no-console
1412
- console.log(`InstanceCache: updating id to '${id}' for record ${String(identifier)}`);
1844
+ if (macroCondition(getGlobalConfig().WarpDriveMirror.activeLogging.LOG_INSTANCE_CACHE)) {
1845
+ if (getGlobalConfig().WarpDriveMirror.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
+ }
1413
1849
  }
1414
1850
  const existingIdentifier = this.store.identifierCache.peekRecordIdentifier({
1415
1851
  type,
1416
1852
  id
1417
1853
  });
1418
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
1854
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
1419
1855
  if (!test) {
1420
1856
  throw new Error(`'${type}' was saved to the server, but the response returned the new id '${id}', which has already been used with another record.'`);
1421
1857
  }
@@ -1480,7 +1916,7 @@ function preloadData(store, identifier, preload) {
1480
1916
  function preloadRelationship(schema, preloadValue) {
1481
1917
  const relatedType = schema.type;
1482
1918
  if (schema.kind === 'hasMany') {
1483
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
1919
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
1484
1920
  if (!test) {
1485
1921
  throw new Error('You need to pass in an array to set a hasMany property on a record');
1486
1922
  }
@@ -1489,7 +1925,7 @@ function preloadRelationship(schema, preloadValue) {
1489
1925
  data: preloadValue.map(value => _convertPreloadRelationshipToJSON(value, relatedType))
1490
1926
  };
1491
1927
  }
1492
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
1928
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
1493
1929
  if (!test) {
1494
1930
  throw new Error('You should not pass in an array to set a belongsTo property on a record');
1495
1931
  }
@@ -1726,7 +2162,9 @@ class CacheManager {
1726
2162
  peek(identifier) {
1727
2163
  return this.#cache.peek(identifier);
1728
2164
  }
1729
-
2165
+ peekRemoteState(identifier) {
2166
+ return this.#cache.peekRemoteState(identifier);
2167
+ }
1730
2168
  /**
1731
2169
  * Peek the Cache for the existing request data associated with
1732
2170
  * a cacheable request
@@ -1949,6 +2387,19 @@ class CacheManager {
1949
2387
  return this.#cache.getAttr(identifier, propertyName);
1950
2388
  }
1951
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
+
1952
2403
  /**
1953
2404
  * Mutate the data for an attribute in the cache
1954
2405
  *
@@ -2073,6 +2524,19 @@ class CacheManager {
2073
2524
  return this.#cache.getRelationship(identifier, propertyName);
2074
2525
  }
2075
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
+
2076
2540
  // Resource State
2077
2541
  // ===============
2078
2542
 
@@ -2156,32 +2620,56 @@ class CacheManager {
2156
2620
  /**
2157
2621
  * @module @ember-data-mirror/store
2158
2622
  */
2159
- // eslint-disable-next-line no-restricted-imports
2160
- let tokenId = 0;
2161
- const CacheOperations = new Set(['added', 'removed', 'state', 'updated']);
2623
+
2162
2624
  function isCacheOperationValue(value) {
2163
- return CacheOperations.has(value);
2625
+ return value === 'added' || value === 'state' || value === 'updated' || value === 'removed' || value === 'invalidated';
2164
2626
  }
2165
2627
  function runLoopIsFlushing() {
2166
2628
  //@ts-expect-error
2167
2629
  return !!_backburner.currentInstance && _backburner._autorun !== true;
2168
2630
  }
2169
- function _unsubscribe(tokens, token, cache) {
2170
- const identifier = tokens.get(token);
2171
- if (macroCondition(getGlobalConfig().WarpDrive.debug.LOG_NOTIFICATIONS)) {
2172
- if (!identifier) {
2173
- // eslint-disable-next-line no-console
2174
- 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().WarpDriveMirror.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().WarpDriveMirror.activeLogging.LOG_NOTIFICATIONS)) {
2647
+ if (getGlobalConfig().WarpDriveMirror.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
+ }
2175
2652
  }
2176
2653
  }
2177
2654
  if (identifier) {
2178
- tokens.delete(token);
2179
- const map = cache.get(identifier);
2180
- map?.delete(token);
2181
- }
2182
- }
2183
-
2184
- /**
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().WarpDriveMirror.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);
2669
+ }
2670
+ }
2671
+
2672
+ /**
2185
2673
  * The NotificationManager provides the ability to subscribe to
2186
2674
  * changes to Cache state.
2187
2675
  *
@@ -2198,7 +2686,6 @@ class NotificationManager {
2198
2686
  this._buffered = new Map();
2199
2687
  this._hasFlush = false;
2200
2688
  this._cache = new Map();
2201
- this._tokens = new Map();
2202
2689
  }
2203
2690
 
2204
2691
  /**
@@ -2230,22 +2717,31 @@ class NotificationManager {
2230
2717
  */
2231
2718
 
2232
2719
  subscribe(identifier, callback) {
2233
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
2720
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
2234
2721
  if (!test) {
2235
2722
  throw new Error(`Expected to receive a stable Identifier to subscribe to`);
2236
2723
  }
2237
2724
  })(identifier === 'resource' || identifier === 'document' || isStableIdentifier(identifier) || isDocumentIdentifier(identifier)) : {};
2238
- let map = this._cache.get(identifier);
2239
- if (!map) {
2240
- map = new Map();
2241
- this._cache.set(identifier, map);
2725
+ let callbacks = this._cache.get(identifier);
2726
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
2727
+ if (!test) {
2728
+ throw new Error(`expected to receive a valid callback`);
2729
+ }
2730
+ })(typeof callback === 'function') : {};
2731
+ macroCondition(getGlobalConfig().WarpDriveMirror.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);
2242
2742
  }
2243
- const unsubToken = macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? {
2244
- _tokenRef: tokenId++
2245
- } : {};
2246
- map.set(unsubToken, callback);
2247
- this._tokens.set(unsubToken, identifier);
2248
- return unsubToken;
2743
+ callbacks.push(callback);
2744
+ return callback;
2249
2745
  }
2250
2746
 
2251
2747
  /**
@@ -2257,7 +2753,7 @@ class NotificationManager {
2257
2753
  */
2258
2754
  unsubscribe(token) {
2259
2755
  if (!this.isDestroyed) {
2260
- _unsubscribe(this._tokens, token, this._cache);
2756
+ _unsubscribe(token, this._cache);
2261
2757
  }
2262
2758
  }
2263
2759
 
@@ -2273,23 +2769,21 @@ class NotificationManager {
2273
2769
  */
2274
2770
 
2275
2771
  notify(identifier, value, key) {
2276
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
2772
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
2277
2773
  if (!test) {
2278
2774
  throw new Error(`Notify does not accept a key argument for the namespace '${value}'. Received key '${key || ''}'.`);
2279
2775
  }
2280
2776
  })(!key || value === 'attributes' || value === 'relationships') : {};
2281
2777
  if (!isStableIdentifier(identifier) && !isDocumentIdentifier(identifier)) {
2282
- if (macroCondition(getGlobalConfig().WarpDrive.debug.LOG_NOTIFICATIONS)) {
2283
- // eslint-disable-next-line no-console
2284
- 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().WarpDriveMirror.activeLogging.LOG_NOTIFICATIONS)) {
2779
+ if (getGlobalConfig().WarpDriveMirror.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
+ }
2285
2783
  }
2286
2784
  return false;
2287
2785
  }
2288
- if (macroCondition(getGlobalConfig().WarpDrive.debug.LOG_NOTIFICATIONS)) {
2289
- // eslint-disable-next-line no-console
2290
- console.log(`Buffering Notify: ${String(identifier.lid)}\t${value}\t${key || ''}`);
2291
- }
2292
- const hasSubscribers = Boolean(this._cache.get(identifier)?.size);
2786
+ const hasSubscribers = Boolean(this._cache.get(identifier)?.length);
2293
2787
  if (isCacheOperationValue(value) || hasSubscribers) {
2294
2788
  let buffer = this._buffered.get(identifier);
2295
2789
  if (!buffer) {
@@ -2297,7 +2791,29 @@ class NotificationManager {
2297
2791
  this._buffered.set(identifier, buffer);
2298
2792
  }
2299
2793
  buffer.push([value, key]);
2300
- this._scheduleNotify();
2794
+ if (macroCondition(getGlobalConfig().WarpDriveMirror.activeLogging.LOG_METRIC_COUNTS)) {
2795
+ if (getGlobalConfig().WarpDriveMirror.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().WarpDriveMirror.activeLogging.LOG_NOTIFICATIONS)) {
2801
+ if (getGlobalConfig().WarpDriveMirror.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().WarpDriveMirror.activeLogging.LOG_NOTIFICATIONS)) {
2808
+ if (getGlobalConfig().WarpDriveMirror.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().WarpDriveMirror.activeLogging.LOG_METRIC_COUNTS)) {
2813
+ if (getGlobalConfig().WarpDriveMirror.debug.LOG_METRIC_COUNTS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_METRIC_COUNTS) {
2814
+ count(`DISCARDED notify ${'type' in identifier ? identifier.type : '<document>'} ${value} ${key}`);
2815
+ }
2816
+ }
2301
2817
  }
2302
2818
  return hasSubscribers;
2303
2819
  }
@@ -2308,33 +2824,36 @@ class NotificationManager {
2308
2824
  const asyncFlush = this.store._enableAsyncFlush;
2309
2825
  if (this._hasFlush) {
2310
2826
  if (asyncFlush !== false && !runLoopIsFlushing()) {
2311
- return;
2827
+ return false;
2312
2828
  }
2313
2829
  }
2314
2830
  if (asyncFlush && !runLoopIsFlushing()) {
2315
2831
  this._hasFlush = true;
2316
- return;
2832
+ return false;
2317
2833
  }
2318
2834
  this._flush();
2835
+ return true;
2319
2836
  }
2320
2837
  _flush() {
2321
- if (this._buffered.size) {
2322
- this._buffered.forEach((states, identifier) => {
2838
+ const buffered = this._buffered;
2839
+ if (buffered.size) {
2840
+ this._buffered = new Map();
2841
+ buffered.forEach((states, identifier) => {
2323
2842
  states.forEach(args => {
2324
2843
  // @ts-expect-error
2325
2844
  this._flushNotification(identifier, args[0], args[1]);
2326
2845
  });
2327
2846
  });
2328
- this._buffered = new Map();
2329
2847
  }
2330
2848
  this._hasFlush = false;
2331
2849
  this._onFlushCB?.();
2332
2850
  this._onFlushCB = undefined;
2333
2851
  }
2334
2852
  _flushNotification(identifier, value, key) {
2335
- if (macroCondition(getGlobalConfig().WarpDrive.debug.LOG_NOTIFICATIONS)) {
2336
- // eslint-disable-next-line no-console
2337
- console.log(`Notifying: ${String(identifier)}\t${value}\t${key || ''}`);
2853
+ if (macroCondition(getGlobalConfig().WarpDriveMirror.activeLogging.LOG_NOTIFICATIONS)) {
2854
+ if (getGlobalConfig().WarpDriveMirror.debug.LOG_NOTIFICATIONS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_NOTIFICATIONS) {
2855
+ log('notify', '', `${'type' in identifier ? identifier.type : 'document'}`, identifier.lid, `${value}`, key || '');
2856
+ }
2338
2857
  }
2339
2858
 
2340
2859
  // TODO for documents this will need to switch based on Identifier kind
@@ -2346,11 +2865,11 @@ class NotificationManager {
2346
2865
  });
2347
2866
  }
2348
2867
  }
2349
- const callbackMap = this._cache.get(identifier);
2350
- if (!callbackMap || !callbackMap.size) {
2868
+ const callbacks = this._cache.get(identifier);
2869
+ if (!callbacks || !callbacks.length) {
2351
2870
  return false;
2352
2871
  }
2353
- callbackMap.forEach(cb => {
2872
+ callbacks.forEach(cb => {
2354
2873
  // @ts-expect-error overload doesn't narrow within body
2355
2874
  cb(identifier, value, key);
2356
2875
  });
@@ -2358,7 +2877,6 @@ class NotificationManager {
2358
2877
  }
2359
2878
  destroy() {
2360
2879
  this.isDestroyed = true;
2361
- this._tokens.clear();
2362
2880
  this._cache.clear();
2363
2881
  }
2364
2882
  }
@@ -2372,71 +2890,6 @@ class NotificationManager {
2372
2890
  */
2373
2891
 
2374
2892
  const NativeProxy = Proxy;
2375
- var __defProp = Object.defineProperty;
2376
- var __export = (target, all) => {
2377
- for (var name in all) __defProp(target, name, {
2378
- get: all[name],
2379
- enumerable: true
2380
- });
2381
- };
2382
-
2383
- // src/runtime.ts
2384
- var runtime_exports = {};
2385
- __export(runtime_exports, {
2386
- c: () => decorateClass,
2387
- f: () => decorateFieldV1,
2388
- g: () => decorateFieldV2,
2389
- i: () => initializeDeferredDecorator,
2390
- m: () => decorateMethodV1,
2391
- n: () => decorateMethodV2,
2392
- p: () => decoratePOJO
2393
- });
2394
- var deferred = /* @__PURE__ */new WeakMap();
2395
- function deferDecorator(proto, prop, desc) {
2396
- let map = deferred.get(proto);
2397
- if (!map) {
2398
- map = /* @__PURE__ */new Map();
2399
- deferred.set(proto, map);
2400
- }
2401
- map.set(prop, desc);
2402
- }
2403
- function findDeferredDecorator(target, prop) {
2404
- let cursor = target.prototype;
2405
- while (cursor) {
2406
- let desc = deferred.get(cursor)?.get(prop);
2407
- if (desc) {
2408
- return desc;
2409
- }
2410
- cursor = cursor.prototype;
2411
- }
2412
- }
2413
- function decorateFieldV1(target, prop, decorators, initializer) {
2414
- return decorateFieldV2(target.prototype, prop, decorators, initializer);
2415
- }
2416
- function decorateFieldV2(prototype, prop, decorators, initializer) {
2417
- let desc = {
2418
- configurable: true,
2419
- enumerable: true,
2420
- writable: true,
2421
- initializer: null
2422
- };
2423
- if (initializer) {
2424
- desc.initializer = initializer;
2425
- }
2426
- for (let decorator of decorators) {
2427
- desc = decorator(prototype, prop, desc) || desc;
2428
- }
2429
- if (desc.initializer === void 0) {
2430
- Object.defineProperty(prototype, prop, desc);
2431
- } else {
2432
- deferDecorator(prototype, prop, desc);
2433
- }
2434
- }
2435
- function decorateMethodV1({
2436
- prototype
2437
- }, prop, decorators) {
2438
- return decorateMethodV2(prototype, prop, decorators);
2439
- }
2440
2893
  function decorateMethodV2(prototype, prop, decorators) {
2441
2894
  const origDesc = Object.getOwnPropertyDescriptor(prototype, prop);
2442
2895
  let desc = {
@@ -2451,46 +2904,6 @@ function decorateMethodV2(prototype, prop, decorators) {
2451
2904
  }
2452
2905
  Object.defineProperty(prototype, prop, desc);
2453
2906
  }
2454
- function initializeDeferredDecorator(target, prop) {
2455
- let desc = findDeferredDecorator(target.constructor, prop);
2456
- if (desc) {
2457
- Object.defineProperty(target, prop, {
2458
- enumerable: desc.enumerable,
2459
- configurable: desc.configurable,
2460
- writable: desc.writable,
2461
- value: desc.initializer ? desc.initializer.call(target) : void 0
2462
- });
2463
- }
2464
- }
2465
- function decorateClass(target, decorators) {
2466
- return decorators.reduce((accum, decorator) => decorator(accum) || accum, target);
2467
- }
2468
- function decoratePOJO(pojo, decorated) {
2469
- for (let [type, prop, decorators] of decorated) {
2470
- if (type === "field") {
2471
- decoratePojoField(pojo, prop, decorators);
2472
- } else {
2473
- decorateMethodV2(pojo, prop, decorators);
2474
- }
2475
- }
2476
- return pojo;
2477
- }
2478
- function decoratePojoField(pojo, prop, decorators) {
2479
- let desc = {
2480
- configurable: true,
2481
- enumerable: true,
2482
- writable: true,
2483
- initializer: () => Object.getOwnPropertyDescriptor(pojo, prop)?.value
2484
- };
2485
- for (let decorator of decorators) {
2486
- desc = decorator(pojo, prop, desc) || desc;
2487
- }
2488
- if (desc.initializer) {
2489
- desc.value = desc.initializer.call(pojo);
2490
- delete desc.initializer;
2491
- }
2492
- Object.defineProperty(pojo, prop, desc);
2493
- }
2494
2907
 
2495
2908
  /**
2496
2909
  @module @ember-data-mirror/store
@@ -2527,7 +2940,7 @@ function safeForEach(instance, arr, store, callback, target) {
2527
2940
  }
2528
2941
  // clone to prevent mutation
2529
2942
  arr = arr.slice();
2530
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
2943
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
2531
2944
  if (!test) {
2532
2945
  throw new Error('`forEach` expects a function as first argument.');
2533
2946
  }
@@ -2577,6 +2990,7 @@ class IdentifierArray {
2577
2990
  isDestroying = false;
2578
2991
  isDestroyed = false;
2579
2992
  _updatingPromise = null;
2993
+ identifier;
2580
2994
  [IS_COLLECTION] = true;
2581
2995
  [SOURCE];
2582
2996
  [NOTIFY]() {
@@ -2615,6 +3029,7 @@ class IdentifierArray {
2615
3029
  this.modelName = options.type;
2616
3030
  this.store = options.store;
2617
3031
  this._manager = options.manager;
3032
+ this.identifier = options.identifier || null;
2618
3033
  this[SOURCE] = options.identifiers;
2619
3034
  this[ARRAY_SIGNAL] = createSignal(this, 'length');
2620
3035
  const store = options.store;
@@ -2681,7 +3096,7 @@ class IdentifierArray {
2681
3096
  // array functions must run through Reflect to work properly
2682
3097
  // binding via other means will not work.
2683
3098
  if (!options.allowMutation) {
2684
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
3099
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
2685
3100
  if (!test) {
2686
3101
  throw new Error(`Mutating this array of records via ${String(prop)} is not allowed.`);
2687
3102
  }
@@ -2689,7 +3104,7 @@ class IdentifierArray {
2689
3104
  return;
2690
3105
  }
2691
3106
  const args = Array.prototype.slice.call(arguments);
2692
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
3107
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
2693
3108
  if (!test) {
2694
3109
  throw new Error(`Cannot start a new array transaction while a previous transaction is underway`);
2695
3110
  }
@@ -2735,7 +3150,7 @@ class IdentifierArray {
2735
3150
  } else if (transaction) {
2736
3151
  return Reflect.set(target, prop, value);
2737
3152
  } else {
2738
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
3153
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
2739
3154
  {
2740
3155
  throw new Error(`unexpected length set`);
2741
3156
  }
@@ -2762,7 +3177,7 @@ class IdentifierArray {
2762
3177
  if (index === null || index > target.length) {
2763
3178
  if (index !== null && transaction) {
2764
3179
  const identifier = recordIdentifierFor(value);
2765
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
3180
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
2766
3181
  if (!test) {
2767
3182
  throw new Error(`Cannot set index ${index} past the end of the array.`);
2768
3183
  }
@@ -2777,7 +3192,7 @@ class IdentifierArray {
2777
3192
  return false;
2778
3193
  }
2779
3194
  if (!options.allowMutation) {
2780
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
3195
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
2781
3196
  if (!test) {
2782
3197
  throw new Error(`Mutating ${String(prop)} on this Array is not allowed.`);
2783
3198
  }
@@ -2785,9 +3200,8 @@ class IdentifierArray {
2785
3200
  return false;
2786
3201
  }
2787
3202
  const original = target[index];
2788
- const newIdentifier = extractIdentifierFromRecord$1(value);
2789
- target[index] = newIdentifier;
2790
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
3203
+ const newIdentifier = extractIdentifierFromRecord$2(value);
3204
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
2791
3205
  if (!test) {
2792
3206
  throw new Error(`Expected a record`);
2793
3207
  }
@@ -2816,7 +3230,7 @@ class IdentifierArray {
2816
3230
  return true;
2817
3231
  },
2818
3232
  deleteProperty(target, prop) {
2819
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
3233
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
2820
3234
  if (!test) {
2821
3235
  throw new Error(`Deleting keys on managed arrays is disallowed`);
2822
3236
  }
@@ -2827,9 +3241,18 @@ class IdentifierArray {
2827
3241
  return Reflect.deleteProperty(target, prop);
2828
3242
  },
2829
3243
  getPrototypeOf() {
2830
- return IdentifierArray.prototype;
3244
+ return Array.prototype;
2831
3245
  }
2832
3246
  });
3247
+ if (macroCondition(getGlobalConfig().WarpDriveMirror.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
+ }
2833
3256
  createArrayTags(proxy, _SIGNAL);
2834
3257
  this[NOTIFY] = this[NOTIFY].bind(proxy);
2835
3258
  return proxy;
@@ -2872,7 +3295,7 @@ class IdentifierArray {
2872
3295
  is finished.
2873
3296
  */
2874
3297
  _update() {
2875
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
3298
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
2876
3299
  if (!test) {
2877
3300
  throw new Error(`_update cannot be used with this array`);
2878
3301
  }
@@ -2915,7 +3338,11 @@ const desc = {
2915
3338
  enumerable: true,
2916
3339
  configurable: false,
2917
3340
  get: function () {
2918
- return this;
3341
+ // here to support computed chains
3342
+ // and {{#each}}
3343
+ if (macroCondition(getGlobalConfig().WarpDriveMirror.deprecations.DEPRECATE_COMPUTED_CHAINS)) {
3344
+ return this;
3345
+ }
2919
3346
  }
2920
3347
  };
2921
3348
  compat(desc);
@@ -2935,12 +3362,12 @@ class Collection extends IdentifierArray {
2935
3362
  } = this;
2936
3363
 
2937
3364
  // TODO save options from initial request?
2938
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
3365
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
2939
3366
  if (!test) {
2940
3367
  throw new Error(`update cannot be used with this array`);
2941
3368
  }
2942
3369
  })(this.modelName) : {};
2943
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
3370
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
2944
3371
  if (!test) {
2945
3372
  throw new Error(`update cannot be used with no query`);
2946
3373
  }
@@ -2966,8 +3393,8 @@ Collection.prototype.query = null;
2966
3393
  // Ensure instanceof works correctly
2967
3394
  // Object.setPrototypeOf(IdentifierArray.prototype, Array.prototype);
2968
3395
 
2969
- function assertRecordPassedToHasMany(record) {
2970
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
3396
+ function assertRecordPassedToHasMany$1(record) {
3397
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
2971
3398
  if (!test) {
2972
3399
  throw new Error(`All elements of a hasMany relationship must be instances of Model, you passed $${typeof record}`);
2973
3400
  }
@@ -2980,11 +3407,11 @@ function assertRecordPassedToHasMany(record) {
2980
3407
  }
2981
3408
  }()) : {};
2982
3409
  }
2983
- function extractIdentifierFromRecord$1(record) {
3410
+ function extractIdentifierFromRecord$2(record) {
2984
3411
  if (!record) {
2985
3412
  return null;
2986
3413
  }
2987
- assertRecordPassedToHasMany(record);
3414
+ assertRecordPassedToHasMany$1(record);
2988
3415
  return recordIdentifierFor(record);
2989
3416
  }
2990
3417
 
@@ -3059,6 +3486,12 @@ class RecordArrayManager {
3059
3486
  this._identifiers = new Map();
3060
3487
  this._set = new Map();
3061
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
+ });
3062
3495
  this._subscription = this.store.notifications.subscribe('resource', (identifier, type) => {
3063
3496
  if (type === 'added') {
3064
3497
  this._visibilitySet.set(identifier, true);
@@ -3073,11 +3506,38 @@ class RecordArrayManager {
3073
3506
  }
3074
3507
  _syncArray(array) {
3075
3508
  const pending = this._pending.get(array);
3076
- if (!pending || this.isDestroying || this.isDestroyed) {
3509
+ const isRequestArray = isCollection(array);
3510
+ if (!isRequestArray && !pending || this.isDestroying || this.isDestroyed) {
3077
3511
  return;
3078
3512
  }
3079
- sync(array, pending, this._set.get(array));
3080
- 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().WarpDriveMirror.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);
3081
3541
  }
3082
3542
 
3083
3543
  /**
@@ -3113,9 +3573,13 @@ class RecordArrayManager {
3113
3573
  }
3114
3574
  return array;
3115
3575
  }
3116
- createArray(config) {
3576
+ getCollection(config) {
3577
+ if (config.identifier && this._keyedArrays.has(config.identifier.lid)) {
3578
+ return this._keyedArrays.get(config.identifier.lid);
3579
+ }
3117
3580
  const options = {
3118
3581
  type: config.type,
3582
+ identifier: config.identifier || null,
3119
3583
  links: config.doc?.links || null,
3120
3584
  meta: config.doc?.meta || null,
3121
3585
  query: config.query || null,
@@ -3128,16 +3592,22 @@ class RecordArrayManager {
3128
3592
  const array = new Collection(options);
3129
3593
  this._managed.add(array);
3130
3594
  this._set.set(array, new Set(options.identifiers || []));
3595
+ if (config.identifier) {
3596
+ this._keyedArrays.set(config.identifier.lid, array);
3597
+ }
3131
3598
  if (config.identifiers) {
3132
3599
  associate(this._identifiers, array, config.identifiers);
3133
3600
  }
3134
3601
  return array;
3135
3602
  }
3136
- dirtyArray(array, delta) {
3603
+ dirtyArray(array, delta, shouldSyncFromCache) {
3137
3604
  if (array === FAKE_ARR) {
3138
3605
  return;
3139
3606
  }
3140
3607
  const tag = array[ARRAY_SIGNAL];
3608
+ if (shouldSyncFromCache) {
3609
+ tag.reason = 'cache-sync';
3610
+ }
3141
3611
  if (!tag.shouldReset) {
3142
3612
  tag.shouldReset = true;
3143
3613
  addTransactionCB(array[NOTIFY]);
@@ -3196,13 +3666,18 @@ class RecordArrayManager {
3196
3666
  populateManagedArray(array, identifiers, payload) {
3197
3667
  this._pending.delete(array);
3198
3668
  const source = array[SOURCE];
3669
+ macroCondition(getGlobalConfig().WarpDriveMirror.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) : {};
3199
3674
  const old = source.slice();
3200
3675
  source.length = 0;
3201
3676
  fastPush(source, identifiers);
3202
3677
  this._set.set(array, new Set(identifiers));
3203
3678
  notifyArray(array);
3204
- array.meta = payload.meta || null;
3205
- array.links = payload.links || null;
3679
+ array.meta = payload?.meta || null;
3680
+ array.links = payload?.links || null;
3206
3681
  array.isLoaded = true;
3207
3682
  disassociate(this._identifiers, array, old);
3208
3683
  associate(this._identifiers, array, identifiers);
@@ -3216,7 +3691,7 @@ class RecordArrayManager {
3216
3691
  changes.delete(identifier);
3217
3692
  } else {
3218
3693
  changes.set(identifier, 'add');
3219
- this.dirtyArray(array, changes.size);
3694
+ this.dirtyArray(array, changes.size, false);
3220
3695
  }
3221
3696
  });
3222
3697
  }
@@ -3230,7 +3705,7 @@ class RecordArrayManager {
3230
3705
  changes.delete(identifier);
3231
3706
  } else {
3232
3707
  changes.set(identifier, 'del');
3233
- this.dirtyArray(array, changes.size);
3708
+ this.dirtyArray(array, changes.size, false);
3234
3709
  }
3235
3710
  });
3236
3711
  }
@@ -3335,6 +3810,9 @@ function sync(array, changes, arraySet) {
3335
3810
  */
3336
3811
  }
3337
3812
  }
3813
+ function isCollection(array) {
3814
+ return array.identifier !== null;
3815
+ }
3338
3816
 
3339
3817
  /**
3340
3818
  * @module @ember-data-mirror/store
@@ -3342,7 +3820,7 @@ function sync(array, changes, arraySet) {
3342
3820
 
3343
3821
  const Touching = getOrSetGlobal('Touching', Symbol('touching'));
3344
3822
  const RequestPromise = getOrSetGlobal('RequestPromise', Symbol('promise'));
3345
- const EMPTY_ARR = macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? Object.freeze([]) : [];
3823
+ const EMPTY_ARR = macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? Object.freeze([]) : [];
3346
3824
  function hasRecordIdentifier(op) {
3347
3825
  return 'recordIdentifier' in op;
3348
3826
  }
@@ -3413,7 +3891,7 @@ class RequestStateService {
3413
3891
  throw error;
3414
3892
  });
3415
3893
  }
3416
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
3894
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
3417
3895
  {
3418
3896
  throw new Error(`Expected a well formed query`);
3419
3897
  }
@@ -3550,12 +4028,12 @@ function constructResource(type, id, lid) {
3550
4028
  if ('id' in resource) {
3551
4029
  resource.id = coerceId(resource.id);
3552
4030
  }
3553
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
4031
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
3554
4032
  if (!test) {
3555
4033
  throw new Error('Expected either id or lid to be a valid string');
3556
4034
  }
3557
4035
  })('id' in resource && isNonEmptyString(resource.id) || isNonEmptyString(resource.lid)) : {};
3558
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
4036
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
3559
4037
  if (!test) {
3560
4038
  throw new Error('if id is present, the type must be a string');
3561
4039
  }
@@ -3571,7 +4049,7 @@ function constructResource(type, id, lid) {
3571
4049
  }
3572
4050
  throw new Error('Expected either id or lid to be a valid string');
3573
4051
  }
3574
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
4052
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
3575
4053
  if (!test) {
3576
4054
  throw new Error('type must be a string');
3577
4055
  }
@@ -3595,6 +4073,118 @@ function constructResource(type, id, lid) {
3595
4073
  */
3596
4074
  // this import location is deprecated but breaks in 4.8 and older
3597
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().WarpDriveMirror.activeLogging.LOG_METRIC_COUNTS)) {
4082
+ if (getGlobalConfig().WarpDriveMirror.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().WarpDriveMirror.activeLogging.__INTERNAL_LOG_NATIVE_MAP_SET_COUNTS)) {
4100
+ if (getGlobalConfig().WarpDriveMirror.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
+
3598
4188
  // `AwaitedKeys` is needed here to resolve any promise types like `PromiseBelongsTo`.
3599
4189
 
3600
4190
  /**
@@ -3639,7 +4229,8 @@ const EmptyClass = class {
3639
4229
  // eslint-disable-next-line @typescript-eslint/no-useless-constructor
3640
4230
  constructor(args) {}
3641
4231
  };
3642
- const BaseClass = macroCondition(dependencySatisfies('ember-source', '*')) ? macroCondition(getGlobalConfig().WarpDrive.deprecations.DEPRECATE_STORE_EXTENDS_EMBER_OBJECT) ? importSync('@ember/object') : EmptyClass : EmptyClass;
4232
+ const _BaseClass = macroCondition(dependencySatisfies('ember-source', '*')) ? macroCondition(getGlobalConfig().WarpDriveMirror.deprecations.DEPRECATE_STORE_EXTENDS_EMBER_OBJECT) ? importSync('@ember/object') : EmptyClass : EmptyClass;
4233
+ const BaseClass = _BaseClass.default ? _BaseClass.default : _BaseClass;
3643
4234
  if (BaseClass !== EmptyClass) {
3644
4235
  deprecate(`The Store class extending from EmberObject is deprecated.
3645
4236
  Please remove usage of EmberObject APIs and mark your class as not requiring it.
@@ -3660,8 +4251,9 @@ const app = new EmberApp(defaults, {
3660
4251
  id: 'ember-data-mirror:deprecate-store-extends-ember-object',
3661
4252
  until: '6.0',
3662
4253
  for: 'ember-data-mirror',
4254
+ url: 'https://deprecations.emberjs.com/id/ember-data-deprecate-store-extends-ember-object',
3663
4255
  since: {
3664
- available: '5.4',
4256
+ available: '4.13',
3665
4257
  enabled: '5.4'
3666
4258
  }
3667
4259
  });
@@ -3721,12 +4313,9 @@ class Store extends BaseClass {
3721
4313
  * import Fetch from '@ember-data-mirror/request/fetch';
3722
4314
  *
3723
4315
  * class extends Store {
3724
- * constructor() {
3725
- * super(...arguments);
3726
- * this.requestManager = new RequestManager();
3727
- * this.requestManager.use([Fetch]);
3728
- * this.requestManager.useCache(CacheHandler);
3729
- * }
4316
+ * requestManager = new RequestManager()
4317
+ * .use([Fetch])
4318
+ * .useCache(CacheHandler);
3730
4319
  * }
3731
4320
  * ```
3732
4321
  *
@@ -3766,6 +4355,17 @@ class Store extends BaseClass {
3766
4355
 
3767
4356
  // Private
3768
4357
 
4358
+ /**
4359
+ * Async flush buffers notifications until flushed
4360
+ * by finalization of a future configured by store.request
4361
+ *
4362
+ * This is useful for ensuring that notifications are delivered
4363
+ * prior to the promise resolving but without risk of promise
4364
+ * interleaving.
4365
+ *
4366
+ * @internal
4367
+ */
4368
+
3769
4369
  // DEBUG-only properties
3770
4370
 
3771
4371
  get isDestroying() {
@@ -3799,18 +4399,17 @@ class Store extends BaseClass {
3799
4399
  // private
3800
4400
  this._requestCache = new RequestStateService(this);
3801
4401
  this._instanceCache = new InstanceCache(this);
3802
- this._documentCache = new Map();
3803
4402
  this.isDestroying = false;
3804
4403
  this.isDestroyed = false;
3805
4404
  }
3806
4405
  _run(cb) {
3807
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
4406
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
3808
4407
  if (!test) {
3809
4408
  throw new Error(`EmberData should never encounter a nested run`);
3810
4409
  }
3811
4410
  })(!this._cbs) : {};
3812
4411
  const _cbs = this._cbs = {};
3813
- if (macroCondition(getGlobalConfig().WarpDrive.env.DEBUG)) {
4412
+ if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
3814
4413
  try {
3815
4414
  cb();
3816
4415
  if (_cbs.coalesce) {
@@ -3839,6 +4438,16 @@ class Store extends BaseClass {
3839
4438
  this._cbs = null;
3840
4439
  }
3841
4440
  }
4441
+
4442
+ /**
4443
+ * Executes the callback, ensurng that any work that calls
4444
+ * store._schedule is executed after in the right order.
4445
+ *
4446
+ * When queues already exist, scheduled callbacks will
4447
+ * join the existing queue.
4448
+ *
4449
+ * @internal
4450
+ */
3842
4451
  _join(cb) {
3843
4452
  if (this._cbs) {
3844
4453
  cb();
@@ -3847,12 +4456,12 @@ class Store extends BaseClass {
3847
4456
  }
3848
4457
  }
3849
4458
  _schedule(name, cb) {
3850
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
4459
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
3851
4460
  if (!test) {
3852
4461
  throw new Error(`EmberData expects to schedule only when there is an active run`);
3853
4462
  }
3854
4463
  })(!!this._cbs) : {};
3855
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
4464
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
3856
4465
  if (!test) {
3857
4466
  throw new Error(`EmberData expects only one flush per queue name, cannot schedule ${name}`);
3858
4467
  }
@@ -3875,7 +4484,7 @@ class Store extends BaseClass {
3875
4484
  return this._requestCache;
3876
4485
  }
3877
4486
  _getAllPending() {
3878
- if (macroCondition(getGlobalConfig().WarpDrive.env.TESTING)) {
4487
+ if (macroCondition(getGlobalConfig().WarpDriveMirror.env.TESTING)) {
3879
4488
  const all = [];
3880
4489
  const pending = this._requestCache._pending;
3881
4490
  pending.forEach(requests => {
@@ -3947,33 +4556,37 @@ class Store extends BaseClass {
3947
4556
  // the user has had the chance to set the prop.
3948
4557
  const opts = {
3949
4558
  store: this,
3950
- [EnableHydration]: true
4559
+ [EnableHydration]: requestConfig[EnableHydration] ?? true
3951
4560
  };
3952
4561
  if (requestConfig.records) {
3953
4562
  const identifierCache = this.identifierCache;
3954
4563
  opts.records = requestConfig.records.map(r => identifierCache.getOrCreateRecordIdentifier(r));
3955
4564
  }
3956
- if (macroCondition(getGlobalConfig().WarpDrive.env.TESTING)) {
4565
+ if (macroCondition(getGlobalConfig().WarpDriveMirror.env.TESTING)) {
3957
4566
  if (this.DISABLE_WAITER) {
3958
4567
  opts.disableTestWaiter = typeof requestConfig.disableTestWaiter === 'boolean' ? requestConfig.disableTestWaiter : true;
3959
4568
  }
3960
4569
  }
3961
- if (macroCondition(getGlobalConfig().WarpDrive.debug.LOG_REQUESTS)) {
3962
- let options;
3963
- try {
3964
- options = JSON.parse(JSON.stringify(requestConfig));
3965
- } catch {
3966
- options = requestConfig;
4570
+ if (macroCondition(getGlobalConfig().WarpDriveMirror.activeLogging.LOG_REQUESTS)) {
4571
+ if (getGlobalConfig().WarpDriveMirror.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);
3967
4580
  }
3968
- // eslint-disable-next-line no-console
3969
- console.log(`request: [[START]] ${requestConfig.op && !requestConfig.url ? '(LEGACY) ' : ''}${requestConfig.op || '<unknown operation>'} ${requestConfig.url || '<empty url>'} ${requestConfig.method || '<empty method>'}`, options);
3970
4581
  }
3971
4582
  const request = Object.assign({}, requestConfig, opts);
3972
4583
  const future = this.requestManager.request(request);
3973
4584
  future.onFinalize(() => {
3974
- if (macroCondition(getGlobalConfig().WarpDrive.debug.LOG_REQUESTS)) {
3975
- // eslint-disable-next-line no-console
3976
- 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().WarpDriveMirror.activeLogging.LOG_REQUESTS)) {
4586
+ if (getGlobalConfig().WarpDriveMirror.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
+ }
3977
4590
  }
3978
4591
  // skip flush for legacy belongsTo
3979
4592
  if (requestConfig.op === 'findBelongsTo' && !requestConfig.url) {
@@ -4036,15 +4649,15 @@ class Store extends BaseClass {
4036
4649
  modelFor(type) {
4037
4650
  // FIXME add deprecation and deprecation stripping
4038
4651
  // FIXME/TODO update RFC to remove this method
4039
- if (macroCondition(getGlobalConfig().WarpDrive.env.DEBUG)) {
4652
+ if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
4040
4653
  assertDestroyedStoreOnly(this, 'modelFor');
4041
4654
  }
4042
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
4655
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
4043
4656
  if (!test) {
4044
4657
  throw new Error(`You need to pass <type> to the store's modelFor method`);
4045
4658
  }
4046
4659
  })(typeof type === 'string' && type.length) : {};
4047
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
4660
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
4048
4661
  if (!test) {
4049
4662
  throw new Error(`No model was found for '${type}' and no schema handles the type`);
4050
4663
  }
@@ -4080,15 +4693,15 @@ class Store extends BaseClass {
4080
4693
  */
4081
4694
 
4082
4695
  createRecord(type, inputProperties) {
4083
- if (macroCondition(getGlobalConfig().WarpDrive.env.DEBUG)) {
4696
+ if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
4084
4697
  assertDestroyingStore(this, 'createRecord');
4085
4698
  }
4086
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
4699
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
4087
4700
  if (!test) {
4088
4701
  throw new Error(`You need to pass a model name to the store's createRecord method`);
4089
4702
  }
4090
4703
  })(type) : {};
4091
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
4704
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
4092
4705
  if (!test) {
4093
4706
  throw new Error(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${type}`);
4094
4707
  }
@@ -4127,7 +4740,7 @@ class Store extends BaseClass {
4127
4740
  };
4128
4741
  if (resource.id) {
4129
4742
  const identifier = this.identifierCache.peekRecordIdentifier(resource);
4130
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
4743
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
4131
4744
  if (!test) {
4132
4745
  throw new Error(`The id ${String(properties.id)} has already been used with another '${normalizedModelName}' record.`);
4133
4746
  }
@@ -4156,12 +4769,12 @@ class Store extends BaseClass {
4156
4769
  @param {unknown} record
4157
4770
  */
4158
4771
  deleteRecord(record) {
4159
- if (macroCondition(getGlobalConfig().WarpDrive.env.DEBUG)) {
4772
+ if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
4160
4773
  assertDestroyingStore(this, 'deleteRecord');
4161
4774
  }
4162
4775
  const identifier = peekRecordIdentifier(record);
4163
4776
  const cache = this.cache;
4164
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
4777
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
4165
4778
  if (!test) {
4166
4779
  throw new Error(`expected the record to be connected to a cache`);
4167
4780
  }
@@ -4179,16 +4792,15 @@ class Store extends BaseClass {
4179
4792
  This will cause the record to be destroyed and freed up for garbage collection.
4180
4793
  Example
4181
4794
  ```javascript
4182
- store.findRecord('post', '1').then(function(post) {
4183
- store.unloadRecord(post);
4184
- });
4795
+ const { content: { data: post } } = await store.request(findRecord({ type: 'post', id: '1' }));
4796
+ store.unloadRecord(post);
4185
4797
  ```
4186
4798
  @method unloadRecord
4187
4799
  @public
4188
4800
  @param {Model} record
4189
4801
  */
4190
4802
  unloadRecord(record) {
4191
- if (macroCondition(getGlobalConfig().WarpDrive.env.DEBUG)) {
4803
+ if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
4192
4804
  assertDestroyingStore(this, 'unloadRecord');
4193
4805
  }
4194
4806
  const identifier = peekRecordIdentifier(record);
@@ -4402,7 +5014,7 @@ class Store extends BaseClass {
4402
5014
  ```app/routes/post.js
4403
5015
  export default class PostRoute extends Route {
4404
5016
  model(params) {
4405
- return this.store.findRecord('post', params.post_id, { include: 'comments' });
5017
+ return this.store.findRecord('post', params.post_id, { include: ['comments'] });
4406
5018
  }
4407
5019
  }
4408
5020
  ```
@@ -4424,13 +5036,13 @@ class Store extends BaseClass {
4424
5036
  In this case, the post's comments would then be available in your template as
4425
5037
  `model.comments`.
4426
5038
  Multiple relationships can be requested using an `include` parameter consisting of a
4427
- comma-separated list (without white-space) while nested relationships can be specified
5039
+ list of relationship names, while nested relationships can be specified
4428
5040
  using a dot-separated sequence of relationship names. So to request both the post's
4429
5041
  comments and the authors of those comments the request would look like this:
4430
5042
  ```app/routes/post.js
4431
5043
  export default class PostRoute extends Route {
4432
5044
  model(params) {
4433
- return this.store.findRecord('post', params.post_id, { include: 'comments,comments.author' });
5045
+ return this.store.findRecord('post', params.post_id, { include: ['comments','comments.author'] });
4434
5046
  }
4435
5047
  }
4436
5048
  ```
@@ -4476,10 +5088,10 @@ class Store extends BaseClass {
4476
5088
  */
4477
5089
 
4478
5090
  findRecord(resource, id, options) {
4479
- if (macroCondition(getGlobalConfig().WarpDrive.env.DEBUG)) {
5091
+ if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
4480
5092
  assertDestroyingStore(this, 'findRecord');
4481
5093
  }
4482
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5094
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
4483
5095
  if (!test) {
4484
5096
  throw new Error(`You need to pass a modelName or resource identifier as the first argument to the store's findRecord method`);
4485
5097
  }
@@ -4487,7 +5099,7 @@ class Store extends BaseClass {
4487
5099
  if (isMaybeIdentifier(resource)) {
4488
5100
  options = id;
4489
5101
  } else {
4490
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5102
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
4491
5103
  if (!test) {
4492
5104
  throw new Error(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${resource}`);
4493
5105
  }
@@ -4555,7 +5167,7 @@ class Store extends BaseClass {
4555
5167
  */
4556
5168
  // TODO @deprecate getReference (and references generally)
4557
5169
  getReference(resource, id) {
4558
- if (macroCondition(getGlobalConfig().WarpDrive.env.DEBUG)) {
5170
+ if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
4559
5171
  assertDestroyingStore(this, 'getReference');
4560
5172
  }
4561
5173
  let resourceIdentifier;
@@ -4566,7 +5178,7 @@ class Store extends BaseClass {
4566
5178
  const normalizedId = ensureStringId(id);
4567
5179
  resourceIdentifier = constructResource(type, normalizedId);
4568
5180
  }
4569
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5181
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
4570
5182
  if (!test) {
4571
5183
  throw new Error('getReference expected to receive either a resource identifier or type and id as arguments');
4572
5184
  }
@@ -4618,15 +5230,15 @@ class Store extends BaseClass {
4618
5230
  // this is basically an "are we not empty" query.
4619
5231
  return isLoaded ? this._instanceCache.getRecord(stableIdentifier) : null;
4620
5232
  }
4621
- if (macroCondition(getGlobalConfig().WarpDrive.env.DEBUG)) {
5233
+ if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
4622
5234
  assertDestroyingStore(this, 'peekRecord');
4623
5235
  }
4624
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5236
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
4625
5237
  if (!test) {
4626
5238
  throw new Error(`You need to pass a model name to the store's peekRecord method`);
4627
5239
  }
4628
5240
  })(identifier) : {};
4629
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5241
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
4630
5242
  if (!test) {
4631
5243
  throw new Error(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${String(identifier)}`);
4632
5244
  }
@@ -4681,20 +5293,20 @@ class Store extends BaseClass {
4681
5293
  */
4682
5294
 
4683
5295
  query(type, query, options = {}) {
4684
- if (macroCondition(getGlobalConfig().WarpDrive.env.DEBUG)) {
5296
+ if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
4685
5297
  assertDestroyingStore(this, 'query');
4686
5298
  }
4687
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5299
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
4688
5300
  if (!test) {
4689
5301
  throw new Error(`You need to pass a model name to the store's query method`);
4690
5302
  }
4691
5303
  })(type) : {};
4692
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5304
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
4693
5305
  if (!test) {
4694
5306
  throw new Error(`You need to pass a query hash to the store's query method`);
4695
5307
  }
4696
5308
  })(query) : {};
4697
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5309
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
4698
5310
  if (!test) {
4699
5311
  throw new Error(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${type}`);
4700
5312
  }
@@ -4796,20 +5408,20 @@ class Store extends BaseClass {
4796
5408
  */
4797
5409
 
4798
5410
  queryRecord(type, query, options) {
4799
- if (macroCondition(getGlobalConfig().WarpDrive.env.DEBUG)) {
5411
+ if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
4800
5412
  assertDestroyingStore(this, 'queryRecord');
4801
5413
  }
4802
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5414
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
4803
5415
  if (!test) {
4804
5416
  throw new Error(`You need to pass a model name to the store's queryRecord method`);
4805
5417
  }
4806
5418
  })(type) : {};
4807
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5419
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
4808
5420
  if (!test) {
4809
5421
  throw new Error(`You need to pass a query hash to the store's queryRecord method`);
4810
5422
  }
4811
5423
  })(query) : {};
4812
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5424
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
4813
5425
  if (!test) {
4814
5426
  throw new Error(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${type}`);
4815
5427
  }
@@ -4951,18 +5563,18 @@ class Store extends BaseClass {
4951
5563
  ```app/routes/posts.js
4952
5564
  export default class PostsRoute extends Route {
4953
5565
  model() {
4954
- return this.store.findAll('post', { include: 'comments' });
5566
+ return this.store.findAll('post', { include: ['comments'] });
4955
5567
  }
4956
5568
  }
4957
5569
  ```
4958
5570
  Multiple relationships can be requested using an `include` parameter consisting of a
4959
- comma-separated list (without white-space) while nested relationships can be specified
5571
+ list or relationship names, while nested relationships can be specified
4960
5572
  using a dot-separated sequence of relationship names. So to request both the posts'
4961
5573
  comments and the authors of those comments the request would look like this:
4962
5574
  ```app/routes/posts.js
4963
5575
  export default class PostsRoute extends Route {
4964
5576
  model() {
4965
- return this.store.findAll('post', { include: 'comments,comments.author' });
5577
+ return this.store.findAll('post', { include: ['comments','comments.author'] });
4966
5578
  }
4967
5579
  }
4968
5580
  ```
@@ -4976,15 +5588,15 @@ class Store extends BaseClass {
4976
5588
  */
4977
5589
 
4978
5590
  findAll(type, options = {}) {
4979
- if (macroCondition(getGlobalConfig().WarpDrive.env.DEBUG)) {
5591
+ if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
4980
5592
  assertDestroyingStore(this, 'findAll');
4981
5593
  }
4982
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5594
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
4983
5595
  if (!test) {
4984
5596
  throw new Error(`You need to pass a model name to the store's findAll method`);
4985
5597
  }
4986
5598
  })(type) : {};
4987
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5599
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
4988
5600
  if (!test) {
4989
5601
  throw new Error(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${type}`);
4990
5602
  }
@@ -5024,15 +5636,15 @@ class Store extends BaseClass {
5024
5636
  */
5025
5637
 
5026
5638
  peekAll(type) {
5027
- if (macroCondition(getGlobalConfig().WarpDrive.env.DEBUG)) {
5639
+ if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
5028
5640
  assertDestroyingStore(this, 'peekAll');
5029
5641
  }
5030
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5642
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
5031
5643
  if (!test) {
5032
5644
  throw new Error(`You need to pass a model name to the store's peekAll method`);
5033
5645
  }
5034
5646
  })(type) : {};
5035
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5647
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
5036
5648
  if (!test) {
5037
5649
  throw new Error(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${type}`);
5038
5650
  }
@@ -5054,10 +5666,10 @@ class Store extends BaseClass {
5054
5666
  */
5055
5667
 
5056
5668
  unloadAll(type) {
5057
- if (macroCondition(getGlobalConfig().WarpDrive.env.DEBUG)) {
5669
+ if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
5058
5670
  assertDestroyedStoreOnly(this, 'unloadAll');
5059
5671
  }
5060
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5672
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
5061
5673
  if (!test) {
5062
5674
  throw new Error(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${String(type)}`);
5063
5675
  }
@@ -5208,7 +5820,7 @@ class Store extends BaseClass {
5208
5820
  */
5209
5821
 
5210
5822
  push(data) {
5211
- if (macroCondition(getGlobalConfig().WarpDrive.env.DEBUG)) {
5823
+ if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
5212
5824
  assertDestroyingStore(this, 'push');
5213
5825
  }
5214
5826
  const pushed = this._push(data, false);
@@ -5230,19 +5842,9 @@ class Store extends BaseClass {
5230
5842
  @return {StableRecordIdentifier|Array<StableRecordIdentifier>|null} identifiers for the primary records that had data loaded
5231
5843
  */
5232
5844
  _push(jsonApiDoc, asyncFlush) {
5233
- if (macroCondition(getGlobalConfig().WarpDrive.env.DEBUG)) {
5845
+ if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
5234
5846
  assertDestroyingStore(this, '_push');
5235
5847
  }
5236
- if (macroCondition(getGlobalConfig().WarpDrive.debug.LOG_PAYLOADS)) {
5237
- try {
5238
- const data = JSON.parse(JSON.stringify(jsonApiDoc));
5239
- // eslint-disable-next-line no-console
5240
- console.log('EmberData | Payload - push', data);
5241
- } catch (e) {
5242
- // eslint-disable-next-line no-console
5243
- console.log('EmberData | Payload - push', jsonApiDoc);
5244
- }
5245
- }
5246
5848
  if (asyncFlush) {
5247
5849
  this._enableAsyncFlush = true;
5248
5850
  }
@@ -5268,10 +5870,10 @@ class Store extends BaseClass {
5268
5870
  * @return {Promise<record>}
5269
5871
  */
5270
5872
  saveRecord(record, options = {}) {
5271
- if (macroCondition(getGlobalConfig().WarpDrive.env.DEBUG)) {
5873
+ if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
5272
5874
  assertDestroyingStore(this, 'saveRecord');
5273
5875
  }
5274
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5876
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
5275
5877
  if (!test) {
5276
5878
  throw new Error(`Unable to initiate save for a record in a disconnected state`);
5277
5879
  }
@@ -5283,7 +5885,7 @@ class Store extends BaseClass {
5283
5885
  // but just in case we reject here to prevent bad things.
5284
5886
  return Promise.reject(new Error(`Record Is Disconnected`));
5285
5887
  }
5286
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5888
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
5287
5889
  if (!test) {
5288
5890
  throw new Error(`Cannot initiate a save request for an unloaded record: ${identifier.lid}`);
5289
5891
  }
@@ -5340,7 +5942,7 @@ class Store extends BaseClass {
5340
5942
  } = this._instanceCache;
5341
5943
  if (!cache) {
5342
5944
  cache = this._instanceCache.cache = this.createCache(this._instanceCache._storeWrapper);
5343
- if (macroCondition(getGlobalConfig().WarpDrive.env.DEBUG)) {
5945
+ if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
5344
5946
  cache = new CacheManager(cache);
5345
5947
  }
5346
5948
  }
@@ -5364,19 +5966,19 @@ class Store extends BaseClass {
5364
5966
  return new this(args);
5365
5967
  }
5366
5968
  }
5367
- if (macroCondition(getGlobalConfig().WarpDrive.deprecations.ENABLE_LEGACY_SCHEMA_SERVICE)) {
5969
+ if (macroCondition(getGlobalConfig().WarpDriveMirror.deprecations.ENABLE_LEGACY_SCHEMA_SERVICE)) {
5368
5970
  Store.prototype.getSchemaDefinitionService = function () {
5369
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5971
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
5370
5972
  if (!test) {
5371
5973
  throw new Error(`You must registerSchemaDefinitionService with the store to use custom model classes`);
5372
5974
  }
5373
5975
  })(this._schema) : {};
5374
5976
  deprecate(`Use \`store.schema\` instead of \`store.getSchemaDefinitionService()\``, false, {
5375
5977
  id: 'ember-data-mirror:schema-service-updates',
5376
- until: '5.0',
5978
+ until: '6.0',
5377
5979
  for: 'ember-data-mirror',
5378
5980
  since: {
5379
- available: '5.4',
5981
+ available: '4.13',
5380
5982
  enabled: '5.4'
5381
5983
  }
5382
5984
  });
@@ -5385,10 +5987,10 @@ if (macroCondition(getGlobalConfig().WarpDrive.deprecations.ENABLE_LEGACY_SCHEMA
5385
5987
  Store.prototype.registerSchemaDefinitionService = function (schema) {
5386
5988
  deprecate(`Use \`store.createSchemaService\` instead of \`store.registerSchemaDefinitionService()\``, false, {
5387
5989
  id: 'ember-data-mirror:schema-service-updates',
5388
- until: '5.0',
5990
+ until: '6.0',
5389
5991
  for: 'ember-data-mirror',
5390
5992
  since: {
5391
- available: '5.4',
5993
+ available: '4.13',
5392
5994
  enabled: '5.4'
5393
5995
  }
5394
5996
  });
@@ -5397,10 +5999,10 @@ if (macroCondition(getGlobalConfig().WarpDrive.deprecations.ENABLE_LEGACY_SCHEMA
5397
5999
  Store.prototype.registerSchema = function (schema) {
5398
6000
  deprecate(`Use \`store.createSchemaService\` instead of \`store.registerSchema()\``, false, {
5399
6001
  id: 'ember-data-mirror:schema-service-updates',
5400
- until: '5.0',
6002
+ until: '6.0',
5401
6003
  for: 'ember-data-mirror',
5402
6004
  since: {
5403
- available: '5.4',
6005
+ available: '4.13',
5404
6006
  enabled: '5.4'
5405
6007
  }
5406
6008
  });
@@ -5409,10 +6011,10 @@ if (macroCondition(getGlobalConfig().WarpDrive.deprecations.ENABLE_LEGACY_SCHEMA
5409
6011
  }
5410
6012
  let assertDestroyingStore;
5411
6013
  let assertDestroyedStoreOnly;
5412
- if (macroCondition(getGlobalConfig().WarpDrive.env.DEBUG)) {
6014
+ if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
5413
6015
  // eslint-disable-next-line @typescript-eslint/no-shadow
5414
6016
  assertDestroyingStore = function assertDestroyingStore(store, method) {
5415
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
6017
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
5416
6018
  if (!test) {
5417
6019
  throw new Error(`Attempted to call store.${method}(), but the store instance has already been destroyed.`);
5418
6020
  }
@@ -5420,7 +6022,7 @@ if (macroCondition(getGlobalConfig().WarpDrive.env.DEBUG)) {
5420
6022
  };
5421
6023
  // eslint-disable-next-line @typescript-eslint/no-shadow
5422
6024
  assertDestroyedStoreOnly = function assertDestroyedStoreOnly(store, method) {
5423
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
6025
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
5424
6026
  if (!test) {
5425
6027
  throw new Error(`Attempted to call store.${method}(), but the store instance has already been destroyed.`);
5426
6028
  }
@@ -5434,13 +6036,13 @@ function normalizeProperties(store, identifier, properties) {
5434
6036
  // assert here
5435
6037
  if (properties !== undefined) {
5436
6038
  if ('id' in properties) {
5437
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
6039
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
5438
6040
  if (!test) {
5439
6041
  throw new Error(`expected id to be a string or null`);
5440
6042
  }
5441
6043
  })(properties.id !== undefined) : {};
5442
6044
  }
5443
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
6045
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
5444
6046
  if (!test) {
5445
6047
  throw new Error(`You passed '${typeof properties}' as properties for record creation instead of an object.`);
5446
6048
  }
@@ -5460,12 +6062,12 @@ function normalizeProperties(store, identifier, properties) {
5460
6062
  const field = defs.get(prop);
5461
6063
  if (!field) continue;
5462
6064
  if (field.kind === 'hasMany') {
5463
- if (macroCondition(getGlobalConfig().WarpDrive.env.DEBUG)) {
6065
+ if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
5464
6066
  assertRecordsPassedToHasMany(properties[prop]);
5465
6067
  }
5466
- properties[prop] = extractIdentifiersFromRecords(properties[prop]);
6068
+ properties[prop] = extractIdentifiersFromRecords$1(properties[prop]);
5467
6069
  } else if (field.kind === 'belongsTo') {
5468
- properties[prop] = extractIdentifierFromRecord(properties[prop]);
6070
+ properties[prop] = extractIdentifierFromRecord$1(properties[prop]);
5469
6071
  }
5470
6072
  }
5471
6073
  }
@@ -5473,12 +6075,12 @@ function normalizeProperties(store, identifier, properties) {
5473
6075
  return properties;
5474
6076
  }
5475
6077
  function assertRecordsPassedToHasMany(records) {
5476
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
6078
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
5477
6079
  if (!test) {
5478
6080
  throw new Error(`You must pass an array of records to set a hasMany relationship`);
5479
6081
  }
5480
6082
  })(Array.isArray(records)) : {};
5481
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
6083
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
5482
6084
  if (!test) {
5483
6085
  throw new Error(`All elements of a hasMany relationship must be instances of Model, you passed ${records.map(r => `${typeof r}`).join(', ')}`);
5484
6086
  }
@@ -5493,459 +6095,340 @@ function assertRecordsPassedToHasMany(records) {
5493
6095
  });
5494
6096
  }()) : {};
5495
6097
  }
5496
- function extractIdentifiersFromRecords(records) {
5497
- return records.map(record => extractIdentifierFromRecord(record));
6098
+ function extractIdentifiersFromRecords$1(records) {
6099
+ return records.map(record => extractIdentifierFromRecord$1(record));
5498
6100
  }
5499
- function extractIdentifierFromRecord(recordOrPromiseRecord) {
6101
+ function extractIdentifierFromRecord$1(recordOrPromiseRecord) {
5500
6102
  if (!recordOrPromiseRecord) {
5501
6103
  return null;
5502
6104
  }
5503
6105
  const extract = recordIdentifierFor;
5504
6106
  return extract(recordOrPromiseRecord);
5505
6107
  }
6108
+ const MUTATION_OPS = new Set(['createRecord', 'updateRecord', 'deleteRecord']);
6109
+ function calcShouldFetch(store, request, hasCachedValue, identifier) {
6110
+ const {
6111
+ cacheOptions
6112
+ } = request;
6113
+ return request.op && MUTATION_OPS.has(request.op) || cacheOptions?.reload || !hasCachedValue || (store.lifetimes && identifier ? store.lifetimes.isHardExpired(identifier, store) : false);
6114
+ }
6115
+ function calcShouldBackgroundFetch(store, request, willFetch, identifier) {
6116
+ const {
6117
+ cacheOptions
6118
+ } = request;
6119
+ return cacheOptions?.backgroundReload || (store.lifetimes && identifier ? store.lifetimes.isSoftExpired(identifier, store) : false);
6120
+ }
6121
+ function isMutation(request) {
6122
+ return Boolean(request.op && MUTATION_OPS.has(request.op));
6123
+ }
6124
+ function isCacheAffecting(document) {
6125
+ if (!isMutation(document.request)) {
6126
+ return true;
6127
+ }
6128
+ // a mutation combined with a 204 has no cache impact when no known records were involved
6129
+ // a createRecord with a 201 with an empty response and no known records should similarly
6130
+ // have no cache impact
6131
+
6132
+ if (document.request.op === 'createRecord' && document.response?.status === 201) {
6133
+ return document.content ? Object.keys(document.content).length > 0 : false;
6134
+ }
6135
+ return document.response?.status !== 204;
6136
+ }
6137
+ function isAggregateError(error) {
6138
+ return error instanceof AggregateError || error.name === 'AggregateError' && Array.isArray(error.errors);
6139
+ }
6140
+ // TODO @runspired, consider if we should deep freeze errors (potentially only in debug) vs cloning them
6141
+ function cloneError(error) {
6142
+ const isAggregate = isAggregateError(error);
6143
+ const cloned = isAggregate ? new AggregateError(structuredClone(error.errors), error.message) : new Error(error.message);
6144
+ cloned.stack = error.stack;
6145
+ cloned.error = error.error;
6146
+
6147
+ // copy over enumerable properties
6148
+ Object.assign(cloned, error);
6149
+ return cloned;
6150
+ }
6151
+ function getPriority(identifier, deduped, priority) {
6152
+ if (identifier) {
6153
+ const existing = deduped.get(identifier);
6154
+ if (existing) {
6155
+ return existing.priority;
6156
+ }
6157
+ }
6158
+ return priority;
6159
+ }
5506
6160
 
5507
6161
  /**
5508
6162
  * @module @ember-data-mirror/store
5509
6163
  */
5510
- function urlFromLink(link) {
5511
- if (typeof link === 'string') return link;
5512
- return link.href;
5513
- }
5514
6164
 
5515
6165
  /**
5516
- * A Document is a class that wraps the response content from a request to the API
5517
- * returned by `Cache.put` or `Cache.peek`, converting resource-identifiers into
5518
- * record instances.
6166
+ * A CacheHandler that adds support for using an EmberData Cache with a RequestManager.
5519
6167
  *
5520
- * It is not directly instantiated by the user, and its properties should not
5521
- * be directly modified. Whether individual properties are mutable or not is
5522
- * determined by the record instance itself.
6168
+ * This handler will only run when a request has supplied a `store` instance. Requests
6169
+ * issued by the store via `store.request()` will automatically have the `store` instance
6170
+ * attached to the request.
5523
6171
  *
5524
- * @public
5525
- * @class Document
6172
+ * ```ts
6173
+ * requestManager.request({
6174
+ * store: store,
6175
+ * url: '/api/posts',
6176
+ * method: 'GET'
6177
+ * });
6178
+ * ```
6179
+ *
6180
+ * When this handler elects to handle a request, it will return the raw `StructuredDocument`
6181
+ * unless the request has `[EnableHydration]` set to `true`. In this case, the handler will
6182
+ * return a `Document` instance that will automatically update the UI when the cache is updated
6183
+ * in the future and will hydrate any identifiers in the StructuredDocument into Record instances.
6184
+ *
6185
+ * When issuing a request via the store, [EnableHydration] is automatically set to `true`. This
6186
+ * means that if desired you can issue requests that utilize the cache without needing to also
6187
+ * utilize Record instances if desired.
6188
+ *
6189
+ * Said differently, you could elect to issue all requests via a RequestManager, without ever using
6190
+ * the store directly, by setting [EnableHydration] to `true` and providing a store instance. Not
6191
+ * necessarily the most useful thing, but the decoupled nature of the RequestManager and incremental-feature
6192
+ * approach of EmberData allows for this flexibility.
6193
+ *
6194
+ * ```ts
6195
+ * import { EnableHydration } from '@warp-drive-mirror/core-types/request';
6196
+ *
6197
+ * requestManager.request({
6198
+ * store: store,
6199
+ * url: '/api/posts',
6200
+ * method: 'GET',
6201
+ * [EnableHydration]: true
6202
+ * });
6203
+ *
6204
+ * @typedoc
5526
6205
  */
5527
- class Document {
5528
- /**
5529
- * The links object for this document, if any
5530
- *
5531
- * e.g.
5532
- *
5533
- * ```
5534
- * {
5535
- * self: '/articles?page[number]=3',
5536
- * }
5537
- * ```
5538
- *
5539
- * @property links
5540
- * @type {object|undefined} - a links object
5541
- * @public
5542
- */
5543
-
5544
- /**
5545
- * The primary data for this document, if any.
5546
- *
5547
- * If this document has no primary data (e.g. because it is an error document)
5548
- * this property will be `undefined`.
5549
- *
5550
- * For collections this will be an array of record instances,
5551
- * for single resource requests it will be a single record instance or null.
5552
- *
5553
- * @property data
5554
- * @public
5555
- * @type {object|Array<object>|null|undefined} - a data object
5556
- */
5557
-
5558
- /**
5559
- * The errors returned by the API for this request, if any
5560
- *
5561
- * @property errors
5562
- * @public
5563
- * @type {object|undefined} - an errors object
5564
- */
5565
-
5566
- /**
5567
- * The meta object for this document, if any
5568
- *
5569
- * @property meta
5570
- * @public
5571
- * @type {object|undefined} - a meta object
5572
- */
6206
+ const CacheHandler = {
6207
+ request(context, next) {
6208
+ // if we have no cache or no cache-key skip cache handling
6209
+ if (!context.request.store || context.request.cacheOptions?.[SkipCache]) {
6210
+ return next(context.request);
6211
+ }
6212
+ const {
6213
+ store
6214
+ } = context.request;
6215
+ const identifier = store.identifierCache.getOrCreateDocumentIdentifier(context.request);
6216
+ if (identifier) {
6217
+ context.setIdentifier(identifier);
6218
+ }
5573
6219
 
5574
- /**
5575
- * The identifier associated with this document, if any
5576
- *
5577
- * @property identifier
5578
- * @public
5579
- * @type {StableDocumentIdentifier|null}
5580
- */
6220
+ // used to dedupe existing requests that match
6221
+ const DEDUPE = store.requestManager._deduped;
6222
+ const activeRequest = identifier && DEDUPE.get(identifier);
6223
+ const peeked = identifier ? store.cache.peekRequest(identifier) : null;
5581
6224
 
5582
- #store;
5583
- constructor(store, identifier) {
5584
- this.#store = store;
5585
- this.identifier = identifier;
5586
- }
5587
- async #request(link, options) {
5588
- const href = this.links?.[link];
5589
- if (!href) {
5590
- return null;
6225
+ // determine if we should skip cache
6226
+ if (calcShouldFetch(store, context.request, !!peeked, identifier)) {
6227
+ if (activeRequest) {
6228
+ activeRequest.priority = {
6229
+ blocking: true
6230
+ };
6231
+ return activeRequest.promise;
6232
+ }
6233
+ let promise = fetchContentAndHydrate(next, context, identifier, {
6234
+ blocking: true
6235
+ });
6236
+ if (identifier) {
6237
+ promise = promise.finally(() => {
6238
+ DEDUPE.delete(identifier);
6239
+ store.notifications.notify(identifier, 'state');
6240
+ });
6241
+ DEDUPE.set(identifier, {
6242
+ priority: {
6243
+ blocking: true
6244
+ },
6245
+ promise
6246
+ });
6247
+ store.notifications.notify(identifier, 'state');
6248
+ }
6249
+ return promise;
5591
6250
  }
5592
- options.method = options.method || 'GET';
5593
- Object.assign(options, {
5594
- url: urlFromLink(href)
5595
- });
5596
- const response = await this.#store.request(options);
5597
- return response.content;
5598
- }
5599
6251
 
5600
- /**
5601
- * Fetches the related link for this document, returning a promise that resolves
5602
- * with the document when the request completes. If no related link is present,
5603
- * will fallback to the self link if present
5604
- *
5605
- * @method fetch
5606
- * @public
5607
- * @param {object} options
5608
- * @return Promise<Document>
5609
- */
5610
- fetch(options = {}) {
5611
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5612
- if (!test) {
5613
- throw new Error(`No self or related link`);
6252
+ // if we have not skipped cache, determine if we should update behind the scenes
6253
+ if (calcShouldBackgroundFetch(store, context.request, false, identifier)) {
6254
+ let promise = activeRequest?.promise || fetchContentAndHydrate(next, context, identifier, {
6255
+ blocking: false
6256
+ });
6257
+ if (identifier && !activeRequest) {
6258
+ promise = promise.finally(() => {
6259
+ DEDUPE.delete(identifier);
6260
+ store.notifications.notify(identifier, 'state');
6261
+ });
6262
+ DEDUPE.set(identifier, {
6263
+ priority: {
6264
+ blocking: false
6265
+ },
6266
+ promise
6267
+ });
6268
+ store.notifications.notify(identifier, 'state');
5614
6269
  }
5615
- })(this.links?.related || this.links?.self) : {};
5616
- options.cacheOptions = options.cacheOptions || {};
5617
- options.cacheOptions.key = this.identifier?.lid;
5618
- return this.#request(this.links.related ? 'related' : 'self', options);
5619
- }
5620
-
5621
- /**
5622
- * Fetches the next link for this document, returning a promise that resolves
5623
- * with the new document when the request completes, or null if there is no
5624
- * next link.
5625
- *
5626
- * @method next
5627
- * @public
5628
- * @param {object} options
5629
- * @return Promise<Document | null>
5630
- */
5631
- next(options = {}) {
5632
- return this.#request('next', options);
5633
- }
5634
-
5635
- /**
5636
- * Fetches the prev link for this document, returning a promise that resolves
5637
- * with the new document when the request completes, or null if there is no
5638
- * prev link.
5639
- *
5640
- * @method prev
5641
- * @public
5642
- * @param {object} options
5643
- * @return Promise<Document | null>
5644
- */
5645
- prev(options = {}) {
5646
- return this.#request('prev', options);
5647
- }
5648
-
5649
- /**
5650
- * Fetches the first link for this document, returning a promise that resolves
5651
- * with the new document when the request completes, or null if there is no
5652
- * first link.
5653
- *
5654
- * @method first
5655
- * @public
5656
- * @param {object} options
5657
- * @return Promise<Document | null>
5658
- */
5659
- first(options = {}) {
5660
- return this.#request('first', options);
5661
- }
5662
-
5663
- /**
5664
- * Fetches the last link for this document, returning a promise that resolves
5665
- * with the new document when the request completes, or null if there is no
5666
- * last link.
5667
- *
5668
- * @method last
5669
- * @public
5670
- * @param {object} options
5671
- * @return Promise<Document | null>
5672
- */
5673
- last(options = {}) {
5674
- return this.#request('last', options);
5675
- }
5676
-
5677
- /**
5678
- * Implemented for `JSON.stringify` support.
5679
- *
5680
- * Returns the JSON representation of the document wrapper.
5681
- *
5682
- * This is a shallow serialization, it does not deeply serialize
5683
- * the document's contents, leaving that to the individual record
5684
- * instances to determine how to do, if at all.
5685
- *
5686
- * @method toJSON
5687
- * @public
5688
- * @return
5689
- */
5690
- toJSON() {
5691
- const data = {};
5692
- data.identifier = this.identifier;
5693
- if (this.data !== undefined) {
5694
- data.data = this.data;
5695
- }
5696
- if (this.links !== undefined) {
5697
- data.links = this.links;
5698
- }
5699
- if (this.errors !== undefined) {
5700
- data.errors = this.errors;
6270
+ store.requestManager._pending.set(context.id, promise);
5701
6271
  }
5702
- if (this.meta !== undefined) {
5703
- data.meta = this.meta;
6272
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
6273
+ if (!test) {
6274
+ throw new Error(`Expected a peeked request to be present`);
6275
+ }
6276
+ })(peeked) : {};
6277
+ const shouldHydrate = context.request[EnableHydration] || false;
6278
+ context.setResponse(peeked.response);
6279
+ if ('error' in peeked) {
6280
+ const content = shouldHydrate ? maybeUpdateUiObjects(store, context.request, {
6281
+ shouldHydrate,
6282
+ identifier
6283
+ }, peeked.content) : peeked.content;
6284
+ const newError = cloneError(peeked);
6285
+ newError.content = content;
6286
+ throw newError;
5704
6287
  }
5705
- return data;
6288
+ const result = shouldHydrate ? maybeUpdateUiObjects(store, context.request, {
6289
+ shouldHydrate,
6290
+ identifier
6291
+ }, peeked.content) : peeked.content;
6292
+ return result;
5706
6293
  }
5707
- }
5708
- defineSignal(Document.prototype, 'data');
5709
- defineSignal(Document.prototype, 'links');
5710
- defineSignal(Document.prototype, 'errors');
5711
- defineSignal(Document.prototype, 'meta');
5712
-
5713
- /**
5714
- * @module @ember-data-mirror/store
5715
- */
5716
-
5717
- /**
5718
- * A service which an application may provide to the store via
5719
- * the store's `lifetimes` property to configure the behavior
5720
- * of the CacheHandler.
5721
- *
5722
- * The default behavior for request lifetimes is to never expire
5723
- * unless manually refreshed via `cacheOptions.reload` or `cacheOptions.backgroundReload`.
5724
- *
5725
- * Implementing this service allows you to programatically define
5726
- * when a request should be considered expired.
5727
- *
5728
- * @class <Interface> CachePolicy
5729
- * @public
5730
- */
5731
- const MUTATION_OPS = new Set(['createRecord', 'updateRecord', 'deleteRecord']);
5732
- function isErrorDocument(document) {
5733
- return 'errors' in document;
5734
- }
5735
- function maybeUpdateUiObjects(store, request, options, document, isFromCache) {
6294
+ };
6295
+ function maybeUpdateUiObjects(store, request, options, document) {
5736
6296
  const {
5737
6297
  identifier
5738
6298
  } = options;
5739
- if (!document) {
5740
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
6299
+ if (!document || !options.shouldHydrate) {
6300
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
5741
6301
  if (!test) {
5742
6302
  throw new Error(`The CacheHandler expected response content but none was found`);
5743
6303
  }
5744
6304
  })(!options.shouldHydrate) : {};
5745
6305
  return document;
5746
6306
  }
5747
- if (isErrorDocument(document)) {
5748
- if (!identifier && !options.shouldHydrate) {
5749
- return document;
5750
- }
5751
- let doc;
5752
- if (identifier) {
5753
- doc = store._documentCache.get(identifier);
5754
- }
5755
- if (!doc) {
5756
- doc = new Document(store, identifier);
5757
- copyDocumentProperties(doc, document);
5758
- if (identifier) {
5759
- store._documentCache.set(identifier, doc);
5760
- }
5761
- } else if (!isFromCache) {
5762
- doc.data = undefined;
5763
- copyDocumentProperties(doc, document);
5764
- }
5765
- return options.shouldHydrate ? doc : document;
6307
+ if (identifier) {
6308
+ return store._instanceCache.getDocument(identifier);
5766
6309
  }
5767
- if (Array.isArray(document.data)) {
5768
- const {
5769
- recordArrayManager
5770
- } = store;
5771
- if (!identifier) {
5772
- if (!options.shouldHydrate) {
5773
- return document;
5774
- }
5775
- const data = recordArrayManager.createArray({
5776
- type: request.url,
5777
- identifiers: document.data,
5778
- doc: document,
5779
- query: request
5780
- });
5781
- const doc = new Document(store, null);
5782
- doc.data = data;
5783
- doc.meta = document.meta;
5784
- doc.links = document.links;
5785
- return doc;
5786
- }
5787
- let managed = recordArrayManager._keyedArrays.get(identifier.lid);
5788
- if (!managed) {
5789
- managed = recordArrayManager.createArray({
5790
- type: identifier.lid,
5791
- identifiers: document.data,
5792
- doc: document
5793
- });
5794
- recordArrayManager._keyedArrays.set(identifier.lid, managed);
5795
- const doc = new Document(store, identifier);
5796
- doc.data = managed;
5797
- doc.meta = document.meta;
5798
- doc.links = document.links;
5799
- store._documentCache.set(identifier, doc);
5800
- return options.shouldHydrate ? doc : document;
5801
- } else {
5802
- const doc = store._documentCache.get(identifier);
5803
- if (!isFromCache) {
5804
- recordArrayManager.populateManagedArray(managed, document.data, document);
5805
- doc.data = managed;
5806
- doc.meta = document.meta;
5807
- doc.links = document.links;
5808
- }
5809
- 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
+ });
6317
+ }
6318
+ function updateCacheForSuccess(store, request, options, document) {
6319
+ let response = null;
6320
+ if (isMutation(request)) {
6321
+ const record = request.data?.record || request.records?.[0];
6322
+ if (record) {
6323
+ response = store.cache.didCommit(record, document);
6324
+
6325
+ // a mutation combined with a 204 has no cache impact when no known records were involved
6326
+ // a createRecord with a 201 with an empty response and no known records should similarly
6327
+ // have no cache impact
6328
+ } else if (isCacheAffecting(document)) {
6329
+ response = store.cache.put(document);
5810
6330
  }
5811
6331
  } else {
5812
- if (!identifier && !options.shouldHydrate) {
5813
- return document;
5814
- }
5815
- const data = document.data ? store.peekRecord(document.data) : null;
5816
- let doc;
5817
- if (identifier) {
5818
- doc = store._documentCache.get(identifier);
5819
- }
5820
- if (!doc) {
5821
- doc = new Document(store, identifier);
5822
- doc.data = data;
5823
- copyDocumentProperties(doc, document);
5824
- if (identifier) {
5825
- store._documentCache.set(identifier, doc);
5826
- }
5827
- } else if (!isFromCache) {
5828
- doc.data = data;
5829
- copyDocumentProperties(doc, document);
5830
- }
5831
- return options.shouldHydrate ? doc : document;
6332
+ response = store.cache.put(document);
5832
6333
  }
6334
+ return maybeUpdateUiObjects(store, request, options, response);
5833
6335
  }
5834
- function calcShouldFetch(store, request, hasCachedValue, identifier) {
6336
+ function handleFetchSuccess(store, context, options, document) {
5835
6337
  const {
5836
- cacheOptions
5837
- } = request;
5838
- return request.op && MUTATION_OPS.has(request.op) || cacheOptions?.reload || !hasCachedValue || (store.lifetimes && identifier ? store.lifetimes.isHardExpired(identifier, store) : false);
6338
+ request
6339
+ } = context;
6340
+ store.requestManager._pending.delete(context.id);
6341
+ store._enableAsyncFlush = true;
6342
+ let response;
6343
+ store._join(() => {
6344
+ response = updateCacheForSuccess(store, request, options, document);
6345
+ });
6346
+ store._enableAsyncFlush = null;
6347
+ if (store.lifetimes?.didRequest) {
6348
+ store.lifetimes.didRequest(context.request, document.response, options.identifier, store);
6349
+ }
6350
+ const finalPriority = getPriority(options.identifier, store.requestManager._deduped, options.priority);
6351
+ if (finalPriority.blocking) {
6352
+ return response;
6353
+ } else {
6354
+ store.notifications._flush();
6355
+ }
5839
6356
  }
5840
- function calcShouldBackgroundFetch(store, request, willFetch, identifier) {
5841
- const {
5842
- cacheOptions
5843
- } = request;
5844
- return cacheOptions?.backgroundReload || (store.lifetimes && identifier ? store.lifetimes.isSoftExpired(identifier, store) : false);
6357
+ function updateCacheForError(store, context, options, error) {
6358
+ let response;
6359
+ if (isMutation(context.request)) {
6360
+ // TODO similar to didCommit we should spec this to be similar to cache.put for handling full response
6361
+ // currently we let the response remain undefiend.
6362
+ const errors = error && error.content && typeof error.content === 'object' && 'errors' in error.content && Array.isArray(error.content.errors) ? error.content.errors : undefined;
6363
+ const record = context.request.data?.record || context.request.records?.[0];
6364
+ store.cache.commitWasRejected(record, errors);
6365
+ } else {
6366
+ response = store.cache.put(error);
6367
+ return maybeUpdateUiObjects(store, context.request, options, response);
6368
+ }
5845
6369
  }
5846
- function isMutation(request) {
5847
- return Boolean(request.op && MUTATION_OPS.has(request.op));
6370
+ function handleFetchError(store, context, options, error) {
6371
+ store.requestManager._pending.delete(context.id);
6372
+ if (context.request.signal?.aborted) {
6373
+ throw error;
6374
+ }
6375
+ store._enableAsyncFlush = true;
6376
+ let response;
6377
+ store._join(() => {
6378
+ response = updateCacheForError(store, context, options, error);
6379
+ });
6380
+ store._enableAsyncFlush = null;
6381
+ if (options.identifier && store.lifetimes?.didRequest) {
6382
+ store.lifetimes.didRequest(context.request, error.response, options.identifier, store);
6383
+ }
6384
+ if (isMutation(context.request)) {
6385
+ throw error;
6386
+ }
6387
+ const finalPriority = getPriority(options.identifier, store.requestManager._deduped, options.priority);
6388
+ if (finalPriority.blocking) {
6389
+ const newError = cloneError(error);
6390
+ newError.content = response;
6391
+ throw newError;
6392
+ } else {
6393
+ store.notifications._flush();
6394
+ }
5848
6395
  }
5849
- function fetchContentAndHydrate(next, context, identifier, shouldFetch, shouldBackgroundFetch) {
6396
+ function fetchContentAndHydrate(next, context, identifier, priority) {
5850
6397
  const {
5851
6398
  store
5852
6399
  } = context.request;
5853
6400
  const shouldHydrate = context.request[EnableHydration] || false;
6401
+ const options = {
6402
+ shouldHydrate,
6403
+ identifier,
6404
+ priority
6405
+ };
5854
6406
  let isMut = false;
5855
6407
  if (isMutation(context.request)) {
5856
6408
  isMut = true;
5857
6409
  // TODO should we handle multiple records in request.records by iteratively calling willCommit for each
5858
6410
  const record = context.request.data?.record || context.request.records?.[0];
5859
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
6411
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
5860
6412
  if (!test) {
5861
6413
  throw new Error(`Expected to receive a list of records included in the ${context.request.op} request`);
5862
6414
  }
5863
6415
  })(record || !shouldHydrate) : {};
5864
6416
  if (record) {
5865
- store.cache.willCommit(record, context);
5866
- }
5867
- }
5868
- if (store.lifetimes?.willRequest) {
5869
- store.lifetimes.willRequest(context.request, identifier, store);
5870
- }
5871
- const promise = next(context.request).then(document => {
5872
- store.requestManager._pending.delete(context.id);
5873
- store._enableAsyncFlush = true;
5874
- let response;
5875
- store._join(() => {
5876
- if (isMutation(context.request)) {
5877
- const record = context.request.data?.record || context.request.records?.[0];
5878
- if (record) {
5879
- response = store.cache.didCommit(record, document);
5880
-
5881
- // a mutation combined with a 204 has no cache impact when no known records were involved
5882
- // a createRecord with a 201 with an empty response and no known records should similarly
5883
- // have no cache impact
5884
- } else if (isCacheAffecting(document)) {
5885
- response = store.cache.put(document);
5886
- }
5887
- } else {
5888
- response = store.cache.put(document);
5889
- }
5890
- response = maybeUpdateUiObjects(store, context.request, {
5891
- shouldHydrate,
5892
- shouldFetch,
5893
- shouldBackgroundFetch,
5894
- identifier
5895
- }, response, false);
5896
- });
5897
- store._enableAsyncFlush = null;
5898
- if (store.lifetimes?.didRequest) {
5899
- store.lifetimes.didRequest(context.request, document.response, identifier, store);
5900
- }
5901
- if (shouldFetch) {
5902
- return response;
5903
- } else if (shouldBackgroundFetch) {
5904
- store.notifications._flush();
5905
- }
5906
- }, error => {
5907
- store.requestManager._pending.delete(context.id);
5908
- if (context.request.signal?.aborted) {
5909
- throw error;
5910
- }
5911
- store.requestManager._pending.delete(context.id);
5912
- store._enableAsyncFlush = true;
5913
- let response;
5914
- store._join(() => {
5915
- if (isMutation(context.request)) {
5916
- // TODO similar to didCommit we should spec this to be similar to cache.put for handling full response
5917
- // currently we let the response remain undefiend.
5918
- const errors = error && error.content && typeof error.content === 'object' && 'errors' in error.content && Array.isArray(error.content.errors) ? error.content.errors : undefined;
5919
- const record = context.request.data?.record || context.request.records?.[0];
5920
- store.cache.commitWasRejected(record, errors);
5921
- // re-throw the original error to preserve `errors` property.
5922
- throw error;
5923
- } else {
5924
- response = store.cache.put(error);
5925
- response = maybeUpdateUiObjects(store, context.request, {
5926
- shouldHydrate,
5927
- shouldFetch,
5928
- shouldBackgroundFetch,
5929
- identifier
5930
- }, response, false);
5931
- }
5932
- });
5933
- store._enableAsyncFlush = null;
5934
- if (identifier && store.lifetimes?.didRequest) {
5935
- store.lifetimes.didRequest(context.request, error.response, identifier, store);
5936
- }
5937
- if (!shouldBackgroundFetch) {
5938
- const newError = cloneError(error);
5939
- newError.content = response;
5940
- throw newError;
5941
- } else {
5942
- store.notifications._flush();
6417
+ store.cache.willCommit(record, context);
5943
6418
  }
6419
+ }
6420
+ if (store.lifetimes?.willRequest) {
6421
+ store.lifetimes.willRequest(context.request, identifier, store);
6422
+ }
6423
+ const promise = next(context.request).then(document => {
6424
+ return handleFetchSuccess(store, context, options, document);
6425
+ }, error => {
6426
+ return handleFetchError(store, context, options, error);
5944
6427
  });
5945
6428
  if (!isMut) {
5946
6429
  return promise;
5947
6430
  }
5948
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
6431
+ macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
5949
6432
  if (!test) {
5950
6433
  throw new Error(`Expected a mutation`);
5951
6434
  }
@@ -5962,129 +6445,475 @@ function fetchContentAndHydrate(next, context, identifier, shouldFetch, shouldBa
5962
6445
  }]
5963
6446
  });
5964
6447
  }
5965
- function isAggregateError(error) {
5966
- return error instanceof AggregateError || error.name === 'AggregateError' && Array.isArray(error.errors);
5967
- }
5968
- // TODO @runspired, consider if we should deep freeze errors (potentially only in debug) vs cloning them
5969
- function cloneError(error) {
5970
- const isAggregate = isAggregateError(error);
5971
- const cloned = isAggregate ? new AggregateError(structuredClone(error.errors), error.message) : new Error(error.message);
5972
- cloned.stack = error.stack;
5973
- cloned.error = error.error;
5974
-
5975
- // copy over enumerable properties
5976
- Object.assign(cloned, error);
5977
- return cloned;
5978
- }
5979
6448
 
5980
6449
  /**
5981
- * A CacheHandler that adds support for using an EmberData Cache with a RequestManager.
5982
- *
5983
- * This handler will only run when a request has supplied a `store` instance. Requests
5984
- * issued by the store via `store.request()` will automatically have the `store` instance
5985
- * attached to the request.
5986
- *
5987
- * ```ts
5988
- * requestManager.request({
5989
- * store: store,
5990
- * url: '/api/posts',
5991
- * method: 'GET'
5992
- * });
5993
- * ```
5994
- *
5995
- * When this handler elects to handle a request, it will return the raw `StructuredDocument`
5996
- * unless the request has `[EnableHydration]` set to `true`. In this case, the handler will
5997
- * return a `Document` instance that will automatically update the UI when the cache is updated
5998
- * in the future and will hydrate any identifiers in the StructuredDocument into Record instances.
5999
- *
6000
- * When issuing a request via the store, [EnableHydration] is automatically set to `true`. This
6001
- * means that if desired you can issue requests that utilize the cache without needing to also
6002
- * utilize Record instances if desired.
6003
- *
6004
- * Said differently, you could elect to issue all requests via a RequestManager, without ever using
6005
- * the store directly, by setting [EnableHydration] to `true` and providing a store instance. Not
6006
- * necessarily the most useful thing, but the decoupled nature of the RequestManager and incremental-feature
6007
- * approach of EmberData allows for this flexibility.
6008
- *
6009
- * ```ts
6010
- * import { EnableHydration } from '@warp-drive-mirror/core-types/request';
6011
- *
6012
- * requestManager.request({
6013
- * store: store,
6014
- * url: '/api/posts',
6015
- * method: 'GET',
6016
- * [EnableHydration]: true
6017
- * });
6018
- *
6019
- * @typedoc
6020
- */
6021
- const CacheHandler = {
6022
- request(context, next) {
6023
- // if we have no cache or no cache-key skip cache handling
6024
- if (!context.request.store || context.request.cacheOptions?.[SkipCache]) {
6025
- return next(context.request);
6026
- }
6027
- const {
6028
- store
6029
- } = context.request;
6030
- const identifier = store.identifierCache.getOrCreateDocumentIdentifier(context.request);
6031
- const peeked = identifier ? store.cache.peekRequest(identifier) : null;
6450
+ @module @ember-data-mirror/store
6451
+ */
6452
+ /**
6453
+ A `ManyArray` is a `MutableArray` that represents the contents of a has-many
6454
+ relationship.
6032
6455
 
6033
- // determine if we should skip cache
6034
- if (calcShouldFetch(store, context.request, !!peeked, identifier)) {
6035
- return fetchContentAndHydrate(next, context, identifier, true, false);
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-mirror/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-mirror/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
+ }
6036
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
+ */
6037
6537
 
6038
- // if we have not skipped cache, determine if we should update behind the scenes
6039
- if (calcShouldBackgroundFetch(store, context.request, false, identifier)) {
6040
- const promise = fetchContentAndHydrate(next, context, identifier, false, true);
6041
- store.requestManager._pending.set(context.id, promise);
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().WarpDriveMirror.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().WarpDriveMirror.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().WarpDriveMirror.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().WarpDriveMirror.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().WarpDriveMirror.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
+ })() : {};
6042
6746
  }
6043
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
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().WarpDriveMirror.env.DEBUG) ? (test => {
6044
6773
  if (!test) {
6045
- throw new Error(`Expected a peeked request to be present`);
6774
+ throw new Error(`Expected the manager for ManyArray to implement reloadHasMany`);
6046
6775
  }
6047
- })(peeked) : {};
6048
- const shouldHydrate = context.request[EnableHydration] || false;
6049
- context.setResponse(peeked.response);
6050
- if ('error' in peeked) {
6051
- const content = shouldHydrate ? maybeUpdateUiObjects(store, context.request, {
6052
- shouldHydrate,
6053
- identifier
6054
- }, peeked.content, true) : peeked.content;
6055
- const newError = cloneError(peeked);
6056
- newError.content = content;
6057
- throw newError;
6058
- }
6059
- const result = shouldHydrate ? maybeUpdateUiObjects(store, context.request, {
6060
- shouldHydrate,
6061
- identifier
6062
- }, peeked.content, true) : peeked.content;
6063
- return result;
6064
- }
6065
- };
6066
- function copyDocumentProperties(target, source) {
6067
- if ('links' in source) {
6068
- target.links = source.links;
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);
6069
6779
  }
6070
- if ('meta' in source) {
6071
- target.meta = source.meta;
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().WarpDriveMirror.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;
6072
6817
  }
6073
- if ('errors' in source) {
6074
- target.errors = source.errors;
6818
+ destroy() {
6819
+ super.destroy(false);
6075
6820
  }
6076
6821
  }
6077
- function isCacheAffecting(document) {
6078
- if (!isMutation(document.request)) {
6079
- return true;
6080
- }
6081
- // a mutation combined with a 204 has no cache impact when no known records were involved
6082
- // a createRecord with a 201 with an empty response and no known records should similarly
6083
- // have no cache impact
6084
-
6085
- if (document.request.op === 'createRecord' && document.response?.status === 201) {
6086
- return document.content ? Object.keys(document.content).length > 0 : false;
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().WarpDriveMirror.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().WarpDriveMirror.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-mirror:deprecate-many-array-duplicates',
6858
+ for: 'ember-data-mirror',
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
+ }
6087
6868
  }
6088
- return document.response?.status !== 204;
6089
6869
  }
6090
- 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 };
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().WarpDriveMirror.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 };