@ember-data/store 4.4.0-alpha.8 → 4.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/addon/-private/system/core-store.ts +119 -130
  2. package/addon/-private/system/ds-model-store.ts +10 -1
  3. package/addon/-private/system/fetch-manager.ts +21 -48
  4. package/addon/-private/system/model/internal-model.ts +160 -264
  5. package/addon/-private/system/{promise-proxies.ts → promise-proxies.js} +21 -31
  6. package/addon/-private/system/{record-array-manager.ts → record-array-manager.js} +60 -87
  7. package/addon/-private/system/record-arrays/adapter-populated-record-array.js +95 -0
  8. package/addon/-private/system/record-arrays/{record-array.ts → record-array.js} +75 -96
  9. package/addon/-private/system/record-data-for.ts +0 -2
  10. package/addon/-private/system/references/belongs-to.ts +2 -3
  11. package/addon/-private/system/references/has-many.ts +2 -4
  12. package/addon/-private/system/schema-definition-service.ts +2 -2
  13. package/addon/-private/system/snapshot-record-array.ts +11 -12
  14. package/addon/-private/system/snapshot.ts +7 -24
  15. package/addon/-private/system/store/common.js +1 -24
  16. package/addon/-private/system/store/finders.js +5 -53
  17. package/addon/-private/system/store/internal-model-factory.ts +7 -8
  18. package/addon/-private/system/store/record-data-store-wrapper.ts +2 -7
  19. package/addon/-private/system/store/serializer-response.js +71 -0
  20. package/addon/-private/ts-interfaces/ds-model.ts +7 -15
  21. package/addon/-private/ts-interfaces/ember-data-json-api.ts +0 -3
  22. package/addon/-private/ts-interfaces/minimum-adapter-interface.ts +20 -19
  23. package/addon/-private/ts-interfaces/minimum-serializer-interface.ts +6 -27
  24. package/addon/-private/ts-interfaces/record-data.ts +1 -4
  25. package/addon/-private/ts-interfaces/record-instance.ts +1 -3
  26. package/addon/-private/ts-interfaces/store.ts +0 -1
  27. package/addon/-private/utils/promise-record.ts +3 -3
  28. package/index.js +0 -3
  29. package/package.json +6 -7
  30. package/addon/-private/system/promise-proxy-base.js +0 -7
  31. package/addon/-private/system/record-arrays/adapter-populated-record-array.ts +0 -129
  32. package/addon/-private/system/store/serializer-response.ts +0 -85
@@ -5,19 +5,8 @@ import { get } from '@ember/object';
5
5
  import { _backburner as emberBackburner, cancel, run } from '@ember/runloop';
6
6
  import { DEBUG } from '@glimmer/env';
7
7
 
8
- import { importSync } from '@embroider/macros';
9
- import RSVP, { resolve } from 'rsvp';
8
+ import RSVP, { Promise } from 'rsvp';
10
9
 
11
- import type { ManyArray } from '@ember-data/model/-private';
12
- import RecordState from '@ember-data/model/-private/record-state';
13
- import type { ManyArrayCreateArgs } from '@ember-data/model/-private/system/many-array';
14
- import type {
15
- BelongsToProxyCreateArgs,
16
- BelongsToProxyMeta,
17
- } from '@ember-data/model/-private/system/promise-belongs-to';
18
- import type PromiseBelongsTo from '@ember-data/model/-private/system/promise-belongs-to';
19
- import type { HasManyProxyCreateArgs } from '@ember-data/model/-private/system/promise-many-array';
20
- import type PromiseManyArray from '@ember-data/model/-private/system/promise-many-array';
21
10
  import { HAS_MODEL_PACKAGE, HAS_RECORD_DATA_PACKAGE } from '@ember-data/private-build-infra';
