@ember-data/store 4.6.1 → 4.7.0
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/-debug/index.js +35 -13
- package/addon/-private/{identifier-cache.ts → caches/identifier-cache.ts} +148 -73
- package/addon/-private/caches/instance-cache.ts +690 -0
- package/addon/-private/{record-data-for.ts → caches/record-data-for.ts} +2 -7
- package/addon/-private/index.ts +44 -24
- package/addon/-private/{model → legacy-model-support}/record-reference.ts +15 -13
- package/addon/-private/{schema-definition-service.ts → legacy-model-support/schema-definition-service.ts} +13 -9
- package/addon/-private/{model → legacy-model-support}/shim-model-class.ts +18 -11
- package/addon/-private/managers/record-array-manager.ts +377 -0
- package/addon/-private/managers/record-data-manager.ts +845 -0
- package/addon/-private/managers/record-data-store-wrapper.ts +421 -0
- package/addon/-private/managers/record-notification-manager.ts +109 -0
- package/addon/-private/network/fetch-manager.ts +567 -0
- package/addon/-private/{finders.js → network/finders.js} +14 -17
- package/addon/-private/{request-cache.ts → network/request-cache.ts} +21 -18
- package/addon/-private/{snapshot-record-array.ts → network/snapshot-record-array.ts} +14 -31
- package/addon/-private/{snapshot.ts → network/snapshot.ts} +40 -49
- package/addon/-private/{promise-proxies.ts → proxies/promise-proxies.ts} +76 -15
- package/addon/-private/{promise-proxy-base.js → proxies/promise-proxy-base.js} +0 -0
- package/addon/-private/record-arrays/identifier-array.ts +924 -0
- package/addon/-private/{core-store.ts → store-service.ts} +574 -215
- package/addon/-private/{coerce-id.ts → utils/coerce-id.ts} +1 -1
- package/addon/-private/{common.js → utils/common.js} +1 -2
- package/addon/-private/utils/construct-resource.ts +2 -2
- package/addon/-private/{identifer-debug-consts.ts → utils/identifer-debug-consts.ts} +0 -0
- package/addon/-private/utils/is-non-empty-string.ts +1 -1
- package/addon/-private/{normalize-model-name.ts → utils/normalize-model-name.ts} +1 -3
- package/addon/-private/utils/promise-record.ts +5 -6
- package/addon/-private/{serializer-response.ts → utils/serializer-response.ts} +2 -2
- package/addon/-private/utils/uuid-polyfill.ts +73 -0
- package/package.json +12 -8
- package/addon/-private/backburner.js +0 -25
- package/addon/-private/errors-utils.js +0 -146
- package/addon/-private/fetch-manager.ts +0 -597
- package/addon/-private/identity-map.ts +0 -54
- package/addon/-private/instance-cache.ts +0 -387
- package/addon/-private/internal-model-factory.ts +0 -359
- package/addon/-private/internal-model-map.ts +0 -121
- package/addon/-private/model/internal-model.ts +0 -602
- package/addon/-private/record-array-manager.ts +0 -444
- package/addon/-private/record-arrays/adapter-populated-record-array.ts +0 -130
- package/addon/-private/record-arrays/record-array.ts +0 -318
- package/addon/-private/record-data-store-wrapper.ts +0 -243
- package/addon/-private/record-notification-manager.ts +0 -73
- package/addon/-private/weak-cache.ts +0 -125
|
@@ -1,359 +0,0 @@
|
|
|
1
|
-
import { assert, warn } from '@ember/debug';
|
|
2
|
-
import { DEBUG } from '@glimmer/env';
|
|
3
|
-
|
|
4
|
-
import type {
|
|
5
|
-
ExistingResourceObject,
|
|
6
|
-
NewResourceIdentifierObject,
|
|
7
|
-
ResourceIdentifierObject,
|
|
8
|
-
} from '@ember-data/types/q/ember-data-json-api';
|
|
9
|
-
import type { StableRecordIdentifier } from '@ember-data/types/q/identifier';
|
|
10
|
-
import type { RecordData } from '@ember-data/types/q/record-data';
|
|
11
|
-
import type { RecordInstance } from '@ember-data/types/q/record-instance';
|
|
12
|
-
|
|
13
|
-
import type Store from './core-store';
|
|
14
|
-
import type { IdentifierCache } from './identifier-cache';
|
|
15
|
-
import IdentityMap from './identity-map';
|
|
16
|
-
import type InternalModelMap from './internal-model-map';
|
|
17
|
-
import InternalModel from './model/internal-model';
|
|
18
|
-
import constructResource from './utils/construct-resource';
|
|
19
|
-
import WeakCache from './weak-cache';
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
@module @ember-data/store
|
|
23
|
-
*/
|
|
24
|
-
const FactoryCache = new WeakCache<Store, InternalModelFactory>(DEBUG ? 'internal-model-factory' : '');
|
|
25
|
-
FactoryCache._generator = (store: Store) => {
|
|
26
|
-
return new InternalModelFactory(store);
|
|
27
|
-
};
|
|
28
|
-
type NewResourceInfo = { type: string; id: string | null };
|
|
29
|
-
|
|
30
|
-
const RecordCache = new WeakCache<RecordInstance | RecordData, StableRecordIdentifier>(DEBUG ? 'identifier' : '');
|
|
31
|
-
if (DEBUG) {
|
|
32
|
-
RecordCache._expectMsg = (key: RecordInstance | RecordData) =>
|
|
33
|
-
`${String(key)} is not a record instantiated by @ember-data/store`;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export function peekRecordIdentifier(record: RecordInstance | RecordData): StableRecordIdentifier | undefined {
|
|
37
|
-
return RecordCache.get(record);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
Retrieves the unique referentially-stable [RecordIdentifier](/ember-data/release/classes/StableRecordIdentifier)
|
|
42
|
-
assigned to the given record instance.
|
|
43
|
-
|
|
44
|
-
```js
|
|
45
|
-
import { recordIdentifierFor } from "@ember-data/store";
|
|
46
|
-
|
|
47
|
-
// ... gain access to a record, for instance with peekRecord or findRecord
|
|
48
|
-
const record = store.peekRecord("user", "1");
|
|
49
|
-
|
|
50
|
-
// get the identifier for the record (see docs for StableRecordIdentifier)
|
|
51
|
-
const identifier = recordIdentifierFor(record);
|
|
52
|
-
|
|
53
|
-
// access the identifier's properties.
|
|
54
|
-
const { id, type, lid } = identifier;
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
@method recordIdentifierFor
|
|
58
|
-
@public
|
|
59
|
-
@static
|
|
60
|
-
@for @ember-data/store
|
|
61
|
-
@param {Object} record a record instance previously obstained from the store.
|
|
62
|
-
@returns {StableRecordIdentifier}
|
|
63
|
-
*/
|
|
64
|
-
export function recordIdentifierFor(record: RecordInstance | RecordData): StableRecordIdentifier {
|
|
65
|
-
return RecordCache.getWithError(record);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export function setRecordIdentifier(record: RecordInstance | RecordData, identifier: StableRecordIdentifier): void {
|
|
69
|
-
if (DEBUG && RecordCache.has(record) && RecordCache.get(record) !== identifier) {
|
|
70
|
-
throw new Error(`${record} was already assigned an identifier`);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/*
|
|
74
|
-
It would be nice to do a reverse check here that an identifier has not
|
|
75
|
-
previously been assigned a record; however, unload + rematerialization
|
|
76
|
-
prevents us from having a great way of doing so when CustomRecordClasses
|
|
77
|
-
don't necessarily give us access to a `isDestroyed` for dematerialized
|
|
78
|
-
instance.
|
|
79
|
-
*/
|
|
80
|
-
|
|
81
|
-
RecordCache.set(record, identifier);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export function internalModelFactoryFor(store: Store): InternalModelFactory {
|
|
85
|
-
return FactoryCache.lookup(store);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* The InternalModelFactory handles the lifecyle of
|
|
90
|
-
* instantiating, caching, and destroying InternalModel
|
|
91
|
-
* instances.
|
|
92
|
-
*
|
|
93
|
-
* @class InternalModelFactory
|
|
94
|
-
* @internal
|
|
95
|
-
*/
|
|
96
|
-
export default class InternalModelFactory {
|
|
97
|
-
declare _identityMap: IdentityMap;
|
|
98
|
-
declare identifierCache: IdentifierCache;
|
|
99
|
-
declare store: Store;
|
|
100
|
-
|
|
101
|
-
constructor(store: Store) {
|
|
102
|
-
this.store = store;
|
|
103
|
-
this.identifierCache = store.identifierCache;
|
|
104
|
-
this.identifierCache.__configureMerge((identifier, matchedIdentifier, resourceData) => {
|
|
105
|
-
let intendedIdentifier = identifier;
|
|
106
|
-
if (identifier.id !== matchedIdentifier.id) {
|
|
107
|
-
intendedIdentifier = 'id' in resourceData && identifier.id === resourceData.id ? identifier : matchedIdentifier;
|
|
108
|
-
} else if (identifier.type !== matchedIdentifier.type) {
|
|
109
|
-
intendedIdentifier =
|
|
110
|
-
'type' in resourceData && identifier.type === resourceData.type ? identifier : matchedIdentifier;
|
|
111
|
-
}
|
|
112
|
-
let altIdentifier = identifier === intendedIdentifier ? matchedIdentifier : identifier;
|
|
113
|
-
|
|
114
|
-
// check for duplicate InternalModel's
|
|
115
|
-
const map = this.modelMapFor(identifier.type);
|
|
116
|
-
let im = map.get(intendedIdentifier.lid);
|
|
117
|
-
let otherIm = map.get(altIdentifier.lid);
|
|
118
|
-
|
|
119
|
-
// we cannot merge internalModels when both have records
|
|
120
|
-
// (this may not be strictly true, we could probably swap the internalModel the record points at)
|
|
121
|
-
if (im && otherIm && im.hasRecord && otherIm.hasRecord) {
|
|
122
|
-
// TODO we probably don't need to throw these errors anymore
|
|
123
|
-
// once InternalModel is fully removed, as we can just "swap"
|
|
124
|
-
// what data source the abandoned record points at so long as
|
|
125
|
-
// it itself is not retained by the store in any way.
|
|
126
|
-
if ('id' in resourceData) {
|
|
127
|
-
throw new Error(
|
|
128
|
-
`Failed to update the 'id' for the RecordIdentifier '${identifier.type}:${identifier.id} (${identifier.lid})' to '${resourceData.id}', because that id is already in use by '${matchedIdentifier.type}:${matchedIdentifier.id} (${matchedIdentifier.lid})'`
|
|
129
|
-
);
|
|
130
|
-
}
|
|
131
|
-
// TODO @runspired determine when this is even possible
|
|
132
|
-
assert(
|
|
133
|
-
`Failed to update the RecordIdentifier '${identifier.type}:${identifier.id} (${identifier.lid})' to merge with the detected duplicate identifier '${matchedIdentifier.type}:${matchedIdentifier.id} (${matchedIdentifier.lid})'`
|
|
134
|
-
);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// remove otherIm from cache
|
|
138
|
-
if (otherIm) {
|
|
139
|
-
map.remove(otherIm, altIdentifier.lid);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
if (im === null && otherIm === null) {
|
|
143
|
-
// nothing more to do
|
|
144
|
-
return intendedIdentifier;
|
|
145
|
-
|
|
146
|
-
// only the other has an InternalModel
|
|
147
|
-
// OR only the other has a Record
|
|
148
|
-
} else if ((im === null && otherIm !== null) || (im && !im.hasRecord && otherIm && otherIm.hasRecord)) {
|
|
149
|
-
if (im) {
|
|
150
|
-
// TODO check if we are retained in any async relationships
|
|
151
|
-
map.remove(im, intendedIdentifier.lid);
|
|
152
|
-
// im.destroy();
|
|
153
|
-
}
|
|
154
|
-
im = otherIm;
|
|
155
|
-
// TODO do we need to notify the id change?
|
|
156
|
-
im._id = intendedIdentifier.id;
|
|
157
|
-
im.identifier = intendedIdentifier;
|
|
158
|
-
map.add(im, intendedIdentifier.lid);
|
|
159
|
-
|
|
160
|
-
// just use im
|
|
161
|
-
} else {
|
|
162
|
-
// otherIm.destroy();
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/*
|
|
166
|
-
TODO @runspired consider adding this to make polymorphism even nicer
|
|
167
|
-
if (HAS_RECORD_DATA_PACKAGE) {
|
|
168
|
-
if (identifier.type !== matchedIdentifier.type) {
|
|
169
|
-
const graphFor = importSync('@ember-data/record-data/-private').graphFor;
|
|
170
|
-
graphFor(this).registerPolymorphicType(identifier.type, matchedIdentifier.type);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
*/
|
|
174
|
-
|
|
175
|
-
return intendedIdentifier;
|
|
176
|
-
});
|
|
177
|
-
this._identityMap = new IdentityMap();
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* Retrieve the InternalModel for a given { type, id, lid }.
|
|
182
|
-
*
|
|
183
|
-
* If an InternalModel does not exist, it instantiates one.
|
|
184
|
-
*
|
|
185
|
-
* If an InternalModel does exist bus has a scheduled destroy,
|
|
186
|
-
* the scheduled destroy will be cancelled.
|
|
187
|
-
*
|
|
188
|
-
* @method lookup
|
|
189
|
-
* @private
|
|
190
|
-
*/
|
|
191
|
-
lookup(resource: ResourceIdentifierObject, data?: ExistingResourceObject): InternalModel {
|
|
192
|
-
if (data !== undefined) {
|
|
193
|
-
// if we've been given data associated with this lookup
|
|
194
|
-
// we must first give secondary-caches for LIDs the
|
|
195
|
-
// opportunity to populate based on it
|
|
196
|
-
this.identifierCache.getOrCreateRecordIdentifier(data);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
const identifier = this.identifierCache.getOrCreateRecordIdentifier(resource);
|
|
200
|
-
const internalModel = this.peek(identifier);
|
|
201
|
-
|
|
202
|
-
if (internalModel) {
|
|
203
|
-
// unloadRecord is async, if one attempts to unload + then sync push,
|
|
204
|
-
// we must ensure the unload is canceled before continuing
|
|
205
|
-
// The createRecord path will take _existingInternalModelForId()
|
|
206
|
-
// which will call `destroySync` instead for this unload + then
|
|
207
|
-
// sync createRecord scenario. Once we have true client-side
|
|
208
|
-
// delete signaling, we should never call destroySync
|
|
209
|
-
if (internalModel.hasScheduledDestroy()) {
|
|
210
|
-
internalModel.cancelDestroy();
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
return internalModel;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
return this._build(identifier, false);
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* Peek the InternalModel for a given { type, id, lid }.
|
|
221
|
-
*
|
|
222
|
-
* If an InternalModel does not exist, return `null`.
|
|
223
|
-
*
|
|
224
|
-
* @method peek
|
|
225
|
-
* @private
|
|
226
|
-
*/
|
|
227
|
-
peek(identifier: StableRecordIdentifier): InternalModel | null {
|
|
228
|
-
return this.modelMapFor(identifier.type).get(identifier.lid);
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
getByResource(resource: ResourceIdentifierObject): InternalModel {
|
|
232
|
-
const normalizedResource = constructResource(resource);
|
|
233
|
-
|
|
234
|
-
return this.lookup(normalizedResource);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
setRecordId(type: string, id: string, lid: string) {
|
|
238
|
-
const resource: NewResourceIdentifierObject = { type, id: null, lid };
|
|
239
|
-
const identifier = this.identifierCache.getOrCreateRecordIdentifier(resource);
|
|
240
|
-
const internalModel = this.peek(identifier);
|
|
241
|
-
|
|
242
|
-
if (internalModel === null) {
|
|
243
|
-
throw new Error(`Cannot set the id ${id} on the record ${type}:${lid} as there is no such record in the cache.`);
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
let oldId = internalModel.id;
|
|
247
|
-
let modelName = internalModel.modelName;
|
|
248
|
-
|
|
249
|
-
// ID absolutely can't be missing if the oldID is empty (missing Id in response for a new record)
|
|
250
|
-
assert(
|
|
251
|
-
`'${modelName}' was saved to the server, but the response does not have an id and your record does not either.`,
|
|
252
|
-
!(id === null && oldId === null)
|
|
253
|
-
);
|
|
254
|
-
|
|
255
|
-
// ID absolutely can't be different than oldID if oldID is not null
|
|
256
|
-
// TODO this assertion and restriction may not strictly be needed in the identifiers world
|
|
257
|
-
assert(
|
|
258
|
-
`Cannot update the id for '${modelName}:${lid}' from '${oldId}' to '${id}'.`,
|
|
259
|
-
!(oldId !== null && id !== oldId)
|
|
260
|
-
);
|
|
261
|
-
|
|
262
|
-
// ID can be null if oldID is not null (altered ID in response for a record)
|
|
263
|
-
// however, this is more than likely a developer error.
|
|
264
|
-
if (oldId !== null && id === null) {
|
|
265
|
-
warn(
|
|
266
|
-
`Your ${modelName} record was saved to the server, but the response does not have an id.`,
|
|
267
|
-
!(oldId !== null && id === null)
|
|
268
|
-
);
|
|
269
|
-
return;
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
let existingInternalModel = this.peekById(modelName, id);
|
|
273
|
-
|
|
274
|
-
assert(
|
|
275
|
-
`'${modelName}' was saved to the server, but the response returned the new id '${id}', which has already been used with another record.'`,
|
|
276
|
-
!existingInternalModel || existingInternalModel === internalModel
|
|
277
|
-
);
|
|
278
|
-
|
|
279
|
-
if (identifier.id === null) {
|
|
280
|
-
// TODO potentially this needs to handle merged result
|
|
281
|
-
this.identifierCache.updateRecordIdentifier(identifier, { type, id });
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
internalModel.setId(id, true);
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
peekById(type: string, id: string): InternalModel | null {
|
|
288
|
-
const identifier = this.identifierCache.peekRecordIdentifier({ type, id });
|
|
289
|
-
let internalModel = identifier ? this.modelMapFor(type).get(identifier.lid) : null;
|
|
290
|
-
|
|
291
|
-
if (internalModel && internalModel.hasScheduledDestroy()) {
|
|
292
|
-
// unloadRecord is async, if one attempts to unload + then sync create,
|
|
293
|
-
// we must ensure the unload is complete before starting the create
|
|
294
|
-
// The push path will take this.lookup()
|
|
295
|
-
// which will call `cancelDestroy` instead for this unload + then
|
|
296
|
-
// sync push scenario. Once we have true client-side
|
|
297
|
-
// delete signaling, we should never call destroySync
|
|
298
|
-
internalModel.destroySync();
|
|
299
|
-
internalModel = null;
|
|
300
|
-
}
|
|
301
|
-
return internalModel;
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
build(newResourceInfo: NewResourceInfo): InternalModel {
|
|
305
|
-
return this._build(newResourceInfo, true);
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
_build(resource: StableRecordIdentifier, isCreate: false): InternalModel;
|
|
309
|
-
_build(resource: NewResourceInfo, isCreate: true): InternalModel;
|
|
310
|
-
_build(resource: StableRecordIdentifier | NewResourceInfo, isCreate: boolean = false): InternalModel {
|
|
311
|
-
if (isCreate === true && resource.id) {
|
|
312
|
-
let existingInternalModel = this.peekById(resource.type, resource.id);
|
|
313
|
-
|
|
314
|
-
assert(
|
|
315
|
-
`The id ${resource.id} has already been used with another '${resource.type}' record.`,
|
|
316
|
-
!existingInternalModel
|
|
317
|
-
);
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
const { identifierCache } = this;
|
|
321
|
-
let identifier: StableRecordIdentifier;
|
|
322
|
-
|
|
323
|
-
if (isCreate === true) {
|
|
324
|
-
identifier = identifierCache.createIdentifierForNewRecord(resource);
|
|
325
|
-
} else {
|
|
326
|
-
identifier = resource as StableRecordIdentifier;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
// lookupFactory should really return an object that creates
|
|
330
|
-
// instances with the injections applied
|
|
331
|
-
let internalModel = new InternalModel(this.store, identifier);
|
|
332
|
-
|
|
333
|
-
this.modelMapFor(resource.type).add(internalModel, identifier.lid);
|
|
334
|
-
|
|
335
|
-
return internalModel;
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
remove(internalModel: InternalModel): void {
|
|
339
|
-
let recordMap = this.modelMapFor(internalModel.modelName);
|
|
340
|
-
let clientId = internalModel.identifier.lid;
|
|
341
|
-
|
|
342
|
-
recordMap.remove(internalModel, clientId);
|
|
343
|
-
|
|
344
|
-
const { identifier } = internalModel;
|
|
345
|
-
this.identifierCache.forgetRecordIdentifier(identifier);
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
modelMapFor(type: string): InternalModelMap {
|
|
349
|
-
return this._identityMap.retrieve(type);
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
clear(type?: string) {
|
|
353
|
-
if (type === undefined) {
|
|
354
|
-
this._identityMap.clear();
|
|
355
|
-
} else {
|
|
356
|
-
this.modelMapFor(type).clear();
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
}
|
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
import { assert } from '@ember/debug';
|
|
2
|
-
|
|
3
|
-
import type { StableRecordIdentifier } from '@ember-data/types/q/identifier';
|
|
4
|
-
import type { ConfidentDict } from '@ember-data/types/q/utils';
|
|
5
|
-
|
|
6
|
-
import InternalModel from './model/internal-model';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
@module @ember-data/store
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
`InternalModelMap` is a custom storage map for internalModels of a given modelName
|
|
14
|
-
used by `IdentityMap`.
|
|
15
|
-
|
|
16
|
-
It was extracted from an implicit pojo based "internalModel map" and preserves
|
|
17
|
-
that interface while we work towards a more official API.
|
|
18
|
-
|
|
19
|
-
@class InternalModelMap
|
|
20
|
-
@internal
|
|
21
|
-
*/
|
|
22
|
-
export default class InternalModelMap {
|
|
23
|
-
_idToModel: ConfidentDict<InternalModel> = Object.create(null);
|
|
24
|
-
_models: InternalModel[] = [];
|
|
25
|
-
modelName: string;
|
|
26
|
-
|
|
27
|
-
constructor(modelName: string) {
|
|
28
|
-
this.modelName = modelName;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
get(id: string): InternalModel | null {
|
|
32
|
-
return this._idToModel[id] || null;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
has(id: string): boolean {
|
|
36
|
-
return !!this._idToModel[id];
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
get length(): number {
|
|
40
|
-
return this._models.length;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
get recordIdentifiers(): StableRecordIdentifier[] {
|
|
44
|
-
return this._models.map((m) => m.identifier);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
set(id: string, internalModel: InternalModel): void {
|
|
48
|
-
assert(`You cannot index an internalModel by an empty id'`, typeof id === 'string' && id.length > 0);
|
|
49
|
-
assert(
|
|
50
|
-
`You cannot set an index for an internalModel to something other than an internalModel`,
|
|
51
|
-
internalModel instanceof InternalModel
|
|
52
|
-
);
|
|
53
|
-
assert(
|
|
54
|
-
`You cannot set an index for an internalModel that is not in the InternalModelMap`,
|
|
55
|
-
this.contains(internalModel)
|
|
56
|
-
);
|
|
57
|
-
assert(
|
|
58
|
-
`You cannot update the id index of an InternalModel once set. Attempted to update ${id}.`,
|
|
59
|
-
!this.has(id) || this.get(id) === internalModel
|
|
60
|
-
);
|
|
61
|
-
|
|
62
|
-
this._idToModel[id] = internalModel;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
add(internalModel: InternalModel, id: string | null): void {
|
|
66
|
-
assert(
|
|
67
|
-
`You cannot re-add an already present InternalModel to the InternalModelMap.`,
|
|
68
|
-
!this.contains(internalModel)
|
|
69
|
-
);
|
|
70
|
-
|
|
71
|
-
if (id) {
|
|
72
|
-
assert(
|
|
73
|
-
`Duplicate InternalModel for ${this.modelName}:${id} detected.`,
|
|
74
|
-
!this.has(id) || this.get(id) === internalModel
|
|
75
|
-
);
|
|
76
|
-
|
|
77
|
-
this._idToModel[id] = internalModel;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
this._models.push(internalModel);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
remove(internalModel: InternalModel, id: string): void {
|
|
84
|
-
delete this._idToModel[id];
|
|
85
|
-
|
|
86
|
-
let loc = this._models.indexOf(internalModel);
|
|
87
|
-
|
|
88
|
-
if (loc !== -1) {
|
|
89
|
-
this._models.splice(loc, 1);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
contains(internalModel: InternalModel): boolean {
|
|
94
|
-
return this._models.indexOf(internalModel) !== -1;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
An array of all models of this modelName
|
|
99
|
-
@property models
|
|
100
|
-
@internal
|
|
101
|
-
@type Array
|
|
102
|
-
*/
|
|
103
|
-
get models(): InternalModel[] {
|
|
104
|
-
return this._models;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
Destroy all models in the map
|
|
109
|
-
|
|
110
|
-
@internal
|
|
111
|
-
*/
|
|
112
|
-
clear(): void {
|
|
113
|
-
let internalModels = this._models;
|
|
114
|
-
this._models = [];
|
|
115
|
-
|
|
116
|
-
for (let i = 0; i < internalModels.length; i++) {
|
|
117
|
-
let internalModel = internalModels[i];
|
|
118
|
-
internalModel.unloadRecord();
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
}
|