@ember-data/store 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.
@@ -1,6 +1,8 @@
1
1
  import { assert } from '@ember/debug';
2
2
  import { DEBUG } from '@glimmer/env';
3
3
 
4
+ import { DEPRECATE_NON_EXPLICIT_POLYMORPHISM } from '@ember-data/private-build-infra/deprecations';
5
+
4
6
  /*
5
7
  Assert that `addedRecord` has a valid type so it can be added to the
6
8
  relationship of the `record`.
@@ -29,18 +31,38 @@ if (DEBUG) {
29
31
  };
30
32
 
31
33
  assertPolymorphicType = function assertPolymorphicType(parentIdentifier, parentDefinition, addedIdentifier, store) {
32
- store = store._store ? store._store : store; // allow usage with storeWrapper
33
- let addedModelName = addedIdentifier.type;
34
- let parentModelName = parentIdentifier.type;
35
- let key = parentDefinition.key;
36
- let relationshipModelName = parentDefinition.type;
37
- let relationshipClass = store.modelFor(relationshipModelName);
38
- let addedClass = store.modelFor(addedModelName);
39
-
40
- let assertionMessage = `The '${addedModelName}' type does not implement '${relationshipModelName}' and thus cannot be assigned to the '${key}' relationship in '${parentModelName}'. Make it a descendant of '${relationshipModelName}' or use a mixin of the same name.`;
41
- let isPolymorphic = checkPolymorphic(relationshipClass, addedClass);
42
-
43
- assert(assertionMessage, isPolymorphic);
34
+ let asserted = false;
35
+
36
+ if (parentDefinition.inverseIsImplicit) {
37
+ return;
38
+ }
39
+ if (parentDefinition.isPolymorphic) {
40
+ let meta = store.getSchemaDefinitionService().relationshipsDefinitionFor(addedIdentifier)[
41
+ parentDefinition.inverseKey
42
+ ];
43
+ if (meta?.options?.as) {
44
+ asserted = true;
45
+ assert(
46
+ `The schema for the relationship '${parentDefinition.inverseKey}' on '${addedIdentifier.type}' type does not implement '${parentDefinition.type}' and thus cannot be assigned to the '${parentDefinition.key}' relationship in '${parentIdentifier.type}'. The definition should specify 'as: "${parentDefinition.type}"' in options.`,
47
+ meta.options.as === parentDefinition.type
48
+ );
49
+ }
50
+ }
51
+
52
+ if (DEPRECATE_NON_EXPLICIT_POLYMORPHISM && !asserted) {
53
+ store = store._store ? store._store : store; // allow usage with storeWrapper
54
+ let addedModelName = addedIdentifier.type;
55
+ let parentModelName = parentIdentifier.type;
56
+ let key = parentDefinition.key;
57
+ let relationshipModelName = parentDefinition.type;
58
+ let relationshipClass = store.modelFor(relationshipModelName);
59
+ let addedClass = store.modelFor(addedModelName);
60
+
61
+ let assertionMessage = `The '${addedModelName}' type does not implement '${relationshipModelName}' and thus cannot be assigned to the '${key}' relationship in '${parentModelName}'. Make it a descendant of '${relationshipModelName}' or use a mixin of the same name.`;
62
+ let isPolymorphic = checkPolymorphic(relationshipClass, addedClass);
63
+
64
+ assert(assertionMessage, isPolymorphic);
65
+ }
44
66
  };
45
67
  }
46
68
 
@@ -4,7 +4,7 @@
4
4
  import { assert, warn } from '@ember/debug';
5
5
  import { DEBUG } from '@glimmer/env';
6
6
 
7
- import { getOwnConfig, importSync, macroCondition } from '@embroider/macros';
7
+ import { getOwnConfig, macroCondition } from '@embroider/macros';
8
8
 
9
9
  import { LOG_IDENTIFIERS } from '@ember-data/private-build-infra/debugging';
10
10
  import type { ExistingResourceObject, ResourceIdentifierObject } from '@ember-data/types/q/ember-data-json-api';
@@ -25,9 +25,9 @@ import coerceId from '../utils/coerce-id';
25
25
  import { DEBUG_CLIENT_ORIGINATED, DEBUG_IDENTIFIER_BUCKET } from '../utils/identifer-debug-consts';
26
26
  import isNonEmptyString from '../utils/is-non-empty-string';
27
27
  import normalizeModelName from '../utils/normalize-model-name';
28
- import WeakCache from '../utils/weak-cache';
28
+ import installPolyfill from '../utils/uuid-polyfill';
29
29
 
30
- const IDENTIFIERS = new WeakSet();
30
+ const IDENTIFIERS = new Set();
31
31
 
32
32
  export function isStableIdentifier(identifier: Object): identifier is StableRecordIdentifier {
33
33
  return IDENTIFIERS.has(identifier);
@@ -37,7 +37,7 @@ const isFastBoot = typeof FastBoot !== 'undefined';
37
37
  const _crypto: Crypto = isFastBoot ? (FastBoot.require('crypto') as Crypto) : window.crypto;
38
38
 
39
39
  if (macroCondition(getOwnConfig<{ polyfillUUID: boolean }>().polyfillUUID)) {
40
- importSync('./utils/uuid-polyfill');
40
+ installPolyfill();
41
41
  }
42
42
 
43
43
  function uuidv4(): string {
@@ -56,7 +56,7 @@ interface KeyOptions {
56
56
  id: IdentifierMap;
57
57
  }
58
58
 
59
- type IdentifierMap = ConfidentDict<StableRecordIdentifier>;
59
+ type IdentifierMap = Map<string, StableRecordIdentifier>;
60
60
  type TypeMap = ConfidentDict<KeyOptions>;
61
61
  export type MergeMethod = (
62
62
  targetIdentifier: StableRecordIdentifier,
@@ -85,12 +85,15 @@ export function setIdentifierResetMethod(method: ResetMethod | null): void {
85
85
  configuredResetMethod = method;
86
86
  }
87
87
 
88
+ type WithLid = { lid: string };
89
+ type WithId = { id: string | null; type: string };
90
+
88
91
  function defaultGenerationMethod(data: ResourceData | { type: string }, bucket: IdentifierBucket): string {
89
- if ('lid' in data && isNonEmptyString(data.lid)) {
90
- return data.lid;
92
+ if (isNonEmptyString((data as WithLid).lid)) {
93
+ return (data as WithLid).lid;
91
94
  }
92
- if ('id' in data) {
93
- let { type, id } = data;
95
+ if ((data as WithId).id !== undefined) {
96
+ let { type, id } = data as WithId;
94
97
  // TODO: add test for id not a string
95
98
  if (isNonEmptyString(coerceId(id))) {
96
99
  return `@lid:${normalizeModelName(type)}-${id}`;
@@ -103,7 +106,7 @@ function defaultEmptyCallback(...args: any[]): any {}
103
106
 
104
107
  let DEBUG_MAP;
105
108
  if (DEBUG) {
106
- DEBUG_MAP = new WeakCache<StableRecordIdentifier, StableRecordIdentifier>('identifier-proxy-target');
109
+ DEBUG_MAP = new WeakMap<StableRecordIdentifier, StableRecordIdentifier>();
107
110
  }
108
111
 
109
112
  /**
@@ -121,7 +124,7 @@ if (DEBUG) {
121
124
  */
