@ember-data/store 4.4.0 → 4.5.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/addon/-private/system/core-store.ts +136 -147
  2. package/addon/-private/system/ds-model-store.ts +1 -10
  3. package/addon/-private/system/fetch-manager.ts +48 -21
  4. package/addon/-private/system/model/internal-model.ts +263 -192
  5. package/addon/-private/system/model/states.js +5 -41
  6. package/addon/-private/system/{promise-proxies.js → promise-proxies.ts} +31 -21
  7. package/addon/-private/system/promise-proxy-base.js +7 -0
  8. package/addon/-private/system/{record-array-manager.js → record-array-manager.ts} +87 -60
  9. package/addon/-private/system/record-arrays/adapter-populated-record-array.ts +129 -0
  10. package/addon/-private/system/record-arrays/{record-array.js → record-array.ts} +96 -75
  11. package/addon/-private/system/record-data-for.ts +2 -0
  12. package/addon/-private/system/references/belongs-to.ts +3 -2
  13. package/addon/-private/system/references/has-many.ts +4 -2
  14. package/addon/-private/system/schema-definition-service.ts +2 -2
  15. package/addon/-private/system/snapshot-record-array.ts +12 -11
  16. package/addon/-private/system/snapshot.ts +24 -7
  17. package/addon/-private/system/store/common.js +24 -1
  18. package/addon/-private/system/store/finders.js +53 -5
  19. package/addon/-private/system/store/internal-model-factory.ts +8 -7
  20. package/addon/-private/system/store/record-data-store-wrapper.ts +7 -2
  21. package/addon/-private/system/store/serializer-response.ts +85 -0
  22. package/addon/-private/ts-interfaces/ds-model.ts +15 -7
  23. package/addon/-private/ts-interfaces/ember-data-json-api.ts +3 -0
  24. package/addon/-private/ts-interfaces/minimum-adapter-interface.ts +19 -20
  25. package/addon/-private/ts-interfaces/minimum-serializer-interface.ts +27 -6
  26. package/addon/-private/ts-interfaces/record-data.ts +4 -1
  27. package/addon/-private/ts-interfaces/record-instance.ts +3 -1
  28. package/addon/-private/ts-interfaces/store.ts +1 -0
  29. package/addon/-private/utils/promise-record.ts +3 -3
  30. package/index.js +3 -0
  31. package/package.json +7 -6
  32. package/addon/-private/system/record-arrays/adapter-populated-record-array.js +0 -95
  33. package/addon/-private/system/store/serializer-response.js +0 -71
@@ -7,9 +7,8 @@ import { DEBUG } from '@glimmer/env';
7
7
  import type DSModelClass from '@ember-data/model';
8
8
 
9
9
  import type { DSModel } from '../ts-interfaces/ds-model';
10
- import type { RecordIdentifier, StableRecordIdentifier } from '../ts-interfaces/identifier';
10
+ import type { StableRecordIdentifier } from '../ts-interfaces/identifier';
11
11
  import type { RecordDataRecordWrapper } from '../ts-interfaces/record-data-record-wrapper';
12
- import type { RelationshipsSchema } from '../ts-interfaces/record-data-schemas';
13
12
  import type { SchemaDefinitionService } from '../ts-interfaces/schema-definition-service';
14
13
  import CoreStore from './core-store';
15
14
  import type ShimModelClass from './model/shim-model-class';
@@ -108,14 +107,6 @@ class Store extends CoreStore {
108
107
  return this._relationshipsDefinitionFor({ type: modelName })[key];
109
108
  }
110
109
 
