@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.
- package/addon/-private/caches/identifier-cache.ts +41 -34
- package/addon/-private/caches/instance-cache.ts +44 -49
- package/addon/-private/caches/record-data-for.ts +1 -6
- package/addon/-private/index.ts +0 -1
- package/addon/-private/legacy-model-support/shim-model-class.ts +10 -8
- package/addon/-private/managers/record-array-manager.ts +202 -325
- package/addon/-private/managers/record-data-store-wrapper.ts +12 -10
- package/addon/-private/managers/record-notification-manager.ts +30 -12
- package/addon/-private/network/fetch-manager.ts +2 -2
- package/addon/-private/network/finders.js +11 -6
- package/addon/-private/network/request-cache.ts +20 -17
- package/addon/-private/network/snapshot-record-array.ts +9 -26
- package/addon/-private/record-arrays/adapter-populated-record-array.ts +12 -41
- package/addon/-private/record-arrays/record-array.ts +32 -71
- package/addon/-private/store-service.ts +88 -50
- package/addon/-private/utils/is-non-empty-string.ts +1 -1
- package/package.json +5 -5
- package/addon/-private/backburner.js +0 -25
- package/addon/-private/utils/weak-cache.ts +0 -125
|
@@ -50,9 +50,13 @@ class LegacyWrapper implements LegacyRecordDataStoreWrapper {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
this._willNotify = true;
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
14
|
-
|
|
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
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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
|
|
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(
|
|
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.
|
|
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 {
|
|
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
|
|
24
|
-
meta
|
|
25
|
-
links
|
|
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(
|
|
91
|
+
return promiseArray(promise);
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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
|
|
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
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
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
|
-
|
|
270
|
-
|
|
271
|
-
|
|
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
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
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
|
|