@ember-data/model 4.8.0-alpha.1 → 4.8.0-alpha.4

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.
@@ -5,25 +5,22 @@ 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 type {
9
- BelongsToRelationship,
10
- ManyRelationship,
11
- RecordData as DefaultRecordData,
12
- } from '@ember-data/record-data/-private';
8
+ import type { BelongsToRelationship, ManyRelationship } from '@ember-data/record-data/-private';
13
9
  import type { UpgradedMeta } from '@ember-data/record-data/-private/graph/-edge-definition';
14
- import type { RelationshipState } from '@ember-data/record-data/-private/graph/-state';
10
+ import ImplicitRelationship from '@ember-data/record-data/-private/relationships/state/implicit';
15
11
  import type Store from '@ember-data/store';
16
- import type { InternalModel } from '@ember-data/store/-private';
17
- import { recordDataFor, recordIdentifierFor, storeFor } from '@ember-data/store/-private';
18
- import type { IdentifierCache } from '@ember-data/store/-private/identifier-cache';
12
+ import { recordIdentifierFor, storeFor } from '@ember-data/store/-private';
13
+ import { IdentifierCache } from '@ember-data/store/-private/caches/identifier-cache';
19
14
  import type { DSModel } from '@ember-data/types/q/ds-model';
20
- import type { ResourceIdentifierObject } from '@ember-data/types/q/ember-data-json-api';
15
+ import {
16
+ CollectionResourceRelationship,
17
+ ResourceIdentifierObject,
18
+ SingleResourceRelationship,
19
+ } from '@ember-data/types/q/ember-data-json-api';
21
20
  import type { StableRecordIdentifier } from '@ember-data/types/q/identifier';
22
21
  import type { RecordData } from '@ember-data/types/q/record-data';
23
22
  import type { JsonApiRelationship } from '@ember-data/types/q/record-data-json-api';
24
- import type { RelationshipSchema } from '@ember-data/types/q/record-data-schemas';
25
23
  import type { RecordInstance } from '@ember-data/types/q/record-instance';
26
- import type { DefaultSingleResourceRelationship } from '@ember-data/types/q/relationship-record-data';
27
24
  import type { FindOptions } from '@ember-data/types/q/store';
28
25
  import type { Dict } from '@ember-data/types/q/utils';
29
26
 
