@ember-data/store 4.12.0-alpha.18 → 4.12.0-alpha.19

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/README.md CHANGED
@@ -19,7 +19,8 @@
19
19
 
20
20
  This package provides [*Ember***Data**](https://github.com/emberjs/data/)'s `Store` class.
21
21
 
22
- The `Store` coordinates interaction between your application, the `Cache`, and sources of data (such as your `API` or a local persistence layer).
22
+ The `Store` coordinates interaction between your application, a [Cache](https://api.emberjs.com/ember-data/release/classes/%3CInterface%3E%20Cache),
23
+ and sources of data (such as your API or a local persistence layer) accessed via a [RequestManager](https://github.com/emberjs/data/tree/main/packages/request).
23
24
 
24
25
  ```mermaid
25
26
  flowchart LR
@@ -57,15 +58,15 @@ After installing you will want to configure your first `Store`. Read more below
57
58
 
58
59
  ## 🔨 Creating A Store
59
60
 
60
- To use a `Store` we will need to do few things: add a `Cache` to store data **in-memory**, add an `Adapter` to fetch data from a source, and implement `instantiateRecord` to tell the store how to display the data for individual resources.
61
+ To use a `Store` we will need to do few things: add a [Cache](https://api.emberjs.com/ember-data/release/classes/%3CInterface%3E%20Cache) to store data **in-memory**, add a [Handler](https://github.com/emberjs/data/tree/main/packages/request#handling-requests) to fetch data from a source, and implement `instantiateRecord` to tell the store how to display the data for individual resources.
61
62
 
62
63
  > **Note** If you are using the package `ember-data` then a `JSON:API` cache and `instantiateRecord` are configured for you by default.
63
64
 
64
65
  ### Configuring A Cache
65
66
 
66
- To start, let's install a `JSON:API` cache. If your app uses `GraphQL` or `REST` other caches may better fit your data. You can author your own cache by creating one that conforms to the [spec]().
67
+ To start, let's install a [JSON:API](https://jsonapi.org/) cache. If your app uses `GraphQL` or `REST` other caches may better fit your data. You can author your own cache by creating one that conforms to the [spec](https://api.emberjs.com/ember-data/release/classes/%3CInterface%3E%20Cache).
67
68
 
68
- The package `@ember-data/json-api` provides a `JSON:API` cache we can use. After installing it, we can configure the store to use this cache.
69
+ The package [@ember-data/json-api](https://github.com/emberjs/data/tree/main/packages/json-api) provides a [JSON:API](https://jsonapi.org/) cache we can use. After installing it, we can configure the store to use this cache.
69
70
 
70
71
  ```js
71
72
  import Store from '@ember-data/store';
@@ -80,9 +81,7 @@ class extends Store {
80
81
 
81
82
  Now that we have a `cache` let's setup something to handle fetching and saving data via our API.
82
83
 
83
- > **Note** [1] the cache from `@ember-data/json-api` is a special cache: if the package is present the `createCache` hook will automatically do the above wiring if the hook is not implemented. We still recommend implementing the hook.
84
- >
85
- > **Note** [2] The `ember-data` package automatically includes the `@ember-data/json-api` cache for you.
84
+ > **Note** The `ember-data` package automatically includes and configures the `@ember-data/json-api` cache for you.
86
85
 
87
86
  ### Handling Requests
88
87
 
@@ -93,7 +92,7 @@ To start, let's install the `FetchManager` from `@ember-data/request` and the ba
93
92
  > **Note** If your app uses `GraphQL`, `REST` or different conventions for `JSON:API` than your cache expects, other handlers may better fit your data. You can author your own handler by creating one that conforms to the [handler interface](https://github.com/emberjs/data/tree/main/packages/request#handling-requests).
94
93
 
95
94
  ```ts
96
- import Store from '@ember-data/store';
95
+ import Store, { CacheHandler } from '@ember-data/store';
97
96
  import RequestManager from '@ember-data/request';
98
97
  import Fetch from '@ember-data/request/fetch';
99
98
 
@@ -102,6 +101,7 @@ export default class extends Store {
102
101
  super(...arguments);
103
102
  this.requestManager = new RequestManager();
104
103
  this.requestManager.use([Fetch]);
104
+ this.requestManager.useCache(CacheHandler);
105
105
  }
106
106
  }
107
107
  ```
@@ -113,12 +113,14 @@ Alternatively if you have configured the `RequestManager` to be a service you ma
113
113
  *app/services/request.js*
114
114
  ```ts
115
115
  import RequestManager from '@ember-data/request';
116
+ import { CacheHandler } from '@ember-data/store';
116
117
  import Fetch from '@ember-data/request/fetch';
117
118
 
118
119
  export default class extends RequestManager {
119
120
  constructor(createArgs) {
120
121
  super(createArgs);
121
122
  this.use([Fetch]);
123
+ this.useCache(CacheHandler);
122
124
  }
123
125
  }
124
126
  ```
@@ -135,7 +137,8 @@ export default class extends Store {
135
137
 
136
138
  ### Presenting Data from the Cache
137
139
 
138
- Now that we have a source and a cach for our data, we need to configure how the Store delivers that data back to our application. We do this via the hook `instantiateRecord`, which allows us to transform the data for a resource before handing it to the application.
140
+ Now that we have a source and a cach for our data, we need to configure how the Store delivers that data back to our application. We do this via the hook [instantiateRecord](https://api.emberjs.com/ember-data/release/classes/Store/methods/instantiateRecord%20(hook)?anchor=instantiateRecord%20(hook)),
141
+ which allows us to transform the data for a resource before handing it to the application.
139
142
 
140
143
  A naive way to present the data would be to return it as JSON. Typically instead this hook will be used to add reactivity and make each unique resource a singleton, ensuring that if the cache updates our presented data will reflect the new state.
141
144
 
@@ -182,6 +185,5 @@ Typically you will choose an existing record implementation such as `@ember-data
182
185
 
183
186
  Because of the boundaries around instantiation and the cache, record implementations should be capable of interop both with each other and with any `Cache`. Due to this, if needed an application can utilize multiple record implementations and multiple cache implementations either to support enhanced features for only a subset of records or to be able to incrementally migrate from one record/cache to another record or cache.
184
187
 
185
- > Note: [1] `@ember-data/model` is a special record implementation: currently, if the package is present the `instantiateRecord` hook will automatically do the above wiring if the hook is not implemented.
186
- >
187
- > Note: [2] The `ember-data` package automatically includes the `@ember-data/model` implementation for you.
188
+ > **Note:** The `ember-data` package automatically includes the `@ember-data/model`
189
+ > package and configures it for you.
package/addon/-private.js CHANGED
@@ -1 +1 @@
1
- export { f as AdapterPopulatedRecordArray, C as CacheHandler, j as IDENTIFIER_ARRAY_TAG, I as IdentifierArray, M as MUTATE, I as RecordArray, R as RecordArrayManager, h as SOURCE, S as Store, _ as _clearCaches, e as coerceId, k as fastPush, i as isStableIdentifier, n as normalizeModelName, g as notifyArray, p as peekCache, r as recordIdentifierFor, l as removeRecordDataFor, c as setIdentifierForgetMethod, a as setIdentifierGenerationMethod, d as setIdentifierResetMethod, b as setIdentifierUpdateMethod, s as storeFor } from "./index-0d52354f";
1
+ export { f as AdapterPopulatedRecordArray, C as CacheHandler, j as IDENTIFIER_ARRAY_TAG, I as IdentifierArray, M as MUTATE, I as RecordArray, R as RecordArrayManager, h as SOURCE, S as Store, _ as _clearCaches, e as coerceId, k as fastPush, i as isStableIdentifier, n as normalizeModelName, g as notifyArray, p as peekCache, r as recordIdentifierFor, l as removeRecordDataFor, c as setIdentifierForgetMethod, a as setIdentifierGenerationMethod, d as setIdentifierResetMethod, b as setIdentifierUpdateMethod, s as storeFor } from "./index-44832181";
@@ -907,16 +907,12 @@ function unsubscribe(token) {
907
907
  }
908
908
  }
909
909
 
910
- /*
911
- Currently only support a single callback per identifier
912
- */
913
-
914
910
  /**
915
911
  * The NotificationManager provides the ability to subscribe to
916
912
  * changes to Cache state.
917
913
  *
918
914
  * This Feature is what allows EmberData to create subscriptions that
919
- * work with any framework or change notification system.
915
+ * work with any framework or change-notification system.
920
916
  *
921
917
  * @class NotificationManager
922
918
  * @public
@@ -930,20 +926,30 @@ class NotificationManager {
930
926
  }
931
927
 
932
928
  /**
933
- * Subscribe to changes for a given resource identifier
929
+ * Subscribe to changes for a given resource identifier, resource addition/removal, or document addition/removal.
934
930
  *
935
931
  * ```ts
936
- * interface NotificationCallback {
932
+ * export type CacheOperation = 'added' | 'removed' | 'updated' | 'state';
933
+ *
934
+ * export interface NotificationCallback {
937
935
  * (identifier: StableRecordIdentifier, notificationType: 'attributes' | 'relationships', key?: string): void;
938
936
  * (identifier: StableRecordIdentifier, notificationType: 'errors' | 'meta' | 'identity' | 'state'): void;
939
937
  * (identifier: StableRecordIdentifier, notificationType: NotificationType, key?: string): void;
940
938
  * }
939
+ * export interface ResourceOperationCallback {
940
+ * // resource updates
941
+ * (identifier: StableRecordIdentifier, notificationType: CacheOperation): void;
942
+ * }
943
+ * export interface DocumentOperationCallback {
944
+ * // document updates
945
+ * (identifier: StableDocumentIdentifier, notificationType: CacheOperation): void;
946
+ * }
941
947
  * ```
942
948
  *
943
949
  * @method subscribe
944
950
  * @public
945
- * @param {StableRecordIdentifier} identifier
946
- * @param {NotificationCallback} callback
951
+ * @param {StableDocumentIdentifier | StableRecordIdentifier | 'resource' | 'document'} identifier
952
+ * @param {NotificationCallback | ResourceOperationCallback | DocumentOperationCallback} callback
947
953
  * @returns {UnsubscribeToken} an opaque token to be used with unsubscribe
948
954
  */
949
955
 
@@ -1397,7 +1403,6 @@ class NonSingletonCacheManager {
1397
1403
  }
1398
1404
  // Cache Management
1399
1405
  // ================
1400
-
1401
1406
  /**
1402
1407
  * Cache the response to a request
1403
1408
  *
@@ -3498,7 +3503,6 @@ var _dec, _class$1, _descriptor$1;
3498
3503
  @extends Ember.ArrayProxy
3499
3504
  @uses Ember.PromiseProxyMixin
3500
3505
  */
3501
-
3502
3506
  let PromiseArray = (_dec = reads('content.meta'), (_class$1 = class PromiseArray extends PromiseArrayProxy {
3503
3507
  constructor(...args) {
3504
3508
  super(...args);
@@ -3638,6 +3642,10 @@ function convertToInt(prop) {
3638
3642
  return num % 1 === 0 ? num : null;
3639
3643
  }
3640
3644
  let Tag = (_class = class Tag {
3645
+ /*
3646
+ * whether this was part of a transaction when last mutated
3647
+ */
3648
+
3641
3649
  constructor() {
3642
3650
  _initializerDefineProperty(this, "ref", _descriptor, this);
3643
3651
  this.shouldReset = false;
@@ -3712,6 +3720,22 @@ let IdentifierArray = (_class3 = class IdentifierArray {
3712
3720
  [NOTIFY]() {
3713
3721
  notifyArray(this);
3714
3722
  }
3723
+
3724
+ /**
3725
+ The modelClass represented by this record array.
3726
+ @property type
3727
+ @public
3728
+ @deprecated
3729
+ @type {subclass of Model}
3730
+ */
3731
+
3732
+ /**
3733
+ The store that created this record array.
3734
+ @property store
3735
+ @private
3736
+ @type Store
3737
+ */
3738
+
3715
3739
  destroy() {
3716
3740
  this.isDestroying = true;
3717
3741
  // changing the reference breaks the Proxy
@@ -4710,12 +4734,23 @@ function sync(array, changes) {
4710
4734
  }
4711
4735
  }
4712
4736
 
4737
+ /**
4738
+ * @module @ember-data/store
4739
+ */
4713
4740
  const Touching = Symbol('touching');
4714
4741
  const RequestPromise = Symbol('promise');
4715
4742
  function hasRecordIdentifier(op) {
4716
4743
  return 'recordIdentifier' in op;
4717
4744
  }
4718
- class RequestCache {
4745
+
4746
+ /**
4747
+ * The RequestStateService is used to track the state of requests
4748
+ * for fetching or updating known resource identifies that are inflight.
4749
+ *
4750
+ * @class RequestStateService
4751
+ * @public
4752
+ */
4753
+ class RequestStateService {
4719
4754
  constructor(store) {
4720
4755
  this._pending = Object.create(null);
4721
4756
  this._done = new Map();
@@ -4727,7 +4762,7 @@ class RequestCache {
4727
4762
  _clearEntries(identifier) {
4728
4763
  this._done.delete(identifier);
4729
4764
  }
4730
- enqueue(promise, queryRequest) {
4765
+ _enqueue(promise, queryRequest) {
4731
4766
  let query = queryRequest.data[0];
4732
4767
  if (hasRecordIdentifier(query)) {
4733
4768
  let lid = query.recordIdentifier.lid;
@@ -4826,18 +4861,66 @@ class RequestCache {
4826
4861
  this._done.set(identifier, requests);
4827
4862
  });
4828
4863
  }
4864
+
4865
+ /**
4866
+ * Subscribe to requests for a given resource identity.
4867
+ *
4868
+ * The callback will receive the current state of the request.
4869
+ *
4870
+ * ```ts
4871
+ * interface RequestState {
4872
+ * state: 'pending' | 'fulfilled' | 'rejected';
4873
+ * type: 'query' | 'mutation';
4874
+ * request: Request;
4875
+ * response?: { data: unknown };
4876
+ * }
4877
+ * ```
4878
+ *
4879
+ * Note: It should be considered dangerous to use this API for more than simple
4880
+ * state derivation or debugging. The `request` and `response` properties are poorly
4881
+ * spec'd and may change unexpectedly when shifting what Handlers are in use or how
4882
+ * requests are issued from the Store.
4883
+ *
4884
+ * We expect to revisit this API in the near future as we continue to refine the
4885
+ * RequestManager ergonomics, as a simpler but more powerful direct integration
4886
+ * with the RequestManager for these purposes is likely to be a better long-term
4887
+ * design.
4888
+ *
4889
+ * @method subscribeForRecord
4890
+ * @public
4891
+ * @param {StableRecordIdentifier} identifier
4892
+ * @param {(state: RequestState) => void} callback
4893
+ */
4829
4894
  subscribeForRecord(identifier, callback) {
4830
4895
  if (!this._subscriptions[identifier.lid]) {
4831
4896
  this._subscriptions[identifier.lid] = [];
4832
4897
  }
4833
4898
  this._subscriptions[identifier.lid].push(callback);
4834
4899
  }
4900
+
4901
+ /**
4902
+ * Retrieve all active requests for a given resource identity.
4903
+ *
4904
+ * @method getPendingRequestsForRecord
4905
+ * @public
4906
+ * @param {StableRecordIdentifier} identifier
4907
+ * @returns {RequestState[]} an array of request states for any pending requests for the given identifier
4908
+ */
4835
4909
  getPendingRequestsForRecord(identifier) {
4836
4910
  if (this._pending[identifier.lid]) {
4837
4911
  return this._pending[identifier.lid];
4838
4912
  }
4839
4913
  return [];
4840
4914
  }
4915
+
4916
+ /**
4917
+ * Retrieve the last completed request for a given resource identity.
4918
+ *
4919
+ * @method getLastRequestForRecord
4920
+ * @public
4921
+ * @param {StableRecordIdentifier} identifier
4922
+ * @returns {RequestState | null} the state of the most recent request for the given identifier
4923
+ */
4841
4924
  getLastRequestForRecord(identifier) {
4842
4925
  let requests = this._done.get(identifier);
4843
4926
  if (requests) {
@@ -4854,6 +4937,27 @@ class RequestCache {
4854
4937
  // hello world
4855
4938
 
4856
4939
  let _Cache;
4940
+
4941
+ /**
4942
+ * A Store coordinates interaction between your application, a [Cache](https://api.emberjs.com/ember-data/release/classes/%3CInterface%3E%20Cache),
4943
+ * and sources of data (such as your API or a local persistence layer)
4944
+ * accessed via a [RequestManager](https://github.com/emberjs/data/tree/main/packages/request).
4945
+ *
4946
+ * ```app/services/store.js
4947
+ * import Store from '@ember-data/store';
4948
+ *
4949
+ * export default class extends Store {}
4950
+ * ```
4951
+ *
4952
+ * Most Ember applications will only have a single `Store` configured as a Service
4953
+ * in this manner. However, setting up multiple stores is possible, including using
4954
+ * each as a unique service.
4955
+ *
4956
+
4957
+ @class Store
4958
+ @public
4959
+ */
4960
+
4857
4961
  class Store {
4858
4962
  /**
4859
4963
  * Provides access to the NotificationManager associated
@@ -4879,6 +4983,80 @@ class Store {
4879
4983
  get schema() {
4880
4984
  return this.getSchemaDefinitionService();
4881
4985
  }
4986
+
4987
+ /**
4988
+ * Provides access to the IdentifierCache instance
4989
+ * for this store.
4990
+ *
4991
+ * The IdentifierCache can be used to generate or
4992
+ * retrieve a stable unique identifier for any resource.
4993
+ *
4994
+ * @property {IdentifierCache} identifierCache
4995
+ * @public
4996
+ */
4997
+
4998
+ /**
4999
+ * Provides access to the requestManager instance associated
5000
+ * with this Store instance.
5001
+ *
5002
+ * When using `ember-data` this property is automatically
5003
+ * set to an instance of `RequestManager`. When not using `ember-data`
5004
+ * you must configure this property yourself, either by declaring
5005
+ * it as a service or by initializing it.
5006
+ *
5007
+ * ```ts
5008
+ * import Store, { CacheHandler } from '@ember-data/store';
5009
+ * import RequestManager from '@ember-data/request';
5010
+ * import Fetch from '@ember/data/request/fetch';
5011
+ *
5012
+ * class extends Store {
5013
+ * constructor() {
5014
+ * super(...arguments);
5015
+ * this.requestManager = new RequestManager();
5016
+ * this.requestManager.use([Fetch]);
5017
+ * this.requestManager.useCache(CacheHandler);
5018
+ * }
5019
+ * }
5020
+ * ```
5021
+ *
5022
+ * @public
5023
+ * @property {RequestManager} requestManager
5024
+ */
5025
+
5026
+ /**
5027
+ * A Property which an App may set to provide a Lifetimes Service
5028
+ * to control when a cached request becomes stale.
5029
+ *
5030
+ * Note, when defined, these methods will only be invoked if a
5031
+ * cache key exists for the request, either because the request
5032
+ * contains `cacheOptions.key` or because the [IdentifierCache](/ember-data/release/classes/IdentifierCache)
5033
+ * was able to generate a key for the request using the configured
5034
+ * [generation method](/ember-data/release/functions/@ember-data%2Fstore/setIdentifierGenerationMethod).
5035
+ *
5036
+ * `isSoftExpired` will only be invoked if `isHardExpired` returns `false`.
5037
+ *
5038
+ * ```ts
5039
+ * store.lifetimes = {
5040
+ * // make the request and ignore the current cache state
5041
+ * isHardExpired(identifier: StableDocumentIdentifier): boolean {
5042
+ * return false;
5043
+ * }
5044
+ *
5045
+ * // make the request in the background if true, return cache state
5046
+ * isSoftExpired(identifier: StableDocumentIdentifier): boolean {
5047
+ * return false;
5048
+ * }
5049
+ * }
5050
+ * ```
5051
+ *
5052
+ * @public
5053
+ * @property {LivetimesService|undefined} lifetimes
5054
+ */
5055
+
5056
+ // Private
5057
+
5058
+ // DEBUG-only properties
5059
+
4882
5060
  /**
4883
5061
  @method init
4884
5062
  @private
@@ -4896,7 +5074,7 @@ class Store {
4896
5074
  });
4897
5075
 
4898
5076
  // private
4899
- this._requestCache = new RequestCache(this);
5077
+ this._requestCache = new RequestStateService(this);
4900
5078
  this._instanceCache = new InstanceCache(this);
4901
5079
  this._adapterCache = Object.create(null);
4902
5080
  this._serializerCache = Object.create(null);