@ember-data/store 4.10.0-alpha.4 → 4.10.0-alpha.6

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 (39) hide show
  1. package/addon/-private.js +1 -0
  2. package/addon/-private.js.map +1 -0
  3. package/addon/index-12e1fcb9.js +7660 -0
  4. package/addon/index-12e1fcb9.js.map +1 -0
  5. package/addon/index.js +1 -0
  6. package/addon/index.js.map +1 -0
  7. package/addon-main.js +90 -0
  8. package/package.json +44 -15
  9. package/addon/-private/caches/identifier-cache.ts +0 -686
  10. package/addon/-private/caches/instance-cache.ts +0 -694
  11. package/addon/-private/caches/record-data-for.ts +0 -34
  12. package/addon/-private/index.ts +0 -59
  13. package/addon/-private/legacy-model-support/record-reference.ts +0 -240
  14. package/addon/-private/legacy-model-support/schema-definition-service.ts +0 -148
  15. package/addon/-private/legacy-model-support/shim-model-class.ts +0 -92
  16. package/addon/-private/managers/record-array-manager.ts +0 -382
  17. package/addon/-private/managers/record-data-manager.ts +0 -845
  18. package/addon/-private/managers/record-data-store-wrapper.ts +0 -425
  19. package/addon/-private/managers/record-notification-manager.ts +0 -111
  20. package/addon/-private/network/fetch-manager.ts +0 -567
  21. package/addon/-private/network/finders.js +0 -104
  22. package/addon/-private/network/request-cache.ts +0 -132
  23. package/addon/-private/network/snapshot-record-array.ts +0 -209
  24. package/addon/-private/network/snapshot.ts +0 -563
  25. package/addon/-private/proxies/promise-proxies.ts +0 -228
  26. package/addon/-private/proxies/promise-proxy-base.js +0 -7
  27. package/addon/-private/record-arrays/identifier-array.ts +0 -974
  28. package/addon/-private/store-service.ts +0 -2904
  29. package/addon/-private/utils/coerce-id.ts +0 -41
  30. package/addon/-private/utils/common.js +0 -65
  31. package/addon/-private/utils/construct-resource.ts +0 -61
  32. package/addon/-private/utils/identifer-debug-consts.ts +0 -3
  33. package/addon/-private/utils/is-non-empty-string.ts +0 -3
  34. package/addon/-private/utils/normalize-model-name.ts +0 -21
  35. package/addon/-private/utils/promise-record.ts +0 -15
  36. package/addon/-private/utils/serializer-response.ts +0 -86
  37. package/addon/-private/utils/uuid-polyfill.ts +0 -73
  38. package/addon/index.ts +0 -14
  39. package/index.js +0 -49
