@ember-data/model 4.8.0-alpha.4 → 4.8.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,8 +7,6 @@ import EmberError from '@ember/error';
7
7
  import EmberObject from '@ember/object';
8
8
  import { dependentKeyCompat } from '@ember/object/compat';
9
9
  import { run } from '@ember/runloop';
10
- import { inject as service } from '@ember/service';
11
- import { isNone } from '@ember/utils';
12
10
  import { DEBUG } from '@glimmer/env';
13
11
  import { tracked } from '@glimmer/tracking';
14
12
  import Ember from 'ember';
@@ -19,12 +17,14 @@ import { HAS_DEBUG_PACKAGE } from '@ember-data/private-build-infra';
19
17
  import {
20
18
  DEPRECATE_EARLY_STATIC,
21
19
  DEPRECATE_MODEL_REOPEN,
20
+ DEPRECATE_NON_EXPLICIT_POLYMORPHISM,
22
21
  DEPRECATE_RELATIONSHIPS_WITHOUT_INVERSE,
23
22
  DEPRECATE_SAVE_PROMISE_ACCESS,
24
23
  } from '@ember-data/private-build-infra/deprecations';
25
24
  import { recordIdentifierFor, storeFor } from '@ember-data/store';
26
- import { coerceId, deprecatedPromiseObject, recordDataFor, WeakCache } from '@ember-data/store/-private';
25
+ import { coerceId, recordDataFor } from '@ember-data/store/-private';
27
26
 
27
+ import { deprecatedPromiseObject } from './deprecated-promise-proxy';
28
28
  import Errors from './errors';
29
29
  import { LegacySupport } from './legacy-relationships-support';
30
30
  import notifyChanges from './notify-changes';
@@ -32,18 +32,21 @@ import RecordState, { peekTag, tagged } from './record-state';
32
32
  import { relationshipFromMeta } from './relationship-meta';
33
33
 
34
34
  const { changeProperties } = Ember;
