@warp-drive/core 5.7.0-alpha.13 → 5.7.0-alpha.14

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 (34) hide show
  1. package/declarations/reactive/-private/symbols.d.ts +0 -1
  2. package/declarations/request/-private/fetch.d.ts +2 -2
  3. package/declarations/request/-private/utils.d.ts +44 -2
  4. package/declarations/store/-private/cache-handler/types.d.ts +1 -1
  5. package/declarations/store/-private/managers/notification-manager.d.ts +4 -7
  6. package/declarations/store/-private/managers/record-array-manager.d.ts +72 -21
  7. package/declarations/store/-private/new-core-tmp/reactivity/signal.d.ts +1 -0
  8. package/declarations/store/-private/record-arrays/-utils.d.ts +82 -0
  9. package/declarations/store/-private/record-arrays/legacy-live-array.d.ts +89 -0
  10. package/declarations/store/-private/record-arrays/{many-array.d.ts → legacy-many-array.d.ts} +43 -101
  11. package/declarations/store/-private/record-arrays/legacy-query.d.ts +103 -0
  12. package/declarations/store/-private/record-arrays/resource-array.d.ts +82 -0
  13. package/declarations/store/-private/store-service.d.ts +3 -3
  14. package/declarations/store/-private.d.ts +4 -2
  15. package/declarations/store/deprecated/store.d.ts +8 -9
  16. package/declarations/types/-private.d.ts +1 -1
  17. package/declarations/types/cache/operations.d.ts +97 -14
  18. package/declarations/types/request.d.ts +21 -0
  19. package/declarations/types/schema/fields.d.ts +1 -1
  20. package/dist/{context-COmAnXUQ.js → context-kQXhkeBj.js} +13 -0
  21. package/dist/graph/-private.js +4 -4
  22. package/dist/index.js +6 -2
  23. package/dist/reactive/-private.js +1 -1
  24. package/dist/reactive.js +3 -3
  25. package/dist/{request-state-BWYju5O9.js → request-state-CCrTjb0Z.js} +861 -799
  26. package/dist/request.js +1 -1
  27. package/dist/store/-private.js +1 -1
  28. package/dist/store.js +2 -1
  29. package/dist/{symbols-BoONANuz.js → symbols-C5p2hcy9.js} +0 -1
  30. package/dist/types/-private.js +1 -1
  31. package/dist/types/request.js +27 -0
  32. package/dist/types/schema/fields.js +2 -0
  33. package/package.json +3 -3
  34. package/declarations/store/-private/record-arrays/identifier-array.d.ts +0 -147
@@ -5,10 +5,10 @@ import { setLogging, getRuntimeConfig } from './types/runtime.js';
5
5
  import { getOrSetGlobal, peekTransient, setTransient } from './types/-private.js';
6
6
  import { CACHE_OWNER, DEBUG_STALE_CACHE_OWNER, DEBUG_IDENTIFIER_BUCKET, DEBUG_CLIENT_ORIGINATED } from './types/identifier.js';
7
7
  import { dasherize } from './utils/string.js';
8
+ import { S as SOURCE, C as Context, D as Destroy, a as Checkout } from "./symbols-C5p2hcy9.js";
8
9
  import { a as createSignal, b as consumeSignal, n as notifySignal, c as createMemo, A as ARRAY_SIGNAL, O as OBJECT_SIGNAL, d as willSyncFlushWatchers } from "./configure-B48bFHOl.js";
9
- import { g as getPromiseResult, s as setPromiseResult } from "./context-COmAnXUQ.js";
10
+ import { g as getPromiseResult, s as setPromiseResult } from "./context-kQXhkeBj.js";
10
11
  import { RecordStore } from './types/symbols.js';
11
- import { S as SOURCE$1, C as Context, D as Destroy, a as Checkout } from "./symbols-BoONANuz.js";
12
12
  function coerceId(id) {
13
13
  if (macroCondition(getGlobalConfig().WarpDrive.deprecations.DEPRECATE_NON_STRICT_ID)) {
14
14
  let normalized;
@@ -1001,7 +1001,7 @@ const Signals = getOrSetGlobal('Signals', Symbol('Signals'));
1001
1001
  * @internal
1002
1002
  */
1003
1003
  function withSignalStore(obj) {
1004
- if (obj[Signals] === undefined) {
1004
+ if (!obj[Signals]) {
1005
1005
  initializeSignalStore(obj);
1006
1006
  }
1007
1007
  return obj[Signals];
@@ -1021,7 +1021,7 @@ function initializeSignalStore(obj) {
1021
1021
  if (!test) {
1022
1022
  throw new Error(`Signal store already exists on object`);
1023
1023
  }
1024
- })(obj[Signals] === undefined) : {};
1024
+ })(!obj[Signals]) : {};
1025
1025
  obj[Signals] = new Map();
1026
1026
  }