@@ -44,7 +41,7 @@ type PromiseBelongsToFactory = { create(args: BelongsToProxyCreateArgs): Promise
44
41
  export class LegacySupport {
45
42
  declare record: DSModel;
46
43
  declare store: Store;
47
- declare recordData: DefaultRecordData;
44
+ declare recordData: RecordData;
48
45
  declare references: Dict<BelongsToReference | HasManyReference>;
49
46
  declare identifier: StableRecordIdentifier;
50
47
  declare _manyArrayCache: Dict<ManyArray>;
@@ -58,7 +55,7 @@ export class LegacySupport {
58
55
  this.record = record;
59
56
  this.store = storeFor(record)!;
60
57
  this.identifier = recordIdentifierFor(record);
61
- this.recordData = this.store._instanceCache.getRecordData(this.identifier) as DefaultRecordData;
58
+ this.recordData = this.store._instanceCache.getRecordData(this.identifier);
62
59
 
63
60
  this._manyArrayCache = Object.create(null) as Dict<ManyArray>;
64
61
  this._relationshipPromisesCache = Object.create(null) as Dict<Promise<ManyArray | RecordInstance>>;
@@ -68,16 +65,16 @@ export class LegacySupport {
68
65
 
69
66
  _findBelongsTo(
70
67
  key: string,
71
- resource: DefaultSingleResourceRelationship,
72
- relationshipMeta: RelationshipSchema,
68
+ resource: SingleResourceRelationship,
69
+ relationship: BelongsToRelationship,
73
70
  options?: FindOptions
74
71
  ): Promise<RecordInstance | null> {
75
72
  // TODO @runspired follow up if parent isNew then we should not be attempting load here
76
73
  // TODO @runspired follow up on whether this should be in the relationship requests cache
77
- return this._findBelongsToByJsonApiResource(resource, this.identifier, relationshipMeta, options).then(
74
+ return this._findBelongsToByJsonApiResource(resource, this.identifier, relationship, options).then(
78
75
  (identifier: StableRecordIdentifier | null) =>
79
- handleCompletedRelationshipRequest(this, key, resource._relationship, identifier),
80
- (e: Error) => handleCompletedRelationshipRequest(this, key, resource._relationship, null, e)
76
+ handleCompletedRelationshipRequest(this, key, relationship, identifier),
77
+ (e: Error) => handleCompletedRelationshipRequest(this, key, relationship, null, e)
81
78
  );
82
79
  }
83
80
 
@@ -87,15 +84,16 @@ export class LegacySupport {
87
84
  return loadingPromise;
88
85
  }
89
86
 
90
- let resource = this.recordData.getBelongsTo(key);
91
- // TODO move this to a public api
92
- if (resource._relationship) {
93
- resource._relationship.state.hasFailedLoadAttempt = false;
94
- resource._relationship.state.shouldForceReload = true;
95
- }
96
- let relationshipMeta = this.store.getSchemaDefinitionService().relationshipsDefinitionFor(this.identifier)[key];
97
- assert(`Attempted to reload a belongsTo relationship but no definition exists for it`, relationshipMeta);
98
- let promise = this._findBelongsTo(key, resource, relationshipMeta, options);
87
+ const graphFor = (
88
+ importSync('@ember-data/record-data/-private') as typeof import('@ember-data/record-data/-private')
89
+ ).graphFor;
90
+ const relationship = graphFor(this.store).get(this.identifier, key);
91
+ assert(`Expected ${key} to be a belongs-to relationship`, isBelongsTo(relationship));
92
+
93
+ let resource = this.recordData.getRelationship(this.identifier, key) as SingleResourceRelationship;
94
+ relationship.state.hasFailedLoadAttempt = false;
95
+ relationship.state.shouldForceReload = true;
96
+ let promise = this._findBelongsTo(key, resource, relationship, options);
99
97
  if (this._relationshipProxyCache[key]) {
100
98
  return this._updatePromiseProxyFor('belongsTo', key, { promise });
101
99
  }
@@ -104,28 +102,31 @@ export class LegacySupport {
104
102
 
105
103
  getBelongsTo(key: string, options?: FindOptions): PromiseBelongsTo | RecordInstance | null {
106
104
  const { identifier, recordData } = this;
107
- let resource = recordData.getBelongsTo(key);
105
+ let resource = recordData.getRelationship(this.identifier, key) as SingleResourceRelationship;
108
106
  let relatedIdentifier =
109
107
  resource && resource.data ? this.store.identifierCache.getOrCreateRecordIdentifier(resource.data) : null;
110
- let relationshipMeta = this.store.getSchemaDefinitionService().relationshipsDefinitionFor(identifier)[key];
111
- assert(`Attempted to access a belongsTo relationship but no definition exists for it`, relationshipMeta);
112
108
 
113
- let store = this.store;
114
- let async = relationshipMeta.options.async;
115
- let isAsync = typeof async === 'undefined' ? true : async;
109
+ const store = this.store;
110
+ const graphFor = (
111
+ importSync('@ember-data/record-data/-private') as typeof import('@ember-data/record-data/-private')
112
+ ).graphFor;
113
+ const relationship = graphFor(store).get(this.identifier, key);
114
+ assert(`Expected ${key} to be a belongs-to relationship`, isBelongsTo(relationship));
115
+
116
+ let isAsync = relationship.definition.isAsync;
116
117
  let _belongsToState: BelongsToProxyMeta = {
117
118
  key,
118
119
  store,
119
120
  legacySupport: this,
120
- modelName: relationshipMeta.type,
121
+ modelName: relationship.definition.type,
121
122
  };
122
123
 
123
124
  if (isAsync) {
124
- if (resource._relationship.state.hasFailedLoadAttempt) {
125
+ if (relationship.state.hasFailedLoadAttempt) {
125
126
  return this._relationshipProxyCache[key] as PromiseBelongsTo;
126
127
  }
127
128
 
128
- let promise = this._findBelongsTo(key, resource, relationshipMeta, options);
129
+ let promise = this._findBelongsTo(key, resource, relationship, options);
129
130
 
130
131
  return this._updatePromiseProxyFor('belongsTo', key, {
131
132
  promise,
@@ -140,8 +141,8 @@ export class LegacySupport {
140
141
  assert(
141
142
  `You looked up the '${key}' relationship on a '${identifier.type}' with id ${
142
143
  identifier.id || 'null'
143
- } but some of the associated records were not loaded. Either make sure they are all loaded together with the parent record, or specify that the relationship is async (\`belongsTo({ async: true })\`)`,
144
- toReturn === null || !store._instanceCache.getInternalModel(relatedIdentifier).isEmpty
144
+ } but some of the associated records were not loaded. Either make sure they are all loaded together with the parent record, or specify that the relationship is async (\`belongsTo(<type>, { async: true, inverse: <inverse> })\`)`,
145
+ toReturn === null || store._instanceCache.recordIsLoaded(relatedIdentifier, true)
145
146
  );
146
147
  return toReturn;
147
148
  }
@@ -149,7 +150,7 @@ export class LegacySupport {
149
150
  }
150
151
 
151
152
  setDirtyBelongsTo(key: string, value: RecordInstance | null) {
152
- return this.recordData.setDirtyBelongsTo(key, extractRecordDataFromRecord(value));
153
+ return this.recordData.setBelongsTo(this.identifier, key, extractIdentifierFromRecord(value));
153
154
  }
154
155
 
155
156
  getManyArray(key: string, definition?: UpgradedMeta): ManyArray {
@@ -166,6 +167,7 @@ export class LegacySupport {
166
167
  manyArray = (ManyArray as unknown as ManyArrayFactory).create({
167
168
  store: this.store,
168
169
  type: this.store.modelFor(definition.type),
170
+ identifier: this.identifier,
169
171
  recordData: this.recordData,
170
172
  key,
171
173
  isPolymorphic: definition.isPolymorphic,
@@ -192,7 +194,7 @@ export class LegacySupport {
192
194
  return loadingPromise;
193
195
  }
194
196
 
195
- const jsonApi = this.recordData.getHasMany(key);
197
+ const jsonApi = this.recordData.getRelationship(this.identifier, key) as CollectionResourceRelationship;
196
198
 
197
199
  loadingPromise = this._findHasManyByJsonApiResource(jsonApi, this.identifier, relationship, options).then(
198
200
  () => handleCompletedRelationshipRequest(this, key, relationship, manyArray),
@@ -251,7 +253,7 @@ export class LegacySupport {
251
253
  assert(
252
254
  `You looked up the '${key}' relationship on a '${this.identifier.type}' with id ${
253
255
  this.identifier.id || 'null'
254
- } but some of the associated records were not loaded. Either make sure they are all loaded together with the parent record, or specify that the relationship is async ('hasMany({ async: true })')`,
256
+ } but some of the associated records were not loaded. Either make sure they are all loaded together with the parent record, or specify that the relationship is async ('hasMany(<type>, { async: true, inverse: <inverse> })')`,
255
257
  !anyUnloaded(this.store, relationship)
256
258
  );
257
259
 
@@ -263,7 +265,7 @@ export class LegacySupport {
263
265
 
264
266
  setDirtyHasMany(key: string, records: RecordInstance[]) {
265
267
  assertRecordsPassedToHasMany(records);
266
- return this.recordData.setDirtyHasMany(key, extractRecordDatasFromRecords(records));
268
+ return this.recordData.setHasMany(this.identifier, key, extractIdentifiersFromRecords(records));
267
269
  }
268
270
 
269
271
  _updatePromiseProxyFor(kind: 'hasMany', key: string, args: HasManyProxyCreateArgs): PromiseManyArray;
@@ -344,7 +346,7 @@ export class LegacySupport {
344
346
  }
345
347
 
346
348
  _findHasManyByJsonApiResource(
347
- resource,
349
+ resource: CollectionResourceRelationship,
348
350
  parentIdentifier: StableRecordIdentifier,
349
351
  relationship: ManyRelationship,
350
352
  options: FindOptions = {}
@@ -354,12 +356,10 @@ export class LegacySupport {
354
356
  return resolve();
355
357
  }
356
358
  const { definition, state } = relationship;
357
- let adapter = this.store.adapterFor(definition.type);
358
-
359
- let { isStale, hasDematerializedInverse, hasReceivedData, isEmpty, shouldForceReload } = state;
359
+ const adapter = this.store.adapterFor(definition.type);
360
+ const { isStale, hasDematerializedInverse, hasReceivedData, isEmpty, shouldForceReload } = state;
360
361
  const allInverseRecordsAreLoaded = areAllInverseRecordsLoaded(this.store, resource);
361
-
362
- let shouldFindViaLink =
362
+ const shouldFindViaLink =
363
363
  resource.links &&
364
364
  resource.links.related &&
365
365
  (typeof adapter.findHasMany === 'function' || typeof resource.data === 'undefined') &&
@@ -369,9 +369,9 @@ export class LegacySupport {
369
369
  if (shouldFindViaLink) {
370
370
  // findHasMany, although not public, does not need to care about our upgrade relationship definitions
371
371
  // and can stick with the public definition API for now.
372
- const relationshipMeta = this.store._instanceCache._storeWrapper.relationshipsDefinitionFor(
373
- definition.inverseType
374
- )[definition.key];
372
+ const relationshipMeta = this.store
373
+ .getSchemaDefinitionService()
374
+ .relationshipsDefinitionFor({ type: definition.inverseType })[definition.key];
375
375
  let adapter = this.store.adapterFor(parentIdentifier.type);
376
376
 
377
377
  /*
@@ -394,16 +394,16 @@ export class LegacySupport {
394
394
  typeof adapter.findHasMany === 'function'
395
395
  );
396
396
 
397
- return _findHasMany(adapter, this.store, parentIdentifier, resource.links.related, relationshipMeta, options);
397
+ return _findHasMany(adapter, this.store, parentIdentifier, resource.links!.related, relationshipMeta, options);
398
398
  }
399
399
 
400
- let preferLocalCache = hasReceivedData && !isEmpty;
401
-
402
- let hasLocalPartialData =
400
+ const preferLocalCache = hasReceivedData && !isEmpty;
401
+ const hasLocalPartialData =
403
402
  hasDematerializedInverse || (isEmpty && Array.isArray(resource.data) && resource.data.length > 0);
404
403
 
405
404
  // fetch using data, pulling from local cache if possible
406
405
  if (!shouldForceReload && !isStale && (preferLocalCache || hasLocalPartialData)) {
406
+ assert(`Expected collection to be an array`, Array.isArray(resource.data));
407
407
  let finds = new Array(resource.data.length);
408
408
  for (let i = 0; i < resource.data.length; i++) {
409
409
  let identifier = this.store.identifierCache.getOrCreateRecordIdentifier(resource.data[i]);
@@ -417,6 +417,7 @@ export class LegacySupport {
417
417
 
418
418
  // fetch by data
419
419
  if (hasData || hasLocalPartialData) {
420
+ assert(`Expected collection to be an array`, Array.isArray(resource.data));
420
421
  let identifiers = resource.data.map((json) => this.store.identifierCache.getOrCreateRecordIdentifier(json));
421
422
  let fetches = new Array(identifiers.length);
422
423
  const manager = this.store._fetchManager;
@@ -438,43 +439,44 @@ export class LegacySupport {
438
439
  }
439
440
 
440
441
  _findBelongsToByJsonApiResource(
441
- resource,
442
+ resource: SingleResourceRelationship,
442
443
  parentIdentifier: StableRecordIdentifier,
443
- relationshipMeta,
444
+ relationship: BelongsToRelationship,
444
445
  options: FindOptions = {}
445
446
  ): Promise<StableRecordIdentifier | null> {
446
447
  if (!resource) {
447
448
  return resolve(null);
448
449
  }
449
450
 
450
- const internalModel = resource.data ? this.store._instanceCache._internalModelForResource(resource.data) : null;
451
-
452
- let { isStale, hasDematerializedInverse, hasReceivedData, isEmpty, shouldForceReload } = resource._relationship
453
- .state as RelationshipState;
454
- const allInverseRecordsAreLoaded = areAllInverseRecordsLoaded(this.store, resource);
451
+ const identifier = resource.data ? this.store.identifierCache.getOrCreateRecordIdentifier(resource.data) : null;
455
452
 
456
- let shouldFindViaLink =
457
- resource.links &&
458
- resource.links.related &&
459
- (shouldForceReload || hasDematerializedInverse || isStale || (!allInverseRecordsAreLoaded && !isEmpty));
453
+ let { isStale, hasDematerializedInverse, hasReceivedData, isEmpty, shouldForceReload } = relationship.state;
460
454
 
461
- if (internalModel) {
462
- // short circuit if we are already loading
463
- let pendingRequest = this.store._fetchManager.getPendingFetch(internalModel.identifier, options);
464
- if (pendingRequest) {
465
- return pendingRequest;
466
- }
455
+ // short circuit if we are already loading
456
+ let pendingRequest = identifier && this.store._fetchManager.getPendingFetch(identifier, options);
457
+ if (pendingRequest) {
458
+ return pendingRequest;
467
459
  }
468
460
 
461
+ const allInverseRecordsAreLoaded = areAllInverseRecordsLoaded(this.store, resource);
462
+ const shouldFindViaLink =
463
+ resource.links?.related &&
464
+ (shouldForceReload || hasDematerializedInverse || isStale || (!allInverseRecordsAreLoaded && !isEmpty));
465
+
469
466
  // fetch via link
470
467
  if (shouldFindViaLink) {
471
- return _findBelongsTo(this.store, parentIdentifier, resource.links.related, relationshipMeta, options);
468
+ const relationshipMeta = this.store.getSchemaDefinitionService().relationshipsDefinitionFor(this.identifier)[
469
+ relationship.definition.key
470
+ ];
471
+ assert(`Attempted to access a belongsTo relationship but no definition exists for it`, relationshipMeta);
472
+
473
+ return _findBelongsTo(this.store, parentIdentifier, resource.links!.related, relationshipMeta, options);
472
474
  }
473
475
 
474
476
  let preferLocalCache = hasReceivedData && allInverseRecordsAreLoaded && !isEmpty;
475
477
  let hasLocalPartialData = hasDematerializedInverse || (isEmpty && resource.data);
476
478
  // null is explicit empty, undefined is "we don't know anything"
477
- let localDataIsEmpty = resource.data === undefined || resource.data === null;
479
+ const localDataIsEmpty = resource.data === undefined || resource.data === null;
478
480
 
479
481
  // fetch using data, pulling from local cache if possible
480
482
  if (!shouldForceReload && !isStale && (preferLocalCache || hasLocalPartialData)) {
@@ -485,22 +487,21 @@ export class LegacySupport {
485
487
  return resolve(null);
486
488
  }
487
489
 
488
- if (!internalModel) {
489
- assert(`No InternalModel found for ${resource.lid}`, internalModel);
490
+ if (!identifier) {
491
+ assert(`No Information found for ${resource.data!.lid}`, identifier);
490
492
  }
491
493
 
492
- return this.store._instanceCache._fetchDataIfNeededForIdentifier(internalModel.identifier, options);
494
+ return this.store._instanceCache._fetchDataIfNeededForIdentifier(identifier, options);
493
495
  }
494
496
 
495
- let resourceIsLocal = !localDataIsEmpty && resource.data.id === null;
497
+ let resourceIsLocal = !localDataIsEmpty && resource.data!.id === null;
496
498
 
497
- if (internalModel && resourceIsLocal) {
498
- return resolve(internalModel.identifier);
499
+ if (identifier && resourceIsLocal) {
500
+ return resolve(identifier);
499
501
  }
500
502
 
501
503
  // fetch by data
502
- if (internalModel && !localDataIsEmpty) {
503
- let identifier = internalModel.identifier;
504
+ if (identifier && !localDataIsEmpty) {
504
505
  assertIdentifierHasId(identifier);
505
506
 
506
507
  return this.store._fetchManager.scheduleFetch(identifier, options);
@@ -512,32 +513,28 @@ export class LegacySupport {
512
513
  }
513
514
 
514
515
  destroy() {
515
- assert(
516
- 'Cannot destroy an internalModel while its record is materialized',
517
- !this.record || this.record.isDestroyed || this.record.isDestroying
518
- );
519
516
  this.isDestroying = true;
520
517
 
521
- const cache = this._manyArrayCache;
518
+ let cache: Dict<{ destroy(): void }> = this._manyArrayCache;
519
+ this._manyArrayCache = Object.create(null);
522
520
  Object.keys(cache).forEach((key) => {
523
521
  cache[key]!.destroy();
524
- delete cache[key];
525
522
  });
526
- const keys = Object.keys(this._relationshipProxyCache);
527
- keys.forEach((key) => {
528
- const proxy = this._relationshipProxyCache[key]!;
523
+
524
+ cache = this._relationshipProxyCache;
525
+ this._relationshipProxyCache = Object.create(null);
526
+ Object.keys(cache).forEach((key) => {
527
+ const proxy = cache[key]!;
529
528
  if (proxy.destroy) {
530
529
  proxy.destroy();
531
530
  }
532
- delete this._relationshipProxyCache[key];
533
531
  });
534
- if (this.references) {
535
- const refs = this.references;
536
- Object.keys(refs).forEach((key) => {
537
- refs[key]!.destroy();
538
- delete refs[key];
539
- });
540
- }
532
+
533
+ cache = this.references;
534
+ this.references = Object.create(null);
535
+ Object.keys(cache).forEach((key) => {
536
+ cache[key]!.destroy();
537
+ });
541
538
  this.isDestroyed = true;
542
539
  }
543
540
  }
@@ -625,32 +622,39 @@ function assertRecordsPassedToHasMany(records: RecordInstance[]) {
625
622
  .map((r) => `${typeof r}`)
626
623
  .join(', ')}`,
627
624
  (function () {
628
- return records.every((record) => Object.prototype.hasOwnProperty.call(record, '_internalModel') === true);
625
+ return records.every((record) => {
626
+ try {
627
+ recordIdentifierFor(record);
628
+ return true;
629
+ } catch {
630
+ return false;
631
+ }
632
+ });
629
633
  })()
630
634
  );
631
635
  }
632
636
 
633
- function extractRecordDatasFromRecords(records: RecordInstance[]): RecordData[] {
634
- return records.map(extractRecordDataFromRecord) as RecordData[];
637
+ function extractIdentifiersFromRecords(records: RecordInstance[]): StableRecordIdentifier[] {
638
+ return records.map(extractIdentifierFromRecord) as StableRecordIdentifier[];
635
639
  }
636
640
 
637
- type PromiseProxyRecord = { then(): void; get(str: 'content'): RecordInstance | null | undefined };
641
+ type PromiseProxyRecord = { then(): void; content: RecordInstance | null | undefined };
638
642
 
639
- function extractRecordDataFromRecord(recordOrPromiseRecord: PromiseProxyRecord | RecordInstance | null) {
643
+ function extractIdentifierFromRecord(recordOrPromiseRecord: PromiseProxyRecord | RecordInstance | null) {
640
644
  if (!recordOrPromiseRecord) {
641
645
  return null;
642
646
  }
643
647
 
644
648
  if (isPromiseRecord(recordOrPromiseRecord)) {
645
- let content = recordOrPromiseRecord.get && recordOrPromiseRecord.get('content');
649
+ let content = recordOrPromiseRecord.content;
646
650
  assert(
647
651
  '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.',
648
652
  content !== undefined
649
653
  );
650
- return content ? recordDataFor(content) : null;
654
+ return content ? recordIdentifierFor(content) : null;
651
655
  }
652
656
 
653
- return recordDataFor(recordOrPromiseRecord);
657
+ return recordIdentifierFor(recordOrPromiseRecord);
654
658
  }
655
659
 
656
660
  function isPromiseRecord(record: PromiseProxyRecord | RecordInstance): record is PromiseProxyRecord {
@@ -659,24 +663,15 @@ function isPromiseRecord(record: PromiseProxyRecord | RecordInstance): record is
659
663
 
660
664
  function anyUnloaded(store: Store, relationship: ManyRelationship) {
661
665
  let state = relationship.currentState;
666
+ const cache = store._instanceCache;
662
667
  const unloaded = state.find((s) => {
663
- let im = store._instanceCache.getInternalModel(s);
664
- return im._isDematerializing || !im.isLoaded;
668
+ let isLoaded = cache.recordIsLoaded(s, true);
669
+ return !isLoaded;
665
670
  });
666
671
 
667
672
  return unloaded || false;
668
673
  }
669
674
 
670
- /**
671
- * Flag indicating whether all inverse records are available
672
- *
673
- * true if the inverse exists and is loaded (not empty)
674
- * true if there is no inverse
675
- * false if the inverse exists and is not loaded (empty)
676
- *
677
- * @internal
678
- * @return {boolean}
679
- */
680
675
  function areAllInverseRecordsLoaded(store: Store, resource: JsonApiRelationship): boolean {
681
676
  const cache = store.identifierCache;
682
677
 
@@ -684,7 +679,7 @@ function areAllInverseRecordsLoaded(store: Store, resource: JsonApiRelationship)
684
679
  // treat as collection
685
680
  // check for unloaded records
686
681
  let hasEmptyRecords = resource.data.reduce((hasEmptyModel, resourceIdentifier) => {
687
- return hasEmptyModel || internalModelForRelatedResource(store, cache, resourceIdentifier).isEmpty;
682
+ return hasEmptyModel || isEmpty(store, cache, resourceIdentifier);
688
683
  }, false);
689
684
 
690
685
  return !hasEmptyRecords;
@@ -693,17 +688,19 @@ function areAllInverseRecordsLoaded(store: Store, resource: JsonApiRelationship)
693
688
  if (!resource.data) {
694
689
  return true;
695
690
  } else {
696
- const internalModel = internalModelForRelatedResource(store, cache, resource.data);
697
- return !internalModel.isEmpty;
691
+ return !isEmpty(store, cache, resource.data);
698
692
  }
699
693
  }
700
694
  }
701
695
 
702
- function internalModelForRelatedResource(
703
- store: Store,
704
- cache: IdentifierCache,
705
- resource: ResourceIdentifierObject
706
- ): InternalModel {
696
+ function isEmpty(store: Store, cache: IdentifierCache, resource: ResourceIdentifierObject): boolean {
707
697
  const identifier = cache.getOrCreateRecordIdentifier(resource);
708
- return store._instanceCache._internalModelForResource(identifier);
698
+ const recordData = store._instanceCache.peek({ identifier, bucket: 'recordData' });
699
+ return !recordData || !!recordData.isEmpty?.(identifier);
700
+ }
701
+
702
+ function isBelongsTo(
703
+ relationship: BelongsToRelationship | ImplicitRelationship | ManyRelationship
704
+ ): relationship is BelongsToRelationship {
705
+ return relationship.definition.kind === 'belongsTo';
709
706
  }
@@ -9,14 +9,15 @@ import EmberObject, { get } from '@ember/object';
9
9
  import { all } from 'rsvp';
10
10
 
11
11
  import type Store from '@ember-data/store';
12
- import { PromiseArray, recordDataFor } from '@ember-data/store/-private';
13
- import type { CreateRecordProperties } from '@ember-data/store/-private/core-store';
14
- import type ShimModelClass from '@ember-data/store/-private/model/shim-model-class';
12
+ import { PromiseArray, recordIdentifierFor } from '@ember-data/store/-private';
13
+ import type ShimModelClass from '@ember-data/store/-private/legacy-model-support/shim-model-class';
14
+ import type { NonSingletonRecordDataManager } from '@ember-data/store/-private/managers/record-data-manager';
15
+ import type { CreateRecordProperties } from '@ember-data/store/-private/store-service';
15
16
  import type { DSModelSchema } from '@ember-data/types/q/ds-model';
16
- import type { Links, PaginationLinks } from '@ember-data/types/q/ember-data-json-api';
17
+ import type { CollectionResourceRelationship, Links, PaginationLinks } from '@ember-data/types/q/ember-data-json-api';
17
18
  import type { StableRecordIdentifier } from '@ember-data/types/q/identifier';
19
+ import type { RecordData } from '@ember-data/types/q/record-data';
18
20
  import type { RecordInstance } from '@ember-data/types/q/record-instance';
19
- import type { RelationshipRecordData } from '@ember-data/types/q/relationship-record-data';
20
21
  import type { FindOptions } from '@ember-data/types/q/store';
21
22
  import type { Dict } from '@ember-data/types/q/utils';
22
23
 
@@ -32,7 +33,8 @@ const MutableArrayWithObject = EmberObject.extend(MutableArray) as unknown as ne
32
33
  export interface ManyArrayCreateArgs {
33
34
  store: Store;
34
35
  type: ShimModelClass;
35
- recordData: RelationshipRecordData;
36
+ identifier: StableRecordIdentifier;
37
+ recordData: RecordData;
36
38
  key: string;
37
39
  isPolymorphic: boolean;
38
40
  isAsync: boolean;
@@ -98,7 +100,8 @@ export default class ManyArray extends MutableArrayWithObject<StableRecordIdenti
98
100
  declare _meta: Dict<unknown> | null;
99
101
  declare _links: Links | PaginationLinks | null;
100
102
  declare currentState: StableRecordIdentifier[];
101
- declare recordData: RelationshipRecordData;
103
+ declare identifier: StableRecordIdentifier;
104
+ declare recordData: RecordData;
102
105
  declare legacySupport: LegacySupport;
103
106
  declare store: Store;
104
107
  declare key: string;
@@ -272,25 +275,22 @@ export default class ManyArray extends MutableArrayWithObject<StableRecordIdenti
272
275
 
273
276
  replace(idx: number, amt: number, objects?: RecordInstance[]) {
274
277
  assert(`Cannot push mutations to the cache while updating the relationship from cache`, !this._isUpdating);
275
- const { store } = this;
278
+ assert(
279
+ 'The third argument to replace needs to be an array.',
280
+ !objects || Array.isArray(objects) || EmberArray.detect(objects)
281
+ );
282
+ const { store, identifier } = this;
276
283
  store._backburner.join(() => {
277
284
  let identifiers: StableRecordIdentifier[];
278
285
  if (amt > 0) {
279
286
  identifiers = this.currentState.slice(idx, idx + amt);
280
- this.recordData.removeFromHasMany(
281
- this.key,
282
- // TODO RecordData V2: recordData should take identifiers not RecordDatas
283
- identifiers.map((identifier) => store._instanceCache.getRecordData(identifier))
284
- );
287
+ this.recordData.removeFromHasMany(identifier, this.key, identifiers);
285
288
  }
286
- if (objects) {
287
- assert(
288
- 'The third argument to replace needs to be an array.',
289
- Array.isArray(objects) || EmberArray.detect(objects)
290
- );
289
+ if (objects && objects.length > 0) {
291
290
  this.recordData.addToHasMany(
291
+ identifier,
292
292
  this.key,
293
- objects.map((obj: RecordInstance) => recordDataFor(obj)),
293
+ objects.map((obj: RecordInstance) => recordIdentifierFor(obj)),
294
294
  idx
295
295
  );
296
296
  }
@@ -305,17 +305,23 @@ export default class ManyArray extends MutableArrayWithObject<StableRecordIdenti
305
305
  }
306
306
  this._isDirty = false;
307
307
  this._isUpdating = true;
308
- let jsonApi = this.recordData.getHasMany(this.key);
308
+ const identifier = this.identifier;
309
+
310
+ let jsonApi = (this.recordData as NonSingletonRecordDataManager).getRelationship(
311
+ identifier,
312
+ this.key,
313
+ true
314
+ ) as CollectionResourceRelationship;
315
+ const cache = this.store._instanceCache;
316
+ const idCache = this.store.identifierCache;
309
317
 
310
318
  let identifiers: StableRecordIdentifier[] = [];
311
319
  if (jsonApi.data) {
312
320
  for (let i = 0; i < jsonApi.data.length; i++) {
313
- // TODO figure out where this state comes from
314
- let im = this.store._instanceCache._internalModelForResource(jsonApi.data[i]);
315
- let shouldRemove = im._isDematerializing || im.isEmpty || !im.isLoaded;
321
+ const identifier = idCache.getOrCreateRecordIdentifier(jsonApi.data[i]);
316
322
 
317
- if (!shouldRemove) {
318
- identifiers.push(im.identifier);
323
+ if (cache.recordIsLoaded(identifier, true)) {
324
+ identifiers.push(identifier);
319
325
  }
320
326
  }
321
327
  }
@@ -348,6 +354,12 @@ export default class ManyArray extends MutableArrayWithObject<StableRecordIdenti
348
354
  this._isUpdating = false;
349
355
  }
350
356
 
357
+ destroy() {
358
+ this._length = 0;
359
+ this.currentState = [];
360
+ return super.destroy();
361
+ }
362
+
351
363
  /**
352
364
  Reloads all of the records in the manyArray. If the manyArray
353
365
  holds a relationship that was originally fetched using a links url
@@ -29,10 +29,8 @@ export default function modelForMixin(store: Store, normalizedModelName: string)
29
29
  let mixin = MaybeMixin && MaybeMixin.class;
30
30
  if (mixin) {
31
31
  let ModelForMixin = Model.extend(mixin);
32
- ModelForMixin.reopenClass({
33
- __isMixin: true,
34
- __mixin: mixin,
35
- });
32
+ ModelForMixin.__isMixin = true;
33
+ ModelForMixin.__mixin = mixin;
36
34
  //Cache the class as a model
37
35
  owner.register('model:' + normalizedModelName, ModelForMixin);
38
36
  }