@ember-data/store 4.8.0-alpha.1 → 4.8.0-alpha.4

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 (41) hide show
  1. package/addon/-debug/index.js +1 -1
  2. package/addon/-private/{identifier-cache.ts → caches/identifier-cache.ts} +114 -47
  3. package/addon/-private/caches/instance-cache.ts +702 -0
  4. package/addon/-private/{record-data-for.ts → caches/record-data-for.ts} +2 -2
  5. package/addon/-private/index.ts +38 -21
  6. package/addon/-private/{model → legacy-model-support}/record-reference.ts +15 -13
  7. package/addon/-private/{schema-definition-service.ts → legacy-model-support/schema-definition-service.ts} +13 -9
  8. package/addon/-private/{model → legacy-model-support}/shim-model-class.ts +8 -4
  9. package/addon/-private/{record-array-manager.ts → managers/record-array-manager.ts} +21 -44
  10. package/addon/-private/managers/record-data-manager.ts +830 -0
  11. package/addon/-private/managers/record-data-store-wrapper.ts +413 -0
  12. package/addon/-private/managers/record-notification-manager.ts +90 -0
  13. package/addon/-private/network/fetch-manager.ts +552 -0
  14. package/addon/-private/{finders.js → network/finders.js} +4 -12
  15. package/addon/-private/{request-cache.ts → network/request-cache.ts} +1 -1
  16. package/addon/-private/{snapshot-record-array.ts → network/snapshot-record-array.ts} +3 -3
  17. package/addon/-private/{snapshot.ts → network/snapshot.ts} +40 -49
  18. package/addon/-private/{promise-proxies.ts → proxies/promise-proxies.ts} +4 -4
  19. package/addon/-private/{promise-proxy-base.js → proxies/promise-proxy-base.js} +0 -0
  20. package/addon/-private/record-arrays/adapter-populated-record-array.ts +9 -11
  21. package/addon/-private/record-arrays/record-array.ts +25 -15
  22. package/addon/-private/{core-store.ts → store-service.ts} +412 -148
  23. package/addon/-private/{coerce-id.ts → utils/coerce-id.ts} +1 -1
  24. package/addon/-private/{common.js → utils/common.js} +1 -2
  25. package/addon/-private/utils/construct-resource.ts +2 -2
  26. package/addon/-private/{identifer-debug-consts.ts → utils/identifer-debug-consts.ts} +0 -0
  27. package/addon/-private/{normalize-model-name.ts → utils/normalize-model-name.ts} +1 -3
  28. package/addon/-private/utils/promise-record.ts +3 -3
  29. package/addon/-private/{serializer-response.ts → utils/serializer-response.ts} +2 -2
  30. package/addon/-private/utils/uuid-polyfill.ts +71 -0
  31. package/addon/-private/{weak-cache.ts → utils/weak-cache.ts} +0 -0
  32. package/package.json +11 -7
  33. package/addon/-private/errors-utils.js +0 -146
  34. package/addon/-private/fetch-manager.ts +0 -597
  35. package/addon/-private/identity-map.ts +0 -54
  36. package/addon/-private/instance-cache.ts +0 -387
  37. package/addon/-private/internal-model-factory.ts +0 -359
  38. package/addon/-private/internal-model-map.ts +0 -121
  39. package/addon/-private/model/internal-model.ts +0 -602
  40. package/addon/-private/record-data-store-wrapper.ts +0 -243
  41. package/addon/-private/record-notification-manager.ts +0 -73
@@ -9,7 +9,7 @@ import { DEBUG } from '@glimmer/env';
9
9
  relationship (specified via `relationshipMeta`) of the `record`.
10
10
 
11
11
  This utility should only be used internally, as both record parameters must
12
- be an InternalModel and the `relationshipMeta` needs to be the meta
12
+ be stable record identifiers and the `relationshipMeta` needs to be the meta
13
13
  information about the relationship, retrieved via
14
14
  `record.relationshipFor(key)`.
15
15
  */
@@ -4,6 +4,9 @@
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';
8
+
9
+ import { LOG_IDENTIFIERS } from '@ember-data/private-build-infra/debugging';
7
10
  import type { ExistingResourceObject, ResourceIdentifierObject } from '@ember-data/types/q/ember-data-json-api';