@@ -1,686 +0,0 @@
1
- /**
2
- @module @ember-data/store
3
- */
4
- import { assert, warn } from '@ember/debug';
5
- import { DEBUG } from '@glimmer/env';
6
-
7
- import { getOwnConfig, macroCondition } from '@embroider/macros';
8
-
9
- import { LOG_IDENTIFIERS } from '@ember-data/private-build-infra/debugging';
10
- import type { ExistingResourceObject, ResourceIdentifierObject } from '@ember-data/types/q/ember-data-json-api';
11
- import type {
12
- ForgetMethod,
13
- GenerationMethod,
14
- Identifier,
15
- IdentifierBucket,
16
- RecordIdentifier,
17
- ResetMethod,
18
- ResourceData,
19
- StableRecordIdentifier,
20
- UpdateMethod,
21
- } from '@ember-data/types/q/identifier';
22
- import type { ConfidentDict } from '@ember-data/types/q/utils';
23
-
24
- import coerceId from '../utils/coerce-id';
25
- import { DEBUG_CLIENT_ORIGINATED, DEBUG_IDENTIFIER_BUCKET } from '../utils/identifer-debug-consts';
26
- import isNonEmptyString from '../utils/is-non-empty-string';
27
- import normalizeModelName from '../utils/normalize-model-name';
28
- import installPolyfill from '../utils/uuid-polyfill';
29
-
30
- const IDENTIFIERS = new Set();
31
-
32
- export function isStableIdentifier(identifier: Object): identifier is StableRecordIdentifier {
33
- return IDENTIFIERS.has(identifier);
34
- }
35
-
36
- const isFastBoot = typeof FastBoot !== 'undefined';
37
- const _crypto: Crypto = isFastBoot ? (FastBoot.require('crypto') as Crypto) : window.crypto;
38
-
39
- if (macroCondition(getOwnConfig<{ polyfillUUID: boolean }>().polyfillUUID)) {
40
- installPolyfill();
41
- }
42
-
43
- function uuidv4(): string {
44
- assert(
45
- 'crypto.randomUUID needs to be avaliable. Some browsers incorrectly disallow it in insecure contexts. You maybe want to enable the polyfill: https://github.com/emberjs/data#randomuuid-polyfill',
46
- _crypto.randomUUID
47
- );
48
- return _crypto.randomUUID();
49
- }
50
-
51
- function freeze<T>(obj: T): T {
52
- if (typeof Object.freeze === 'function') {
53
- return Object.freeze(obj);
54
- }
55
- return obj;
56
- }
57
-
58
- interface KeyOptions {
59
- lid: IdentifierMap;
60
- id: IdentifierMap;
61
- }
62
-
63
- type IdentifierMap = Map<string, StableRecordIdentifier>;
64
- type TypeMap = ConfidentDict<KeyOptions>;
65
- export type MergeMethod = (
66
- targetIdentifier: StableRecordIdentifier,
67
- matchedIdentifier: StableRecordIdentifier,
68
- resourceData: ResourceIdentifierObject | ExistingResourceObject
69
- ) => StableRecordIdentifier;
70
-
71
- let configuredForgetMethod: ForgetMethod | null;
72
- let configuredGenerationMethod: GenerationMethod | null;
73
- let configuredResetMethod: ResetMethod | null;
74
- let configuredUpdateMethod: UpdateMethod | null;
75
-
76
- export function setIdentifierGenerationMethod(method: GenerationMethod | null): void {
77
- configuredGenerationMethod = method;
78
- }
79
-
80
- export function setIdentifierUpdateMethod(method: UpdateMethod | null): void {
81
- configuredUpdateMethod = method;
82
- }
83
-
84
- export function setIdentifierForgetMethod(method: ForgetMethod | null): void {
85
- configuredForgetMethod = method;
86
- }
87
-
88
- export function setIdentifierResetMethod(method: ResetMethod | null): void {
89
- configuredResetMethod = method;
90
- }
91
-
92
- type WithLid = { lid: string };
93
- type WithId = { id: string | null; type: string };
94
-
95
- function defaultGenerationMethod(data: ResourceData | { type: string }, bucket: IdentifierBucket): string {
96
- if (isNonEmptyString((data as WithLid).lid)) {
97
- return (data as WithLid).lid;
98
- }
99
- if ((data as WithId).id !== undefined) {
100
- let { type, id } = data as WithId;
101
- // TODO: add test for id not a string
102
- if (isNonEmptyString(coerceId(id))) {
103
- return `@lid:${normalizeModelName(type)}-${id}`;
104
- }
105
- }
106
- return uuidv4();
107
- }
108
-
109
- function defaultEmptyCallback(...args: any[]): any {}
110
-
111
- let DEBUG_MAP;
112
- if (DEBUG) {
113
- DEBUG_MAP = new WeakMap<StableRecordIdentifier, StableRecordIdentifier>();
114
- }
115
-
116
- /**
117
- * Each instance of {Store} receives a unique instance of a IdentifierCache.
118
- *
119
- * This cache is responsible for assigning or retrieving the unique identify
120
- * for arbitrary resource data encountered by the store. Data representing
121
- * a unique resource or record should always be represented by the same
122
- * identifier.
123
- *
124
- * It can be configured by consuming applications.
125
- *
126
- * @class IdentifierCache
127
- @public
128
- */
129
- export class IdentifierCache {
130
- _cache = {
131
- lids: new Map<string, StableRecordIdentifier>(),
132
- types: Object.create(null) as TypeMap,
133
- };
134
- declare _generate: GenerationMethod;
135
- declare _update: UpdateMethod;
136
- declare _forget: ForgetMethod;
137
- declare _reset: ResetMethod;
138
- declare _merge: MergeMethod;
139
- declare _isDefaultConfig: boolean;
140
-
141
- constructor() {
142
- // we cache the user configuredGenerationMethod at init because it must
143
- // be configured prior and is not allowed to be changed
144
- this._generate = configuredGenerationMethod || defaultGenerationMethod;
145
- this._update = configuredUpdateMethod || defaultEmptyCallback;
146
- this._forget = configuredForgetMethod || defaultEmptyCallback;
147
- this._reset = configuredResetMethod || defaultEmptyCallback;
148
- this._merge = defaultEmptyCallback;
149
- this._isDefaultConfig = !configuredGenerationMethod;
150
- }
151
-
152
- /**
153
- * Internal hook to allow management of merge conflicts with identifiers.
154
- *
155
- * we allow late binding of this private internal merge so that
156
- * the cache can insert itself here to handle elimination of duplicates
157
- *
158
- * @method __configureMerge
159
- * @private
160
- */
161
- __configureMerge(method: MergeMethod | null) {
162
- this._merge = method || defaultEmptyCallback;
163
- }
164
-
165
- /**
166
- * @method _getRecordIdentifier
167
- * @private
168
- */
169
- _getRecordIdentifier(resource: ResourceIdentifierObject, shouldGenerate: true): StableRecordIdentifier;
170
- _getRecordIdentifier(resource: ResourceIdentifierObject, shouldGenerate: false): StableRecordIdentifier | undefined;
171
- _getRecordIdentifier(
172
- resource: ResourceIdentifierObject,
173
- shouldGenerate: boolean = false
174
- ): StableRecordIdentifier | undefined {
175
- // short circuit if we're already the stable version
176
- if (isStableIdentifier(resource)) {
177
- if (DEBUG) {
178
- // TODO should we instead just treat this case as a new generation skipping the short circuit?
179
- if (!this._cache.lids.has(resource.lid) || this._cache.lids.get(resource.lid) !== resource) {
180
- throw new Error(`The supplied identifier ${resource} does not belong to this store instance`);
181
- }
182
- }
183
- if (LOG_IDENTIFIERS) {
184
- // eslint-disable-next-line no-console
185
- console.log(`Identifiers: Peeked Identifier was already Stable ${String(resource)}`);
186
- }
187
- return resource;
188
- }
189
-
190
- let lid = coerceId(resource.lid);
191
- let identifier: StableRecordIdentifier | undefined = lid !== null ? this._cache.lids.get(lid) : undefined;
192
-
193
- if (identifier !== undefined) {
194
- if (LOG_IDENTIFIERS) {
195
- // eslint-disable-next-line no-console
196
- console.log(`Identifiers: cache HIT ${identifier}`, resource);
197
- }
198
- return identifier;
199
- }
200
-
201
- if (LOG_IDENTIFIERS) {
202
- // eslint-disable-next-line no-console
203
- console.groupCollapsed(`Identifiers: ${shouldGenerate ? 'Generating' : 'Peeking'} Identifier`, resource);
204
- }
205
-
206
- if (shouldGenerate === false) {
207
- if (!(resource as ExistingResourceObject).type || !(resource as ExistingResourceObject).id) {
208
- return;
209
- }
210
- }
211
-
212
- // `type` must always be present
213
- assert('resource.type needs to be a string', 'type' in resource && isNonEmptyString(resource.type));
214
-
215
- let type = resource.type && normalizeModelName(resource.type);
216
- let id = coerceId(resource.id);
217
-
218
- let keyOptions = getTypeIndex(this._cache.types, type);
219
-
220
- // go straight for the stable RecordIdentifier key'd to `lid`
221
- if (lid !== null) {
222
- identifier = keyOptions.lid.get(lid);
223
- }
224
-
225
- // we may have not seen this resource before
226
- // but just in case we check our own secondary lookup (`id`)
227
- if (identifier === undefined && id !== null) {
228
- identifier = keyOptions.id.get(id);
229
- }
230
-
231
- if (identifier === undefined) {
232
- // we have definitely not seen this resource before
233
- // so we allow the user configured `GenerationMethod` to tell us
234
- let newLid = this._generate(resource, 'record');
235
- if (LOG_IDENTIFIERS) {
236
- // eslint-disable-next-line no-console
237
- console.log(`Identifiers: lid ${newLid} determined for resource`, resource);
238
- }
239
-
240
- // we do this _even_ when `lid` is present because secondary lookups
241
- // may need to be populated, but we enforce not giving us something
242
- // different than expected
243
- if (lid !== null && newLid !== lid) {
244
- throw new Error(`You should not change the <lid> of a RecordIdentifier`);
245
- } else if (lid === null && !this._isDefaultConfig) {
246
- // allow configuration to tell us that we have
247
- // seen this `lid` before. E.g. a secondary lookup
248
- // connects this resource to a previously seen
249
- // resource.
250
- identifier = keyOptions.lid.get(newLid);
251
- }
252
-
253
- if (shouldGenerate === true) {
254
- if (identifier === undefined) {
255
- // if we still don't have an identifier, time to generate one
256
- identifier = makeStableRecordIdentifier(id, type, newLid, 'record', false);
257
-
258
- // populate our unique table
259
- if (DEBUG) {
260
- // realistically if you hit this it means you changed `type` :/
261
- // TODO consider how to handle type change assertions more gracefully
262
- if (this._cache.lids.has(identifier.lid)) {
263
- throw new Error(`You should not change the <type> of a RecordIdentifier`);
264
- }
265
- }
266
- this._cache.lids.set(identifier.lid, identifier);
267
-
268
- // populate our primary lookup table
269
- // TODO consider having the `lid` cache be
270
- // one level up
271
- keyOptions.lid.set(identifier.lid, identifier);
272
-
273
- if (LOG_IDENTIFIERS) {
274
- if (shouldGenerate) {
275
- // eslint-disable-next-line no-console
276
- console.log(`Identifiers: generated ${String(identifier)} for`, resource);
277
- if (resource[DEBUG_IDENTIFIER_BUCKET]) {
278
- // eslint-disable-next-line no-console
279
- console.trace(
280
- `[WARNING] Identifiers: generated a new identifier from a previously used identifier. This is likely a bug.`
281
- );
282
- }
283
- }
284
- }
285
- }
286
-
287
- // populate our own secondary lookup table
288
- // even for the "successful" secondary lookup
289
- // by `_generate()`, since we missed the cache
290
- // previously
291
- // we use identifier.id instead of id here
292
- // because they may not match and we prefer
293
- // what we've set via resource data
294
- if (identifier.id !== null) {
295
- keyOptions.id.set(identifier.id, identifier);
296
-
297
- // TODO allow filling out of `id` here
298
- // for the `username` non-client created
299
- // case.
300
- }
301
- }
302
- }
303
-
304
- if (LOG_IDENTIFIERS) {
305
- if (!identifier && !shouldGenerate) {
306
- // eslint-disable-next-line no-console
307
- console.log(`Identifiers: cache MISS`, resource);
308
- }
309
- // eslint-disable-next-line no-console
310
- console.groupEnd();
311
- }
312
-
313
- return identifier;
314
- }
315
-
316
- /**
317
- * allows us to peek without generating when needed
318
- * useful for the "create" case when we need to see if
319
- * we are accidentally overwritting something
320
- *
321
- * @method peekRecordIdentifier
322
- * @param resource
323
- * @returns {StableRecordIdentifier | undefined}
324
- * @private
325
- */
326
- peekRecordIdentifier(resource: ResourceIdentifierObject | Identifier): StableRecordIdentifier | undefined {
327
- return this._getRecordIdentifier(resource, false);
328
- }
329
-
330
- /**
331
- Returns the Identifier for the given Resource, creates one if it does not yet exist.
332
-
333
- Specifically this means that we:
334
-
335
- - validate the `id` `type` and `lid` combo against known identifiers
336
- - return an object with an `lid` that is stable (repeated calls with the same
337
- `id` + `type` or `lid` will return the same `lid` value)
338
- - this referential stability of the object itself is guaranteed
339
-
340
- @method getOrCreateRecordIdentifier
341
- @param resource
342
- @returns {StableRecordIdentifier}
343
- @public
344
- */
345
- getOrCreateRecordIdentifier(resource: ResourceData | Identifier): StableRecordIdentifier {
346
- return this._getRecordIdentifier(resource, true);
347
- }
348
-
349
- /**
350
- Returns a new Identifier for the supplied data. Call this method to generate
351
- an identifier when a new resource is being created local to the client and
352
- potentially does not have an `id`.
353
-
354
- Delegates generation to the user supplied `GenerateMethod` if one has been provided
355
- with the signature `generateMethod({ type }, 'record')`.
356
-
357
- @method createIdentifierForNewRecord
358
- @param data
359
- @returns {StableRecordIdentifier}
360
- @public
361
- */
362
- createIdentifierForNewRecord(data: { type: string; id?: string | null }): StableRecordIdentifier {
363
- let newLid = this._generate(data, 'record');
364
- let identifier = makeStableRecordIdentifier(data.id || null, data.type, newLid, 'record', true);
365
- let keyOptions = getTypeIndex(this._cache.types, data.type);
366
-
367
- // populate our unique table
368
- if (DEBUG) {
369
- if (this._cache.lids.has(identifier.lid)) {
370
- throw new Error(`The lid generated for the new record is not unique as it matches an existing identifier`);
371
- }
372
- }
373
- this._cache.lids.set(identifier.lid, identifier);
374
-
375
- // populate the type+lid cache
376
- keyOptions.lid.set(newLid, identifier);
377
- if (data.id) {
378
- keyOptions.id.set(data.id, identifier);
379
- }
380
-
381
- if (LOG_IDENTIFIERS) {
382
- // eslint-disable-next-line no-console
383
- console.log(`Identifiers: createded identifier ${String(identifier)} for newly generated resource`, data);
384
- }
385
-
386
- return identifier;
387
- }
388
-
389
- /**
390
- Provides the opportunity to update secondary lookup tables for existing identifiers
391
- Called after an identifier created with `createIdentifierForNewRecord` has been
392
- committed.
393
-
394
- Assigned `id` to an `Identifier` if `id` has not previously existed; however,
395
- attempting to change the `id` or calling update without providing an `id` when
396
- one is missing will throw an error.
397
-
398
- - sets `id` (if `id` was previously `null`)
399
- - `lid` and `type` MUST NOT be altered post creation
400
-
401
- If a merge occurs, it is possible the returned identifier does not match the originally
402
- provided identifier. In this case the abandoned identifier will go through the usual
403
- `forgetRecordIdentifier` codepaths.
404
-
405
- @method updateRecordIdentifier
406
- @param identifierObject
407
- @param data
408
- @returns {StableRecordIdentifier}
409
- @public
410
- */
411
- updateRecordIdentifier(identifierObject: RecordIdentifier, data: ResourceData): StableRecordIdentifier {
412
- let identifier = this.getOrCreateRecordIdentifier(identifierObject);
413
-
414
- let newId =
415
- (data as ExistingResourceObject).id !== undefined ? coerceId((data as ExistingResourceObject).id) : null;
416
- let existingIdentifier = detectMerge(this._cache.types, identifier, data, newId, this._cache.lids);
417
-
418
- if (!existingIdentifier) {
419
- // If the incoming type does not match the identifier type, we need to create an identifier for the incoming
420
- // data so we can merge the incoming data with the existing identifier, see #7325 and #7363
421
- if (
422
- (data as ExistingResourceObject).type &&
423
- identifier.type !== normalizeModelName((data as ExistingResourceObject).type)
424
- ) {
425
- let incomingDataResource = { ...data };
426
- // Need to strip the lid from the incomingData in order force a new identifier creation
427
- delete incomingDataResource.lid;
428
- existingIdentifier = this.getOrCreateRecordIdentifier(incomingDataResource);
429
- }
430
- }
431
-
432
- if (existingIdentifier) {
433
- let keyOptions = getTypeIndex(this._cache.types, identifier.type);
434
- let generatedIdentifier = identifier;
435
- identifier = this._mergeRecordIdentifiers(
436
- keyOptions,
437
- generatedIdentifier,
438
- existingIdentifier,
439
- data,
440
- newId as string
441
- );
442
- if (LOG_IDENTIFIERS) {
443
- // eslint-disable-next-line no-console
444
- console.log(
445
- `Identifiers: merged identifiers ${generatedIdentifier.lid} and ${existingIdentifier.lid} for resource into ${identifier.lid}`,
446
- data
447
- );
448
- }
449
- }
450
-
451
- let id = identifier.id;
452
- performRecordIdentifierUpdate(identifier, data, this._update);
453
- newId = identifier.id;
454
-
455
- // add to our own secondary lookup table
456
- if (id !== newId && newId !== null) {
457
- if (LOG_IDENTIFIERS) {
458
- // eslint-disable-next-line no-console
459
- console.log(
460
- `Identifiers: updated id for identifier ${identifier.lid} from '${id}' to '${newId}' for resource`,
461
- data
462
- );
463
- }
464
- let keyOptions = getTypeIndex(this._cache.types, identifier.type);
465
- keyOptions.id.set(newId, identifier);
466
-
467
- if (id !== null) {
468
- keyOptions.id.delete(id);
469
- }
470
- } else if (LOG_IDENTIFIERS) {
471
- // eslint-disable-next-line no-console
472
- console.log(`Identifiers: updated identifier ${identifier.lid} resource`, data);
473
- }
474
-
475
- return identifier;
476
- }
477
-
478
- /**
479
- * @method _mergeRecordIdentifiers
480
- * @private
481
- */
482
- _mergeRecordIdentifiers(
483
- keyOptions: KeyOptions,
484
- identifier: StableRecordIdentifier,
485
- existingIdentifier: StableRecordIdentifier,
486
- data: ResourceIdentifierObject | ExistingResourceObject,
487
- newId: string
488
- ): StableRecordIdentifier {
489
- // delegate determining which identifier to keep to the configured MergeMethod
490
- let kept = this._merge(identifier, existingIdentifier, data);
491
- let abandoned = kept === identifier ? existingIdentifier : identifier;
492
-
493
- // cleanup the identifier we no longer need
494
- this.forgetRecordIdentifier(abandoned);
495
-
496
- // ensure a secondary cache entry for this id for the identifier we do keep
497
- keyOptions.id.set(newId, kept);
498
- // ensure a secondary cache entry for this id for the abandoned identifier's type we do keep
499
- let baseKeyOptions = getTypeIndex(this._cache.types, existingIdentifier.type);
500
- baseKeyOptions.id.set(newId, kept);
501
-
502
- // make sure that the `lid` on the data we are processing matches the lid we kept
503
- data.lid = kept.lid;
504
-
505
- return kept;
506
- }
507
-
508
- /**
509
- Provides the opportunity to eliminate an identifier from secondary lookup tables
510
- as well as eliminates it from ember-data's own lookup tables and book keeping.
511
-
512
- Useful when a record has been deleted and the deletion has been persisted and
513
- we do not care about the record anymore. Especially useful when an `id` of a
514
- deleted record might be reused later for a new record.
515
-
516
- @method forgetRecordIdentifier
517
- @param identifierObject
518
- @public
519
- */
520
- forgetRecordIdentifier(identifierObject: RecordIdentifier): void {
521
- let identifier = this.getOrCreateRecordIdentifier(identifierObject);
522
- let keyOptions = getTypeIndex(this._cache.types, identifier.type);
523
- if (identifier.id !== null) {
524
- keyOptions.id.delete(identifier.id);
525
- }
526
- this._cache.lids.delete(identifier.lid);
527
- keyOptions.lid.delete(identifier.lid);
528
-
529
- IDENTIFIERS.delete(identifierObject);
530
- this._forget(identifier, 'record');
531
- if (LOG_IDENTIFIERS) {
532
- // eslint-disable-next-line no-console
533
- console.log(`Identifiers: released identifier ${identifierObject.lid}`);
534
- }
535
- }
536
-
537
- destroy() {
538
- this._reset();
539
- }
540
- }
541
-
542
- function getTypeIndex(typeMap: TypeMap, type: string): KeyOptions {
543
- let typeIndex: KeyOptions = typeMap[type];
544
-
545
- if (typeIndex === undefined) {
546
- typeIndex = {
547
- lid: new Map(),
548
- id: new Map(),
549
- };
550
- typeMap[type] = typeIndex;
551
- }
552
-
553
- return typeIndex;
554
- }
555
-
556
- function makeStableRecordIdentifier(
557
- id: string | null,
558
- type: string,
559
- lid: string,
560
- bucket: IdentifierBucket,
561
- clientOriginated: boolean = false
562
- ): Readonly<StableRecordIdentifier> {
563
- let recordIdentifier = {
564
- lid,
565
- id,
566
- type,
567
- };
568
- IDENTIFIERS.add(recordIdentifier);
569
-
570
- if (DEBUG) {
571
- // we enforce immutability in dev
572
- // but preserve our ability to do controlled updates to the reference
573
- let wrapper = {
574
- get lid() {
575
- return recordIdentifier.lid;
576
- },
577
- get id() {
578
- return recordIdentifier.id;
579
- },
580
- get type() {
581
- return recordIdentifier.type;
582
- },
583
- toString() {
584
- let { type, id, lid } = recordIdentifier;
585
- return `${clientOriginated ? '[CLIENT_ORIGINATED] ' : ''}${type}:${id} (${lid})`;
586
- },
587
- toJSON() {
588
- let { type, id, lid } = recordIdentifier;
589
- return { type, id, lid };
590
- },
591
- };
592
- wrapper[DEBUG_CLIENT_ORIGINATED] = clientOriginated;
593
- wrapper[DEBUG_IDENTIFIER_BUCKET] = bucket;
594
- IDENTIFIERS.add(wrapper);
595
- DEBUG_MAP.set(wrapper, recordIdentifier);
596
- wrapper = freeze(wrapper);
597
- return wrapper;
598
- }
599
-
600
- return recordIdentifier;
601
- }
602
-
603
- function performRecordIdentifierUpdate(identifier: StableRecordIdentifier, data: ResourceData, updateFn: UpdateMethod) {
604
- if (DEBUG) {
605
- let { lid } = data;
606
- let id = 'id' in data ? data.id : undefined;
607
- let type = 'type' in data && data.type && normalizeModelName(data.type);
608
-
609
- // get the mutable instance behind our proxy wrapper
610
- let wrapper = identifier;
611
- identifier = DEBUG_MAP.get(wrapper);
612
-
613
- if (lid !== undefined) {
614
- let newLid = coerceId(lid);
615
- if (newLid !== identifier.lid) {
616
- throw new Error(
617
- `The 'lid' for a RecordIdentifier cannot be updated once it has been created. Attempted to set lid for '${wrapper}' to '${lid}'.`
618
- );
619
- }
620
- }
621
-
622
- if (id !== undefined) {
623
- let newId = coerceId(id);
624
-
625
- if (identifier.id !== null && identifier.id !== newId) {
626
- // here we warn and ignore, as this may be a mistake, but we allow the user
627
- // to have multiple cache-keys pointing at a single lid so we cannot error
628
- warn(
629
- `The 'id' for a RecordIdentifier should not be updated once it has been set. Attempted to set id for '${wrapper}' to '${newId}'.`,
630
- false,
631
- { id: 'ember-data:multiple-ids-for-identifier' }
632
- );
633
- }
634
- }
635
-
636
- // TODO consider just ignoring here to allow flexible polymorphic support
637
- if (type && type !== identifier.type) {
638
- throw new Error(
639
- `The 'type' for a RecordIdentifier cannot be updated once it has been set. Attempted to set type for '${wrapper}' to '${type}'.`
640
- );
641
- }
642
-
643
- updateFn(wrapper, data, 'record');
644
- } else {
645
- updateFn(identifier, data, 'record');
646
- }
647
-
648
- // upgrade the ID, this is a "one time only" ability
649
- // for the multiple-cache-key scenario we "could"
650
- // use a heuristic to guess the best id for display
651
- // (usually when `data.id` is available and `data.attributes` is not)
652
- if ((data as ExistingResourceObject).id !== undefined) {
653
- identifier.id = coerceId((data as ExistingResourceObject).id);
654
- }
655
- }
656
-
657
- function detectMerge(
658
- typesCache: ConfidentDict<KeyOptions>,
659
- identifier: StableRecordIdentifier,
660
- data: ResourceIdentifierObject | ExistingResourceObject,
661
- newId: string | null,
662
- lids: IdentifierMap
663
- ): StableRecordIdentifier | false {
664
- const { id, type, lid } = identifier;
665
- if (id !== null && id !== newId && newId !== null) {
666
- let keyOptions = getTypeIndex(typesCache, identifier.type);
667
- let existingIdentifier = keyOptions.id.get(newId);
668
-
669
- return existingIdentifier !== undefined ? existingIdentifier : false;
670
- } else {
671
- let newType = (data as ExistingResourceObject).type && normalizeModelName((data as ExistingResourceObject).type);
672
-
673
- // If the ids and type are the same but lid is not the same, we should trigger a merge of the identifiers
674
- if (id !== null && id === newId && newType === type && data.lid && data.lid !== lid) {
675
- let existingIdentifier = lids.get(data.lid);
676
- return existingIdentifier !== undefined ? existingIdentifier : false;
677
- // If the lids are the same, and ids are the same, but types are different we should trigger a merge of the identifiers
678
- } else if (id !== null && id === newId && newType && newType !== type && data.lid && data.lid === lid) {
679
- let keyOptions = getTypeIndex(typesCache, newType);
680
- let existingIdentifier = keyOptions.id.get(id);
681
- return existingIdentifier !== undefined ? existingIdentifier : false;
682
- }
683
- }
684
-
685
- return false;
686
- }