@ember-data/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.
- package/LICENSE.md +19 -7
- package/README.md +5 -8
- package/dist/-private.js +1 -1
- package/dist/index.js +1 -1
- package/dist/{cache-handler-C5ilAUZ5.js → many-array-BwVo-2vv.js} +1699 -870
- package/dist/many-array-BwVo-2vv.js.map +1 -0
- package/logos/NCC-1701-a-blue.svg +4 -0
- package/logos/NCC-1701-a-gold.svg +4 -0
- package/logos/NCC-1701-a-gold_100.svg +1 -0
- package/logos/NCC-1701-a-gold_base-64.txt +1 -0
- package/logos/NCC-1701-a.svg +4 -0
- package/logos/README.md +4 -0
- package/logos/docs-badge.svg +2 -0
- package/logos/github-header.svg +444 -0
- package/logos/social1.png +0 -0
- package/logos/social2.png +0 -0
- package/logos/warp-drive-logo-dark.svg +4 -0
- package/logos/warp-drive-logo-gold.svg +4 -0
- package/package.json +41 -49
- package/unstable-preview-types/-private/cache-handler/handler.d.ts +62 -0
- package/unstable-preview-types/-private/cache-handler/handler.d.ts.map +1 -0
- package/unstable-preview-types/-private/{cache-handler.d.ts → cache-handler/types.d.ts} +25 -62
- package/unstable-preview-types/-private/cache-handler/types.d.ts.map +1 -0
- package/unstable-preview-types/-private/cache-handler/utils.d.ts +34 -0
- package/unstable-preview-types/-private/cache-handler/utils.d.ts.map +1 -0
- package/unstable-preview-types/-private/caches/identifier-cache.d.ts +7 -3
- package/unstable-preview-types/-private/caches/identifier-cache.d.ts.map +1 -1
- package/unstable-preview-types/-private/caches/instance-cache.d.ts +4 -1
- package/unstable-preview-types/-private/caches/instance-cache.d.ts.map +1 -1
- package/unstable-preview-types/-private/debug/utils.d.ts +9 -0
- package/unstable-preview-types/-private/debug/utils.d.ts.map +1 -0
- package/unstable-preview-types/-private/document.d.ts +23 -14
- package/unstable-preview-types/-private/document.d.ts.map +1 -1
- package/unstable-preview-types/-private/legacy-model-support/record-reference.d.ts.map +1 -1
- package/unstable-preview-types/-private/legacy-model-support/shim-model-class.d.ts +3 -3
- package/unstable-preview-types/-private/legacy-model-support/shim-model-class.d.ts.map +1 -1
- package/unstable-preview-types/-private/managers/cache-capabilities-manager.d.ts +3 -3
- package/unstable-preview-types/-private/managers/cache-capabilities-manager.d.ts.map +1 -1
- package/unstable-preview-types/-private/managers/cache-manager.d.ts +23 -1
- package/unstable-preview-types/-private/managers/cache-manager.d.ts.map +1 -1
- package/unstable-preview-types/-private/managers/notification-manager.d.ts +11 -6
- package/unstable-preview-types/-private/managers/notification-manager.d.ts.map +1 -1
- package/unstable-preview-types/-private/managers/record-array-manager.d.ts +9 -5
- package/unstable-preview-types/-private/managers/record-array-manager.d.ts.map +1 -1
- package/unstable-preview-types/-private/record-arrays/identifier-array.d.ts +14 -2
- package/unstable-preview-types/-private/record-arrays/identifier-array.d.ts.map +1 -1
- package/unstable-preview-types/-private/record-arrays/many-array.d.ts +199 -0
- package/unstable-preview-types/-private/record-arrays/many-array.d.ts.map +1 -0
- package/unstable-preview-types/-private/record-arrays/native-proxy-type-fix.d.ts +3 -3
- package/unstable-preview-types/-private/store-service.d.ts +92 -19
- package/unstable-preview-types/-private/store-service.d.ts.map +1 -1
- package/unstable-preview-types/-private.d.ts +7 -4
- package/unstable-preview-types/-private.d.ts.map +1 -1
- package/unstable-preview-types/-types/q/cache-capabilities-manager.d.ts +4 -4
- package/unstable-preview-types/-types/q/cache-capabilities-manager.d.ts.map +1 -1
- package/unstable-preview-types/-types/q/ds-model.d.ts +3 -3
- package/unstable-preview-types/-types/q/ds-model.d.ts.map +1 -1
- package/unstable-preview-types/-types/q/schema-service.d.ts +16 -8
- package/unstable-preview-types/-types/q/schema-service.d.ts.map +1 -1
- package/unstable-preview-types/index.d.ts +24 -23
- package/unstable-preview-types/index.d.ts.map +1 -1
- package/dist/cache-handler-C5ilAUZ5.js.map +0 -1
- package/unstable-preview-types/-private/cache-handler.d.ts.map +0 -1
- /package/{ember-data-logo-dark.svg → logos/ember-data-logo-dark.svg} +0 -0
- /package/{ember-data-logo-light.svg → logos/ember-data-logo-light.svg} +0 -0
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { deprecate, warn } from '@ember/debug';
|
|
2
2
|
import { macroCondition, getGlobalConfig, dependencySatisfies, importSync } from '@embroider/macros';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { dasherize } from '@ember-data/request-utils/string';
|
|
7
|
-
import { defineSignal, createSignal, subscribe, createArrayTags, addToTransaction, addTransactionCB } from '@ember-data/tracking/-private';
|
|
3
|
+
import { EnableHydration, SkipCache } from '@warp-drive/core-types/request';
|
|
4
|
+
import { setLogging, getRuntimeConfig } from '@warp-drive/core-types/runtime';
|
|
5
|
+
import { getOrSetGlobal, peekTransient, setTransient } from '@warp-drive/core-types/-private';
|
|
8
6
|
import { _backburner } from '@ember/runloop';
|
|
7
|
+
import { defineSubscription, notifySignal, defineSignal, createSignal, subscribe, createArrayTags, addToTransaction, addTransactionCB } from '@ember-data/tracking/-private';
|
|
8
|
+
import { CACHE_OWNER, DEBUG_STALE_CACHE_OWNER, DEBUG_IDENTIFIER_BUCKET, DEBUG_CLIENT_ORIGINATED } from '@warp-drive/core-types/identifier';
|
|
9
|
+
import { dasherize } from '@ember-data/request-utils/string';
|
|
9
10
|
import { compat } from '@ember-data/tracking';
|
|
10
11
|
|
|
11
12
|
/**
|
|
@@ -25,7 +26,7 @@ function coerceId(id) {
|
|
|
25
26
|
until: '6.0',
|
|
26
27
|
for: 'ember-data',
|
|
27
28
|
since: {
|
|
28
|
-
available: '
|
|
29
|
+
available: '4.13',
|
|
29
30
|
enabled: '5.3'
|
|
30
31
|
}
|
|
31
32
|
});
|
|
@@ -60,7 +61,7 @@ function normalizeModelName(type) {
|
|
|
60
61
|
until: '6.0',
|
|
61
62
|
for: 'ember-data',
|
|
62
63
|
since: {
|
|
63
|
-
available: '
|
|
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') :
|
|
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,16 +130,15 @@ function hasType(resource) {
|
|
|
129
130
|
/**
|
|
130
131
|
@module @ember-data/store
|
|
131
132
|
*/
|
|
132
|
-
const IDENTIFIERS = getOrSetGlobal('IDENTIFIERS', new Set());
|
|
133
133
|
const DOCUMENTS = getOrSetGlobal('DOCUMENTS', new Set());
|
|
134
134
|
function isStableIdentifier(identifier) {
|
|
135
|
-
return identifier[CACHE_OWNER] !== undefined
|
|
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') :
|
|
141
|
+
const _crypto = isFastBoot ? FastBoot.require('crypto') : globalThis.crypto;
|
|
142
142
|
if (macroCondition(getGlobalConfig().WarpDrive.polyfillUUID)) {
|
|
143
143
|
installPolyfill();
|
|
144
144
|
}
|
|
@@ -310,9 +310,11 @@ class IdentifierCache {
|
|
|
310
310
|
*/
|
|
311
311
|
|
|
312
312
|
_getRecordIdentifier(resource, shouldGenerate) {
|
|
313
|
-
if (macroCondition(getGlobalConfig().WarpDrive.
|
|
314
|
-
|
|
315
|
-
|
|
313
|
+
if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_IDENTIFIERS)) {
|
|
314
|
+
if (getGlobalConfig().WarpDrive.debug.LOG_IDENTIFIERS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_IDENTIFIERS) {
|
|
315
|
+
// eslint-disable-next-line no-console
|
|
316
|
+
console.groupCollapsed(`Identifiers: ${shouldGenerate ? 'Generating' : 'Peeking'} Identifier`, resource);
|
|
317
|
+
}
|
|
316
318
|
}
|
|
317
319
|
// short circuit if we're already the stable version
|
|
318
320
|
if (isStableIdentifier(resource)) {
|
|
@@ -322,33 +324,41 @@ class IdentifierCache {
|
|
|
322
324
|
throw new Error(`The supplied identifier ${JSON.stringify(resource)} does not belong to this store instance`);
|
|
323
325
|
}
|
|
324
326
|
}
|
|
325
|
-
if (macroCondition(getGlobalConfig().WarpDrive.
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
327
|
+
if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_IDENTIFIERS)) {
|
|
328
|
+
if (getGlobalConfig().WarpDrive.debug.LOG_IDENTIFIERS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_IDENTIFIERS) {
|
|
329
|
+
// eslint-disable-next-line no-console
|
|
330
|
+
console.log(`Identifiers: cache HIT - Stable ${resource.lid}`);
|
|
331
|
+
// eslint-disable-next-line no-console
|
|
332
|
+
console.groupEnd();
|
|
333
|
+
}
|
|
330
334
|
}
|
|
331
335
|
return resource;
|
|
332
336
|
}
|
|
333
337
|
|
|
334
338
|
// the resource is unknown, ask the application to identify this data for us
|
|
335
339
|
const lid = this._generate(resource, 'record');
|
|
336
|
-
if (macroCondition(getGlobalConfig().WarpDrive.
|
|
337
|
-
|
|
338
|
-
|
|
340
|
+
if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_IDENTIFIERS)) {
|
|
341
|
+
if (getGlobalConfig().WarpDrive.debug.LOG_IDENTIFIERS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_IDENTIFIERS) {
|
|
342
|
+
// eslint-disable-next-line no-console
|
|
343
|
+
console.log(`Identifiers: ${lid ? 'no ' : ''}lid ${lid ? lid + ' ' : ''}determined for resource`, resource);
|
|
344
|
+
}
|
|
339
345
|
}
|
|
340
346
|
let identifier = /*#__NOINLINE__*/getIdentifierFromLid(this._cache, lid, resource);
|
|
341
347
|
if (identifier !== null) {
|
|
342
|
-
if (macroCondition(getGlobalConfig().WarpDrive.
|
|
343
|
-
|
|
344
|
-
|
|
348
|
+
if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_IDENTIFIERS)) {
|
|
349
|
+
if (getGlobalConfig().WarpDrive.debug.LOG_IDENTIFIERS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_IDENTIFIERS) {
|
|
350
|
+
// eslint-disable-next-line no-console
|
|
351
|
+
console.groupEnd();
|
|
352
|
+
}
|
|
345
353
|
}
|
|
346
354
|
return identifier;
|
|
347
355
|
}
|
|
348
356
|
if (shouldGenerate === 0) {
|
|
349
|
-
if (macroCondition(getGlobalConfig().WarpDrive.
|
|
350
|
-
|
|
351
|
-
|
|
357
|
+
if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_IDENTIFIERS)) {
|
|
358
|
+
if (getGlobalConfig().WarpDrive.debug.LOG_IDENTIFIERS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_IDENTIFIERS) {
|
|
359
|
+
// eslint-disable-next-line no-console
|
|
360
|
+
console.groupEnd();
|
|
361
|
+
}
|
|
352
362
|
}
|
|
353
363
|
return;
|
|
354
364
|
}
|
|
@@ -366,9 +376,11 @@ class IdentifierCache {
|
|
|
366
376
|
identifier = /*#__NOINLINE__*/makeStableRecordIdentifier(keyInfo, 'record', false);
|
|
367
377
|
}
|
|
368
378
|
addResourceToCache(this._cache, identifier);
|
|
369
|
-
if (macroCondition(getGlobalConfig().WarpDrive.
|
|
370
|
-
|
|
371
|
-
|
|
379
|
+
if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_IDENTIFIERS)) {
|
|
380
|
+
if (getGlobalConfig().WarpDrive.debug.LOG_IDENTIFIERS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_IDENTIFIERS) {
|
|
381
|
+
// eslint-disable-next-line no-console
|
|
382
|
+
console.groupEnd();
|
|
383
|
+
}
|
|
372
384
|
}
|
|
373
385
|
return identifier;
|
|
374
386
|
}
|
|
@@ -462,9 +474,11 @@ class IdentifierCache {
|
|
|
462
474
|
|
|
463
475
|
/*#__NOINLINE__*/
|
|
464
476
|
addResourceToCache(this._cache, identifier);
|
|
465
|
-
if (macroCondition(getGlobalConfig().WarpDrive.
|
|
466
|
-
|
|
467
|
-
|
|
477
|
+
if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_IDENTIFIERS)) {
|
|
478
|
+
if (getGlobalConfig().WarpDrive.debug.LOG_IDENTIFIERS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_IDENTIFIERS) {
|
|
479
|
+
// eslint-disable-next-line no-console
|
|
480
|
+
console.log(`Identifiers: created identifier ${String(identifier)} for newly generated resource`, data);
|
|
481
|
+
}
|
|
468
482
|
}
|
|
469
483
|
return identifier;
|
|
470
484
|
}
|
|
@@ -511,9 +525,11 @@ class IdentifierCache {
|
|
|
511
525
|
if (hadLid) {
|
|
512
526
|
data.lid = identifier.lid;
|
|
513
527
|
}
|
|
514
|
-
if (macroCondition(getGlobalConfig().WarpDrive.
|
|
515
|
-
|
|
516
|
-
|
|
528
|
+
if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_IDENTIFIERS)) {
|
|
529
|
+
if (getGlobalConfig().WarpDrive.debug.LOG_IDENTIFIERS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_IDENTIFIERS) {
|
|
530
|
+
// eslint-disable-next-line no-console
|
|
531
|
+
console.log(`Identifiers: merged identifiers ${generatedIdentifier.lid} and ${existingIdentifier.lid} for resource into ${identifier.lid}`, data);
|
|
532
|
+
}
|
|
517
533
|
}
|
|
518
534
|
}
|
|
519
535
|
const id = identifier.id;
|
|
@@ -523,9 +539,11 @@ class IdentifierCache {
|
|
|
523
539
|
|
|
524
540
|
// add to our own secondary lookup table
|
|
525
541
|
if (id !== newId && newId !== null) {
|
|
526
|
-
if (macroCondition(getGlobalConfig().WarpDrive.
|
|
527
|
-
|
|
528
|
-
|
|
542
|
+
if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_IDENTIFIERS)) {
|
|
543
|
+
if (getGlobalConfig().WarpDrive.debug.LOG_IDENTIFIERS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_IDENTIFIERS) {
|
|
544
|
+
// eslint-disable-next-line no-console
|
|
545
|
+
console.log(`Identifiers: updated id for identifier ${identifier.lid} from '${String(id)}' to '${String(newId)}' for resource`, data);
|
|
546
|
+
}
|
|
529
547
|
}
|
|
530
548
|
const typeSet = this._cache.resourcesByType[identifier.type];
|
|
531
549
|
macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
|
|
@@ -537,9 +555,11 @@ class IdentifierCache {
|
|
|
537
555
|
if (id !== null) {
|
|
538
556
|
typeSet.id.delete(id);
|
|
539
557
|
}
|
|
540
|
-
} else if (macroCondition(getGlobalConfig().WarpDrive.
|
|
541
|
-
|
|
542
|
-
|
|
558
|
+
} else if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_IDENTIFIERS)) {
|
|
559
|
+
if (getGlobalConfig().WarpDrive.debug.LOG_IDENTIFIERS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_IDENTIFIERS) {
|
|
560
|
+
// eslint-disable-next-line no-console
|
|
561
|
+
console.log(`Identifiers: updated identifier ${identifier.lid} resource`, data);
|
|
562
|
+
}
|
|
543
563
|
}
|
|
544
564
|
return identifier;
|
|
545
565
|
}
|
|
@@ -621,11 +641,12 @@ class IdentifierCache {
|
|
|
621
641
|
identifier[DEBUG_STALE_CACHE_OWNER] = identifier[CACHE_OWNER];
|
|
622
642
|
}
|
|
623
643
|
identifier[CACHE_OWNER] = undefined;
|
|
624
|
-
IDENTIFIERS.delete(identifier);
|
|
625
644
|
this._forget(identifier, 'record');
|
|
626
|
-
if (macroCondition(getGlobalConfig().WarpDrive.
|
|
627
|
-
|
|
628
|
-
|
|
645
|
+
if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_IDENTIFIERS)) {
|
|
646
|
+
if (getGlobalConfig().WarpDrive.debug.LOG_IDENTIFIERS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_IDENTIFIERS) {
|
|
647
|
+
// eslint-disable-next-line no-console
|
|
648
|
+
console.log(`Identifiers: released identifier ${identifierObject.lid}`);
|
|
649
|
+
}
|
|
629
650
|
}
|
|
630
651
|
}
|
|
631
652
|
destroy() {
|
|
@@ -637,20 +658,17 @@ class IdentifierCache {
|
|
|
637
658
|
}
|
|
638
659
|
}
|
|
639
660
|
function makeStableRecordIdentifier(recordIdentifier, bucket, clientOriginated) {
|
|
640
|
-
IDENTIFIERS.add(recordIdentifier);
|
|
641
661
|
if (macroCondition(getGlobalConfig().WarpDrive.env.DEBUG)) {
|
|
642
662
|
// we enforce immutability in dev
|
|
643
663
|
// but preserve our ability to do controlled updates to the reference
|
|
644
664
|
let wrapper = {
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
},
|
|
665
|
+
type: recordIdentifier.type,
|
|
666
|
+
lid: recordIdentifier.lid,
|
|
648
667
|
get id() {
|
|
649
668
|
return recordIdentifier.id;
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
|
|
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(
|
|
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(
|
|
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
|
|
694
|
-
wrapper[DEBUG_IDENTIFIER_BUCKET] = bucket;
|
|
695
|
-
IDENTIFIERS.add(wrapper);
|
|
717
|
+
Object.setPrototypeOf(wrapper, proto);
|
|
696
718
|
DEBUG_MAP.set(wrapper, recordIdentifier);
|
|
697
719
|
wrapper = freeze(wrapper);
|
|
698
720
|
return wrapper;
|
|
@@ -775,9 +797,11 @@ function detectMerge(cache, keyInfo, identifier, data) {
|
|
|
775
797
|
}
|
|
776
798
|
function getIdentifierFromLid(cache, lid, resource) {
|
|
777
799
|
const identifier = cache.resources.get(lid);
|
|
778
|
-
if (macroCondition(getGlobalConfig().WarpDrive.
|
|
779
|
-
|
|
780
|
-
|
|
800
|
+
if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_IDENTIFIERS)) {
|
|
801
|
+
if (getGlobalConfig().WarpDrive.debug.LOG_IDENTIFIERS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_IDENTIFIERS) {
|
|
802
|
+
// eslint-disable-next-line no-console
|
|
803
|
+
console.log(`Identifiers: cache ${identifier ? 'HIT' : 'MISS'} - Non-Stable ${lid}`, resource);
|
|
804
|
+
}
|
|
781
805
|
}
|
|
782
806
|
return identifier || null;
|
|
783
807
|
}
|
|
@@ -796,88 +820,473 @@ 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
|
-
|
|
802
|
-
*/
|
|
906
|
+
* @module @ember-data/store
|
|
907
|
+
*/
|
|
908
|
+
function urlFromLink(link) {
|
|
909
|
+
if (typeof link === 'string') return link;
|
|
910
|
+
return link.href;
|
|
911
|
+
}
|
|
803
912
|
|
|
804
913
|
/**
|
|
805
|
-
|
|
806
|
-
|
|
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
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
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
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
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
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
1016
|
+
* Fetches the related link for this document, returning a promise that resolves
|
|
1017
|
+
* with the document when the request completes. If no related link is present,
|
|
1018
|
+
* will fallback to the self link if present
|
|
1019
|
+
*
|
|
1020
|
+
* @method fetch
|
|
1021
|
+
* @public
|
|
1022
|
+
* @param {object} options
|
|
1023
|
+
* @return Promise<Document>
|
|
1024
|
+
*/
|
|
1025
|
+
fetch(options = {}) {
|
|
1026
|
+
macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
|
|
1027
|
+
if (!test) {
|
|
1028
|
+
throw new Error(`No self or related link`);
|
|
1029
|
+
}
|
|
1030
|
+
})(this.links?.related || this.links?.self) : {};
|
|
1031
|
+
options.cacheOptions = options.cacheOptions || {};
|
|
1032
|
+
options.cacheOptions.key = this.identifier?.lid;
|
|
1033
|
+
return this.#request(this.links.related ? 'related' : 'self', options);
|
|
864
1034
|
}
|
|
865
1035
|
|
|
866
1036
|
/**
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
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);
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
/**
|
|
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().WarpDrive.env.DEBUG) ? (test => {
|
|
1139
|
+
if (!test) {
|
|
1140
|
+
throw new Error(`No cache data was found for the document '${identifier.lid}'`);
|
|
1141
|
+
}
|
|
1142
|
+
})(doc) : {};
|
|
1143
|
+
return 'errors' in doc ? doc.errors : undefined;
|
|
1144
|
+
}
|
|
1145
|
+
});
|
|
1146
|
+
defineSubscription(ReactiveDocument.prototype, 'data', {
|
|
1147
|
+
get() {
|
|
1148
|
+
const {
|
|
1149
|
+
identifier,
|
|
1150
|
+
_localCache
|
|
1151
|
+
} = this;
|
|
1152
|
+
const doc = identifier ? this._store.cache.peek(identifier) : _localCache.document;
|
|
1153
|
+
macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
|
|
1154
|
+
if (!test) {
|
|
1155
|
+
throw new Error(`No cache data was found for the document '${identifier?.lid ?? '<uncached document>'}'`);
|
|
1156
|
+
}
|
|
1157
|
+
})(doc) : {};
|
|
1158
|
+
const data = 'data' in doc ? doc.data : undefined;
|
|
1159
|
+
if (Array.isArray(data)) {
|
|
1160
|
+
return this._store.recordArrayManager.getCollection({
|
|
1161
|
+
type: identifier ? identifier.lid : _localCache.request.url,
|
|
1162
|
+
identifiers: data.slice(),
|
|
1163
|
+
doc: identifier ? undefined : doc,
|
|
1164
|
+
identifier: identifier ?? null
|
|
1165
|
+
});
|
|
1166
|
+
} else if (data) {
|
|
1167
|
+
return this._store.peekRecord(data);
|
|
1168
|
+
} else {
|
|
1169
|
+
return data;
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
});
|
|
1173
|
+
defineSubscription(ReactiveDocument.prototype, 'links', {
|
|
1174
|
+
get() {
|
|
1175
|
+
const {
|
|
1176
|
+
identifier
|
|
1177
|
+
} = this;
|
|
1178
|
+
if (!identifier) {
|
|
1179
|
+
return this._localCache.document.links;
|
|
1180
|
+
}
|
|
1181
|
+
const data = this._store.cache.peek(identifier);
|
|
1182
|
+
macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
|
|
1183
|
+
if (!test) {
|
|
1184
|
+
throw new Error(`No cache data was found for the document '${identifier.lid}'`);
|
|
1185
|
+
}
|
|
1186
|
+
})(data) : {};
|
|
1187
|
+
return data.links;
|
|
1188
|
+
}
|
|
1189
|
+
});
|
|
1190
|
+
defineSubscription(ReactiveDocument.prototype, 'meta', {
|
|
1191
|
+
get() {
|
|
1192
|
+
const {
|
|
1193
|
+
identifier
|
|
1194
|
+
} = this;
|
|
1195
|
+
if (!identifier) {
|
|
1196
|
+
return this._localCache.document.meta;
|
|
1197
|
+
}
|
|
1198
|
+
const data = this._store.cache.peek(identifier);
|
|
1199
|
+
macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
|
|
1200
|
+
if (!test) {
|
|
1201
|
+
throw new Error(`No cache data was found for the document '${identifier.lid}'`);
|
|
1202
|
+
}
|
|
1203
|
+
})(data) : {};
|
|
1204
|
+
return data.meta;
|
|
1205
|
+
}
|
|
1206
|
+
});
|
|
1207
|
+
|
|
1208
|
+
/**
|
|
1209
|
+
@module @ember-data/store
|
|
1210
|
+
*/
|
|
1211
|
+
|
|
1212
|
+
/**
|
|
1213
|
+
A `RecordReference` is a low-level API that allows users and
|
|
1214
|
+
addon authors to perform meta-operations on a record.
|
|
1215
|
+
|
|
1216
|
+
@class RecordReference
|
|
1217
|
+
@public
|
|
1218
|
+
*/
|
|
1219
|
+
class RecordReference {
|
|
1220
|
+
// unsubscribe token given to us by the notification manager
|
|
1221
|
+
___token;
|
|
1222
|
+
___identifier;
|
|
1223
|
+
constructor(store, identifier) {
|
|
1224
|
+
this.store = store;
|
|
1225
|
+
this.___identifier = identifier;
|
|
1226
|
+
this.___token = store.notifications.subscribe(identifier, (_, bucket, notifiedKey) => {
|
|
1227
|
+
if (bucket === 'identity' || bucket === 'attributes' && notifiedKey === 'id') {
|
|
1228
|
+
this._ref++;
|
|
1229
|
+
}
|
|
1230
|
+
});
|
|
1231
|
+
}
|
|
1232
|
+
destroy() {
|
|
1233
|
+
this.store.notifications.unsubscribe(this.___token);
|
|
1234
|
+
}
|
|
1235
|
+
get type() {
|
|
1236
|
+
return this.identifier().type;
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
/**
|
|
1240
|
+
The `id` of the record that this reference refers to.
|
|
1241
|
+
Together, the `type` and `id` properties form a composite key for
|
|
1242
|
+
the identity map.
|
|
1243
|
+
Example
|
|
1244
|
+
```javascript
|
|
1245
|
+
let userRef = store.getReference('user', 1);
|
|
1246
|
+
userRef.id(); // '1'
|
|
1247
|
+
```
|
|
1248
|
+
@method id
|
|
1249
|
+
@public
|
|
1250
|
+
@return {String} The id of the record.
|
|
1251
|
+
*/
|
|
1252
|
+
id() {
|
|
1253
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
|
1254
|
+
this._ref; // consume the tracked prop
|
|
1255
|
+
return this.___identifier.id;
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
/**
|
|
1259
|
+
The `identifier` of the record that this reference refers to.
|
|
1260
|
+
Together, the `type` and `id` properties form a composite key for
|
|
1261
|
+
the identity map.
|
|
1262
|
+
Example
|
|
1263
|
+
```javascript
|
|
1264
|
+
let userRef = store.getReference('user', 1);
|
|
1265
|
+
userRef.identifier(); // '1'
|
|
1266
|
+
```
|
|
1267
|
+
@method identifier
|
|
1268
|
+
@public
|
|
1269
|
+
@return {String} The identifier of the record.
|
|
1270
|
+
*/
|
|
1271
|
+
identifier() {
|
|
1272
|
+
return this.___identifier;
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
/**
|
|
1276
|
+
How the reference will be looked up when it is loaded. Currently
|
|
1277
|
+
this always returns `identity` to signify that a record will be
|
|
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';
|
|
881
1290
|
}
|
|
882
1291
|
|
|
883
1292
|
/**
|
|
@@ -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;
|
|
@@ -1236,6 +1646,14 @@ 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) {
|
|
@@ -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.
|
|
1255
|
-
|
|
1256
|
-
|
|
1672
|
+
if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_INSTANCE_CACHE)) {
|
|
1673
|
+
if (getGlobalConfig().WarpDrive.debug.LOG_INSTANCE_CACHE || globalThis.getWarpDriveRuntimeConfig().debug.LOG_INSTANCE_CACHE) {
|
|
1674
|
+
logGroup('reactive-ui', '', identifier.type, identifier.lid, 'created', '');
|
|
1675
|
+
// eslint-disable-next-line no-console
|
|
1676
|
+
console.log({
|
|
1677
|
+
properties
|
|
1678
|
+
});
|
|
1679
|
+
// eslint-disable-next-line no-console
|
|
1680
|
+
console.groupEnd();
|
|
1681
|
+
}
|
|
1257
1682
|
}
|
|
1258
1683
|
}
|
|
1259
1684
|
return record;
|
|
@@ -1299,9 +1724,10 @@ 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.
|
|
1303
|
-
|
|
1304
|
-
|
|
1727
|
+
if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_INSTANCE_CACHE)) {
|
|
1728
|
+
if (getGlobalConfig().WarpDrive.debug.LOG_INSTANCE_CACHE || globalThis.getWarpDriveRuntimeConfig().debug.LOG_INSTANCE_CACHE) {
|
|
1729
|
+
log('reactive-ui', '', identifier.type, identifier.lid, 'disconnected', '');
|
|
1730
|
+
}
|
|
1305
1731
|
}
|
|
1306
1732
|
}
|
|
1307
1733
|
unloadRecord(identifier) {
|
|
@@ -1317,9 +1743,11 @@ class InstanceCache {
|
|
|
1317
1743
|
})() : {};
|
|
1318
1744
|
}
|
|
1319
1745
|
}
|
|
1320
|
-
if (macroCondition(getGlobalConfig().WarpDrive.
|
|
1321
|
-
|
|
1322
|
-
|
|
1746
|
+
if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_INSTANCE_CACHE)) {
|
|
1747
|
+
if (getGlobalConfig().WarpDrive.debug.LOG_INSTANCE_CACHE || globalThis.getWarpDriveRuntimeConfig().debug.LOG_INSTANCE_CACHE) {
|
|
1748
|
+
// eslint-disable-next-line no-console
|
|
1749
|
+
console.groupCollapsed(`InstanceCache: unloading record for ${String(identifier)}`);
|
|
1750
|
+
}
|
|
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.
|
|
1336
|
-
|
|
1337
|
-
|
|
1763
|
+
if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_INSTANCE_CACHE)) {
|
|
1764
|
+
if (getGlobalConfig().WarpDrive.debug.LOG_INSTANCE_CACHE || globalThis.getWarpDriveRuntimeConfig().debug.LOG_INSTANCE_CACHE) {
|
|
1765
|
+
// eslint-disable-next-line no-console
|
|
1766
|
+
console.log(`InstanceCache: destroyed record for ${String(identifier)}`);
|
|
1767
|
+
}
|
|
1338
1768
|
}
|
|
1339
1769
|
}
|
|
1340
1770
|
if (cache) {
|
|
1341
1771
|
cache.unloadRecord(identifier);
|
|
1342
1772
|
removeRecordDataFor(identifier);
|
|
1343
|
-
if (macroCondition(getGlobalConfig().WarpDrive.
|
|
1344
|
-
|
|
1345
|
-
|
|
1773
|
+
if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_INSTANCE_CACHE)) {
|
|
1774
|
+
if (getGlobalConfig().WarpDrive.debug.LOG_INSTANCE_CACHE || globalThis.getWarpDriveRuntimeConfig().debug.LOG_INSTANCE_CACHE) {
|
|
1775
|
+
// eslint-disable-next-line no-console
|
|
1776
|
+
console.log(`InstanceCache: destroyed cache for ${String(identifier)}`);
|
|
1777
|
+
}
|
|
1346
1778
|
}
|
|
1347
1779
|
} else {
|
|
1348
1780
|
this.disconnect(identifier);
|
|
1349
1781
|
}
|
|
1350
1782
|
this.store._requestCache._clearEntries(identifier);
|
|
1351
|
-
if (macroCondition(getGlobalConfig().WarpDrive.
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1783
|
+
if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_INSTANCE_CACHE)) {
|
|
1784
|
+
if (getGlobalConfig().WarpDrive.debug.LOG_INSTANCE_CACHE || globalThis.getWarpDriveRuntimeConfig().debug.LOG_INSTANCE_CACHE) {
|
|
1785
|
+
// eslint-disable-next-line no-console
|
|
1786
|
+
console.log(`InstanceCache: unloaded RecordData for ${String(identifier)}`);
|
|
1787
|
+
// eslint-disable-next-line no-console
|
|
1788
|
+
console.groupEnd();
|
|
1789
|
+
}
|
|
1356
1790
|
}
|
|
1357
1791
|
});
|
|
1358
1792
|
}
|
|
@@ -1407,9 +1841,11 @@ 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.
|
|
1411
|
-
|
|
1412
|
-
|
|
1844
|
+
if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_INSTANCE_CACHE)) {
|
|
1845
|
+
if (getGlobalConfig().WarpDrive.debug.LOG_INSTANCE_CACHE || globalThis.getWarpDriveRuntimeConfig().debug.LOG_INSTANCE_CACHE) {
|
|
1846
|
+
// eslint-disable-next-line no-console
|
|
1847
|
+
console.log(`InstanceCache: updating id to '${id}' for record ${String(identifier)}`);
|
|
1848
|
+
}
|
|
1413
1849
|
}
|
|
1414
1850
|
const existingIdentifier = this.store.identifierCache.peekRecordIdentifier({
|
|
1415
1851
|
type,
|
|
@@ -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,28 +2620,52 @@ class CacheManager {
|
|
|
2156
2620
|
/**
|
|
2157
2621
|
* @module @ember-data/store
|
|
2158
2622
|
*/
|
|
2159
|
-
|
|
2160
|
-
let tokenId = 0;
|
|
2161
|
-
const CacheOperations = new Set(['added', 'removed', 'state', 'updated']);
|
|
2623
|
+
|
|
2162
2624
|
function isCacheOperationValue(value) {
|
|
2163
|
-
return
|
|
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
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2631
|
+
function count(label) {
|
|
2632
|
+
// @ts-expect-error
|
|
2633
|
+
// eslint-disable-next-line
|
|
2634
|
+
globalThis.__WarpDriveMetricCountData[label] = (globalThis.__WarpDriveMetricCountData[label] || 0) + 1;
|
|
2635
|
+
}
|
|
2636
|
+
function asInternalToken(token) {
|
|
2637
|
+
macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
|
|
2638
|
+
if (!test) {
|
|
2639
|
+
throw new Error(`Expected a token with a 'for' property`);
|
|
2640
|
+
}
|
|
2641
|
+
})(token && typeof token === 'function' && 'for' in token) : {};
|
|
2642
|
+
}
|
|
2643
|
+
function _unsubscribe(token, cache) {
|
|
2644
|
+
asInternalToken(token);
|
|
2645
|
+
const identifier = token.for;
|
|
2646
|
+
if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_NOTIFICATIONS)) {
|
|
2647
|
+
if (getGlobalConfig().WarpDrive.debug.LOG_NOTIFICATIONS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_NOTIFICATIONS) {
|
|
2648
|
+
if (!identifier) {
|
|
2649
|
+
// eslint-disable-next-line no-console
|
|
2650
|
+
console.log('Passed unknown unsubscribe token to unsubscribe', identifier);
|
|
2651
|
+
}
|
|
2175
2652
|
}
|
|
2176
2653
|
}
|
|
2177
2654
|
if (identifier) {
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2655
|
+
const callbacks = cache.get(identifier);
|
|
2656
|
+
if (!callbacks) {
|
|
2657
|
+
return;
|
|
2658
|
+
}
|
|
2659
|
+
const index = callbacks.indexOf(token);
|
|
2660
|
+
if (index === -1) {
|
|
2661
|
+
macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
|
|
2662
|
+
if (!test) {
|
|
2663
|
+
throw new Error(`Cannot unsubscribe a token that is not subscribed`);
|
|
2664
|
+
}
|
|
2665
|
+
})(index !== -1) : {};
|
|
2666
|
+
return;
|
|
2667
|
+
}
|
|
2668
|
+
callbacks.splice(index, 1);
|
|
2181
2669
|
}
|
|
2182
2670
|
}
|
|
2183
2671
|
|
|
@@ -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
|
/**
|
|
@@ -2235,17 +2722,26 @@ class NotificationManager {
|
|
|
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
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2725
|
+
let callbacks = this._cache.get(identifier);
|
|
2726
|
+
macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
|
|
2727
|
+
if (!test) {
|
|
2728
|
+
throw new Error(`expected to receive a valid callback`);
|
|
2729
|
+
}
|
|
2730
|
+
})(typeof callback === 'function') : {};
|
|
2731
|
+
macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
|
|
2732
|
+
if (!test) {
|
|
2733
|
+
throw new Error(`cannot subscribe with the same callback twice`);
|
|
2734
|
+
}
|
|
2735
|
+
})(!callbacks || !callbacks.includes(callback)) : {};
|
|
2736
|
+
// we use the callback as the cancellation token
|
|
2737
|
+
//@ts-expect-error
|
|
2738
|
+
callback.for = identifier;
|
|
2739
|
+
if (!callbacks) {
|
|
2740
|
+
callbacks = [];
|
|
2741
|
+
this._cache.set(identifier, callbacks);
|
|
2242
2742
|
}
|
|
2243
|
-
|
|
2244
|
-
|
|
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(
|
|
2756
|
+
_unsubscribe(token, this._cache);
|
|
2261
2757
|
}
|
|
2262
2758
|
}
|
|
2263
2759
|
|
|
@@ -2279,17 +2775,15 @@ class NotificationManager {
|
|
|
2279
2775
|
}
|
|
2280
2776
|
})(!key || value === 'attributes' || value === 'relationships') : {};
|
|
2281
2777
|
if (!isStableIdentifier(identifier) && !isDocumentIdentifier(identifier)) {
|
|
2282
|
-
if (macroCondition(getGlobalConfig().WarpDrive.
|
|
2283
|
-
|
|
2284
|
-
|
|
2778
|
+
if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_NOTIFICATIONS)) {
|
|
2779
|
+
if (getGlobalConfig().WarpDrive.debug.LOG_NOTIFICATIONS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_NOTIFICATIONS) {
|
|
2780
|
+
// eslint-disable-next-line no-console
|
|
2781
|
+
console.log(`Notifying: Expected to receive a stable Identifier to notify '${value}' '${key || ''}' with, but ${String(identifier)} is not in the cache`, identifier);
|
|
2782
|
+
}
|
|
2285
2783
|
}
|
|
2286
2784
|
return false;
|
|
2287
2785
|
}
|
|
2288
|
-
|
|
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
|
-
|
|
2794
|
+
if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_METRIC_COUNTS)) {
|
|
2795
|
+
if (getGlobalConfig().WarpDrive.debug.LOG_METRIC_COUNTS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_METRIC_COUNTS) {
|
|
2796
|
+
count(`notify ${'type' in identifier ? identifier.type : '<document>'} ${value} ${key}`);
|
|
2797
|
+
}
|
|
2798
|
+
}
|
|
2799
|
+
if (!this._scheduleNotify()) {
|
|
2800
|
+
if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_NOTIFICATIONS)) {
|
|
2801
|
+
if (getGlobalConfig().WarpDrive.debug.LOG_NOTIFICATIONS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_NOTIFICATIONS) {
|
|
2802
|
+
log('notify', 'buffered', `${'type' in identifier ? identifier.type : 'document'}`, identifier.lid, `${value}`, key || '');
|
|
2803
|
+
}
|
|
2804
|
+
}
|
|
2805
|
+
}
|
|
2806
|
+
} else {
|
|
2807
|
+
if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_NOTIFICATIONS)) {
|
|
2808
|
+
if (getGlobalConfig().WarpDrive.debug.LOG_NOTIFICATIONS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_NOTIFICATIONS) {
|
|
2809
|
+
log('notify', 'discarded', `${'type' in identifier ? identifier.type : 'document'}`, identifier.lid, `${value}`, key || '');
|
|
2810
|
+
}
|
|
2811
|
+
}
|
|
2812
|
+
if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_METRIC_COUNTS)) {
|
|
2813
|
+
if (getGlobalConfig().WarpDrive.debug.LOG_METRIC_COUNTS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_METRIC_COUNTS) {
|
|
2814
|
+
count(`DISCARDED notify ${'type' in identifier ? identifier.type : '<document>'} ${value} ${key}`);
|
|
2815
|
+
}
|
|
2816
|
+
}
|
|
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
|
-
|
|
2322
|
-
|
|
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.
|
|
2336
|
-
|
|
2337
|
-
|
|
2853
|
+
if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_NOTIFICATIONS)) {
|
|
2854
|
+
if (getGlobalConfig().WarpDrive.debug.LOG_NOTIFICATIONS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_NOTIFICATIONS) {
|
|
2855
|
+
log('notify', '', `${'type' in identifier ? identifier.type : 'document'}`, identifier.lid, `${value}`, key || '');
|
|
2856
|
+
}
|
|
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
|
|
2350
|
-
if (!
|
|
2868
|
+
const callbacks = this._cache.get(identifier);
|
|
2869
|
+
if (!callbacks || !callbacks.length) {
|
|
2351
2870
|
return false;
|
|
2352
2871
|
}
|
|
2353
|
-
|
|
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/store
|
|
@@ -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;
|
|
@@ -2785,8 +3200,7 @@ class IdentifierArray {
|
|
|
2785
3200
|
return false;
|
|
2786
3201
|
}
|
|
2787
3202
|
const original = target[index];
|
|
2788
|
-
const newIdentifier = extractIdentifierFromRecord$
|
|
2789
|
-
target[index] = newIdentifier;
|
|
3203
|
+
const newIdentifier = extractIdentifierFromRecord$2(value);
|
|
2790
3204
|
macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
|
|
2791
3205
|
if (!test) {
|
|
2792
3206
|
throw new Error(`Expected a record`);
|
|
@@ -2827,9 +3241,18 @@ class IdentifierArray {
|
|
|
2827
3241
|
return Reflect.deleteProperty(target, prop);
|
|
2828
3242
|
},
|
|
2829
3243
|
getPrototypeOf() {
|
|
2830
|
-
return
|
|
3244
|
+
return Array.prototype;
|
|
2831
3245
|
}
|
|
2832
3246
|
});
|
|
3247
|
+
if (macroCondition(getGlobalConfig().WarpDrive.env.DEBUG)) {
|
|
3248
|
+
Object.defineProperty(this, '__SHOW_ME_THE_DATA_(debug mode only)__', {
|
|
3249
|
+
enumerable: false,
|
|
3250
|
+
configurable: true,
|
|
3251
|
+
get() {
|
|
3252
|
+
return proxy.slice();
|
|
3253
|
+
}
|
|
3254
|
+
});
|
|
3255
|
+
}
|
|
2833
3256
|
createArrayTags(proxy, _SIGNAL);
|
|
2834
3257
|
this[NOTIFY] = this[NOTIFY].bind(proxy);
|
|
2835
3258
|
return proxy;
|
|
@@ -2915,7 +3338,11 @@ const desc = {
|
|
|
2915
3338
|
enumerable: true,
|
|
2916
3339
|
configurable: false,
|
|
2917
3340
|
get: function () {
|
|
2918
|
-
|
|
3341
|
+
// here to support computed chains
|
|
3342
|
+
// and {{#each}}
|
|
3343
|
+
if (macroCondition(getGlobalConfig().WarpDrive.deprecations.DEPRECATE_COMPUTED_CHAINS)) {
|
|
3344
|
+
return this;
|
|
3345
|
+
}
|
|
2919
3346
|
}
|
|
2920
3347
|
};
|
|
2921
3348
|
compat(desc);
|
|
@@ -2966,7 +3393,7 @@ Collection.prototype.query = null;
|
|
|
2966
3393
|
// Ensure instanceof works correctly
|
|
2967
3394
|
// Object.setPrototypeOf(IdentifierArray.prototype, Array.prototype);
|
|
2968
3395
|
|
|
2969
|
-
function assertRecordPassedToHasMany(record) {
|
|
3396
|
+
function assertRecordPassedToHasMany$1(record) {
|
|
2970
3397
|
macroCondition(getGlobalConfig().WarpDrive.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}`);
|
|
@@ -2980,11 +3407,11 @@ function assertRecordPassedToHasMany(record) {
|
|
|
2980
3407
|
}
|
|
2981
3408
|
}()) : {};
|
|
2982
3409
|
}
|
|
2983
|
-
function extractIdentifierFromRecord$
|
|
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
|
-
|
|
3509
|
+
const isRequestArray = isCollection(array);
|
|
3510
|
+
if (!isRequestArray && !pending || this.isDestroying || this.isDestroyed) {
|
|
3077
3511
|
return;
|
|
3078
3512
|
}
|
|
3079
|
-
|
|
3080
|
-
|
|
3513
|
+
|
|
3514
|
+
// first flush any staged changes
|
|
3515
|
+
if (pending) {
|
|
3516
|
+
sync(array, pending, this._set.get(array));
|
|
3517
|
+
this._pending.delete(array);
|
|
3518
|
+
}
|
|
3519
|
+
|
|
3520
|
+
// then pull new state if required
|
|
3521
|
+
if (isRequestArray) {
|
|
3522
|
+
const tag = array[ARRAY_SIGNAL];
|
|
3523
|
+
if (tag.reason === 'cache-sync') {
|
|
3524
|
+
tag.reason = null;
|
|
3525
|
+
const doc = this.store.cache.peek(array.identifier);
|
|
3526
|
+
macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
|
|
3527
|
+
if (!test) {
|
|
3528
|
+
throw new Error(`Expected to find a document for ${array.identifier.lid} but found none`);
|
|
3529
|
+
}
|
|
3530
|
+
})(doc) : {};
|
|
3531
|
+
const data = !('data' in doc) || !Array.isArray(doc.data) ? [] : doc.data;
|
|
3532
|
+
// TODO technically we should destroy here if
|
|
3533
|
+
// !('data' in doc) || !Array.isArray(doc.data)
|
|
3534
|
+
// is true.
|
|
3535
|
+
this.populateManagedArray(array, data, null);
|
|
3536
|
+
}
|
|
3537
|
+
}
|
|
3538
|
+
}
|
|
3539
|
+
mutate(mutation) {
|
|
3540
|
+
this.store.cache.mutate(mutation);
|
|
3081
3541
|
}
|
|
3082
3542
|
|
|
3083
3543
|
/**
|
|
@@ -3113,9 +3573,13 @@ class RecordArrayManager {
|
|
|
3113
3573
|
}
|
|
3114
3574
|
return array;
|
|
3115
3575
|
}
|
|
3116
|
-
|
|
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().WarpDrive.env.DEBUG) ? (test => {
|
|
3670
|
+
if (!test) {
|
|
3671
|
+
throw new Error(`The new state of the collection should not be using the same array reference as the original state.`);
|
|
3672
|
+
}
|
|
3673
|
+
})(source !== identifiers) : {};
|
|
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
|
|
3205
|
-
array.links = payload
|
|
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/store
|
|
@@ -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().WarpDrive.activeLogging.LOG_METRIC_COUNTS)) {
|
|
4082
|
+
if (getGlobalConfig().WarpDrive.debug.LOG_METRIC_COUNTS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_METRIC_COUNTS) {
|
|
4083
|
+
// @ts-expect-error adding to globalThis
|
|
4084
|
+
// eslint-disable-next-line
|
|
4085
|
+
globalThis.__WarpDriveMetricCountData = globalThis.__WarpDriveMetricCountData || {};
|
|
4086
|
+
|
|
4087
|
+
// @ts-expect-error adding to globalThis
|
|
4088
|
+
globalThis.getWarpDriveMetricCounts = () => {
|
|
4089
|
+
// @ts-expect-error
|
|
4090
|
+
// eslint-disable-next-line
|
|
4091
|
+
return structuredClone(globalThis.__WarpDriveMetricCountData);
|
|
4092
|
+
};
|
|
4093
|
+
|
|
4094
|
+
// @ts-expect-error adding to globalThis
|
|
4095
|
+
globalThis.resetWarpDriveMetricCounts = () => {
|
|
4096
|
+
// @ts-expect-error
|
|
4097
|
+
globalThis.__WarpDriveMetricCountData = {};
|
|
4098
|
+
};
|
|
4099
|
+
if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.__INTERNAL_LOG_NATIVE_MAP_SET_COUNTS)) {
|
|
4100
|
+
if (getGlobalConfig().WarpDrive.debug.__INTERNAL_LOG_NATIVE_MAP_SET_COUNTS || globalThis.getWarpDriveRuntimeConfig().debug.__INTERNAL_LOG_NATIVE_MAP_SET_COUNTS) {
|
|
4101
|
+
// @ts-expect-error adding to globalThis
|
|
4102
|
+
globalThis.__primitiveInstanceId = 0;
|
|
4103
|
+
function interceptAndLog(klassName, methodName) {
|
|
4104
|
+
const klass = globalThis[klassName];
|
|
4105
|
+
if (methodName === 'constructor') {
|
|
4106
|
+
const instantiationLabel = `new ${klassName}()`;
|
|
4107
|
+
// @ts-expect-error
|
|
4108
|
+
globalThis[klassName] = class extends klass {
|
|
4109
|
+
// @ts-expect-error
|
|
4110
|
+
constructor(...args) {
|
|
4111
|
+
// eslint-disable-next-line
|
|
4112
|
+
super(...args);
|
|
4113
|
+
// @ts-expect-error
|
|
4114
|
+
|
|
4115
|
+
const instanceId = globalThis.__primitiveInstanceId++;
|
|
4116
|
+
// @ts-expect-error
|
|
4117
|
+
// eslint-disable-next-line
|
|
4118
|
+
globalThis.__WarpDriveMetricCountData[instantiationLabel] =
|
|
4119
|
+
// @ts-expect-error
|
|
4120
|
+
// eslint-disable-next-line
|
|
4121
|
+
(globalThis.__WarpDriveMetricCountData[instantiationLabel] || 0) + 1;
|
|
4122
|
+
// @ts-expect-error
|
|
4123
|
+
this.instanceName = `${klassName}:${instanceId} - ${new Error().stack?.split('\n')[2]}`;
|
|
4124
|
+
}
|
|
4125
|
+
};
|
|
4126
|
+
} else {
|
|
4127
|
+
// @ts-expect-error
|
|
4128
|
+
// eslint-disable-next-line
|
|
4129
|
+
const original = klass.prototype[methodName];
|
|
4130
|
+
const logName = `${klassName}.${methodName}`;
|
|
4131
|
+
|
|
4132
|
+
// @ts-expect-error
|
|
4133
|
+
klass.prototype[methodName] = function (...args) {
|
|
4134
|
+
// @ts-expect-error
|
|
4135
|
+
// eslint-disable-next-line
|
|
4136
|
+
globalThis.__WarpDriveMetricCountData[logName] = (globalThis.__WarpDriveMetricCountData[logName] || 0) + 1;
|
|
4137
|
+
// @ts-expect-error
|
|
4138
|
+
const {
|
|
4139
|
+
instanceName
|
|
4140
|
+
} = this;
|
|
4141
|
+
if (!instanceName) {
|
|
4142
|
+
// @ts-expect-error
|
|
4143
|
+
const instanceId = globalThis.__primitiveInstanceId++;
|
|
4144
|
+
// @ts-expect-error
|
|
4145
|
+
this.instanceName = `${klassName}.${methodName}:${instanceId} - ${new Error().stack?.split('\n')[2]}`;
|
|
4146
|
+
}
|
|
4147
|
+
const instanceLogName = `${logName} (${instanceName})`;
|
|
4148
|
+
// @ts-expect-error
|
|
4149
|
+
// eslint-disable-next-line
|
|
4150
|
+
globalThis.__WarpDriveMetricCountData[instanceLogName] =
|
|
4151
|
+
// @ts-expect-error
|
|
4152
|
+
// eslint-disable-next-line
|
|
4153
|
+
(globalThis.__WarpDriveMetricCountData[instanceLogName] || 0) + 1;
|
|
4154
|
+
// eslint-disable-next-line
|
|
4155
|
+
return original.apply(this, args);
|
|
4156
|
+
};
|
|
4157
|
+
}
|
|
4158
|
+
}
|
|
4159
|
+
interceptAndLog('Set', 'constructor');
|
|
4160
|
+
interceptAndLog('Set', 'add');
|
|
4161
|
+
interceptAndLog('Set', 'delete');
|
|
4162
|
+
interceptAndLog('Set', 'has');
|
|
4163
|
+
interceptAndLog('Set', 'set');
|
|
4164
|
+
interceptAndLog('Set', 'get');
|
|
4165
|
+
interceptAndLog('Map', 'constructor');
|
|
4166
|
+
interceptAndLog('Map', 'set');
|
|
4167
|
+
interceptAndLog('Map', 'delete');
|
|
4168
|
+
interceptAndLog('Map', 'has');
|
|
4169
|
+
interceptAndLog('Map', 'add');
|
|
4170
|
+
interceptAndLog('Map', 'get');
|
|
4171
|
+
interceptAndLog('WeakSet', 'constructor');
|
|
4172
|
+
interceptAndLog('WeakSet', 'add');
|
|
4173
|
+
interceptAndLog('WeakSet', 'delete');
|
|
4174
|
+
interceptAndLog('WeakSet', 'has');
|
|
4175
|
+
interceptAndLog('WeakSet', 'set');
|
|
4176
|
+
interceptAndLog('WeakSet', 'get');
|
|
4177
|
+
interceptAndLog('WeakMap', 'constructor');
|
|
4178
|
+
interceptAndLog('WeakMap', 'set');
|
|
4179
|
+
interceptAndLog('WeakMap', 'delete');
|
|
4180
|
+
interceptAndLog('WeakMap', 'has');
|
|
4181
|
+
interceptAndLog('WeakMap', 'add');
|
|
4182
|
+
interceptAndLog('WeakMap', 'get');
|
|
4183
|
+
}
|
|
4184
|
+
}
|
|
4185
|
+
}
|
|
4186
|
+
}
|
|
4187
|
+
|
|
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
|
|
4232
|
+
const _BaseClass = macroCondition(dependencySatisfies('ember-source', '*')) ? macroCondition(getGlobalConfig().WarpDrive.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:deprecate-store-extends-ember-object',
|
|
3661
4252
|
until: '6.0',
|
|
3662
4253
|
for: 'ember-data',
|
|
4254
|
+
url: 'https://deprecations.emberjs.com/id/ember-data-deprecate-store-extends-ember-object',
|
|
3663
4255
|
since: {
|
|
3664
|
-
available: '
|
|
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/request/fetch';
|
|
3722
4314
|
*
|
|
3723
4315
|
* class extends Store {
|
|
3724
|
-
*
|
|
3725
|
-
*
|
|
3726
|
-
*
|
|
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,7 +4399,6 @@ 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
|
}
|
|
@@ -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();
|
|
@@ -3947,7 +4556,7 @@ 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;
|
|
@@ -3958,22 +4567,26 @@ class Store extends BaseClass {
|
|
|
3958
4567
|
opts.disableTestWaiter = typeof requestConfig.disableTestWaiter === 'boolean' ? requestConfig.disableTestWaiter : true;
|
|
3959
4568
|
}
|
|
3960
4569
|
}
|
|
3961
|
-
if (macroCondition(getGlobalConfig().WarpDrive.
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
|
|
3965
|
-
|
|
3966
|
-
|
|
4570
|
+
if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_REQUESTS)) {
|
|
4571
|
+
if (getGlobalConfig().WarpDrive.debug.LOG_REQUESTS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_REQUESTS) {
|
|
4572
|
+
let options;
|
|
4573
|
+
try {
|
|
4574
|
+
options = JSON.parse(JSON.stringify(requestConfig));
|
|
4575
|
+
} catch {
|
|
4576
|
+
options = requestConfig;
|
|
4577
|
+
}
|
|
4578
|
+
// eslint-disable-next-line no-console
|
|
4579
|
+
console.log(`request: [[START]] ${requestConfig.op && !requestConfig.url ? '(LEGACY) ' : ''}${requestConfig.op || '<unknown operation>'} ${requestConfig.url || '<empty url>'} ${requestConfig.method || '<empty method>'}`, options);
|
|
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.
|
|
3975
|
-
|
|
3976
|
-
|
|
4585
|
+
if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_REQUESTS)) {
|
|
4586
|
+
if (getGlobalConfig().WarpDrive.debug.LOG_REQUESTS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_REQUESTS) {
|
|
4587
|
+
// eslint-disable-next-line no-console
|
|
4588
|
+
console.log(`request: [[FINALIZE]] ${requestConfig.op && !requestConfig.url ? '(LEGACY) ' : ''}${requestConfig.op || '<unknown operation>'} ${requestConfig.url || '<empty url>'} ${requestConfig.method || '<empty method>'}`);
|
|
4589
|
+
}
|
|
3977
4590
|
}
|
|
3978
4591
|
// skip flush for legacy belongsTo
|
|
3979
4592
|
if (requestConfig.op === 'findBelongsTo' && !requestConfig.url) {
|
|
@@ -4179,9 +4792,8 @@ 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')
|
|
4183
|
-
|
|
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
|
|
@@ -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
|
-
|
|
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
|
```
|
|
@@ -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
|
-
|
|
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
|
```
|
|
@@ -5233,16 +5845,6 @@ class Store extends BaseClass {
|
|
|
5233
5845
|
if (macroCondition(getGlobalConfig().WarpDrive.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
|
}
|
|
@@ -5373,10 +5975,10 @@ if (macroCondition(getGlobalConfig().WarpDrive.deprecations.ENABLE_LEGACY_SCHEMA
|
|
|
5373
5975
|
})(this._schema) : {};
|
|
5374
5976
|
deprecate(`Use \`store.schema\` instead of \`store.getSchemaDefinitionService()\``, false, {
|
|
5375
5977
|
id: 'ember-data:schema-service-updates',
|
|
5376
|
-
until: '
|
|
5978
|
+
until: '6.0',
|
|
5377
5979
|
for: 'ember-data',
|
|
5378
5980
|
since: {
|
|
5379
|
-
available: '
|
|
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:schema-service-updates',
|
|
5388
|
-
until: '
|
|
5990
|
+
until: '6.0',
|
|
5389
5991
|
for: 'ember-data',
|
|
5390
5992
|
since: {
|
|
5391
|
-
available: '
|
|
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:schema-service-updates',
|
|
5400
|
-
until: '
|
|
6002
|
+
until: '6.0',
|
|
5401
6003
|
for: 'ember-data',
|
|
5402
6004
|
since: {
|
|
5403
|
-
available: '
|
|
6005
|
+
available: '4.13',
|
|
5404
6006
|
enabled: '5.4'
|
|
5405
6007
|
}
|
|
5406
6008
|
});
|
|
@@ -5463,9 +6065,9 @@ function normalizeProperties(store, identifier, properties) {
|
|
|
5463
6065
|
if (macroCondition(getGlobalConfig().WarpDrive.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
|
}
|
|
@@ -5493,250 +6095,208 @@ 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/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
|
|
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
|
-
*
|
|
5521
|
-
*
|
|
5522
|
-
*
|
|
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
|
-
*
|
|
5525
|
-
*
|
|
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/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
|
-
|
|
5528
|
-
|
|
5529
|
-
|
|
5530
|
-
|
|
5531
|
-
|
|
5532
|
-
|
|
5533
|
-
|
|
5534
|
-
|
|
5535
|
-
|
|
5536
|
-
|
|
5537
|
-
|
|
5538
|
-
|
|
5539
|
-
|
|
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
|
-
|
|
5576
|
-
|
|
5577
|
-
|
|
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
|
-
|
|
5583
|
-
|
|
5584
|
-
|
|
5585
|
-
|
|
5586
|
-
|
|
5587
|
-
|
|
5588
|
-
|
|
5589
|
-
|
|
5590
|
-
|
|
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
|
-
|
|
5602
|
-
|
|
5603
|
-
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
|
|
5607
|
-
|
|
5608
|
-
|
|
5609
|
-
|
|
5610
|
-
|
|
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');
|
|
6269
|
+
}
|
|
6270
|
+
store.requestManager._pending.set(context.id, promise);
|
|
6271
|
+
}
|
|
5611
6272
|
macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
|
|
5612
6273
|
if (!test) {
|
|
5613
|
-
throw new Error(`
|
|
6274
|
+
throw new Error(`Expected a peeked request to be present`);
|
|
5614
6275
|
}
|
|
5615
|
-
})(
|
|
5616
|
-
|
|
5617
|
-
|
|
5618
|
-
|
|
5619
|
-
|
|
5620
|
-
|
|
5621
|
-
|
|
5622
|
-
|
|
5623
|
-
|
|
5624
|
-
|
|
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;
|
|
5701
|
-
}
|
|
5702
|
-
if (this.meta !== undefined) {
|
|
5703
|
-
data.meta = this.meta;
|
|
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
|
-
|
|
6288
|
+
const result = shouldHydrate ? maybeUpdateUiObjects(store, context.request, {
|
|
6289
|
+
shouldHydrate,
|
|
6290
|
+
identifier
|
|
6291
|
+
}, peeked.content) : peeked.content;
|
|
6292
|
+
return result;
|
|
5706
6293
|
}
|
|
5707
|
-
}
|
|
5708
|
-
|
|
5709
|
-
defineSignal(Document.prototype, 'links');
|
|
5710
|
-
defineSignal(Document.prototype, 'errors');
|
|
5711
|
-
defineSignal(Document.prototype, 'meta');
|
|
5712
|
-
|
|
5713
|
-
/**
|
|
5714
|
-
* @module @ember-data/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) {
|
|
6299
|
+
if (!document || !options.shouldHydrate) {
|
|
5740
6300
|
macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
|
|
5741
6301
|
if (!test) {
|
|
5742
6302
|
throw new Error(`The CacheHandler expected response content but none was found`);
|
|
@@ -5744,113 +6304,105 @@ function maybeUpdateUiObjects(store, request, options, document, isFromCache) {
|
|
|
5744
6304
|
})(!options.shouldHydrate) : {};
|
|
5745
6305
|
return document;
|
|
5746
6306
|
}
|
|
5747
|
-
if (
|
|
5748
|
-
|
|
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
|
-
|
|
5768
|
-
|
|
5769
|
-
|
|
5770
|
-
|
|
5771
|
-
|
|
5772
|
-
|
|
5773
|
-
|
|
5774
|
-
|
|
5775
|
-
|
|
5776
|
-
|
|
5777
|
-
|
|
5778
|
-
|
|
5779
|
-
|
|
5780
|
-
|
|
5781
|
-
|
|
5782
|
-
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
|
|
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
|
-
|
|
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
|
|
6336
|
+
function handleFetchSuccess(store, context, options, document) {
|
|
5835
6337
|
const {
|
|
5836
|
-
|
|
5837
|
-
} =
|
|
5838
|
-
|
|
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
|
|
5841
|
-
|
|
5842
|
-
|
|
5843
|
-
|
|
5844
|
-
|
|
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
|
|
5847
|
-
|
|
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,
|
|
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;
|
|
@@ -5867,80 +6419,11 @@ function fetchContentAndHydrate(next, context, identifier, shouldFetch, shouldBa
|
|
|
5867
6419
|
}
|
|
5868
6420
|
if (store.lifetimes?.willRequest) {
|
|
5869
6421
|
store.lifetimes.willRequest(context.request, identifier, store);
|
|
5870
|
-
}
|
|
5871
|
-
const promise = next(context.request).then(document => {
|
|
5872
|
-
store
|
|
5873
|
-
|
|
5874
|
-
|
|
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();
|
|
5943
|
-
}
|
|
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;
|
|
@@ -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
|
-
|
|
5982
|
-
|
|
5983
|
-
|
|
5984
|
-
|
|
5985
|
-
|
|
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/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/store
|
|
6451
|
+
*/
|
|
6452
|
+
/**
|
|
6453
|
+
A `ManyArray` is a `MutableArray` that represents the contents of a has-many
|
|
6454
|
+
relationship.
|
|
6032
6455
|
|
|
6033
|
-
|
|
6034
|
-
|
|
6035
|
-
|
|
6456
|
+
The `ManyArray` is instantiated lazily the first time the relationship is
|
|
6457
|
+
requested.
|
|
6458
|
+
|
|
6459
|
+
This class is not intended to be directly instantiated by consuming applications.
|
|
6460
|
+
|
|
6461
|
+
### Inverses
|
|
6462
|
+
|
|
6463
|
+
Often, the relationships in Ember Data applications will have
|
|
6464
|
+
an inverse. For example, imagine the following models are
|
|
6465
|
+
defined:
|
|
6466
|
+
|
|
6467
|
+
```app/models/post.js
|
|
6468
|
+
import Model, { hasMany } from '@ember-data/model';
|
|
6469
|
+
|
|
6470
|
+
export default class PostModel extends Model {
|
|
6471
|
+
@hasMany('comment') comments;
|
|
6472
|
+
}
|
|
6473
|
+
```
|
|
6474
|
+
|
|
6475
|
+
```app/models/comment.js
|
|
6476
|
+
import Model, { belongsTo } from '@ember-data/model';
|
|
6477
|
+
|
|
6478
|
+
export default class CommentModel extends Model {
|
|
6479
|
+
@belongsTo('post') post;
|
|
6480
|
+
}
|
|
6481
|
+
```
|
|
6482
|
+
|
|
6483
|
+
If you created a new instance of `Post` and added
|
|
6484
|
+
a `Comment` record to its `comments` has-many
|
|
6485
|
+
relationship, you would expect the comment's `post`
|
|
6486
|
+
property to be set to the post that contained
|
|
6487
|
+
the has-many.
|
|
6488
|
+
|
|
6489
|
+
We call the record to which a relationship belongs-to the
|
|
6490
|
+
relationship's _owner_.
|
|
6491
|
+
|
|
6492
|
+
@class ManyArray
|
|
6493
|
+
@public
|
|
6494
|
+
*/
|
|
6495
|
+
class RelatedCollection extends IdentifierArray {
|
|
6496
|
+
/**
|
|
6497
|
+
The loading state of this array
|
|
6498
|
+
@property {Boolean} isLoaded
|
|
6499
|
+
@public
|
|
6500
|
+
*/
|
|
6501
|
+
|
|
6502
|
+
/**
|
|
6503
|
+
`true` if the relationship is polymorphic, `false` otherwise.
|
|
6504
|
+
@property {Boolean} isPolymorphic
|
|
6505
|
+
@private
|
|
6506
|
+
*/
|
|
6507
|
+
|
|
6508
|
+
/**
|
|
6509
|
+
Metadata associated with the request for async hasMany relationships.
|
|
6510
|
+
Example
|
|
6511
|
+
Given that the server returns the following JSON payload when fetching a
|
|
6512
|
+
hasMany relationship:
|
|
6513
|
+
```js
|
|
6514
|
+
{
|
|
6515
|
+
"comments": [{
|
|
6516
|
+
"id": 1,
|
|
6517
|
+
"comment": "This is the first comment",
|
|
6518
|
+
}, {
|
|
6519
|
+
// ...
|
|
6520
|
+
}],
|
|
6521
|
+
"meta": {
|
|
6522
|
+
"page": 1,
|
|
6523
|
+
"total": 5
|
|
6524
|
+
}
|
|
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
|
-
|
|
6039
|
-
|
|
6040
|
-
|
|
6041
|
-
|
|
6538
|
+
/**
|
|
6539
|
+
* Retrieve the links for this relationship
|
|
6540
|
+
*
|
|
6541
|
+
@property {Object | null} links
|
|
6542
|
+
@public
|
|
6543
|
+
*/
|
|
6544
|
+
|
|
6545
|
+
constructor(options) {
|
|
6546
|
+
super(options);
|
|
6547
|
+
this.isLoaded = options.isLoaded || false;
|
|
6548
|
+
this.isAsync = options.isAsync || false;
|
|
6549
|
+
this.isPolymorphic = options.isPolymorphic || false;
|
|
6550
|
+
this.identifier = options.identifier;
|
|
6551
|
+
this.key = options.key;
|
|
6552
|
+
}
|
|
6553
|
+
[MUTATE](target, receiver, prop, args, _SIGNAL) {
|
|
6554
|
+
switch (prop) {
|
|
6555
|
+
case 'length 0':
|
|
6556
|
+
{
|
|
6557
|
+
Reflect.set(target, 'length', 0);
|
|
6558
|
+
mutateReplaceRelatedRecords(this, [], _SIGNAL);
|
|
6559
|
+
return true;
|
|
6560
|
+
}
|
|
6561
|
+
case 'replace cell':
|
|
6562
|
+
{
|
|
6563
|
+
const [index, prior, value] = args;
|
|
6564
|
+
target[index] = value;
|
|
6565
|
+
mutateReplaceRelatedRecord(this, {
|
|
6566
|
+
value,
|
|
6567
|
+
prior,
|
|
6568
|
+
index
|
|
6569
|
+
}, _SIGNAL);
|
|
6570
|
+
return true;
|
|
6571
|
+
}
|
|
6572
|
+
case 'push':
|
|
6573
|
+
{
|
|
6574
|
+
const newValues = extractIdentifiersFromRecords(args);
|
|
6575
|
+
assertNoDuplicates(this, target, currentState => currentState.push(...newValues), `Cannot push duplicates to a hasMany's state.`);
|
|
6576
|
+
if (macroCondition(getGlobalConfig().WarpDrive.deprecations.DEPRECATE_MANY_ARRAY_DUPLICATES)) {
|
|
6577
|
+
// dedupe
|
|
6578
|
+
const seen = new Set(target);
|
|
6579
|
+
const unique = new Set();
|
|
6580
|
+
args.forEach(item => {
|
|
6581
|
+
const identifier = recordIdentifierFor(item);
|
|
6582
|
+
if (!seen.has(identifier)) {
|
|
6583
|
+
seen.add(identifier);
|
|
6584
|
+
unique.add(item);
|
|
6585
|
+
}
|
|
6586
|
+
});
|
|
6587
|
+
const newArgs = Array.from(unique);
|
|
6588
|
+
const result = Reflect.apply(target[prop], receiver, newArgs);
|
|
6589
|
+
if (newArgs.length) {
|
|
6590
|
+
mutateAddToRelatedRecords(this, {
|
|
6591
|
+
value: extractIdentifiersFromRecords(newArgs)
|
|
6592
|
+
}, _SIGNAL);
|
|
6593
|
+
}
|
|
6594
|
+
return result;
|
|
6595
|
+
}
|
|
6596
|
+
|
|
6597
|
+
// else, no dedupe, error on duplicates
|
|
6598
|
+
const result = Reflect.apply(target[prop], receiver, args);
|
|
6599
|
+
if (newValues.length) {
|
|
6600
|
+
mutateAddToRelatedRecords(this, {
|
|
6601
|
+
value: newValues
|
|
6602
|
+
}, _SIGNAL);
|
|
6603
|
+
}
|
|
6604
|
+
return result;
|
|
6605
|
+
}
|
|
6606
|
+
case 'pop':
|
|
6607
|
+
{
|
|
6608
|
+
const result = Reflect.apply(target[prop], receiver, args);
|
|
6609
|
+
if (result) {
|
|
6610
|
+
mutateRemoveFromRelatedRecords(this, {
|
|
6611
|
+
value: recordIdentifierFor(result)
|
|
6612
|
+
}, _SIGNAL);
|
|
6613
|
+
}
|
|
6614
|
+
return result;
|
|
6615
|
+
}
|
|
6616
|
+
case 'unshift':
|
|
6617
|
+
{
|
|
6618
|
+
const newValues = extractIdentifiersFromRecords(args);
|
|
6619
|
+
assertNoDuplicates(this, target, currentState => currentState.unshift(...newValues), `Cannot unshift duplicates to a hasMany's state.`);
|
|
6620
|
+
if (macroCondition(getGlobalConfig().WarpDrive.deprecations.DEPRECATE_MANY_ARRAY_DUPLICATES)) {
|
|
6621
|
+
// dedupe
|
|
6622
|
+
const seen = new Set(target);
|
|
6623
|
+
const unique = new Set();
|
|
6624
|
+
args.forEach(item => {
|
|
6625
|
+
const identifier = recordIdentifierFor(item);
|
|
6626
|
+
if (!seen.has(identifier)) {
|
|
6627
|
+
seen.add(identifier);
|
|
6628
|
+
unique.add(item);
|
|
6629
|
+
}
|
|
6630
|
+
});
|
|
6631
|
+
const newArgs = Array.from(unique);
|
|
6632
|
+
const result = Reflect.apply(target[prop], receiver, newArgs);
|
|
6633
|
+
if (newArgs.length) {
|
|
6634
|
+
mutateAddToRelatedRecords(this, {
|
|
6635
|
+
value: extractIdentifiersFromRecords(newArgs),
|
|
6636
|
+
index: 0
|
|
6637
|
+
}, _SIGNAL);
|
|
6638
|
+
}
|
|
6639
|
+
return result;
|
|
6640
|
+
}
|
|
6641
|
+
|
|
6642
|
+
// else, no dedupe, error on duplicates
|
|
6643
|
+
const result = Reflect.apply(target[prop], receiver, args);
|
|
6644
|
+
if (newValues.length) {
|
|
6645
|
+
mutateAddToRelatedRecords(this, {
|
|
6646
|
+
value: newValues,
|
|
6647
|
+
index: 0
|
|
6648
|
+
}, _SIGNAL);
|
|
6649
|
+
}
|
|
6650
|
+
return result;
|
|
6651
|
+
}
|
|
6652
|
+
case 'shift':
|
|
6653
|
+
{
|
|
6654
|
+
const result = Reflect.apply(target[prop], receiver, args);
|
|
6655
|
+
if (result) {
|
|
6656
|
+
mutateRemoveFromRelatedRecords(this, {
|
|
6657
|
+
value: recordIdentifierFor(result),
|
|
6658
|
+
index: 0
|
|
6659
|
+
}, _SIGNAL);
|
|
6660
|
+
}
|
|
6661
|
+
return result;
|
|
6662
|
+
}
|
|
6663
|
+
case 'sort':
|
|
6664
|
+
{
|
|
6665
|
+
const result = Reflect.apply(target[prop], receiver, args);
|
|
6666
|
+
mutateSortRelatedRecords(this, result.map(recordIdentifierFor), _SIGNAL);
|
|
6667
|
+
return result;
|
|
6668
|
+
}
|
|
6669
|
+
case 'splice':
|
|
6670
|
+
{
|
|
6671
|
+
const [start, deleteCount, ...adds] = args;
|
|
6672
|
+
|
|
6673
|
+
// detect a full replace
|
|
6674
|
+
if (start === 0 && deleteCount === this[SOURCE].length) {
|
|
6675
|
+
const newValues = extractIdentifiersFromRecords(adds);
|
|
6676
|
+
assertNoDuplicates(this, target, currentState => currentState.splice(start, deleteCount, ...newValues), `Cannot replace a hasMany's state with a new state that contains duplicates.`);
|
|
6677
|
+
if (macroCondition(getGlobalConfig().WarpDrive.deprecations.DEPRECATE_MANY_ARRAY_DUPLICATES)) {
|
|
6678
|
+
// dedupe
|
|
6679
|
+
const current = new Set(adds);
|
|
6680
|
+
const unique = Array.from(current);
|
|
6681
|
+
const newArgs = [start, deleteCount].concat(unique);
|
|
6682
|
+
const result = Reflect.apply(target[prop], receiver, newArgs);
|
|
6683
|
+
mutateReplaceRelatedRecords(this, extractIdentifiersFromRecords(unique), _SIGNAL);
|
|
6684
|
+
return result;
|
|
6685
|
+
}
|
|
6686
|
+
|
|
6687
|
+
// else, no dedupe, error on duplicates
|
|
6688
|
+
const result = Reflect.apply(target[prop], receiver, args);
|
|
6689
|
+
mutateReplaceRelatedRecords(this, newValues, _SIGNAL);
|
|
6690
|
+
return result;
|
|
6691
|
+
}
|
|
6692
|
+
const newValues = extractIdentifiersFromRecords(adds);
|
|
6693
|
+
assertNoDuplicates(this, target, currentState => currentState.splice(start, deleteCount, ...newValues), `Cannot splice a hasMany's state with a new state that contains duplicates.`);
|
|
6694
|
+
if (macroCondition(getGlobalConfig().WarpDrive.deprecations.DEPRECATE_MANY_ARRAY_DUPLICATES)) {
|
|
6695
|
+
// dedupe
|
|
6696
|
+
const currentState = target.slice();
|
|
6697
|
+
currentState.splice(start, deleteCount);
|
|
6698
|
+
const seen = new Set(currentState);
|
|
6699
|
+
const unique = [];
|
|
6700
|
+
adds.forEach(item => {
|
|
6701
|
+
const identifier = recordIdentifierFor(item);
|
|
6702
|
+
if (!seen.has(identifier)) {
|
|
6703
|
+
seen.add(identifier);
|
|
6704
|
+
unique.push(item);
|
|
6705
|
+
}
|
|
6706
|
+
});
|
|
6707
|
+
const newArgs = [start, deleteCount, ...unique];
|
|
6708
|
+
const result = Reflect.apply(target[prop], receiver, newArgs);
|
|
6709
|
+
if (deleteCount > 0) {
|
|
6710
|
+
mutateRemoveFromRelatedRecords(this, {
|
|
6711
|
+
value: result.map(recordIdentifierFor),
|
|
6712
|
+
index: start
|
|
6713
|
+
}, _SIGNAL);
|
|
6714
|
+
}
|
|
6715
|
+
if (unique.length > 0) {
|
|
6716
|
+
mutateAddToRelatedRecords(this, {
|
|
6717
|
+
value: extractIdentifiersFromRecords(unique),
|
|
6718
|
+
index: start
|
|
6719
|
+
}, _SIGNAL);
|
|
6720
|
+
}
|
|
6721
|
+
return result;
|
|
6722
|
+
}
|
|
6723
|
+
|
|
6724
|
+
// else, no dedupe, error on duplicates
|
|
6725
|
+
const result = Reflect.apply(target[prop], receiver, args);
|
|
6726
|
+
if (deleteCount > 0) {
|
|
6727
|
+
mutateRemoveFromRelatedRecords(this, {
|
|
6728
|
+
value: result.map(recordIdentifierFor),
|
|
6729
|
+
index: start
|
|
6730
|
+
}, _SIGNAL);
|
|
6731
|
+
}
|
|
6732
|
+
if (newValues.length > 0) {
|
|
6733
|
+
mutateAddToRelatedRecords(this, {
|
|
6734
|
+
value: newValues,
|
|
6735
|
+
index: start
|
|
6736
|
+
}, _SIGNAL);
|
|
6737
|
+
}
|
|
6738
|
+
return result;
|
|
6739
|
+
}
|
|
6740
|
+
default:
|
|
6741
|
+
macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
|
|
6742
|
+
{
|
|
6743
|
+
throw new Error(`unable to convert ${prop} into a transaction that updates the cache state for this record array`);
|
|
6744
|
+
}
|
|
6745
|
+
})() : {};
|
|
6042
6746
|
}
|
|
6747
|
+
}
|
|
6748
|
+
notify() {
|
|
6749
|
+
const signal = this[ARRAY_SIGNAL];
|
|
6750
|
+
signal.shouldReset = true;
|
|
6751
|
+
notifyArray(this);
|
|
6752
|
+
}
|
|
6753
|
+
|
|
6754
|
+
/**
|
|
6755
|
+
Reloads all of the records in the manyArray. If the manyArray
|
|
6756
|
+
holds a relationship that was originally fetched using a links url
|
|
6757
|
+
EmberData will revisit the original links url to repopulate the
|
|
6758
|
+
relationship.
|
|
6759
|
+
If the ManyArray holds the result of a `store.query()` reload will
|
|
6760
|
+
re-run the original query.
|
|
6761
|
+
Example
|
|
6762
|
+
```javascript
|
|
6763
|
+
let user = store.peekRecord('user', '1')
|
|
6764
|
+
await login(user);
|
|
6765
|
+
let permissions = await user.permissions;
|
|
6766
|
+
await permissions.reload();
|
|
6767
|
+
```
|
|
6768
|
+
@method reload
|
|
6769
|
+
@public
|
|
6770
|
+
*/
|
|
6771
|
+
reload(options) {
|
|
6043
6772
|
macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
|
|
6044
6773
|
if (!test) {
|
|
6045
|
-
throw new Error(`Expected
|
|
6774
|
+
throw new Error(`Expected the manager for ManyArray to implement reloadHasMany`);
|
|
6046
6775
|
}
|
|
6047
|
-
})(
|
|
6048
|
-
|
|
6049
|
-
|
|
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;
|
|
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);
|
|
6064
6779
|
}
|
|
6065
|
-
|
|
6066
|
-
|
|
6067
|
-
|
|
6068
|
-
|
|
6069
|
-
|
|
6070
|
-
|
|
6071
|
-
|
|
6780
|
+
|
|
6781
|
+
/**
|
|
6782
|
+
Saves all of the records in the `ManyArray`.
|
|
6783
|
+
Note: this API can only be used in legacy mode with a configured Adapter.
|
|
6784
|
+
Example
|
|
6785
|
+
```javascript
|
|
6786
|
+
const { content: { data: inbox } } = await store.request(findRecord({ type: 'inbox', id: '1' }));
|
|
6787
|
+
let messages = await inbox.messages;
|
|
6788
|
+
messages.forEach((message) => {
|
|
6789
|
+
message.isRead = true;
|
|
6790
|
+
});
|
|
6791
|
+
messages.save();
|
|
6792
|
+
```
|
|
6793
|
+
@method save
|
|
6794
|
+
@public
|
|
6795
|
+
@return {PromiseArray} promise
|
|
6796
|
+
*/
|
|
6797
|
+
|
|
6798
|
+
/**
|
|
6799
|
+
Create a child record within the owner
|
|
6800
|
+
@method createRecord
|
|
6801
|
+
@public
|
|
6802
|
+
@param {Object} hash
|
|
6803
|
+
@return {Model} record
|
|
6804
|
+
*/
|
|
6805
|
+
createRecord(hash) {
|
|
6806
|
+
const {
|
|
6807
|
+
store
|
|
6808
|
+
} = this;
|
|
6809
|
+
macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
|
|
6810
|
+
if (!test) {
|
|
6811
|
+
throw new Error(`Expected modelName to be set`);
|
|
6812
|
+
}
|
|
6813
|
+
})(this.modelName) : {};
|
|
6814
|
+
const record = store.createRecord(this.modelName, hash);
|
|
6815
|
+
this.push(record);
|
|
6816
|
+
return record;
|
|
6072
6817
|
}
|
|
6073
|
-
|
|
6074
|
-
|
|
6818
|
+
destroy() {
|
|
6819
|
+
super.destroy(false);
|
|
6075
6820
|
}
|
|
6076
6821
|
}
|
|
6077
|
-
|
|
6078
|
-
|
|
6079
|
-
|
|
6080
|
-
|
|
6081
|
-
|
|
6082
|
-
|
|
6083
|
-
|
|
6084
|
-
|
|
6085
|
-
|
|
6086
|
-
|
|
6822
|
+
RelatedCollection.prototype.isAsync = false;
|
|
6823
|
+
RelatedCollection.prototype.isPolymorphic = false;
|
|
6824
|
+
RelatedCollection.prototype.identifier = null;
|
|
6825
|
+
RelatedCollection.prototype.cache = null;
|
|
6826
|
+
RelatedCollection.prototype._inverseIsAsync = false;
|
|
6827
|
+
RelatedCollection.prototype.key = '';
|
|
6828
|
+
RelatedCollection.prototype.DEPRECATED_CLASS_NAME = 'ManyArray';
|
|
6829
|
+
function assertRecordPassedToHasMany(record) {
|
|
6830
|
+
macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
|
|
6831
|
+
if (!test) {
|
|
6832
|
+
throw new Error(`All elements of a hasMany relationship must be instances of Model, you passed ${typeof record}`);
|
|
6833
|
+
}
|
|
6834
|
+
})(function () {
|
|
6835
|
+
try {
|
|
6836
|
+
recordIdentifierFor(record);
|
|
6837
|
+
return true;
|
|
6838
|
+
} catch {
|
|
6839
|
+
return false;
|
|
6840
|
+
}
|
|
6841
|
+
}()) : {};
|
|
6842
|
+
}
|
|
6843
|
+
function extractIdentifiersFromRecords(records) {
|
|
6844
|
+
return records.map(extractIdentifierFromRecord);
|
|
6845
|
+
}
|
|
6846
|
+
function extractIdentifierFromRecord(recordOrPromiseRecord) {
|
|
6847
|
+
assertRecordPassedToHasMany(recordOrPromiseRecord);
|
|
6848
|
+
return recordIdentifierFor(recordOrPromiseRecord);
|
|
6849
|
+
}
|
|
6850
|
+
function assertNoDuplicates(collection, target, callback, reason) {
|
|
6851
|
+
const state = target.slice();
|
|
6852
|
+
callback(state);
|
|
6853
|
+
if (state.length !== new Set(state).size) {
|
|
6854
|
+
const duplicates = state.filter((currentValue, currentIndex) => state.indexOf(currentValue) !== currentIndex);
|
|
6855
|
+
if (macroCondition(getGlobalConfig().WarpDrive.deprecations.DEPRECATE_MANY_ARRAY_DUPLICATES)) {
|
|
6856
|
+
deprecate(`${reason} This behavior is deprecated. Found duplicates for the following records within the new state provided to \`<${collection.identifier.type}:${collection.identifier.id || collection.identifier.lid}>.${collection.key}\`\n\t- ${Array.from(new Set(duplicates)).map(r => isStableIdentifier(r) ? r.lid : recordIdentifierFor(r).lid).sort((a, b) => a.localeCompare(b)).join('\n\t- ')}`, false, {
|
|
6857
|
+
id: 'ember-data:deprecate-many-array-duplicates',
|
|
6858
|
+
for: 'ember-data',
|
|
6859
|
+
until: '6.0',
|
|
6860
|
+
since: {
|
|
6861
|
+
enabled: '5.3',
|
|
6862
|
+
available: '4.13'
|
|
6863
|
+
}
|
|
6864
|
+
});
|
|
6865
|
+
} else {
|
|
6866
|
+
throw new Error(`${reason} Found duplicates for the following records within the new state provided to \`<${collection.identifier.type}:${collection.identifier.id || collection.identifier.lid}>.${collection.key}\`\n\t- ${Array.from(new Set(duplicates)).map(r => isStableIdentifier(r) ? r.lid : recordIdentifierFor(r).lid).sort((a, b) => a.localeCompare(b)).join('\n\t- ')}`);
|
|
6867
|
+
}
|
|
6087
6868
|
}
|
|
6088
|
-
return document.response?.status !== 204;
|
|
6089
6869
|
}
|
|
6090
|
-
|
|
6870
|
+
function mutateAddToRelatedRecords(collection, operationInfo, _SIGNAL) {
|
|
6871
|
+
mutate(collection, {
|
|
6872
|
+
op: 'add',
|
|
6873
|
+
record: collection.identifier,
|
|
6874
|
+
field: collection.key,
|
|
6875
|
+
...operationInfo
|
|
6876
|
+
}, _SIGNAL);
|
|
6877
|
+
}
|
|
6878
|
+
function mutateRemoveFromRelatedRecords(collection, operationInfo, _SIGNAL) {
|
|
6879
|
+
mutate(collection, {
|
|
6880
|
+
op: 'remove',
|
|
6881
|
+
record: collection.identifier,
|
|
6882
|
+
field: collection.key,
|
|
6883
|
+
...operationInfo
|
|
6884
|
+
}, _SIGNAL);
|
|
6885
|
+
}
|
|
6886
|
+
function mutateReplaceRelatedRecord(collection, operationInfo, _SIGNAL) {
|
|
6887
|
+
mutate(collection, {
|
|
6888
|
+
op: 'replaceRelatedRecord',
|
|
6889
|
+
record: collection.identifier,
|
|
6890
|
+
field: collection.key,
|
|
6891
|
+
...operationInfo
|
|
6892
|
+
}, _SIGNAL);
|
|
6893
|
+
}
|
|
6894
|
+
function mutateReplaceRelatedRecords(collection, value, _SIGNAL) {
|
|
6895
|
+
mutate(collection, {
|
|
6896
|
+
op: 'replaceRelatedRecords',
|
|
6897
|
+
record: collection.identifier,
|
|
6898
|
+
field: collection.key,
|
|
6899
|
+
value
|
|
6900
|
+
}, _SIGNAL);
|
|
6901
|
+
}
|
|
6902
|
+
function mutateSortRelatedRecords(collection, value, _SIGNAL) {
|
|
6903
|
+
mutate(collection, {
|
|
6904
|
+
op: 'sortRelatedRecords',
|
|
6905
|
+
record: collection.identifier,
|
|
6906
|
+
field: collection.key,
|
|
6907
|
+
value
|
|
6908
|
+
}, _SIGNAL);
|
|
6909
|
+
}
|
|
6910
|
+
function mutate(collection, mutation, _SIGNAL) {
|
|
6911
|
+
macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
|
|
6912
|
+
if (!test) {
|
|
6913
|
+
throw new Error(`Expected the manager for ManyArray to implement mutate`);
|
|
6914
|
+
}
|
|
6915
|
+
})(typeof collection._manager.mutate === 'function') : {};
|
|
6916
|
+
collection._manager.mutate(mutation);
|
|
6917
|
+
addToTransaction(_SIGNAL);
|
|
6918
|
+
}
|
|
6919
|
+
export { ARRAY_SIGNAL as A, CacheHandler as C, IdentifierArray as I, MUTATE as M, RecordArrayManager as R, Store as S, _clearCaches as _, setIdentifierGenerationMethod as a, setIdentifierUpdateMethod as b, setIdentifierForgetMethod as c, setIdentifierResetMethod as d, setKeyInfoForResource as e, isDocumentIdentifier as f, constructResource as g, coerceId as h, isStableIdentifier as i, ensureStringId as j, Collection as k, SOURCE as l, fastPush as m, notifyArray as n, removeRecordDataFor as o, peekCache as p, setRecordIdentifier as q, recordIdentifierFor as r, storeFor as s, StoreMap as t, setCacheFor as u, normalizeModelName as v, RelatedCollection as w, log as x, logGroup as y };
|