@ember-data/model 4.8.0-alpha.5 → 4.9.0-alpha.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.
@@ -0,0 +1,54 @@
1
+ import { deprecate } from '@ember/debug';
2
+
3
+ import { resolve } from 'rsvp';
4
+
5
+ import { PromiseObject } from './promise-proxy-base';
6
+
7
+ function promiseObject<T>(promise: Promise<T>): PromiseObject<T> {
8
+ return PromiseObject.create({
9
+ promise: resolve(promise),
10
+ }) as PromiseObject<T>;
11
+ }
12
+
13
+ // constructor is accessed in some internals but not including it in the copyright for the deprecation
14
+ const ALLOWABLE_METHODS = ['constructor', 'then', 'catch', 'finally'];
15
+ const PROXIED_OBJECT_PROPS = ['content', 'isPending', 'isSettled', 'isRejected', 'isFulfilled', 'promise', 'reason'];
16
+
17
+ export function deprecatedPromiseObject<T>(promise: Promise<T>): PromiseObject<T> {
18
+ const promiseObjectProxy: PromiseObject<T> = promiseObject(promise);
19
+ const handler = {
20
+ get(target: object, prop: string, receiver?: object): unknown {
21
+ if (typeof prop === 'symbol') {
22
+ return Reflect.get(target, prop, receiver);
23
+ }
24
+ if (!ALLOWABLE_METHODS.includes(prop)) {
25
+ deprecate(
26
+ `Accessing ${prop} is deprecated. The return type is being changed fomr PromiseObjectProxy to a Promise. The only available methods to access on this promise are .then, .catch and .finally`,
27
+ false,
28
+ {
29
+ id: 'ember-data:model-save-promise',
30
+ until: '5.0',
31
+ for: '@ember-data/store',
32
+ since: {
33
+ available: '4.4',
34
+ enabled: '4.4',
35
+ },
36
+ }
37
+ );
38
+ }
39
+
40
+ const value: unknown = target[prop];
41
+ if (value && typeof value === 'function' && typeof value.bind === 'function') {
42
+ return value.bind(target);
43
+ }
44
+
45
+ if (PROXIED_OBJECT_PROPS.includes(prop)) {
46
+ return value;
47
+ }
48
+
49
+ return undefined;
50
+ },
51
+ };
52
+
53
+ return new Proxy(promiseObjectProxy, handler);
54
+ }
@@ -260,8 +260,10 @@ function hasMany(type, options) {
260
260
  }
261
261
  }
262
262
  const support = lookupLegacySupport(this);
263
+ const manyArray = support.getManyArray(key);
264
+ assert(`You must pass an array of records to set a hasMany relationship`, Array.isArray(records));
263
265
  this.store._join(() => {
264
- support.setDirtyHasMany(key, records);
266
+ manyArray.splice(0, manyArray.length, ...records);
265
267
  });
266
268
 
267
269
  return support.getHasMany(key);
@@ -1,23 +1,21 @@
1
- import { assert } from '@ember/debug';
1
+ import { assert, deprecate } from '@ember/debug';
2
2
  import { DEBUG } from '@glimmer/env';
3
3
 
4
4
  import { importSync } from '@embroider/macros';
5
5
  import { all, resolve } from 'rsvp';
6
6
 
7
7
  import { HAS_RECORD_DATA_PACKAGE } from '@ember-data/private-build-infra';
8
+ import { DEPRECATE_PROMISE_PROXIES } from '@ember-data/private-build-infra/deprecations';
8
9
  import type { UpgradedMeta } from '@ember-data/record-data/-private/graph/-edge-definition';
10
+ import type { LocalRelationshipOperation } from '@ember-data/record-data/-private/graph/-operations';
9
11
  import type { ImplicitRelationship } from '@ember-data/record-data/-private/graph/index';
10
12
  import type BelongsToRelationship from '@ember-data/record-data/-private/relationships/state/belongs-to';
11
13
  import type ManyRelationship from '@ember-data/record-data/-private/relationships/state/has-many';