122
125
  export class IdentifierCache {
123
126
  _cache = {
124
- lids: Object.create(null) as IdentifierMap,
127
+ lids: new Map<string, StableRecordIdentifier>(),
125
128
  types: Object.create(null) as TypeMap,
126
129
  };
127
130
  declare _generate: GenerationMethod;
@@ -129,6 +132,7 @@ export class IdentifierCache {
129
132
  declare _forget: ForgetMethod;
130
133
  declare _reset: ResetMethod;
131
134
  declare _merge: MergeMethod;
135
+ declare _isDefaultConfig: boolean;
132
136
 
133
137
  constructor() {
134
138
  // we cache the user configuredGenerationMethod at init because it must
@@ -138,6 +142,7 @@ export class IdentifierCache {
138
142
  this._forget = configuredForgetMethod || defaultEmptyCallback;
139
143
  this._reset = configuredResetMethod || defaultEmptyCallback;
140
144
  this._merge = defaultEmptyCallback;
145
+ this._isDefaultConfig = !configuredGenerationMethod;
141
146
  }
142
147
 
143
148
  /**
@@ -167,7 +172,7 @@ export class IdentifierCache {
167
172
  if (isStableIdentifier(resource)) {
168
173
  if (DEBUG) {
169
174
  // TODO should we instead just treat this case as a new generation skipping the short circuit?
170
- if (!(this._cache.lids[resource.lid] !== undefined) || this._cache.lids[resource.lid] !== resource) {
175
+ if (!this._cache.lids.has(resource.lid) || this._cache.lids.get(resource.lid) !== resource) {
171
176
  throw new Error(`The supplied identifier ${resource} does not belong to this store instance`);
172
177
  }
173
178
  }
@@ -179,7 +184,7 @@ export class IdentifierCache {
179
184
  }
180
185
 
181
186
  let lid = coerceId(resource.lid);
182
- let identifier: StableRecordIdentifier | undefined = lid !== null ? this._cache.lids[lid] : undefined;
187
+ let identifier: StableRecordIdentifier | undefined = lid !== null ? this._cache.lids.get(lid) : undefined;
183
188
 
184
189
  if (identifier !== undefined) {
185
190
  if (LOG_IDENTIFIERS) {
@@ -210,13 +215,13 @@ export class IdentifierCache {
210
215
 
211
216
  // go straight for the stable RecordIdentifier key'd to `lid`
212
217
  if (lid !== null) {
213
- identifier = keyOptions.lid[lid];
218
+ identifier = keyOptions.lid.get(lid);
214
219
  }
215
220
 
216
221
  // we may have not seen this resource before
217
222
  // but just in case we check our own secondary lookup (`id`)
218
223
  if (identifier === undefined && id !== null) {
219
- identifier = keyOptions.id[id];
224
+ identifier = keyOptions.id.get(id);
220
225
  }
221
226
 
222
227
  if (identifier === undefined) {
@@ -233,12 +238,12 @@ export class IdentifierCache {
233
238
  // different than expected
234
239
  if (lid !== null && newLid !== lid) {
235
240
  throw new Error(`You should not change the <lid> of a RecordIdentifier`);
236
- } else if (lid === null) {
241
+ } else if (lid === null && !this._isDefaultConfig) {
237
242
  // allow configuration to tell us that we have
238
243
  // seen this `lid` before. E.g. a secondary lookup
239
244
  // connects this resource to a previously seen
240
245
  // resource.
241
- identifier = keyOptions.lid[newLid];
246
+ identifier = keyOptions.lid.get(newLid);
242
247
  }
243
248
 
244
249
  if (shouldGenerate === true) {
@@ -250,16 +255,16 @@ export class IdentifierCache {
250
255
  if (DEBUG) {
251
256
  // realistically if you hit this it means you changed `type` :/
252
257
  // TODO consider how to handle type change assertions more gracefully
253
- if (this._cache.lids[identifier.lid] !== undefined) {
258
+ if (this._cache.lids.has(identifier.lid)) {
254
259
  throw new Error(`You should not change the <type> of a RecordIdentifier`);
255
260
  }
256
261
  }
257
- this._cache.lids[identifier.lid] = identifier;
262
+ this._cache.lids.set(identifier.lid, identifier);
258
263
 
259
264
  // populate our primary lookup table
260
265
  // TODO consider having the `lid` cache be
261
266
  // one level up
262
- keyOptions.lid[identifier.lid] = identifier;
267
+ keyOptions.lid.set(identifier.lid, identifier);
263
268
 
264
269
  if (LOG_IDENTIFIERS && shouldGenerate) {
265
270
  // eslint-disable-next-line no-console
@@ -281,7 +286,7 @@ export class IdentifierCache {
281
286
  // because they may not match and we prefer
282
287
  // what we've set via resource data
283
288
  if (identifier.id !== null) {
284
- keyOptions.id[identifier.id] = identifier;
289
+ keyOptions.id.set(identifier.id, identifier);
285
290
 
286
291
  // TODO allow filling out of `id` here
287
292
  // for the `username` non-client created
@@ -355,14 +360,17 @@ export class IdentifierCache {
355
360
 
356
361
  // populate our unique table
357
362
  if (DEBUG) {
358
- if (this._cache.lids[identifier.lid] !== undefined) {
363
+ if (this._cache.lids.has(identifier.lid)) {
359
364
  throw new Error(`The lid generated for the new record is not unique as it matches an existing identifier`);
360
365
  }
361
366
  }
362
- this._cache.lids[identifier.lid] = identifier;
367
+ this._cache.lids.set(identifier.lid, identifier);
363
368
 
364
369
  // populate the type+lid cache
365
- keyOptions.lid[newLid] = identifier;
370
+ keyOptions.lid.set(newLid, identifier);
371
+ if (data.id) {
372
+ keyOptions.id.set(data.id, identifier);
373
+ }
366
374
 
367
375
  if (LOG_IDENTIFIERS) {
368
376
  // eslint-disable-next-line no-console
@@ -448,10 +456,10 @@ export class IdentifierCache {
448
456
  );
449
457
  }
450
458
  let keyOptions = getTypeIndex(this._cache.types, identifier.type);
451
- keyOptions.id[newId] = identifier;
459
+ keyOptions.id.set(newId, identifier);
452
460
 
453
461
  if (id !== null) {
454
- keyOptions.id[id] = undefined as unknown as StableRecordIdentifier;
462
+ keyOptions.id.delete(id);
455
463
  }
456
464
  } else if (LOG_IDENTIFIERS) {
457
465
  // eslint-disable-next-line no-console
@@ -480,10 +488,10 @@ export class IdentifierCache {
480
488
  this.forgetRecordIdentifier(abandoned);
481
489
 
482
490
  // ensure a secondary cache entry for this id for the identifier we do keep
483
- keyOptions.id[newId] = kept;
491
+ keyOptions.id.set(newId, kept);
484
492
  // ensure a secondary cache entry for this id for the abandoned identifier's type we do keep
485
493
  let baseKeyOptions = getTypeIndex(this._cache.types, existingIdentifier.type);
486
- baseKeyOptions.id[newId] = kept;
494
+ baseKeyOptions.id.set(newId, kept);
487
495
 
488
496
  // make sure that the `lid` on the data we are processing matches the lid we kept
489
497
  data.lid = kept.lid;
@@ -507,10 +515,10 @@ export class IdentifierCache {
507
515
  let identifier = this.getOrCreateRecordIdentifier(identifierObject);
508
516
  let keyOptions = getTypeIndex(this._cache.types, identifier.type);
509
517
  if (identifier.id !== null) {
510
- keyOptions.id[identifier.id] = undefined as unknown as StableRecordIdentifier;
518
+ keyOptions.id.delete(identifier.id);
511
519
  }
512
- this._cache.lids[identifier.lid] = undefined as unknown as StableRecordIdentifier;
513
- keyOptions.lid[identifier.lid] = undefined as unknown as StableRecordIdentifier;
520
+ this._cache.lids.delete(identifier.lid);
521
+ keyOptions.lid.delete(identifier.lid);
514
522
 
515
523
  IDENTIFIERS.delete(identifierObject);
516
524
  this._forget(identifier, 'record');
@@ -530,8 +538,8 @@ function getTypeIndex(typeMap: TypeMap, type: string): KeyOptions {
530
538
 
531
539
  if (typeIndex === undefined) {
532
540
  typeIndex = {
533
- lid: Object.create(null),
534
- id: Object.create(null),
541
+ lid: new Map(),
542
+ id: new Map(),
535
543
  };
536
544
  typeMap[type] = typeIndex;
537
545
  }
@@ -650,7 +658,7 @@ function detectMerge(
650
658
  const { id, type, lid } = identifier;
651
659
  if (id !== null && id !== newId && newId !== null) {
652
660
  let keyOptions = getTypeIndex(typesCache, identifier.type);
653
- let existingIdentifier = keyOptions.id[newId];
661
+ let existingIdentifier = keyOptions.id.get(newId);
654
662
 
655
663
  return existingIdentifier !== undefined ? existingIdentifier : false;
656
664
  } else {
@@ -658,12 +666,12 @@ function detectMerge(
658
666
 
659
667
  // If the ids and type are the same but lid is not the same, we should trigger a merge of the identifiers
660
668
  if (id !== null && id === newId && newType === type && data.lid && data.lid !== lid) {
661
- let existingIdentifier = lids[data.lid];
669
+ let existingIdentifier = lids.get(data.lid);
662
670
  return existingIdentifier !== undefined ? existingIdentifier : false;
663
671
  // If the lids are the same, and ids are the same, but types are different we should trigger a merge of the identifiers
664
672
  } else if (id !== null && id === newId && newType && newType !== type && data.lid && data.lid === lid) {
665
673
  let keyOptions = getTypeIndex(typesCache, newType);
666
- let existingIdentifier = keyOptions.id[id];
674
+ let existingIdentifier = keyOptions.id.get(id);
667
675
  return existingIdentifier !== undefined ? existingIdentifier : false;
668
676
  }
669
677
  }