@ember-data/model 5.4.0-alpha.6 → 5.4.0-alpha.61

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 (78) hide show
  1. package/README.md +19 -0
  2. package/addon/-private.js +2 -2
  3. package/addon/has-many-BuTJfT5a.js +652 -0
  4. package/addon/has-many-BuTJfT5a.js.map +1 -0
  5. package/addon/{hooks-7a58334b.js → hooks-BMoEt0SI.js} +34 -15
  6. package/addon/hooks-BMoEt0SI.js.map +1 -0
  7. package/addon/hooks.js +1 -1
  8. package/addon/index.js +3 -3
  9. package/addon/index.js.map +1 -1
  10. package/addon/migration-support.js +118 -0
  11. package/addon/migration-support.js.map +1 -0
  12. package/addon/{model-520a761c.js → model-DJ9C4aZ-.js} +2159 -2113
  13. package/addon/model-DJ9C4aZ-.js.map +1 -0
  14. package/addon/util-CSYkrwfb.js +35 -0
  15. package/addon/util-CSYkrwfb.js.map +1 -0
  16. package/addon-main.js +1 -0
  17. package/ember-data-logo-dark.svg +12 -0
  18. package/ember-data-logo-light.svg +12 -0
  19. package/package.json +88 -37
  20. package/unstable-preview-types/-private/attr.d.ts +174 -0
  21. package/unstable-preview-types/-private/attr.d.ts.map +1 -0
  22. package/unstable-preview-types/-private/attr.type-test.d.ts +4 -0
  23. package/unstable-preview-types/-private/attr.type-test.d.ts.map +1 -0
  24. package/unstable-preview-types/-private/belongs-to.d.ts +180 -0
  25. package/unstable-preview-types/-private/belongs-to.d.ts.map +1 -0
  26. package/unstable-preview-types/-private/belongs-to.type-test.d.ts +4 -0
  27. package/unstable-preview-types/-private/belongs-to.type-test.d.ts.map +1 -0
  28. package/unstable-preview-types/-private/debug/assert-polymorphic-type.d.ts +8 -0
  29. package/unstable-preview-types/-private/debug/assert-polymorphic-type.d.ts.map +1 -0
  30. package/unstable-preview-types/-private/errors.d.ts +308 -0
  31. package/unstable-preview-types/-private/errors.d.ts.map +1 -0
  32. package/unstable-preview-types/-private/has-many.d.ts +169 -0
  33. package/unstable-preview-types/-private/has-many.d.ts.map +1 -0
  34. package/unstable-preview-types/-private/has-many.type-test.d.ts +4 -0
  35. package/unstable-preview-types/-private/has-many.type-test.d.ts.map +1 -0
  36. package/unstable-preview-types/-private/hooks.d.ts +13 -0
  37. package/unstable-preview-types/-private/hooks.d.ts.map +1 -0
  38. package/unstable-preview-types/-private/legacy-relationships-support.d.ts +64 -0
  39. package/unstable-preview-types/-private/legacy-relationships-support.d.ts.map +1 -0
  40. package/unstable-preview-types/-private/many-array.d.ts +198 -0
  41. package/unstable-preview-types/-private/many-array.d.ts.map +1 -0
  42. package/unstable-preview-types/-private/model-for-mixin.d.ts +6 -0
  43. package/unstable-preview-types/-private/model-for-mixin.d.ts.map +1 -0
  44. package/unstable-preview-types/-private/model-methods.d.ts +36 -0
  45. package/unstable-preview-types/-private/model-methods.d.ts.map +1 -0
  46. package/unstable-preview-types/-private/model.d.ts +1303 -0
  47. package/unstable-preview-types/-private/model.d.ts.map +1 -0
  48. package/unstable-preview-types/-private/model.type-test.d.ts +4 -0
  49. package/unstable-preview-types/-private/model.type-test.d.ts.map +1 -0
  50. package/unstable-preview-types/-private/notify-changes.d.ts +8 -0
  51. package/unstable-preview-types/-private/notify-changes.d.ts.map +1 -0
  52. package/unstable-preview-types/-private/promise-belongs-to.d.ts +49 -0
  53. package/unstable-preview-types/-private/promise-belongs-to.d.ts.map +1 -0
  54. package/unstable-preview-types/-private/promise-many-array.d.ts +131 -0
  55. package/unstable-preview-types/-private/promise-many-array.d.ts.map +1 -0
  56. package/unstable-preview-types/-private/promise-proxy-base.d.ts +35 -0
  57. package/unstable-preview-types/-private/record-state.d.ts +91 -0
  58. package/unstable-preview-types/-private/record-state.d.ts.map +1 -0
  59. package/unstable-preview-types/-private/references/belongs-to.d.ts +503 -0
  60. package/unstable-preview-types/-private/references/belongs-to.d.ts.map +1 -0
  61. package/unstable-preview-types/-private/references/has-many.d.ts +515 -0
  62. package/unstable-preview-types/-private/references/has-many.d.ts.map +1 -0
  63. package/unstable-preview-types/-private/schema-provider.d.ts +27 -0
  64. package/unstable-preview-types/-private/schema-provider.d.ts.map +1 -0
  65. package/unstable-preview-types/-private/util.d.ts +11 -0
  66. package/unstable-preview-types/-private/util.d.ts.map +1 -0
  67. package/unstable-preview-types/-private.d.ts +12 -0
  68. package/unstable-preview-types/-private.d.ts.map +1 -0
  69. package/unstable-preview-types/hooks.d.ts +5 -0
  70. package/unstable-preview-types/hooks.d.ts.map +1 -0
  71. package/unstable-preview-types/index.d.ts +75 -0
  72. package/unstable-preview-types/index.d.ts.map +1 -0
  73. package/unstable-preview-types/migration-support.d.ts +11 -0
  74. package/unstable-preview-types/migration-support.d.ts.map +1 -0
  75. package/addon/has-many-665b8730.js +0 -507
  76. package/addon/has-many-665b8730.js.map +0 -1
  77. package/addon/hooks-7a58334b.js.map +0 -1
  78. package/addon/model-520a761c.js.map +0 -1