22
11
  import type {
23
12
  BelongsToRelationship,
@@ -25,21 +14,16 @@ import type {
25
14
  RecordData as DefaultRecordData,
26
15
  } from '@ember-data/record-data/-private';
27
16
  import type { UpgradedMeta } from '@ember-data/record-data/-private/graph/-edge-definition';
28
- import type {
29
- DefaultSingleResourceRelationship,
30
- RelationshipRecordData,
31
- } from '@ember-data/record-data/-private/ts-interfaces/relationship-record-data';
32
17
 
33
- import type { DSModel } from '../../ts-interfaces/ds-model';
18
+ import { DSModel } from '../../ts-interfaces/ds-model';
34
19
  import type { StableRecordIdentifier } from '../../ts-interfaces/identifier';
35
- import type { ChangedAttributesHash, RecordData } from '../../ts-interfaces/record-data';
20
+ import type { RecordData } from '../../ts-interfaces/record-data';
36
21
  import type { JsonApiResource, JsonApiValidationError } from '../../ts-interfaces/record-data-json-api';
37
- import type { RelationshipSchema } from '../../ts-interfaces/record-data-schemas';
38
22
  import type { RecordInstance } from '../../ts-interfaces/record-instance';
39
23
  import type { FindOptions } from '../../ts-interfaces/store';
40
- import type { Dict } from '../../ts-interfaces/utils';
24
+ import type { ConfidentDict } from '../../ts-interfaces/utils';
41
25
  import type CoreStore from '../core-store';
42
- import type { CreateRecordProperties } from '../core-store';
26
+ import type Store from '../ds-model-store';
43
27
  import { errorsHashToArray } from '../errors-utils';
44
28
  import recordDataFor from '../record-data-for';
45
29
  import { BelongsToReference, HasManyReference, RecordReference } from '../references';
@@ -47,11 +31,10 @@ import Snapshot from '../snapshot';
47
31
  import { internalModelFactoryFor } from '../store/internal-model-factory';
48
32
  import RootState from './states';
49
33
 
50
- type PrivateModelModule = {
51
- ManyArray: { create(args: ManyArrayCreateArgs): ManyArray };
52
- PromiseBelongsTo: { create(args: BelongsToProxyCreateArgs): PromiseBelongsTo };
53
- PromiseManyArray: new (...args: unknown[]) => PromiseManyArray;
54
- };
34
+ // move to TS hacks module that we can delete when this is no longer a necessary recast
35
+ type ManyArray = InstanceType<typeof import('@ember-data/model/-private').ManyArray>;
36
+ type PromiseBelongsTo = InstanceType<typeof import('@ember-data/model/-private').PromiseBelongsTo>;
37
+ type PromiseManyArray = InstanceType<typeof import('@ember-data/model/-private').PromiseManyArray>;
55
38
 
56
39
  /**
57
40
  @module @ember-data/store
@@ -59,22 +42,18 @@ type PrivateModelModule = {
59
42
 
60
43
  const { hasOwnProperty } = Object.prototype;
61
44
 
62
- let _ManyArray: PrivateModelModule['ManyArray'];
63
- let _PromiseBelongsTo: PrivateModelModule['PromiseBelongsTo'];
64
- let _PromiseManyArray: PrivateModelModule['PromiseManyArray'];
45
+ let ManyArray: ManyArray;
46
+ let PromiseBelongsTo: PromiseBelongsTo;
47
+ let _PromiseManyArray: any; // TODO find a way to get the klass type here
65
48
 
66
49
  let _found = false;
67
50
  let _getModelPackage: () => boolean;
68
51
  if (HAS_MODEL_PACKAGE) {
69
52
  _getModelPackage = function () {
70
53
  if (!_found) {
71
- let modelPackage = importSync('@ember-data/model/-private') as PrivateModelModule;
72
- ({
73
- ManyArray: _ManyArray,
74
- PromiseBelongsTo: _PromiseBelongsTo,
75
- PromiseManyArray: _PromiseManyArray,
76
- } = modelPackage);
77
- if (_ManyArray && _PromiseBelongsTo && _PromiseManyArray) {
54
+ let modelPackage = require('@ember-data/model/-private');
55
+ ({ ManyArray, PromiseBelongsTo, PromiseManyArray: _PromiseManyArray } = modelPackage);
56
+ if (ManyArray && PromiseBelongsTo && _PromiseManyArray) {
78
57
  _found = true;
79
58
  }
80
59
  }
@@ -82,6 +61,13 @@ if (HAS_MODEL_PACKAGE) {
82
61
  };
83
62
  }
84
63
 
64
+ interface BelongsToMetaWrapper {
65
+ key: string;
66
+ store: CoreStore;
67
+ originatingInternalModel: InternalModel;
68
+ modelName: string;
69
+ }
70
+
85
71
  /*
86
72
  The TransitionChainMap caches the `state.enters`, `state.setups`, and final state reached
87
73
  when transitioning from one state to another, so that future transitions can replay the
@@ -92,32 +78,18 @@ if (HAS_MODEL_PACKAGE) {
92
78
  and setups. It may also be faster to do a two level cache (from: { to }) instead of caching based
93
79
  on a key that adds the two together.
94
80
  */
95
- // TODO before deleting the state machine we should
96
- // ensure all things in this map were properly accounted for.
97
- // in the RecordState class.
98
81
  const TransitionChainMap = Object.create(null);
99
82
 
100
83
  const _extractPivotNameCache = Object.create(null);
101
84
  const _splitOnDotCache = Object.create(null);
102
85
 
103
- function splitOnDot(name: string): string[] {
86
+ function splitOnDot(name) {
104
87
  return _splitOnDotCache[name] || (_splitOnDotCache[name] = name.split('.'));
105
88
  }
106
89
 
107
- function extractPivotName(name: string): string {
90
+ function extractPivotName(name) {
108
91
  return _extractPivotNameCache[name] || (_extractPivotNameCache[name] = splitOnDot(name)[0]);
109
92
  }
110
-
111
- function isDSModel(record: RecordInstance | null): record is DSModel {
112
- return (
113
- HAS_MODEL_PACKAGE &&
114
- !!record &&
115
- 'constructor' in record &&
116
- 'isModel' in record.constructor &&
117
- record.constructor.isModel === true
118
- );
119
- }
120
-
121
93
  export default class InternalModel {
122
94
  declare _id: string | null;
123
95
  declare modelName: string;
@@ -134,29 +106,25 @@ export default class InternalModel {
134
106
 
135
107
  // Not typed yet
136
108
  declare _promiseProxy: any;
137
- declare _record: RecordInstance | null;
109
+ declare _record: any;
138
110
  declare _scheduledDestroy: any;
139
111
  declare _modelClass: any;
140
112
  declare _deferredTriggers: any;
141
113
  declare __recordArrays: any;
142
114
  declare references: any;
143
115
  declare _recordReference: RecordReference;
144
- declare _manyArrayCache: Dict<ManyArray>;
116
+ declare _manyArrayCache: ConfidentDict<ManyArray>;
145
117
 
146
- declare _relationshipPromisesCache: Dict<Promise<ManyArray | RecordInstance>>;
147
- declare _relationshipProxyCache: Dict<PromiseManyArray | PromiseBelongsTo>;
118
+ declare _relationshipPromisesCache: ConfidentDict<RSVP.Promise<any>>;
119
+ declare _relationshipProxyCache: ConfidentDict<PromiseManyArray | PromiseBelongsTo>;
148
120
  declare error: any;
149
- declare currentState: RecordState;
121
+ declare currentState: any;
150
122
  declare _previousState: any;
151
- declare store: CoreStore;
152
- declare identifier: StableRecordIdentifier;
153
123
 
154
- constructor(store: CoreStore, identifier: StableRecordIdentifier) {
124
+ constructor(public store: CoreStore | Store, public identifier: StableRecordIdentifier) {
155
125
  if (HAS_MODEL_PACKAGE) {
156
126
  _getModelPackage();
157
127
  }
158
- this.store = store;
159
- this.identifier = identifier;
160
128
  this._id = identifier.id;
161
129
  this._isUpdatingId = false;
162
130
  this.modelName = identifier.type;
@@ -288,28 +256,15 @@ export default class InternalModel {
288
256
  }
289
257
  }
290
258
 
291
- getRecord(properties?: CreateRecordProperties): RecordInstance {
292
- let record = this._record;
293
-
294
- if (this._isDematerializing) {
295
- // TODO we should assert here instead of this return.
296
- return null as unknown as RecordInstance;
297
- }
298
-
299
- if (!record) {
259
+ getRecord(properties?): Object {
260
+ if (!this._record && !this._isDematerializing) {
300
261
  let { store } = this;
301
262
 
302
- record = this._record = store._instantiateRecord(
303
- this,
304
- this.modelName,
305
- this._recordData,
306
- this.identifier,
307
- properties
308
- );
263
+ this._record = store._instantiateRecord(this, this.modelName, this._recordData, this.identifier, properties);
309
264
  this._triggerDeferredTriggers();
310
265
  }
311
266
 
312
- return record;
267
+ return this._record;
313
268
  }
314
269
 
315
270
  dematerializeRecord() {
@@ -333,11 +288,9 @@ export default class InternalModel {
333
288
  });
334
289
 
335
290
  if (this._record) {
336
- let keys = Object.keys(this._relationshipProxyCache);
337
- keys.forEach((key) => {
338
- let proxy = this._relationshipProxyCache[key]!;
339
- if (proxy.destroy) {
340
- proxy.destroy();
291
+ Object.keys(this._relationshipProxyCache).forEach((key) => {
292
+ if (this._relationshipProxyCache[key].destroy) {
293
+ this._relationshipProxyCache[key].destroy();
341
294
  }
342
295
  delete this._relationshipProxyCache[key];
343
296
  });
@@ -371,9 +324,9 @@ export default class InternalModel {
371
324
  });
372
325
  }
373
326
 
374
- save(options: FindOptions = {}): Promise<void> {
327
+ save(options): Promise<void> {
375
328
  if (this._deletedRecordWasNew) {
376
- return resolve();
329
+ return Promise.resolve();
377
330
  }
378
331
  let promiseLabel = 'DS: Model#save ' + this;
379
332
  let resolver = RSVP.defer<void>(promiseLabel);
@@ -382,8 +335,22 @@ export default class InternalModel {
382
335
  return this.store.scheduleSave(this, resolver, options) as Promise<void>;
383
336
  }
384
337
 
385
- reload(options: Dict<unknown> = {}): Promise<InternalModel> {
386
- return this.store._reloadRecord(this, options);
338
+ reload(options) {
339
+ if (!options) {
340
+ options = {};
341
+ }
342
+ let internalModel = this;
343
+
344
+ return internalModel.store._reloadRecord(internalModel, options).then(
345
+ function () {
346
+ //TODO NOW seems like we shouldn't need to do this
347
+ return internalModel;
348
+ },
349
+ function (error) {
350
+ throw error;
351
+ },
352
+ 'DS: Model#reload complete, update flags'
353
+ );
387
354
  }
388
355
 
389
356
  /*
@@ -458,32 +425,26 @@ export default class InternalModel {
458
425
  }
459
426
  }
460
427
 
461
- _findBelongsTo(
462
- key: string,
463
- resource: DefaultSingleResourceRelationship,
464
- relationshipMeta: RelationshipSchema,
465
- options?: Dict<unknown>
466
- ): Promise<RecordInstance | null> {
428
+ _findBelongsTo(key, resource, relationshipMeta, options) {
467
429
  // TODO @runspired follow up if parent isNew then we should not be attempting load here
468
- // TODO @runspired follow up on whether this should be in the relationship requests cache
469
430
  return this.store._findBelongsToByJsonApiResource(resource, this, relationshipMeta, options).then(
470
- (internalModel) => handleCompletedRelationshipRequest(this, key, resource._relationship, internalModel),
431
+ (internalModel) => handleCompletedRelationshipRequest(this, key, resource._relationship, internalModel, null),
471
432
  (e) => handleCompletedRelationshipRequest(this, key, resource._relationship, null, e)
472
433
  );
473
434
  }
474
435
 
475
- getBelongsTo(key: string, options?: Dict<unknown>): PromiseBelongsTo | RecordInstance | null {
436
+ getBelongsTo(key, options) {
476
437
  let resource = (this._recordData as DefaultRecordData).getBelongsTo(key);
477
438
  let identifier =
478
439
  resource && resource.data ? this.store.identifierCache.getOrCreateRecordIdentifier(resource.data) : null;
479
440
  let relationshipMeta = this.store._relationshipMetaFor(this.modelName, null, key);
480
- assert(`Attempted to access a belongsTo relationship but no definition exists for it`, relationshipMeta);
441
+ if (!relationshipMeta) return;
481
442
 
482
443
  let store = this.store;
483
444
  let parentInternalModel = this;
484
445
  let async = relationshipMeta.options.async;
485
446
  let isAsync = typeof async === 'undefined' ? true : async;
486
- let _belongsToState: BelongsToProxyMeta = {
447
+ let _belongsToState: BelongsToMetaWrapper = {
487
448
  key,
488
449
  store,
489
450
  originatingInternalModel: this,
@@ -494,7 +455,7 @@ export default class InternalModel {
494
455
  let internalModel = identifier !== null ? store._internalModelForResource(identifier) : null;
495
456
 
496
457
  if (resource._relationship.state.hasFailedLoadAttempt) {
497
- return this._relationshipProxyCache[key] as PromiseBelongsTo;
458
+ return this._relationshipProxyCache[key];
498
459
  }
499
460
 
500
461
  let promise = this._findBelongsTo(key, resource, relationshipMeta, options);
@@ -518,49 +479,49 @@ export default class InternalModel {
518
479
  "' with id " +
519
480
  parentInternalModel.id +
520
481
  ' 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 })`)',
521
- toReturn === null || !internalModel.currentState.isEmpty
482
+ toReturn === null || !(toReturn as DSModel).isEmpty
522
483
  );
523
484
  return toReturn;
524
485
  }
525
486
  }
526
487
  }
527
488
 
528
- getManyArray(key: string, definition?: UpgradedMeta): ManyArray {
529
- assert('hasMany only works with the @ember-data/record-data package', HAS_RECORD_DATA_PACKAGE);
530
- let manyArray: ManyArray | undefined = this._manyArrayCache[key];
531
- if (!definition) {
532
- const graphFor = (
533
- importSync('@ember-data/record-data/-private') as typeof import('@ember-data/record-data/-private')
534
- ).graphFor;
535
- definition = graphFor(this.store).get(this.identifier, key).definition as UpgradedMeta;
536
- }
537
-
538
- if (!manyArray) {
539
- manyArray = _ManyArray.create({
540
- store: this.store,
541
- type: this.store.modelFor(definition.type),
542
- recordData: this._recordData as RelationshipRecordData,
543
- key,
544
- isPolymorphic: definition.isPolymorphic,
545
- isAsync: definition.isAsync,
546
- _inverseIsAsync: definition.inverseIsAsync,
547
- internalModel: this,
548
- isLoaded: !definition.isAsync,
549
- });
550
- this._manyArrayCache[key] = manyArray;
551
- }
489
+ getManyArray(key: string, definition?: UpgradedMeta) {
490
+ if (HAS_RECORD_DATA_PACKAGE) {
491
+ let manyArray = this._manyArrayCache[key];
492
+ if (!definition) {
493
+ const graphFor = require('@ember-data/record-data/-private').graphFor;
494
+ definition = graphFor(this.store).get(this.identifier, key).definition as UpgradedMeta;
495
+ }
552
496
 
553
- return manyArray;
497
+ if (!manyArray) {
498
+ manyArray = ManyArray.create({
499
+ store: this.store,
500
+ type: this.store.modelFor(definition.type),
501
+ recordData: this._recordData,
502
+ key,
503
+ isPolymorphic: definition.isPolymorphic,
504
+ isAsync: definition.isAsync,
505
+ _inverseIsAsync: definition.inverseIsAsync,
506
+ internalModel: this,
507
+ isLoaded: !definition.isAsync,
508
+ });
509
+ this._manyArrayCache[key] = manyArray;
510
+ }
511
+
512
+ return manyArray;
513
+ }
514
+ assert(`hasMany only works with the @ember-data/record-data package`, HAS_RECORD_DATA_PACKAGE);
554
515
  }
555
516
 
556
517
  fetchAsyncHasMany(
557
518
  key: string,
558
- relationship: ManyRelationship,
559
- manyArray: ManyArray,
560
- options?: Dict<unknown>
561
- ): Promise<ManyArray> {
519
+ relationship: ManyRelationship | BelongsToRelationship,
520
+ manyArray,
521
+ options
522
+ ): RSVP.Promise<unknown> {
562
523
  if (HAS_RECORD_DATA_PACKAGE) {
563
- let loadingPromise = this._relationshipPromisesCache[key] as Promise<ManyArray> | undefined;
524
+ let loadingPromise = this._relationshipPromisesCache[key];
564
525
  if (loadingPromise) {
565
526
  return loadingPromise;
566
527
  }
@@ -568,27 +529,25 @@ export default class InternalModel {
568
529
  const jsonApi = this._recordData.getHasMany(key);
569
530
 
570
531
  loadingPromise = this.store._findHasManyByJsonApiResource(jsonApi, this, relationship, options).then(
571
- () => handleCompletedRelationshipRequest(this, key, relationship, manyArray),
532
+ () => handleCompletedRelationshipRequest(this, key, relationship, manyArray, null),
572
533
  (e) => handleCompletedRelationshipRequest(this, key, relationship, manyArray, e)
573
534
  );
574
535
  this._relationshipPromisesCache[key] = loadingPromise;
575
536
  return loadingPromise;
576
537
  }
577
- assert('hasMany only works with the @ember-data/record-data package');
538
+ assert(`hasMany only works with the @ember-data/record-data package`);
578
539
  }
579
540
 
580
- getHasMany(key: string, options?): PromiseManyArray | ManyArray {
541
+ getHasMany(key: string, options?) {
581
542
  if (HAS_RECORD_DATA_PACKAGE) {
582
- const graphFor = (
583
- importSync('@ember-data/record-data/-private') as typeof import('@ember-data/record-data/-private')
584
- ).graphFor;
585
- const relationship = graphFor(this.store).get(this.identifier, key) as ManyRelationship;
543
+ const graphFor = require('@ember-data/record-data/-private').graphFor;
544
+ const relationship = graphFor(this.store).get(this.identifier, key);
586
545
  const { definition, state } = relationship;
587
546
  let manyArray = this.getManyArray(key, definition);
588
547
 
589
548
  if (definition.isAsync) {
590
549
  if (state.hasFailedLoadAttempt) {
591
- return this._relationshipProxyCache[key] as PromiseManyArray;
550
+ return this._relationshipProxyCache[key];
592
551
  }
593
552
 
594
553
  let promise = this.fetchAsyncHasMany(key, relationship, manyArray, options);
@@ -606,55 +565,47 @@ export default class InternalModel {
606
565
  assert(`hasMany only works with the @ember-data/record-data package`);
607
566
  }
608
567
 
609
- _updatePromiseProxyFor(kind: 'hasMany', key: string, args: HasManyProxyCreateArgs): PromiseManyArray;
610
- _updatePromiseProxyFor(kind: 'belongsTo', key: string, args: BelongsToProxyCreateArgs): PromiseBelongsTo;
611
- _updatePromiseProxyFor(
612
- kind: 'belongsTo',
613
- key: string,
614
- args: { promise: Promise<RecordInstance | null> }
615
- ): PromiseBelongsTo;
616
568
  _updatePromiseProxyFor(
617
569
  kind: 'hasMany' | 'belongsTo',
618
570
  key: string,
619
- args: BelongsToProxyCreateArgs | HasManyProxyCreateArgs | { promise: Promise<RecordInstance | null> }
620
- ): PromiseBelongsTo | PromiseManyArray {
571
+ args: {
572
+ promise: RSVP.Promise<any>;
573
+ content?: RecordInstance | ManyArray | null;
574
+ _belongsToState?: BelongsToMetaWrapper;
575
+ }
576
+ ) {
621
577
  let promiseProxy = this._relationshipProxyCache[key];
622
578
  if (kind === 'hasMany') {
623
- const { promise, content } = args as HasManyProxyCreateArgs;
624
579
  if (promiseProxy) {
625
- assert(`Expected a PromiseManyArray`, '_update' in promiseProxy);
626
- promiseProxy._update(promise, content);
580
+ promiseProxy._update(args.promise, args.content);
627
581
  } else {
628
- promiseProxy = this._relationshipProxyCache[key] = new _PromiseManyArray(promise, content);
582
+ promiseProxy = this._relationshipProxyCache[key] = new _PromiseManyArray(args.promise, args.content);
629
583
  }
630
584
  return promiseProxy;
631
585
  }
632
586
  if (promiseProxy) {
633
- const { promise, content } = args as BelongsToProxyCreateArgs;
634
- assert(`Expected a PromiseBelongsTo`, '_belongsToState' in promiseProxy);
635
-
636
- if (content !== undefined) {
637
- promiseProxy.set('content', content);
587
+ if (args.content !== undefined) {
588
+ // this usage of `any` can be removed when `@types/ember_object` proxy allows `null` for content
589
+ promiseProxy.set('content', args.content as any);
638
590
  }
639
- promiseProxy.set('promise', promise);
591
+ promiseProxy.set('promise', args.promise);
640
592
  } else {
593
+ const klass = PromiseBelongsTo;
641
594
  // this usage of `any` can be removed when `@types/ember_object` proxy allows `null` for content
642
- this._relationshipProxyCache[key] = promiseProxy = _PromiseBelongsTo.create(args as any);
595
+ this._relationshipProxyCache[key] = klass.create(args as any);
643
596
  }
644
597
 
645
- return promiseProxy;
598
+ return this._relationshipProxyCache[key];
646
599
  }
647
600
 
648
- reloadHasMany(key: string, options) {
601
+ reloadHasMany(key, options) {
649
602
  if (HAS_RECORD_DATA_PACKAGE) {
650
603
  let loadingPromise = this._relationshipPromisesCache[key];
651
604
  if (loadingPromise) {
652
605
  return loadingPromise;
653
606
  }
654
- const graphFor = (
655
- importSync('@ember-data/record-data/-private') as typeof import('@ember-data/record-data/-private')
656
- ).graphFor;
657
- const relationship = graphFor(this.store).get(this.identifier, key) as ManyRelationship;
607
+ const graphFor = require('@ember-data/record-data/-private').graphFor;
608
+ const relationship = graphFor(this.store).get(this.identifier, key);
658
609
  const { definition, state } = relationship;
659
610
 
660
611
  state.hasFailedLoadAttempt = false;
@@ -671,8 +622,8 @@ export default class InternalModel {
671
622
  assert(`hasMany only works with the @ember-data/record-data package`);
672
623
  }
673
624
 
674
- reloadBelongsTo(key: string, options?: Dict<unknown>): Promise<RecordInstance | null> {
675
- let loadingPromise = this._relationshipPromisesCache[key] as Promise<RecordInstance | null> | undefined;
625
+ reloadBelongsTo(key, options) {
626
+ let loadingPromise = this._relationshipPromisesCache[key];
676
627
  if (loadingPromise) {
677
628
  return loadingPromise;
678
629
  }
@@ -684,7 +635,6 @@ export default class InternalModel {
684
635
  resource._relationship.state.shouldForceReload = true;
685
636
  }
686
637
  let relationshipMeta = this.store._relationshipMetaFor(this.modelName, null, key);
687
- assert(`Attempted to reload a belongsTo relationship but no definition exists for it`, relationshipMeta);
688
638
  let promise = this._findBelongsTo(key, resource, relationshipMeta, options);
689
639
  if (this._relationshipProxyCache[key]) {
690
640
  return this._updatePromiseProxyFor('belongsTo', key, { promise });
@@ -703,7 +653,7 @@ export default class InternalModel {
703
653
  destroy() {
704
654
  assert(
705
655
  'Cannot destroy an internalModel while its record is materialized',
706
- !this._record || this._record.isDestroyed || this._record.isDestroying
656
+ !this._record || this._record.get('isDestroyed') || this._record.get('isDestroying')
707
657
  );
708
658
  this.isDestroying = true;
709
659
  if (this._recordReference) {
@@ -712,13 +662,13 @@ export default class InternalModel {
712
662
  this._recordReference = null;
713
663
  let cache = this._manyArrayCache;
714
664
  Object.keys(cache).forEach((key) => {
715
- cache[key]!.destroy();
665
+ cache[key].destroy();
716
666
  delete cache[key];
717
667
  });
718
668
  if (this.references) {
719
669
  cache = this.references;
720
670
  Object.keys(cache).forEach((key) => {
721
- cache[key]!.destroy();
671
+ cache[key].destroy();
722
672
  delete cache[key];
723
673
  });
724
674
  }
@@ -728,35 +678,24 @@ export default class InternalModel {
728
678
  }
729
679
 
730
680
  setupData(data) {
731
- const hasRecord = this.hasRecord;
732
- if (hasRecord) {
733
- let changedKeys = this._recordData.pushData(data, true);
734
- this.notifyAttributes(changedKeys);
735
- } else {
736
- this._recordData.pushData(data);
681
+ let changedKeys = this._recordData.pushData(data, this.hasRecord);
682
+ if (this.hasRecord) {
683
+ // TODO @runspired should this be going through the notification manager?
684
+ this._record._notifyProperties(changedKeys);
737
685
  }
738
686
  this.send('pushedData');
739
687
  }
740
688
 
741
- notifyAttributes(keys: string[]): void {
742
- let manager = this.store._notificationManager;
743
- let { identifier } = this;
744
-
745
- for (let i = 0; i < keys.length; i++) {
746
- manager.notify(identifier, 'attributes', keys[i]);
747
- }
748
- }
749
-
750
- setDirtyHasMany(key: string, records) {
689
+ setDirtyHasMany(key, records) {
751
690
  assertRecordsPassedToHasMany(records);
752
691
  return this._recordData.setDirtyHasMany(key, extractRecordDatasFromRecords(records));
753
692
  }
754
693
 
755
- setDirtyBelongsTo(key: string, value) {
694
+ setDirtyBelongsTo(key, value) {
756
695
  return this._recordData.setDirtyBelongsTo(key, extractRecordDataFromRecord(value));
757
696
  }
758
697
 
759
- setDirtyAttribute<T>(key: string, value: T): T {
698
+ setDirtyAttribute(key, value) {
760
699
  if (this.isDeleted()) {
761
700
  if (DEBUG) {
762
701
  throw new EmberError(`Attempted to set '${key}' to '${value}' on the deleted record ${this}`);
@@ -778,11 +717,11 @@ export default class InternalModel {
778
717
  return value;
779
718
  }
780
719
 
781
- get isDestroyed(): boolean {
720
+ get isDestroyed() {
782
721
  return this._isDestroyed;
783
722
  }
784
723
 
785
- get hasRecord(): boolean {
724
+ get hasRecord() {
786
725
  return !!this._record;
787
726
  }
788
727
 
@@ -790,7 +729,7 @@ export default class InternalModel {
790
729
  return new Snapshot(options, this.identifier, this.store);
791
730
  }
792
731
 
793
- hasChangedAttributes(): boolean {
732
+ hasChangedAttributes() {
794
733
  if (!this.__recordData) {
795
734
  // no need to calculate changed attributes when calling `findRecord`
796
735
  return false;
@@ -798,7 +737,7 @@ export default class InternalModel {
798
737
  return this._recordData.hasChangedAttributes();
799
738
  }
800
739
 
801
- changedAttributes(): ChangedAttributesHash {
740
+ changedAttributes() {
802
741
  if (!this.__recordData) {
803
742
  // no need to calculate changed attributes when calling `findRecord`
804
743
  return {};
@@ -806,16 +745,16 @@ export default class InternalModel {
806
745
  return this._recordData.changedAttributes();
807
746
  }
808
747
 
809
- adapterWillCommit(): void {
748
+ adapterWillCommit() {
810
749
  this._recordData.willCommit();
811
750
  this.send('willCommit');
812
751
  }
813
752
 
814
- adapterDidDirty(): void {
753
+ adapterDidDirty() {
815
754
  this.send('becomeDirty');
816
755
  }
817
756
 
818
- send(name: string, context?) {
757
+ send(name, context?) {
819
758
  let currentState = this.currentState;
820
759
 
821
760
  if (!currentState[name]) {
@@ -846,7 +785,7 @@ export default class InternalModel {
846
785
  }
847
786
  }
848
787
 
849
- notifyPropertyChange(key: string) {
788
+ notifyPropertyChange(key) {
850
789
  if (this.hasRecord) {
851
790
  // TODO this should likely *mostly* be the `attributes` bucket
852
791
  // but it seems for local mutations we rely on computed updating
@@ -856,7 +795,7 @@ export default class InternalModel {
856
795
  }
857
796
  }
858
797
 
859
- notifyStateChange(key?: string) {
798
+ notifyStateChange(key?) {
860
799
  if (this.hasRecord) {
861
800
  this.store._notificationManager.notify(this.identifier, 'state');
862
801
  }
@@ -872,24 +811,24 @@ export default class InternalModel {
872
811
  rollbackAttributes() {
873
812
  this.store._backburner.join(() => {
874
813
  let dirtyKeys = this._recordData.rollbackAttributes();
875
- if (this.isError) {
814
+ if (get(this, 'isError')) {
876
815
  this.didCleanError();
877
816
  }
878
817
 
879
818
  this.send('rolledBack');
880
819
 
881
- if (this.hasRecord && dirtyKeys && dirtyKeys.length > 0) {
882
- this.notifyAttributes(dirtyKeys);
820
+ if (this._record && dirtyKeys && dirtyKeys.length > 0) {
821
+ this._record._notifyProperties(dirtyKeys);
883
822
  }
884
823
  });
885
824
  }
886
825
 
887
- transitionTo(name: string) {
826
+ transitionTo(name) {
888
827
  // POSSIBLE TODO: Remove this code and replace with
889
828
  // always having direct reference to state objects
890
829
 
891
830
  let pivotName = extractPivotName(name);
892
- let state: any = this.currentState;
831
+ let state = this.currentState;
893
832
  let transitionMapId = `${state.stateName}->${name}`;
894
833
 
895
834
  do {
@@ -934,12 +873,13 @@ export default class InternalModel {
934
873
  }
935
874
 
936
875
  this.currentState = state;
937
-
938
- // isDSModel is the guard we want, but may be too restrictive if
939
- // ember-m3 / ember-data-model-fragments were relying on this still.
940
- if (this.hasRecord && isDSModel(this._record)) {
941
- // TODO eliminate this.
876
+ if (this.hasRecord && typeof this._record.notifyPropertyChange === 'function') {
877
+ // TODO refactor Model to have all flags pull from the notification manager
878
+ // and for currentState.stateName to be constructed from flag state.
879
+ // Probably just port this work from ember-m3
880
+ // After that we can eliminate this.
942
881
  this.notifyStateChange('currentState');
882
+ // this._record.notifyPropertyChange('currentState');
943
883
  }
944
884
 
945
885
  for (i = 0, l = setups.length; i < l; i++) {
@@ -947,7 +887,7 @@ export default class InternalModel {
947
887
  }
948
888
  }
949
889
 
950
- _unhandledEvent(state, name: string, context) {
890
+ _unhandledEvent(state, name, context) {
951
891
  let errorMessage = 'Attempted to handle event `' + name + '` ';
952
892
  errorMessage += 'on ' + String(this) + ' while in state ';
953
893
  errorMessage += state.stateName + '. ';
@@ -975,7 +915,7 @@ export default class InternalModel {
975
915
  return;
976
916
  }
977
917
  let triggers = this._deferredTriggers;
978
- let record = this._record as DSModel;
918
+ let record = this._record;
979
919
  let trigger = record.trigger;
980
920
  // TODO Igor make nicer check
981
921
  if (trigger && typeof trigger === 'function') {
@@ -1117,34 +1057,26 @@ export default class InternalModel {
1117
1057
  this.store._notificationManager.notify(this.identifier, 'attributes');
1118
1058
  }
1119
1059
 
1120
- hasErrors(): boolean {
1121
- // TODO add assertion forcing consuming RecordData's to implement getErrors
1060
+ hasErrors() {
1122
1061
  if (this._recordData.getErrors) {
1123
1062
  return this._recordData.getErrors(this.identifier).length > 0;
1124
1063
  } else {
1125
- // we can't have errors if we never tried loading
1126
- if (!this._record) {
1127
- return false;
1128
- }
1129
- let errors = (this._record as DSModel).errors;
1064
+ let errors = (this.getRecord() as DSModel).errors;
1130
1065
  return errors.length > 0;
1131
1066
  }
1132
1067
  }
1133
1068
 
1134
1069
  // FOR USE DURING COMMIT PROCESS
1135
- adapterDidInvalidate(parsedErrors, error?) {
1070
+ adapterDidInvalidate(parsedErrors, error) {
1136
1071
  // TODO @runspired this should be handled by RecordState
1137
1072
  // and errors should be dirtied but lazily fetch if at
1138
1073
  // all possible. We should only notify errors here.
1139
1074
  let attribute;
1140
1075
  if (error && parsedErrors) {
1141
- // TODO add assertion forcing consuming RecordData's to implement getErrors
1142
1076
  if (!this._recordData.getErrors) {
1143
- let record = this.getRecord() as DSModel;
1144
- let errors = record.errors;
1145
1077
  for (attribute in parsedErrors) {
1146
1078
  if (hasOwnProperty.call(parsedErrors, attribute)) {
1147
- errors._add(attribute, parsedErrors[attribute]);
1079
+ (this.getRecord() as DSModel).errors._add(attribute, parsedErrors[attribute]);
1148
1080
  }
1149
1081
  }
1150
1082
  }
@@ -1185,9 +1117,7 @@ export default class InternalModel {
1185
1117
  // because of the intimate API access involved. This is something we will need to redesign.
1186
1118
  assert(`snapshot.belongsTo only supported for @ember-data/record-data`);
1187
1119
  }
1188
- const graphFor = (
1189
- importSync('@ember-data/record-data/-private') as typeof import('@ember-data/record-data/-private')
1190
- ).graphFor;
1120
+ const graphFor = require('@ember-data/record-data/-private').graphFor;
1191
1121
  const relationship = graphFor(this.store._storeWrapper).get(this.identifier, name);
1192
1122
 
1193
1123
  if (DEBUG && kind) {
@@ -1215,39 +1145,7 @@ export default class InternalModel {
1215
1145
  }
1216
1146
  }
1217
1147
 
1218
- function handleCompletedRelationshipRequest(
1219
- internalModel: InternalModel,
1220
- key: string,
1221
- relationship: BelongsToRelationship,
1222
- value: InternalModel | null
1223
- ): RecordInstance | null;
1224
- function handleCompletedRelationshipRequest(
1225
- internalModel: InternalModel,
1226
- key: string,
1227
- relationship: ManyRelationship,
1228
- value: ManyArray
1229
- ): ManyArray;
1230
- function handleCompletedRelationshipRequest(
1231
- internalModel: InternalModel,
1232
- key: string,
1233
- relationship: BelongsToRelationship,
1234
- value: null,
1235
- error: Error
1236
- ): never;
1237
- function handleCompletedRelationshipRequest(
1238
- internalModel: InternalModel,
1239
- key: string,
1240
- relationship: ManyRelationship,
1241
- value: ManyArray,
1242
- error: Error
1243
- ): never;
1244
- function handleCompletedRelationshipRequest(
1245
- internalModel: InternalModel,
1246
- key: string,
1247
- relationship: BelongsToRelationship | ManyRelationship,
1248
- value: ManyArray | InternalModel | null,
1249
- error?: Error
1250
- ): ManyArray | RecordInstance | null {
1148
+ function handleCompletedRelationshipRequest(internalModel, key, relationship, value, error) {
1251
1149
  delete internalModel._relationshipPromisesCache[key];
1252
1150
  relationship.state.shouldForceReload = false;
1253
1151
  const isHasMany = relationship.definition.kind === 'hasMany';
@@ -1255,7 +1153,7 @@ function handleCompletedRelationshipRequest(
1255
1153
  if (isHasMany) {
1256
1154
  // we don't notify the record property here to avoid refetch
1257
1155
  // only the many array
1258
- (value as ManyArray).notify();
1156
+ value.notify();
1259
1157
  }
1260
1158
 
1261
1159
  if (error) {
@@ -1270,9 +1168,7 @@ function handleCompletedRelationshipRequest(
1270
1168
  // has never been accessed
1271
1169
  if (proxy && !isHasMany) {
1272
1170
  if (proxy.content && proxy.content.isDestroying) {
1273
- // TODO @types/ember__object incorrectly disallows `null`, we should either
1274
- // override or fix upstream
1275
- (proxy as PromiseBelongsTo).set('content', null as unknown as undefined);
1171
+ proxy.set('content', null);
1276
1172
  }
1277
1173
  }
1278
1174
 
@@ -1280,14 +1176,14 @@ function handleCompletedRelationshipRequest(
1280
1176
  }
1281
1177
 
1282
1178
  if (isHasMany) {
1283
- (value as ManyArray).set('isLoaded', true);
1179
+ value.set('isLoaded', true);
1284
1180
  }
1285
1181
 
1286
1182
  relationship.state.hasFailedLoadAttempt = false;
1287
1183
  // only set to not stale if no error is thrown
1288
1184
  relationship.state.isStale = false;
1289
1185
 
1290
- return isHasMany || !value ? (value as ManyArray | null) : (value as InternalModel).getRecord();
1186
+ return value;
1291
1187
  }
1292
1188
 
1293
1189
  export function assertRecordsPassedToHasMany(records) {