1027
1027
  function createInternalSignal(signals, obj, key, initialValue) {
@@ -1437,11 +1437,11 @@ defineGate(ReactiveDocument.prototype, 'data', {
1437
1437
  })(doc) : {};
1438
1438
  const data = 'data' in doc ? doc.data : undefined;
1439
1439
  if (Array.isArray(data)) {
1440
- return this._store.recordArrayManager.getCollection({
1441
- type: identifier ? identifier.lid : _localCache.request.url,
1442
- identifiers: data.slice(),
1443
- doc: identifier ? undefined : doc,
1444
- identifier: identifier ?? null
1440
+ return identifier ? this._store.recordArrayManager.getCollection({
1441
+ source: data.slice(),
1442
+ requestKey: identifier
1443
+ }) : this._store.recordArrayManager.getCollection({
1444
+ source: data.slice()
1445
1445
  });
1446
1446
  } else if (data) {
1447
1447
  return this._store.peekRecord(data);
@@ -1637,7 +1637,7 @@ function performArrayExtensionGet(receiver, extensions, signals, prop, _SIGNAL,
1637
1637
  }
1638
1638
  const ARRAY_GETTER_METHODS$1 = new Set([Symbol.iterator, 'concat', 'entries', 'every', 'fill', 'filter', 'find', 'findIndex', 'flat', 'flatMap', 'forEach', 'includes', 'indexOf', 'join', 'keys', 'lastIndexOf', 'map', 'reduce', 'reduceRight', 'slice', 'some', 'values']);
1639
1639
  // const ARRAY_SETTER_METHODS = new Set<KeyType>(['push', 'pop', 'unshift', 'shift', 'splice', 'sort']);
1640
- const SYNC_PROPS$1 = new Set(['[]', 'length']);
1640
+ const SYNC_PROPS = new Set(['[]', 'length']);
1641
1641
  function isArrayGetter$1(prop) {
1642
1642
  return ARRAY_GETTER_METHODS$1.has(prop);
1643
1643
  }
@@ -1684,7 +1684,7 @@ class ManagedArray {
1684
1684
  constructor(context, owner, data) {
1685
1685
  // eslint-disable-next-line @typescript-eslint/no-this-alias
1686
1686
  const self = this;
1687
- this[SOURCE$1] = data?.slice();
1687
+ this[SOURCE] = data?.slice();
1688
1688
  const IS_EDITABLE = context.editable ?? false;
1689
1689
  this[Context] = context;
1690
1690
  const schema = context.store.schema;
@@ -1711,7 +1711,7 @@ class ManagedArray {
1711
1711
  Map;
1712
1712
  const ManagedRecordRefs = field.kind === 'schema-array' ? new RefStorage() : null;
1713
1713
  const extensions = context.legacy ? schema.CAUTION_MEGA_DANGER_ZONE_arrayExtensions(field) : null;
1714
- const proxy = new Proxy(this[SOURCE$1], {
1714
+ const proxy = new Proxy(this[SOURCE], {
1715
1715
  get(target, prop, receiver) {
1716
1716
  if (prop === ARRAY_SIGNAL) {
1717
1717
  return _SIGNAL;
@@ -1723,12 +1723,12 @@ class ManagedArray {
1723
1723
  return self.owner;
1724
1724
  }
1725
1725
  const index = convertToInt$1(prop);
1726
- if (_SIGNAL.isStale && (index !== null || SYNC_PROPS$1.has(prop) || isArrayGetter$1(prop))) {
1726
+ if (_SIGNAL.isStale && (index !== null || SYNC_PROPS.has(prop) || isArrayGetter$1(prop))) {
1727
1727
  _SIGNAL.isStale = false;
1728
1728
  const newData = cache.getAttr(context.resourceKey, context.path);
1729
- if (newData && newData !== self[SOURCE$1]) {
1730
- self[SOURCE$1].length = 0;
1731
- self[SOURCE$1].push(...newData);
1729
+ if (newData && newData !== self[SOURCE]) {
1730
+ self[SOURCE].length = 0;
1731
+ self[SOURCE].push(...newData);
1732
1732
  }
1733
1733
  }
1734
1734
  if (prop === 'length') {
@@ -1834,7 +1834,7 @@ class ManagedArray {
1834
1834
  }
1835
1835
  schemaObjectKeyValue = KeyMode === '@identity' ? rawValue : KeyMode === '@index' ? index : rawValue[KeyMode];
1836
1836
  }
1837
- if (!schemaObjectKeyValue) {
1837
+ if (!schemaObjectKeyValue && schemaObjectKeyValue !== 0) {
1838
1838
  macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
1839
1839
  {
1840
1840
  throw new Error(`Unexpected out of bounds access on SchemaArray`);
@@ -1952,17 +1952,17 @@ class ManagedArray {
1952
1952
  const reflect = Reflect.set(target, prop, value, receiver);
1953
1953
  if (reflect) {
1954
1954
  if (!field.type) {
1955
- cache.setAttr(context.resourceKey, context.path, self[SOURCE$1]);
1955
+ cache.setAttr(context.resourceKey, context.path, self[SOURCE]);
1956
1956
  _SIGNAL.isStale = true;
1957
1957
  return true;
1958
1958
  }
1959
- let rawValue = self[SOURCE$1];
1959
+ let rawValue = self[SOURCE];
1960
1960
  if (field.kind !== 'schema-array') {
1961
1961
  const transform = schema.transformation(field);
1962
1962
  if (!transform) {
1963
1963
  throw new Error(`No '${field.type}' transform defined for use by ${context.resourceKey.type}.${String(prop)}`);
1964
1964
  }
1965
- rawValue = self[SOURCE$1].map(item => transform.serialize(item, field.options ?? null, self.owner));
1965
+ rawValue = self[SOURCE].map(item => transform.serialize(item, field.options ?? null, self.owner));
1966
1966
  }
1967
1967
  cache.setAttr(context.resourceKey, context.path, rawValue);
1968
1968
  _SIGNAL.isStale = true;
@@ -1988,7 +1988,7 @@ class ManagedArray {
1988
1988
  // A(identifierArray) since it is not configurable
1989
1989
  // which is preferable to the `meta` override we used
1990
1990
  // before which required importing all of Ember
1991
- const desc$1 = {
1991
+ const desc = {
1992
1992
  enumerable: true,
1993
1993
  configurable: false,
1994
1994
  get: function () {
@@ -2000,7 +2000,7 @@ const desc$1 = {
2000
2000
  }
2001
2001
  };
2002
2002
  // compat(desc);
2003
- Object.defineProperty(ManagedArray.prototype, '[]', desc$1);
2003
+ Object.defineProperty(ManagedArray.prototype, '[]', desc);
2004
2004
  function getArrayField(context) {
2005
2005
  const signal = entangleSignal(context.signals, context.record, context.path.at(-1), null);
2006
2006
  // the thing we hand out needs to know its owner and path in a private manner
@@ -2210,7 +2210,7 @@ class ManyArrayManager {
2210
2210
  if (rawValue.links) {
2211
2211
  array.links = rawValue.links;
2212
2212
  }
2213
- const currentState = array[SOURCE];
2213
+ const currentState = array[Context].source;
2214
2214
 
2215
2215
  // unlike in the normal RecordArray case, we don't need to divorce the reference
2216
2216
  // because we don't need to worry about associate/disassociate since the graph
@@ -2303,26 +2303,19 @@ function getHasManyField(context) {
2303
2303
  if (!rawValue) {
2304
2304
  return null;
2305
2305
  }
2306
- const managedArray = new RelatedCollection({
2306
+ const managedArray = createLegacyManyArray({
2307
2307
  store,
2308
+ manager: new ManyArrayManager(record, editable),
2309
+ source: rawValue.data?.slice() ?? [],
2308
2310
  type: field.type,
2311
+ isLoaded: true,
2312
+ editable,
2313
+ isAsync: field.options.async ?? false,
2314
+ isPolymorphic: field.options.polymorphic ?? false,
2315
+ field,
2309
2316
  identifier: resourceKey,
2310
- cache,
2311
- field: context.legacy ? field : undefined,
2312
- // we divorce the reference here because ManyArray mutates the target directly
2313
- // before sending the mutation op to the cache. We may be able to avoid this in the future
2314
- identifiers: rawValue.data?.slice(),
2315
- key: field.name,
2316
- meta: rawValue.meta || null,
2317
2317
  links: rawValue.links || null,
2318
- isPolymorphic: field.options.polymorphic ?? false,
2319
- isAsync: field.options.async ?? false,
2320
- // TODO: Grab the proper value
2321
- _inverseIsAsync: false,
2322
- // @ts-expect-error Typescript doesn't have a way for us to thread the generic backwards so it infers unknown instead of T
2323
- manager: new ManyArrayManager(record, editable),
2324
- isLoaded: true,
2325
- allowMutation: editable
2318
+ meta: rawValue.meta || null
2326
2319
  });
2327
2320
  signal.value = managedArray;
2328
2321
  return managedArray;
@@ -2402,7 +2395,7 @@ function setIdentityField(context) {
2402
2395
  })(!didChange || resourceKey.id === null) : {};
2403
2396
  if (normalizedId !== null && didChange) {
2404
2397
  store._instanceCache.setRecordId(resourceKey, normalizedId);
2405
- store.notifications.notify(resourceKey, 'identity');
2398
+ store.notifications.notify(resourceKey, 'identity', null);
2406
2399
  }
2407
2400
  return true;
2408
2401
  }
@@ -2425,7 +2418,7 @@ function setLocalField(context) {
2425
2418
  }
2426
2419
  return true;
2427
2420
  }
2428
- const ObjectSymbols = new Set([OBJECT_SIGNAL, Context, SOURCE$1]);
2421
+ const ObjectSymbols = new Set([OBJECT_SIGNAL, Context, SOURCE]);
2429
2422
 
2430
2423
  // const ignoredGlobalFields = new Set<string>(['setInterval', 'nodeType', 'nodeName', 'length', 'document', STRUCTURED]);
2431
2424
 
@@ -2438,7 +2431,7 @@ class ManagedObject {
2438
2431
  } = context;
2439
2432
  // eslint-disable-next-line @typescript-eslint/no-this-alias
2440
2433
  const self = this;
2441
- this[SOURCE$1] = Object.assign({}, context.value);
2434
+ this[SOURCE] = Object.assign({}, context.value);
2442
2435
  const signals = withSignalStore(this);
2443
2436
  const _SIGNAL = this[OBJECT_SIGNAL] = entangleSignal(signals, this, OBJECT_SIGNAL, undefined);
2444
2437
  this[Context] = context;
@@ -2450,12 +2443,12 @@ class ManagedObject {
2450
2443
 
2451
2444
  // prettier-ignore
2452
2445
  const extensions = !context.legacy ? null : schema.CAUTION_MEGA_DANGER_ZONE_objectExtensions(field, null);
2453
- const proxy = new Proxy(this[SOURCE$1], {
2446
+ const proxy = new Proxy(this[SOURCE], {
2454
2447
  ownKeys() {
2455
- return Object.keys(self[SOURCE$1]);
2448
+ return Object.keys(self[SOURCE]);
2456
2449
  },
2457
2450
  has(target, prop) {
2458
- return prop in self[SOURCE$1];
2451
+ return prop in self[SOURCE];
2459
2452
  },
2460
2453
  getOwnPropertyDescriptor(target, prop) {
2461
2454
  return {
@@ -2490,26 +2483,26 @@ class ManagedObject {
2490
2483
  if (_SIGNAL.isStale) {
2491
2484
  _SIGNAL.isStale = false;
2492
2485
  let newData = cache.getAttr(identifier, path);
2493
- if (newData && newData !== self[SOURCE$1]) {
2486
+ if (newData && newData !== self[SOURCE]) {
2494
2487
  if (field.type) {
2495
2488
  const transform = schema.transformation(field);
2496
2489
  newData = transform.hydrate(newData, field.options ?? null, context.record);
2497
2490
  }
2498
- self[SOURCE$1] = Object.assign({}, newData); // Add type assertion for newData
2491
+ self[SOURCE] = Object.assign({}, newData); // Add type assertion for newData
2499
2492
  }
2500
2493
  }
2501
2494
 
2502
2495
  // toJSON and extensions need to come after we update data if stale
2503
2496
  if (prop === 'toJSON') {
2504
2497
  return function () {
2505
- return structuredClone(self[SOURCE$1]);
2498
+ return structuredClone(self[SOURCE]);
2506
2499
  };
2507
2500
  }
2508
2501
 
2509
2502
  // we always defer to data before extensions
2510
- if (prop in self[SOURCE$1]) {
2503
+ if (prop in self[SOURCE]) {
2511
2504
  consumeInternalSignal(_SIGNAL);
2512
- return self[SOURCE$1][prop];
2505
+ return self[SOURCE][prop];
2513
2506
  }
2514
2507
  if (isExtensionProp(extensions, prop)) {
2515
2508
  return performObjectExtensionGet(receiver, extensions, signals, prop);
@@ -2525,7 +2518,7 @@ class ManagedObject {
2525
2518
 
2526
2519
  // since objects function as dictionaries, we can't defer to schema/data before extensions
2527
2520
  // unless the prop is in the existing data.
2528
- if (!(prop in self[SOURCE$1]) && isExtensionProp(extensions, prop)) {
2521
+ if (!(prop in self[SOURCE]) && isExtensionProp(extensions, prop)) {
2529
2522
  return performExtensionSet(receiver, extensions, signals, prop, value);
2530
2523
  }
2531
2524
  const reflect = Reflect.set(target, prop, value, receiver);
@@ -2533,10 +2526,10 @@ class ManagedObject {
2533
2526
  return false;
2534
2527
  }
2535
2528
  if (!field.type) {
2536
- cache.setAttr(identifier, path, self[SOURCE$1]);
2529
+ cache.setAttr(identifier, path, self[SOURCE]);
2537
2530
  } else {
2538
2531
  const transform = schema.transformation(field);
2539
- const val = transform.serialize(self[SOURCE$1], field.options ?? null, context.record);
2532
+ const val = transform.serialize(self[SOURCE], field.options ?? null, context.record);
2540
2533
  cache.setAttr(identifier, path, val);
2541
2534
  }
2542
2535
  _SIGNAL.isStale = true;
@@ -3101,7 +3094,7 @@ class ReactiveResource {
3101
3094
  if (signal) {
3102
3095
  const peeked = signal.value;
3103
3096
  if (peeked) {
3104
- notifyInternalSignal(peeked[ARRAY_SIGNAL]);
3097
+ notifyInternalSignal(peeked[Context].signal);
3105
3098
  }
3106
3099
  }
3107
3100
  return;
@@ -3177,6 +3170,16 @@ class ReactiveResource {
3177
3170
  enumerable: true,
3178
3171
  configurable: true
3179
3172
  };
3173
+ case '@hash':
3174
+ return schemaForField.name ? {
3175
+ writable: false,
3176
+ enumerable: true,
3177
+ configurable: true
3178
+ } : {
3179
+ writable: false,
3180
+ enumerable: false,
3181
+ configurable: false
3182
+ };
3180
3183
  case '@local':
3181
3184
  case 'field':
3182
3185
  case 'attribute':
@@ -3916,41 +3919,7 @@ class InstanceCache {
3916
3919
 
3917
3920
  // TODO is this join still necessary?
3918
3921
  this.store._join(() => {
3919
- const record = this.__instances.record.get(identifier);
3920
- const cache = this.cache;
3921
- if (record) {
3922
- this.store.teardownRecord(record);
3923
- this.__instances.record.delete(identifier);
3924
- StoreMap.delete(record);
3925
- RecordCache.delete(record);
3926
- if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_INSTANCE_CACHE)) {
3927
- if (getGlobalConfig().WarpDrive.debug.LOG_INSTANCE_CACHE || globalThis.getWarpDriveRuntimeConfig().debug.LOG_INSTANCE_CACHE) {
3928
- // eslint-disable-next-line no-console
3929
- console.log(`InstanceCache: destroyed record for ${String(identifier)}`);
3930
- }
3931
- }
3932
- }
3933
- if (cache) {
3934
- cache.unloadRecord(identifier);
3935
- StoreMap.delete(identifier);
3936
- if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_INSTANCE_CACHE)) {
3937
- if (getGlobalConfig().WarpDrive.debug.LOG_INSTANCE_CACHE || globalThis.getWarpDriveRuntimeConfig().debug.LOG_INSTANCE_CACHE) {
3938
- // eslint-disable-next-line no-console
3939
- console.log(`InstanceCache: destroyed cache for ${String(identifier)}`);
3940
- }
3941
- }
3942
- } else {
3943
- this.disconnect(identifier);
3944
- }
3945
- this.store._requestCache._clearEntries(identifier);
3946
- if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_INSTANCE_CACHE)) {
3947
- if (getGlobalConfig().WarpDrive.debug.LOG_INSTANCE_CACHE || globalThis.getWarpDriveRuntimeConfig().debug.LOG_INSTANCE_CACHE) {
3948
- // eslint-disable-next-line no-console
3949
- console.log(`InstanceCache: unloaded RecordData for ${String(identifier)}`);
3950
- // eslint-disable-next-line no-console
3951
- console.groupEnd();
3952
- }
3953
- }
3922
+ unloadRecord(this, identifier);
3954
3923
  });
3955
3924
  }
3956
3925
  clear(type) {
@@ -4029,7 +3998,7 @@ class InstanceCache {
4029
3998
 
4030
3999
  // TODO update resource cache if needed ?
4031
4000
  // TODO handle consequences of identifier merge for notifications
4032
- this.store.notifications.notify(identifier, 'identity');
4001
+ this.store.notifications.notify(identifier, 'identity', null);
4033
4002
  }
4034
4003
  }
4035
4004
  function getNewRecord(instances, identifier, properties) {
@@ -4073,6 +4042,43 @@ function _clearCaches() {
4073
4042
  RecordCache.clear();
4074
4043
  StoreMap.clear();
4075
4044
  }
4045
+ function unloadRecord(instances, identifier) {
4046
+ const record = instances.__instances.record.get(identifier);
4047
+ const cache = instances.cache;
4048
+ if (record) {
4049
+ instances.store.teardownRecord(record);
4050
+ instances.__instances.record.delete(identifier);
4051
+ StoreMap.delete(record);
4052
+ RecordCache.delete(record);
4053
+ if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_INSTANCE_CACHE)) {
4054
+ if (getGlobalConfig().WarpDrive.debug.LOG_INSTANCE_CACHE || globalThis.getWarpDriveRuntimeConfig().debug.LOG_INSTANCE_CACHE) {
4055
+ // eslint-disable-next-line no-console
4056
+ console.log(`InstanceCache: destroyed record for ${String(identifier)}`);
4057
+ }
4058
+ }
4059
+ }
4060
+ if (cache) {
4061
+ cache.unloadRecord(identifier);
4062
+ StoreMap.delete(identifier);
4063
+ if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_INSTANCE_CACHE)) {
4064
+ if (getGlobalConfig().WarpDrive.debug.LOG_INSTANCE_CACHE || globalThis.getWarpDriveRuntimeConfig().debug.LOG_INSTANCE_CACHE) {
4065
+ // eslint-disable-next-line no-console
4066
+ console.log(`InstanceCache: destroyed cache for ${String(identifier)}`);
4067
+ }
4068
+ }
4069
+ } else {
4070
+ instances.disconnect(identifier);
4071
+ }
4072
+ instances.store._requestCache._clearEntries(identifier);
4073
+ if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_INSTANCE_CACHE)) {
4074
+ if (getGlobalConfig().WarpDrive.debug.LOG_INSTANCE_CACHE || globalThis.getWarpDriveRuntimeConfig().debug.LOG_INSTANCE_CACHE) {
4075
+ // eslint-disable-next-line no-console
4076
+ console.log(`InstanceCache: unloaded RecordData for ${String(identifier)}`);
4077
+ // eslint-disable-next-line no-console
4078
+ console.groupEnd();
4079
+ }
4080
+ }
4081
+ }
4076
4082
 
4077
4083
  /**
4078
4084
  * The CacheManager wraps a Cache enforcing that only
@@ -4716,6 +4722,11 @@ class NotificationManager {
4716
4722
  */
4717
4723
 
4718
4724
  subscribe(identifier, callback) {
4725
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
4726
+ if (!test) {
4727
+ throw new Error(`Expected not to be destroyed`);
4728
+ }
4729
+ })(!this.isDestroyed) : {};
4719
4730
  macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
4720
4731
  if (!test) {
4721
4732
  throw new Error(`Expected to receive a stable Identifier to subscribe to`);
@@ -4761,6 +4772,9 @@ class NotificationManager {
4761
4772
  */
4762
4773
 
4763
4774
  notify(identifier, value, key) {
4775
+ if (this.isDestroyed) {
4776
+ return false;
4777
+ }
4764
4778
  macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
4765
4779
  if (!test) {
4766
4780
  throw new Error(`Notify does not accept a key argument for the namespace '${value}'. Received key '${key || ''}'.`);
@@ -4775,14 +4789,14 @@ class NotificationManager {
4775
4789
  }
4776
4790
  return false;
4777
4791
  }
4778
- const hasSubscribers = Boolean(this._cache.get(identifier)?.length);
4779
- if (isCacheOperationValue(value) || hasSubscribers) {
4792
+ const _hasSubscribers = hasSubscribers(this._cache, identifier, value);
4793
+ if (_hasSubscribers) {
4780
4794
  let buffer = this._buffered.get(identifier);
4781
4795
  if (!buffer) {
4782
4796
  buffer = [];
4783
4797
  this._buffered.set(identifier, buffer);
4784
4798
  }
4785
- buffer.push([value, key]);
4799
+ buffer.push([value, key || null]);
4786
4800
  if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_METRIC_COUNTS)) {
4787
4801
  if (getGlobalConfig().WarpDrive.debug.LOG_METRIC_COUNTS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_METRIC_COUNTS) {
4788
4802
  count(`notify ${'type' in identifier ? identifier.type : '<document>'} ${value} ${key}`);
@@ -4807,7 +4821,7 @@ class NotificationManager {
4807
4821
  }
4808
4822
  }
4809
4823
  }
4810
- return hasSubscribers;
4824
+ return _hasSubscribers;
4811
4825
  }
4812
4826
 
4813
4827
  /** @internal */
@@ -4834,43 +4848,17 @@ class NotificationManager {
4834
4848
  const buffered = this._buffered;
4835
4849
  if (buffered.size) {
4836
4850
  this._buffered = new Map();
4837
- buffered.forEach((states, identifier) => {
4838
- states.forEach(args => {
4851
+ for (const [identifier, states] of buffered) {
4852
+ for (let i = 0; i < states.length; i++) {
4839
4853
  // @ts-expect-error
4840
- this._flushNotification(identifier, args[0], args[1]);
4841
- });
4842
- });
4854
+ _flushNotification(this._cache, identifier, states[i][0], states[i][1]);
4855
+ }
4856
+ }
4843
4857
  }
4844
4858
  this._hasFlush = false;
4845
4859
  this._onFlushCB?.();
4846
4860
  this._onFlushCB = undefined;
4847
4861
  }
4848
- _flushNotification(identifier, value, key) {
4849
- if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_NOTIFICATIONS)) {
4850
- if (getGlobalConfig().WarpDrive.debug.LOG_NOTIFICATIONS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_NOTIFICATIONS) {
4851
- log('notify', '', `${'type' in identifier ? identifier.type : 'document'}`, identifier.lid, `${value}`, key || '');
4852
- }
4853
- }
4854
-
4855
- // TODO for documents this will need to switch based on Identifier kind
4856
- if (isCacheOperationValue(value)) {
4857
- const callbackMap = this._cache.get(isDocumentIdentifier(identifier) ? 'document' : 'resource');
4858
- if (callbackMap) {
4859
- callbackMap.forEach(cb => {
4860
- cb(identifier, value);
4861
- });
4862
- }
4863
- }
4864
- const callbacks = this._cache.get(identifier);
4865
- if (!callbacks || !callbacks.length) {
4866
- return false;
4867
- }
4868
- callbacks.forEach(cb => {
4869
- // @ts-expect-error overload doesn't narrow within body
4870
- cb(identifier, value, key);
4871
- });
4872
- return true;
4873
- }
4874
4862
 
4875
4863
  /** @internal */
4876
4864
  destroy() {
@@ -4878,31 +4866,72 @@ class NotificationManager {
4878
4866
  this._cache.clear();
4879
4867
  }
4880
4868
  }
4869
+ function _flushNotification(cache, identifier, value, key) {
4870
+ if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_NOTIFICATIONS)) {
4871
+ if (getGlobalConfig().WarpDrive.debug.LOG_NOTIFICATIONS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_NOTIFICATIONS) {
4872
+ log('notify', '', `${'type' in identifier ? identifier.type : 'document'}`, identifier.lid, `${value}`, key || '');
4873
+ }
4874
+ }
4881
4875
 
4882
- /* eslint-disable @typescript-eslint/no-explicit-any */
4883
- /*
4884
- We redefine Proxy because the native Proxy type treats the `target` and
4885
- `receiver` as the same type incorrectly.
4886
-
4887
- We ported this from Typescript's own Proxy types on 3/10/2024.
4888
- */
4876
+ // TODO for documents this will need to switch based on Identifier kind
4877
+ if (isCacheOperationValue(value)) {
4878
+ const callbackMap = cache.get(isDocumentIdentifier(identifier) ? 'document' : 'resource');
4879
+ if (callbackMap) {
4880
+ callbackMap.forEach(cb => {
4881
+ cb(identifier, value);
4882
+ });
4883
+ }
4884
+ }
4885
+ const callbacks = cache.get(identifier);
4886
+ if (!callbacks || !callbacks.length) {
4887
+ return false;
4888
+ }
4889
+ callbacks.forEach(cb => {
4890
+ // @ts-expect-error overload doesn't narrow within body
4891
+ cb(identifier, value, key);
4892
+ });
4893
+ return true;
4894
+ }
4895
+ function hasSubscribers(cache, identifier, value) {
4896
+ const hasSubscriber = Boolean(cache.get(identifier)?.length);
4897
+ if (hasSubscriber || !isCacheOperationValue(value)) {
4898
+ return hasSubscriber;
4899
+ }
4900
+ const callbackMap = cache.get(isDocumentIdentifier(identifier) ? 'document' : 'resource');
4901
+ return Boolean(callbackMap?.length);
4902
+ }
4903
+ function update() {
4904
+ if (this.isUpdating) {
4905
+ return this._updatingPromise;
4906
+ }
4907
+ this.isUpdating = true;
4889
4908
 
4890
- const NativeProxy = Proxy;
4909
+ // @ts-expect-error
4910
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call
4911
+ const updatingPromise = this._update();
4912
+ void updatingPromise.finally(() => {
4913
+ this._updatingPromise = null;
4914
+ if (this.isDestroying || this.isDestroyed) {
4915
+ return;
4916
+ }
4917
+ this.isUpdating = false;
4918
+ });
4919
+ this._updatingPromise = updatingPromise;
4920
+ return updatingPromise;
4921
+ }
4922
+ function save() {
4923
+ const context = this[Context];
4924
+ const promise = Promise.all(this.map(record => context.store.saveRecord(record))).then(() => this);
4925
+ return promise;
4926
+ }
4891
4927
  const ARRAY_GETTER_METHODS = new Set([Symbol.iterator, 'concat', 'entries', 'every', 'fill', 'filter', 'find', 'findIndex', 'flat', 'flatMap', 'forEach', 'includes', 'indexOf', 'join', 'keys', 'lastIndexOf', 'map', 'reduce', 'reduceRight', 'slice', 'some', 'values']);
4892
4928
  const ARRAY_SETTER_METHODS = new Set(['push', 'pop', 'unshift', 'shift', 'splice', 'sort']);
4893
- const SYNC_PROPS = new Set(['[]', 'length', 'links', 'meta']);
4894
4929
  function isArrayGetter(prop) {
4895
4930
  return ARRAY_GETTER_METHODS.has(prop);
4896
4931
  }
4897
4932
  function isArraySetter(prop) {
4898
4933
  return ARRAY_SETTER_METHODS.has(prop);
4899
4934
  }
4900
- function isSelfProp(self, prop) {
4901
- return prop in self;
4902
- }
4903
- const SOURCE = getOrSetGlobal('#source', Symbol('#source'));
4904
- const MUTATE = getOrSetGlobal('#update', Symbol('#update'));
4905
- const IS_COLLECTION = getOrSetGlobal('IS_COLLECTION', Symbol.for('Collection'));
4906
4935
  function convertToInt(prop) {
4907
4936
  if (typeof prop === 'symbol') return null;
4908
4937
  const num = Number(prop);
@@ -4933,388 +4962,30 @@ function safeForEach(instance, arr, store, callback, target) {
4933
4962
  return instance;
4934
4963
  }
4935
4964
 
4936
- /**
4937
- A record array is an array that contains records of a certain type (or modelName).
4938
- The record array materializes records as needed when they are retrieved for the first
4939
- time. You should not create record arrays yourself. Instead, an instance of
4940
- `RecordArray` or its subclasses will be returned by your application's store
4941
- in response to queries.
4942
-
4943
- This class should not be imported and instantiated by consuming applications.
4965
+ /* eslint-disable @typescript-eslint/no-explicit-any */
4966
+ /*
4967
+ We redefine Proxy because the native Proxy type treats the `target` and
4968
+ `receiver` as the same type incorrectly.
4944
4969
 
4945
- @class RecordArray
4946
- @public
4970
+ We ported this from Typescript's own Proxy types on 3/10/2024.
4947
4971
  */
4948
4972
 
4973
+ const NativeProxy = Proxy;
4974
+ const IS_COLLECTION = getOrSetGlobal('IS_COLLECTION', Symbol.for('Collection'));
4975
+ function isContextProp(prop) {
4976
+ return prop === 'destroy' || prop === 'isDestroying' || prop === 'isDestroyed';
4977
+ }
4949
4978
  // these are "internally" mutable, they should not be mutated by consumers
4950
4979
  // though this is not currently enforced.
4951
4980
  //
4952
4981
  // all of these should become gated by field-type as they shouldn't be available
4953
4982
  // on request results or non-legacy relationships.
4954
- const MUTABLE_PROPS = ['_updatingPromise', 'isDestroying', 'isDestroyed', 'query', 'isUpdating', 'isLoaded', 'meta', 'links', 'isAsync', 'isPolymorphic', 'identifier', 'cache', '_inverseIsAsync', 'key', 'DEPRECATED_CLASS_NAME'];
4955
- class IdentifierArray {
4956
- /**
4957
- The flag to signal a `RecordArray` is currently loading data.
4958
- Example
4959
- ```javascript
4960
- let people = store.peekAll('person');
4961
- people.isUpdating; // false
4962
- people.update();
4963
- people.isUpdating; // true
4964
- ```
4965
- @property isUpdating
4966
- @public
4967
- @type Boolean
4968
- */
4969
-
4970
- isLoaded = true;
4971
- isDestroying = false;
4972
- isDestroyed = false;
4973
- _updatingPromise = null;
4974
- identifier;
4975
-
4976
- /**
4977
- The store that created this record array.
4978
- @property store
4979
- @private
4980
- @type Store
4981
- */
4982
-
4983
- destroy(clear) {
4984
- this.isDestroying = !clear;
4985
- // changing the reference breaks the Proxy
4986
- // this[SOURCE] = [];
4987
- this[SOURCE].length = 0;
4988
- notifyInternalSignal(this[ARRAY_SIGNAL]);
4989
- this.isDestroyed = !clear;
4990
- }
4991
- constructor(options) {
4992
- // eslint-disable-next-line @typescript-eslint/no-this-alias
4993
- const self = this;
4994
- this.modelName = options.type;
4995
- this.store = options.store;
4996
- this._manager = options.manager;
4997
- this.identifier = options.identifier || null;
4998
- this[SOURCE] = options.identifiers;
4999
- this[IS_COLLECTION] = true;
5000
-
5001
- // we attach the signal storage to the class
5002
- // so that its easier to find debugging.
5003
- const signals = withSignalStore(this);
5004
- const store = options.store;
5005
- const boundFns = new Map();
5006
- const PrivateState = {
5007
- links: options.links || null,
5008
- meta: options.meta || null
5009
- };
5010
- let transaction = false;
5011
-
5012
- // when a mutation occurs
5013
- // we track all mutations within the call
5014
- // and forward them as one
5015
- let _SIGNAL = null;
5016
- const extensions = options.field && this.store.schema.CAUTION_MEGA_DANGER_ZONE_arrayExtensions ? this.store.schema.CAUTION_MEGA_DANGER_ZONE_arrayExtensions(options.field) : null;
5017
- const proxy = new NativeProxy(this[SOURCE], {
5018
- get(target, prop, receiver) {
5019
- const index = convertToInt(prop);
5020
- if (_SIGNAL.isStale && (index !== null || SYNC_PROPS.has(prop) || isArrayGetter(prop))) {
5021
- options.manager._syncArray(receiver);
5022
- _SIGNAL.isStale = false;
5023
- }
5024
- if (index !== null) {
5025
- const identifier = target[index];
5026
- if (!transaction) {
5027
- consumeInternalSignal(_SIGNAL);
5028
- }
5029
- return identifier && store._instanceCache.getRecord(identifier);
5030
- }
5031
- if (prop === ARRAY_SIGNAL) {
5032
- return _SIGNAL;
5033
- }
5034
- if (prop === 'length') {
5035
- return consumeInternalSignal(_SIGNAL), target.length;
5036
- }
5037
- if (prop === 'meta') return consumeInternalSignal(_SIGNAL), PrivateState.meta;
5038
- if (prop === 'links') return consumeInternalSignal(_SIGNAL), PrivateState.links;
5039
- if (prop === '[]') return consumeInternalSignal(_SIGNAL), receiver;
5040
- if (isArrayGetter(prop)) {
5041
- let fn = boundFns.get(prop);
5042
- if (fn === undefined) {
5043
- if (prop === 'forEach') {
5044
- fn = function () {
5045
- consumeInternalSignal(_SIGNAL);
5046
- transaction = true;
5047
- const result = safeForEach(receiver, target, store, arguments[0], arguments[1]);
5048
- transaction = false;
5049
- return result;
5050
- };
5051
- } else {
5052
- fn = function () {
5053
- consumeInternalSignal(_SIGNAL);
5054
- // array functions must run through Reflect to work properly
5055
- // binding via other means will not work.
5056
- transaction = true;
5057
- const result = Reflect.apply(target[prop], receiver, arguments);
5058
- transaction = false;
5059
- return result;
5060
- };
5061
- }
5062
- boundFns.set(prop, fn);
5063
- }
5064
- return fn;
5065
- }
5066
- if (isArraySetter(prop)) {
5067
- let fn = boundFns.get(prop);
5068
- if (fn === undefined) {
5069
- fn = function () {
5070
- // array functions must run through Reflect to work properly
5071
- // binding via other means will not work.
5072
- if (!options.allowMutation) {
5073
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5074
- if (!test) {
5075
- throw new Error(`Mutating this array of records via ${String(prop)} is not allowed.`);
5076
- }
5077
- })(options.allowMutation) : {};
5078
- return;
5079
- }
5080
- const args = Array.prototype.slice.call(arguments);
5081
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5082
- if (!test) {
5083
- throw new Error(`Cannot start a new array transaction while a previous transaction is underway`);
5084
- }
5085
- })(!transaction) : {};
5086
- transaction = true;
5087
- const result = options[MUTATE](target, receiver, prop, args, _SIGNAL);
5088
- transaction = false;
5089
- return result;
5090
- };
5091
- boundFns.set(prop, fn);
5092
- }
5093
- return fn;
5094
- }
5095
- if (isSelfProp(self, prop)) {
5096
- if (prop === SOURCE) {
5097
- return self[prop];
5098
- }
5099
- let fn = boundFns.get(prop);
5100
- if (fn) return fn;
5101
- const outcome = self[prop];
5102
- if (typeof outcome === 'function') {
5103
- fn = function () {
5104
- consumeInternalSignal(_SIGNAL);
5105
- // array functions must run through Reflect to work properly
5106
- // binding via other means will not work.
5107
- return Reflect.apply(outcome, receiver, arguments);
5108
- };
5109
- boundFns.set(prop, fn);
5110
- return fn;
5111
- }
5112
- return consumeInternalSignal(_SIGNAL), outcome;
5113
- }
5114
- if (isExtensionProp(extensions, prop)) {
5115
- return performArrayExtensionGet(receiver, extensions, signals, prop, _SIGNAL, boundFns, v => void (transaction = v));
5116
- }
5117
- return target[prop];
5118
- },
5119
- // FIXME: Should this get a generic like get above?
5120
- set(target, prop, value, receiver) {
5121
- if (!options.allowMutation && !MUTABLE_PROPS.includes(prop)) {
5122
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5123
- if (!test) {
5124
- throw new Error(`Mutating ${String(prop)} on this Array is not allowed.`);
5125
- }
5126
- })(options.allowMutation) : {};
5127
- return false;
5128
- }
5129
- if (prop === 'length') {
5130
- if (!transaction && value === 0) {
5131
- transaction = true;
5132
- options[MUTATE](target, receiver, 'length 0', [], _SIGNAL);
5133
- transaction = false;
5134
- return true;
5135
- } else if (transaction) {
5136
- return Reflect.set(target, prop, value);
5137
- } else {
5138
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5139
- {
5140
- throw new Error(`unexpected length set`);
5141
- }
5142
- })() : {};
5143
- }
5144
- }
5145
- if (prop === 'links') {
5146
- PrivateState.links = value || null;
5147
- return true;
5148
- }
5149
- if (prop === 'meta') {
5150
- PrivateState.meta = value || null;
5151
- return true;
5152
- }
5153
- if (isExtensionProp(extensions, prop)) {
5154
- return performExtensionSet(receiver, extensions, signals, prop, value);
5155
- }
5156
- const index = convertToInt(prop);
5157
-
5158
- // we do not allow "holey" arrays and so if the index is
5159
- // greater than length then we will disallow setting it.
5160
- // however, there is a special case for "unshift" with more than
5161
- // one item being inserted since current items will be moved to the
5162
- // new indices first.
5163
- // we "loosely" detect this by just checking whether we are in
5164
- // a transaction.
5165
- if (index === null || index > target.length) {
5166
- if (index !== null && transaction) {
5167
- const identifier = recordIdentifierFor(value);
5168
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5169
- if (!test) {
5170
- throw new Error(`Cannot set index ${index} past the end of the array.`);
5171
- }
5172
- })(isStableIdentifier(identifier)) : {};
5173
- target[index] = identifier;
5174
- return true;
5175
- } else if (isSelfProp(self, prop)) {
5176
- // @ts-expect-error not all properties are indeces and we can't safely cast
5177
- self[prop] = value;
5178
- return true;
5179
- }
5180
- return false;
5181
- }
5182
- const original = target[index];
5183
- const newIdentifier = extractIdentifierFromRecord$2(value);
5184
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5185
- if (!test) {
5186
- throw new Error(`Expected a record`);
5187
- }
5188
- })(isStableIdentifier(newIdentifier)) : {};
5189
- // We generate "transactions" whenever a setter method on the array
5190
- // is called and might bulk update multiple array cells. Fundamentally,
5191
- // all array operations decompose into individual cell replacements.
5192
- // e.g. a push is really a "replace cell at next index with new value"
5193
- // or a splice is "shift all values left/right by X and set out of new
5194
- // bounds cells to undefined"
5195
- //
5196
- // so, if we are in a transaction, then this is not a user generated change
5197
- // but one generated by a setter method. In this case we want to only apply
5198
- // the change to the target array and not call the MUTATE method.
5199
- // If there is no transaction though, then this means the user themselves has
5200
- // directly changed the value of a specific index and we need to thus generate
5201
- // a mutation for that change.
5202
- // e.g. "arr.push(newVal)" is handled by a "addToRelatedRecords" mutation within
5203
- // a transaction.
5204
- // while "arr[arr.length] = newVal;" is handled by this replace cell code path.
5205
- if (!transaction) {
5206
- options[MUTATE](target, receiver, 'replace cell', [index, original, newIdentifier], _SIGNAL);
5207
- } else {
5208
- target[index] = newIdentifier;
5209
- }
5210
- return true;
5211
- },
5212
- deleteProperty(target, prop) {
5213
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5214
- if (!test) {
5215
- throw new Error(`Deleting keys on managed arrays is disallowed`);
5216
- }
5217
- })(transaction) : {};
5218
- if (!transaction) {
5219
- return false;
5220
- }
5221
- return Reflect.deleteProperty(target, prop);
5222
- },
5223
- getPrototypeOf() {
5224
- return Array.prototype;
5225
- }
5226
- });
5227
- if (macroCondition(getGlobalConfig().WarpDrive.env.DEBUG)) {
5228
- Object.defineProperty(this, '__SHOW_ME_THE_DATA_(debug mode only)__', {
5229
- enumerable: false,
5230
- configurable: true,
5231
- get() {
5232
- return proxy.slice();
5233
- }
5234
- });
5235
- }
5236
-
5237
- // we entangle the signal on the returned proxy since that is
5238
- // the object that other code will be interfacing with.
5239
- _SIGNAL = entangleSignal(signals, proxy, ARRAY_SIGNAL, undefined);
5240
- return proxy;
5241
- }
5242
-
5243
- /**
5244
- Used to get the latest version of all of the records in this array
5245
- from the adapter.
5246
- Example
5247
- ```javascript
5248
- let people = store.peekAll('person');
5249
- people.isUpdating; // false
5250
- people.update().then(function() {
5251
- people.isUpdating; // false
5252
- });
5253
- people.isUpdating; // true
5254
- ```
5255
- @public
5256
- */
5257
- update() {
5258
- if (this.isUpdating) {
5259
- return this._updatingPromise;
5260
- }
5261
- this.isUpdating = true;
5262
- const updatingPromise = this._update();
5263
- void updatingPromise.finally(() => {
5264
- this._updatingPromise = null;
5265
- if (this.isDestroying || this.isDestroyed) {
5266
- return;
5267
- }
5268
- this.isUpdating = false;
5269
- });
5270
- this._updatingPromise = updatingPromise;
5271
- return updatingPromise;
5272
- }
5273
-
5274
- /*
5275
- Update this Array and return a promise which resolves once the update
5276
- is finished.
5277
- */
5278
- _update() {
5279
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5280
- if (!test) {
5281
- throw new Error(`_update cannot be used with this array`);
5282
- }
5283
- })(this.modelName) : {};
5284
- // @ts-expect-error typescript is unable to handle the complexity of
5285
- // T = unknown, modelName = string
5286
- // T extends TypedRecordInstance, modelName = TypeFromInstance<T>
5287
- // both being valid options to pass through here.
5288
- return this.store.findAll(this.modelName, {
5289
- reload: true
5290
- });
5291
- }
5292
-
5293
- // TODO deprecate
5294
- /**
5295
- Saves all of the records in the `RecordArray`.
5296
- Example
5297
- ```javascript
5298
- let messages = store.peekAll('message');
5299
- messages.forEach(function(message) {
5300
- message.hasBeenSeen = true;
5301
- });
5302
- messages.save();
5303
- ```
5304
- @public
5305
- @return {Promise<IdentifierArray>} promise
5306
- */
5307
- save() {
5308
- const promise = Promise.all(this.map(record => this.store.saveRecord(record))).then(() => this);
5309
- return promise;
5310
- }
5311
- }
5312
-
4983
+ const MUTABLE_PROPS = ['_updatingPromise', 'isDestroying', 'isDestroyed', 'query', 'isUpdating', 'isLoaded', 'meta', 'links', 'isAsync', 'isPolymorphic', 'identifier', 'cache', 'key', 'DEPRECATED_CLASS_NAME'];
5313
4984
  // this will error if someone tries to call
5314
4985
  // A(identifierArray) since it is not configurable
5315
4986
  // which is preferable to the `meta` override we used
5316
4987
  // before which required importing all of Ember
5317
- const desc = {
4988
+ const ARR_BRACKET_DESC = {
5318
4989
  enumerable: true,
5319
4990
  configurable: false,
5320
4991
  get: function () {
@@ -5325,58 +4996,312 @@ const desc = {
5325
4996
  }
5326
4997
  }
5327
4998
  };
5328
- // compat(desc);
5329
- Object.defineProperty(IdentifierArray.prototype, '[]', desc);
5330
- defineSignal(IdentifierArray.prototype, 'isUpdating', false);
5331
- class Collection extends IdentifierArray {
5332
- query = null;
5333
- constructor(options) {
5334
- super(options);
5335
- this.query = options.query || null;
5336
- this.isLoaded = options.isLoaded || false;
5337
- }
5338
- _update() {
5339
- const {
5340
- store,
5341
- query
5342
- } = this;
4999
+ const IS_UPDATING_DESC = createSignalDescriptor('isUpdating', false);
5000
+ const ArrayHandler = {
5001
+ getOwnPropertyDescriptor(target, prop) {
5002
+ if (prop === '[]') {
5003
+ // proxies do not allow you to report a descriptor as non-configurable
5004
+ // if there is no descriptor or the underlying descriptor is configurable
5005
+ const underlying = Reflect.getOwnPropertyDescriptor(target, prop);
5006
+ if (!underlying) {
5007
+ Object.defineProperty(target, prop, ARR_BRACKET_DESC);
5008
+ }
5009
+ return ARR_BRACKET_DESC;
5010
+ }
5011
+ if (prop === 'isUpdating') {
5012
+ return IS_UPDATING_DESC;
5013
+ }
5014
+ return Reflect.getOwnPropertyDescriptor(target, prop);
5015
+ },
5016
+ get(target, prop, receiver) {
5017
+ const CONTEXT = target[Context];
5018
+
5019
+ // we place this prop first as it needs initialized
5020
+ // prior to the signal check below
5021
+ if (prop === Signals) return CONTEXT.signals;
5022
+ const index = convertToInt(prop);
5023
+ if (
5024
+ // prettier-ignore
5025
+ CONTEXT.signal.isStale && (index !== null || prop === 'length' || prop === '[]' || CONTEXT.data && prop in CONTEXT.data || isArrayGetter(prop))) {
5026
+ CONTEXT.manager._syncArray(receiver);
5027
+ CONTEXT.signal.isStale = false;
5028
+ }
5029
+ if (index !== null) {
5030
+ const identifier = target[index];
5031
+ if (!CONTEXT.transaction) {
5032
+ consumeInternalSignal(CONTEXT.signal);
5033
+ }
5034
+ return identifier && CONTEXT.store._instanceCache.getRecord(identifier);
5035
+ }
5036
+ if (prop === 'length') return consumeInternalSignal(CONTEXT.signal), target.length;
5037
+ if (prop === '[]') return consumeInternalSignal(CONTEXT.signal), receiver;
5038
+ // TODO move this to ?? so its only on subclasses
5039
+ if (prop === 'isUpdating') return IS_UPDATING_DESC.get.call(receiver);
5040
+ if (prop === IS_COLLECTION) return true;
5041
+ if (prop === Context) return CONTEXT;
5042
+ if (isContextProp(prop)) {
5043
+ return CONTEXT[prop];
5044
+ }
5045
+ if (CONTEXT.data && prop in CONTEXT.data) {
5046
+ // all props in data are reactive props.
5047
+ return consumeInternalSignal(CONTEXT.signal), CONTEXT.data[prop];
5048
+ }
5049
+ if (isArrayGetter(prop)) {
5050
+ let fn = CONTEXT.boundFns.get(prop);
5051
+ if (fn === undefined) {
5052
+ if (prop === 'forEach') {
5053
+ fn = function () {
5054
+ consumeInternalSignal(CONTEXT.signal);
5055
+ CONTEXT.transaction = true;
5056
+ const result = safeForEach(receiver, target, CONTEXT.store, arguments[0], arguments[1]);
5057
+ CONTEXT.transaction = false;
5058
+ return result;
5059
+ };
5060
+ } else {
5061
+ fn = function () {
5062
+ consumeInternalSignal(CONTEXT.signal);
5063
+ // array functions must run through Reflect to work properly
5064
+ // binding via other means will not work.
5065
+ CONTEXT.transaction = true;
5066
+ const result = Reflect.apply(target[prop], receiver, arguments);
5067
+ CONTEXT.transaction = false;
5068
+ return result;
5069
+ };
5070
+ }
5071
+ CONTEXT.boundFns.set(prop, fn);
5072
+ }
5073
+ return fn;
5074
+ }
5075
+ if (isArraySetter(prop)) {
5076
+ let fn = CONTEXT.boundFns.get(prop);
5077
+ if (fn === undefined) {
5078
+ fn = function () {
5079
+ // array functions must run through Reflect to work properly
5080
+ // binding via other means will not work.
5081
+ if (!CONTEXT.editable) {
5082
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5083
+ if (!test) {
5084
+ throw new Error(`Mutating this array of records via ${String(prop)} is not allowed.`);
5085
+ }
5086
+ })(CONTEXT.editable) : {};
5087
+ return;
5088
+ }
5089
+ const args = Array.prototype.slice.call(arguments);
5090
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5091
+ if (!test) {
5092
+ throw new Error(`Cannot start a new array transaction while a previous transaction is underway`);
5093
+ }
5094
+ })(!CONTEXT.transaction) : {};
5095
+ CONTEXT.transaction = true;
5096
+ const result = CONTEXT.mutate(target, receiver, prop, args, CONTEXT.signal);
5097
+ CONTEXT.transaction = false;
5098
+ return result;
5099
+ };
5100
+ CONTEXT.boundFns.set(prop, fn);
5101
+ }
5102
+ return fn;
5103
+ }
5104
+ if (CONTEXT.features && prop in CONTEXT.features) {
5105
+ let fn = CONTEXT.boundFns.get(prop);
5106
+ if (fn) return fn;
5107
+
5108
+ // @ts-expect-error
5109
+ const outcome = CONTEXT.features[prop];
5110
+ if (typeof outcome === 'function') {
5111
+ fn = function () {
5112
+ consumeInternalSignal(CONTEXT.signal);
5113
+ // array functions must run through Reflect to work properly
5114
+ // binding via other means will not work.
5115
+ return Reflect.apply(outcome, receiver, arguments);
5116
+ };
5117
+ CONTEXT.boundFns.set(prop, fn);
5118
+ return fn;
5119
+ }
5120
+ return consumeInternalSignal(CONTEXT.signal), outcome;
5121
+ }
5122
+ if (isExtensionProp(CONTEXT.extensions, prop)) {
5123
+ return performArrayExtensionGet(receiver, CONTEXT.extensions, CONTEXT.signals, prop, CONTEXT.signal, CONTEXT.boundFns, v => void (CONTEXT.transaction = v));
5124
+ }
5125
+ return target[prop];
5126
+ },
5127
+ set(target, prop, value, receiver) {
5128
+ const CONTEXT = target[Context];
5129
+ if (prop === Signals) {
5130
+ CONTEXT.signals = value;
5131
+ return true;
5132
+ }
5133
+ if (!CONTEXT.editable && !MUTABLE_PROPS.includes(prop)) {
5134
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5135
+ if (!test) {
5136
+ throw new Error(`Mutating ${String(prop)} on this Array is not allowed.`);
5137
+ }
5138
+ })(CONTEXT.editable) : {};
5139
+ return false;
5140
+ }
5141
+ if (prop === 'length') {
5142
+ if (!CONTEXT.transaction && value === 0) {
5143
+ CONTEXT.transaction = true;
5144
+ CONTEXT.mutate(target, receiver, 'length 0', [], CONTEXT.signal);
5145
+ CONTEXT.transaction = false;
5146
+ return true;
5147
+ } else if (CONTEXT.transaction) {
5148
+ return Reflect.set(target, prop, value);
5149
+ } else {
5150
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5151
+ {
5152
+ throw new Error(`unexpected length set`);
5153
+ }
5154
+ })() : {};
5155
+ }
5156
+ }
5157
+ if (isContextProp(prop)) {
5158
+ // @ts-expect-error
5159
+ CONTEXT[prop] = value;
5160
+ return true;
5161
+ }
5162
+ if (CONTEXT.data && prop in CONTEXT.data) {
5163
+ CONTEXT.data[prop] = value || null;
5164
+ return true;
5165
+ }
5343
5166
 
5344
- // TODO save options from initial request?
5167
+ // TODO move this to subclass
5168
+ if (prop === 'isUpdating') {
5169
+ IS_UPDATING_DESC.set.call(receiver, value);
5170
+ return true;
5171
+ }
5172
+ if (isExtensionProp(CONTEXT.extensions, prop)) {
5173
+ return performExtensionSet(receiver, CONTEXT.extensions, CONTEXT.signals, prop, value);
5174
+ }
5175
+ const index = convertToInt(prop);
5176
+
5177
+ // we do not allow "holey" arrays and so if the index is
5178
+ // greater than length then we will disallow setting it.
5179
+ // however, there is a special case for "unshift" with more than
5180
+ // one item being inserted since current items will be moved to the
5181
+ // new indices first.
5182
+ // we "loosely" detect this by just checking whether we are in
5183
+ // a transaction.
5184
+ if (index === null || index > target.length) {
5185
+ if (index !== null && CONTEXT.transaction) {
5186
+ const identifier = recordIdentifierFor(value);
5187
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5188
+ if (!test) {
5189
+ throw new Error(`Cannot set index ${index} past the end of the array.`);
5190
+ }
5191
+ })(isStableIdentifier(identifier)) : {};
5192
+ target[index] = identifier;
5193
+ return true;
5194
+ } else if (CONTEXT.features && prop in CONTEXT.features) {
5195
+ CONTEXT.features[prop] = value;
5196
+ return true;
5197
+ }
5198
+ return false;
5199
+ }
5200
+ const original = target[index];
5201
+ const newIdentifier = extractIdentifierFromRecord$2(value);
5345
5202
  macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5346
5203
  if (!test) {
5347
- throw new Error(`update cannot be used with this array`);
5348
- }
5349
- })(this.modelName) : {};
5204
+ throw new Error(`Expected a record`);
5205
+ }
5206
+ })(newIdentifier && isStableIdentifier(newIdentifier)) : {};
5207
+ // We generate "transactions" whenever a setter method on the array
5208
+ // is called and might bulk update multiple array cells. Fundamentally,
5209
+ // all array operations decompose into individual cell replacements.
5210
+ // e.g. a push is really a "replace cell at next index with new value"
5211
+ // or a splice is "shift all values left/right by X and set out of new
5212
+ // bounds cells to undefined"
5213
+ //
5214
+ // so, if we are in a transaction, then this is not a user generated change
5215
+ // but one generated by a setter method. In this case we want to only apply
5216
+ // the change to the target array and not call the MUTATE method.
5217
+ // If there is no transaction though, then this means the user themselves has
5218
+ // directly changed the value of a specific index and we need to thus generate
5219
+ // a mutation for that change.
5220
+ // e.g. "arr.push(newVal)" is handled by a "addToRelatedRecords" mutation within
5221
+ // a transaction.
5222
+ // while "arr[arr.length] = newVal;" is handled by this replace cell code path.
5223
+ if (!CONTEXT.transaction) {
5224
+ CONTEXT.mutate(target, receiver, 'replace cell', [index, original, newIdentifier], CONTEXT.signal);
5225
+ } else {
5226
+ target[index] = newIdentifier;
5227
+ }
5228
+ return true;
5229
+ },
5230
+ deleteProperty(target, prop) {
5231
+ const CONTEXT = target[Context];
5350
5232
  macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5351
5233
  if (!test) {
5352
- throw new Error(`update cannot be used with no query`);
5234
+ throw new Error(`Deleting keys on managed arrays is disallowed`);
5353
5235
  }
5354
- })(query) : {};
5355
- // @ts-expect-error typescript is unable to handle the complexity of
5356
- // T = unknown, modelName = string
5357
- // T extends TypedRecordInstance, modelName = TypeFromInstance<T>
5358
- // both being valid options to pass through here.
5359
- const promise = store.query(this.modelName, query, {
5360
- _recordArray: this
5361
- });
5362
- return promise;
5236
+ })(CONTEXT.transaction) : {};
5237
+ if (!CONTEXT.transaction) {
5238
+ return false;
5239
+ }
5240
+ return Reflect.deleteProperty(target, prop);
5241
+ },
5242
+ getPrototypeOf() {
5243
+ return Array.prototype;
5363
5244
  }