111
- _attributesDefinitionFor(identifier: RecordIdentifier | { type: string }) {
112
- return this.getSchemaDefinitionService().attributesDefinitionFor(identifier);
113
- }
114
-
115
- _relationshipsDefinitionFor(identifier: RecordIdentifier | { type: string }): RelationshipsSchema {
116
- return this.getSchemaDefinitionService().relationshipsDefinitionFor(identifier);
117
- }
118
-
119
110
  getSchemaDefinitionService(): SchemaDefinitionService {
120
111
  if (!this._schemaDefinitionService) {
121
112
  this._schemaDefinitionService = new DSModelSchemaDefinitionService(this);
@@ -2,19 +2,24 @@
2
2
  * @module @ember-data/store
3
3
  */
4
4
  import { A } from '@ember/array';
5
- import { assert, warn } from '@ember/debug';
5
+ import { assert, deprecate, warn } from '@ember/debug';
6
6
  import { _backburner as emberBackburner } from '@ember/runloop';
7
7
  import { DEBUG } from '@glimmer/env';
8
8
 
9
- import { default as RSVP, Promise } from 'rsvp';
9
+ import { default as RSVP, resolve } from 'rsvp';
10
+
11
+ import { DEPRECATE_RSVP_PROMISE } from '@ember-data/private-build-infra/deprecations';
10
12
 
11
13
  import type { CollectionResourceDocument, SingleResourceDocument } from '../ts-interfaces/ember-data-json-api';
12
14
  import type { FindRecordQuery, Request, SaveRecordMutation } from '../ts-interfaces/fetch-manager';
13
15
  import type { ExistingRecordIdentifier, RecordIdentifier, StableRecordIdentifier } from '../ts-interfaces/identifier';
16
+ import type { MinimumSerializerInterface } from '../ts-interfaces/minimum-serializer-interface';
17
+ import { FindOptions } from '../ts-interfaces/store';
14
18
  import type { Dict } from '../ts-interfaces/utils';
15
19
  import coerceId from './coerce-id';
16
20
  import type CoreStore from './core-store';
17
21
  import { errorsArrayToHash } from './errors-utils';
22
+ import ShimModelClass from './model/shim-model-class';
18
23
  import RequestCache, { RequestPromise } from './request-cache';
19
24
  import type { PrivateSnapshot } from './snapshot';
20
25
  import Snapshot from './snapshot';
@@ -30,8 +35,15 @@ function payloadIsNotBlank(adapterPayload): boolean {
30
35
  }
31
36
  }
32
37
 
38
+ type AdapterErrors = Error & { errors?: string[]; isAdapterError?: true };
39
+ type SerializerWithParseErrors = MinimumSerializerInterface & {
40
+ extractErrors?(store: CoreStore, modelClass: ShimModelClass, error: AdapterErrors, recordId: string | null): any;
41
+ };
42
+
33
43
  export const SaveOp: unique symbol = Symbol('SaveOp');
34
44
 
45
+ export type FetchMutationOptions = FindOptions & { [SaveOp]: 'createRecord' | 'deleteRecord' | 'updateRecord' };
46
+
35
47
  interface PendingFetchItem {
36
48
  identifier: ExistingRecordIdentifier;
37
49
  queryRequest: Request;
@@ -44,7 +56,7 @@ interface PendingSaveItem {
44
56
  resolver: RSVP.Deferred<any>;
45
57
  snapshot: Snapshot;
46
58
  identifier: RecordIdentifier;
47
- options: { [k: string]: unknown; [SaveOp]: 'createRecord' | 'saveRecord' | 'updateRecord' };
59
+ options: FetchMutationOptions;
48
60
  queryRequest: Request;
49
61
  }
50
62
 
@@ -78,7 +90,7 @@ export default class FetchManager {
78
90
 
79
91
  @internal
80
92
  */
81
- scheduleSave(identifier: RecordIdentifier, options: any = {}): RSVP.Promise<null | SingleResourceDocument> {
93
+ scheduleSave(identifier: RecordIdentifier, options: FetchMutationOptions): Promise<null | SingleResourceDocument> {
82
94
  let promiseLabel = 'DS: Model#save ' + this;
83
95
  let resolver = RSVP.defer<null | SingleResourceDocument>(promiseLabel);
84
96
  let query: SaveRecordMutation = {
@@ -125,8 +137,8 @@ export default class FetchManager {
125
137
  typeof adapter[operation] === 'function'
126
138
  );
127
139
 
128
- let promise = Promise.resolve().then(() => adapter[operation](store, modelClass, snapshot));
129
- let serializer = store.serializerFor(modelName);
140
+ let promise = resolve().then(() => adapter[operation](store, modelClass, snapshot));
141
+ let serializer: SerializerWithParseErrors | null = store.serializerFor(modelName);
130
142
  let label = `DS: Extract and notify about ${operation} completion of ${internalModel}`;
131
143
 
132
144
  assert(
@@ -134,11 +146,26 @@ export default class FetchManager {
134
146
  promise !== undefined
135
147
  );
136
148
 
137
- promise = guardDestroyedStore(promise, store, label);
138
- promise = _guard(promise, _bind(_objectIsAlive, internalModel));
139
-
140
- promise = promise.then(
149
+ promise = _guard(guardDestroyedStore(promise, store, label), _bind(_objectIsAlive, internalModel)).then(
141
150
  (adapterPayload) => {
151
+ if (!_objectIsAlive(internalModel)) {
152
+ if (DEPRECATE_RSVP_PROMISE) {
153
+ deprecate(
154
+ `A Promise while saving ${modelName} did not resolve by the time your model was destroyed. This will error in a future release.`,
155
+ false,
156
+ {
157
+ id: 'ember-data:rsvp-unresolved-async',
158
+ until: '5.0',
159
+ for: '@ember-data/store',
160
+ since: {
161
+ available: '4.5',
162
+ enabled: '4.5',
163
+ },
164
+ }
165
+ );
166
+ }
167
+ }
168
+
142
169
  if (adapterPayload) {
143
170
  return normalizeResponseHelper(serializer, store, modelClass, adapterPayload, snapshot.id, operation);
144
171
  }
@@ -147,7 +174,8 @@ export default class FetchManager {
147
174
  if (error && error.isAdapterError === true && error.code === 'InvalidError') {
148
175
  let parsedErrors = error.errors;
149
176
 
150
- if (typeof serializer.extractErrors === 'function') {
177
+ // TODO deprecate extractErrors being called and/or make it part of the public interface
178
+ if (serializer && typeof serializer.extractErrors === 'function') {
151
179
  parsedErrors = serializer.extractErrors(store, modelClass, error, snapshot.id);
152
180
  } else {
153
181
  parsedErrors = errorsArrayToHash(error.errors);
@@ -179,7 +207,7 @@ export default class FetchManager {
179
207
  }
180
208
  }
181
209
 
182
- scheduleFetch(identifier: ExistingRecordIdentifier, options: any, shouldTrace: boolean): RSVP.Promise<any> {
210
+ scheduleFetch(identifier: ExistingRecordIdentifier, options: any, shouldTrace: boolean): Promise<any> {
183
211
  // TODO Probably the store should pass in the query object
184
212
 
185
213
  let query: FindRecordQuery = {
@@ -262,17 +290,16 @@ export default class FetchManager {
262
290
 
263
291
  let snapshot = new Snapshot(fetchItem.options, identifier, this._store);
264
292
  let klass = this._store.modelFor(identifier.type);
265
-
266
- let promise = Promise.resolve().then(() => {
267
- return adapter.findRecord(this._store, klass, identifier.id, snapshot);
268
- });
269
-
270
293
  let id = identifier.id;
271
-
272
294
  let label = `DS: Handle Adapter#findRecord of '${modelName}' with id: '${id}'`;
273
295
 
274
- promise = guardDestroyedStore(promise, this._store, label);
275
- promise = promise.then(
296
+ let promise = guardDestroyedStore(
297
+ resolve().then(() => {
298
+ return adapter.findRecord(this._store, klass, identifier.id, snapshot);
299
+ }),
300
+ this._store,
301
+ label
302
+ ).then(
276
303
  (adapterPayload) => {
277
304
  assert(
278
305
  `You made a 'findRecord' request for a '${modelName}' with id '${id}', but the adapter's response did not have any data`,
@@ -487,7 +514,7 @@ export default class FetchManager {
487
514
 
488
515
  let groups: Snapshot[][];
489
516
  if (adapter.groupRecordsForFindMany) {
490
- groups = adapter.groupRecordsForFindMany(this, snapshots);
517
+ groups = adapter.groupRecordsForFindMany(this._store, snapshots);
491
518
  } else {
492
519
  groups = [snapshots];
493
520
  }