@ember-data/model 4.8.0-alpha.2 → 4.8.0-alpha.5

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,23 @@ 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';
13
8
  import type { UpgradedMeta } from '@ember-data/record-data/-private/graph/-edge-definition';
14
- import type { RelationshipState } from '@ember-data/record-data/-private/graph/-state';
9
+ import type { ImplicitRelationship } from '@ember-data/record-data/-private/graph/index';
10
+ import type BelongsToRelationship from '@ember-data/record-data/-private/relationships/state/belongs-to';
11
+ import type ManyRelationship from '@ember-data/record-data/-private/relationships/state/has-many';
15
12
  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';
13
+ import { recordIdentifierFor, storeFor } from '@ember-data/store/-private';
14
+ import { IdentifierCache } from '@ember-data/store/-private/caches/identifier-cache';
19
15
  import type { DSModel } from '@ember-data/types/q/ds-model';
20
- import type { ResourceIdentifierObject } from '@ember-data/types/q/ember-data-json-api';
16
+ import {
17
+ CollectionResourceRelationship,
18
+ ResourceIdentifierObject,
19
+ SingleResourceRelationship,
20
+ } from '@ember-data/types/q/ember-data-json-api';
21
21
  import type { StableRecordIdentifier } from '@ember-data/types/q/identifier';
22
22
  import type { RecordData } from '@ember-data/types/q/record-data';
23
23
  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
24
  import type { RecordInstance } from '@ember-data/types/q/record-instance';
26
- import type { DefaultSingleResourceRelationship } from '@ember-data/types/q/relationship-record-data';
27
25
  import type { FindOptions } from '@ember-data/types/q/store';
28
26
  import type { Dict } from '@ember-data/types/q/utils';
29
27
 