12
14
  import type Store from '@ember-data/store';
13
- import { recordIdentifierFor, storeFor } from '@ember-data/store/-private';
14
- import { IdentifierCache } from '@ember-data/store/-private/caches/identifier-cache';
15
+ import { fastPush, isStableIdentifier, recordIdentifierFor, SOURCE, storeFor } from '@ember-data/store/-private';
16
+ import type { NonSingletonRecordDataManager } from '@ember-data/store/-private/managers/record-data-manager';
15
17
  import type { DSModel } from '@ember-data/types/q/ds-model';
16
- import {
17
- CollectionResourceRelationship,
18
- ResourceIdentifierObject,
19
- SingleResourceRelationship,
20
- } from '@ember-data/types/q/ember-data-json-api';
18
+ import { CollectionResourceRelationship, SingleResourceRelationship } from '@ember-data/types/q/ember-data-json-api';
21
19
  import type { StableRecordIdentifier } from '@ember-data/types/q/identifier';
22
20
  import type { RecordData } from '@ember-data/types/q/record-data';
23
21
  import type { JsonApiRelationship } from '@ember-data/types/q/record-data-json-api';
@@ -27,8 +25,7 @@ import type { Dict } from '@ember-data/types/q/utils';
27
25
 
28
26
  import { _findBelongsTo, _findHasMany } from './legacy-data-fetch';
29
27
  import { assertIdentifierHasId } from './legacy-data-utils';
30
- import type { ManyArrayCreateArgs } from './many-array';
31
- import ManyArray from './many-array';
28
+ import RelatedCollection from './many-array';
32
29
  import type { BelongsToProxyCreateArgs, BelongsToProxyMeta } from './promise-belongs-to';
33
30
  import PromiseBelongsTo from './promise-belongs-to';
34
31
  import type { HasManyProxyCreateArgs } from './promise-many-array';
@@ -36,7 +33,6 @@ import PromiseManyArray from './promise-many-array';
36
33
  import BelongsToReference from './references/belongs-to';
37
34
  import HasManyReference from './references/has-many';
38
35
 
39
- type ManyArrayFactory = { create(args: ManyArrayCreateArgs): ManyArray };
40
36
  type PromiseBelongsToFactory = { create(args: BelongsToProxyCreateArgs): PromiseBelongsTo };
41
37
 