5364
- destroy(clear) {
5365
- super.destroy(clear);
5366
- this._manager._managed.delete(this);
5367
- this._manager._pending.delete(this);
5245
+ };
5246
+ const ILLEGAL_MUTATION = () => {
5247
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5248
+ {
5249
+ throw new Error(`ILLEGAL OPERATION: This ReactiveResourceArray is immutable`);
5250
+ }
5251
+ })() : {};
5252
+ };
5253
+ function createReactiveResourceArray(options) {
5254
+ const TARGET = options.source;
5255
+ const context = {
5256
+ store: options.store,
5257
+ manager: options.manager,
5258
+ editable: options.editable,
5259
+ source: options.source,
5260
+ data: options.data,
5261
+ features: options.features,
5262
+ extensions: options.extensions,
5263
+ options: options.options,
5264
+ destroy: options.destroy || destroy,
5265
+ mutate: options.mutate || ILLEGAL_MUTATION,
5266
+ signals: null,
5267
+ signal: null,
5268
+ isDestroying: false,
5269
+ isDestroyed: false,
5270
+ transaction: false,
5271
+ boundFns: new Map()
5272
+ };
5273
+ TARGET[Context] = context;
5274
+ const proxy = new NativeProxy(TARGET, ArrayHandler);
5275
+ if (macroCondition(getGlobalConfig().WarpDrive.env.DEBUG)) {
5276
+ Object.defineProperty(TARGET, '__SHOW_ME_THE_DATA_(debug mode only)__', {
5277
+ enumerable: false,
5278
+ configurable: true,
5279
+ get() {
5280
+ return proxy.slice();
5281
+ }
5282
+ });
5368
5283
  }
5284
+
5285
+ // we entangle the signal on the returned proxy since that is
5286
+ // the object that other code will be interfacing with.
5287
+ // when a mutation occurs
5288
+ // we track all mutations within the call
5289
+ // and forward them as one
5290
+ withSignalStore(proxy);
5291
+ context.signal = createInternalSignal(context.signals, proxy, ARRAY_SIGNAL, undefined);
5292
+ return proxy;
5369
5293
  }