@@ -44,7 +42,7 @@ type PromiseBelongsToFactory = { create(args: BelongsToProxyCreateArgs): Promise
44
42
  export class LegacySupport {
45
43
  declare record: DSModel;
46
44
  declare store: Store;
47
- declare recordData: DefaultRecordData;
45
+ declare recordData: RecordData;
48
46
  declare references: Dict<BelongsToReference | HasManyReference>;
49
47
  declare identifier: StableRecordIdentifier;
50
48
  declare _manyArrayCache: Dict<ManyArray>;
@@ -58,7 +56,7 @@ export class LegacySupport {
58
56
  this.record = record;
59
57
  this.store = storeFor(record)!;
60
58
  this.identifier = recordIdentifierFor(record);
61
- this.recordData = this.store._instanceCache.getRecordData(this.identifier) as DefaultRecordData;
59
+ this.recordData = this.store._instanceCache.getRecordData(this.identifier);
62
60
 
63
61
  this._manyArrayCache = Object.create(null) as Dict<ManyArray>;
64
62
  this._relationshipPromisesCache = Object.create(null) as Dict<Promise<ManyArray | RecordInstance>>;
@@ -68,16 +66,16 @@ export class LegacySupport {
68
66
 
69
67
  _findBelongsTo(
70
68
  key: string,
71
- resource: DefaultSingleResourceRelationship,
72
- relationshipMeta: RelationshipSchema,
69
+ resource: SingleResourceRelationship,
70
+ relationship: BelongsToRelationship,
73
71
  options?: FindOptions
74
72
  ): Promise<RecordInstance | null> {
75
73
  // TODO @runspired follow up if parent isNew then we should not be attempting load here
76
74
  // TODO @runspired follow up on whether this should be in the relationship requests cache
77
- return this._findBelongsToByJsonApiResource(resource, this.identifier, relationshipMeta, options).then(
75
+ return this._findBelongsToByJsonApiResource(resource, this.identifier, relationship, options).then(
78
76
  (identifier: StableRecordIdentifier | null) =>
79
- handleCompletedRelationshipRequest(this, key, resource._relationship, identifier),
80
- (e: Error) => handleCompletedRelationshipRequest(this, key, resource._relationship, null, e)
77
+ handleCompletedRelationshipRequest(this, key, relationship, identifier),
78
+ (e: Error) => handleCompletedRelationshipRequest(this, key, relationship, null, e)
81
79
  );
82
80
  }
83
81
 
@@ -87,15 +85,16 @@ export class LegacySupport {
87
85
  return loadingPromise;
88
86
  }
89
87
 
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);
88
+ const graphFor = (
89
+ importSync('@ember-data/record-data/-private') as typeof import('@ember-data/record-data/-private')
90
+ ).graphFor;
91
+ const relationship = graphFor(this.store).get(this.identifier, key);
92
+ assert(`Expected ${key} to be a belongs-to relationship`, isBelongsTo(relationship));
93
+
94
+ let resource = this.recordData.getRelationship(this.identifier, key) as SingleResourceRelationship;
95
+ relationship.state.hasFailedLoadAttempt = false;
96
+ relationship.state.shouldForceReload = true;
97
+ let promise = this._findBelongsTo(key, resource, relationship, options);
99
98
  if (this._relationshipProxyCache[key]) {
100
99
  return this._updatePromiseProxyFor('belongsTo', key, { promise });
101
100
  }
@@ -104,28 +103,31 @@ export class LegacySupport {
104
103
 
105
104
  getBelongsTo(key: string, options?: FindOptions): PromiseBelongsTo | RecordInstance | null {
106
105
  const { identifier, recordData } = this;
107
- let resource = recordData.getBelongsTo(key);
106
+ let resource = recordData.getRelationship(this.identifier, key) as SingleResourceRelationship;
108
107
  let relatedIdentifier =
109
108
  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
109
 
113
- let store = this.store;
114
- let async = relationshipMeta.options.async;
115
- let isAsync = typeof async === 'undefined' ? true : async;
110
+ const store = this.store;
111
+ const graphFor = (
112
+ importSync('@ember-data/record-data/-private') as typeof import('@ember-data/record-data/-private')
113
+ ).graphFor;
114
+ const relationship = graphFor(store).get(this.identifier, key);
115
+ assert(`Expected ${key} to be a belongs-to relationship`, isBelongsTo(relationship));
116
+
117
+ let isAsync = relationship.definition.isAsync;
116
118
  let _belongsToState: BelongsToProxyMeta = {
117
119
  key,
118
120
  store,
119
121
  legacySupport: this,
120
- modelName: relationshipMeta.type,
122
+ modelName: relationship.definition.type,
121
123
  };
122
124
 
123
125
  if (isAsync) {
124
- if (resource._relationship.state.hasFailedLoadAttempt) {
126
+ if (relationship.state.hasFailedLoadAttempt) {
125
127
  return this._relationshipProxyCache[key] as PromiseBelongsTo;
126
128
  }
127
129
 
128
- let promise = this._findBelongsTo(key, resource, relationshipMeta, options);
130
+ let promise = this._findBelongsTo(key, resource, relationship, options);
129
131
 
130
132
  return this._updatePromiseProxyFor('belongsTo', key, {
131
133
  promise,
@@ -140,8 +142,8 @@ export class LegacySupport {
140
142
  assert(
141
143
  `You looked up the '${key}' relationship on a '${identifier.type}' with id ${
142
144
  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
145
+ } 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> })\`)`,
146
+ toReturn === null || store._instanceCache.recordIsLoaded(relatedIdentifier, true)
145
147
  );
146
148
  return toReturn;
147
149
  }
@@ -149,7 +151,7 @@ export class LegacySupport {
149
151
  }
150
152
 
151
153
  setDirtyBelongsTo(key: string, value: RecordInstance | null) {
152
- return this.recordData.setDirtyBelongsTo(key, extractRecordDataFromRecord(value));
154
+ return this.recordData.setBelongsTo(this.identifier, key, extractIdentifierFromRecord(value));
153
155
  }
154
156
 
155
157
  getManyArray(key: string, definition?: UpgradedMeta): ManyArray {
@@ -166,6 +168,7 @@ export class LegacySupport {
166
168
  manyArray = (ManyArray as unknown as ManyArrayFactory).create({
167
169
  store: this.store,
168
170
  type: this.store.modelFor(definition.type),
171
+ identifier: this.identifier,
169
172
  recordData: this.recordData,
170
173
  key,
171
174
  isPolymorphic: definition.isPolymorphic,
@@ -192,7 +195,7 @@ export class LegacySupport {
192
195
  return loadingPromise;
193
196
  }
194
197
 
195
- const jsonApi = this.recordData.getHasMany(key);
198
+ const jsonApi = this.recordData.getRelationship(this.identifier, key) as CollectionResourceRelationship;
196
199
 
197
200
  loadingPromise = this._findHasManyByJsonApiResource(jsonApi, this.identifier, relationship, options).then(
198
201
  () => handleCompletedRelationshipRequest(this, key, relationship, manyArray),
@@ -251,7 +254,7 @@ export class LegacySupport {
251
254
  assert(
252
255
  `You looked up the '${key}' relationship on a '${this.identifier.type}' with id ${
253
256
  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 })')`,
257
+ } 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
258
  !anyUnloaded(this.store, relationship)
256
259
  );
257
260
 
@@ -263,7 +266,7 @@ export class LegacySupport {
263
266
 
264
267
  setDirtyHasMany(key: string, records: RecordInstance[]) {
265
268
  assertRecordsPassedToHasMany(records);
266
- return this.recordData.setDirtyHasMany(key, extractRecordDatasFromRecords(records));
269
+ return this.recordData.setHasMany(this.identifier, key, extractIdentifiersFromRecords(records));
267
270
  }
268
271
 
269
272
  _updatePromiseProxyFor(kind: 'hasMany', key: string, args: HasManyProxyCreateArgs): PromiseManyArray;
@@ -318,7 +321,8 @@ export class LegacySupport {
318
321
  const graphFor = (
319
322
  importSync('@ember-data/record-data/-private') as typeof import('@ember-data/record-data/-private')
320
323
  ).graphFor;
321
- const relationship = graphFor(this.store).get(this.identifier, name);
324
+ const graph = graphFor(this.store);
325
+ const relationship = graph.get(this.identifier, name);
322
326
 
323
327
  if (DEBUG && kind) {
324
328
  let modelName = this.identifier.type;
@@ -332,9 +336,15 @@ export class LegacySupport {
332
336
  let relationshipKind = relationship.definition.kind;
333
337
 
334
338
  if (relationshipKind === 'belongsTo') {
335
- reference = new BelongsToReference(this.store, this.identifier, relationship as BelongsToRelationship, name);
339
+ reference = new BelongsToReference(
340
+ this.store,
341
+ graph,
342
+ this.identifier,
343
+ relationship as BelongsToRelationship,
344
+ name
345
+ );
336
346
  } else if (relationshipKind === 'hasMany') {
337
- reference = new HasManyReference(this.store, this.identifier, relationship as ManyRelationship, name);
347
+ reference = new HasManyReference(this.store, graph, this.identifier, relationship as ManyRelationship, name);
338
348
  }
339
349
 
340
350
  this.references[name] = reference;
@@ -344,7 +354,7 @@ export class LegacySupport {
344
354
  }
345
355
 
346
356
  _findHasManyByJsonApiResource(
347
- resource,
357
+ resource: CollectionResourceRelationship,
348
358
  parentIdentifier: StableRecordIdentifier,
349
359
  relationship: ManyRelationship,
350
360
  options: FindOptions = {}
@@ -354,12 +364,10 @@ export class LegacySupport {
354
364
  return resolve();
355
365
  }
356
366
  const { definition, state } = relationship;
357
- let adapter = this.store.adapterFor(definition.type);
358
-
359
- let { isStale, hasDematerializedInverse, hasReceivedData, isEmpty, shouldForceReload } = state;
367
+ const adapter = this.store.adapterFor(definition.type);
368
+ const { isStale, hasDematerializedInverse, hasReceivedData, isEmpty, shouldForceReload } = state;
360
369
  const allInverseRecordsAreLoaded = areAllInverseRecordsLoaded(this.store, resource);
361
-
362
- let shouldFindViaLink =
370
+ const shouldFindViaLink =
363
371
  resource.links &&
364
372
  resource.links.related &&
365
373
  (typeof adapter.findHasMany === 'function' || typeof resource.data === 'undefined') &&
@@ -369,9 +377,9 @@ export class LegacySupport {
369
377
  if (shouldFindViaLink) {
370
378
  // findHasMany, although not public, does not need to care about our upgrade relationship definitions
371
379
  // and can stick with the public definition API for now.
372
- const relationshipMeta = this.store._instanceCache._storeWrapper.relationshipsDefinitionFor(
373
- definition.inverseType
374
- )[definition.key];
380
+ const relationshipMeta = this.store
381
+ .getSchemaDefinitionService()
382
+ .relationshipsDefinitionFor({ type: definition.inverseType })[definition.key];
375
383
  let adapter = this.store.adapterFor(parentIdentifier.type);
376
384
 
377
385
  /*
@@ -394,16 +402,16 @@ export class LegacySupport {
394
402
  typeof adapter.findHasMany === 'function'
395
403
  );
396
404
 
397
- return _findHasMany(adapter, this.store, parentIdentifier, resource.links.related, relationshipMeta, options);
405
+ return _findHasMany(adapter, this.store, parentIdentifier, resource.links!.related, relationshipMeta, options);
398
406
  }
399
407
 
400
- let preferLocalCache = hasReceivedData && !isEmpty;
401
-
402
- let hasLocalPartialData =
408
+ const preferLocalCache = hasReceivedData && !isEmpty;
409
+ const hasLocalPartialData =
403
410
  hasDematerializedInverse || (isEmpty && Array.isArray(resource.data) && resource.data.length > 0);
404
411
 
405
412
  // fetch using data, pulling from local cache if possible
406
413
  if (!shouldForceReload && !isStale && (preferLocalCache || hasLocalPartialData)) {
414
+ assert(`Expected collection to be an array`, Array.isArray(resource.data));
407
415
  let finds = new Array(resource.data.length);
408
416
  for (let i = 0; i < resource.data.length; i++) {
409
417
  let identifier = this.store.identifierCache.getOrCreateRecordIdentifier(resource.data[i]);
@@ -417,6 +425,7 @@ export class LegacySupport {
417
425
 
418
426
  // fetch by data
419
427
  if (hasData || hasLocalPartialData) {
428
+ assert(`Expected collection to be an array`, Array.isArray(resource.data));
420
429
  let identifiers = resource.data.map((json) => this.store.identifierCache.getOrCreateRecordIdentifier(json));
421
430
  let fetches = new Array(identifiers.length);
422
431
  const manager = this.store._fetchManager;
@@ -438,43 +447,44 @@ export class LegacySupport {
438
447
  }
439
448
 
440
449
  _findBelongsToByJsonApiResource(
441
- resource,
450
+ resource: SingleResourceRelationship,
442
451
  parentIdentifier: StableRecordIdentifier,
443
- relationshipMeta,
452
+ relationship: BelongsToRelationship,
444
453
  options: FindOptions = {}
445
454
  ): Promise<StableRecordIdentifier | null> {
446
455
  if (!resource) {
447
456
  return resolve(null);
448
457
  }
449
458
 
450
- const internalModel = resource.data ? this.store._instanceCache._internalModelForResource(resource.data) : null;
459
+ const identifier = resource.data ? this.store.identifierCache.getOrCreateRecordIdentifier(resource.data) : null;
451
460
 
452
- let { isStale, hasDematerializedInverse, hasReceivedData, isEmpty, shouldForceReload } = resource._relationship
453
- .state as RelationshipState;
454
- const allInverseRecordsAreLoaded = areAllInverseRecordsLoaded(this.store, resource);
461
+ let { isStale, hasDematerializedInverse, hasReceivedData, isEmpty, shouldForceReload } = relationship.state;
455
462
 
456
- let shouldFindViaLink =
457
- resource.links &&
458
- resource.links.related &&
459
- (shouldForceReload || hasDematerializedInverse || isStale || (!allInverseRecordsAreLoaded && !isEmpty));
460
-
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
- }
463
+ // short circuit if we are already loading
464
+ let pendingRequest = identifier && this.store._fetchManager.getPendingFetch(identifier, options);
465
+ if (pendingRequest) {
466
+ return pendingRequest;
467
467
  }
468
468
 
469
+ const allInverseRecordsAreLoaded = areAllInverseRecordsLoaded(this.store, resource);
470
+ const shouldFindViaLink =
471
+ resource.links?.related &&
472
+ (shouldForceReload || hasDematerializedInverse || isStale || (!allInverseRecordsAreLoaded && !isEmpty));
473
+
469
474
  // fetch via link
470
475
  if (shouldFindViaLink) {
471
- return _findBelongsTo(this.store, parentIdentifier, resource.links.related, relationshipMeta, options);
476
+ const relationshipMeta = this.store.getSchemaDefinitionService().relationshipsDefinitionFor(this.identifier)[
477
+ relationship.definition.key
478
+ ];
479
+ assert(`Attempted to access a belongsTo relationship but no definition exists for it`, relationshipMeta);
480
+
481
+ return _findBelongsTo(this.store, parentIdentifier, resource.links!.related, relationshipMeta, options);
472
482
  }
473
483
 
474
484
  let preferLocalCache = hasReceivedData && allInverseRecordsAreLoaded && !isEmpty;
475
485
  let hasLocalPartialData = hasDematerializedInverse || (isEmpty && resource.data);
476
486
  // null is explicit empty, undefined is "we don't know anything"
477
- let localDataIsEmpty = resource.data === undefined || resource.data === null;
487
+ const localDataIsEmpty = resource.data === undefined || resource.data === null;
478
488
 
479
489
  // fetch using data, pulling from local cache if possible
480
490
  if (!shouldForceReload && !isStale && (preferLocalCache || hasLocalPartialData)) {
@@ -485,22 +495,21 @@ export class LegacySupport {
485
495
  return resolve(null);
486
496
  }
487
497
 
488
- if (!internalModel) {
489
- assert(`No InternalModel found for ${resource.lid}`, internalModel);
498
+ if (!identifier) {
499
+ assert(`No Information found for ${resource.data!.lid}`, identifier);
490
500
  }
491
501
 
492
- return this.store._instanceCache._fetchDataIfNeededForIdentifier(internalModel.identifier, options);
502
+ return this.store._instanceCache._fetchDataIfNeededForIdentifier(identifier, options);
493
503
  }
494
504
 
495
- let resourceIsLocal = !localDataIsEmpty && resource.data.id === null;
505
+ let resourceIsLocal = !localDataIsEmpty && resource.data!.id === null;
496
506
 
497
- if (internalModel && resourceIsLocal) {
498
- return resolve(internalModel.identifier);
507
+ if (identifier && resourceIsLocal) {
508
+ return resolve(identifier);
499
509
  }
500
510
 
501
511
  // fetch by data
502
- if (internalModel && !localDataIsEmpty) {
503
- let identifier = internalModel.identifier;
512
+ if (identifier && !localDataIsEmpty) {
504
513
  assertIdentifierHasId(identifier);
505
514
 
506
515
  return this.store._fetchManager.scheduleFetch(identifier, options);
@@ -512,32 +521,28 @@ export class LegacySupport {
512
521
  }
513
522
 
514
523
  destroy() {
515
- assert(
516
- 'Cannot destroy an internalModel while its record is materialized',
517
- !this.record || this.record.isDestroyed || this.record.isDestroying
518
- );
519
524
  this.isDestroying = true;
520
525
 
521
- const cache = this._manyArrayCache;
526
+ let cache: Dict<{ destroy(): void }> = this._manyArrayCache;
527
+ this._manyArrayCache = Object.create(null);
522
528
  Object.keys(cache).forEach((key) => {
523
529
  cache[key]!.destroy();
524
- delete cache[key];
525
530
  });
526
- const keys = Object.keys(this._relationshipProxyCache);
527
- keys.forEach((key) => {
528
- const proxy = this._relationshipProxyCache[key]!;
531
+
532
+ cache = this._relationshipProxyCache;
533
+ this._relationshipProxyCache = Object.create(null);
534
+ Object.keys(cache).forEach((key) => {
535
+ const proxy = cache[key]!;
529
536
  if (proxy.destroy) {
530
537
  proxy.destroy();
531
538
  }
532
- delete this._relationshipProxyCache[key];
533
539
  });
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
- }
540
+
541
+ cache = this.references;
542
+ this.references = Object.create(null);
543
+ Object.keys(cache).forEach((key) => {
544
+ cache[key]!.destroy();
545
+ });
541
546
  this.isDestroyed = true;
542
547
  }
543
548
  }
@@ -625,32 +630,39 @@ function assertRecordsPassedToHasMany(records: RecordInstance[]) {
625
630
  .map((r) => `${typeof r}`)
626
631
  .join(', ')}`,
627
632
  (function () {
628
- return records.every((record) => Object.prototype.hasOwnProperty.call(record, '_internalModel') === true);
633
+ return records.every((record) => {
634
+ try {
635
+ recordIdentifierFor(record);
636
+ return true;
637
+ } catch {
638
+ return false;
639
+ }
640
+ });
629
641
  })()
630
642
  );
631
643
  }
632
644
 
633
- function extractRecordDatasFromRecords(records: RecordInstance[]): RecordData[] {
634
- return records.map(extractRecordDataFromRecord) as RecordData[];
645
+ function extractIdentifiersFromRecords(records: RecordInstance[]): StableRecordIdentifier[] {
646
+ return records.map(extractIdentifierFromRecord) as StableRecordIdentifier[];
635
647
  }
636
648
 
637
- type PromiseProxyRecord = { then(): void; get(str: 'content'): RecordInstance | null | undefined };
649
+ type PromiseProxyRecord = { then(): void; content: RecordInstance | null | undefined };
638
650
 
639
- function extractRecordDataFromRecord(recordOrPromiseRecord: PromiseProxyRecord | RecordInstance | null) {
651
+ function extractIdentifierFromRecord(recordOrPromiseRecord: PromiseProxyRecord | RecordInstance | null) {
640
652
  if (!recordOrPromiseRecord) {
641
653
  return null;
642
654
  }
643
655
 
644
656
  if (isPromiseRecord(recordOrPromiseRecord)) {
645
- let content = recordOrPromiseRecord.get && recordOrPromiseRecord.get('content');
657
+ let content = recordOrPromiseRecord.content;
646
658
  assert(
647
659
  '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
660
  content !== undefined
649
661
  );
650
- return content ? recordDataFor(content) : null;
662
+ return content ? recordIdentifierFor(content) : null;
651
663
  }
652
664
 
653
- return recordDataFor(recordOrPromiseRecord);
665
+ return recordIdentifierFor(recordOrPromiseRecord);
654
666
  }
655
667
 
656
668
  function isPromiseRecord(record: PromiseProxyRecord | RecordInstance): record is PromiseProxyRecord {
@@ -658,25 +670,16 @@ function isPromiseRecord(record: PromiseProxyRecord | RecordInstance): record is
658
670
  }
659
671
 
660
672
  function anyUnloaded(store: Store, relationship: ManyRelationship) {
661
- let state = relationship.currentState;
673
+ let state = relationship.localState;
674
+ const cache = store._instanceCache;
662
675
  const unloaded = state.find((s) => {
663
- let im = store._instanceCache.getInternalModel(s);
664
- return im._isDematerializing || !im.isLoaded;
676
+ let isLoaded = cache.recordIsLoaded(s, true);
677
+ return !isLoaded;
665
678
  });
666
679
 
667
680
  return unloaded || false;
668
681
  }
669
682
 
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
683
  function areAllInverseRecordsLoaded(store: Store, resource: JsonApiRelationship): boolean {
681
684
  const cache = store.identifierCache;
682
685
 
@@ -684,7 +687,7 @@ function areAllInverseRecordsLoaded(store: Store, resource: JsonApiRelationship)
684
687
  // treat as collection
685
688
  // check for unloaded records
686
689
  let hasEmptyRecords = resource.data.reduce((hasEmptyModel, resourceIdentifier) => {
687
- return hasEmptyModel || internalModelForRelatedResource(store, cache, resourceIdentifier).isEmpty;
690
+ return hasEmptyModel || isEmpty(store, cache, resourceIdentifier);
688
691
  }, false);
689
692
 
690
693
  return !hasEmptyRecords;
@@ -693,17 +696,19 @@ function areAllInverseRecordsLoaded(store: Store, resource: JsonApiRelationship)
693
696
  if (!resource.data) {
694
697
  return true;
695
698
  } else {
696
- const internalModel = internalModelForRelatedResource(store, cache, resource.data);
697
- return !internalModel.isEmpty;
699
+ return !isEmpty(store, cache, resource.data);
698
700
  }
699
701
  }
700
702
  }
701
703
 
702
- function internalModelForRelatedResource(
703
- store: Store,
704
- cache: IdentifierCache,
705
- resource: ResourceIdentifierObject
706
- ): InternalModel {
704
+ function isEmpty(store: Store, cache: IdentifierCache, resource: ResourceIdentifierObject): boolean {
707
705
  const identifier = cache.getOrCreateRecordIdentifier(resource);
708
- return store._instanceCache._internalModelForResource(identifier);
706
+ const recordData = store._instanceCache.__instances.recordData.get(identifier);
707
+ return !recordData || recordData.isEmpty(identifier);
708
+ }
709
+
710
+ function isBelongsTo(
711
+ relationship: BelongsToRelationship | ImplicitRelationship | ManyRelationship
712
+ ): relationship is BelongsToRelationship {
713
+ return relationship.definition.kind === 'belongsTo';
709
714
  }
@@ -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;
276
- store._backburner.join(() => {
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;
283
+ store._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