@ember-data/store 4.12.0-beta.6 → 4.12.0-beta.7

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
@@ -80,66 +80,64 @@ class extends Store {
80
80
 
81
81
  Now that we have a `cache` let's setup something to handle fetching and saving data via our API.
82
82
 
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.
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
84
  >
85
- > Note: [2] The `ember-data` package automatically includes the `@ember-data/json-api` cache for you.
85
+ > **Note** [2] The `ember-data` package automatically includes the `@ember-data/json-api` cache for you.
86
86
 
87
- ### Adding An Adapter
87
+ ### Handling Requests
88
88
 
89
- When *Ember***Data** needs to fetch or save data it will pass that request to your application's `Adapter` for fulfillment. How this fulfillment occurs (in-memory, device storage, via single or multiple API requests, etc.) is up to that Adapter.
89
+ When *Ember***Data** needs to fetch or save data it will pass that request to your application's `RequestManager` for fulfillment. How this fulfillment occurs (in-memory, device storage, via single or multiple API requests, etc.) is then up to the registered request handlers.
90
90
 
91
- To start, let's install a `JSON:API` adapter. If your app uses `GraphQL` or `REST` other adapters may better fit your data. You can author your own adapter by creating one that conforms to the [spec]().
91
+ To start, let's install the `FetchManager` from `@ember-data/request` and the basic `Fetch` handler from ``@ember-data/request/fetch`.
92
92
 
93
- The package `@ember-data/adapter` provides a `JSON:API` adapter we can use. After installing it, we can configure the store to use this adapter.
93
+ > **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
94
 
95
- ```js
95
+ ```ts
96
96
  import Store from '@ember-data/store';
97
- import Adapter from '@ember-data/adapter/json-api';
98
-
99
- class extends Store {
100
- #adapter = new Adapter();
101
-
102
- adapterFor() {
103
- return this.#adapter;
97
+ import RequestManager from '@ember-data/request';
98
+ import Fetch from '@ember-data/request/fetch';
99
+
100
+ export default class extends Store {
101
+ constructor() {
102
+ super(...arguments);
103
+ this.requestManager = new RequestManager();
104
+ this.requestManager.use([Fetch]);
104
105
  }
105
106
  }
106
107
  ```
107
108
 
108
- If you want to know more about using Adapters with Ember read the next section, else lets skip to [Presenting Data from the Cache](#presenting-data-from-the-cache) to configure how our application will interact with our data.
109
+ **Using RequestManager as a Service**
109
110
 
110
- #### Using with Ember
111
+ Alternatively if you have configured the `RequestManager` to be a service you may re-use it.
111
112
 
112
- Note: If you are using Ember and would like to make use of `service` injections in your adapter, you will want to additionally `setOwner` for the Adapter.
113
-
114
- ```js
115
- import Store from '@ember-data/store';
116
- import Adapter from '@ember-data/adapter/json-api';
117
- import { getOwner, setOwner } from '@ember/application';
113
+ *app/services/request.js*
114
+ ```ts
115
+ import RequestManager from '@ember-data/request';
116
+ import Fetch from '@ember-data/request/fetch';
118
117
 
119
- class extends Store {
120
- #adapter = null;
121
-
122
- adapterFor() {
123
- let adapter = this.#adapter;
124
- if (!adapter) {
125
- const owner = getOwner(this);
126
- adapter = new Adapter();
127
- setOwner(adapter, owner);
128
- this.#adapter = adapter;
129
- }
130
-
131
- return adapter;
118
+ export default class extends RequestManager {
119
+ constructor(createArgs) {
120
+ super(createArgs);
121
+ this.use([Fetch]);
132
122
  }
133
123
  }
134
124
  ```
135
125
 
136
- By default when using with Ember you only need to implement this hook if you want your adapter usage to be statically analyzeable. *Ember***Data** will attempt to resolve adapters using Ember's resolver. To provide a single Adapter for your application like the above you would provide it as the default export of the file `app/adapters/application.{js/ts}`
126
+ *app/services/store.js*
127
+ ```ts
128
+ import Store from '@ember-data/store';
129
+ import { service } from '@ember/service';
130
+
131
+ export default class extends Store {
132
+ @service('request') requestManager
133
+ }
134
+ ```
137
135
 
138
136
  ### Presenting Data from the Cache
139
137
 
140
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.
141
139
 
142
- 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 uniue resource a singleton, ensuring that if the cache updates our presented data will reflect the new state.
140
+ 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.
143
141
 
144
142
  Below is an example of using the hooks `instantiateRecord` and a `teardownRecord` to provide minimal read-only reactive state for simple resources.
145
143
 
@@ -156,25 +154,34 @@ class extends Store {
156
154
  record.type = identifier.type;
157
155
  record.id = identifier.id;
158
156
 
159
- notifications.subscribe(identifier, (_, change) => {
157
+ // update the TrackedObject whenever attributes change
158
+ const token = notifications.subscribe(identifier, (_, change) => {
160
159
  if (change === 'attributes') {
161
160
  Object.assign(record, cache.peek(identifier));
162
161
  }
163
162
  });
164
163
 
164
+ record.destroy = () => {
165
+ this.notifications.unsubscribe(token);
166
+ };
167
+
165
168
  return record;
166
169
  }
170
+
171
+ teardownRecord(record: FakeRecord) {
172
+ record.destroy();
173
+ }
167
174
  }
168
175
  ```
169
176
 
170
177
  Because `instantiateRecord` is opaque to the nature of the record, an implementation can be anything from a fairly simple object to a robust proxy that intelligently links together associated records through relationships.
171
178
 
172
- This also enables creating a record that separates `edit` flows from `create` flows entirely. A record class might choose to implement a `checkout`method that gives access to an editable instance while the primary record continues to be read-only and reflect only persisted (non-mutated) state.
179
+ This also enables creating a record that separates `edit` flows from `create` flows entirely. A record class might choose to implement a `checkout` method that gives access to an editable instance while the primary record continues to be read-only and reflect only persisted (non-mutated) state.
173
180
 
174
181
  Typically you will choose an existing record implementation such as `@ember-data/model` for your application.
175
182
 
176
183
  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.
177
184
 
178
- > Note: [1] `@ember-data/model` is a special record implementation: if the package is present the `instantiateRecord` hook will automatically do the above wiring if the hook is not implemented. Due to the complexity of this legacy package's use of Ember's resolver, we do not recommend wiring this package manually.
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.
179
186
  >
180
187
  > Note: [2] The `ember-data` package automatically includes the `@ember-data/model` implementation 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-35424a6b";
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-f7d3d5e5";
@@ -727,9 +727,9 @@ function _applyDecoratedDescriptor(target, property, decorators, descriptor, con
727
727
  return desc;
728
728
  }
729
729
  let tokenId = 0;
730
- const CacheOperations$1 = new Set(['added', 'removed', 'state', 'updated']);
730
+ const CacheOperations = new Set(['added', 'removed', 'state', 'updated']);
731
731
  function isCacheOperationValue(value) {
732
- return CacheOperations$1.has(value);
732
+ return CacheOperations.has(value);
733
733
  }
734
734
  function runLoopIsFlushing() {
735
735
  //@ts-expect-error
@@ -4888,8 +4888,10 @@ class Store {
4888
4888
  // we lazily set the cache handler when we issue the first request
4889
4889
  // because constructor doesn't allow for this to run after
4890
4890
  // the user has had the chance to set the prop.
4891
+ const storeSymbol = Symbol.for('ember-data:enable-hydration');
4891
4892
  let opts = {
4892
- store: this
4893
+ store: this,
4894
+ [storeSymbol]: true
4893
4895
  };
4894
4896
  if (macroCondition(getOwnConfig().env.TESTING)) {
4895
4897
  if (this.DISABLE_WAITER) {
@@ -6798,11 +6800,7 @@ function secretInit(record, cache, identifier, store) {
6798
6800
  StoreMap.set(record, store);
6799
6801
  setCacheFor(record, cache);
6800
6802
  }
6801
- const CacheOperations = new Set(['findRecord', 'findAll', 'query', 'queryRecord', 'findBelongsTo', 'findHasMany', 'updateRecord', 'createRecord', 'deleteRecord']);
6802
6803
  function getHydratedContent(store, request, document) {
6803
- if (!request.op || !CacheOperations.has(request.op)) {
6804
- return document;
6805
- }
6806
6804
  if (Array.isArray(document.data)) {
6807
6805
  const {
6808
6806
  lid
@@ -6829,14 +6827,9 @@ function getHydratedContent(store, request, document) {
6829
6827
  }
6830
6828
  return managed;
6831
6829
  } else {
6832
- switch (request.op) {
6833
- case 'findBelongsTo':
6834
- case 'queryRecord':
6835
- case 'findRecord':
6836
- return document.data ? store.peekRecord(document.data) : null;
6837
- default:
6838
- return document.data;
6839
- }
6830
+ return Object.assign({}, document, {
6831
+ data: document.data ? store.peekRecord(document.data) : null
6832
+ });
6840
6833
  }
6841
6834
  }
6842
6835
  function calcShouldFetch(store, request, hasCachedValue, lid) {
@@ -6859,12 +6852,13 @@ function fetchContentAndHydrate(next, context, shouldFetch, shouldBackgroundFetc
6859
6852
  const {
6860
6853
  store
6861
6854
  } = context.request;
6855
+ const shouldHydrate = context.request[Symbol.for('ember-data:enable-hydration')] || false;
6862
6856
  return next(context.request).then(document => {
6863
6857
  store._enableAsyncFlush = true;
6864
6858
  let response;
6865
6859
  store._join(() => {
6866
6860
  response = store.cache.put(document);
6867
- if (shouldFetch) {
6861
+ if (shouldFetch && shouldHydrate) {
6868
6862
  response = getHydratedContent(store, context.request, response);
6869
6863
  }
6870
6864
  });
@@ -6887,8 +6881,8 @@ function fetchContentAndHydrate(next, context, shouldFetch, shouldBackgroundFetc
6887
6881
  }
6888
6882
  const CacheHandler = {
6889
6883
  request(context, next) {
6890
- // if we are a legacy request or did not originate from the store, skip cache handling
6891
- if (!context.request.store || context.request.op && CacheOperations.has(context.request.op) && !context.request.url) {
6884
+ // if we have no cache or no cache-key skip cache handling
6885
+ if (!context.request.store || !(context.request.cacheOptions?.key || context.request.url)) {
6892
6886
  return next(context.request);
6893
6887
  }
6894
6888
  const {
@@ -6916,7 +6910,8 @@ const CacheHandler = {
6916
6910
  if ('error' in peeked) {
6917
6911
  throw peeked.error;
6918
6912
  }
6919
- return Promise.resolve(getHydratedContent(store, context.request, peeked.content));
6913
+ const shouldHydrate = context.request[Symbol.for('ember-data:enable-hydration')] || false;
6914
+ return Promise.resolve(shouldHydrate ? getHydratedContent(store, context.request, peeked.content) : peeked.content);
6920
6915
  }
6921
6916
  };
6922
6917
  function normalizeModelName(modelName) {