35
- export const LEGACY_SUPPORT = new WeakCache(DEBUG ? 'legacy-relationships' : '');
36
- LEGACY_SUPPORT._generator = (record) => {
35
+ export const LEGACY_SUPPORT = new Map();
36
+
37
+ export function lookupLegacySupport(record) {
37
38
  const identifier = recordIdentifierFor(record);
38
39
  let support = LEGACY_SUPPORT.get(identifier);
39
40
 
40
41
  if (!support) {
41
42
  support = new LegacySupport(record);
42
43
  LEGACY_SUPPORT.set(identifier, support);
44
+ LEGACY_SUPPORT.set(record, support);
43
45
  }
44
46
 
45
47
  return support;
46
- };
48
+ }
49
+
47
50
  function findPossibleInverses(type, inverseType, name, relationshipsSoFar) {
48
51
  let possibleRelationships = relationshipsSoFar || [];
49
52
 
@@ -55,7 +58,7 @@ function findPossibleInverses(type, inverseType, name, relationshipsSoFar) {
55
58
  let relationshipsForType = relationshipMap.get(type.modelName);
56
59
  let relationships = Array.isArray(relationshipsForType)
57
60
  ? relationshipsForType.filter((relationship) => {
58
- let optionsForRelationship = inverseType.metaForProperty(relationship.name).options;
61
+ let optionsForRelationship = relationship.options;
59
62
 
60
63
  if (!optionsForRelationship.inverse && optionsForRelationship.inverse !== null) {
61
64
  return true;
@@ -119,7 +122,6 @@ function computeOnce(target, key, desc) {
119
122
  @extends Ember.EmberObject
120
123
  */
121
124
  class Model extends EmberObject {
122
- @service store;
123
125
  ___private_notifications;
124
126
 
125
127
  init(options = {}) {
@@ -130,29 +132,29 @@ class Model extends EmberObject {
130
132
  }
131
133
  const createProps = options._createProps;
132
134
  const _secretInit = options._secretInit;
133
- delete options._createProps;
134
- delete options._secretInit;
135
+ options._createProps = null;
136
+ options._secretInit = null;
137
+
138
+ let store = (this.store = _secretInit.store);
135
139
  super.init(options);
136
140
 
137
- _secretInit(this);
141
+ let identity = _secretInit.identifier;
142
+ _secretInit.cb(this, _secretInit.recordData, identity, _secretInit.store);
143
+
138
144
  this.___recordState = DEBUG ? new RecordState(this) : null;
139
145
 
140
146
  this.setProperties(createProps);
141
147
 
142
- let store = storeFor(this);
143
148
  let notifications = store._notificationManager;
144
- let identity = recordIdentifierFor(this);
145
-
146
149
  this.___private_notifications = notifications.subscribe(identity, (identifier, type, key) => {
147
150
  notifyChanges(identifier, type, key, this, store);
148
151
  });
149
152
  }
150
153
 
151
154
  destroy() {
152
- LEGACY_SUPPORT.get(this)?.destroy();
155
+ const identifier = recordIdentifierFor(this);
153
156
  this.___recordState?.destroy();
154
157
  const store = storeFor(this);
155
- const identifier = recordIdentifierFor(this);
156
158
  store._notificationManager.unsubscribe(this.___private_notifications);
157
159
  // Legacy behavior is to notify the relationships on destroy
158
160
  // such that they "clear". It's uncertain this behavior would
@@ -160,7 +162,16 @@ class Model extends EmberObject {
160
162
  // to simply not notify, for this reason the store does not itself
161
163
  // notify individual changes once the delete has been signaled,
162
164
  // this decision is left to model instances.
163
- notifyChanges(identifier, 'relationships', undefined, this, store);
165
+
166
+ this.eachRelationship((key, meta) => {
167
+ if (meta.kind === 'belongsTo') {
168
+ this.notifyPropertyChange(key);
169
+ }
170
+ });
171
+ LEGACY_SUPPORT.get(this)?.destroy();
172
+ LEGACY_SUPPORT.delete(this);
173
+ LEGACY_SUPPORT.delete(identifier);
174
+
164
175
  super.destroy();
165
176
  }
166
177
 
@@ -493,9 +504,8 @@ class Model extends EmberObject {
493
504
  }
494
505
  }
495
506
 
496
- // TODO just write a nice toString
497
- toStringExtension() {
498
- return this.id;
507
+ toString() {
508
+ return `<model::${this.constructor.modelName}:${this.id}>`;
499
509
  }
500
510
 
501
511
  /**
@@ -838,12 +848,15 @@ class Model extends EmberObject {
838
848
  rollbackAttributes() {
839
849
  const { currentState } = this;
840
850
  const { isNew } = currentState;
841
- recordDataFor(this).rollbackAttrs(recordIdentifierFor(this));
842
- this.errors.clear();
843
- currentState.cleanErrorRequests();
844
- if (isNew) {
845
- this.unloadRecord();
846
- }
851
+
852
+ storeFor(this)._join(() => {
853
+ recordDataFor(this).rollbackAttrs(recordIdentifierFor(this));
854
+ this.errors.clear();
855
+ currentState.cleanErrorRequests();
856
+ if (isNew) {
857
+ this.unloadRecord();
858
+ }
859
+ });
847
860
  }
848
861
 
849
862
  /**
@@ -1037,7 +1050,7 @@ class Model extends EmberObject {
1037
1050
  @return {BelongsToReference} reference for this relationship
1038
1051
  */
1039
1052
  belongsTo(name) {
1040
- return LEGACY_SUPPORT.lookup(this).referenceFor('belongsTo', name);
1053
+ return lookupLegacySupport(this).referenceFor('belongsTo', name);
1041
1054
  }
1042
1055
 
1043
1056
  /**
@@ -1100,7 +1113,7 @@ class Model extends EmberObject {
1100
1113
  @return {HasManyReference} reference for this relationship
1101
1114
  */
1102
1115
  hasMany(name) {
1103
- return LEGACY_SUPPORT.lookup(this).referenceFor('hasMany', name);
1116
+ return lookupLegacySupport(this).referenceFor('hasMany', name);
1104
1117
  }
1105
1118
 
1106
1119
  /**
@@ -1142,7 +1155,7 @@ class Model extends EmberObject {
1142
1155
  record.eachRelationship(function(name, descriptor) {
1143
1156
  if (descriptor.kind === 'hasMany') {
1144
1157
  let serializedHasManyName = name.toUpperCase() + '_IDS';
1145
- json[serializedHasManyName] = record.get(name).mapBy('id');
1158
+ json[serializedHasManyName] = record.get(name).map(r => r.id);
1146
1159
  }
1147
1160
  });
1148
1161
 
@@ -1381,42 +1394,46 @@ class Model extends EmberObject {
1381
1394
  this.modelName
1382
1395
  );
1383
1396
  }
1384
- let inverseType = this.typeForRelationship(name, store);
1385
- if (!inverseType) {
1386
- return null;
1387
- }
1388
1397
 
1389
- let propertyMeta = this.metaForProperty(name);
1398
+ const relationship = this.relationshipsByName.get(name);
1399
+ const { options } = relationship;
1400
+ const isPolymorphic = options.polymorphic;
1401
+
1390
1402
  //If inverse is manually specified to be null, like `comments: hasMany('message', { inverse: null })`
1391
- let options = propertyMeta.options;
1392
- if (options.inverse === null) {
1403
+ const isExplicitInverseNull = options.inverse === null;
1404
+ const isAbstractType =
1405
+ !isExplicitInverseNull && isPolymorphic && !store.getSchemaDefinitionService().doesTypeExist(relationship.type);
1406
+
1407
+ if (isExplicitInverseNull || isAbstractType) {
1408
+ assert(
1409
+ `No schema for the abstract type '${relationship.type}' for the polymorphic relationship '${name}' on '${this.modelName}' was provided by the SchemaDefinitionService.`,
1410
+ !isPolymorphic || isExplicitInverseNull
1411
+ );
1393
1412
  return null;
1394
1413
  }
1395
1414
 
1396
- let inverseName, inverseKind, inverse, inverseOptions;
1415
+ let fieldOnInverse, inverseKind, inverseRelationship, inverseOptions;
1416
+ let inverseSchema = this.typeForRelationship(name, store);
1397
1417
 
1418
+ // if the type does not exist and we are not polymorphic
1398
1419
  //If inverse is specified manually, return the inverse
1399
- if (options.inverse) {
1400
- inverseName = options.inverse;
1401
- inverse = inverseType.relationshipsByName.get(inverseName);
1420
+ if (options.inverse !== undefined) {
1421
+ fieldOnInverse = options.inverse;
1422
+ inverseRelationship = inverseSchema && inverseSchema.relationshipsByName.get(fieldOnInverse);
1402
1423
 
1403
1424
  assert(
1404
- "We found no inverse relationships by the name of '" +
1405
- inverseName +
1406
- "' on the '" +
1407
- inverseType.modelName +
1408
- "' model. This is most likely due to a missing attribute on your model definition.",
1409
- !isNone(inverse)
1425
+ `We found no field named '${fieldOnInverse}' on the schema for '${inverseSchema.modelName}' to be the inverse of the '${name}' relationship on '${this.modelName}'. This is most likely due to a missing field on your model definition.`,
1426
+ inverseRelationship
1410
1427
  );
1411
1428
 
1412
1429
  // TODO probably just return the whole inverse here
1413
- inverseKind = inverse.kind;
1414
- inverseOptions = inverse.options;
1430
+ inverseKind = inverseRelationship.kind;
1431
+ inverseOptions = inverseRelationship.options;
1415
1432
  } else {
1416
1433
  //No inverse was specified manually, we need to use a heuristic to guess one
1417
- if (propertyMeta.type === propertyMeta.parentModelName) {
1434
+ if (relationship.type === relationship.parentModelName) {
1418
1435
  warn(
1419
- `Detected a reflexive relationship by the name of '${name}' without an inverse option. Look at https://guides.emberjs.com/current/models/relationships/#toc_reflexive-relations for how to explicitly specify inverses.`,
1436
+ `Detected a reflexive relationship named '${name}' on the schema for '${relationship.type}' without an inverse option. Look at https://guides.emberjs.com/current/models/relationships/#toc_reflexive-relations for how to explicitly specify inverses.`,
1420
1437
  false,
1421
1438
  {
1422
1439
  id: 'ds.model.reflexive-relationship-without-inverse',
@@ -1424,30 +1441,33 @@ class Model extends EmberObject {
1424
1441
  );
1425
1442
  }
1426
1443
 
1427
- let possibleRelationships = findPossibleInverses(this, inverseType, name);
1444
+ let possibleRelationships = findPossibleInverses(this, inverseSchema, name);
1428
1445
 
1429
1446
  if (possibleRelationships.length === 0) {
1430
1447
  return null;
1431
1448
  }
1432
1449
 
1433
- let filteredRelationships = possibleRelationships.filter((possibleRelationship) => {
1434
- let optionsForRelationship = inverseType.metaForProperty(possibleRelationship.name).options;
1435
- return name === optionsForRelationship.inverse;
1436
- });
1450
+ if (DEBUG) {
1451
+ let filteredRelationships = possibleRelationships.filter((possibleRelationship) => {
1452
+ let optionsForRelationship = possibleRelationship.options;
1453
+ return name === optionsForRelationship.inverse;
1454
+ });
1437
1455
 
1438
- assert(
1439
- "You defined the '" +
1440
- name +
1441
- "' relationship on " +
1442
- this +
1443
- ', but you defined the inverse relationships of type ' +
1444
- inverseType.toString() +
1445
- ' multiple times. Look at https://guides.emberjs.com/current/models/relationships/#toc_explicit-inverses for how to explicitly specify inverses',
1446
- filteredRelationships.length < 2
1447
- );
1456
+ assert(
1457
+ "You defined the '" +
1458
+ name +
1459
+ "' relationship on " +
1460
+ this +
1461
+ ', but you defined the inverse relationships of type ' +
1462
+ inverseSchema.toString() +
1463
+ ' multiple times. Look at https://guides.emberjs.com/current/models/relationships/#toc_explicit-inverses for how to explicitly specify inverses',
1464
+ filteredRelationships.length < 2
1465
+ );
1466
+ }
1448
1467
 
1449
- if (filteredRelationships.length === 1) {
1450
- possibleRelationships = filteredRelationships;
1468
+ let explicitRelationship = possibleRelationships.find((relationship) => relationship.options.inverse === name);
1469
+ if (explicitRelationship) {
1470
+ possibleRelationships = [explicitRelationship];
1451
1471
  }
1452
1472
 
1453
1473
  assert(
@@ -1458,24 +1478,78 @@ class Model extends EmberObject {
1458
1478
  ', but multiple possible inverse relationships of type ' +
1459
1479
  this +
1460
1480
  ' were found on ' +
1461
- inverseType +
1481
+ inverseSchema +
1462
1482
  '. Look at https://guides.emberjs.com/current/models/relationships/#toc_explicit-inverses for how to explicitly specify inverses',
1463
1483
  possibleRelationships.length === 1
1464
1484
  );
1465
1485
 
1466
- inverseName = possibleRelationships[0].name;
1486
+ fieldOnInverse = possibleRelationships[0].name;
1467
1487
  inverseKind = possibleRelationships[0].kind;
1468
1488
  inverseOptions = possibleRelationships[0].options;
1469
1489
  }
1470
1490
 
1491
+ // ensure inverse is properly configured
1492
+ if (DEBUG && isPolymorphic) {
1493
+ if (DEPRECATE_NON_EXPLICIT_POLYMORPHISM) {
1494
+ if (!inverseOptions.as) {
1495
+ deprecate(
1496
+ `Relationships that satisfy polymorphic relationships MUST define which abstract-type they are satisfying using 'as'. The field '${fieldOnInverse}' on type '${inverseSchema.modelName}' is misconfigured.`,
1497
+ false,
1498
+ {
1499
+ id: 'ember-data:non-explicit-relationships',
1500
+ since: { enabled: '4.8', available: '4.8' },
1501
+ until: '5.0',
1502
+ for: 'ember-data',
1503
+ }
1504
+ );
1505
+ }
1506
+ } else {
1507
+ assert(
1508
+ `Relationships that satisfy polymorphic relationships MUST define which abstract-type they are satisfying using 'as'. The field '${fieldOnInverse}' on type '${inverseSchema.modelName}' is misconfigured.`,
1509
+ inverseOptions.as
1510
+ );
1511
+ assert(
1512
+ `options.as should match the expected type of the polymorphic relationship. Expected field '${fieldOnInverse}' on type '${inverseSchema.modelName}' to specify '${relationship.type}' but found '${inverseOptions.as}'`,
1513
+ !!inverseOptions.as && relationship.type === inverseOptions.as
1514
+ );
1515
+ }
1516
+ }
1517
+
1518
+ // ensure we are properly configured
1519
+ if (DEBUG && inverseOptions.polymorphic) {
1520
+ if (DEPRECATE_NON_EXPLICIT_POLYMORPHISM) {
1521
+ if (!options.as) {
1522
+ deprecate(
1523
+ `Relationships that satisfy polymorphic relationships MUST define which abstract-type they are satisfying using 'as'. The field '${name}' on type '${this.modelName}' is misconfigured.`,
1524
+ false,
1525
+ {
1526
+ id: 'ember-data:non-explicit-relationships',
1527
+ since: { enabled: '4.8', available: '4.8' },
1528
+ until: '5.0',
1529
+ for: 'ember-data',
1530
+ }
1531
+ );
1532
+ }
1533
+ } else {
1534
+ assert(
1535
+ `Relationships that satisfy polymorphic relationships MUST define which abstract-type they are satisfying using 'as'. The field '${name}' on type '${this.modelName}' is misconfigured.`,
1536
+ options.as
1537
+ );
1538
+ assert(
1539
+ `options.as should match the expected type of the polymorphic relationship. Expected field '${name}' on type '${this.modelName}' to specify '${inverseRelationship.type}' but found '${options.as}'`,
1540
+ !!options.as && inverseRelationship.type === options.as
1541
+ );
1542
+ }
1543
+ }
1544
+
1471
1545
  assert(
1472
- `The ${inverseType.modelName}:${inverseName} relationship declares 'inverse: null', but it was resolved as the inverse for ${this.modelName}:${name}.`,
1473
- !inverseOptions || inverseOptions.inverse !== null
1546
+ `The ${inverseSchema.modelName}:${fieldOnInverse} relationship declares 'inverse: null', but it was resolved as the inverse for ${this.modelName}:${name}.`,
1547
+ inverseOptions.inverse !== null
1474
1548
  );
1475
1549
 
1476
1550
  return {
1477
- type: inverseType,
1478
- name: inverseName,
1551
+ type: inverseSchema,
1552
+ name: fieldOnInverse,
1479
1553
  kind: inverseKind,
1480
1554
  options: inverseOptions,
1481
1555
  };
@@ -4,11 +4,11 @@ import type PromiseProxyMixin from '@ember/object/promise-proxy-mixin';
4
4
  import type ObjectProxy from '@ember/object/proxy';
5
5
 
6
6
  import type Store from '@ember-data/store';
7
- import { PromiseObject } from '@ember-data/store/-private';
8
7
  import type { RecordInstance } from '@ember-data/types/q/record-instance';
9
8
  import type { Dict } from '@ember-data/types/q/utils';
10
9
 
11
10
  import { LegacySupport } from './legacy-relationships-support';
11
+ import { PromiseObject } from './promise-proxy-base';
12
12
 
13
13
  export interface BelongsToProxyMeta {
14
14
  key: string;
@@ -2,18 +2,23 @@ import ArrayMixin, { NativeArray } from '@ember/array';
2
2
  import type ArrayProxy from '@ember/array/proxy';
3
3
  import { assert, deprecate } from '@ember/debug';
4
4
  import { dependentKeyCompat } from '@ember/object/compat';
5
+ import { DEBUG } from '@glimmer/env';
5
6
  import { tracked } from '@glimmer/tracking';
6
7
  import Ember from 'ember';
7
8
 
8
9
  import { resolve } from 'rsvp';
9
10
 
10
- import type { ManyArray } from 'ember-data/-private';
11
-
12
- import { DEPRECATE_PROMISE_MANY_ARRAY_BEHAVIORS } from '@ember-data/private-build-infra/deprecations';
11
+ import {
12
+ DEPRECATE_A_USAGE,
13
+ DEPRECATE_COMPUTED_CHAINS,
14
+ DEPRECATE_PROMISE_MANY_ARRAY_BEHAVIORS,
15
+ } from '@ember-data/private-build-infra/deprecations';
13
16
  import { StableRecordIdentifier } from '@ember-data/types/q/identifier';
14
17
  import type { RecordInstance } from '@ember-data/types/q/record-instance';
15
18
  import { FindOptions } from '@ember-data/types/q/store';
16
19
 
20
+ import type ManyArray from './many-array';
21
+
17
22
  export interface HasManyProxyCreateArgs {
18
23
  promise: Promise<ManyArray>;
19
24
  content?: ManyArray;
@@ -53,13 +58,27 @@ export default class PromiseManyArray {
53
58
  this.isDestroyed = false;
54
59
  this.isDestroying = false;
55
60
 
56
- const meta = Ember.meta(this);
57
- meta.hasMixin = (mixin: Object) => {
58
- if (mixin === NativeArray || mixin === ArrayMixin) {
59
- return true;
60
- }
61
- return false;
62
- };
61
+ if (DEPRECATE_A_USAGE) {
62
+ const meta = Ember.meta(this);
63
+ meta.hasMixin = (mixin: Object) => {
64
+ deprecate(`Do not use A() on an EmberData PromiseManyArray`, false, {
65
+ id: 'ember-data:no-a-with-array-like',
66
+ until: '5.0',
67
+ since: { enabled: '4.8', available: '4.8' },
68
+ for: 'ember-data',
69
+ });
70
+ // @ts-expect-error ArrayMixin is more than a type
71
+ if (mixin === NativeArray || mixin === ArrayMixin) {
72
+ return true;
73
+ }
74
+ return false;
75
+ };
76
+ } else if (DEBUG) {
77
+ const meta = Ember.meta(this);
78
+ meta.hasMixin = (mixin: Object) => {
79
+ assert(`Do not use A() on an EmberData PromiseManyArray`);
80
+ };
81
+ }
63
82
  }
64
83
 
65
84
  //---- Methods/Properties on ArrayProxy that we will keep as our API
@@ -75,7 +94,9 @@ export default class PromiseManyArray {
75
94
  get length(): number {
76
95
  // shouldn't be needed, but ends up being needed
77
96
  // for computed chains even in 4.x
78
- this['[]'];
97
+ if (DEPRECATE_COMPUTED_CHAINS) {
98
+ this['[]'];
99
+ }
79
100
  return this.content ? this.content.length : 0;
80
101
  }
81
102
 
@@ -85,7 +106,9 @@ export default class PromiseManyArray {
85
106
  // to recompute. We entangle the '[]' tag from
86
107
  @dependentKeyCompat
87
108
  get '[]'() {
88
- return this.content ? this.content['[]'] : this.content;
109
+ if (DEPRECATE_COMPUTED_CHAINS) {
110
+ return this.content?.length && this.content;
111
+ }
89
112
  }
90
113
 
91
114
  /**
@@ -99,7 +122,6 @@ export default class PromiseManyArray {
99
122
  * @private
100
123
  */
101
124
  forEach(cb) {
102
- this['[]']; // needed for < 3.23 support e.g. 3.20 lts
103
125
  if (this.content && this.length) {
104
126
  this.content.forEach(cb);
105
127
  }
@@ -0,0 +1,4 @@
1
+ import PromiseProxyMixin from '@ember/object/promise-proxy-mixin';
2
+ import ObjectProxy from '@ember/object/proxy';
3
+
4
+ export const PromiseObject = ObjectProxy.extend(PromiseProxyMixin);
@@ -105,7 +105,7 @@ export function tagged(_target, key, desc) {
105
105
 
106
106
  /**
107
107
  Historically EmberData managed a state machine
108
- for each record, the currentState for which
108
+ for each record, the localState for which
109
109
  was reflected onto Model.
110
110
 
111
111
  This implements the flags and stateName for backwards compat
@@ -1,15 +1,17 @@
1
+ import { deprecate } from '@ember/debug';
1
2
  import { dependentKeyCompat } from '@ember/object/compat';
2
3
  import { cached, tracked } from '@glimmer/tracking';
3
4
 
4
5
  import type { Object as JSONObject, Value as JSONValue } from 'json-typescript';
5
6
  import { resolve } from 'rsvp';
6
7
 
7
- import type { BelongsToRelationship } from '@ember-data/record-data/-private';
8
+ import { DEPRECATE_PROMISE_PROXIES } from '@ember-data/private-build-infra/deprecations';
9
+ import type { Graph } from '@ember-data/record-data/-private/graph/graph';
10
+ import type BelongsToRelationship from '@ember-data/record-data/-private/relationships/state/belongs-to';
8
11
  import type Store from '@ember-data/store';
9
12
  import { assertPolymorphicType } from '@ember-data/store/-debug';
10
13
  import { recordIdentifierFor } from '@ember-data/store/-private';
11
14
  import type { NotificationType } from '@ember-data/store/-private/managers/record-notification-manager';
12
- import type { DebugWeakCache } from '@ember-data/store/-private/utils/weak-cache';
13
15
  import type {
14
16
  LinkObject,
15
17
  Links,
@@ -54,6 +56,7 @@ export default class BelongsToReference {
54
56
  declare type: string;
55
57
  ___identifier: StableRecordIdentifier;
56
58
  declare store: Store;
59
+ declare graph: Graph;
57
60
 
58
61
  // unsubscribe tokens given to us by the notification manager
59
62
  ___token!: object;
@@ -63,10 +66,12 @@ export default class BelongsToReference {
63
66
 
64
67
  constructor(
65
68
  store: Store,
69
+ graph: Graph,
66
70
  parentIdentifier: StableRecordIdentifier,
67
71
  belongsToRelationship: BelongsToRelationship,
68
72
  key: string
69
73
  ) {
74
+ this.graph = graph;
70
75
  this.key = key;
71
76
  this.belongsToRelationship = belongsToRelationship;
72
77
  this.type = belongsToRelationship.definition.type;
@@ -382,8 +387,25 @@ export default class BelongsToReference {
382
387
  @return {Promise<record>} A promise that resolves with the new value in this belongs-to relationship.
383
388
  */
384
389
  async push(data: SingleResourceDocument | Promise<SingleResourceDocument>): Promise<RecordInstance> {
385
- // TODO @deprecate pushing unresolved payloads
386
- const jsonApiDoc = await resolve(data);
390
+ let jsonApiDoc: SingleResourceDocument = data as SingleResourceDocument;
391
+ if (DEPRECATE_PROMISE_PROXIES && (data as { then: unknown }).then) {
392
+ jsonApiDoc = await resolve(data);
393
+ if (jsonApiDoc !== data) {
394
+ deprecate(
395
+ `You passed in a Promise to a Reference API that now expects a resolved value. await the value before setting it.`,
396
+ false,
397
+ {
398
+ id: 'ember-data:deprecate-promise-proxies',
399
+ until: '5.0',
400
+ since: {
401
+ enabled: '4.8',
402
+ available: '4.8',
403
+ },
404
+ for: 'ember-data',
405
+ }
406
+ );
407
+ }
408
+ }
387
409
  let record = this.store.push(jsonApiDoc);
388
410
 
389
411
  // eslint-disable-next-line @typescript-eslint/no-unsafe-call
@@ -394,9 +416,9 @@ export default class BelongsToReference {
394
416
  this.store
395
417
  );
396
418
 
397
- const { graph, identifier } = this.belongsToRelationship;
398
- this.store._backburner.join(() => {
399
- graph.push({
419
+ const { identifier } = this.belongsToRelationship;
420
+ this.store._join(() => {
421
+ this.graph.push({
400
422
  op: 'replaceRelatedRecord',
401
423
  record: identifier,
402
424
  field: this.key,
@@ -525,9 +547,9 @@ export default class BelongsToReference {
525
547
  @return {Promise} a promise that resolves with the record in this belongs-to relationship.
526
548
  */
527
549
  load(options?: Dict<unknown>) {
528
- const support: LegacySupport = (
529
- LEGACY_SUPPORT as DebugWeakCache<StableRecordIdentifier, LegacySupport>
530
- ).getWithError(this.___identifier);
550
+ const support: LegacySupport = (LEGACY_SUPPORT as Map<StableRecordIdentifier, LegacySupport>).get(
551
+ this.___identifier
552
+ )!;
531
553
  return support.getBelongsTo(this.key, options);
532
554
  }
533
555
 
@@ -582,9 +604,9 @@ export default class BelongsToReference {
582
604
  @return {Promise} a promise that resolves with the record in this belongs-to relationship after the reload has completed.
583
605
  */
584
606
  reload(options?: Dict<unknown>) {
585
- const support: LegacySupport = (
586
- LEGACY_SUPPORT as DebugWeakCache<StableRecordIdentifier, LegacySupport>
587
- ).getWithError(this.___identifier);
607
+ const support: LegacySupport = (LEGACY_SUPPORT as Map<StableRecordIdentifier, LegacySupport>).get(
608
+ this.___identifier
609
+ )!;
588
610
  return support.reloadBelongsTo(this.key, options).then(() => this.value());
589
611
  }
590
612
  }