5370
- // trick the proxy "in" check
5371
- Collection.prototype.query = null;
5372
5294
 
5373
5295
  // Ensure instanceof works correctly
5374
5296
  // Object.setPrototypeOf(IdentifierArray.prototype, Array.prototype);
5375
5297
 
5376
- function assertRecordPassedToHasMany$1(record) {
5298
+ function extractIdentifierFromRecord$2(record) {
5299
+ if (!record) {
5300
+ return null;
5301
+ }
5377
5302
  macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5378
5303
  if (!test) {
5379
- throw new Error(`All elements of a hasMany relationship must be instances of Model, you passed $${typeof record}`);
5304
+ throw new Error(`All elements of a ReactiveResourceArray must be instances of a ReactiveResource, you passed $${typeof record}`);
5380
5305
  }
5381
5306
  })(function () {
5382
5307
  try {
@@ -5386,14 +5311,155 @@ function assertRecordPassedToHasMany$1(record) {
5386
5311
  return false;
5387
5312
  }
5388
5313
  }()) : {};
5389
- }
5390
- function extractIdentifierFromRecord$2(record) {
5391
- if (!record) {
5392
- return null;
5393
- }
5394
- assertRecordPassedToHasMany$1(record);
5395
5314
  return recordIdentifierFor(record);
5396
5315
  }