package/README.md CHANGED
@@ -15,6 +15,25 @@
15
15
  />
16
16
  </p>
17
17
 
18
+ <p align="center">Runtime Classes for use as a Schema Source and Resource Presentation for EmberData</p>
19
+
20
+ ## Installation
21
+
22
+ Install using your javascript package manager of choice. For instance with [pnpm](https://pnpm.io/)
23
+
24
+ ```no-highlight
25
+ pnpm add @ember-data/model
26
+ ```
27
+
28
+ **Tagged Releases**
29
+
30
+ - ![NPM Canary Version](https://img.shields.io/npm/v/%40ember-data/model/canary?label=%40canary&color=FFBF00)
31
+ - ![NPM Beta Version](https://img.shields.io/npm/v/%40ember-data/model/beta?label=%40beta&color=ff00ff)
32
+ - ![NPM Stable Version](https://img.shields.io/npm/v/%40ember-data/model/latest?label=%40latest&color=90EE90)
33
+ - ![NPM LTS Version](https://img.shields.io/npm/v/%40ember-data/model/lts?label=%40lts&color=0096FF)
34
+ - ![NPM LTS 4.12 Version](https://img.shields.io/npm/v/%40ember-data/model/lts-4-12?label=%40lts-4-12&color=bbbbbb)
35
+
36
+
18
37
  <p align="center">Provides a Presentation Model for resource data in an EmberData Cache</p>
19
38
 
20
39
  This package implements the EmberData Store's `instantiateRecord` and `teardownRecord` hooks
package/addon/-private.js CHANGED
@@ -1,2 +1,2 @@
1
- export { a as attr, b as belongsTo, h as hasMany } from "./has-many-665b8730";
2
- export { E as Errors, L as LEGACY_SUPPORT, R as ManyArray, M as Model, P as PromiseBelongsTo, a as PromiseManyArray } from "./model-520a761c";
1
+ export { a as attr, b as belongsTo, h as hasMany } from "./has-many-BuTJfT5a";
2
+ export { E as Errors, L as LEGACY_SUPPORT, R as ManyArray, M as Model, P as PromiseBelongsTo, a as PromiseManyArray, l as lookupLegacySupport } from "./model-DJ9C4aZ-";
@@ -0,0 +1,652 @@
1
+ import { assert, warn, deprecate } from '@ember/debug';
2
+ import { computed } from '@ember/object';
3
+ import { recordIdentifierFor } from '@ember-data/store';
4
+ import { peekCache } from '@ember-data/store/-private';
5
+ import { i as isElementDescriptor, n as normalizeModelName } from "./util-CSYkrwfb";
6
+ import { macroCondition, getOwnConfig } from '@embroider/macros';
7
+ import { RecordStore } from '@warp-drive/core-types/symbols';
8
+ import { l as lookupLegacySupport } from "./model-DJ9C4aZ-";
9
+ import { dasherize } from '@ember/string';
10
+ import { singularize } from 'ember-inflector';
11
+
12
+ /**
13
+ @module @ember-data/model
14
+ */
15
+ function _attr(type, options) {
16
+ if (typeof type === 'object') {
17
+ options = type;
18
+ type = undefined;
19
+ } else {
20
+ options = options || {};
21
+ }
22
+ const meta = {
23
+ type: type,
24
+ kind: 'attribute',
25
+ isAttribute: true,
26
+ options: options,
27
+ key: null
28
+ };
29
+ return computed({
30
+ get(key) {
31
+ if (macroCondition(getOwnConfig().env.DEBUG)) {
32
+ if (['currentState'].includes(key)) {
33
+ throw new Error(`'${key}' is a reserved property name on instances of classes extending Model. Please choose a different property name for your attr on ${this.constructor.toString()}`);
34
+ }
35
+ }
36
+ if (this.isDestroyed || this.isDestroying) {
37
+ return;
38
+ }
39
+ return peekCache(this).getAttr(recordIdentifierFor(this), key);
40
+ },
41
+ set(key, value) {
42
+ if (macroCondition(getOwnConfig().env.DEBUG)) {
43
+ if (['currentState'].includes(key)) {
44
+ throw new Error(`'${key}' is a reserved property name on instances of classes extending Model. Please choose a different property name for your attr on ${this.constructor.toString()}`);
45
+ }
46
+ }
47
+ const identifier = recordIdentifierFor(this);
48
+ assert(`Attempted to set '${key}' on the deleted record ${identifier.type}:${identifier.id} (${identifier.lid})`, !this.currentState.isDeleted);
49
+ const cache = peekCache(this);
50
+ const currentValue = cache.getAttr(identifier, key);
51
+ if (currentValue !== value) {
52
+ cache.setAttr(identifier, key, value);
53
+ if (!this.isValid) {
54
+ const {
55
+ errors
56
+ } = this;
57
+ if (errors.get(key)) {
58
+ errors.remove(key);
59
+ this.currentState.cleanErrorRequests();
60
+ }
61
+ }
62
+ }
63
+ return value;
64
+ }
65
+ }).meta(meta);
66
+ }
67
+
68
+ // NOTE: Usage of Explicit ANY
69
+ // -------------------------------------------------------------------
70
+ // any is required here because we are the maximal not the minimal
71
+ // subset of options allowed. If we used unknown, object, or
72
+ // Record<string, unknown> we would get type errors when we try to
73
+ // assert against a more specific implementation with precise options.
74
+ // -------------------------------------------------------------------
75
+
76
+ // see note on Explicit ANY above
77
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
78
+
79
+ /**
80
+ * The return type of `void` is a lie to appease TypeScript. The actual return type
81
+ * is a descriptor, but typescript incorrectly insists that decorator functions return
82
+ * `void` or `any`.
83
+ *
84
+ * @typedoc
85
+ */
86
+
87
+ /**
88
+ `attr` defines an attribute on a [Model](/ember-data/release/classes/Model).
89
+ By default, attributes are passed through as-is, however you can specify an
90
+ optional type to have the value automatically transformed.
91
+ EmberData ships with four basic transform types: `string`, `number`,
92
+ `boolean` and `date`. You can define your own transforms by subclassing
93
+ [Transform](/ember-data/release/classes/Transform).
94
+
95
+ Note that you cannot use `attr` to define an attribute of `id`.
96
+
97
+ `attr` takes an optional hash as a second parameter, currently
98
+ supported options are:
99
+
100
+ - `defaultValue`: Pass a string or a function to be called to set the attribute
101
+ to a default value if and only if the key is absent from the payload response.
102
+
103
+ Example
104
+
105
+ ```app/models/user.js
106
+ import Model, { attr } from '@ember-data/model';
107
+
108
+ export default class UserModel extends Model {
109
+ @attr('string') username;
110
+ @attr('string') email;
111
+ @attr('boolean', { defaultValue: false }) verified;
112
+ }
113
+ ```
114
+
115
+ Default value can also be a function. This is useful it you want to return
116
+ a new object for each attribute.
117
+
118
+ ```app/models/user.js
119
+ import Model, { attr } from '@ember-data/model';
120
+
121
+ export default class UserModel extends Model {
122
+ @attr('string') username;
123
+ @attr('string') email;
124
+
125
+ @attr({
126
+ defaultValue() {
127
+ return {};
128
+ }
129
+ })
130
+ settings;
131
+ }
132
+ ```
133
+
134
+ The `options` hash is passed as second argument to a transforms'
135
+ `serialize` and `deserialize` method. This allows to configure a
136
+ transformation and adapt the corresponding value, based on the config:
137
+
138
+ ```app/models/post.js
139
+ import Model, { attr } from '@ember-data/model';
140
+
141
+ export default class PostModel extends Model {
142
+ @attr('text', {
143
+ uppercase: true
144
+ })
145
+ text;
146
+ }
147
+ ```
148
+
149
+ ```app/transforms/text.js
150
+ export default class TextTransform {
151
+ serialize(value, options) {
152
+ if (options.uppercase) {
153
+ return value.toUpperCase();
154
+ }
155
+
156
+ return value;
157
+ }
158
+
159
+ deserialize(value) {
160
+ return value;
161
+ }
162
+
163
+ static create() {
164
+ return new this();
165
+ }
166
+ }
167
+ ```
168
+
169
+ @method attr
170
+ @public
171
+ @static
172
+ @for @ember-data/model
173
+ @param {String|Object} type the attribute type
174
+ @param {Object} options a hash of options
175
+ @return {Attribute}
176
+ */
177
+
178
+ // see note on DataDecorator for why void
179
+ function attr(type, options, desc) {
180
+ const args = [type, options, desc];
181
+ // see note on DataDecorator for why void
182
+ return isElementDescriptor(args) ? _attr()(...args) : _attr(type, options);
183
+ }
184
+
185
+ // get: () => getT;
186
+ // // set: (value: Awaited<getT>) => void;
187
+ // set: (value: getT) => void;
188
+ // // init: () => getT;
189
+ // };
190
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
191
+ // BelongsToDecoratorObject<getT>;
192
+
193
+ function _belongsTo(type, options) {
194
+ assert(`Expected options.async from @belongsTo('${type}', options) to be a boolean`, options && typeof options.async === 'boolean');
195
+ assert(`Expected options.inverse from @belongsTo('${type}', options) to be either null or the string type of the related resource.`, options.inverse === null || typeof options.inverse === 'string' && options.inverse.length > 0);
196
+ const meta = {
197
+ type: normalizeModelName(type),
198
+ isRelationship: true,
199
+ options: options,
200
+ kind: 'belongsTo',
201
+ name: '<Unknown BelongsTo>',
202
+ key: null
203
+ };
204
+ return computed({
205
+ get(key) {
206
+ // this is a legacy behavior we may not carry into a new model setup
207
+ // it's better to error on disconnected records so users find errors
208
+ // in their logic.
209
+ if (this.isDestroying || this.isDestroyed) {
210
+ return null;
211
+ }
212
+ const support = lookupLegacySupport(this);
213
+ if (macroCondition(getOwnConfig().env.DEBUG)) {
214
+ if (['currentState'].includes(key)) {
215
+ throw new Error(`'${key}' is a reserved property name on instances of classes extending Model. Please choose a different property name for your belongsTo on ${this.constructor.toString()}`);
216
+ }
217
+ if (Object.prototype.hasOwnProperty.call(options, 'serialize')) {
218
+ warn(`You provided a serialize option on the "${key}" property in the "${support.identifier.type}" class, this belongs in the serializer. See Serializer and it's implementations https://api.emberjs.com/ember-data/release/classes/Serializer`, false, {
219
+ id: 'ds.model.serialize-option-in-belongs-to'
220
+ });
221
+ }
222
+ if (Object.prototype.hasOwnProperty.call(options, 'embedded')) {
223
+ warn(`You provided an embedded option on the "${key}" property in the "${support.identifier.type}" class, this belongs in the serializer. See EmbeddedRecordsMixin https://api.emberjs.com/ember-data/release/classes/EmbeddedRecordsMixin`, false, {
224
+ id: 'ds.model.embedded-option-in-belongs-to'
225
+ });
226
+ }
227
+ }
228
+ return support.getBelongsTo(key);
229
+ },
230
+ set(key, value) {
231
+ const support = lookupLegacySupport(this);
232
+ if (macroCondition(getOwnConfig().env.DEBUG)) {
233
+ if (['currentState'].includes(key)) {
234
+ throw new Error(`'${key}' is a reserved property name on instances of classes extending Model. Please choose a different property name for your belongsTo on ${this.constructor.toString()}`);
235
+ }
236
+ }
237
+ this[RecordStore]._join(() => {
238
+ support.setDirtyBelongsTo(key, value);
239
+ });
240
+ return support.getBelongsTo(key);
241
+ }
242
+ }).meta(meta);
243
+ }
244
+
245
+ /**
246
+ `belongsTo` is used to define One-To-One and One-To-Many, and One-To-None
247
+ relationships on a [Model](/ember-data/release/classes/Model).
248
+
249
+ `belongsTo` takes a configuration hash as a second parameter, currently
250
+ supported options are:
251
+
252
+ - `async`: (*required*) A boolean value used to declare whether this is a sync (false) or async (true) relationship.
253
+ - `inverse`: (*required*) A string used to identify the inverse property on a related model, or `null`.
254
+ - `polymorphic`: (*optional*) A boolean value to mark the relationship as polymorphic
255
+ - `as`: (*optional*) A string used to declare the abstract type "this" record satisfies for polymorphism.
256
+
257
+ ### Examples
258
+
259
+ To declare a **one-to-many** (or many-to-many) relationship, use
260
+ `belongsTo` in combination with `hasMany`:
261
+
262
+ ```js
263
+ // app/models/comment.js
264
+ import Model, { belongsTo } from '@ember-data/model';
265
+
266
+ export default class Comment extends Model {
267
+ @belongsTo('post', { async: false, inverse: 'comments' }) post;
268
+ }
269
+
270
+ // app/models/post.js
271
+ import Model, { hasMany } from '@ember-data/model';
272
+
273
+ export default class Post extends Model {
274
+ @hasMany('comment', { async: false, inverse: 'post' }) comments;
275
+ }
276
+ ```
277
+
278
+ To declare a **one-to-one** relationship with managed inverses, use `belongsTo` for both sides:
279
+
280
+ ```js
281
+ // app/models/author.js
282
+ import Model, { belongsTo } from '@ember-data/model';
283
+
284
+ export default class Author extends Model {
285
+ @belongsTo('address', { async: true, inverse: 'owner' }) address;
286
+ }
287
+
288
+ // app/models/address.js
289
+ import Model, { belongsTo } from '@ember-data/model';
290
+
291
+ export default class Address extends Model {
292
+ @belongsTo('author', { async: true, inverse: 'address' }) owner;
293
+ }
294
+ ```
295
+
296
+ To declare a **one-to-one** relationship without managed inverses, use `belongsTo` for both sides
297
+ with `null` as the inverse:
298
+
299
+ ```js
300
+ // app/models/author.js
301
+ import Model, { belongsTo } from '@ember-data/model';
302
+
303
+ export default class Author extends Model {
304
+ @belongsTo('address', { async: true, inverse: null }) address;
305
+ }
306
+
307
+ // app/models/address.js
308
+ import Model, { belongsTo } from '@ember-data/model';
309
+
310
+ export default class Address extends Model {
311
+ @belongsTo('author', { async: true, inverse: null }) owner;
312
+ }
313
+ ```
314
+
315
+ To declare a one-to-none relationship between two models, use
316
+ `belongsTo` with inverse set to `null` on just one side::
317
+
318
+ ```js
319
+ // app/models/person.js
320
+ import Model, { belongsTo } from '@ember-data/model';
321
+
322
+ export default class Person extends Model {
323
+ @belongsTo('person', { async: false, inverse: null }) bestFriend;
324
+ }
325
+ ```
326
+
327
+ #### Sync vs Async Relationships
328
+
329
+ EmberData fulfills relationships using resource data available in
330
+ the cache.
331
+
332
+ Sync relationships point directly to the known related resources.
333
+
334
+ When a relationship is declared as async, if any of the known related
335
+ resources have not been loaded, they will be fetched. The property
336
+ on the record when accessed provides a promise that resolves once
337
+ all resources are loaded.
338
+
339
+ Async relationships may take advantage of links. On access, if the related
340
+ link has not been loaded, or if any known resources are not available in
341
+ the cache, the fresh state will be fetched using the link.
342
+
343
+ In contrast to async relationship, accessing a sync relationship
344
+ will error on access when any of the known related resources have
345
+ not been loaded.
346
+
347
+ If you are using `links` with sync relationships, you have to use
348
+ the BelongsTo reference API to fetch or refresh related resources
349
+ that aren't loaded. For instance, for a `bestFriend` relationship:
350
+
351
+ ```js
352
+ person.belongsTo('bestFriend').reload();
353
+ ```
354
+
355
+ #### Polymorphic Relationships
356
+
357
+ To declare a polymorphic relationship, use `hasMany` with the `polymorphic`
358
+ option set to `true`:
359
+
360
+ ```js
361
+ // app/models/comment.js
362
+ import Model, { belongsTo } from '@ember-data/model';
363
+
364
+ export default class Comment extends Model {
365
+ @belongsTo('commentable', { async: false, inverse: 'comments', polymorphic: true }) parent;
366
+ }
367
+ ```
368
+
369
+ `'commentable'` here is referred to as the "abstract type" for the polymorphic
370
+ relationship.
371
+
372
+ Polymorphic relationships with `inverse: null` will accept any type of record as their content.
373
+ Polymorphic relationships with `inverse` set to a string will only accept records with a matching
374
+ inverse relationships declaring itself as satisfying the abstract type.
375
+
376
+ Below, 'as' is used to declare the that 'post' record satisfies the abstract type 'commentable'
377
+ for this relationship.
378
+
379
+ ```js
380
+ // app/models/post.js
381
+ import Model, { hasMany } from '@ember-data/model';
382
+
383
+ export default class Post extends Model {
384
+ @hasMany('comment', { async: false, inverse: 'parent', as: 'commentable' }) comments;
385
+ }
386
+ ```
387
+
388
+ Note: every Model that declares an inverse to a polymorphic relationship must
389
+ declare itself exactly the same. This is because polymorphism is based on structural
390
+ traits.
391
+
392
+ Polymorphic to polymorphic relationships are supported. Both sides of the relationship
393
+ must be declared as polymorphic, and the `as` option must be used to declare the abstract
394
+ type each record satisfies on both sides.
395
+
396
+ @method belongsTo
397
+ @public
398
+ @static
399
+ @for @ember-data/model
400
+ @param {string} type (optional) the name of the related resource
401
+ @param {object} options (optional) a hash of options
402
+ @return {PropertyDescriptor} relationship
403
+ */
404
+
405
+ // export function belongsTo<K extends Promise<unknown>, T extends Awaited<K> = Awaited<K>>(
406
+ // type: TypeFromInstance<NoNull<T>>,
407
+ // options: RelationshipOptions<T, true>
408
+ // ): RelationshipDecorator<K>;
409
+
410
+ function belongsTo(type, options) {
411
+ if (macroCondition(getOwnConfig().env.DEBUG)) {
412
+ assert(`belongsTo must be invoked with a type and options. Did you mean \`@belongsTo(${type}, { async: false, inverse: null })\`?`, !isElementDescriptor(arguments));
413
+ }
414
+ return _belongsTo(type, options);
415
+ }
416
+
417
+ /**
418
+ @module @ember-data/model
419
+ */
420
+ function normalizeType(type) {
421
+ if (macroCondition(getOwnConfig().deprecations.DEPRECATE_NON_STRICT_TYPES)) {
422
+ const result = singularize(dasherize(type));
423
+ deprecate(`The resource type '${type}' is not normalized. Update your application code to use '${result}' instead of '${type}'.`, result === type, {
424
+ id: 'ember-data:deprecate-non-strict-types',
425
+ until: '6.0',
426
+ for: 'ember-data',
427
+ since: {
428
+ available: '5.3',
429
+ enabled: '5.3'
430
+ }
431
+ });
432
+ return result;
433
+ }
434
+ return type;
435
+ }
436
+ function _hasMany(type, options) {
437
+ assert(`Expected hasMany options.async to be a boolean`, options && typeof options.async === 'boolean');
438
+
439
+ // Metadata about relationships is stored on the meta of
440
+ // the relationship. This is used for introspection and
441
+ // serialization. Note that `key` is populated lazily
442
+ // the first time the CP is called.
443
+ const meta = {
444
+ type: normalizeType(type),
445
+ options,
446
+ isRelationship: true,
447
+ kind: 'hasMany',
448
+ name: '<Unknown BelongsTo>',
449
+ key: null
450
+ };
451
+ return computed({
452
+ get(key) {
453
+ if (macroCondition(getOwnConfig().env.DEBUG)) {
454
+ if (['currentState'].includes(key)) {
455
+ throw new Error(`'${key}' is a reserved property name on instances of classes extending Model. Please choose a different property name for your hasMany on ${this.constructor.toString()}`);
456
+ }
457
+ }
458
+ if (this.isDestroying || this.isDestroyed) {
459
+ return [];
460
+ }
461
+ return lookupLegacySupport(this).getHasMany(key);
462
+ },
463
+ set(key, records) {
464
+ if (macroCondition(getOwnConfig().env.DEBUG)) {
465
+ if (['currentState'].includes(key)) {
466
+ throw new Error(`'${key}' is a reserved property name on instances of classes extending Model. Please choose a different property name for your hasMany on ${this.constructor.toString()}`);
467
+ }
468
+ }
469
+ const support = lookupLegacySupport(this);
470
+ const manyArray = support.getManyArray(key);
471
+ assert(`You must pass an array of records to set a hasMany relationship`, Array.isArray(records));
472
+ this[RecordStore]._join(() => {
473
+ manyArray.splice(0, manyArray.length, ...records);
474
+ });
475
+ return support.getHasMany(key);
476
+ }
477
+ }).meta(meta);
478
+ }
479
+
480
+ /**
481
+ `hasMany` is used to define Many-To-One and Many-To-Many, and Many-To-None
482
+ relationships on a [Model](/ember-data/release/classes/Model).
483
+
484
+ `hasMany` takes a configuration hash as a second parameter, currently
485
+ supported options are:
486
+
487
+ - `async`: (*required*) A boolean value used to declare whether this is a sync (false) or async (true) relationship.
488
+ - `inverse`: (*required*) A string used to identify the inverse property on a related model, or `null`.
489
+ - `polymorphic`: (*optional*) A boolean value to mark the relationship as polymorphic
490
+ - `as`: (*optional*) A string used to declare the abstract type "this" record satisfies for polymorphism.
491
+
492
+ ### Examples
493
+
494
+ To declare a **many-to-one** (or one-to-many) relationship, use
495
+ `belongsTo` in combination with `hasMany`:
496
+
497
+ ```js
498
+ // app/models/post.js
499
+ import Model, { hasMany } from '@ember-data/model';
500
+
501
+ export default class Post extends Model {
502
+ @hasMany('comment', { async: false, inverse: 'post' }) comments;
503
+ }
504
+
505
+
506
+ // app/models/comment.js
507
+ import Model, { belongsTo } from '@ember-data/model';
508
+
509
+ export default class Comment extends Model {
510
+ @belongsTo('post', { async: false, inverse: 'comments' }) post;
511
+ }
512
+ ```
513
+
514
+ To declare a **many-to-many** relationship with managed inverses, use `hasMany` for both sides:
515
+
516
+ ```js
517
+ // app/models/post.js
518
+ import Model, { hasMany } from '@ember-data/model';
519
+
520
+ export default class Post extends Model {
521
+ @hasMany('tag', { async: true, inverse: 'posts' }) tags;
522
+ }
523
+
524
+ // app/models/tag.js
525
+ import Model, { hasMany } from '@ember-data/model';
526
+
527
+ export default class Tag extends Model {
528
+ @hasMany('post', { async: true, inverse: 'tags' }) posts;
529
+ }
530
+ ```
531
+
532
+ To declare a **many-to-many** relationship without managed inverses, use `hasMany` for both sides
533
+ with `null` as the inverse:
534
+
535
+ ```js
536
+ // app/models/post.js
537
+ import Model, { hasMany } from '@ember-data/model';
538
+
539
+ export default class Post extends Model {
540
+ @hasMany('tag', { async: true, inverse: null }) tags;
541
+ }
542
+
543
+ // app/models/tag.js
544
+ import Model, { hasMany } from '@ember-data/model';
545
+
546
+ export default class Tag extends Model {
547
+ @hasMany('post', { async: true, inverse: null }) posts;
548
+ }
549
+ ```
550
+
551
+ To declare a many-to-none relationship between two models, use
552
+ `hasMany` with inverse set to `null` on just one side::
553
+
554
+ ```js
555
+ // app/models/post.js
556
+ import Model, { hasMany } from '@ember-data/model';
557
+
558
+ export default class Post extends Model {
559
+ @hasMany('category', { async: true, inverse: null }) categories;
560
+ }
561
+ ```
562
+
563
+ #### Sync vs Async Relationships
564
+
565
+ EmberData fulfills relationships using resource data available in
566
+ the cache.
567
+
568
+ Sync relationships point directly to the known related resources.
569
+
570
+ When a relationship is declared as async, if any of the known related
571
+ resources have not been loaded, they will be fetched. The property
572
+ on the record when accessed provides a promise that resolves once
573
+ all resources are loaded.
574
+
575
+ Async relationships may take advantage of links. On access, if the related
576
+ link has not been loaded, or if any known resources are not available in
577
+ the cache, the fresh state will be fetched using the link.
578
+
579
+ In contrast to async relationship, accessing a sync relationship
580
+ will error on access when any of the known related resources have
581
+ not been loaded.
582
+
583
+ If you are using `links` with sync relationships, you have to use
584
+ the HasMany reference API to fetch or refresh related resources
585
+ that aren't loaded. For instance, for a `comments` relationship:
586
+
587
+ ```js
588
+ post.hasMany('comments').reload();
589
+ ```
590
+
591
+ #### Polymorphic Relationships
592
+
593
+ To declare a polymorphic relationship, use `hasMany` with the `polymorphic`
594
+ option set to `true`:
595
+
596
+ ```js
597
+ // app/models/comment.js
598
+ import Model, { belongsTo } from '@ember-data/model';
599
+
600
+ export default class Comment extends Model {
601
+ @belongsTo('commentable', { async: false, inverse: 'comments', polymorphic: true }) parent;
602
+ }
603
+ ```
604
+
605
+ `'commentable'` here is referred to as the "abstract type" for the polymorphic
606
+ relationship.
607
+
608
+ Polymorphic relationships with `inverse: null` will accept any type of record as their content.
609
+ Polymorphic relationships with `inverse` set to a string will only accept records with a matching
610
+ inverse relationships declaring itself as satisfying the abstract type.
611
+
612
+ Below, 'as' is used to declare the that 'post' record satisfies the abstract type 'commentable'
613
+ for this relationship.
614
+
615
+ ```js
616
+ // app/models/post.js
617
+ import Model, { hasMany } from '@ember-data/model';
618
+
619
+ export default class Post extends Model {
620
+ @hasMany('comment', { async: false, inverse: 'parent', as: 'commentable' }) comments;
621
+ }
622
+ ```
623
+
624
+ Note: every Model that declares an inverse to a polymorphic relationship must
625
+ declare itself exactly the same. This is because polymorphism is based on structural
626
+ traits.
627
+
628
+ Polymorphic to polymorphic relationships are supported. Both sides of the relationship
629
+ must be declared as polymorphic, and the `as` option must be used to declare the abstract
630
+ type each record satisfies on both sides.
631
+
632
+ @method hasMany
633
+ @public
634
+ @static
635
+ @for @ember-data/model
636
+ @param {string} type (optional) the name of the related resource
637
+ @param {object} options (optional) a hash of options
638
+ @return {PropertyDescriptor} relationship
639
+ */
640
+
641
+ // export function hasMany<K extends Promise<unknown>, T extends Awaited<K> = Awaited<K>>(
642
+ // type: TypeFromInstance<NoNull<T>>,
643
+ // options: RelationshipOptions<T, true>
644
+ // ): RelationshipDecorator<K>;
645
+
646
+ function hasMany(type, options) {
647
+ if (macroCondition(getOwnConfig().env.DEBUG)) {
648
+ assert(`hasMany must be invoked with a type and options. Did you mean \`@hasMany(${type}, { async: false, inverse: null })\`?`, !isElementDescriptor(arguments));
649
+ }
650
+ return _hasMany(type, options);
651
+ }
652
+ export { attr as a, belongsTo as b, hasMany as h };