@ember-data/store 4.8.0-alpha.3 → 4.8.0-alpha.6
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 +65 -66
- package/addon/-private/caches/instance-cache.ts +173 -236
- package/addon/-private/caches/record-data-for.ts +1 -6
- package/addon/-private/index.ts +13 -10
- package/addon/-private/legacy-model-support/record-reference.ts +12 -10
- package/addon/-private/legacy-model-support/schema-definition-service.ts +9 -4
- package/addon/-private/legacy-model-support/shim-model-class.ts +17 -10
- package/addon/-private/managers/record-array-manager.ts +282 -321
- package/addon/-private/managers/record-data-manager.ts +822 -0
- package/addon/-private/managers/record-data-store-wrapper.ts +295 -91
- package/addon/-private/managers/record-notification-manager.ts +45 -32
- package/addon/-private/network/fetch-manager.ts +292 -300
- 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 +12 -29
- package/addon/-private/network/snapshot.ts +25 -27
- package/addon/-private/proxies/promise-proxies.ts +72 -11
- package/addon/-private/record-arrays/identifier-array.ts +924 -0
- package/addon/-private/store-service.ts +380 -114
- package/addon/-private/utils/is-non-empty-string.ts +1 -1
- package/addon/-private/utils/promise-record.ts +2 -3
- package/addon/-private/utils/uuid-polyfill.ts +71 -0
- package/package.json +11 -7
- package/addon/-private/backburner.js +0 -25
- package/addon/-private/record-arrays/adapter-populated-record-array.ts +0 -128
- package/addon/-private/record-arrays/record-array.ts +0 -320
- package/addon/-private/utils/weak-cache.ts +0 -125
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
import { getOwner, setOwner } from '@ember/application';
|
|
5
5
|
import { assert, deprecate } from '@ember/debug';
|
|
6
6
|
import { _backburner as emberBackburner, run } from '@ember/runloop';
|
|
7
|
-
import type { Backburner } from '@ember/runloop/-private/backburner';
|
|
8
7
|
import Service from '@ember/service';
|
|
9
8
|
import { registerWaiter, unregisterWaiter } from '@ember/test';
|
|
10
9
|
import { DEBUG } from '@glimmer/env';
|
|
@@ -17,7 +16,9 @@ import { HAS_MODEL_PACKAGE, HAS_RECORD_DATA_PACKAGE } from '@ember-data/private-
|
|
|
17
16
|
import {
|
|
18
17
|
DEPRECATE_HAS_RECORD,
|
|
19
18
|
DEPRECATE_JSON_API_FALLBACK,
|
|
19
|
+
DEPRECATE_PROMISE_PROXIES,
|
|
20
20
|
DEPRECATE_STORE_FIND,
|
|
21
|
+
DEPRECATE_V1CACHE_STORE_APIS,
|
|
21
22
|
} from '@ember-data/private-build-infra/deprecations';
|
|
22
23
|
import type { RecordData as RecordDataClass } from '@ember-data/record-data/-private';
|
|
23
24
|
import type { DSModel } from '@ember-data/types/q/ds-model';
|
|
@@ -31,15 +32,14 @@ import type {
|
|
|
31
32
|
import type { StableExistingRecordIdentifier, StableRecordIdentifier } from '@ember-data/types/q/identifier';
|
|
32
33
|
import type { MinimumAdapterInterface } from '@ember-data/types/q/minimum-adapter-interface';
|
|
33
34
|
import type { MinimumSerializerInterface } from '@ember-data/types/q/minimum-serializer-interface';
|
|
34
|
-
import type { RecordData } from '@ember-data/types/q/record-data';
|
|
35
|
+
import type { RecordData, RecordDataV1 } from '@ember-data/types/q/record-data';
|
|
35
36
|
import { JsonApiValidationError } from '@ember-data/types/q/record-data-json-api';
|
|
36
|
-
import type {
|
|
37
|
+
import type { RecordDataStoreWrapper } from '@ember-data/types/q/record-data-store-wrapper';
|
|
37
38
|
import type { RecordInstance } from '@ember-data/types/q/record-instance';
|
|
38
39
|
import type { SchemaDefinitionService } from '@ember-data/types/q/schema-definition-service';
|
|
39
40
|
import type { FindOptions } from '@ember-data/types/q/store';
|
|
40
41
|
import type { Dict } from '@ember-data/types/q/utils';
|
|
41
42
|
|
|
42
|
-
import edBackburner from './backburner';
|
|
43
43
|
import { IdentifierCache } from './caches/identifier-cache';
|
|
44
44
|
import {
|
|
45
45
|
InstanceCache,
|
|
@@ -50,20 +50,21 @@ import {
|
|
|
50
50
|
storeFor,
|
|
51
51
|
StoreMap,
|
|
52
52
|
} from './caches/instance-cache';
|
|
53
|
-
import { setRecordDataFor } from './caches/record-data-for';
|
|
53
|
+
import recordDataFor, { setRecordDataFor } from './caches/record-data-for';
|
|
54
54
|
import RecordReference from './legacy-model-support/record-reference';
|
|
55
55
|
import { DSModelSchemaDefinitionService, getModelFactory } from './legacy-model-support/schema-definition-service';
|
|
56
56
|
import type ShimModelClass from './legacy-model-support/shim-model-class';
|
|
57
57
|
import { getShimClass } from './legacy-model-support/shim-model-class';
|
|
58
58
|
import RecordArrayManager from './managers/record-array-manager';
|
|
59
|
-
import
|
|
59
|
+
import type { NonSingletonRecordDataManager } from './managers/record-data-manager';
|
|
60
60
|
import NotificationManager from './managers/record-notification-manager';
|
|
61
61
|
import FetchManager, { SaveOp } from './network/fetch-manager';
|
|
62
62
|
import { _findAll, _query, _queryRecord } from './network/finders';
|
|
63
63
|
import type RequestCache from './network/request-cache';
|
|
64
|
+
import type Snapshot from './network/snapshot';
|
|
65
|
+
import SnapshotRecordArray from './network/snapshot-record-array';
|
|
64
66
|
import { PromiseArray, promiseArray, PromiseObject, promiseObject } from './proxies/promise-proxies';
|
|
65
|
-
import
|
|
66
|
-
import RecordArray from './record-arrays/record-array';
|
|
67
|
+
import IdentifierArray, { Collection } from './record-arrays/identifier-array';
|
|
67
68
|
import coerceId, { ensureStringId } from './utils/coerce-id';
|
|
68
69
|
import constructResource from './utils/construct-resource';
|
|
69
70
|
import normalizeModelName from './utils/normalize-model-name';
|
|
@@ -164,18 +165,8 @@ export interface CreateRecordProperties {
|
|
|
164
165
|
*/
|
|
165
166
|
|
|
166
167
|
class Store extends Service {
|
|
167
|
-
|
|
168
|
-
* Ember Data uses several specialized micro-queues for organizing
|
|
169
|
-
and coalescing similar async work.
|
|
168
|
+
__private_singleton_recordData!: RecordData;
|
|
170
169
|
|
|
171
|
-
These queues are currently controlled by a flush scheduled into
|
|
172
|
-
ember-data's custom backburner instance.
|
|
173
|
-
*
|
|
174
|
-
* EmberData specific backburner instance
|
|
175
|
-
* @property _backburner
|
|
176
|
-
* @private
|
|
177
|
-
*/
|
|
178
|
-
declare _backburner: Backburner;
|
|
179
170
|
declare recordArrayManager: RecordArrayManager;
|
|
180
171
|
|
|
181
172
|
declare _notificationManager: NotificationManager;
|
|
@@ -227,10 +218,6 @@ class Store extends Service {
|
|
|
227
218
|
this._serializerCache = Object.create(null);
|
|
228
219
|
this._modelFactoryCache = Object.create(null);
|
|
229
220
|
|
|
230
|
-
// private
|
|
231
|
-
// TODO we should find a path to something simpler than backburner
|
|
232
|
-
this._backburner = edBackburner;
|
|
233
|
-
|
|
234
221
|
if (DEBUG) {
|
|
235
222
|
if (this.generateStackTracesForTrackedRequests === undefined) {
|
|
236
223
|
this.generateStackTracesForTrackedRequests = false;
|
|
@@ -278,41 +265,108 @@ class Store extends Service {
|
|
|
278
265
|
}
|
|
279
266
|
}
|
|
280
267
|
|
|
268
|
+
declare _cbs: { coalesce?: () => void; sync?: () => void; notify?: () => void } | null;
|
|
269
|
+
_run(cb: () => void) {
|
|
270
|
+
assert(`EmberData should never encounter a nested run`, !this._cbs);
|
|
271
|
+
const _cbs: { coalesce?: () => void; sync?: () => void; notify?: () => void } = (this._cbs = {});
|
|
272
|
+
cb();
|
|
273
|
+
if (_cbs.coalesce) {
|
|
274
|
+
_cbs.coalesce();
|
|
275
|
+
}
|
|
276
|
+
if (_cbs.sync) {
|
|
277
|
+
_cbs.sync();
|
|
278
|
+
}
|
|
279
|
+
if (_cbs.notify) {
|
|
280
|
+
_cbs.notify();
|
|
281
|
+
}
|
|
282
|
+
this._cbs = null;
|
|
283
|
+
}
|
|
284
|
+
_join(cb: () => void): void {
|
|
285
|
+
if (this._cbs) {
|
|
286
|
+
cb();
|
|
287
|
+
} else {
|
|
288
|
+
this._run(cb);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
_schedule(name: 'coalesce' | 'sync' | 'notify', cb: () => void): void {
|
|
293
|
+
assert(`EmberData expects to schedule only when there is an active run`, !!this._cbs);
|
|
294
|
+
assert(`EmberData expects only one flush per queue name, cannot schedule ${name}`, !this._cbs[name]);
|
|
295
|
+
|
|
296
|
+
this._cbs[name] = cb;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Retrieve the RequestStateService instance
|
|
301
|
+
* associated with this Store.
|
|
302
|
+
*
|
|
303
|
+
* This can be used to query the status of requests
|
|
304
|
+
* that have been initiated for a given identifier.
|
|
305
|
+
*
|
|
306
|
+
* @method getRequestStateService
|
|
307
|
+
* @returns {RequestStateService}
|
|
308
|
+
* @public
|
|
309
|
+
*/
|
|
281
310
|
getRequestStateService(): RequestCache {
|
|
282
311
|
return this._fetchManager.requestCache;
|
|
283
312
|
}
|
|
284
313
|
|
|
314
|
+
/**
|
|
315
|
+
* A hook which an app or addon may implement. Called when
|
|
316
|
+
* the Store is attempting to create a Record Instance for
|
|
317
|
+
* a resource.
|
|
318
|
+
*
|
|
319
|
+
* This hook can be used to select or instantiate any desired
|
|
320
|
+
* mechanism of presentating cache data to the ui for access
|
|
321
|
+
* mutation, and interaction.
|
|
322
|
+
*
|
|
323
|
+
* @method instantiateRecord (hook)
|
|
324
|
+
* @param identifier
|
|
325
|
+
* @param createRecordArgs
|
|
326
|
+
* @param recordDataFor
|
|
327
|
+
* @param notificationManager
|
|
328
|
+
* @returns A record instance
|
|
329
|
+
* @public
|
|
330
|
+
*/
|
|
285
331
|
instantiateRecord(
|
|
286
332
|
identifier: StableRecordIdentifier,
|
|
287
333
|
createRecordArgs: { [key: string]: unknown },
|
|
288
|
-
recordDataFor: (identifier: StableRecordIdentifier) =>
|
|
334
|
+
recordDataFor: (identifier: StableRecordIdentifier) => RecordData,
|
|
289
335
|
notificationManager: NotificationManager
|
|
290
336
|
): DSModel | RecordInstance {
|
|
291
337
|
if (HAS_MODEL_PACKAGE) {
|
|
292
338
|
let modelName = identifier.type;
|
|
293
|
-
let store = this;
|
|
294
339
|
|
|
295
340
|
let recordData = this._instanceCache.getRecordData(identifier);
|
|
341
|
+
// TODO deprecate allowing unknown args setting
|
|
296
342
|
let createOptions: any = {
|
|
297
|
-
// TODO deprecate allowing unknown args setting
|
|
298
343
|
_createProps: createRecordArgs,
|
|
299
344
|
// TODO @deprecate consider deprecating accessing record properties during init which the below is necessary for
|
|
300
|
-
_secretInit:
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
345
|
+
_secretInit: {
|
|
346
|
+
identifier,
|
|
347
|
+
recordData,
|
|
348
|
+
store: this,
|
|
349
|
+
cb: secretInit,
|
|
304
350
|
},
|
|
305
|
-
container: null, // necessary hack for setOwner?
|
|
306
351
|
};
|
|
307
352
|
|
|
308
353
|
// ensure that `getOwner(this)` works inside a model instance
|
|
309
354
|
setOwner(createOptions, getOwner(this));
|
|
310
|
-
|
|
311
|
-
return getModelFactory(this, this._modelFactoryCache, modelName).create(createOptions);
|
|
355
|
+
return getModelFactory(this, this._modelFactoryCache, modelName).class.create(createOptions);
|
|
312
356
|
}
|
|
313
357
|
assert(`You must implement the store's instantiateRecord hook for your custom model class.`);
|
|
314
358
|
}
|
|
315
359
|
|
|
360
|
+
/**
|
|
361
|
+
* A hook which an app or addon may implement. Called when
|
|
362
|
+
* the Store is destroying a Record Instance. This hook should
|
|
363
|
+
* be used to teardown any custom record instances instantiated
|
|
364
|
+
* with `instantiateRecord`.
|
|
365
|
+
*
|
|
366
|
+
* @method teardownRecord (hook)
|
|
367
|
+
* @public
|
|
368
|
+
* @param record
|
|
369
|
+
*/
|
|
316
370
|
teardownRecord(record: DSModel | RecordInstance): void {
|
|
317
371
|
if (HAS_MODEL_PACKAGE) {
|
|
318
372
|
assert(
|
|
@@ -325,6 +379,16 @@ class Store extends Service {
|
|
|
325
379
|
}
|
|
326
380
|
}
|
|
327
381
|
|
|
382
|
+
/**
|
|
383
|
+
* Provides access to the SchemaDefinitionService instance
|
|
384
|
+
* for this Store instance.
|
|
385
|
+
*
|
|
386
|
+
* The SchemaDefinitionService can be used to query for
|
|
387
|
+
* information about the schema of a resource.
|
|
388
|
+
*
|
|
389
|
+
* @method getSchemaDefinitionService
|
|
390
|
+
* @public
|
|
391
|
+
*/
|
|
328
392
|
getSchemaDefinitionService(): SchemaDefinitionService {
|
|
329
393
|
if (HAS_MODEL_PACKAGE && !this._schemaDefinitionService) {
|
|
330
394
|
// it is potentially a mistake for the RFC to have not enabled chaining these services, though highlander rule is nice.
|
|
@@ -338,7 +402,20 @@ class Store extends Service {
|
|
|
338
402
|
return this._schemaDefinitionService;
|
|
339
403
|
}
|
|
340
404
|
|
|
405
|
+
/**
|
|
406
|
+
* Allows an app to register a custom SchemaDefinitionService
|
|
407
|
+
* for use when information about a resource's schema needs
|
|
408
|
+
* to be queried.
|
|
409
|
+
*
|
|
410
|
+
* This method can only be called once and needs to be called before
|
|
411
|
+
* `getSchemaDefinitionService` is called when using `@ember-data/model`.
|
|
412
|
+
*
|
|
413
|
+
* @method registerSchemaDefinitionService
|
|
414
|
+
* @param {SchemaDefinitionService} schema
|
|
415
|
+
* @public
|
|
416
|
+
*/
|
|
341
417
|
registerSchemaDefinitionService(schema: SchemaDefinitionService) {
|
|
418
|
+
assert(`Cannot register a schema definition service when one already exists`, !this._schemaDefinitionService);
|
|
342
419
|
this._schemaDefinitionService = schema;
|
|
343
420
|
}
|
|
344
421
|
|
|
@@ -348,6 +425,12 @@ class Store extends Service {
|
|
|
348
425
|
When used with Model from @ember-data/model the return is the model class,
|
|
349
426
|
but this is not guaranteed.
|
|
350
427
|
|
|
428
|
+
If looking to query attribute or relationship information it is
|
|
429
|
+
recommended to use `getSchemaDefinitionService` instead. This method
|
|
430
|
+
should be considered legacy and exists primarily to continue to support
|
|
431
|
+
Adapter/Serializer APIs which expect it's return value in their method
|
|
432
|
+
signatures.
|
|
433
|
+
|
|
351
434
|
The class of a model might be useful if you want to get a list of all the
|
|
352
435
|
relationship names of the model, see
|
|
353
436
|
[`relationshipNames`](/ember-data/release/classes/Model?anchor=relationshipNames)
|
|
@@ -438,8 +521,9 @@ class Store extends Service {
|
|
|
438
521
|
// of record-arrays via ember's run loop, not our own.
|
|
439
522
|
//
|
|
440
523
|
// to remove this, we would need to move to a new `async` API.
|
|
441
|
-
|
|
442
|
-
|
|
524
|
+
let record!: RecordInstance;
|
|
525
|
+
emberBackburner.join(() => {
|
|
526
|
+
this._join(() => {
|
|
443
527
|
let normalizedModelName = normalizeModelName(modelName);
|
|
444
528
|
let properties = { ...inputProperties };
|
|
445
529
|
|
|
@@ -473,12 +557,20 @@ class Store extends Service {
|
|
|
473
557
|
|
|
474
558
|
const identifier = this.identifierCache.createIdentifierForNewRecord(resource);
|
|
475
559
|
const recordData = this._instanceCache.getRecordData(identifier);
|
|
476
|
-
recordData.clientDidCreate();
|
|
477
|
-
this.recordArrayManager.recordDidChange(identifier);
|
|
478
560
|
|
|
479
|
-
|
|
561
|
+
const createOptions = normalizeProperties(
|
|
562
|
+
this,
|
|
563
|
+
identifier,
|
|
564
|
+
properties,
|
|
565
|
+
(recordData as NonSingletonRecordDataManager).managedVersion === '1'
|
|
566
|
+
);
|
|
567
|
+
const resultProps = recordData.clientDidCreate(identifier, createOptions);
|
|
568
|
+
this.recordArrayManager.identifierAdded(identifier);
|
|
569
|
+
|
|
570
|
+
record = this._instanceCache.getRecord(identifier, resultProps);
|
|
480
571
|
});
|
|
481
572
|
});
|
|
573
|
+
return record;
|
|
482
574
|
}
|
|
483
575
|
|
|
484
576
|
/**
|
|
@@ -502,26 +594,16 @@ class Store extends Service {
|
|
|
502
594
|
if (DEBUG) {
|
|
503
595
|
assertDestroyingStore(this, 'deleteRecord');
|
|
504
596
|
}
|
|
505
|
-
// TODO eliminate this interleaving
|
|
506
|
-
// it is unlikely we need both an outer join and the inner run
|
|
507
|
-
// of our own queue
|
|
508
|
-
this._backburner.join(() => {
|
|
509
|
-
const identifier = peekRecordIdentifier(record);
|
|
510
|
-
if (identifier) {
|
|
511
|
-
run(() => {
|
|
512
|
-
const backburner = this._backburner;
|
|
513
|
-
backburner.run(() => {
|
|
514
|
-
const recordData = this._instanceCache.peek({ identifier, bucket: 'recordData' });
|
|
515
597
|
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
598
|
+
const identifier = peekRecordIdentifier(record);
|
|
599
|
+
const recordData = identifier && this._instanceCache.peek({ identifier, bucket: 'recordData' });
|
|
600
|
+
assert(`expected a recordData instance to exist for the record`, recordData);
|
|
601
|
+
this._join(() => {
|
|
602
|
+
recordData.setIsDeleted(identifier, true);
|
|
603
|
+
|
|
604
|
+
if (recordData.isNew(identifier)) {
|
|
605
|
+
run(() => {
|
|
606
|
+
this._instanceCache.unloadRecord(identifier);
|
|
525
607
|
});
|
|
526
608
|
}
|
|
527
609
|
});
|
|
@@ -547,7 +629,7 @@ class Store extends Service {
|
|
|
547
629
|
if (DEBUG) {
|
|
548
630
|
assertDestroyingStore(this, 'unloadRecord');
|
|
549
631
|
}
|
|
550
|
-
|
|
632
|
+
const identifier = peekRecordIdentifier(record);
|
|
551
633
|
if (identifier) {
|
|
552
634
|
this._instanceCache.unloadRecord(identifier);
|
|
553
635
|
}
|
|
@@ -1014,14 +1096,14 @@ class Store extends Service {
|
|
|
1014
1096
|
assertIdentifierHasId(identifier);
|
|
1015
1097
|
promise = this._fetchManager.scheduleFetch(identifier, options);
|
|
1016
1098
|
} else {
|
|
1017
|
-
let snapshot =
|
|
1099
|
+
let snapshot: Snapshot | null = null;
|
|
1018
1100
|
let adapter = this.adapterFor(identifier.type);
|
|
1019
1101
|
|
|
1020
1102
|
// Refetch the record if the adapter thinks the record is stale
|
|
1021
1103
|
if (
|
|
1022
1104
|
typeof options.reload === 'undefined' &&
|
|
1023
1105
|
adapter.shouldReloadRecord &&
|
|
1024
|
-
adapter.shouldReloadRecord(this, snapshot)
|
|
1106
|
+
adapter.shouldReloadRecord(this, (snapshot = this._instanceCache.createSnapshot(identifier, options)))
|
|
1025
1107
|
) {
|
|
1026
1108
|
assertIdentifierHasId(identifier);
|
|
1027
1109
|
promise = this._fetchManager.scheduleFetch(identifier, options);
|
|
@@ -1031,7 +1113,10 @@ class Store extends Service {
|
|
|
1031
1113
|
options.backgroundReload !== false &&
|
|
1032
1114
|
(options.backgroundReload ||
|
|
1033
1115
|
!adapter.shouldBackgroundReloadRecord ||
|
|
1034
|
-
adapter.shouldBackgroundReloadRecord(
|
|
1116
|
+
adapter.shouldBackgroundReloadRecord(
|
|
1117
|
+
this,
|
|
1118
|
+
(snapshot = snapshot || this._instanceCache.createSnapshot(identifier, options))
|
|
1119
|
+
))
|
|
1035
1120
|
) {
|
|
1036
1121
|
assertIdentifierHasId(identifier);
|
|
1037
1122
|
this._fetchManager.scheduleFetch(identifier, options);
|
|
@@ -1042,7 +1127,11 @@ class Store extends Service {
|
|
|
1042
1127
|
}
|
|
1043
1128
|
}
|
|
1044
1129
|
|
|
1045
|
-
|
|
1130
|
+
if (DEPRECATE_PROMISE_PROXIES) {
|
|
1131
|
+
return promiseRecord(this, promise);
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
return promise.then((identifier: StableRecordIdentifier) => this.peekRecord(identifier));
|
|
1046
1135
|
}
|
|
1047
1136
|
|
|
1048
1137
|
/**
|
|
@@ -1201,6 +1290,7 @@ class Store extends Service {
|
|
|
1201
1290
|
```
|
|
1202
1291
|
|
|
1203
1292
|
@method hasRecordForId
|
|
1293
|
+
@deprecated
|
|
1204
1294
|
@public
|
|
1205
1295
|
@param {String} modelName
|
|
1206
1296
|
@param {(String|Integer)} id
|
|
@@ -1272,8 +1362,8 @@ class Store extends Service {
|
|
|
1272
1362
|
decoded: "/api/v1/person?ids[]=1&ids[]=2&ids[]=3"
|
|
1273
1363
|
```
|
|
1274
1364
|
|
|
1275
|
-
This method returns a promise, which is resolved with
|
|
1276
|
-
[`
|
|
1365
|
+
This method returns a promise, which is resolved with a
|
|
1366
|
+
[`Collection`](/ember-data/release/classes/Collection)
|
|
1277
1367
|
once the server returns.
|
|
1278
1368
|
|
|
1279
1369
|
@since 1.13.0
|
|
@@ -1284,7 +1374,7 @@ class Store extends Service {
|
|
|
1284
1374
|
@param {Object} options optional, may include `adapterOptions` hash which will be passed to adapter.query
|
|
1285
1375
|
@return {Promise} promise
|
|
1286
1376
|
*/
|
|
1287
|
-
query(modelName: string, query, options): PromiseArray<RecordInstance,
|
|
1377
|
+
query(modelName: string, query, options): PromiseArray<RecordInstance, Collection> | Promise<Collection> {
|
|
1288
1378
|
if (DEBUG) {
|
|
1289
1379
|
assertDestroyingStore(this, 'query');
|
|
1290
1380
|
}
|
|
@@ -1318,9 +1408,12 @@ class Store extends Service {
|
|
|
1318
1408
|
query,
|
|
1319
1409
|
recordArray,
|
|
1320
1410
|
adapterOptionsWrapper
|
|
1321
|
-
) as unknown as Promise<
|
|
1411
|
+
) as unknown as Promise<Collection>;
|
|
1322
1412
|
|
|
1323
|
-
|
|
1413
|
+
if (DEPRECATE_PROMISE_PROXIES) {
|
|
1414
|
+
return promiseArray(queryPromise);
|
|
1415
|
+
}
|
|
1416
|
+
return queryPromise;
|
|
1324
1417
|
}
|
|
1325
1418
|
|
|
1326
1419
|
/**
|
|
@@ -1421,7 +1514,11 @@ class Store extends Service {
|
|
|
1421
1514
|
@param {Object} options optional, may include `adapterOptions` hash which will be passed to adapter.queryRecord
|
|
1422
1515
|
@return {Promise} promise which resolves with the found record or `null`
|
|
1423
1516
|
*/
|
|
1424
|
-
queryRecord(
|
|
1517
|
+
queryRecord(
|
|
1518
|
+
modelName: string,
|
|
1519
|
+
query,
|
|
1520
|
+
options?
|
|
1521
|
+
): PromiseObject<RecordInstance | null> | Promise<RecordInstance | null> {
|
|
1425
1522
|
if (DEBUG) {
|
|
1426
1523
|
assertDestroyingStore(this, 'queryRecord');
|
|
1427
1524
|
}
|
|
@@ -1454,7 +1551,10 @@ class Store extends Service {
|
|
|
1454
1551
|
adapterOptionsWrapper
|
|
1455
1552
|
) as Promise<StableRecordIdentifier | null>;
|
|
1456
1553
|
|
|
1457
|
-
|
|
1554
|
+
if (DEPRECATE_PROMISE_PROXIES) {
|
|
1555
|
+
return promiseObject(promise.then((identifier) => identifier && this.peekRecord(identifier)));
|
|
1556
|
+
}
|
|
1557
|
+
return promise.then((identifier) => identifier && this.peekRecord(identifier));
|
|
1458
1558
|
}
|
|
1459
1559
|
|
|
1460
1560
|
/**
|
|
@@ -1648,7 +1748,7 @@ class Store extends Service {
|
|
|
1648
1748
|
findAll(
|
|
1649
1749
|
modelName: string,
|
|
1650
1750
|
options: { reload?: boolean; backgroundReload?: boolean } = {}
|
|
1651
|
-
): PromiseArray<RecordInstance,
|
|
1751
|
+
): PromiseArray<RecordInstance, IdentifierArray> {
|
|
1652
1752
|
if (DEBUG) {
|
|
1653
1753
|
assertDestroyingStore(this, 'findAll');
|
|
1654
1754
|
}
|
|
@@ -1674,7 +1774,7 @@ class Store extends Service {
|
|
|
1674
1774
|
array.isUpdating = true;
|
|
1675
1775
|
fetch = _findAll(adapter, this, normalizedModelName, options);
|
|
1676
1776
|
} else {
|
|
1677
|
-
let snapshotArray = array
|
|
1777
|
+
let snapshotArray = new SnapshotRecordArray(this, array, options);
|
|
1678
1778
|
|
|
1679
1779
|
if (options.reload !== false) {
|
|
1680
1780
|
if (
|
|
@@ -1682,7 +1782,7 @@ class Store extends Service {
|
|
|
1682
1782
|
(!adapter.shouldReloadAll && snapshotArray.length === 0)
|
|
1683
1783
|
) {
|
|
1684
1784
|
array.isUpdating = true;
|
|
1685
|
-
fetch = _findAll(adapter, this, modelName, options);
|
|
1785
|
+
fetch = _findAll(adapter, this, modelName, options, snapshotArray);
|
|
1686
1786
|
}
|
|
1687
1787
|
}
|
|
1688
1788
|
|
|
@@ -1695,14 +1795,17 @@ class Store extends Service {
|
|
|
1695
1795
|
adapter.shouldBackgroundReloadAll(this, snapshotArray)
|
|
1696
1796
|
) {
|
|
1697
1797
|
array.isUpdating = true;
|
|
1698
|
-
_findAll(adapter, this, modelName, options);
|
|
1798
|
+
_findAll(adapter, this, modelName, options, snapshotArray);
|
|
1699
1799
|
}
|
|
1700
1800
|
|
|
1701
1801
|
fetch = resolve(array);
|
|
1702
1802
|
}
|
|
1703
1803
|
}
|
|
1704
1804
|
|
|
1705
|
-
|
|
1805
|
+
if (DEPRECATE_PROMISE_PROXIES) {
|
|
1806
|
+
return promiseArray(fetch);
|
|
1807
|
+
}
|
|
1808
|
+
return fetch;
|
|
1706
1809
|
}
|
|
1707
1810
|
|
|
1708
1811
|
/**
|
|
@@ -1730,7 +1833,7 @@ class Store extends Service {
|
|
|
1730
1833
|
@param {String} modelName
|
|
1731
1834
|
@return {RecordArray}
|
|
1732
1835
|
*/
|
|
1733
|
-
peekAll(modelName) {
|
|
1836
|
+
peekAll(modelName: string): IdentifierArray {
|
|
1734
1837
|
if (DEBUG) {
|
|
1735
1838
|
assertDestroyingStore(this, 'peekAll');
|
|
1736
1839
|
}
|
|
@@ -1739,8 +1842,9 @@ class Store extends Service {
|
|
|
1739
1842
|
`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${modelName}`,
|
|
1740
1843
|
typeof modelName === 'string'
|
|
1741
1844
|
);
|
|
1742
|
-
|
|
1743
|
-
|
|
1845
|
+
|
|
1846
|
+
let type = normalizeModelName(modelName);
|
|
1847
|
+
return this.recordArrayManager.liveArrayFor(type);
|
|
1744
1848
|
}
|
|
1745
1849
|
|
|
1746
1850
|
/**
|
|
@@ -1767,12 +1871,29 @@ class Store extends Service {
|
|
|
1767
1871
|
!modelName || typeof modelName === 'string'
|
|
1768
1872
|
);
|
|
1769
1873
|
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1874
|
+
this._join(() => {
|
|
1875
|
+
if (modelName === undefined) {
|
|
1876
|
+
// destroy the graph before unloadAll
|
|
1877
|
+
// since then we avoid churning relationships
|
|
1878
|
+
// during unload
|
|
1879
|
+
if (HAS_RECORD_DATA_PACKAGE) {
|
|
1880
|
+
const peekGraph = (
|
|
1881
|
+
importSync('@ember-data/record-data/-private') as typeof import('@ember-data/record-data/-private')
|
|
1882
|
+
).peekGraph;
|
|
1883
|
+
let graph = peekGraph(this);
|
|
1884
|
+
if (graph) {
|
|
1885
|
+
graph.identifiers.clear();
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
this._notificationManager.destroy();
|
|
1889
|
+
|
|
1890
|
+
this.recordArrayManager.clear();
|
|
1891
|
+
this._instanceCache.clear();
|
|
1892
|
+
} else {
|
|
1893
|
+
let normalizedModelName = normalizeModelName(modelName);
|
|
1894
|
+
this._instanceCache.clear(normalizedModelName);
|
|
1895
|
+
}
|
|
1896
|
+
});
|
|
1776
1897
|
}
|
|
1777
1898
|
|
|
1778
1899
|
/**
|
|
@@ -1960,7 +2081,8 @@ class Store extends Service {
|
|
|
1960
2081
|
if (DEBUG) {
|
|
1961
2082
|
assertDestroyingStore(this, '_push');
|
|
1962
2083
|
}
|
|
1963
|
-
let
|
|
2084
|
+
let ret;
|
|
2085
|
+
this._join(() => {
|
|
1964
2086
|
let included = jsonApiDoc.included;
|
|
1965
2087
|
let i, length;
|
|
1966
2088
|
|
|
@@ -1977,11 +2099,13 @@ class Store extends Service {
|
|
|
1977
2099
|
for (i = 0; i < length; i++) {
|
|
1978
2100
|
identifiers[i] = this._instanceCache.loadData(jsonApiDoc.data[i]);
|
|
1979
2101
|
}
|
|
1980
|
-
|
|
2102
|
+
ret = identifiers;
|
|
2103
|
+
return;
|
|
1981
2104
|
}
|
|
1982
2105
|
|
|
1983
2106
|
if (jsonApiDoc.data === null) {
|
|
1984
|
-
|
|
2107
|
+
ret = null;
|
|
2108
|
+
return;
|
|
1985
2109
|
}
|
|
1986
2110
|
|
|
1987
2111
|
assert(
|
|
@@ -1991,11 +2115,11 @@ class Store extends Service {
|
|
|
1991
2115
|
typeof jsonApiDoc.data === 'object'
|
|
1992
2116
|
);
|
|
1993
2117
|
|
|
1994
|
-
|
|
2118
|
+
ret = this._instanceCache.loadData(jsonApiDoc.data);
|
|
2119
|
+
return;
|
|
1995
2120
|
});
|
|
1996
2121
|
|
|
1997
|
-
|
|
1998
|
-
return identifiers;
|
|
2122
|
+
return ret;
|
|
1999
2123
|
}
|
|
2000
2124
|
|
|
2001
2125
|
/**
|
|
@@ -2090,7 +2214,15 @@ class Store extends Service {
|
|
|
2090
2214
|
return this._instanceCache.createSnapshot(recordIdentifierFor(record)).serialize(options);
|
|
2091
2215
|
}
|
|
2092
2216
|
|
|
2093
|
-
|
|
2217
|
+
/**
|
|
2218
|
+
* Trigger a save for a Record.
|
|
2219
|
+
*
|
|
2220
|
+
* @method saveRecord
|
|
2221
|
+
* @public
|
|
2222
|
+
* @param {RecordInstance} record
|
|
2223
|
+
* @param options
|
|
2224
|
+
* @returns {Promise<RecordInstance>}
|
|
2225
|
+
*/
|
|
2094
2226
|
saveRecord(record: RecordInstance, options: Dict<unknown> = {}): Promise<RecordInstance> {
|
|
2095
2227
|
assert(`Unable to initate save for a record in a disconnected state`, storeFor(record));
|
|
2096
2228
|
let identifier = recordIdentifierFor(record);
|
|
@@ -2110,7 +2242,7 @@ class Store extends Service {
|
|
|
2110
2242
|
return resolve(record);
|
|
2111
2243
|
}
|
|
2112
2244
|
|
|
2113
|
-
recordData.willCommit();
|
|
2245
|
+
recordData.willCommit(identifier);
|
|
2114
2246
|
if (isDSModel(record)) {
|
|
2115
2247
|
record.errors.clear();
|
|
2116
2248
|
}
|
|
@@ -2120,9 +2252,9 @@ class Store extends Service {
|
|
|
2120
2252
|
}
|
|
2121
2253
|
let operation: 'createRecord' | 'deleteRecord' | 'updateRecord' = 'updateRecord';
|
|
2122
2254
|
|
|
2123
|
-
if (recordData.isNew
|
|
2255
|
+
if (recordData.isNew(identifier)) {
|
|
2124
2256
|
operation = 'createRecord';
|
|
2125
|
-
} else if (recordData.isDeleted
|
|
2257
|
+
} else if (recordData.isDeleted(identifier)) {
|
|
2126
2258
|
operation = 'deleteRecord';
|
|
2127
2259
|
}
|
|
2128
2260
|
|
|
@@ -2142,7 +2274,7 @@ class Store extends Service {
|
|
|
2142
2274
|
have an outer run loop available still from the first
|
|
2143
2275
|
call to `store._push`;
|
|
2144
2276
|
*/
|
|
2145
|
-
this.
|
|
2277
|
+
this._join(() => {
|
|
2146
2278
|
if (DEBUG) {
|
|
2147
2279
|
assertDestroyingStore(this, 'saveRecord');
|
|
2148
2280
|
}
|
|
@@ -2163,8 +2295,11 @@ class Store extends Service {
|
|
|
2163
2295
|
|
|
2164
2296
|
//We first make sure the primary data has been updated
|
|
2165
2297
|
const recordData = this._instanceCache.getRecordData(actualIdentifier);
|
|
2166
|
-
recordData.didCommit(data);
|
|
2167
|
-
|
|
2298
|
+
recordData.didCommit(identifier, data);
|
|
2299
|
+
|
|
2300
|
+
if (operation === 'deleteRecord') {
|
|
2301
|
+
this.recordArrayManager.identifierRemoved(actualIdentifier);
|
|
2302
|
+
}
|
|
2168
2303
|
|
|
2169
2304
|
if (payload && payload.included) {
|
|
2170
2305
|
this._push({ data: null, included: payload.included });
|
|
@@ -2189,19 +2324,15 @@ class Store extends Service {
|
|
|
2189
2324
|
* Instantiation hook allowing applications or addons to configure the store
|
|
2190
2325
|
* to utilize a custom RecordData implementation.
|
|
2191
2326
|
*
|
|
2192
|
-
* @method createRecordDataFor
|
|
2327
|
+
* @method createRecordDataFor (hook)
|
|
2193
2328
|
* @public
|
|
2194
|
-
* @param
|
|
2195
|
-
* @param id
|
|
2196
|
-
* @param lid
|
|
2329
|
+
* @param identifier
|
|
2197
2330
|
* @param storeWrapper
|
|
2198
2331
|
*/
|
|
2199
2332
|
createRecordDataFor(
|
|
2200
|
-
|
|
2201
|
-
id: string | null,
|
|
2202
|
-
lid: string,
|
|
2333
|
+
identifier: StableRecordIdentifier,
|
|
2203
2334
|
storeWrapper: RecordDataStoreWrapper
|
|
2204
|
-
): RecordData {
|
|
2335
|
+
): RecordData | RecordDataV1 {
|
|
2205
2336
|
if (HAS_RECORD_DATA_PACKAGE) {
|
|
2206
2337
|
// we can't greedily use require as this causes
|
|
2207
2338
|
// a cycle we can't easily fix (or clearly pin point) at present.
|
|
@@ -2214,12 +2345,30 @@ class Store extends Service {
|
|
|
2214
2345
|
).RecordData;
|
|
2215
2346
|
}
|
|
2216
2347
|
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2348
|
+
if (DEPRECATE_V1CACHE_STORE_APIS && arguments.length === 4) {
|
|
2349
|
+
deprecate(
|
|
2350
|
+
`Store.createRecordDataFor(<type>, <id>, <lid>, <storeWrapper>) has been deprecated in favor of Store.createRecordDataFor(<identifier>, <storeWrapper>)`,
|
|
2351
|
+
false,
|
|
2352
|
+
{
|
|
2353
|
+
id: 'ember-data:deprecate-v1cache-store-apis',
|
|
2354
|
+
for: 'ember-data',
|
|
2355
|
+
until: '5.0',
|
|
2356
|
+
since: { enabled: '4.8', available: '4.8' },
|
|
2357
|
+
}
|
|
2358
|
+
);
|
|
2359
|
+
identifier = this.identifierCache.getOrCreateRecordIdentifier({
|
|
2360
|
+
type: arguments[0],
|
|
2361
|
+
id: arguments[1],
|
|
2362
|
+
lid: arguments[2],
|
|
2363
|
+
});
|
|
2364
|
+
storeWrapper = arguments[3];
|
|
2365
|
+
}
|
|
2366
|
+
|
|
2367
|
+
this.__private_singleton_recordData = this.__private_singleton_recordData || new _RecordData(storeWrapper);
|
|
2368
|
+
(
|
|
2369
|
+
this.__private_singleton_recordData as RecordData & { createCache(identifier: StableRecordIdentifier): void }
|
|
2370
|
+
).createCache(identifier);
|
|
2371
|
+
return this.__private_singleton_recordData;
|
|
2223
2372
|
}
|
|
2224
2373
|
|
|
2225
2374
|
assert(`Expected store.createRecordDataFor to be implemented but it wasn't`);
|
|
@@ -2424,12 +2573,8 @@ class Store extends Service {
|
|
|
2424
2573
|
willDestroy() {
|
|
2425
2574
|
super.willDestroy();
|
|
2426
2575
|
this.recordArrayManager.destroy();
|
|
2427
|
-
|
|
2428
2576
|
this.identifierCache.destroy();
|
|
2429
2577
|
|
|
2430
|
-
// destroy the graph before unloadAll
|
|
2431
|
-
// since then we avoid churning relationships
|
|
2432
|
-
// during unload
|
|
2433
2578
|
if (HAS_RECORD_DATA_PACKAGE) {
|
|
2434
2579
|
const peekGraph = (
|
|
2435
2580
|
importSync('@ember-data/record-data/-private') as typeof import('@ember-data/record-data/-private')
|
|
@@ -2574,3 +2719,124 @@ function errorsHashToArray(errors): JsonApiValidationError[] {
|
|
|
2574
2719
|
|
|
2575
2720
|
return out;
|
|
2576
2721
|
}
|
|
2722
|
+
|
|
2723
|
+
function normalizeProperties(
|
|
2724
|
+
store: Store,
|
|
2725
|
+
identifier: StableRecordIdentifier,
|
|
2726
|
+
properties?: { [key: string]: unknown },
|
|
2727
|
+
isForV1: boolean = false
|
|
2728
|
+
): { [key: string]: unknown } | undefined {
|
|
2729
|
+
// assert here
|
|
2730
|
+
if (properties !== undefined) {
|
|
2731
|
+
if ('id' in properties) {
|
|
2732
|
+
assert(`expected id to be a string or null`, properties.id !== undefined);
|
|
2733
|
+
}
|
|
2734
|
+
assert(
|
|
2735
|
+
`You passed '${typeof properties}' as properties for record creation instead of an object.`,
|
|
2736
|
+
typeof properties === 'object' && properties !== null
|
|
2737
|
+
);
|
|
2738
|
+
|
|
2739
|
+
const { type } = identifier;
|
|
2740
|
+
|
|
2741
|
+
// convert relationship Records to RecordDatas before passing to RecordData
|
|
2742
|
+
let defs = store.getSchemaDefinitionService().relationshipsDefinitionFor({ type });
|
|
2743
|
+
|
|
2744
|
+
if (defs !== null) {
|
|
2745
|
+
let keys = Object.keys(properties);
|
|
2746
|
+
let relationshipValue;
|
|
2747
|
+
|
|
2748
|
+
for (let i = 0; i < keys.length; i++) {
|
|
2749
|
+
let prop = keys[i];
|
|
2750
|
+
let def = defs[prop];
|
|
2751
|
+
|
|
2752
|
+
if (def !== undefined) {
|
|
2753
|
+
if (def.kind === 'hasMany') {
|
|
2754
|
+
if (DEBUG) {
|
|
2755
|
+
assertRecordsPassedToHasMany(properties[prop] as RecordInstance[]);
|
|
2756
|
+
}
|
|
2757
|
+
relationshipValue = extractIdentifiersFromRecords(properties[prop] as RecordInstance[], isForV1);
|
|
2758
|
+
} else {
|
|
2759
|
+
relationshipValue = extractIdentifierFromRecord(properties[prop] as RecordInstance, isForV1);
|
|
2760
|
+
}
|
|
2761
|
+
|
|
2762
|
+
properties[prop] = relationshipValue;
|
|
2763
|
+
}
|
|
2764
|
+
}
|
|
2765
|
+
}
|
|
2766
|
+
}
|
|
2767
|
+
return properties;
|
|
2768
|
+
}
|
|
2769
|
+
|
|
2770
|
+
function assertRecordsPassedToHasMany(records: RecordInstance[]) {
|
|
2771
|
+
assert(`You must pass an array of records to set a hasMany relationship`, Array.isArray(records));
|
|
2772
|
+
assert(
|
|
2773
|
+
`All elements of a hasMany relationship must be instances of Model, you passed ${records
|
|
2774
|
+
.map((r) => `${typeof r}`)
|
|
2775
|
+
.join(', ')}`,
|
|
2776
|
+
(function () {
|
|
2777
|
+
return records.every((record) => {
|
|
2778
|
+
try {
|
|
2779
|
+
recordIdentifierFor(record);
|
|
2780
|
+
return true;
|
|
2781
|
+
} catch {
|
|
2782
|
+
return false;
|
|
2783
|
+
}
|
|
2784
|
+
});
|
|
2785
|
+
})()
|
|
2786
|
+
);
|
|
2787
|
+
}
|
|
2788
|
+
|
|
2789
|
+
function extractIdentifiersFromRecords(records: RecordInstance[], isForV1: boolean = false): StableRecordIdentifier[] {
|
|
2790
|
+
return records.map((record) => extractIdentifierFromRecord(record, isForV1)) as StableRecordIdentifier[];
|
|
2791
|
+
}
|
|
2792
|
+
|
|
2793
|
+
type PromiseProxyRecord = { then(): void; content: RecordInstance | null | undefined };
|
|
2794
|
+
|
|
2795
|
+
function extractIdentifierFromRecord(
|
|
2796
|
+
recordOrPromiseRecord: PromiseProxyRecord | RecordInstance | null,
|
|
2797
|
+
isForV1: boolean = false
|
|
2798
|
+
) {
|
|
2799
|
+
if (!recordOrPromiseRecord) {
|
|
2800
|
+
return null;
|
|
2801
|
+
}
|
|
2802
|
+
const extract = isForV1 ? recordDataFor : recordIdentifierFor;
|
|
2803
|
+
|
|
2804
|
+
if (DEPRECATE_PROMISE_PROXIES && isPromiseRecord(recordOrPromiseRecord)) {
|
|
2805
|
+
let content = recordOrPromiseRecord.content;
|
|
2806
|
+
assert(
|
|
2807
|
+
'You passed in a promise that did not originate from an EmberData relationship. You can only pass promises that come from a belongsTo or hasMany relationship to the get call.',
|
|
2808
|
+
content !== undefined
|
|
2809
|
+
);
|
|
2810
|
+
deprecate(
|
|
2811
|
+
`You passed in a PromiseProxy to a Relationship API that now expects a resolved value. await the value before setting it.`,
|
|
2812
|
+
false,
|
|
2813
|
+
{
|
|
2814
|
+
id: 'ember-data:deprecate-promise-proxies',
|
|
2815
|
+
until: '5.0',
|
|
2816
|
+
since: {
|
|
2817
|
+
enabled: '4.8',
|
|
2818
|
+
available: '4.8',
|
|
2819
|
+
},
|
|
2820
|
+
for: 'ember-data',
|
|
2821
|
+
}
|
|
2822
|
+
);
|
|
2823
|
+
return content ? extract(content) : null;
|
|
2824
|
+
}
|
|
2825
|
+
|
|
2826
|
+
return extract(recordOrPromiseRecord);
|
|
2827
|
+
}
|
|
2828
|
+
|
|
2829
|
+
function isPromiseRecord(record: PromiseProxyRecord | RecordInstance): record is PromiseProxyRecord {
|
|
2830
|
+
return !!record.then;
|
|
2831
|
+
}
|
|
2832
|
+
|
|
2833
|
+
function secretInit(
|
|
2834
|
+
record: RecordInstance,
|
|
2835
|
+
recordData: RecordData,
|
|
2836
|
+
identifier: StableRecordIdentifier,
|
|
2837
|
+
store: Store
|
|
2838
|
+
): void {
|
|
2839
|
+
setRecordIdentifier(record, identifier);
|
|
2840
|
+
StoreMap.set(record, store);
|
|
2841
|
+
setRecordDataFor(record, recordData);
|
|
2842
|
+
}
|