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

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.
@@ -50,9 +50,13 @@ class LegacyWrapper implements LegacyRecordDataStoreWrapper {
50
50
  }
51
51
 
52
52
  this._willNotify = true;
53
- let backburner: any = this._store._backburner;
54
-
55
- backburner.schedule('notify', this, this._flushNotifications);
53
+ // it's possible a RecordData adhoc notifies us,
54
+ // in which case we sync flush
55
+ if (this._store._cbs) {
56
+ this._store._schedule('notify', () => this._flushNotifications());
57
+ } else {
58
+ this._flushNotifications();
59
+ }
56
60
  }
57
61
 
58
62
  _flushNotifications(): void {
@@ -83,7 +87,7 @@ class LegacyWrapper implements LegacyRecordDataStoreWrapper {
83
87
  this._store._notificationManager.notify(identifier, namespace, key);
84
88
 
85
89
  if (namespace === 'state') {
86
- this._store.recordArrayManager.recordDidChange(identifier);
90
+ this._store.recordArrayManager.identifierChanged(identifier);
87
91
  }
88
92
  }
89
93
 
@@ -205,7 +209,7 @@ class LegacyWrapper implements LegacyRecordDataStoreWrapper {
205
209
  const identifier = this.identifierCache.getOrCreateRecordIdentifier(resource);
206
210
 
207
211
  this._store._notificationManager.notify(identifier, 'state');
208
- this._store.recordArrayManager.recordDidChange(identifier);
212
+ this._store.recordArrayManager.identifierChanged(identifier);
209
213
  }
210
214
 
211
215
  recordDataFor(type: string, id: string, lid?: string | null): RecordData;
@@ -239,7 +243,7 @@ class LegacyWrapper implements LegacyRecordDataStoreWrapper {
239
243
 
240
244
  if (!id && !lid) {
241
245
  recordData.clientDidCreate(identifier);
242
- this._store.recordArrayManager.recordDidChange(identifier);
246
+ this._store.recordArrayManager.identifierAdded(identifier);
243
247
  }
244
248
 
245
249
  return recordData;
@@ -346,9 +350,7 @@ class V2RecordDataStoreWrapper implements StoreWrapper {
346
350
  }
347
351
 
348
352
  this._willNotify = true;
349
- let backburner: any = this._store._backburner;
350
-
351
- backburner.schedule('notify', this, this._flushNotifications);
353
+ this._store._schedule('notify', () => this._flushNotifications());
352
354
  }
353
355
 
354
356
  _flushNotifications(): void {
@@ -379,7 +381,7 @@ class V2RecordDataStoreWrapper implements StoreWrapper {
379
381
  this._store._notificationManager.notify(identifier, namespace, key);
380
382
 
381
383
  if (namespace === 'state') {
382
- this._store.recordArrayManager.recordDidChange(identifier);
384
+ this._store.recordArrayManager.identifierChanged(identifier);
383
385
  }
384
386
  }
385
387
 
@@ -6,15 +6,12 @@ import type { StableRecordIdentifier } from '@ember-data/types/q/identifier';
6
6
 
7
7
  import { isStableIdentifier } from '../caches/identifier-cache';
8
8
  import type Store from '../store-service';
9
- import WeakCache from '../utils/weak-cache';
10
9
 
11
10
  type UnsubscribeToken = object;
12
11
  let tokenId = 0;
13
- const Cache = new WeakCache<StableRecordIdentifier, Map<UnsubscribeToken, NotificationCallback>>(
14
- DEBUG ? 'subscribers' : ''
15
- );
16
- Cache._generator = () => new Map();
17
- const Tokens = new WeakCache<UnsubscribeToken, StableRecordIdentifier>(DEBUG ? 'identifier' : '');
12
+
13
+ const Cache = new Map<StableRecordIdentifier, Map<UnsubscribeToken, NotificationCallback>>();
14
+ const Tokens = new Map<UnsubscribeToken, StableRecordIdentifier>();
18
15
 
19
16
  export type NotificationType = 'attributes' | 'relationships' | 'identity' | 'errors' | 'meta' | 'state';
20
17
 
@@ -24,25 +21,38 @@ export interface NotificationCallback {
24
21
  (identifier: StableRecordIdentifier, notificationType: NotificationType, key?: string): void;
25
22
  }
26
23
 
24
+ // TODO this isn't importable anyway, remove and use a map on the manager?
27
25
  export function unsubscribe(token: UnsubscribeToken) {
28
26
  let identifier = Tokens.get(token);
29
- assert('Passed unknown unsubscribe token to unsubscribe', identifier);
30
- Tokens.delete(token);
31
- const map = Cache.get(identifier);
32
- map?.delete(token);
27
+ if (LOG_NOTIFICATIONS && !identifier) {
28
+ // eslint-disable-next-line no-console
29
+ console.log('Passed unknown unsubscribe token to unsubscribe', identifier);
30
+ }
31
+ if (identifier) {
32
+ Tokens.delete(token);
33
+ const map = Cache.get(identifier);
34
+ map?.delete(token);
35
+ }
33
36
  }
34
37
  /*
35
38
  Currently only support a single callback per identifier
36
39
  */
37
40
  export default class NotificationManager {
38
41
  declare store: Store;
42
+ declare isDestroyed: boolean;
39
43
  constructor(store: Store) {
40
44
  this.store = store;
45
+ this.isDestroyed = false;
41
46
  }
42
47
 
43
48
  subscribe(identifier: StableRecordIdentifier, callback: NotificationCallback): UnsubscribeToken {
44
49
  assert(`Expected to receive a stable Identifier to subscribe to`, isStableIdentifier(identifier));
45
- let map = Cache.lookup(identifier);
50
+ let map = Cache.get(identifier);
51
+
52
+ if (!map) {
53
+ map = new Map();
54
+ Cache.set(identifier, map);
55
+ }
46
56
  let unsubToken = DEBUG ? { _tokenRef: tokenId++ } : {};
47
57
  map.set(unsubToken, callback);
48
58
  Tokens.set(unsubToken, identifier);
@@ -50,7 +60,9 @@ export default class NotificationManager {
50
60
  }
51
61
 
52
62
  unsubscribe(token: UnsubscribeToken) {
53
- unsubscribe(token);
63
+ if (!this.isDestroyed) {
64
+ unsubscribe(token);
65
+ }
54
66
  }
55
67
 
56
68
  // deactivated type signature overloads because pass-through was failing to match any. Bring back if possible.
@@ -87,4 +99,10 @@ export default class NotificationManager {
87
99
  });
88
100
  return true;
89
101
  }
102
+
103
+ destroy() {
104
+ this.isDestroyed = true;
105
+ Tokens.clear();
106
+ Cache.clear();
107
+ }
90
108
  }
@@ -86,7 +86,7 @@ export default class FetchManager {
86
86
  }
87
87
 
88
88
  clearEntries(identifier: StableRecordIdentifier) {
89
- delete this.requestCache._done[identifier.lid];
89
+ this.requestCache._done.delete(identifier);
90
90
  }
91
91
 
92
92
  /**
@@ -213,7 +213,7 @@ export default class FetchManager {
213
213
  },
214
214
  (error) => {
215
215
  const recordData = store._instanceCache.peek({ identifier, bucket: 'recordData' });
216
- if (!recordData || recordData.isEmpty?.(identifier) || isLoading) {
216
+ if (!recordData || recordData.isEmpty(identifier) || isLoading) {
217
217
  store._instanceCache.unloadRecord(identifier);
218
218
  }
219
219
  throw error;
@@ -4,6 +4,7 @@ import { Promise } from 'rsvp';
4
4
 
5
5
  import { guardDestroyedStore } from '../utils/common';
6
6
  import { normalizeResponseHelper } from '../utils/serializer-response';
7
+ import SnapshotRecordArray from './snapshot-record-array';
7
8
 
8
9
  /**
9
10
  @module @ember-data/store
@@ -17,10 +18,10 @@ function payloadIsNotBlank(adapterPayload) {
17
18
  }
18
19
  }
19
20
 
20
- export function _findAll(adapter, store, modelName, options) {
21
+ export function _findAll(adapter, store, modelName, options, snapshotArray) {
21
22
  let modelClass = store.modelFor(modelName); // adapter.findAll depends on the class
22
23
  let recordArray = store.peekAll(modelName);
23
- let snapshotArray = recordArray._createSnapshot(options);
24
+ snapshotArray = snapshotArray || new SnapshotRecordArray(store, recordArray, options);
24
25
  let promise = Promise.resolve().then(() => adapter.findAll(store, modelClass, null, snapshotArray));
25
26
  let label = 'DS: Handle Adapter#findAll of ' + modelClass;
26
27
 
@@ -36,8 +37,7 @@ export function _findAll(adapter, store, modelName, options) {
36
37
  let payload = normalizeResponseHelper(serializer, store, modelClass, adapterPayload, null, 'findAll');
37
38
 
38
39
  store._push(payload);
39
- store.recordArrayManager._didUpdateAll(modelName);
40
-
40
+ recordArray.isUpdating = false;
41
41
  return recordArray;
42
42
  },
43
43
  null,
@@ -49,7 +49,12 @@ export function _query(adapter, store, modelName, query, recordArray, options) {
49
49
  let modelClass = store.modelFor(modelName); // adapter.query needs the class
50
50
 
51
51
  // TODO @deprecate RecordArrays being passed to Adapters
52
- recordArray = recordArray || store.recordArrayManager.createAdapterPopulatedRecordArray(modelName, query);
52
+ recordArray =
53
+ recordArray ||
54
+ store.recordArrayManager.createArray({
55
+ type: modelName,
56
+ query,
57
+ });
53
58
  let promise = Promise.resolve().then(() => adapter.query(store, modelClass, query, recordArray, options));
54
59
 
55
60
  let label = `DS: Handle Adapter#query of ${modelName}`;
@@ -65,7 +70,7 @@ export function _query(adapter, store, modelName, query, recordArray, options) {
65
70
  'The response to store.query is expected to be an array but it was a single record. Please wrap your response in an array or use `store.queryRecord` to query for a single record.',
66
71
  Array.isArray(identifiers)
67
72
  );
68
- recordArray._setIdentifiers(identifiers, payload);
73
+ store.recordArrayManager.populateManagedArray(recordArray, identifiers, payload);
69
74
 
70
75
  return recordArray;
71
76
  },
@@ -5,7 +5,7 @@ import type {
5
5
  RequestState,
6
6
  SaveRecordMutation,
7
7
  } from '@ember-data/types/q/fetch-manager';
8
- import type { RecordIdentifier } from '@ember-data/types/q/identifier';
8
+ import type { RecordIdentifier, StableRecordIdentifier } from '@ember-data/types/q/identifier';
9
9
 
10
10
  const Touching: unique symbol = Symbol('touching');
11
11
  export const RequestPromise: unique symbol = Symbol('promise');
@@ -23,7 +23,7 @@ function hasRecordIdentifier(op: Operation): op is RecordOperation {
23
23
 
24
24
  export default class RequestCache {
25
25
  _pending: { [lid: string]: InternalRequest[] } = Object.create(null);
26
- _done: { [lid: string]: InternalRequest[] } = Object.create(null);
26
+ _done: Map<StableRecordIdentifier, InternalRequest[]> = new Map();
27
27
  _subscriptions: { [lid: string]: Function[] } = Object.create(null);
28
28
 
29
29
  enqueue(promise: Promise<any>, queryRequest: Request) {
@@ -86,22 +86,25 @@ export default class RequestCache {
86
86
 
87
87
  _addDone(request: InternalRequest) {
88
88
  request[Touching].forEach((identifier) => {
89
- if (!this._done[identifier.lid]) {
90
- this._done[identifier.lid] = [];
91
- }
92
89
  // TODO add support for multiple
93
90
  let requestDataOp = request.request.data[0].op;
94
- this._done[identifier.lid] = this._done[identifier.lid].filter((req) => {
95
- // TODO add support for multiple
96
- let data;
97
- if (req.request.data instanceof Array) {
98
- data = req.request.data[0];
99
- } else {
100
- data = req.request.data;
101
- }
102
- return data.op !== requestDataOp;
103
- });
104
- this._done[identifier.lid].push(request);
91
+ let requests = this._done.get(identifier);
92
+
93
+ if (requests) {
94
+ requests = requests.filter((req) => {
95
+ // TODO add support for multiple
96
+ let data;
97
+ if (req.request.data instanceof Array) {
98
+ data = req.request.data[0];
99
+ } else {
100
+ data = req.request.data;
101
+ }
102
+ return data.op !== requestDataOp;
103
+ });
104
+ }
105
+ requests = requests || [];
106
+ requests.push(request);
107
+ this._done.set(identifier, requests);
105
108
  });
106
109
  }
107
110
 
@@ -120,7 +123,7 @@ export default class RequestCache {
120
123
  }
121
124
 
122
125
  getLastRequestForRecord(identifier: RecordIdentifier): RequestState | null {
123
- let requests = this._done[identifier.lid];
126
+ let requests = this._done.get(identifier);
124
127
  if (requests) {
125
128
  return requests[requests.length - 1];
126
129
  }
@@ -6,10 +6,12 @@ import { deprecate } from '@ember/debug';
6
6
 
7
7
  import { DEPRECATE_SNAPSHOT_MODEL_CLASS_ACCESS } from '@ember-data/private-build-infra/deprecations';
8
8
  import type { ModelSchema } from '@ember-data/types/q/ds-model';
9
+ import { StableRecordIdentifier } from '@ember-data/types/q/identifier';
9
10
  import type { FindOptions } from '@ember-data/types/q/store';
10
11
  import type { Dict } from '@ember-data/types/q/utils';
11
12
 
12
13
  import type RecordArray from '../record-arrays/record-array';
14
+ import Store from '../store-service';
13
15
  import type Snapshot from './snapshot';
14
16
  /**
15
17
  SnapshotRecordArray is not directly instantiable.
@@ -23,6 +25,7 @@ export default class SnapshotRecordArray {
23
25
  declare _snapshots: Snapshot[] | null;
24
26
  declare _recordArray: RecordArray;
25
27
  declare _type: ModelSchema | null;
28
+ declare __store: Store;
26
29
 
27
30
  declare length: number;
28
31
  declare meta: Dict<unknown> | null;
@@ -41,7 +44,8 @@ export default class SnapshotRecordArray {
41
44
  @param {Object} meta
42
45
  @param options
43
46
  */
44
- constructor(recordArray: RecordArray, meta: Dict<unknown> | null, options: FindOptions = {}) {
47
+ constructor(store: Store, recordArray: RecordArray, options: FindOptions = {}) {
48
+ this.__store = store;
45
49
  /**
46
50
  An array of snapshots
47
51
  @private
@@ -79,29 +83,6 @@ export default class SnapshotRecordArray {
79
83
  */
80
84
  this.length = recordArray.length as unknown as number; // deal with computedProperty shennanigans
81
85
 
82
- /**
83
- Meta objects for the record array.
84
-
85
- Example
86
-
87
- ```app/adapters/post.js
88
- import JSONAPIAdapter from '@ember-data/adapter/json-api';
89
-
90
- export default class PostAdapter extends JSONAPIAdapter {
91
- shouldReloadAll(store, snapshotRecordArray) {
92
- let lastRequestTime = snapshotRecordArray.meta.lastRequestTime;
93
- let twentyMinutes = 20 * 60 * 1000;
94
- return Date.now() > lastRequestTime + twentyMinutes;
95
- }
96
- });
97
- ```
98
-
99
- @property meta
100
- @public
101
- @type {Object}
102
- */
103
- this.meta = meta;
104
-
105
86
  /**
106
87
  A hash of adapter options passed into the store method for this request.
107
88
 
@@ -200,8 +181,10 @@ export default class SnapshotRecordArray {
200
181
  if (this._snapshots !== null) {
201
182
  return this._snapshots;
202
183
  }
203
-
204
- this._snapshots = this._recordArray._takeSnapshot();
184
+ const { _instanceCache } = this.__store;
185
+ this._snapshots = this._recordArray.content.map((identifier: StableRecordIdentifier) =>
186
+ _instanceCache.createSnapshot(identifier)
187
+ );
205
188
 
206
189
  return this._snapshots;
207
190
  }
@@ -1,18 +1,16 @@
1
1
  import type NativeArray from '@ember/array/-private/native-array';
2
2
  import { assert } from '@ember/debug';
3
3
 
4
- import type { CollectionResourceDocument, Links, Meta, PaginationLinks } from '@ember-data/types/q/ember-data-json-api';
4
+ import type { Links, PaginationLinks } from '@ember-data/types/q/ember-data-json-api';
5
5
  import type { StableRecordIdentifier } from '@ember-data/types/q/identifier';
6
6
  import type { RecordInstance } from '@ember-data/types/q/record-instance';
7
- import type { FindOptions } from '@ember-data/types/q/store';
8
7
  import type { Dict } from '@ember-data/types/q/utils';
9
8
 
10
9
  import type RecordArrayManager from '../managers/record-array-manager';
11
- import SnapshotRecordArray from '../network/snapshot-record-array';
12
10
  import type { PromiseArray } from '../proxies/promise-proxies';
13
11
  import { promiseArray } from '../proxies/promise-proxies';
14
12
  import type Store from '../store-service';
15
- import RecordArray from './record-array';
13
+ import RecordArray, { MANAGED } from './record-array';
16
14
 
17
15
  export interface AdapterPopulatedRecordArrayCreateArgs {
18
16
  modelName: string;
@@ -20,9 +18,9 @@ export interface AdapterPopulatedRecordArrayCreateArgs {
20
18
  manager: RecordArrayManager;
21
19
  content: NativeArray<StableRecordIdentifier>;
22
20
  isLoaded: boolean;
23
- query?: Dict<unknown>;
24
- meta?: Meta;
25
- links?: Links | PaginationLinks | null;
21
+ query: Dict<unknown> | null;
22
+ meta: Dict<unknown> | null;
23
+ links: Links | PaginationLinks | null;
26
24
  }
27
25
 
28
26
  /**
@@ -71,6 +69,7 @@ export default class AdapterPopulatedRecordArray extends RecordArray {
71
69
  declare links: Links | PaginationLinks | null;
72
70
  declare meta: Dict<unknown> | null;
73
71
  declare query: Dict<unknown> | null;
72
+ [MANAGED] = true;
74
73
 
75
74
  init(props?: AdapterPopulatedRecordArrayCreateArgs) {
76
75
  assert(`Cannot initialize AdapterPopulatedRecordArray with isUpdating`, !props || !('isUpdating' in props));
@@ -86,43 +85,15 @@ export default class AdapterPopulatedRecordArray extends RecordArray {
86
85
 
87
86
  _update(): PromiseArray<RecordInstance, AdapterPopulatedRecordArray> {
88
87
  const { store, query } = this;
88
+ const promise = store.query(this.modelName, query, { _recordArray: this });
89
89
 
90
90
  // TODO save options from initial request?
91
- return promiseArray(store.query(this.modelName, query, { _recordArray: this }));
91
+ return promiseArray(promise);
92
92
  }
93
93
 
94
- _setObjects(identifiers: StableRecordIdentifier[], payload: CollectionResourceDocument) {
95
- // TODO: initial load should not cause change events at all, only
96
- // subsequent. This requires changing the public api of adapter.query, but
97
- // hopefully we can do that soon.
98
- this.content.setObjects(identifiers);
99
-
100
- this.setProperties({
101
- isLoaded: true,
102
- isUpdating: false,
103
- // TODO this assign kills the root reference but a deep-copy would be required
104
- // for both meta and links to actually not be by-ref. We whould likely change
105
- // this to a dev-only deep-freeze.
106
- meta: Object.assign({}, payload.meta),
107
- links: Object.assign({}, payload.links),
108
- });
109
-
110
- this.manager._associateWithRecordArray(identifiers, this);
111
- }
112
-
113
- _createSnapshot(options: FindOptions) {
114
- // this is private for users, but public for ember-data internals
115
- // meta will only be present for an AdapterPopulatedRecordArray
116
- return new SnapshotRecordArray(this, this.meta, options);
117
- }
118
-
119
- /**
120
- @method _setIdentifiers
121
- @param {StableRecordIdentifier[]} identifiers
122
- @param {Object} payload normalized payload
123
- @private
124
- */
125
- _setIdentifiers(identifiers: StableRecordIdentifier[], payload: CollectionResourceDocument): void {
126
- this._setObjects(identifiers, payload);
94
+ willDestroy() {
95
+ super.willDestroy();
96
+ this.manager._managed.delete(this);
97
+ this.manager._pending.delete(this);
127
98
  }
128
99
  }
@@ -9,17 +9,15 @@ import { tracked } from '@glimmer/tracking';
9
9
 
10
10
  import { Promise } from 'rsvp';
11
11
 
12
- import type { RecordArrayManager, Snapshot } from 'ember-data/-private';
13
-
14
12
  import { DEPRECATE_SNAPSHOT_MODEL_CLASS_ACCESS } from '@ember-data/private-build-infra/deprecations';
15
13
  import type { StableRecordIdentifier } from '@ember-data/types/q/identifier';
16
14
  import type { RecordInstance } from '@ember-data/types/q/record-instance';
17
- import type { FindOptions } from '@ember-data/types/q/store';
18
15
 
19
- import SnapshotRecordArray from '../network/snapshot-record-array';
16
+ import RecordArrayManager, { disassociateIdentifier } from '../managers/record-array-manager';
20
17
  import type { PromiseArray } from '../proxies/promise-proxies';
21
18
  import { promiseArray } from '../proxies/promise-proxies';
22
19
  import type Store from '../store-service';
20
+ import type AdapterPopulatedRecordArray from './adapter-populated-record-array';
23
21
 
24
22
  function recordForIdentifier(store: Store, identifier: StableRecordIdentifier): RecordInstance {
25
23
  return store._instanceCache.getRecord(identifier);
@@ -32,6 +30,7 @@ export interface RecordArrayCreateArgs {
32
30
  content: NativeArray<StableRecordIdentifier>;
33
31
  isLoaded: boolean;
34
32
  }
33
+ export const MANAGED = Symbol('#managed');
35
34
 
36
35
  /**
37
36
  A record array is an array that contains records of a certain modelName. The record
@@ -58,7 +57,6 @@ export default class RecordArray extends ArrayProxy<StableRecordIdentifier, Reco
58
57
  @type Ember.Array
59
58
  */
60
59
  declare content: NativeArray<StableRecordIdentifier>;
61
- declare _getDeprecatedEventedInfo: () => string;
62
60
  declare modelName: string;
63
61
  /**
64
62
  The flag to signal a `RecordArray` is finished loading data.
@@ -100,6 +98,7 @@ export default class RecordArray extends ArrayProxy<StableRecordIdentifier, Reco
100
98
  @type Boolean
101
99
  */
102
100
  @tracked isUpdating: boolean = false;
101
+ [MANAGED]: boolean = false;
103
102
 
104
103
  init(props?: RecordArrayCreateArgs) {
105
104
  assert(`Cannot initialize RecordArray with isUpdating`, !props || !('isUpdating' in props));
@@ -111,6 +110,12 @@ export default class RecordArray extends ArrayProxy<StableRecordIdentifier, Reco
111
110
  this._updatingPromise = null;
112
111
  }
113
112
 
113
+ _notify() {
114
+ // until we kill ArrayProxy we immediately sync
115
+ // because otherwise we double notify
116
+ this.manager._syncArray(this);
117
+ }
118
+
114
119
  replace() {
115
120
  throw new Error(
116
121
  `The result of a server query (for all ${this.modelName} types) is immutable. To modify contents, use toArray()`
@@ -216,17 +221,7 @@ export default class RecordArray extends ArrayProxy<StableRecordIdentifier, Reco
216
221
  return promiseArray<RecordInstance, RecordArray>(promise);
217
222
  }
218
223
 
219
- /**
220
- @method _unregisterFromManager
221
- @internal
222
- */
223
- _unregisterFromManager() {
224
- this.manager.unregisterRecordArray(this);
225
- }
226
-
227
224
  willDestroy() {
228
- this._unregisterFromManager();
229
- this._dissociateFromOwnRecords();
230
225
  // TODO: we should not do work during destroy:
231
226
  // * when objects are destroyed, they should simply be left to do
232
227
  // * if logic errors do to this, that logic needs to be more careful during
@@ -241,66 +236,32 @@ export default class RecordArray extends ArrayProxy<StableRecordIdentifier, Reco
241
236
  super.willDestroy();
242
237
  }
243
238
 
244
- /**
245
- @method _createSnapshot
246
- @private
247
- */
248
- _createSnapshot(options: FindOptions) {
249
- // this is private for users, but public for ember-data internals
250
- // meta will only be present for an AdapterPopulatedRecordArray
251
- return new SnapshotRecordArray(this, null, options);
252
- }
253
-
254
- /**
255
- @method _dissociateFromOwnRecords
256
- @internal
257
- */
258
- _dissociateFromOwnRecords() {
259
- this.content.forEach((identifier) => {
260
- let recordArrays = this.manager.getRecordArraysForIdentifier(identifier);
261
-
262
- if (recordArrays) {
263
- recordArrays.delete(this);
239
+ _updateState(changes: Map<StableRecordIdentifier, 'add' | 'del'>) {
240
+ const content = this.content;
241
+ const adds: StableRecordIdentifier[] = [];
242
+ const removes: StableRecordIdentifier[] = [];
243
+ const isManaged = this[MANAGED];
244
+ changes.forEach((value, key) => {
245
+ value === 'add' ? adds.push(key) : removes.push(key);
246
+ if (isManaged && value === 'del') {
247
+ disassociateIdentifier(this as unknown as AdapterPopulatedRecordArray, key);
264
248
  }
265
249
  });
266
- }
267
-
268
- /**
269
- Adds identifiers to the `RecordArray` without duplicates
270
-
271
- @method _pushIdentifiers
272
- @internal
273
- @param {StableRecordIdentifier[]} identifiers
274
- */
275
- _pushIdentifiers(identifiers: StableRecordIdentifier[]): void {
276
- this.content.pushObjects(identifiers);
277
- }
278
-
279
- /**
280
- Removes identifiers from the `RecordArray`.
281
-
282
- @method _removeIdentifiers
283
- @internal
284
- @param {StableRecordIdentifier[]} identifiers
285
- */
286
- _removeIdentifiers(identifiers: StableRecordIdentifier[]): void {
287
- // if we are unloading all there's no point in an expensive diff
288
- // and traversal.
289
- if (identifiers.length === this.content.length) {
290
- this.content.clear();
291
- } else {
292
- // TODO This is horribly innefficient, we should refactor RecordArray
293
- // to be a native class of our own.
294
- this.content.removeObjects(identifiers);
250
+ if (removes.length) {
251
+ if (removes.length === content.length) {
252
+ content.clear();
253
+ } else {
254
+ content.removeObjects(removes);
255
+ }
295
256
  }
296
- }
297
257
 
298
- /**
299
- @method _takeSnapshot
300
- @internal
301
- */
302
- _takeSnapshot(): Snapshot[] {
303
- return this.content.map((identifier) => this.store._instanceCache.createSnapshot(identifier));
258
+ if (adds.length) {
259
+ if (content.length === 0) {
260
+ content.setObjects(adds);
261
+ } else {
262
+ content.addObjects(adds);
263
+ }
264
+ }
304
265
  }
305
266
  }
306
267