8
11
  import type {
9
12
  ForgetMethod,
@@ -18,11 +21,11 @@ import type {
18
21
  } from '@ember-data/types/q/identifier';
19
22
  import type { ConfidentDict } from '@ember-data/types/q/utils';
20
23
 
21
- import coerceId from './coerce-id';
22
- import { DEBUG_CLIENT_ORIGINATED, DEBUG_IDENTIFIER_BUCKET } from './identifer-debug-consts';
23
- import normalizeModelName from './normalize-model-name';
24
- import isNonEmptyString from './utils/is-non-empty-string';
25
- import WeakCache from './weak-cache';
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 WeakCache from '../utils/weak-cache';
26
29
 
27
30
  const IDENTIFIERS = new WeakSet();
28
31
 
@@ -33,6 +36,10 @@ export function isStableIdentifier(identifier: Object): identifier is StableReco
33
36
  const isFastBoot = typeof FastBoot !== 'undefined';
34
37
  const _crypto: Crypto = isFastBoot ? (FastBoot.require('crypto') as Crypto) : window.crypto;
35
38
 
39
+ if (macroCondition(getOwnConfig<{ polyfillUUID: boolean }>().polyfillUUID)) {
40
+ importSync('./utils/uuid-polyfill');
41
+ }
42
+
36
43
  function uuidv4(): string {
37
44
  return _crypto.randomUUID();
38
45
  }
@@ -47,7 +54,6 @@ function freeze<T>(obj: T): T {
47
54
  interface KeyOptions {
48
55
  lid: IdentifierMap;
49
56
  id: IdentifierMap;
50
- _allIdentifiers: StableRecordIdentifier[];
51
57
  }
52
58
 
53
59
  type IdentifierMap = ConfidentDict<StableRecordIdentifier>;
@@ -114,19 +120,15 @@ if (DEBUG) {
114
120
  @public
115
121
  */
116
122
  export class IdentifierCache {
117
- // Typescript still leaks private properties in the final
118
- // compiled class, so we may want to move these from _underscore
119
- // to a WeakMap to avoid leaking
120
- // currently we leak this for test purposes
121
123
  _cache = {
122
124
  lids: Object.create(null) as IdentifierMap,
123
125
  types: Object.create(null) as TypeMap,
124
126
  };
125
- private _generate: GenerationMethod;
126
- private _update: UpdateMethod;
127
- private _forget: ForgetMethod;
128
- private _reset: ResetMethod;
129
- private _merge: MergeMethod;
127
+ declare _generate: GenerationMethod;
128
+ declare _update: UpdateMethod;
129
+ declare _forget: ForgetMethod;
130
+ declare _reset: ResetMethod;
131
+ declare _merge: MergeMethod;
130
132
 
131
133
  constructor() {
132
134
  // we cache the user configuredGenerationMethod at init because it must
@@ -141,8 +143,8 @@ export class IdentifierCache {
141
143
  /**
142
144
  * Internal hook to allow management of merge conflicts with identifiers.
143
145
  *
144
- * we allow late binding of this private internal merge so that `internalModelFactory`
145
- * can insert itself here to handle elimination of duplicates
146
+ * we allow late binding of this private internal merge so that
147
+ * the cache can insert itself here to handle elimination of duplicates
146
148
  *
147
149
  * @method __configureMerge
148
150
  * @private
@@ -155,12 +157,9 @@ export class IdentifierCache {
155
157
  * @method _getRecordIdentifier
156
158
  * @private
157
159
  */
158
- private _getRecordIdentifier(resource: ResourceIdentifierObject, shouldGenerate: true): StableRecordIdentifier;
159
- private _getRecordIdentifier(
160
- resource: ResourceIdentifierObject,
161
- shouldGenerate: false
162
- ): StableRecordIdentifier | undefined;
163
- private _getRecordIdentifier(
160
+ _getRecordIdentifier(resource: ResourceIdentifierObject, shouldGenerate: true): StableRecordIdentifier;
161
+ _getRecordIdentifier(resource: ResourceIdentifierObject, shouldGenerate: false): StableRecordIdentifier | undefined;
162
+ _getRecordIdentifier(
164
163
  resource: ResourceIdentifierObject,
165
164
  shouldGenerate: boolean = false
166
165
  ): StableRecordIdentifier | undefined {
@@ -168,10 +167,14 @@ export class IdentifierCache {
168
167
  if (isStableIdentifier(resource)) {
169
168
  if (DEBUG) {
170
169
  // TODO should we instead just treat this case as a new generation skipping the short circuit?
171
- if (!(resource.lid in this._cache.lids) || this._cache.lids[resource.lid] !== resource) {
170
+ if (!(this._cache.lids[resource.lid] !== undefined) || this._cache.lids[resource.lid] !== resource) {
172
171
  throw new Error(`The supplied identifier ${resource} does not belong to this store instance`);
173
172
  }
174
173
  }
174
+ if (LOG_IDENTIFIERS) {
175
+ // eslint-disable-next-line no-console
176
+ console.log(`Identifiers: Peeked Identifier was already Stable ${String(resource)}`);
177
+ }
175
178
  return resource;
176
179
  }
177
180
 
@@ -179,11 +182,20 @@ export class IdentifierCache {
179
182
  let identifier: StableRecordIdentifier | undefined = lid !== null ? this._cache.lids[lid] : undefined;
180
183
 
181
184
  if (identifier !== undefined) {
185
+ if (LOG_IDENTIFIERS) {
186
+ // eslint-disable-next-line no-console
187
+ console.log(`Identifiers: cache HIT ${identifier}`, resource);
188
+ }
182
189
  return identifier;
183
190
  }
184
191
 
192
+ if (LOG_IDENTIFIERS) {
193
+ // eslint-disable-next-line no-console
194
+ console.groupCollapsed(`Identifiers: ${shouldGenerate ? 'Generating' : 'Peeking'} Identifier`, resource);
195
+ }
196
+
185
197
  if (shouldGenerate === false) {
186
- if (!('type' in resource) || !('id' in resource) || !resource.type || !resource.id) {
198
+ if (!(resource as ExistingResourceObject).type || !(resource as ExistingResourceObject).id) {
187
199
  return;
188
200
  }
189
201
  }
@@ -211,6 +223,10 @@ export class IdentifierCache {
211
223
  // we have definitely not seen this resource before
212
224
  // so we allow the user configured `GenerationMethod` to tell us
213
225
  let newLid = this._generate(resource, 'record');
226
+ if (LOG_IDENTIFIERS) {
227
+ // eslint-disable-next-line no-console
228
+ console.log(`Identifiers: lid ${newLid} determined for resource`, resource);
229
+ }
214
230
 
215
231
  // we do this _even_ when `lid` is present because secondary lookups
216
232
  // may need to be populated, but we enforce not giving us something
@@ -234,7 +250,7 @@ export class IdentifierCache {
234
250
  if (DEBUG) {
235
251
  // realistically if you hit this it means you changed `type` :/
236
252
  // TODO consider how to handle type change assertions more gracefully
237
- if (identifier.lid in this._cache.lids) {
253
+ if (this._cache.lids[identifier.lid] !== undefined) {
238
254
  throw new Error(`You should not change the <type> of a RecordIdentifier`);
239
255
  }
240
256
  }
@@ -244,9 +260,17 @@ export class IdentifierCache {
244
260
  // TODO consider having the `lid` cache be
245
261
  // one level up
246
262
  keyOptions.lid[identifier.lid] = identifier;
247
- // TODO exists temporarily to support `peekAll`
248
- // but likely to move
249
- keyOptions._allIdentifiers.push(identifier);
263
+
264
+ if (LOG_IDENTIFIERS && shouldGenerate) {
265
+ // eslint-disable-next-line no-console
266
+ console.log(`Identifiers: generated ${String(identifier)} for`, resource);
267
+ if (resource[DEBUG_IDENTIFIER_BUCKET]) {
268
+ // eslint-disable-next-line no-console
269
+ console.trace(
270
+ `[WARNING] Identifiers: generated a new identifier from a previously used identifier. This is likely a bug.`
271
+ );
272
+ }
273
+ }
250
274
  }
251
275
 
252
276
  // populate our own secondary lookup table
@@ -266,6 +290,15 @@ export class IdentifierCache {
266
290
  }
267
291
  }
268
292
 
293
+ if (LOG_IDENTIFIERS) {
294
+ if (!identifier && !shouldGenerate) {
295
+ // eslint-disable-next-line no-console
296
+ console.log(`Identifiers: cache MISS`, resource);
297
+ }
298
+ // eslint-disable-next-line no-console
299
+ console.groupEnd();
300
+ }
301
+
269
302
  return identifier;
270
303
  }
271
304
 
@@ -322,7 +355,7 @@ export class IdentifierCache {
322
355
 
323
356
  // populate our unique table
324
357
  if (DEBUG) {
325
- if (identifier.lid in this._cache.lids) {
358
+ if (this._cache.lids[identifier.lid] !== undefined) {
326
359
  throw new Error(`The lid generated for the new record is not unique as it matches an existing identifier`);
327
360
  }
328
361
  }
@@ -330,9 +363,11 @@ export class IdentifierCache {
330
363
 
331
364
  // populate the type+lid cache
332
365
  keyOptions.lid[newLid] = identifier;
333
- // ensure a peekAll sees our new identifier too
334
- // TODO move this outta here?
335
- keyOptions._allIdentifiers.push(identifier);
366
+
367
+ if (LOG_IDENTIFIERS) {
368
+ // eslint-disable-next-line no-console
369
+ console.log(`Identifiers: createded identifier ${String(identifier)} for newly generated resource`, data);
370
+ }
336
371
 
337
372
  return identifier;
338
373
  }
@@ -362,13 +397,17 @@ export class IdentifierCache {
362
397
  updateRecordIdentifier(identifierObject: RecordIdentifier, data: ResourceData): StableRecordIdentifier {
363
398
  let identifier = this.getOrCreateRecordIdentifier(identifierObject);
364
399
 
365
- let newId = 'id' in data ? coerceId(data.id) : null;
400
+ let newId =
401
+ (data as ExistingResourceObject).id !== undefined ? coerceId((data as ExistingResourceObject).id) : null;
366
402
  let existingIdentifier = detectMerge(this._cache.types, identifier, data, newId, this._cache.lids);
367
403
 
368
404
  if (!existingIdentifier) {
369
405
  // If the incoming type does not match the identifier type, we need to create an identifier for the incoming
370
406
  // data so we can merge the incoming data with the existing identifier, see #7325 and #7363
371
- if ('type' in data && data.type && identifier.type !== normalizeModelName(data.type)) {
407
+ if (
408
+ (data as ExistingResourceObject).type &&
409
+ identifier.type !== normalizeModelName((data as ExistingResourceObject).type)
410
+ ) {
372
411
  let incomingDataResource = { ...data };
373
412
  // Need to strip the lid from the incomingData in order force a new identifier creation
374
413
  delete incomingDataResource.lid;
@@ -378,7 +417,21 @@ export class IdentifierCache {
378
417
 
379
418
  if (existingIdentifier) {
380
419
  let keyOptions = getTypeIndex(this._cache.types, identifier.type);
381
- identifier = this._mergeRecordIdentifiers(keyOptions, identifier, existingIdentifier, data, newId as string);
420
+ let generatedIdentifier = identifier;
421
+ identifier = this._mergeRecordIdentifiers(
422
+ keyOptions,
423
+ generatedIdentifier,
424
+ existingIdentifier,
425
+ data,
426
+ newId as string
427
+ );
428
+ if (LOG_IDENTIFIERS) {
429
+ // eslint-disable-next-line no-console
430
+ console.log(
431
+ `Identifiers: merged identifiers ${generatedIdentifier.lid} and ${existingIdentifier.lid} for resource into ${identifier.lid}`,
432
+ data
433
+ );
434
+ }
382
435
  }
383
436
 
384
437
  let id = identifier.id;
@@ -387,12 +440,22 @@ export class IdentifierCache {
387
440
 
388
441
  // add to our own secondary lookup table
389
442
  if (id !== newId && newId !== null) {
443
+ if (LOG_IDENTIFIERS) {
444
+ // eslint-disable-next-line no-console
445
+ console.log(
446
+ `Identifiers: updated id for identifier ${identifier.lid} from '${id}' to '${newId}' for resource`,
447
+ data
448
+ );
449
+ }
390
450
  let keyOptions = getTypeIndex(this._cache.types, identifier.type);
391
451
  keyOptions.id[newId] = identifier;
392
452
 
393
453
  if (id !== null) {
394
- delete keyOptions.id[id];
454
+ keyOptions.id[id] = undefined as unknown as StableRecordIdentifier;
395
455
  }
456
+ } else if (LOG_IDENTIFIERS) {
457
+ // eslint-disable-next-line no-console
458
+ console.log(`Identifiers: updated identifier ${identifier.lid} resource`, data);
396
459
  }
397
460
 
398
461
  return identifier;
@@ -444,16 +507,17 @@ export class IdentifierCache {
444
507
  let identifier = this.getOrCreateRecordIdentifier(identifierObject);
445
508
  let keyOptions = getTypeIndex(this._cache.types, identifier.type);
446
509
  if (identifier.id !== null) {
447
- delete keyOptions.id[identifier.id];
510
+ keyOptions.id[identifier.id] = undefined as unknown as StableRecordIdentifier;
448
511
  }
449
- delete this._cache.lids[identifier.lid];
450
- delete keyOptions.lid[identifier.lid];
451
-
452
- let index = keyOptions._allIdentifiers.indexOf(identifier);
453
- keyOptions._allIdentifiers.splice(index, 1);
512
+ this._cache.lids[identifier.lid] = undefined as unknown as StableRecordIdentifier;
513
+ keyOptions.lid[identifier.lid] = undefined as unknown as StableRecordIdentifier;
454
514
 
455
515
  IDENTIFIERS.delete(identifierObject);
456
516
  this._forget(identifier, 'record');
517
+ if (LOG_IDENTIFIERS) {
518
+ // eslint-disable-next-line no-console
519
+ console.log(`Identifiers: released identifier ${identifierObject.lid}`);
520
+ }
457
521
  }
458
522
 
459
523
  destroy() {
@@ -468,7 +532,6 @@ function getTypeIndex(typeMap: TypeMap, type: string): KeyOptions {
468
532
  typeIndex = {
469
533
  lid: Object.create(null),
470
534
  id: Object.create(null),
471
- _allIdentifiers: [],
472
535
  };
473
536
  typeMap[type] = typeIndex;
474
537
  }
@@ -507,6 +570,10 @@ function makeStableRecordIdentifier(
507
570
  let { type, id, lid } = recordIdentifier;
508
571
  return `${clientOriginated ? '[CLIENT_ORIGINATED] ' : ''}${type}:${id} (${lid})`;
509
572
  },
573
+ toJSON() {
574
+ let { type, id, lid } = recordIdentifier;
575
+ return { type, id, lid };
576
+ },
510
577
  };
511
578
  wrapper[DEBUG_CLIENT_ORIGINATED] = clientOriginated;
512
579
  wrapper[DEBUG_IDENTIFIER_BUCKET] = bucket;
@@ -568,8 +635,8 @@ function performRecordIdentifierUpdate(identifier: StableRecordIdentifier, data:
568
635
  // for the multiple-cache-key scenario we "could"
569
636
  // use a heuristic to guess the best id for display
570
637
  // (usually when `data.id` is available and `data.attributes` is not)
571
- if ('id' in data && data.id !== undefined) {
572
- identifier.id = coerceId(data.id);
638
+ if ((data as ExistingResourceObject).id !== undefined) {
639
+ identifier.id = coerceId((data as ExistingResourceObject).id);
573
640
  }
574
641
  }
575
642
 
@@ -587,7 +654,7 @@ function detectMerge(
587
654
 
588
655
  return existingIdentifier !== undefined ? existingIdentifier : false;
589
656
  } else {
590
- let newType = 'type' in data && data.type && normalizeModelName(data.type);
657
+ let newType = (data as ExistingResourceObject).type && normalizeModelName((data as ExistingResourceObject).type);
591
658
 
592
659
  // If the ids and type are the same but lid is not the same, we should trigger a merge of the identifiers
593
660
  if (id !== null && id === newId && newType === type && data.lid && data.lid !== lid) {