5316
+ function destroy(clear) {
5317
+ const context = this[Context];
5318
+ this.isDestroying = !clear;
5319
+ // changing the reference breaks the Proxy
5320
+ // this[SOURCE] = [];
5321
+ context.source.length = 0;
5322
+ notifyInternalSignal(context.signal);
5323
+ this.isDestroyed = !clear;
5324
+ }
5325
+ function createRequestCollection(config) {
5326
+ return createReactiveResourceArray({
5327
+ store: config.store,
5328
+ manager: config.manager,
5329
+ editable: false,
5330
+ source: config.source,
5331
+ data: null,
5332
+ features: null,
5333
+ extensions: null,
5334
+ options: config.options,
5335
+ destroy: null,
5336
+ mutate: null
5337
+ });
5338
+ }
5339
+
5340
+ /**
5341
+ * The options for {@link createLegacyLiveArray}
5342
+ *
5343
+ * @internal
5344
+ */
5345
+ /**
5346
+ * Creates a {@link LegacyLiveArray}
5347
+ *
5348
+ * @internal
5349
+ */
5350
+ function createLegacyLiveArray(options) {
5351
+ return createReactiveResourceArray({
5352
+ store: options.store,
5353
+ manager: options.manager,
5354
+ editable: false,
5355
+ source: options.source,
5356
+ data: null,
5357
+ features: {
5358
+ modelName: options.type,
5359
+ update,
5360
+ _update: _updateLiveArray,
5361
+ save,
5362
+ DEPRECATED_CLASS_NAME: 'LiveArray',
5363
+ isUpdating: false,
5364
+ isLoaded: true,
5365
+ _updatingPromise: null
5366
+ },
5367
+ extensions: null,
5368
+ options: null,
5369
+ destroy: null,
5370
+ mutate: null
5371
+ });
5372
+ }
5373
+ function _updateLiveArray() {
5374
+ const context = this[Context];
5375
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5376
+ if (!test) {
5377
+ throw new Error(`_update cannot be used with this array`);
5378
+ }
5379
+ })(this.modelName) : {};
5380
+ // @ts-expect-error typescript is unable to handle the complexity of
5381
+ // T = unknown, modelName = string
5382
+ // T extends TypedRecordInstance, modelName = TypeFromInstance<T>
5383
+ // both being valid options to pass through here.
5384
+ return context.store.findAll(this.modelName, {
5385
+ reload: true
5386
+ });
5387
+ }
5388
+
5389
+ /**
5390
+ * The options for {@link createLegacyQueryArray}
5391
+ *
5392
+ * See also {@link LegacyLiveArrayCreateOptions} which
5393
+ * this extends.
5394
+ *
5395
+ * @internal
5396
+ */
5397
+ /**
5398
+ * Creates a {@link LegacyQueryArray}
5399
+ *
5400
+ * Options: {@link LegacyQueryArrayCreateOptions}
5401
+ *
5402
+ * @internal
5403
+ */
5404
+ function createLegacyQueryArray(options) {
5405
+ return createReactiveResourceArray({
5406
+ store: options.store,
5407
+ manager: options.manager,
5408
+ editable: false,
5409
+ source: options.source,
5410
+ data: {
5411
+ links: options.links,
5412
+ meta: options.meta
5413
+ },
5414
+ features: {
5415
+ query: options.query,
5416
+ modelName: options.type,
5417
+ update,
5418
+ _update: _updateCollection,
5419
+ save,
5420
+ DEPRECATED_CLASS_NAME: 'LegacyQueryArray',
5421
+ isUpdating: false,
5422
+ isLoaded: options.isLoaded,
5423
+ _updatingPromise: null
5424
+ },
5425
+ extensions: null,
5426
+ options: null,
5427
+ destroy: destroyCollection,
5428
+ mutate: null
5429
+ });
5430
+ }
5431
+ function _updateCollection() {
5432
+ const context = this[Context];
5433
+ const {
5434
+ query
5435
+ } = this;
5436
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5437
+ if (!test) {
5438
+ throw new Error(`update cannot be used with this array`);
5439
+ }
5440
+ })(this.modelName) : {};
5441
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5442
+ if (!test) {
5443
+ throw new Error(`update cannot be used with no query`);
5444
+ }
5445
+ })(query) : {};
5446
+ // @ts-expect-error typescript is unable to handle the complexity of
5447
+ // T = unknown, modelName = string
5448
+ // T extends TypedRecordInstance, modelName = TypeFromInstance<T>
5449
+ // both being valid options to pass through here.
5450
+ const promise = context.store.query(this.modelName, query, {
5451
+ _recordArray: this
5452
+ });
5453
+ return promise;
5454
+ }
5455
+ function destroyCollection(clear) {
5456
+ destroy.call(this, clear ?? false);
5457
+ const {
5458
+ manager
5459
+ } = this[Context];
5460
+ manager._managed.delete(this);
5461
+ manager._pending.delete(this);
5462
+ }
5397
5463
  const FAKE_ARR = getOrSetGlobal('FAKE_ARR', {});