42
38
  export class LegacySupport {
@@ -45,8 +41,8 @@ export class LegacySupport {
45
41
  declare recordData: RecordData;
46
42
  declare references: Dict<BelongsToReference | HasManyReference>;
47
43
  declare identifier: StableRecordIdentifier;
48
- declare _manyArrayCache: Dict<ManyArray>;
49
- declare _relationshipPromisesCache: Dict<Promise<ManyArray | RecordInstance>>;
44
+ declare _manyArrayCache: Dict<RelatedCollection>;
45
+ declare _relationshipPromisesCache: Dict<Promise<RelatedCollection | RecordInstance>>;
50
46
  declare _relationshipProxyCache: Dict<PromiseManyArray | PromiseBelongsTo>;
51
47
 
52
48
  declare isDestroying: boolean;
@@ -58,12 +54,38 @@ export class LegacySupport {
58
54
  this.identifier = recordIdentifierFor(record);
59
55
  this.recordData = this.store._instanceCache.getRecordData(this.identifier);
60
56
 
61
- this._manyArrayCache = Object.create(null) as Dict<ManyArray>;
62
- this._relationshipPromisesCache = Object.create(null) as Dict<Promise<ManyArray | RecordInstance>>;
57
+ this._manyArrayCache = Object.create(null) as Dict<RelatedCollection>;
58
+ this._relationshipPromisesCache = Object.create(null) as Dict<Promise<RelatedCollection | RecordInstance>>;
63
59
  this._relationshipProxyCache = Object.create(null) as Dict<PromiseManyArray | PromiseBelongsTo>;
64
60
  this.references = Object.create(null) as Dict<BelongsToReference>;
65
61
  }
66
62
 
63
+ _syncArray(array: RelatedCollection) {
64
+ // It’s possible the parent side of the relationship may have been destroyed by this point
65
+ if (this.isDestroyed || this.isDestroying) {
66
+ return;
67
+ }
68
+ const currentState = array[SOURCE];
69
+ const identifier = this.identifier;
70
+
71
+ let [identifiers, jsonApi] = this._getCurrentState(identifier, array.key);
72
+
73
+ if (jsonApi.meta) {
74
+ array.meta = jsonApi.meta;
75
+ }
76
+
77
+ if (jsonApi.links) {
78
+ array.links = jsonApi.links;
79
+ }
80
+
81
+ currentState.length = 0;
82
+ fastPush(currentState, identifiers);
83
+ }
84
+
85
+ updateCache(operation: LocalRelationshipOperation): void {
86
+ this.recordData.update(operation);
87
+ }
88
+
67
89
  _findBelongsTo(
68
90
  key: string,
69
91
  resource: SingleResourceRelationship,
@@ -104,8 +126,8 @@ export class LegacySupport {
104
126
  getBelongsTo(key: string, options?: FindOptions): PromiseBelongsTo | RecordInstance | null {
105
127
  const { identifier, recordData } = this;
106
128
  let resource = recordData.getRelationship(this.identifier, key) as SingleResourceRelationship;
107
- let relatedIdentifier =
108
- resource && resource.data ? this.store.identifierCache.getOrCreateRecordIdentifier(resource.data) : null;
129
+ let relatedIdentifier = resource && resource.data ? resource.data : null;
130
+ assert(`Expected a stable identifier`, !relatedIdentifier || isStableIdentifier(relatedIdentifier));
109
131
 
110
132
  const store = this.store;
111
133
  const graphFor = (
@@ -128,10 +150,11 @@ export class LegacySupport {
128
150
  }
129
151
 
130
152
  let promise = this._findBelongsTo(key, resource, relationship, options);
153
+ const isLoaded = relatedIdentifier && store._instanceCache.recordIsLoaded(relatedIdentifier);
131
154
 
132
155
  return this._updatePromiseProxyFor('belongsTo', key, {
133
156
  promise,
134
- content: relatedIdentifier ? store._instanceCache.getRecord(relatedIdentifier) : null,
157
+ content: isLoaded ? store._instanceCache.getRecord(relatedIdentifier!) : null,
135
158
  _belongsToState,
136
159
  });
137
160
  } else {
@@ -151,53 +174,100 @@ export class LegacySupport {
151
174
  }
152
175
 
153
176
  setDirtyBelongsTo(key: string, value: RecordInstance | null) {
154
- return this.recordData.setBelongsTo(this.identifier, key, extractIdentifierFromRecord(value));
177
+ return this.recordData.update(
178
+ {
179
+ op: 'replaceRelatedRecord',
180
+ record: this.identifier,
181
+ field: key,
182
+ value: extractIdentifierFromRecord(value),
183
+ },
184
+ // @ts-expect-error
185
+ true
186
+ );
155
187
  }
156
188
 
157
- getManyArray(key: string, definition?: UpgradedMeta): ManyArray {
158
- assert('hasMany only works with the @ember-data/record-data package', HAS_RECORD_DATA_PACKAGE);
159
- let manyArray: ManyArray | undefined = this._manyArrayCache[key];
160
- if (!definition) {
161
- const graphFor = (
162
- importSync('@ember-data/record-data/-private') as typeof import('@ember-data/record-data/-private')
163
- ).graphFor;
164
- definition = graphFor(this.store).get(this.identifier, key).definition;
189
+ _getCurrentState(
190
+ identifier: StableRecordIdentifier,
191
+ field: string
192
+ ): [StableRecordIdentifier[], CollectionResourceRelationship] {
193
+ let jsonApi = (this.recordData as NonSingletonRecordDataManager).getRelationship(
194
+ identifier,
195
+ field,
196
+ true
197
+ ) as CollectionResourceRelationship;
198
+ const cache = this.store._instanceCache;
199
+ let identifiers: StableRecordIdentifier[] = [];
200
+ if (jsonApi.data) {
201
+ for (let i = 0; i < jsonApi.data.length; i++) {
202
+ const identifier = jsonApi.data[i];
203
+ assert(`Expected a stable identifier`, isStableIdentifier(identifier));
204
+ if (cache.recordIsLoaded(identifier, true)) {
205
+ identifiers.push(identifier);
206
+ }
207
+ }
165
208
  }
166
209
 
167
- if (!manyArray) {
168
- manyArray = (ManyArray as unknown as ManyArrayFactory).create({
169
- store: this.store,
170
- type: this.store.modelFor(definition.type),
171
- identifier: this.identifier,
172
- recordData: this.recordData,
173
- key,
174
- isPolymorphic: definition.isPolymorphic,
175
- isAsync: definition.isAsync,
176
- _inverseIsAsync: definition.inverseIsAsync,
177
- legacySupport: this,
178
- isLoaded: !definition.isAsync,
179
- });
180
- this._manyArrayCache[key] = manyArray;
181
- }
210
+ return [identifiers, jsonApi];
211
+ }
212
+
213
+ getManyArray(key: string, definition?: UpgradedMeta): RelatedCollection {
214
+ if (HAS_RECORD_DATA_PACKAGE) {
215
+ let manyArray: RelatedCollection | undefined = this._manyArrayCache[key];
216
+ if (!definition) {
217
+ const graphFor = (
218
+ importSync('@ember-data/record-data/-private') as typeof import('@ember-data/record-data/-private')
219
+ ).graphFor;
220
+ definition = graphFor(this.store).get(this.identifier, key).definition;
221
+ }
182
222
 
183
- return manyArray;
223
+ if (!manyArray) {
224
+ const [identifiers, doc] = this._getCurrentState(this.identifier, key);
225
+
226
+ manyArray = new RelatedCollection({
227
+ store: this.store,
228
+ type: definition.type,
229
+ identifier: this.identifier,
230
+ recordData: this.recordData,
231
+ identifiers,
232
+ key,
233
+ meta: doc.meta || null,
234
+ links: doc.links || null,
235
+ isPolymorphic: definition.isPolymorphic,
236
+ isAsync: definition.isAsync,
237
+ _inverseIsAsync: definition.inverseIsAsync,
238
+ manager: this,
239
+ isLoaded: !definition.isAsync,
240
+ allowMutation: true,
241
+ });
242
+ this._manyArrayCache[key] = manyArray;
243
+ }
244
+
245
+ return manyArray;
246
+ }
247
+ assert('hasMany only works with the @ember-data/record-data package');
184
248
  }
185
249
 
186
250
  fetchAsyncHasMany(
187
251
  key: string,
188
252
  relationship: ManyRelationship,
189
- manyArray: ManyArray,
253
+ manyArray: RelatedCollection,
190
254
  options?: FindOptions
191
- ): Promise<ManyArray> {
255
+ ): Promise<RelatedCollection> {
192
256
  if (HAS_RECORD_DATA_PACKAGE) {
193
- let loadingPromise = this._relationshipPromisesCache[key] as Promise<ManyArray> | undefined;
257
+ let loadingPromise = this._relationshipPromisesCache[key] as Promise<RelatedCollection> | undefined;
194
258
  if (loadingPromise) {
195
259
  return loadingPromise;
196
260
  }
197
261
 
198
262
  const jsonApi = this.recordData.getRelationship(this.identifier, key) as CollectionResourceRelationship;
263
+ const promise = this._findHasManyByJsonApiResource(jsonApi, this.identifier, relationship, options);
264
+
265
+ if (!promise) {
266
+ manyArray.isLoaded = true;
267
+ return resolve(manyArray);
268
+ }
199
269
 
200
- loadingPromise = this._findHasManyByJsonApiResource(jsonApi, this.identifier, relationship, options).then(
270
+ loadingPromise = promise.then(
201
271
  () => handleCompletedRelationshipRequest(this, key, relationship, manyArray),
202
272
  (e: Error) => handleCompletedRelationshipRequest(this, key, relationship, manyArray, e)
203
273
  );
@@ -233,7 +303,7 @@ export class LegacySupport {
233
303
  assert(`hasMany only works with the @ember-data/record-data package`);
234
304
  }
235
305
 
236
- getHasMany(key: string, options?: FindOptions): PromiseManyArray | ManyArray {
306
+ getHasMany(key: string, options?: FindOptions): PromiseManyArray | RelatedCollection {
237
307
  if (HAS_RECORD_DATA_PACKAGE) {
238
308
  const graphFor = (
239
309
  importSync('@ember-data/record-data/-private') as typeof import('@ember-data/record-data/-private')
@@ -264,11 +334,6 @@ export class LegacySupport {
264
334
  assert(`hasMany only works with the @ember-data/record-data package`);
265
335
  }
266
336
 
267
- setDirtyHasMany(key: string, records: RecordInstance[]) {
268
- assertRecordsPassedToHasMany(records);
269
- return this.recordData.setHasMany(this.identifier, key, extractIdentifiersFromRecords(records));
270
- }
271
-
272
337
  _updatePromiseProxyFor(kind: 'hasMany', key: string, args: HasManyProxyCreateArgs): PromiseManyArray;
273
338
  _updatePromiseProxyFor(kind: 'belongsTo', key: string, args: BelongsToProxyCreateArgs): PromiseBelongsTo;
274
339
  _updatePromiseProxyFor(
@@ -358,10 +423,10 @@ export class LegacySupport {
358
423
  parentIdentifier: StableRecordIdentifier,
359
424
  relationship: ManyRelationship,
360
425
  options: FindOptions = {}
361
- ): Promise<void | unknown[]> {
426
+ ): Promise<void | unknown[]> | void {
362
427
  if (HAS_RECORD_DATA_PACKAGE) {
363
428
  if (!resource) {
364
- return resolve();
429
+ return;
365
430
  }
366
431
  const { definition, state } = relationship;
367
432
  const adapter = this.store.adapterFor(definition.type);
@@ -411,11 +476,19 @@ export class LegacySupport {
411
476
 
412
477
  // fetch using data, pulling from local cache if possible
413
478
  if (!shouldForceReload && !isStale && (preferLocalCache || hasLocalPartialData)) {
479
+ if (allInverseRecordsAreLoaded) {
480
+ return;
481
+ }
414
482
  assert(`Expected collection to be an array`, Array.isArray(resource.data));
483
+ if (allInverseRecordsAreLoaded) {
484
+ return;
485
+ }
415
486
  let finds = new Array(resource.data.length);
487
+ let cache = this.store._instanceCache;
416
488
  for (let i = 0; i < resource.data.length; i++) {
417
- let identifier = this.store.identifierCache.getOrCreateRecordIdentifier(resource.data[i]);
418
- finds[i] = this.store._instanceCache._fetchDataIfNeededForIdentifier(identifier, options);
489
+ const identifier = resource.data[i];
490
+ assert(`expected a stable identifier`, isStableIdentifier(identifier));
491
+ finds[i] = cache._fetchDataIfNeededForIdentifier(identifier, options);
419
492
  }
420
493
 
421
494
  return all(finds);
@@ -425,8 +498,9 @@ export class LegacySupport {
425
498
 
426
499
  // fetch by data
427
500
  if (hasData || hasLocalPartialData) {
428
- assert(`Expected collection to be an array`, Array.isArray(resource.data));
429
- let identifiers = resource.data.map((json) => this.store.identifierCache.getOrCreateRecordIdentifier(json));
501
+ const identifiers = resource.data;
502
+ assert(`Expected collection to be an array`, Array.isArray(identifiers));
503
+ assert(`Expected stable identifiers`, identifiers.every(isStableIdentifier));
430
504
  let fetches = new Array(identifiers.length);
431
505
  const manager = this.store._fetchManager;
432
506
 
@@ -441,7 +515,7 @@ export class LegacySupport {
441
515
 
442
516
  // we were explicitly told we have no data and no links.
443
517
  // TODO if the relationshipIsStale, should we hit the adapter anyway?
444
- return resolve();
518
+ return;
445
519
  }
446
520
  assert(`hasMany only works with the @ember-data/record-data package`);
447
521
  }
@@ -456,7 +530,8 @@ export class LegacySupport {
456
530
  return resolve(null);
457
531
  }
458
532
 
459
- const identifier = resource.data ? this.store.identifierCache.getOrCreateRecordIdentifier(resource.data) : null;
533
+ const identifier = resource.data ? resource.data : null;
534
+ assert(`Expected a stable identifier`, !identifier || isStableIdentifier(identifier));
460
535
 
461
536
  let { isStale, hasDematerializedInverse, hasReceivedData, isEmpty, shouldForceReload } = relationship.state;
462
537
 
@@ -557,8 +632,8 @@ function handleCompletedRelationshipRequest(
557
632
  recordExt: LegacySupport,
558
633
  key: string,
559
634
  relationship: ManyRelationship,
560
- value: ManyArray
561
- ): ManyArray;
635
+ value: RelatedCollection
636
+ ): RelatedCollection;
562
637
  function handleCompletedRelationshipRequest(
563
638
  recordExt: LegacySupport,
564
639
  key: string,
@@ -570,16 +645,16 @@ function handleCompletedRelationshipRequest(
570
645
  recordExt: LegacySupport,
571
646
  key: string,
572
647
  relationship: ManyRelationship,
573
- value: ManyArray,
648
+ value: RelatedCollection,
574
649
  error: Error
575
650
  ): never;
576
651
  function handleCompletedRelationshipRequest(
577
652
  recordExt: LegacySupport,
578
653
  key: string,
579
654
  relationship: BelongsToRelationship | ManyRelationship,
580
- value: ManyArray | StableRecordIdentifier | null,
655
+ value: RelatedCollection | StableRecordIdentifier | null,
581
656
  error?: Error
582
- ): ManyArray | RecordInstance | null {
657
+ ): RelatedCollection | RecordInstance | null {
583
658
  delete recordExt._relationshipPromisesCache[key];
584
659
  relationship.state.shouldForceReload = false;
585
660
  const isHasMany = relationship.definition.kind === 'hasMany';
@@ -587,7 +662,7 @@ function handleCompletedRelationshipRequest(
587
662
  if (isHasMany) {
588
663
  // we don't notify the record property here to avoid refetch
589
664
  // only the many array
590
- (value as ManyArray).notify();
665
+ (value as RelatedCollection).notify();
591
666
  }
592
667
 
593
668
  if (error) {
@@ -611,7 +686,7 @@ function handleCompletedRelationshipRequest(
611
686
  }
612
687
 
613
688
  if (isHasMany) {
614
- (value as ManyArray).set('isLoaded', true);
689
+ (value as RelatedCollection).isLoaded = true;
615
690
  }
616
691
 
617
692
  relationship.state.hasFailedLoadAttempt = false;
@@ -619,33 +694,10 @@ function handleCompletedRelationshipRequest(
619
694
  relationship.state.isStale = false;
620
695
 
621
696
  return isHasMany || !value
622
- ? (value as ManyArray | null)
697
+ ? (value as RelatedCollection | null)
623
698
  : recordExt.store.peekRecord(value as StableRecordIdentifier);
624
699
  }
625
700
 
626
- function assertRecordsPassedToHasMany(records: RecordInstance[]) {
627
- assert(`You must pass an array of records to set a hasMany relationship`, Array.isArray(records));
628
- assert(
629
- `All elements of a hasMany relationship must be instances of Model, you passed ${records
630
- .map((r) => `${typeof r}`)
631
- .join(', ')}`,
632
- (function () {
633
- return records.every((record) => {
634
- try {
635
- recordIdentifierFor(record);
636
- return true;
637
- } catch {
638
- return false;
639
- }
640
- });
641
- })()
642
- );
643
- }
644
-
645
- function extractIdentifiersFromRecords(records: RecordInstance[]): StableRecordIdentifier[] {
646
- return records.map(extractIdentifierFromRecord) as StableRecordIdentifier[];
647
- }
648
-
649
701
  type PromiseProxyRecord = { then(): void; content: RecordInstance | null | undefined };
650
702
 
651
703
  function extractIdentifierFromRecord(recordOrPromiseRecord: PromiseProxyRecord | RecordInstance | null) {
@@ -653,12 +705,25 @@ function extractIdentifierFromRecord(recordOrPromiseRecord: PromiseProxyRecord |
653
705
  return null;
654
706
  }
655
707
 
656
- if (isPromiseRecord(recordOrPromiseRecord)) {
708
+ if (DEPRECATE_PROMISE_PROXIES && isPromiseRecord(recordOrPromiseRecord)) {
657
709
  let content = recordOrPromiseRecord.content;
658
710
  assert(
659
711
  'You passed in a promise that did not originate from an EmberData relationship. You can only pass promises that come from a belongsTo or hasMany relationship to the get call.',
660
712
  content !== undefined
661
713
  );
714
+ deprecate(
715
+ `You passed in a PromiseProxy to a Relationship API that now expects a resolved value. await the value before setting it.`,
716
+ false,
717
+ {
718
+ id: 'ember-data:deprecate-promise-proxies',
719
+ until: '5.0',
720
+ since: {
721
+ enabled: '4.8',
722
+ available: '4.8',
723
+ },
724
+ for: 'ember-data',
725
+ }
726
+ );
662
727
  return content ? recordIdentifierFor(content) : null;
663
728
  }
664
729
 
@@ -681,30 +746,21 @@ function anyUnloaded(store: Store, relationship: ManyRelationship) {
681
746
  }
682
747
 
683
748
  function areAllInverseRecordsLoaded(store: Store, resource: JsonApiRelationship): boolean {
684
- const cache = store.identifierCache;
749
+ const instanceCache = store._instanceCache;
750
+ const identifiers = resource.data;
685
751
 
686
- if (Array.isArray(resource.data)) {
752
+ if (Array.isArray(identifiers)) {
753
+ assert(`Expected stable identifiers`, identifiers.every(isStableIdentifier));
687
754
  // treat as collection
688
755
  // check for unloaded records
689
- let hasEmptyRecords = resource.data.reduce((hasEmptyModel, resourceIdentifier) => {
690
- return hasEmptyModel || isEmpty(store, cache, resourceIdentifier);
691
- }, false);
692
-
693
- return !hasEmptyRecords;
694
- } else {
695
- // treat as single resource
696
- if (!resource.data) {
697
- return true;
698
- } else {
699
- return !isEmpty(store, cache, resource.data);
700
- }
756
+ return identifiers.every((identifier: StableRecordIdentifier) => instanceCache.recordIsLoaded(identifier));
701
757
  }
702
- }
703
758
 
704
- function isEmpty(store: Store, cache: IdentifierCache, resource: ResourceIdentifierObject): boolean {
705
- const identifier = cache.getOrCreateRecordIdentifier(resource);
706
- const recordData = store._instanceCache.__instances.recordData.get(identifier);
707
- return !recordData || recordData.isEmpty(identifier);
759
+ // treat as single resource
760
+ if (!identifiers) return true;
761
+
762
+ assert(`Expected stable identifiers`, isStableIdentifier(identifiers));
763
+ return instanceCache.recordIsLoaded(identifiers);
708
764
  }
709
765
 
710
766
  function isBelongsTo(