5398
5464
  const SLICE_BATCH_SIZE = 4761;
5399
5465
  /**
@@ -5429,7 +5495,6 @@ const SLICE_BATCH_SIZE = 4761;
5429
5495
  * Sincerely,
5430
5496
  * - runspired (Chris Thoburn) 08/21/2022
5431
5497
  *
5432
- * @function fastPush
5433
5498
  * @internal
5434
5499
  * @param target the array to push into
5435
5500
  * @param source the items to push into target
@@ -5447,6 +5512,54 @@ function fastPush(target, source) {
5447
5512
  @internal
5448
5513
  */
5449
5514
  class RecordArrayManager {
5515
+ /**
5516
+ *
5517
+ */
5518
+
5519
+ /**
5520
+ * LiveArray (peekAll/findAll) array instances
5521
+ * keyed by their ResourceType.
5522
+ */
5523
+
5524
+ /**
5525
+ *
5526
+ */
5527
+
5528
+ /**
5529
+ * Buffered changes to apply keyed by the array to
5530
+ * which to apply them to.
5531
+ */
5532
+
5533
+ /**
5534
+ * An inverse map from StableRecordIdentifier to the list
5535
+ * of arrays it can be found in, useful for fast updates
5536
+ * when state changes to a resource occur.
5537
+ */
5538
+
5539
+ /**
5540
+ * When we do not yet have a LiveArray, this keeps track of
5541
+ * the added/removed identifiers to enable us to more efficiently
5542
+ * produce the LiveArray later.
5543
+ *
5544
+ * It's possible that using a Set and only storing additions instead of
5545
+ * additions and deletes would be more efficient.
5546
+ */
5547
+
5548
+ /**
5549
+ * KeyedArrays are arrays associated to a specific RequestKey.
5550
+ */
5551
+
5552
+ /**
5553
+ * The visibility set tracks whether a given identifier should
5554
+ * be shown in RecordArrays. It is used to dedupe added/removed
5555
+ * and state change events.
5556
+ *
5557
+ * As a Map, it grows to be very large - there may be ways to
5558
+ * reduce its size by instead migrating to it functioning as
5559
+ * an exclusion list. Any entry not in the list would be considered
5560
+ * visible.
5561
+ */
5562
+
5450
5563
  constructor(options) {
5451
5564
  this.store = options.store;
5452
5565
  this.isDestroying = false;
@@ -5459,12 +5572,15 @@ class RecordArrayManager {
5459
5572
  this._identifiers = new Map();
5460
5573
  this._set = new Map();
5461
5574
  this._visibilitySet = new Map();
5462
- this._subscription = this.store.notifications.subscribe('document', (identifier, type) => {
5575
+ this._documentSubscription = this.store.notifications.subscribe('document', (identifier, type) => {
5463
5576
  if (type === 'updated' && this._keyedArrays.has(identifier.lid)) {
5464
5577
  const array = this._keyedArrays.get(identifier.lid);
5465
5578
  this.dirtyArray(array, 0, true);
5466
5579
  }
5467
5580
  });
5581
+ this._subscribeToResourceChanges();
5582
+ }
5583
+ _subscribeToResourceChanges() {
5468
5584
  this._subscription = this.store.notifications.subscribe('resource', (identifier, type) => {
5469
5585
  const schema = this.store.schema.resource?.(identifier);
5470
5586
  // If we are a polaris mode schema
@@ -5488,8 +5604,8 @@ class RecordArrayManager {
5488
5604
  }
5489
5605
  _syncArray(array) {
5490
5606
  const pending = this._pending.get(array);
5491
- const isRequestArray = isCollection(array);
5492
- if (!isRequestArray && !pending || this.isDestroying || this.isDestroyed) {
5607
+ const isLegacyQuery = isLegacyQueryArray(array);
5608
+ if (isLegacyQuery && !pending || this.isDestroying || this.isDestroyed) {
5493
5609
  return;
5494
5610
  }
5495
5611
 
@@ -5500,16 +5616,18 @@ class RecordArrayManager {
5500
5616
  }
5501
5617
 
5502
5618
  // then pull new state if required
5503
- if (isRequestArray) {
5504
- const signal = array[ARRAY_SIGNAL];
5619
+ if (!isLegacyQuery && !isLegacyLiveArray(array)) {
5620
+ const context = array[Context];
5621
+ const signal = context.signal;
5622
+ const identifier = context.options.requestKey;
5505
5623
 
5506
5624
  // we only need to rebuild the array from cache if a full sync is required
5507
5625
  // due to notification that the cache has changed
5508
5626
  if (signal.value === 'cache-sync') {
5509
- const doc = this.store.cache.peek(array.identifier);
5627
+ const doc = this.store.cache.peek(identifier);
5510
5628
  macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5511
5629
  if (!test) {
5512
- throw new Error(`Expected to find a document for ${array.identifier.lid} but found none`);
5630
+ throw new Error(`Expected to find a document for ${identifier.lid} but found none`);
5513
5631
  }
5514
5632
  })(doc) : {};
5515
5633
  const data = !('data' in doc) || !Array.isArray(doc.data) ? [] : doc.data;
@@ -5544,12 +5662,11 @@ class RecordArrayManager {
5544
5662
  this._staged.delete(type);
5545
5663
  }
5546
5664
  if (!array) {
5547
- array = new IdentifierArray({
5548
- type,
5549
- identifiers,
5665
+ array = createLegacyLiveArray({
5550
5666
  store: this.store,
5551
- allowMutation: false,
5552
- manager: this
5667
+ manager: this,
5668
+ source: identifiers,
5669
+ type
5553
5670
  });
5554
5671
  this._live.set(type, array);
5555
5672
  this._set.set(array, new Set(identifiers));
@@ -5557,37 +5674,55 @@ class RecordArrayManager {
5557
5674
  return array;
5558
5675
  }
5559
5676
  getCollection(config) {
5560
- if (config.identifier && this._keyedArrays.has(config.identifier.lid)) {
5561
- return this._keyedArrays.get(config.identifier.lid);
5562
- }
5563
- const options = {
5564
- type: config.type,
5565
- identifier: config.identifier || null,
5566
- links: config.doc?.links || null,
5567
- meta: config.doc?.meta || null,
5568
- query: config.query || null,
5569
- identifiers: config.identifiers || [],
5570
- isLoaded: !!config.identifiers?.length,
5571
- allowMutation: false,
5572
- store: this.store,
5573
- manager: this
5574
- };
5575
- const array = new Collection(options);
5576
- this._managed.add(array);
5577
- this._set.set(array, new Set(options.identifiers || []));
5578
- if (config.identifier) {
5579
- this._keyedArrays.set(config.identifier.lid, array);
5677
+ if ('requestKey' in config && this._keyedArrays.has(config.requestKey.lid)) {
5678
+ return this._keyedArrays.get(config.requestKey.lid);
5580
5679
  }
5581
- if (config.identifiers) {
5582
- associate(this._identifiers, array, config.identifiers);
5680
+ let array = null;
5681
+ if ('requestKey' in config) {
5682
+ const options = {
5683
+ store: this.store,
5684
+ manager: this,
5685
+ source: config.source,
5686
+ options: {
5687
+ requestKey: config.requestKey
5688
+ }
5689
+ };
5690
+ array = createRequestCollection(options);
5691
+ this._keyedArrays.set(config.requestKey.lid, array);
5692
+ this._set.set(array, new Set(config.source));
5693
+ associate(this._identifiers, array, config.source);
5694
+ } else if ('query' in config) {
5695
+ const options = {
5696
+ store: this.store,
5697
+ manager: this,
5698
+ source: [],
5699
+ type: config.type,
5700
+ query: config.query,
5701
+ isLoaded: false,
5702
+ links: null,
5703
+ meta: null
5704
+ };
5705
+ array = createLegacyQueryArray(options);
5706
+ this._set.set(array, new Set());
5707
+ } else {
5708
+ const options = {
5709
+ store: this.store,
5710
+ manager: this,
5711
+ source: config.source,
5712
+ options: null
5713
+ };
5714
+ array = createRequestCollection(options);
5715
+ this._set.set(array, new Set(config.source));
5716
+ associate(this._identifiers, array, config.source);
5583
5717
  }
5718
+ this._managed.add(array);
5584
5719
  return array;
5585
5720
  }
5586
5721
  dirtyArray(array, delta, shouldSyncFromCache) {
5587
5722
  if (array === FAKE_ARR) {
5588
5723
  return;
5589
5724
  }
5590
- const signal = array[ARRAY_SIGNAL];
5725
+ const signal = array[Context].signal;
5591
5726
  if (!signal.isStale || delta > 0) {
5592
5727
  notifyInternalSignal(signal);
5593
5728
 
@@ -5619,7 +5754,7 @@ class RecordArrayManager {
5619
5754
 
5620
5755
  // during unloadAll we can ignore removes since we've already
5621
5756
  // cleared the array.
5622
- if (liveArray && liveArray[SOURCE].length === 0 && isRemove) {
5757
+ if (liveArray && liveArray[Context].source.length === 0 && isRemove) {
5623
5758
  const pendingLive = allPending.get(liveArray);
5624
5759
  if (!pendingLive || pendingLive.size === 0) {
5625
5760
  return pending;
@@ -5646,7 +5781,7 @@ class RecordArrayManager {
5646
5781
  }
5647
5782
  populateManagedArray(array, identifiers, payload) {
5648
5783
  this._pending.delete(array);
5649
- const source = array[SOURCE];
5784
+ const source = array[Context].source;
5650
5785
  macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5651
5786
  if (!test) {
5652
5787
  throw new Error(`The new state of the collection should not be using the same array reference as the original state.`);
@@ -5656,12 +5791,12 @@ class RecordArrayManager {
5656
5791
  source.length = 0;
5657
5792
  fastPush(source, identifiers);
5658
5793
  this._set.set(array, new Set(identifiers));
5659
- if (!isCollection(array)) {
5660
- notifyInternalSignal(array[ARRAY_SIGNAL]);
5794
+ if (isLegacyQueryArray(array)) {
5795
+ notifyInternalSignal(array[Context].signal);
5661
5796
  array.meta = payload?.meta || null;
5662
5797
  array.links = payload?.links || null;
5798
+ array.isLoaded = true;
5663
5799
  }
5664
- array.isLoaded = true;
5665
5800
  disassociate(this._identifiers, array, old);
5666
5801
  associate(this._identifiers, array, identifiers);
5667
5802
  }
@@ -5707,21 +5842,34 @@ class RecordArrayManager {
5707
5842
  this.identifierRemoved(identifier);
5708
5843
  }
5709
5844
  }
5845
+ pause() {
5846
+ this.store.notifications.unsubscribe(this._subscription);
5847
+ }
5848
+ resume() {
5849
+ this._subscribeToResourceChanges();
5850
+ }
5710
5851
  clear(isClear = true) {
5711
- this._live.forEach(array => array.destroy(isClear));
5712
- this._managed.forEach(array => array.destroy(isClear));
5852
+ for (const array of this._live.values()) {
5853
+ array.destroy(isClear);
5854
+ }
5855
+ for (const array of this._managed.values()) {
5856
+ array.destroy(isClear);
5857
+ }
5713
5858
  this._managed.clear();
5714
5859
  this._identifiers.clear();
5715
5860
  this._pending.clear();
5716
- this._set.forEach(set => set.clear());
5861
+ for (const set of this._set.values()) {
5862
+ set.clear();
5863
+ }
5717
5864
  this._visibilitySet.clear();
5718
5865
  }
5719
5866
  destroy() {
5720
5867
  this.isDestroying = true;
5721
5868
  this.clear(false);
5722
5869
  this._live.clear();
5723
- this.isDestroyed = true;
5724
5870
  this.store.notifications.unsubscribe(this._subscription);
5871
+ this.store.notifications.unsubscribe(this._documentSubscription);
5872
+ this.isDestroyed = true;
5725
5873
  }
5726
5874
  }
5727
5875
  function associate(ArraysCache, array, identifiers) {
@@ -5747,7 +5895,7 @@ function disassociateIdentifier(ArraysCache, array, identifier) {
5747
5895
  }
5748
5896
  }
5749
5897
  function sync(array, changes, arraySet) {
5750
- const state = array[SOURCE];
5898
+ const state = array[Context].source;
5751
5899
  const adds = [];
5752
5900
  const removes = [];
5753
5901
  changes.forEach((value, key) => {
@@ -5793,8 +5941,13 @@ function sync(array, changes, arraySet) {
5793
5941
  */
5794
5942
  }
5795
5943
  }
5796
- function isCollection(array) {
5797
- return array.identifier !== null;
5944
+ function isLegacyQueryArray(array) {
5945
+ const context = array[Context];
5946
+ return context.features !== null && context.features.DEPRECATED_CLASS_NAME === 'LegacyQueryArray';
5947
+ }
5948
+ function isLegacyLiveArray(array) {
5949
+ const context = array[Context];
5950
+ return context.features !== null && context.features.DEPRECATED_CLASS_NAME === 'LiveArray';
5798
5951
  }
5799
5952
  const Touching = getOrSetGlobal('Touching', Symbol('touching'));
5800
5953
  const RequestPromise = getOrSetGlobal('RequestPromise', Symbol('promise'));
@@ -6846,7 +6999,9 @@ class Store extends BaseClass {
6846
6999
  }
6847
7000
  })(!type || typeof type === 'string') : {};
6848
7001
  this._join(() => {
7002
+ this._enableAsyncFlush = true;
6849
7003
  if (type === undefined) {
7004
+ this.recordArrayManager.pause();
6850
7005
  // destroy the graph before unloadAll
6851
7006
  // since then we avoid churning relationships
6852
7007
  // during unload
@@ -6856,6 +7011,11 @@ class Store extends BaseClass {
6856
7011
  } else {
6857
7012
  this._instanceCache.clear(normalizeModelName(type));
6858
7013
  }
7014
+ this._enableAsyncFlush = null;
7015
+ this.notifications._flush();
7016
+ if (type === undefined) {
7017
+ this.recordArrayManager.resume();
7018
+ }
6859
7019
  });
6860
7020
  }
6861
7021
 
@@ -7058,7 +7218,7 @@ class Store extends BaseClass {
7058
7218
  this.notifications.destroy();
7059
7219
  this.recordArrayManager.destroy();
7060
7220
  this.identifierCache.destroy();
7061
- this.unloadAll();
7221
+ this._instanceCache.clear();
7062
7222
  this.isDestroyed = true;
7063
7223
  }
7064
7224
 
@@ -8027,7 +8187,7 @@ const CacheHandler = {
8027
8187
  if (identifier) {
8028
8188
  promise = promise.finally(() => {
8029
8189
  DEDUPE.delete(identifier);
8030
- store.notifications.notify(identifier, 'state');
8190
+ store.notifications.notify(identifier, 'state', null);
8031
8191
  });
8032
8192
  DEDUPE.set(identifier, {
8033
8193
  priority: {
@@ -8035,7 +8195,7 @@ const CacheHandler = {
8035
8195
  },
8036
8196
  promise
8037
8197
  });
8038
- store.notifications.notify(identifier, 'state');
8198
+ store.notifications.notify(identifier, 'state', null);
8039
8199
  }
8040
8200
  return promise;
8041
8201
  }
@@ -8048,7 +8208,7 @@ const CacheHandler = {
8048
8208
  if (identifier && !activeRequest) {
8049
8209
  promise = promise.finally(() => {
8050
8210
  DEDUPE.delete(identifier);
8051
- store.notifications.notify(identifier, 'state');
8211
+ store.notifications.notify(identifier, 'state', null);
8052
8212
  });
8053
8213
  DEDUPE.set(identifier, {
8054
8214
  priority: {
@@ -8056,7 +8216,7 @@ const CacheHandler = {
8056
8216
  },
8057
8217
  promise
8058
8218
  });
8059
- store.notifications.notify(identifier, 'state');
8219
+ store.notifications.notify(identifier, 'state', null);
8060
8220
  }
8061
8221
  store.requestManager._pending.set(context.id, promise);
8062
8222
  }
@@ -8287,6 +8447,47 @@ function constructResource(type, id, lid) {
8287
8447
  };
8288
8448
  }
8289
8449
  }
8450
+
8451
+ /**
8452
+ * The options for {@link createLegacyManyArray}
8453
+ *
8454
+ * @internal
8455
+ */
8456
+ /**
8457
+ * Creates a {@link LegacyManyArray}
8458
+ *
8459
+ * @internal
8460
+ */
8461
+ function createLegacyManyArray(options) {
8462
+ const extensions = options.store.schema.CAUTION_MEGA_DANGER_ZONE_arrayExtensions ? options.store.schema.CAUTION_MEGA_DANGER_ZONE_arrayExtensions(options.field) : null;
8463
+ return createReactiveResourceArray({
8464
+ store: options.store,
8465
+ manager: options.manager,
8466
+ editable: options.editable,
8467
+ source: options.source,
8468
+ data: {
8469
+ links: options.links,
8470
+ meta: options.meta
8471
+ },
8472
+ features: {
8473
+ modelName: options.type,
8474
+ save,
8475
+ DEPRECATED_CLASS_NAME: 'ManyArray',
8476
+ isLoaded: options.isLoaded,
8477
+ isAsync: options.isAsync,
8478
+ isPolymorphic: options.isPolymorphic,
8479
+ identifier: options.identifier,
8480
+ key: options.field.name,
8481
+ reload,
8482
+ createRecord,
8483
+ notify
8484
+ },
8485
+ extensions,
8486
+ options: null,
8487
+ destroy: destroyLegacyManyArray,
8488
+ mutate: _MUTATE
8489
+ });
8490
+ }
8290
8491
  function _MUTATE(target, receiver, prop, args, _SIGNAL) {
8291
8492
  const collection = receiver;
8292
8493
  switch (prop) {
@@ -8409,7 +8610,7 @@ function _MUTATE(target, receiver, prop, args, _SIGNAL) {
8409
8610
  const [start, deleteCount, ...adds] = args;
8410
8611
 
8411
8612
  // detect a full replace
8412
- if (start === 0 && deleteCount === collection[SOURCE].length) {
8613
+ if (start === 0 && deleteCount === collection[Context].source.length) {
8413
8614
  const newValues = extractIdentifiersFromRecords(adds);
8414
8615
  assertNoDuplicates(collection, target, currentState => currentState.splice(start, deleteCount, ...newValues), `Cannot replace a hasMany's state with a new state that contains duplicates.`);
8415
8616
  if (macroCondition(getGlobalConfig().WarpDrive.deprecations.DEPRECATE_MANY_ARRAY_DUPLICATES)) {
@@ -8483,190 +8684,37 @@ function _MUTATE(target, receiver, prop, args, _SIGNAL) {
8483
8684
  })() : {};
8484
8685
  }
8485
8686
  }
8486
- /**
8487
- A `ManyArray` is a `MutableArray` that represents the contents of a has-many
8488
- relationship.
8489
-
8490
- The `ManyArray` is instantiated lazily the first time the relationship is
8491
- requested.
8492
-
8493
- This class is not intended to be directly instantiated by consuming applications.
8494
-
8495
- ### Inverses
8496
-
8497
- Often, the relationships in Ember Data applications will have
8498
- an inverse. For example, imagine the following models are
8499
- defined:
8500
-
8501
- ```js [app/models/post.js]
8502
- import Model, { hasMany } from '@ember-data/model';
8503
-
8504
- export default class PostModel extends Model {
8505
- @hasMany('comment') comments;
8506
- }
8507
- ```
8508
-
8509
- ```js [app/models/comment.js]
8510
- import { Model, belongsTo } from '@warp-drive/legacy/model';
8511
-
8512
- export default class CommentModel extends Model {
8513
- @belongsTo('post') post;
8514
- }
8515
- ```
8516
-
8517
- If you created a new instance of `Post` and added
8518
- a `Comment` record to its `comments` has-many
8519
- relationship, you would expect the comment's `post`
8520
- property to be set to the post that contained
8521
- the has-many.
8522
-
8523
- We call the record to which a relationship belongs-to the
8524
- relationship's _owner_.
8525
-
8526
- @class ManyArray
8527
- @public
8528
- */
8529
- class RelatedCollection extends IdentifierArray {
8530
- /**
8531
- The loading state of this array
8532
- @property isLoaded
8533
- @type {Boolean}
8534
- @public
8535
- */
8536
-
8537
- /**
8538
- `true` if the relationship is polymorphic, `false` otherwise.
8539
- @property isPolymorphic
8540
- @type {Boolean}
8541
- @private
8542
- */
8543
-
8544
- /**
8545
- Metadata associated with the request for async hasMany relationships.
8546
- Example
8547
- Given that the server returns the following JSON payload when fetching a
8548
- hasMany relationship:
8549
- ```js
8550
- {
8551
- "comments": [{
8552
- "id": 1,
8553
- "comment": "This is the first comment",
8554
- }, {
8555
- // ...
8556
- }],
8557
- "meta": {
8558
- "page": 1,
8559
- "total": 5
8560
- }
8687
+ function notify() {
8688
+ notifyInternalSignal(this[Context].signal);
8689
+ }
8690
+ function reload(options) {
8691
+ const {
8692
+ manager
8693
+ } = this[Context];
8694
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
8695
+ if (!test) {
8696
+ throw new Error(`Expected the manager for ManyArray to implement reloadHasMany`);
8561
8697
  }
8562
- ```
8563
- You can then access the meta data via the `meta` property:
8564
- ```js
8565
- let comments = await post.comments;
8566
- let meta = comments.meta;
8567
- // meta.page => 1
8568
- // meta.total => 5
8569
- ```
8570
- @property meta
8571
- @type {Object | null}
8572
- @public
8573
- */
8574
-
8575
- /**
8576
- * Retrieve the links for this relationship
8577
- *
8578
- @property links
8579
- @type {Object | null}
8580
- @public
8581
- */
8582
-
8583
- constructor(options) {
8584
- options[MUTATE] = _MUTATE;
8585
- super(options);
8586
- this.isLoaded = options.isLoaded || false;
8587
- this.isAsync = options.isAsync || false;
8588
- this.isPolymorphic = options.isPolymorphic || false;
8589
- this.identifier = options.identifier;
8590
- this.key = options.key;
8591
- }
8592
- notify() {
8593
- notifyInternalSignal(this[ARRAY_SIGNAL]);
8594
- }
8595
-
8596
- /**
8597
- Reloads all of the records in the manyArray. If the manyArray
8598
- holds a relationship that was originally fetched using a links url
8599
- WarpDrive will revisit the original links url to repopulate the
8600
- relationship.
8601
- If the ManyArray holds the result of a `store.query()` reload will
8602
- re-run the original query.
8603
- Example
8604
- ```javascript
8605
- let user = store.peekRecord('user', '1')
8606
- await login(user);
8607
- let permissions = await user.permissions;
8608
- await permissions.reload();
8609
- ```
8610
- @public
8611
- */
8612
- reload(options) {
8613
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
8614
- if (!test) {
8615
- throw new Error(`Expected the manager for ManyArray to implement reloadHasMany`);
8616
- }
8617
- })(typeof this._manager.reloadHasMany === 'function') : {};
8618
- // TODO this is odd, we don't ask the store for anything else like this?
8619
- return this._manager.reloadHasMany(this.key, options);
8620
- }
8621
-
8622
- /**
8623
- Create a child record within the owner
8624
- @public
8625
- @param {Object} hash
8626
- @return {Model} record
8627
- */
8628
- createRecord(hash) {
8629
- const {
8630
- store
8631
- } = this;
8632
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
8633
- if (!test) {
8634
- throw new Error(`Expected modelName to be set`);
8635
- }
8636
- })(this.modelName) : {};
8637
- const record = store.createRecord(this.modelName, hash);
8638
- this.push(record);
8639
- return record;
8640
- }
8641
-
8642
- /**
8643
- Saves all of the records in the `ManyArray`.
8644
- Note: this API can only be used in legacy mode with a configured Adapter.
8645
- Example
8646
- ```javascript
8647
- const { content: { data: inbox } } = await store.request(findRecord({ type: 'inbox', id: '1' }));
8648
- let messages = await inbox.messages;
8649
- messages.forEach((message) => {
8650
- message.isRead = true;
8651
- });
8652
- messages.save();
8653
- ```
8654
- @public
8655
- @return {PromiseArray} promise
8656
- */
8657
-
8658
- /** @internal */
8659
- destroy() {
8660
- super.destroy(false);
8661
- }
8698
+ })(typeof manager.reloadHasMany === 'function') : {};
8699
+ // TODO this is odd, we don't ask the store for anything else like this?
8700
+ return manager.reloadHasMany(this.key, options);
8701
+ }
8702
+ function createRecord(hash) {
8703
+ const {
8704
+ store
8705
+ } = this[Context];
8706
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
8707
+ if (!test) {
8708
+ throw new Error(`Expected modelName to be set`);
8709
+ }
8710
+ })(this.modelName) : {};
8711
+ const record = store.createRecord(this.modelName, hash);
8712
+ this.push(record);
8713
+ return record;
8714
+ }
8715
+ function destroyLegacyManyArray() {
8716
+ destroy.call(this, false);
8662
8717
  }
8663
- RelatedCollection.prototype.isAsync = false;
8664
- RelatedCollection.prototype.isPolymorphic = false;
8665
- RelatedCollection.prototype.identifier = null;
8666
- RelatedCollection.prototype.cache = null;
8667
- RelatedCollection.prototype._inverseIsAsync = false;
8668
- RelatedCollection.prototype.key = '';
8669
- RelatedCollection.prototype.DEPRECATED_CLASS_NAME = 'ManyArray';
8670
8718
  function assertRecordPassedToHasMany(record) {
8671
8719
  macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
8672
8720
  if (!test) {
@@ -8689,12 +8737,13 @@ function extractIdentifierFromRecord(recordOrPromiseRecord) {
8689
8737
  return recordIdentifierFor(recordOrPromiseRecord);
8690
8738
  }
8691
8739
  function assertNoDuplicates(collection, target, callback, reason) {
8740
+ const identifier = collection[Context].features.identifier;
8692
8741
  const state = target.slice();
8693
8742
  callback(state);
8694
8743
  if (state.length !== new Set(state).size) {
8695
8744
  const duplicates = state.filter((currentValue, currentIndex) => state.indexOf(currentValue) !== currentIndex);
8696
8745
  if (macroCondition(getGlobalConfig().WarpDrive.deprecations.DEPRECATE_MANY_ARRAY_DUPLICATES)) {
8697
- deprecate(`${reason} This behavior is deprecated. Found duplicates for the following records within the new state provided to \`<${collection.identifier.type}:${collection.identifier.id || collection.identifier.lid}>.${collection.key}\`\n\t- ${Array.from(new Set(duplicates)).map(r => isStableIdentifier(r) ? r.lid : recordIdentifierFor(r).lid).sort((a, b) => a.localeCompare(b)).join('\n\t- ')}`, false, {
8746
+ deprecate(`${reason} This behavior is deprecated. Found duplicates for the following records within the new state provided to \`<${identifier.type}:${identifier.id || identifier.lid}>.${collection.key}\`\n\t- ${Array.from(new Set(duplicates)).map(r => isStableIdentifier(r) ? r.lid : recordIdentifierFor(r).lid).sort((a, b) => a.localeCompare(b)).join('\n\t- ')}`, false, {
8698
8747
  id: 'ember-data:deprecate-many-array-duplicates',
8699
8748
  for: 'ember-data',
8700
8749
  until: '6.0',
@@ -8704,62 +8753,75 @@ function assertNoDuplicates(collection, target, callback, reason) {
8704
8753
  }
8705
8754
  });
8706
8755
  } else {
8707
- throw new Error(`${reason} Found duplicates for the following records within the new state provided to \`<${collection.identifier.type}:${collection.identifier.id || collection.identifier.lid}>.${collection.key}\`\n\t- ${Array.from(new Set(duplicates)).map(r => isStableIdentifier(r) ? r.lid : recordIdentifierFor(r).lid).sort((a, b) => a.localeCompare(b)).join('\n\t- ')}`);
8756
+ throw new Error(`${reason} Found duplicates for the following records within the new state provided to \`<${identifier.type}:${identifier.id || identifier.lid}>.${collection.key}\`\n\t- ${Array.from(new Set(duplicates)).map(r => isStableIdentifier(r) ? r.lid : recordIdentifierFor(r).lid).sort((a, b) => a.localeCompare(b)).join('\n\t- ')}`);
8708
8757
  }
8709
8758
  }
8710
8759
  }
8711
8760
  function mutateAddToRelatedRecords(collection, operationInfo, _SIGNAL) {
8761
+ const identifier = collection[Context].features.identifier;
8762
+
8712
8763
  // FIXME field needs to use sourceKey
8713
8764
  mutate(collection, {
8714
8765
  op: 'add',
8715
- record: collection.identifier,
8766
+ record: identifier,
8716
8767
  field: collection.key,
8717
8768
  ...operationInfo
8718
8769
  }, _SIGNAL);
8719
8770
  }
8720
8771
  function mutateRemoveFromRelatedRecords(collection, operationInfo, _SIGNAL) {
8772
+ const identifier = collection[Context].features.identifier;
8773
+
8721
8774
  // FIXME field needs to use sourceKey
8722
8775
  mutate(collection, {
8723
8776
  op: 'remove',
8724
- record: collection.identifier,
8777
+ record: identifier,
8725
8778
  field: collection.key,
8726
8779
  ...operationInfo
8727
8780
  }, _SIGNAL);
8728
8781
  }
8729
8782
  function mutateReplaceRelatedRecord(collection, operationInfo, _SIGNAL) {
8783
+ const identifier = collection[Context].features.identifier;
8784
+
8730
8785
  // FIXME field needs to use sourceKey
8731
8786
  mutate(collection, {
8732
8787
  op: 'replaceRelatedRecord',
8733
- record: collection.identifier,
8788
+ record: identifier,
8734
8789
  field: collection.key,
8735
8790
  ...operationInfo
8736
8791
  }, _SIGNAL);
8737
8792
  }
8738
8793
  function mutateReplaceRelatedRecords(collection, value, _SIGNAL) {
8794
+ const identifier = collection[Context].features.identifier;
8795
+
8739
8796
  // FIXME field needs to use sourceKey
8740
8797
  mutate(collection, {
8741
8798
  op: 'replaceRelatedRecords',
8742
- record: collection.identifier,
8799
+ record: identifier,
8743
8800
  field: collection.key,
8744
8801
  value
8745
8802
  }, _SIGNAL);
8746
8803
  }
8747
8804
  function mutateSortRelatedRecords(collection, value, _SIGNAL) {
8805
+ const identifier = collection[Context].features.identifier;
8806
+
8748
8807
  // FIXME field needs to use sourceKey
8749
8808
  mutate(collection, {
8750
8809
  op: 'sortRelatedRecords',
8751
- record: collection.identifier,
8810
+ record: identifier,
8752
8811
  field: collection.key,
8753
8812
  value
8754
8813
  }, _SIGNAL);
8755
8814
  }
8756
8815
  function mutate(collection, mutation, _SIGNAL) {
8816
+ const {
8817
+ manager
8818
+ } = collection[Context];
8757
8819
  macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
8758
8820
  if (!test) {
8759
8821
  throw new Error(`Expected the manager for ManyArray to implement mutate`);
8760
8822
  }
8761
- })(typeof collection._manager.mutate === 'function') : {};
8762
- collection._manager.mutate(mutation);
8823
+ })(typeof manager.mutate === 'function') : {};
8824
+ manager.mutate(mutation);
8763
8825
  notifyInternalSignal(_SIGNAL);
8764
8826
  }
8765
8827
  const PromiseCache = new WeakMap();
@@ -9941,4 +10003,4 @@ function getRequestState(future) {
9941
10003
  }
9942
10004
  return state;
9943
10005
  }
9944
- export { peekInternalSignal as A, withSignalStore as B, CacheHandler as C, DISPOSE as D, notifyInternalSignal as E, consumeInternalSignal as F, getOrCreateInternalSignal as G, ReactiveResource as H, IdentifierArray as I, isNonIdentityCacheableField as J, getFieldCacheKeyStrict as K, setIdentifierGenerationMethod as L, MUTATE as M, setIdentifierUpdateMethod as N, setIdentifierForgetMethod as O, setIdentifierResetMethod as P, setKeyInfoForResource as Q, RecordArrayManager as R, Store as S, _clearCaches as _, isDocumentIdentifier as a, coerceId as b, constructResource as c, Collection as d, ensureStringId as e, SOURCE as f, fastPush as g, setRecordIdentifier as h, isStableIdentifier as i, StoreMap as j, RelatedCollection as k, log as l, logGroup as m, normalizeModelName as n, getPromiseState as o, createRequestSubscription as p, getRequestState as q, recordIdentifierFor as r, storeFor as s, memoized as t, gate as u, entangleSignal as v, defineSignal as w, defineGate as x, defineNonEnumerableSignal as y, Signals as z };
10006
+ export { notifyInternalSignal as A, consumeInternalSignal as B, CacheHandler as C, DISPOSE as D, getOrCreateInternalSignal as E, ReactiveResource as F, isNonIdentityCacheableField as G, getFieldCacheKeyStrict as H, setIdentifierGenerationMethod as I, setIdentifierUpdateMethod as J, setIdentifierForgetMethod as K, setIdentifierResetMethod as L, setKeyInfoForResource as M, RecordArrayManager as R, Store as S, _clearCaches as _, isDocumentIdentifier as a, coerceId as b, constructResource as c, setRecordIdentifier as d, ensureStringId as e, fastPush as f, StoreMap as g, createLegacyManyArray as h, isStableIdentifier as i, logGroup as j, getPromiseState as k, log as l, createRequestSubscription as m, normalizeModelName as n, getRequestState as o, memoized as p, gate as q, recordIdentifierFor as r, storeFor as s, entangleSignal as t, defineSignal as u, defineGate as v, defineNonEnumerableSignal as w, Signals as x, peekInternalSignal as y, withSignalStore as z };