@warp-drive/core 5.7.0-alpha.0 → 5.7.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.
@@ -1139,6 +1139,7 @@ function gate(_target, key, desc) {
1139
1139
  const getter = desc.get;
1140
1140
  // eslint-disable-next-line @typescript-eslint/unbound-method
1141
1141
  const setter = desc.set;
1142
+ const isLocal = desc.isLocal;
1142
1143
  desc.get = function () {
1143
1144
  const signals = withSignalStore(this);
1144
1145
  let signal = peekInternalSignal(signals, key);
@@ -1165,6 +1166,11 @@ function gate(_target, key, desc) {
1165
1166
  setter.call(this, v);
1166
1167
  // when a gate is set, we do not notify the signal
1167
1168
  // as its update is controlled externally.
1169
+ // unless it specifically sets itself to be locally managed
1170
+ if (isLocal) {
1171
+ signal.isStale = true;
1172
+ notifyInternalSignal(signal);
1173
+ }
1168
1174
  };
1169
1175
  }
1170
1176
  return desc;
@@ -1557,185 +1563,6 @@ function _log(scope, prefix, subScop1, subScop2, subScop3, subScop4) {
1557
1563
  }
1558
1564
  return [];
1559
1565
  }
1560
-
1561
- /**
1562
- A `RecordReference` is a low-level API that allows users and
1563
- addon authors to perform meta-operations on a record.
1564
-
1565
- @hideconstructor
1566
- @public
1567
- */
1568
- class RecordReference {
1569
- // unsubscribe token given to us by the notification manager
1570
-
1571
- constructor(store, identifier) {
1572
- this.store = store;
1573
- this.___identifier = identifier;
1574
- this.___token = store.notifications.subscribe(identifier, (_, bucket, notifiedKey) => {
1575
- if (bucket === 'identity' || bucket === 'attributes' && notifiedKey === 'id') {
1576
- this._ref++;
1577
- }
1578
- });
1579
- }
1580
-
1581
- /** @internal */
1582
- destroy() {
1583
- this.store.notifications.unsubscribe(this.___token);
1584
- }
1585
- get type() {
1586
- return this.identifier().type;
1587
- }
1588
-
1589
- /**
1590
- The `id` of the record that this reference refers to.
1591
- Together, the `type` and `id` properties form a composite key for
1592
- the identity map.
1593
- Example
1594
- ```javascript
1595
- let userRef = store.getReference('user', 1);
1596
- userRef.id(); // '1'
1597
- ```
1598
- @public
1599
- */
1600
- id() {
1601
- // eslint-disable-next-line @typescript-eslint/no-unused-expressions
1602
- this._ref; // consume the tracked prop
1603
- return this.___identifier.id;
1604
- }
1605
-
1606
- /**
1607
- The `identifier` of the record that this reference refers to.
1608
- Together, the `type` and `id` properties form a composite key for
1609
- the identity map.
1610
- Example
1611
- ```javascript
1612
- let userRef = store.getReference('user', 1);
1613
- userRef.identifier(); // '1'
1614
- ```
1615
- @public
1616
- */
1617
- identifier() {
1618
- return this.___identifier;
1619
- }
1620
-
1621
- /**
1622
- How the reference will be looked up when it is loaded. Currently
1623
- this always returns `identity` to signify that a record will be
1624
- loaded by its `type` and `id`.
1625
- Example
1626
- ```javascript
1627
- const userRef = store.getReference('user', 1);
1628
- userRef.remoteType(); // 'identity'
1629
- ```
1630
- @public
1631
- */
1632
- remoteType() {
1633
- return 'identity';
1634
- }
1635
-
1636
- /**
1637
- This API allows you to provide a reference with new data. The
1638
- simplest usage of this API is similar to `store.push`: you provide a
1639
- normalized hash of data and the object represented by the reference
1640
- will update.
1641
- If you pass a promise to `push`, Ember Data will not ask the adapter
1642
- for the data if another attempt to fetch it is made in the
1643
- interim. When the promise resolves, the underlying object is updated
1644
- with the new data, and the promise returned by *this function* is resolved
1645
- with that object.
1646
- For example, `recordReference.push(promise)` will be resolved with a
1647
- record.
1648
- Example
1649
- ```javascript
1650
- let userRef = store.getReference('user', 1);
1651
- // provide data for reference
1652
- userRef.push({
1653
- data: {
1654
- id: "1",
1655
- type: "user",
1656
- attributes: {
1657
- username: "@user"
1658
- }
1659
- }
1660
- }).then(function(user) {
1661
- userRef.value() === user;
1662
- });
1663
- ```
1664
- @public
1665
- @param objectOrPromise a JSON:API ResourceDocument or a promise resolving to one
1666
- @return a promise for the value (record or relationship)
1667
- */
1668
- push(objectOrPromise) {
1669
- // TODO @deprecate pushing unresolved payloads
1670
- return Promise.resolve(objectOrPromise).then(data => {
1671
- return this.store.push(data);
1672
- });
1673
- }
1674
-
1675
- /**
1676
- If the entity referred to by the reference is already loaded, it is
1677
- present as `reference.value`. Otherwise the value returned by this function
1678
- is `null`.
1679
- Example
1680
- ```javascript
1681
- let userRef = store.getReference('user', 1);
1682
- userRef.value(); // user
1683
- ```
1684
- @public
1685
- */
1686
- value() {
1687
- return this.store.peekRecord(this.___identifier);
1688
- }
1689
-
1690
- /**
1691
- Triggers a fetch for the backing entity based on its `remoteType`
1692
- (see `remoteType` definitions per reference type).
1693
- Example
1694
- ```javascript
1695
- let userRef = store.getReference('user', 1);
1696
- // load user (via store.find)
1697
- userRef.load().then(...)
1698
- ```
1699
- @public
1700
- */
1701
- load() {
1702
- const id = this.id();
1703
- if (id !== null) {
1704
- return this.store.findRecord(this.type, id);
1705
- }
1706
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
1707
- {
1708
- throw new Error(`Unable to fetch record of type ${this.type} without an id`);
1709
- }
1710
- })() : {};
1711
- }
1712
-
1713
- /**
1714
- Reloads the record if it is already loaded. If the record is not
1715
- loaded it will load the record via `store.findRecord`
1716
- Example
1717
- ```javascript
1718
- let userRef = store.getReference('user', 1);
1719
- // or trigger a reload
1720
- userRef.reload().then(...)
1721
- ```
1722
- @public
1723
- */
1724
- reload() {
1725
- const id = this.id();
1726
- if (id !== null) {
1727
- return this.store.findRecord(this.type, id, {
1728
- reload: true
1729
- });
1730
- }
1731
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
1732
- {
1733
- throw new Error(`Unable to fetch record of type ${this.type} without an id`);
1734
- }
1735
- })() : {};
1736
- }
1737
- }
1738
- defineSignal(RecordReference.prototype, '_ref');
1739
1566
  class CacheCapabilitiesManager {
1740
1567
  constructor(_store) {
1741
1568
  this._store = _store;
@@ -1921,7 +1748,6 @@ class InstanceCache {
1921
1748
  this.store = store;
1922
1749
  this.__instances = {
1923
1750
  record: new Map(),
1924
- reference: new WeakMap(),
1925
1751
  document: new Map()
1926
1752
  };
1927
1753
  this._storeWrapper = new CacheCapabilitiesManager(this.store);
@@ -2015,15 +1841,6 @@ class InstanceCache {
2015
1841
  }
2016
1842
  return record;
2017
1843
  }
2018
- getReference(identifier) {
2019
- const cache = this.__instances.reference;
2020
- let reference = cache.get(identifier);
2021
- if (!reference) {
2022
- reference = new RecordReference(this.store, identifier);
2023
- cache.set(identifier, reference);
2024
- }
2025
- return reference;
2026
- }
2027
1844
  recordIsLoaded(identifier, filterDeleted = false) {
2028
1845
  const cache = this.cache;
2029
1846
  if (!cache) {
@@ -2201,208 +2018,39 @@ class InstanceCache {
2201
2018
  this.store.notifications.notify(identifier, 'identity');
2202
2019
  }
2203
2020
  }
2204
- function _resourceIsFullDeleted(identifier, cache) {
2205
- return cache.isDeletionCommitted(identifier) || cache.isNew(identifier) && cache.isDeleted(identifier);
2206
- }
2207
- function resourceIsFullyDeleted(instanceCache, identifier) {
2208
- const cache = instanceCache.cache;
2209
- return !cache || _resourceIsFullDeleted(identifier, cache);
2021
+ function _clearCaches() {
2022
+ RecordCache.clear();
2023
+ StoreMap.clear();
2024
+ CacheForIdentifierCache.clear();
2210
2025
  }
2211
2026
 
2212
- /*
2213
- When a find request is triggered on the store, the user can optionally pass in
2214
- attributes and relationships to be preloaded. These are meant to behave as if they
2215
- came back from the server, except the user obtained them out of band and is informing
2216
- the store of their existence. The most common use case is for supporting client side
2217
- nested URLs, such as `/posts/1/comments/2` so the user can do
2218
- `store.findRecord('comment', 2, { preload: { post: 1 } })` without having to fetch the post.
2027
+ /**
2028
+ * The CacheManager wraps a Cache enforcing that only
2029
+ * the public API surface area is exposed.
2030
+ *
2031
+ * Hence, it is the value of `Store.cache`, wrapping
2032
+ * the cache instance returned by `Store.createCache`.
2033
+ *
2034
+ * It handles translating between cache versions when
2035
+ * necessary, for instance when a Store is configured
2036
+ * to use both a v1 and a v2 cache depending on some
2037
+ * heuristic.
2038
+ *
2039
+ * Starting with the v2 spec, the cache is designed such
2040
+ * that it must be implemented as a singleton.
2041
+ *
2042
+ * @class CacheManager
2043
+ * @public
2044
+ */
2045
+ class CacheManager {
2046
+ version = '2';
2047
+ #cache;
2048
+ constructor(cache) {
2049
+ this.#cache = cache;
2050
+ }
2219
2051
 
2220
- Preloaded data can be attributes and relationships passed in either as IDs or as actual
2221
- models.
2222
- */
2223
-
2224
- function preloadData(store, identifier, preload) {
2225
- const jsonPayload = {};
2226
- //TODO(Igor) consider the polymorphic case
2227
- const schemas = store.schema;
2228
- const fields = schemas.fields(identifier);
2229
- Object.keys(preload).forEach(key => {
2230
- const preloadValue = preload[key];
2231
- const field = fields.get(key);
2232
- if (field && (field.kind === 'hasMany' || field.kind === 'belongsTo')) {
2233
- if (!jsonPayload.relationships) {
2234
- jsonPayload.relationships = {};
2235
- }
2236
- jsonPayload.relationships[key] = preloadRelationship(field, preloadValue);
2237
- } else {
2238
- if (!jsonPayload.attributes) {
2239
- jsonPayload.attributes = {};
2240
- }
2241
- jsonPayload.attributes[key] = preloadValue;
2242
- }
2243
- });
2244
- const cache = store.cache;
2245
- const hasRecord = Boolean(store._instanceCache.peek(identifier));
2246
- cache.upsert(identifier, jsonPayload, hasRecord);
2247
- }
2248
- function preloadRelationship(schema, preloadValue) {
2249
- const relatedType = schema.type;
2250
- if (schema.kind === 'hasMany') {
2251
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
2252
- if (!test) {
2253
- throw new Error('You need to pass in an array to set a hasMany property on a record');
2254
- }
2255
- })(Array.isArray(preloadValue)) : {};
2256
- return {
2257
- data: preloadValue.map(value => _convertPreloadRelationshipToJSON(value, relatedType))
2258
- };
2259
- }
2260
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
2261
- if (!test) {
2262
- throw new Error('You should not pass in an array to set a belongsTo property on a record');
2263
- }
2264
- })(!Array.isArray(preloadValue)) : {};
2265
- return {
2266
- data: preloadValue ? _convertPreloadRelationshipToJSON(preloadValue, relatedType) : null
2267
- };
2268
- }
2269
-
2270
- /*
2271
- findRecord('user', '1', { preload: { friends: ['1'] }});
2272
- findRecord('user', '1', { preload: { friends: [record] }});
2273
- */
2274
- function _convertPreloadRelationshipToJSON(value, type) {
2275
- if (typeof value === 'string' || typeof value === 'number') {
2276
- return {
2277
- type,
2278
- id: ensureStringId(value)
2279
- };
2280
- }
2281
- // TODO if not a record instance assert it's an identifier
2282
- // and allow identifiers to be used
2283
- return recordIdentifierFor(value);
2284
- }
2285
- function _clearCaches() {
2286
- RecordCache.clear();
2287
- StoreMap.clear();
2288
- CacheForIdentifierCache.clear();
2289
- }
2290
-
2291
- // if modelFor turns out to be a bottleneck we should replace with a Map
2292
- // and clear it during store teardown.
2293
- const AvailableShims = getOrSetGlobal('AvailableShims', new WeakMap());
2294
- function getShimClass(store, modelName) {
2295
- let shims = AvailableShims.get(store);
2296
- if (!shims) {
2297
- shims = Object.create(null);
2298
- AvailableShims.set(store, shims);
2299
- }
2300
- let shim = shims[modelName];
2301
- if (shim === undefined) {
2302
- shim = shims[modelName] = new ShimModelClass(store, modelName);
2303
- }
2304
- return shim;
2305
- }
2306
-
2307
- // Mimics the static apis of @ember-data/model
2308
- class ShimModelClass {
2309
- constructor(store, modelName) {
2310
- this.__store = store;
2311
- this.modelName = modelName;
2312
- }
2313
- get fields() {
2314
- const fields = new Map();
2315
- const fieldSchemas = this.__store.schema.fields({
2316
- type: this.modelName
2317
- });
2318
- fieldSchemas.forEach((schema, key) => {
2319
- if (schema.kind === 'attribute' || schema.kind === 'belongsTo' || schema.kind === 'hasMany') {
2320
- fields.set(key, schema.kind);
2321
- }
2322
- });
2323
- return fields;
2324
- }
2325
- get attributes() {
2326
- const attrs = new Map();
2327
- const fields = this.__store.schema.fields({
2328
- type: this.modelName
2329
- });
2330
- fields.forEach((schema, key) => {
2331
- if (schema.kind === 'attribute') {
2332
- attrs.set(key, schema);
2333
- }
2334
- });
2335
- return attrs;
2336
- }
2337
- get relationshipsByName() {
2338
- const rels = new Map();
2339
- const fields = this.__store.schema.fields({
2340
- type: this.modelName
2341
- });
2342
- fields.forEach((schema, key) => {
2343
- if (schema.kind === 'belongsTo' || schema.kind === 'hasMany') {
2344
- rels.set(key, schema);
2345
- }
2346
- });
2347
- return rels;
2348
- }
2349
- eachAttribute(callback, binding) {
2350
- this.__store.schema.fields({
2351
- type: this.modelName
2352
- }).forEach((schema, key) => {
2353
- if (schema.kind === 'attribute') {
2354
- callback.call(binding, key, schema);
2355
- }
2356
- });
2357
- }
2358
- eachRelationship(callback, binding) {
2359
- this.__store.schema.fields({
2360
- type: this.modelName
2361
- }).forEach((schema, key) => {
2362
- if (schema.kind === 'belongsTo' || schema.kind === 'hasMany') {
2363
- callback.call(binding, key, schema);
2364
- }
2365
- });
2366
- }
2367
- eachTransformedAttribute(callback, binding) {
2368
- this.__store.schema.fields({
2369
- type: this.modelName
2370
- }).forEach((schema, key) => {
2371
- if (schema.kind === 'attribute') {
2372
- const type = schema.type;
2373
- if (type) callback.call(binding, key, type);
2374
- }
2375
- });
2376
- }
2377
- }
2378
-
2379
- /**
2380
- * The CacheManager wraps a Cache enforcing that only
2381
- * the public API surface area is exposed.
2382
- *
2383
- * Hence, it is the value of `Store.cache`, wrapping
2384
- * the cache instance returned by `Store.createCache`.
2385
- *
2386
- * It handles translating between cache versions when
2387
- * necessary, for instance when a Store is configured
2388
- * to use both a v1 and a v2 cache depending on some
2389
- * heuristic.
2390
- *
2391
- * Starting with the v2 spec, the cache is designed such
2392
- * that it must be implemented as a singleton.
2393
- *
2394
- * @class CacheManager
2395
- * @public
2396
- */
2397
- class CacheManager {
2398
- version = '2';
2399
- #cache;
2400
- constructor(cache) {
2401
- this.#cache = cache;
2402
- }
2403
-
2404
- // Cache Management
2405
- // ================
2052
+ // Cache Management
2053
+ // ================
2406
2054
 
2407
2055
  /**
2408
2056
  * Cache the response to a request
@@ -4426,57 +4074,6 @@ class RequestStateService {
4426
4074
  return null;
4427
4075
  }
4428
4076
  }
4429
- function isNonEmptyString(str) {
4430
- return Boolean(str && typeof str === 'string');
4431
- }
4432
- function constructResource(type, id, lid) {
4433
- if (typeof type === 'object' && type !== null) {
4434
- const resource = type;
4435
- if (isStableIdentifier(resource)) {
4436
- return resource;
4437
- }
4438
- if ('id' in resource) {
4439
- resource.id = coerceId(resource.id);
4440
- }
4441
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
4442
- if (!test) {
4443
- throw new Error('Expected either id or lid to be a valid string');
4444
- }
4445
- })('id' in resource && isNonEmptyString(resource.id) || isNonEmptyString(resource.lid)) : {};
4446
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
4447
- if (!test) {
4448
- throw new Error('if id is present, the type must be a string');
4449
- }
4450
- })(!('id' in resource) || typeof resource.type === 'string') : {};
4451
- return resource;
4452
- } else {
4453
- const trueId = coerceId(id);
4454
- if (!isNonEmptyString(trueId)) {
4455
- if (isNonEmptyString(lid)) {
4456
- return {
4457
- lid
4458
- };
4459
- }
4460
- throw new Error('Expected either id or lid to be a valid string');
4461
- }
4462
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
4463
- if (!test) {
4464
- throw new Error('type must be a string');
4465
- }
4466
- })(typeof type === 'string') : {};
4467
- if (isNonEmptyString(lid)) {
4468
- return {
4469
- type,
4470
- id: trueId,
4471
- lid
4472
- };
4473
- }
4474
- return {
4475
- type,
4476
- id: trueId
4477
- };
4478
- }
4479
- }
4480
4077
 
4481
4078
  // this import location is deprecated but breaks in 4.8 and older
4482
4079
  // @ts-expect-error adding to globalThis
@@ -5023,46 +4620,6 @@ class Store extends BaseClass {
5023
4620
  return future;
5024
4621
  }
5025
4622
 
5026
- /**
5027
- Returns the schema for a particular resource type (modelName).
5028
- When used with Model from @ember-data/model the return is the model class,
5029
- but this is not guaranteed.
5030
- If looking to query attribute or relationship information it is
5031
- recommended to use `getSchemaDefinitionService` instead. This method
5032
- should be considered legacy and exists primarily to continue to support
5033
- Adapter/Serializer APIs which expect it's return value in their method
5034
- signatures.
5035
- The class of a model might be useful if you want to get a list of all the
5036
- relationship names of the model, see
5037
- [`relationshipNames`](/ember-data/release/classes/Model?anchor=relationshipNames)
5038
- for example.
5039
- @public
5040
- @deprecated
5041
- @param {String} type
5042
- @return {ModelSchema}
5043
- */
5044
-
5045
- modelFor(type) {
5046
- // FIXME add deprecation and deprecation stripping
5047
- // FIXME/TODO update RFC to remove this method
5048
- if (macroCondition(getGlobalConfig().WarpDrive.env.DEBUG)) {
5049
- assertDestroyedStoreOnly(this, 'modelFor');
5050
- }
5051
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5052
- if (!test) {
5053
- throw new Error(`You need to pass <type> to the store's modelFor method`);
5054
- }
5055
- })(typeof type === 'string' && type.length) : {};
5056
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5057
- if (!test) {
5058
- throw new Error(`No model was found for '${type}' and no schema handles the type`);
5059
- }
5060
- })(this.schema.hasResource({
5061
- type
5062
- })) : {};
5063
- return getShimClass(this, type);
5064
- }
5065
-
5066
4623
  /**
5067
4624
  Create a new record in the current store. The properties passed
5068
4625
  to this method are set on the newly created record.
@@ -5203,403 +4760,27 @@ class Store extends BaseClass {
5203
4760
  }
5204
4761
 
5205
4762
  /**
5206
- This method returns a record for a given identifier or type and id combination.
5207
- The `findRecord` method will always resolve its promise with the same
5208
- object for a given identifier or type and `id`.
5209
- The `findRecord` method will always return a **promise** that will be
5210
- resolved with the record.
4763
+ Get a record by a given type and ID without triggering a fetch.
4764
+ This method will synchronously return the record if it is available in the store,
4765
+ otherwise it will return `null`. A record is available if it has been fetched earlier, or
4766
+ pushed manually into the store.
4767
+ See [findRecord](../methods/findRecord?anchor=findRecord) if you would like to request this record from the backend.
4768
+ _Note: This is a synchronous method and does not return a promise._
5211
4769
  **Example 1**
5212
- ```js [app/routes/post.js]
5213
- export default class PostRoute extends Route {
5214
- model({ post_id }) {
5215
- return this.store.findRecord('post', post_id);
5216
- }
5217
- }
4770
+ ```js
4771
+ let post = store.peekRecord('post', '1');
4772
+ post.id; // '1'
5218
4773
  ```
5219
- **Example 2**
5220
- `findRecord` can be called with a single identifier argument instead of the combination
4774
+ `peekRecord` can be called with a single identifier argument instead of the combination
5221
4775
  of `type` (modelName) and `id` as separate arguments. You may recognize this combo as
5222
4776
  the typical pairing from [JSON:API](https://jsonapi.org/format/#document-resource-object-identification)
5223
- ```js [app/routes/post.js]
5224
- export default class PostRoute extends Route {
5225
- model({ post_id: id }) {
5226
- return this.store.findRecord({ type: 'post', id });
5227
- }
5228
- }
4777
+ **Example 2**
4778
+ ```js
4779
+ let post = store.peekRecord({ type: 'post', id });
4780
+ post.id; // '1'
5229
4781
  ```
5230
- **Example 3**
5231
- If you have previously received an lid via an Identifier for this record, and the record
5232
- has already been assigned an id, you can find the record again using just the lid.
5233
- ```js [app/routes/post.js]
5234
- store.findRecord({ lid });
5235
- ```
5236
- If the record is not yet available, the store will ask the adapter's `findRecord`
5237
- method to retrieve and supply the necessary data. If the record is already present
5238
- in the store, it depends on the reload behavior _when_ the returned promise
5239
- resolves.
5240
- ### Preloading
5241
- You can optionally `preload` specific attributes and relationships that you know of
5242
- by passing them via the passed `options`.
5243
- For example, if your Ember route looks like `/posts/1/comments/2` and your API route
5244
- for the comment also looks like `/posts/1/comments/2` if you want to fetch the comment
5245
- without also fetching the post you can pass in the post to the `findRecord` call:
5246
- ```js [app/routes/post-comments.js]
5247
- export default class PostRoute extends Route {
5248
- model({ post_id, comment_id: id }) {
5249
- return this.store.findRecord({ type: 'comment', id, { preload: { post: post_id }} });
5250
- }
5251
- }
5252
- ```
5253
- In your adapter you can then access this id without triggering a network request via the
5254
- snapshot:
5255
- ```js [app/adapters/application.js]
5256
- export default class Adapter {
5257
- findRecord(store, schema, id, snapshot) {
5258
- let type = schema.modelName;
5259
- if (type === 'comment')
5260
- let postId = snapshot.belongsTo('post', { id: true });
5261
- return fetch(`./posts/${postId}/comments/${id}`)
5262
- .then(response => response.json())
5263
- }
5264
- }
5265
- static create() {
5266
- return new this();
5267
- }
5268
- }
5269
- ```
5270
- This could also be achieved by supplying the post id to the adapter via the adapterOptions
5271
- property on the options hash.
5272
- ```js [app/routes/post-comments.js]
5273
- export default class PostRoute extends Route {
5274
- model({ post_id, comment_id: id }) {
5275
- return this.store.findRecord({ type: 'comment', id, { adapterOptions: { post: post_id }} });
5276
- }
5277
- }
5278
- ```
5279
- ```js [app/adapters/application.js]
5280
- export default class Adapter {
5281
- findRecord(store, schema, id, snapshot) {
5282
- let type = schema.modelName;
5283
- if (type === 'comment')
5284
- let postId = snapshot.adapterOptions.post;
5285
- return fetch(`./posts/${postId}/comments/${id}`)
5286
- .then(response => response.json())
5287
- }
5288
- }
5289
- static create() {
5290
- return new this();
5291
- }
5292
- }
5293
- ```
5294
- If you have access to the post model you can also pass the model itself to preload:
5295
- ```javascript
5296
- let post = await store.findRecord('post', '1');
5297
- let comment = await store.findRecord('comment', '2', { post: myPostModel });
5298
- ```
5299
- ### Reloading
5300
- The reload behavior is configured either via the passed `options` hash or
5301
- the result of the adapter's `shouldReloadRecord`.
5302
- If `{ reload: true }` is passed or `adapter.shouldReloadRecord` evaluates
5303
- to `true`, then the returned promise resolves once the adapter returns
5304
- data, regardless if the requested record is already in the store:
5305
- ```js
5306
- store.push({
5307
- data: {
5308
- id: 1,
5309
- type: 'post',
5310
- revision: 1
5311
- }
5312
- });
5313
- // adapter#findRecord resolves with
5314
- // [
5315
- // {
5316
- // id: 1,
5317
- // type: 'post',
5318
- // revision: 2
5319
- // }
5320
- // ]
5321
- store.findRecord('post', '1', { reload: true }).then(function(post) {
5322
- post.revision; // 2
5323
- });
5324
- ```
5325
- If no reload is indicated via the above mentioned ways, then the promise
5326
- immediately resolves with the cached version in the store.
5327
- ### Background Reloading
5328
- Optionally, if `adapter.shouldBackgroundReloadRecord` evaluates to `true`,
5329
- then a background reload is started, which updates the records' data, once
5330
- it is available:
5331
- ```js
5332
- // app/adapters/post.js
5333
- import ApplicationAdapter from "./application";
5334
- export default class PostAdapter extends ApplicationAdapter {
5335
- shouldReloadRecord(store, snapshot) {
5336
- return false;
5337
- },
5338
- shouldBackgroundReloadRecord(store, snapshot) {
5339
- return true;
5340
- }
5341
- });
5342
- // ...
5343
- store.push({
5344
- data: {
5345
- id: 1,
5346
- type: 'post',
5347
- revision: 1
5348
- }
5349
- });
5350
- let blogPost = store.findRecord('post', '1').then(function(post) {
5351
- post.revision; // 1
5352
- });
5353
- // later, once adapter#findRecord resolved with
5354
- // [
5355
- // {
5356
- // id: 1,
5357
- // type: 'post',
5358
- // revision: 2
5359
- // }
5360
- // ]
5361
- blogPost.revision; // 2
5362
- ```
5363
- If you would like to force or prevent background reloading, you can set a
5364
- boolean value for `backgroundReload` in the options object for
5365
- `findRecord`.
5366
- ```js [app/routes/post/edit.js]
5367
- export default class PostEditRoute extends Route {
5368
- model(params) {
5369
- return this.store.findRecord('post', params.post_id, { backgroundReload: false });
5370
- }
5371
- }
5372
- ```
5373
- If you pass an object on the `adapterOptions` property of the options
5374
- argument it will be passed to your adapter via the snapshot
5375
- ```js [app/routes/post/edit.js]
5376
- export default class PostEditRoute extends Route {
5377
- model(params) {
5378
- return this.store.findRecord('post', params.post_id, {
5379
- adapterOptions: { subscribe: false }
5380
- });
5381
- }
5382
- }
5383
- ```
5384
- ```js [app/adapters/post.js]
5385
- import MyCustomAdapter from './custom-adapter';
5386
- export default class PostAdapter extends MyCustomAdapter {
5387
- findRecord(store, type, id, snapshot) {
5388
- if (snapshot.adapterOptions.subscribe) {
5389
- // ...
5390
- }
5391
- // ...
5392
- }
5393
- }
5394
- ```
5395
- See [peekRecord](../methods/peekRecord?anchor=peekRecord) to get the cached version of a record.
5396
- ### Retrieving Related Model Records
5397
- If you use an adapter such as Ember's default
5398
- [`JSONAPIAdapter`](/ember-data/release/classes/JSONAPIAdapter)
5399
- that supports the [JSON API specification](http://jsonapi.org/) and if your server
5400
- endpoint supports the use of an
5401
- ['include' query parameter](http://jsonapi.org/format/#fetching-includes),
5402
- you can use `findRecord()` or `findAll()` to automatically retrieve additional records related to
5403
- the one you request by supplying an `include` parameter in the `options` object.
5404
- For example, given a `post` model that has a `hasMany` relationship with a `comment`
5405
- model, when we retrieve a specific post we can have the server also return that post's
5406
- comments in the same request:
5407
- ```js [app/routes/post.js]
5408
- export default class PostRoute extends Route {
5409
- model(params) {
5410
- return this.store.findRecord('post', params.post_id, { include: ['comments'] });
5411
- }
5412
- }
5413
- ```
5414
- ```js [app/adapters/application.js]
5415
- export default class Adapter {
5416
- findRecord(store, schema, id, snapshot) {
5417
- let type = schema.modelName;
5418
- if (type === 'post')
5419
- let includes = snapshot.adapterOptions.include;
5420
- return fetch(`./posts/${postId}?include=${includes}`)
5421
- .then(response => response.json())
5422
- }
5423
- }
5424
- static create() {
5425
- return new this();
5426
- }
5427
- }
5428
- ```
5429
- In this case, the post's comments would then be available in your template as
5430
- `model.comments`.
5431
- Multiple relationships can be requested using an `include` parameter consisting of a
5432
- list of relationship names, while nested relationships can be specified
5433
- using a dot-separated sequence of relationship names. So to request both the post's
5434
- comments and the authors of those comments the request would look like this:
5435
- ```js [app/routes/post.js]
5436
- export default class PostRoute extends Route {
5437
- model(params) {
5438
- return this.store.findRecord('post', params.post_id, { include: ['comments','comments.author'] });
5439
- }
5440
- }
5441
- ```
5442
- ### Retrieving Specific Fields by Type
5443
- If your server endpoint supports the use of a ['fields' query parameter](https://jsonapi.org/format/#fetching-sparse-fieldsets),
5444
- you can use pass those fields through to your server. At this point in time, this requires a few manual steps on your part.
5445
- 1. Implement `buildQuery` in your adapter.
5446
- ```js [app/adapters/application.js]
5447
- buildQuery(snapshot) {
5448
- let query = super.buildQuery(...arguments);
5449
- let { fields } = snapshot.adapterOptions;
5450
- if (fields) {
5451
- query.fields = fields;
5452
- }
5453
- return query;
5454
- }
5455
- ```
5456
- 2. Then pass through the applicable fields to your `findRecord` request.
5457
- Given a `post` model with attributes body, title, publishDate and meta, you can retrieve a filtered list of attributes.
5458
- ```js [app/routes/post.js]
5459
- export default class extends Route {
5460
- model(params) {
5461
- return this.store.findRecord('post', params.post_id, { adapterOptions: { fields: { post: 'body,title' } });
5462
- }
5463
- }
5464
- ```
5465
- Moreover, you can filter attributes on related models as well. If a `post` has a `belongsTo` relationship to a user,
5466
- just include the relationship key and attributes.
5467
- ```js [app/routes/post.js]
5468
- export default class extends Route {
5469
- model(params) {
5470
- return this.store.findRecord('post', params.post_id, { adapterOptions: { fields: { post: 'body,title', user: 'name,email' } });
5471
- }
5472
- }
5473
- ```
5474
- @since 1.13.0
5475
- @public
5476
- @param {String|object} type - either a string representing the name of the resource or a ResourceIdentifier object containing both the type (a string) and the id (a string) for the record or an lid (a string) of an existing record
5477
- @param {(String|Integer|Object)} id - optional object with options for the request only if the first param is a ResourceIdentifier, else the string id of the record to be retrieved
5478
- @param {Object} [options] - if the first param is a string this will be the optional options for the request. See examples for available options.
5479
- @return {Promise} promise
5480
- */
5481
-
5482
- findRecord(resource, id, options) {
5483
- if (macroCondition(getGlobalConfig().WarpDrive.env.DEBUG)) {
5484
- assertDestroyingStore(this, 'findRecord');
5485
- }
5486
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5487
- if (!test) {
5488
- throw new Error(`You need to pass a modelName or resource identifier as the first argument to the store's findRecord method`);
5489
- }
5490
- })(resource) : {};
5491
- if (isMaybeIdentifier(resource)) {
5492
- options = id;
5493
- } else {
5494
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5495
- if (!test) {
5496
- throw new Error(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${resource}`);
5497
- }
5498
- })(typeof resource === 'string') : {};
5499
- const type = normalizeModelName(resource);
5500
- const normalizedId = ensureStringId(id);
5501
- resource = constructResource(type, normalizedId);
5502
- }
5503
- const identifier = this.identifierCache.getOrCreateRecordIdentifier(resource);
5504
- options = options || {};
5505
- if (options.preload) {
5506
- // force reload if we preload to ensure we don't resolve the promise
5507
- // until we are complete, else we will end up background-reloading
5508
- // even for initial load.
5509
- if (!this._instanceCache.recordIsLoaded(identifier)) {
5510
- options.reload = true;
5511
- }
5512
- this._join(() => {
5513
- preloadData(this, identifier, options.preload);
5514
- });
5515
- }
5516
- const promise = this.request({
5517
- op: 'findRecord',
5518
- data: {
5519
- record: identifier,
5520
- options
5521
- },
5522
- cacheOptions: {
5523
- [SkipCache]: true
5524
- }
5525
- });
5526
- return promise.then(document => {
5527
- return document.content;
5528
- });
5529
- }
5530
-
5531
- /**
5532
- Get the reference for the specified record.
5533
- Example
5534
- ```javascript
5535
- let userRef = store.getReference('user', '1');
5536
- // check if the user is loaded
5537
- let isLoaded = userRef.value() !== null;
5538
- // get the record of the reference (null if not yet available)
5539
- let user = userRef.value();
5540
- // get the identifier of the reference
5541
- if (userRef.remoteType() === 'id') {
5542
- let id = userRef.id();
5543
- }
5544
- // load user (via store.find)
5545
- userRef.load().then(...)
5546
- // or trigger a reload
5547
- userRef.reload().then(...)
5548
- // provide data for reference
5549
- userRef.push({ id: 1, username: '@user' }).then(function(user) {
5550
- userRef.value() === user;
5551
- });
5552
- ```
5553
- @public
5554
- @param {String|object} resource - modelName (string) or Identifier (object)
5555
- @param {String|Integer} id
5556
- @since 2.5.0
5557
- @return {RecordReference}
5558
- */
5559
- // TODO @deprecate getReference (and references generally)
5560
- getReference(resource, id) {
5561
- if (macroCondition(getGlobalConfig().WarpDrive.env.DEBUG)) {
5562
- assertDestroyingStore(this, 'getReference');
5563
- }
5564
- let resourceIdentifier;
5565
- if (arguments.length === 1 && isMaybeIdentifier(resource)) {
5566
- resourceIdentifier = resource;
5567
- } else {
5568
- const type = normalizeModelName(resource);
5569
- const normalizedId = ensureStringId(id);
5570
- resourceIdentifier = constructResource(type, normalizedId);
5571
- }
5572
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5573
- if (!test) {
5574
- throw new Error('getReference expected to receive either a resource identifier or type and id as arguments');
5575
- }
5576
- })(isMaybeIdentifier(resourceIdentifier)) : {};
5577
- const identifier = this.identifierCache.getOrCreateRecordIdentifier(resourceIdentifier);
5578
- return this._instanceCache.getReference(identifier);
5579
- }
5580
-
5581
- /**
5582
- Get a record by a given type and ID without triggering a fetch.
5583
- This method will synchronously return the record if it is available in the store,
5584
- otherwise it will return `null`. A record is available if it has been fetched earlier, or
5585
- pushed manually into the store.
5586
- See [findRecord](../methods/findRecord?anchor=findRecord) if you would like to request this record from the backend.
5587
- _Note: This is a synchronous method and does not return a promise._
5588
- **Example 1**
5589
- ```js
5590
- let post = store.peekRecord('post', '1');
5591
- post.id; // '1'
5592
- ```
5593
- `peekRecord` can be called with a single identifier argument instead of the combination
5594
- of `type` (modelName) and `id` as separate arguments. You may recognize this combo as
5595
- the typical pairing from [JSON:API](https://jsonapi.org/format/#document-resource-object-identification)
5596
- **Example 2**
5597
- ```js
5598
- let post = store.peekRecord({ type: 'post', id });
5599
- post.id; // '1'
5600
- ```
5601
- If you have previously received an lid from an Identifier for this record, you can lookup the record again using
5602
- just the lid.
4782
+ If you have previously received an lid from an Identifier for this record, you can lookup the record again using
4783
+ just the lid.
5603
4784
  **Example 3**
5604
4785
  ```js
5605
4786
  let post = store.peekRecord({ lid });
@@ -5645,430 +4826,73 @@ class Store extends BaseClass {
5645
4826
  }
5646
4827
 
5647
4828
  /**
5648
- This method delegates a query to the adapter. This is the one place where
5649
- adapter-level semantics are exposed to the application.
5650
- Each time this method is called a new request is made through the adapter.
5651
- Exposing queries this way seems preferable to creating an abstract query
5652
- language for all server-side queries, and then require all adapters to
5653
- implement them.
5654
- ---
5655
- If you do something like this:
5656
- ```javascript
5657
- store.query('person', { page: 1 });
5658
- ```
5659
- The request made to the server will look something like this:
5660
- ```
5661
- GET "/api/v1/person?page=1"
5662
- ```
5663
- ---
5664
- If you do something like this:
4829
+ This method returns a filtered array that contains all of the
4830
+ known records for a given type in the store.
4831
+ Note that because it's just a filter, the result will contain any
4832
+ locally created records of the type, however, it will not make a
4833
+ request to the backend to retrieve additional records. If you
4834
+ would like to request all the records from the backend please use
4835
+ [store.findAll](../methods/findAll?anchor=findAll).
4836
+ Also note that multiple calls to `peekAll` for a given type will always
4837
+ return the same `RecordArray`.
4838
+ Example
5665
4839
  ```javascript
5666
- store.query('person', { ids: ['1', '2', '3'] });
5667
- ```
5668
- The request made to the server will look something like this:
5669
- ```
5670
- GET "/api/v1/person?ids%5B%5D=1&ids%5B%5D=2&ids%5B%5D=3"
5671
- decoded: "/api/v1/person?ids[]=1&ids[]=2&ids[]=3"
4840
+ let localPosts = store.peekAll('post');
5672
4841
  ```
5673
- This method returns a promise, which is resolved with a
5674
- [`Collection`](/ember-data/release/classes/Collection)
5675
- once the server returns.
5676
4842
  @since 1.13.0
5677
4843
  @public
5678
4844
  @param {String} type the name of the resource
5679
- @param {Object} query a query to be used by the adapter
5680
- @param {Object} options optional, may include `adapterOptions` hash which will be passed to adapter.query
5681
- @return {Promise} promise
4845
+ @return {RecordArray}
5682
4846
  */
5683
4847
 
5684
- query(type, query, options = {}) {
4848
+ peekAll(type) {
5685
4849
  if (macroCondition(getGlobalConfig().WarpDrive.env.DEBUG)) {
5686
- assertDestroyingStore(this, 'query');
4850
+ assertDestroyingStore(this, 'peekAll');
5687
4851
  }
5688
4852
  macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5689
4853
  if (!test) {
5690
- throw new Error(`You need to pass a model name to the store's query method`);
4854
+ throw new Error(`You need to pass a model name to the store's peekAll method`);
5691
4855
  }
5692
4856
  })(type) : {};
5693
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5694
- if (!test) {
5695
- throw new Error(`You need to pass a query hash to the store's query method`);
5696
- }
5697
- })(query) : {};
5698
4857
  macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5699
4858
  if (!test) {
5700
4859
  throw new Error(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${type}`);
5701
4860
  }
5702
4861
  })(typeof type === 'string') : {};
5703
- const promise = this.request({
5704
- op: 'query',
5705
- data: {
5706
- type: normalizeModelName(type),
5707
- query,
5708
- options: options
5709
- },
5710
- cacheOptions: {
5711
- [SkipCache]: true
5712
- }
5713
- });
5714
- return promise.then(document => document.content);
4862
+ return this.recordArrayManager.liveArrayFor(normalizeModelName(type));
5715
4863
  }
5716
4864
 
5717
4865
  /**
5718
- This method makes a request for one record, where the `id` is not known
5719
- beforehand (if the `id` is known, use [`findRecord`](../methods/findRecord?anchor=findRecord)
5720
- instead).
5721
- This method can be used when it is certain that the server will return a
5722
- single object for the primary data.
5723
- Each time this method is called a new request is made through the adapter.
5724
- Let's assume our API provides an endpoint for the currently logged in user
5725
- via:
5726
- ```
5727
- // GET /api/current_user
5728
- {
5729
- user: {
5730
- id: 1234,
5731
- username: 'admin'
5732
- }
5733
- }
5734
- ```
5735
- Since the specific `id` of the `user` is not known beforehand, we can use
5736
- `queryRecord` to get the user:
5737
- ```javascript
5738
- store.queryRecord('user', {}).then(function(user) {
5739
- let username = user.username;
5740
- // do thing
5741
- });
5742
- ```
5743
- The request is made through the adapters' `queryRecord`:
5744
- ```js [app/adapters/user.js]
5745
- import Adapter from '@ember-data/adapter';
5746
- import $ from 'jquery';
5747
- export default class UserAdapter extends Adapter {
5748
- queryRecord(modelName, query) {
5749
- return $.getJSON('/api/current_user');
5750
- }
5751
- }
5752
- ```
5753
- Note: the primary use case for `store.queryRecord` is when a single record
5754
- is queried and the `id` is not known beforehand. In all other cases
5755
- `store.query` and using the first item of the array is likely the preferred
5756
- way:
5757
- ```
5758
- // GET /users?username=unique
5759
- {
5760
- data: [{
5761
- id: 1234,
5762
- type: 'user',
5763
- attributes: {
5764
- username: "unique"
5765
- }
5766
- }]
5767
- }
5768
- ```
5769
- ```javascript
5770
- store.query('user', { username: 'unique' }).then(function(users) {
5771
- return users.firstObject;
5772
- }).then(function(user) {
5773
- let id = user.id;
5774
- });
5775
- ```
5776
- This method returns a promise, which resolves with the found record.
5777
- If the adapter returns no data for the primary data of the payload, then
5778
- `queryRecord` resolves with `null`:
5779
- ```
5780
- // GET /users?username=unique
5781
- {
5782
- data: null
5783
- }
5784
- ```
4866
+ This method unloads all records in the store.
4867
+ It schedules unloading to happen during the next run loop.
4868
+ Optionally you can pass a type which unload all records for a given type.
5785
4869
  ```javascript
5786
- store.queryRecord('user', { username: 'unique' }).then(function(user) {
5787
- // user is null
5788
- });
4870
+ store.unloadAll();
4871
+ store.unloadAll('post');
5789
4872
  ```
5790
- @since 1.13.0
4873
+ @param {String} type the name of the resource
5791
4874
  @public
5792
- @param {String} type
5793
- @param {Object} query an opaque query to be used by the adapter
5794
- @param {Object} options optional, may include `adapterOptions` hash which will be passed to adapter.queryRecord
5795
- @return {Promise} promise which resolves with the found record or `null`
5796
4875
  */
5797
4876
 
5798
- queryRecord(type, query, options) {
4877
+ unloadAll(type) {
5799
4878
  if (macroCondition(getGlobalConfig().WarpDrive.env.DEBUG)) {
5800
- assertDestroyingStore(this, 'queryRecord');
4879
+ assertDestroyedStoreOnly(this, 'unloadAll');
5801
4880
  }
5802
4881
  macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5803
4882
  if (!test) {
5804
- throw new Error(`You need to pass a model name to the store's queryRecord method`);
4883
+ throw new Error(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${String(type)}`);
5805
4884
  }
5806
- })(type) : {};
5807
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5808
- if (!test) {
5809
- throw new Error(`You need to pass a query hash to the store's queryRecord method`);
5810
- }
5811
- })(query) : {};
5812
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5813
- if (!test) {
5814
- throw new Error(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${type}`);
5815
- }
5816
- })(typeof type === 'string') : {};
5817
- const promise = this.request({
5818
- op: 'queryRecord',
5819
- data: {
5820
- type: normalizeModelName(type),
5821
- query,
5822
- options: options || {}
5823
- },
5824
- cacheOptions: {
5825
- [SkipCache]: true
5826
- }
5827
- });
5828
- return promise.then(document => document.content);
5829
- }
5830
-
5831
- /**
5832
- `findAll` asks the adapter's `findAll` method to find the records for the
5833
- given type, and returns a promise which will resolve with all records of
5834
- this type present in the store, even if the adapter only returns a subset
5835
- of them.
5836
- ```js [app/routes/authors.js]
5837
- export default class AuthorsRoute extends Route {
5838
- model(params) {
5839
- return this.store.findAll('author');
5840
- }
5841
- }
5842
- ```
5843
- _When_ the returned promise resolves depends on the reload behavior,
5844
- configured via the passed `options` hash and the result of the adapter's
5845
- `shouldReloadAll` method.
5846
- ### Reloading
5847
- If `{ reload: true }` is passed or `adapter.shouldReloadAll` evaluates to
5848
- `true`, then the returned promise resolves once the adapter returns data,
5849
- regardless if there are already records in the store:
5850
- ```js
5851
- store.push({
5852
- data: {
5853
- id: 'first',
5854
- type: 'author'
5855
- }
5856
- });
5857
- // adapter#findAll resolves with
5858
- // [
5859
- // {
5860
- // id: 'second',
5861
- // type: 'author'
5862
- // }
5863
- // ]
5864
- store.findAll('author', { reload: true }).then(function(authors) {
5865
- authors.getEach('id'); // ['first', 'second']
5866
- });
5867
- ```
5868
- If no reload is indicated via the above mentioned ways, then the promise
5869
- immediately resolves with all the records currently loaded in the store.
5870
- ### Background Reloading
5871
- Optionally, if `adapter.shouldBackgroundReloadAll` evaluates to `true`,
5872
- then a background reload is started. Once this resolves, the array with
5873
- which the promise resolves, is updated automatically so it contains all the
5874
- records in the store:
5875
- ```js [app/adapters/application.js]
5876
- import Adapter from '@ember-data/adapter';
5877
- export default class ApplicationAdapter extends Adapter {
5878
- shouldReloadAll(store, snapshotsArray) {
5879
- return false;
5880
- },
5881
- shouldBackgroundReloadAll(store, snapshotsArray) {
5882
- return true;
5883
- }
5884
- });
5885
- // ...
5886
- store.push({
5887
- data: {
5888
- id: 'first',
5889
- type: 'author'
5890
- }
5891
- });
5892
- let allAuthors;
5893
- store.findAll('author').then(function(authors) {
5894
- authors.getEach('id'); // ['first']
5895
- allAuthors = authors;
5896
- });
5897
- // later, once adapter#findAll resolved with
5898
- // [
5899
- // {
5900
- // id: 'second',
5901
- // type: 'author'
5902
- // }
5903
- // ]
5904
- allAuthors.getEach('id'); // ['first', 'second']
5905
- ```
5906
- If you would like to force or prevent background reloading, you can set a
5907
- boolean value for `backgroundReload` in the options object for
5908
- `findAll`.
5909
- ```js [app/routes/post/edit.js]
5910
- export default class PostEditRoute extends Route {
5911
- model() {
5912
- return this.store.findAll('post', { backgroundReload: false });
5913
- }
5914
- }
5915
- ```
5916
- If you pass an object on the `adapterOptions` property of the options
5917
- argument it will be passed to you adapter via the `snapshotRecordArray`
5918
- ```js [app/routes/posts.js]
5919
- export default class PostsRoute extends Route {
5920
- model(params) {
5921
- return this.store.findAll('post', {
5922
- adapterOptions: { subscribe: false }
5923
- });
5924
- }
5925
- }
5926
- ```
5927
- ```js [app/adapters/post.js]
5928
- import MyCustomAdapter from './custom-adapter';
5929
- export default class UserAdapter extends MyCustomAdapter {
5930
- findAll(store, type, sinceToken, snapshotRecordArray) {
5931
- if (snapshotRecordArray.adapterOptions.subscribe) {
5932
- // ...
5933
- }
5934
- // ...
5935
- }
5936
- }
5937
- ```
5938
- See [peekAll](../methods/peekAll?anchor=peekAll) to get an array of current records in the
5939
- store, without waiting until a reload is finished.
5940
- ### Retrieving Related Model Records
5941
- If you use an adapter such as Ember's default
5942
- [`JSONAPIAdapter`](/ember-data/release/classes/JSONAPIAdapter)
5943
- that supports the [JSON API specification](http://jsonapi.org/) and if your server
5944
- endpoint supports the use of an
5945
- ['include' query parameter](http://jsonapi.org/format/#fetching-includes),
5946
- you can use `findAll()` to automatically retrieve additional records related to
5947
- those requested by supplying an `include` parameter in the `options` object.
5948
- For example, given a `post` model that has a `hasMany` relationship with a `comment`
5949
- model, when we retrieve all of the post records we can have the server also return
5950
- all of the posts' comments in the same request:
5951
- ```js [app/routes/posts.js]
5952
- export default class PostsRoute extends Route {
5953
- model() {
5954
- return this.store.findAll('post', { include: ['comments'] });
5955
- }
5956
- }
5957
- ```
5958
- Multiple relationships can be requested using an `include` parameter consisting of a
5959
- list or relationship names, while nested relationships can be specified
5960
- using a dot-separated sequence of relationship names. So to request both the posts'
5961
- comments and the authors of those comments the request would look like this:
5962
- ```js [app/routes/posts.js]
5963
- export default class PostsRoute extends Route {
5964
- model() {
5965
- return this.store.findAll('post', { include: ['comments','comments.author'] });
5966
- }
5967
- }
5968
- ```
5969
- See [query](../methods/query?anchor=query) to only get a subset of records from the server.
5970
- @since 1.13.0
5971
- @public
5972
- @param {String} type the name of the resource
5973
- @param {Object} options
5974
- @return {Promise} promise
5975
- */
5976
-
5977
- findAll(type, options = {}) {
5978
- if (macroCondition(getGlobalConfig().WarpDrive.env.DEBUG)) {
5979
- assertDestroyingStore(this, 'findAll');
5980
- }
5981
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5982
- if (!test) {
5983
- throw new Error(`You need to pass a model name to the store's findAll method`);
5984
- }
5985
- })(type) : {};
5986
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5987
- if (!test) {
5988
- throw new Error(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${type}`);
5989
- }
5990
- })(typeof type === 'string') : {};
5991
- const promise = this.request({
5992
- op: 'findAll',
5993
- data: {
5994
- type: normalizeModelName(type),
5995
- options: options || {}
5996
- },
5997
- cacheOptions: {
5998
- [SkipCache]: true
5999
- }
6000
- });
6001
- return promise.then(document => document.content);
6002
- }
6003
-
6004
- /**
6005
- This method returns a filtered array that contains all of the
6006
- known records for a given type in the store.
6007
- Note that because it's just a filter, the result will contain any
6008
- locally created records of the type, however, it will not make a
6009
- request to the backend to retrieve additional records. If you
6010
- would like to request all the records from the backend please use
6011
- [store.findAll](../methods/findAll?anchor=findAll).
6012
- Also note that multiple calls to `peekAll` for a given type will always
6013
- return the same `RecordArray`.
6014
- Example
6015
- ```javascript
6016
- let localPosts = store.peekAll('post');
6017
- ```
6018
- @since 1.13.0
6019
- @public
6020
- @param {String} type the name of the resource
6021
- @return {RecordArray}
6022
- */
6023
-
6024
- peekAll(type) {
6025
- if (macroCondition(getGlobalConfig().WarpDrive.env.DEBUG)) {
6026
- assertDestroyingStore(this, 'peekAll');
6027
- }
6028
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
6029
- if (!test) {
6030
- throw new Error(`You need to pass a model name to the store's peekAll method`);
6031
- }
6032
- })(type) : {};
6033
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
6034
- if (!test) {
6035
- throw new Error(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${type}`);
6036
- }
6037
- })(typeof type === 'string') : {};
6038
- return this.recordArrayManager.liveArrayFor(normalizeModelName(type));
6039
- }
6040
-
6041
- /**
6042
- This method unloads all records in the store.
6043
- It schedules unloading to happen during the next run loop.
6044
- Optionally you can pass a type which unload all records for a given type.
6045
- ```javascript
6046
- store.unloadAll();
6047
- store.unloadAll('post');
6048
- ```
6049
- @param {String} type the name of the resource
6050
- @public
6051
- */
6052
-
6053
- unloadAll(type) {
6054
- if (macroCondition(getGlobalConfig().WarpDrive.env.DEBUG)) {
6055
- assertDestroyedStoreOnly(this, 'unloadAll');
6056
- }
6057
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
6058
- if (!test) {
6059
- throw new Error(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${String(type)}`);
6060
- }
6061
- })(!type || typeof type === 'string') : {};
6062
- this._join(() => {
6063
- if (type === undefined) {
6064
- // destroy the graph before unloadAll
6065
- // since then we avoid churning relationships
6066
- // during unload
6067
- this._graph?.identifiers.clear();
6068
- this.recordArrayManager.clear();
6069
- this._instanceCache.clear();
6070
- } else {
6071
- this._instanceCache.clear(normalizeModelName(type));
4885
+ })(!type || typeof type === 'string') : {};
4886
+ this._join(() => {
4887
+ if (type === undefined) {
4888
+ // destroy the graph before unloadAll
4889
+ // since then we avoid churning relationships
4890
+ // during unload
4891
+ this._graph?.identifiers.clear();
4892
+ this.recordArrayManager.clear();
4893
+ this._instanceCache.clear();
4894
+ } else {
4895
+ this._instanceCache.clear(normalizeModelName(type));
6072
4896
  }
6073
4897
  });
6074
4898
  }
@@ -6241,81 +5065,10 @@ class Store extends BaseClass {
6241
5065
  return 'data' in ret ? ret.data : null;
6242
5066
  }
6243
5067
 
6244
- /**
6245
- * Trigger a save for a Record.
6246
- *
6247
- * Returns a promise resolving with the same record when the save is complete.
6248
- *
6249
- * @public
6250
- * @param {unknown} record
6251
- * @param options
6252
- * @return {Promise<record>}
6253
- */
6254
- saveRecord(record, options = {}) {
6255
- if (macroCondition(getGlobalConfig().WarpDrive.env.DEBUG)) {
6256
- assertDestroyingStore(this, 'saveRecord');
6257
- }
6258
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
6259
- if (!test) {
6260
- throw new Error(`Unable to initiate save for a record in a disconnected state`);
6261
- }
6262
- })(storeFor(record)) : {};
6263
- const identifier = recordIdentifierFor(record);
6264
- const cache = this.cache;
6265
- if (!identifier) {
6266
- // this commonly means we're disconnected
6267
- // but just in case we reject here to prevent bad things.
6268
- return Promise.reject(new Error(`Record Is Disconnected`));
6269
- }
6270
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
6271
- if (!test) {
6272
- throw new Error(`Cannot initiate a save request for an unloaded record: ${identifier.lid}`);
6273
- }
6274
- })(this._instanceCache.recordIsLoaded(identifier)) : {};
6275
- if (resourceIsFullyDeleted(this._instanceCache, identifier)) {
6276
- return Promise.resolve(record);
6277
- }
6278
- if (!options) {
6279
- options = {};
6280
- }
6281
- let operation = 'updateRecord';
6282
- if (cache.isNew(identifier)) {
6283
- operation = 'createRecord';
6284
- } else if (cache.isDeleted(identifier)) {
6285
- operation = 'deleteRecord';
6286
- }
6287
- const request = {
6288
- op: operation,
6289
- data: {
6290
- options,
6291
- record: identifier
6292
- },
6293
- records: [identifier],
6294
- cacheOptions: {
6295
- [SkipCache]: true
6296
- }
6297
- };
6298
- return this.request(request).then(document => document.content);
6299
- }
6300
-
6301
- /**
6302
- * Instantiation hook allowing applications or addons to configure the store
6303
- * to utilize a custom Cache implementation.
6304
- *
6305
- * This hook should not be called directly by consuming applications or libraries.
6306
- * Use `Store.cache` to access the Cache instance.
6307
- *
6308
- * @public
6309
- * @param storeWrapper
6310
- * @return {Cache}
6311
- */
6312
-
6313
5068
  /**
6314
5069
  * Returns the cache instance associated to this Store, instantiates the Cache
6315
5070
  * if necessary via `Store.createCache`
6316
5071
  *
6317
- * @property cache
6318
- * @type {Cache}
6319
5072
  * @public
6320
5073
  */
6321
5074
  get cache() {
@@ -6330,6 +5083,8 @@ class Store extends BaseClass {
6330
5083
  }
6331
5084
  return cache;
6332
5085
  }
5086
+
5087
+ /** @internal */
6333
5088
  destroy() {
6334
5089
  if (this.isDestroyed) {
6335
5090
  // @ember/test-helpers will call destroy multiple times
@@ -6344,6 +5099,12 @@ class Store extends BaseClass {
6344
5099
  this.unloadAll();
6345
5100
  this.isDestroyed = true;
6346
5101
  }
5102
+
5103
+ /**
5104
+ * This method
5105
+ *
5106
+ * @private
5107
+ */
6347
5108
  static create(args) {
6348
5109
  return new this(args);
6349
5110
  }
@@ -6487,6 +5248,748 @@ function extractIdentifierFromRecord$1(recordOrPromiseRecord) {
6487
5248
  const extract = recordIdentifierFor;
6488
5249
  return extract(recordOrPromiseRecord);
6489
5250
  }
5251
+
5252
+ /*
5253
+ When a find request is triggered on the store, the user can optionally pass in
5254
+ attributes and relationships to be preloaded. These are meant to behave as if they
5255
+ came back from the server, except the user obtained them out of band and is informing
5256
+ the store of their existence. The most common use case is for supporting client side
5257
+ nested URLs, such as `/posts/1/comments/2` so the user can do
5258
+ `store.findRecord('comment', 2, { preload: { post: 1 } })` without having to fetch the post.
5259
+
5260
+ Preloaded data can be attributes and relationships passed in either as IDs or as actual
5261
+ models.
5262
+ */
5263
+ function preloadData(store, identifier, preload) {
5264
+ const jsonPayload = {};
5265
+ //TODO(Igor) consider the polymorphic case
5266
+ const schemas = store.schema;
5267
+ const fields = schemas.fields(identifier);
5268
+ Object.keys(preload).forEach(key => {
5269
+ const preloadValue = preload[key];
5270
+ const field = fields.get(key);
5271
+ if (field && (field.kind === 'hasMany' || field.kind === 'belongsTo')) {
5272
+ if (!jsonPayload.relationships) {
5273
+ jsonPayload.relationships = {};
5274
+ }
5275
+ jsonPayload.relationships[key] = preloadRelationship(field, preloadValue);
5276
+ } else {
5277
+ if (!jsonPayload.attributes) {
5278
+ jsonPayload.attributes = {};
5279
+ }
5280
+ jsonPayload.attributes[key] = preloadValue;
5281
+ }
5282
+ });
5283
+ const cache = store.cache;
5284
+ const hasRecord = Boolean(store._instanceCache.peek(identifier));
5285
+ cache.upsert(identifier, jsonPayload, hasRecord);
5286
+ }
5287
+ function preloadRelationship(schema, preloadValue) {
5288
+ const relatedType = schema.type;
5289
+ if (schema.kind === 'hasMany') {
5290
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5291
+ if (!test) {
5292
+ throw new Error('You need to pass in an array to set a hasMany property on a record');
5293
+ }
5294
+ })(Array.isArray(preloadValue)) : {};
5295
+ return {
5296
+ data: preloadValue.map(value => _convertPreloadRelationshipToJSON(value, relatedType))
5297
+ };
5298
+ }
5299
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5300
+ if (!test) {
5301
+ throw new Error('You should not pass in an array to set a belongsTo property on a record');
5302
+ }
5303
+ })(!Array.isArray(preloadValue)) : {};
5304
+ return {
5305
+ data: preloadValue ? _convertPreloadRelationshipToJSON(preloadValue, relatedType) : null
5306
+ };
5307
+ }
5308
+
5309
+ /*
5310
+ findRecord('user', '1', { preload: { friends: ['1'] }});
5311
+ findRecord('user', '1', { preload: { friends: [record] }});
5312
+ */
5313
+ function _convertPreloadRelationshipToJSON(value, type) {
5314
+ if (typeof value === 'string' || typeof value === 'number') {
5315
+ return {
5316
+ type,
5317
+ id: ensureStringId(value)
5318
+ };
5319
+ }
5320
+ // TODO if not a record instance assert it's an identifier
5321
+ // and allow identifiers to be used
5322
+ return recordIdentifierFor(value);
5323
+ }
5324
+
5325
+ /**
5326
+ * Minimum subset of static schema methods and properties on the
5327
+ * "model" class.
5328
+ *
5329
+ * Only used when using the legacy schema-service implementation
5330
+ * for @ember-data/model or when wrapping schema for legacy
5331
+ * Adapters/Serializers.
5332
+ *
5333
+ */
5334
+
5335
+ function _resourceIsFullDeleted(identifier, cache) {
5336
+ return cache.isDeletionCommitted(identifier) || cache.isNew(identifier) && cache.isDeleted(identifier);
5337
+ }
5338
+ function resourceIsFullyDeleted(instanceCache, identifier) {
5339
+ const cache = instanceCache.cache;
5340
+ return !cache || _resourceIsFullDeleted(identifier, cache);
5341
+ }
5342
+
5343
+ /**
5344
+ A `RecordReference` is a low-level API that allows users and
5345
+ addon authors to perform meta-operations on a record.
5346
+
5347
+ @hideconstructor
5348
+ @public
5349
+ */
5350
+ class RecordReference {
5351
+ /** @internal */
5352
+
5353
+ // unsubscribe token given to us by the notification manager
5354
+ /** @internal */
5355
+ ___token;
5356
+ /** @internal */
5357
+ ___identifier;
5358
+ /** @internal */
5359
+
5360
+ constructor(store, identifier) {
5361
+ this.store = store;
5362
+ this.___identifier = identifier;
5363
+ this.___token = store.notifications.subscribe(identifier, (_, bucket, notifiedKey) => {
5364
+ if (bucket === 'identity' || bucket === 'attributes' && notifiedKey === 'id') {
5365
+ this._ref++;
5366
+ }
5367
+ });
5368
+ }
5369
+
5370
+ /** @internal */
5371
+ destroy() {
5372
+ this.store.notifications.unsubscribe(this.___token);
5373
+ }
5374
+ get type() {
5375
+ return this.identifier().type;
5376
+ }
5377
+
5378
+ /**
5379
+ The `id` of the record that this reference refers to.
5380
+ Together, the `type` and `id` properties form a composite key for
5381
+ the identity map.
5382
+ Example
5383
+ ```javascript
5384
+ let userRef = store.getReference('user', 1);
5385
+ userRef.id(); // '1'
5386
+ ```
5387
+ @public
5388
+ @return The id of the record.
5389
+ */
5390
+ id() {
5391
+ // eslint-disable-next-line @typescript-eslint/no-unused-expressions
5392
+ this._ref; // consume the tracked prop
5393
+ return this.___identifier.id;
5394
+ }
5395
+
5396
+ /**
5397
+ The `identifier` of the record that this reference refers to.
5398
+ Together, the `type` and `id` properties form a composite key for
5399
+ the identity map.
5400
+ Example
5401
+ ```javascript
5402
+ let userRef = store.getReference('user', 1);
5403
+ userRef.identifier(); // '1'
5404
+ ```
5405
+ @public
5406
+ @return The identifier of the record.
5407
+ */
5408
+ identifier() {
5409
+ return this.___identifier;
5410
+ }
5411
+
5412
+ /**
5413
+ How the reference will be looked up when it is loaded. Currently
5414
+ this always returns `identity` to signify that a record will be
5415
+ loaded by its `type` and `id`.
5416
+ Example
5417
+ ```javascript
5418
+ const userRef = store.getReference('user', 1);
5419
+ userRef.remoteType(); // 'identity'
5420
+ ```
5421
+ @public
5422
+ */
5423
+ remoteType() {
5424
+ return 'identity';
5425
+ }
5426
+
5427
+ /**
5428
+ This API allows you to provide a reference with new data. The
5429
+ simplest usage of this API is similar to `store.push`: you provide a
5430
+ normalized hash of data and the object represented by the reference
5431
+ will update.
5432
+ If you pass a promise to `push`, Ember Data will not ask the adapter
5433
+ for the data if another attempt to fetch it is made in the
5434
+ interim. When the promise resolves, the underlying object is updated
5435
+ with the new data, and the promise returned by *this function* is resolved
5436
+ with that object.
5437
+ For example, `recordReference.push(promise)` will be resolved with a
5438
+ record.
5439
+ Example
5440
+ ```javascript
5441
+ let userRef = store.getReference('user', 1);
5442
+ // provide data for reference
5443
+ userRef.push({
5444
+ data: {
5445
+ id: "1",
5446
+ type: "user",
5447
+ attributes: {
5448
+ username: "@user"
5449
+ }
5450
+ }
5451
+ }).then(function(user) {
5452
+ userRef.value() === user;
5453
+ });
5454
+ ```
5455
+ @public
5456
+ @param objectOrPromise a JSON:API ResourceDocument or a promise resolving to one
5457
+ @return a promise for the value (record or relationship)
5458
+ */
5459
+ push(objectOrPromise) {
5460
+ // TODO @deprecate pushing unresolved payloads
5461
+ return Promise.resolve(objectOrPromise).then(data => {
5462
+ return this.store.push(data);
5463
+ });
5464
+ }
5465
+
5466
+ /**
5467
+ If the entity referred to by the reference is already loaded, it is
5468
+ present as `reference.value`. Otherwise the value returned by this function
5469
+ is `null`.
5470
+ Example
5471
+ ```javascript
5472
+ let userRef = store.getReference('user', 1);
5473
+ userRef.value(); // user
5474
+ ```
5475
+ @public
5476
+ @return the record for this RecordReference
5477
+ */
5478
+ value() {
5479
+ return this.store.peekRecord(this.___identifier);
5480
+ }
5481
+
5482
+ /**
5483
+ Triggers a fetch for the backing entity based on its `remoteType`
5484
+ (see `remoteType` definitions per reference type).
5485
+ Example
5486
+ ```javascript
5487
+ let userRef = store.getReference('user', 1);
5488
+ // load user (via store.find)
5489
+ userRef.load().then(...)
5490
+ ```
5491
+ @public
5492
+ @return the record for this RecordReference
5493
+ */
5494
+ load() {
5495
+ const id = this.id();
5496
+ if (id !== null) {
5497
+ return this.store.findRecord(this.type, id);
5498
+ }
5499
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5500
+ {
5501
+ throw new Error(`Unable to fetch record of type ${this.type} without an id`);
5502
+ }
5503
+ })() : {};
5504
+ }
5505
+
5506
+ /**
5507
+ Reloads the record if it is already loaded. If the record is not
5508
+ loaded it will load the record via `store.findRecord`
5509
+ Example
5510
+ ```javascript
5511
+ let userRef = store.getReference('user', 1);
5512
+ // or trigger a reload
5513
+ userRef.reload().then(...)
5514
+ ```
5515
+ @public
5516
+ @return the record for this RecordReference
5517
+ */
5518
+ reload() {
5519
+ const id = this.id();
5520
+ if (id !== null) {
5521
+ return this.store.findRecord(this.type, id, {
5522
+ reload: true
5523
+ });
5524
+ }
5525
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5526
+ {
5527
+ throw new Error(`Unable to fetch record of type ${this.type} without an id`);
5528
+ }
5529
+ })() : {};
5530
+ }
5531
+ }
5532
+ defineSignal(RecordReference.prototype, '_ref');
5533
+
5534
+ // if modelFor turns out to be a bottleneck we should replace with a Map
5535
+ // and clear it during store teardown.
5536
+ const AvailableShims = getOrSetGlobal('AvailableShims', new WeakMap());
5537
+ function getShimClass(store, modelName) {
5538
+ let shims = AvailableShims.get(store);
5539
+ if (!shims) {
5540
+ shims = Object.create(null);
5541
+ AvailableShims.set(store, shims);
5542
+ }
5543
+ let shim = shims[modelName];
5544
+ if (shim === undefined) {
5545
+ shim = shims[modelName] = new ShimModelClass(store, modelName);
5546
+ }
5547
+ return shim;
5548
+ }
5549
+
5550
+ // Mimics the static apis of @ember-data/model
5551
+ class ShimModelClass {
5552
+ constructor(store, modelName) {
5553
+ this.__store = store;
5554
+ this.modelName = modelName;
5555
+ }
5556
+ get fields() {
5557
+ const fields = new Map();
5558
+ const fieldSchemas = this.__store.schema.fields({
5559
+ type: this.modelName
5560
+ });
5561
+ fieldSchemas.forEach((schema, key) => {
5562
+ if (schema.kind === 'attribute' || schema.kind === 'belongsTo' || schema.kind === 'hasMany') {
5563
+ fields.set(key, schema.kind);
5564
+ }
5565
+ });
5566
+ return fields;
5567
+ }
5568
+ get attributes() {
5569
+ const attrs = new Map();
5570
+ const fields = this.__store.schema.fields({
5571
+ type: this.modelName
5572
+ });
5573
+ fields.forEach((schema, key) => {
5574
+ if (schema.kind === 'attribute') {
5575
+ attrs.set(key, schema);
5576
+ }
5577
+ });
5578
+ return attrs;
5579
+ }
5580
+ get relationshipsByName() {
5581
+ const rels = new Map();
5582
+ const fields = this.__store.schema.fields({
5583
+ type: this.modelName
5584
+ });
5585
+ fields.forEach((schema, key) => {
5586
+ if (schema.kind === 'belongsTo' || schema.kind === 'hasMany') {
5587
+ rels.set(key, schema);
5588
+ }
5589
+ });
5590
+ return rels;
5591
+ }
5592
+ eachAttribute(callback, binding) {
5593
+ this.__store.schema.fields({
5594
+ type: this.modelName
5595
+ }).forEach((schema, key) => {
5596
+ if (schema.kind === 'attribute') {
5597
+ callback.call(binding, key, schema);
5598
+ }
5599
+ });
5600
+ }
5601
+ eachRelationship(callback, binding) {
5602
+ this.__store.schema.fields({
5603
+ type: this.modelName
5604
+ }).forEach((schema, key) => {
5605
+ if (schema.kind === 'belongsTo' || schema.kind === 'hasMany') {
5606
+ callback.call(binding, key, schema);
5607
+ }
5608
+ });
5609
+ }
5610
+ eachTransformedAttribute(callback, binding) {
5611
+ this.__store.schema.fields({
5612
+ type: this.modelName
5613
+ }).forEach((schema, key) => {
5614
+ if (schema.kind === 'attribute') {
5615
+ const type = schema.type;
5616
+ if (type) callback.call(binding, key, type);
5617
+ }
5618
+ });
5619
+ }
5620
+ }
5621
+ if (macroCondition(getGlobalConfig().WarpDrive.deprecations.ENABLE_LEGACY_REQUEST_METHODS)) {
5622
+ Store.prototype.findRecord = function (resource, id, options) {
5623
+ deprecate(`store.findRecord is deprecated. Use store.request instead.`, false, {
5624
+ id: 'warp-drive:deprecate-legacy-request-methods',
5625
+ until: '6.0',
5626
+ for: '@warp-drive/core',
5627
+ url: 'https://docs.warp-drive.io/api/@warp-drive/core/build-config/deprecations/variables/ENABLE_LEGACY_REQUEST_METHODS',
5628
+ since: {
5629
+ enabled: '5.7',
5630
+ available: '5.7'
5631
+ }
5632
+ });
5633
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5634
+ if (!test) {
5635
+ throw new Error(`Attempted to call store.findRecord(), but the store instance has already been destroyed.`);
5636
+ }
5637
+ })(!(this.isDestroying || this.isDestroyed)) : {};
5638
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5639
+ if (!test) {
5640
+ throw new Error(`You need to pass a modelName or resource identifier as the first argument to the store's findRecord method`);
5641
+ }
5642
+ })(resource) : {};
5643
+ if (isMaybeIdentifier(resource)) {
5644
+ options = id;
5645
+ } else {
5646
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5647
+ if (!test) {
5648
+ throw new Error(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${resource}`);
5649
+ }
5650
+ })(typeof resource === 'string') : {};
5651
+ const type = normalizeModelName(resource);
5652
+ const normalizedId = ensureStringId(id);
5653
+ resource = constructResource(type, normalizedId);
5654
+ }
5655
+ const identifier = this.identifierCache.getOrCreateRecordIdentifier(resource);
5656
+ options = options || {};
5657
+ if (options.preload) {
5658
+ // force reload if we preload to ensure we don't resolve the promise
5659
+ // until we are complete, else we will end up background-reloading
5660
+ // even for initial load.
5661
+ if (!this._instanceCache.recordIsLoaded(identifier)) {
5662
+ options.reload = true;
5663
+ }
5664
+ this._join(() => {
5665
+ preloadData(this, identifier, options.preload);
5666
+ });
5667
+ }
5668
+ const promise = this.request({
5669
+ op: 'findRecord',
5670
+ data: {
5671
+ record: identifier,
5672
+ options
5673
+ },
5674
+ cacheOptions: {
5675
+ [SkipCache]: true
5676
+ }
5677
+ });
5678
+ return promise.then(document => {
5679
+ return document.content;
5680
+ });
5681
+ };
5682
+ Store.prototype.findAll = function (type, options = {}) {
5683
+ deprecate(`store.findAll is deprecated. Use store.request instead.`, false, {
5684
+ id: 'warp-drive:deprecate-legacy-request-methods',
5685
+ until: '6.0',
5686
+ for: '@warp-drive/core',
5687
+ url: 'https://docs.warp-drive.io/api/@warp-drive/core/build-config/deprecations/variables/ENABLE_LEGACY_REQUEST_METHODS',
5688
+ since: {
5689
+ enabled: '5.7',
5690
+ available: '5.7'
5691
+ }
5692
+ });
5693
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5694
+ if (!test) {
5695
+ throw new Error(`Attempted to call store.findAll(), but the store instance has already been destroyed.`);
5696
+ }
5697
+ })(!(this.isDestroying || this.isDestroyed)) : {};
5698
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5699
+ if (!test) {
5700
+ throw new Error(`You need to pass a model name to the store's findAll method`);
5701
+ }
5702
+ })(type) : {};
5703
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5704
+ if (!test) {
5705
+ throw new Error(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${type}`);
5706
+ }
5707
+ })(typeof type === 'string') : {};
5708
+ const promise = this.request({
5709
+ op: 'findAll',
5710
+ data: {
5711
+ type: normalizeModelName(type),
5712
+ options: options || {}
5713
+ },
5714
+ cacheOptions: {
5715
+ [SkipCache]: true
5716
+ }
5717
+ });
5718
+ return promise.then(document => document.content);
5719
+ };
5720
+ Store.prototype.query = function (type, query, options = {}) {
5721
+ deprecate(`store.query is deprecated. Use store.request instead.`, false, {
5722
+ id: 'warp-drive:deprecate-legacy-request-methods',
5723
+ until: '6.0',
5724
+ for: '@warp-drive/core',
5725
+ url: 'https://docs.warp-drive.io/api/@warp-drive/core/build-config/deprecations/variables/ENABLE_LEGACY_REQUEST_METHODS',
5726
+ since: {
5727
+ enabled: '5.7',
5728
+ available: '5.7'
5729
+ }
5730
+ });
5731
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5732
+ if (!test) {
5733
+ throw new Error(`Attempted to call store.query(), but the store instance has already been destroyed.`);
5734
+ }
5735
+ })(!(this.isDestroying || this.isDestroyed)) : {};
5736
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5737
+ if (!test) {
5738
+ throw new Error(`You need to pass a model name to the store's query method`);
5739
+ }
5740
+ })(type) : {};
5741
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5742
+ if (!test) {
5743
+ throw new Error(`You need to pass a query hash to the store's query method`);
5744
+ }
5745
+ })(query) : {};
5746
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5747
+ if (!test) {
5748
+ throw new Error(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${type}`);
5749
+ }
5750
+ })(typeof type === 'string') : {};
5751
+ const promise = this.request({
5752
+ op: 'query',
5753
+ data: {
5754
+ type: normalizeModelName(type),
5755
+ query,
5756
+ options: options
5757
+ },
5758
+ cacheOptions: {
5759
+ [SkipCache]: true
5760
+ }
5761
+ });
5762
+ return promise.then(document => document.content);
5763
+ };
5764
+ Store.prototype.queryRecord = function (type, query, options) {
5765
+ deprecate(`store.queryRecord is deprecated. Use store.request instead.`, false, {
5766
+ id: 'warp-drive:deprecate-legacy-request-methods',
5767
+ until: '6.0',
5768
+ for: '@warp-drive/core',
5769
+ url: 'https://docs.warp-drive.io/api/@warp-drive/core/build-config/deprecations/variables/ENABLE_LEGACY_REQUEST_METHODS',
5770
+ since: {
5771
+ enabled: '5.7',
5772
+ available: '5.7'
5773
+ }
5774
+ });
5775
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5776
+ if (!test) {
5777
+ throw new Error(`Attempted to call store.queryRecord(), but the store instance has already been destroyed.`);
5778
+ }
5779
+ })(!(this.isDestroying || this.isDestroyed)) : {};
5780
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5781
+ if (!test) {
5782
+ throw new Error(`You need to pass a model name to the store's queryRecord method`);
5783
+ }
5784
+ })(type) : {};
5785
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5786
+ if (!test) {
5787
+ throw new Error(`You need to pass a query hash to the store's queryRecord method`);
5788
+ }
5789
+ })(query) : {};
5790
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5791
+ if (!test) {
5792
+ throw new Error(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${type}`);
5793
+ }
5794
+ })(typeof type === 'string') : {};
5795
+ const promise = this.request({
5796
+ op: 'queryRecord',
5797
+ data: {
5798
+ type: normalizeModelName(type),
5799
+ query,
5800
+ options: options || {}
5801
+ },
5802
+ cacheOptions: {
5803
+ [SkipCache]: true
5804
+ }
5805
+ });
5806
+ return promise.then(document => document.content);
5807
+ };
5808
+ Store.prototype.getReference = function (resource, id) {
5809
+ deprecate(`store.getReference is deprecated. There is no direct replacement. For working with the cache and relationships, use the cache with the appropriate identifiers. To load, use store.request.`, false, {
5810
+ id: 'warp-drive:deprecate-legacy-request-methods',
5811
+ until: '6.0',
5812
+ for: '@warp-drive/core',
5813
+ url: 'https://docs.warp-drive.io/api/@warp-drive/core/build-config/deprecations/variables/ENABLE_LEGACY_REQUEST_METHODS',
5814
+ since: {
5815
+ enabled: '5.7',
5816
+ available: '5.7'
5817
+ }
5818
+ });
5819
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5820
+ if (!test) {
5821
+ throw new Error(`Attempted to call store.getReference(), but the store instance has already been destroyed.`);
5822
+ }
5823
+ })(!(this.isDestroying || this.isDestroyed)) : {};
5824
+ let resourceIdentifier;
5825
+ if (arguments.length === 1 && isMaybeIdentifier(resource)) {
5826
+ resourceIdentifier = resource;
5827
+ } else {
5828
+ const type = normalizeModelName(resource);
5829
+ const normalizedId = ensureStringId(id);
5830
+ resourceIdentifier = constructResource(type, normalizedId);
5831
+ }
5832
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5833
+ if (!test) {
5834
+ throw new Error('getReference expected to receive either a resource identifier or type and id as arguments');
5835
+ }
5836
+ })(isMaybeIdentifier(resourceIdentifier)) : {};
5837
+ const identifier = this.identifierCache.getOrCreateRecordIdentifier(resourceIdentifier);
5838
+ const cache = upgradeInstanceCaches(this._instanceCache.__instances).reference;
5839
+ let reference = cache.get(identifier);
5840
+ if (!reference) {
5841
+ reference = new RecordReference(this, identifier);
5842
+ cache.set(identifier, reference);
5843
+ }
5844
+ return reference;
5845
+ };
5846
+ Store.prototype.modelFor = function (type) {
5847
+ deprecate(`store.modelFor is deprecated, please use store.schema.fields({ type: '${type}' }) to access schema information instead.`, false, {
5848
+ id: 'warp-drive:deprecate-legacy-request-methods',
5849
+ until: '6.0',
5850
+ for: '@warp-drive/core',
5851
+ url: 'https://docs.warp-drive.io/api/@warp-drive/core/build-config/deprecations/variables/ENABLE_LEGACY_REQUEST_METHODS',
5852
+ since: {
5853
+ enabled: '5.7',
5854
+ available: '5.7'
5855
+ }
5856
+ });
5857
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5858
+ if (!test) {
5859
+ throw new Error(`Attempted to call store.modelFor(), but the store instance has already been destroyed.`);
5860
+ }
5861
+ })(!this.isDestroyed) : {};
5862
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5863
+ if (!test) {
5864
+ throw new Error(`You need to pass <type> to the store's modelFor method`);
5865
+ }
5866
+ })(typeof type === 'string' && type.length) : {};
5867
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5868
+ if (!test) {
5869
+ throw new Error(`No model was found for '${type}' and no schema handles the type`);
5870
+ }
5871
+ })(this.schema.hasResource({
5872
+ type
5873
+ })) : {};
5874
+ return getShimClass(this, type);
5875
+ };
5876
+ Store.prototype.saveRecord = function (record, options = {}) {
5877
+ deprecate(`store.saveRecord is deprecated, please use store.request to initiate a save request instead.`, false, {
5878
+ id: 'warp-drive:deprecate-legacy-request-methods',
5879
+ until: '6.0',
5880
+ for: '@warp-drive/core',
5881
+ url: 'https://docs.warp-drive.io/api/@warp-drive/core/build-config/deprecations/variables/ENABLE_LEGACY_REQUEST_METHODS',
5882
+ since: {
5883
+ enabled: '5.7',
5884
+ available: '5.7'
5885
+ }
5886
+ });
5887
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5888
+ if (!test) {
5889
+ throw new Error(`Attempted to call store.saveRecord(), but the store instance has already been destroyed.`);
5890
+ }
5891
+ })(!(this.isDestroying || this.isDestroyed)) : {};
5892
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5893
+ if (!test) {
5894
+ throw new Error(`Unable to initiate save for a record in a disconnected state`);
5895
+ }
5896
+ })(storeFor(record)) : {};
5897
+ const identifier = recordIdentifierFor(record);
5898
+ const cache = this.cache;
5899
+ if (!identifier) {
5900
+ // this commonly means we're disconnected
5901
+ // but just in case we reject here to prevent bad things.
5902
+ return Promise.reject(new Error(`Record Is Disconnected`));
5903
+ }
5904
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5905
+ if (!test) {
5906
+ throw new Error(`Cannot initiate a save request for an unloaded record: ${identifier.lid}`);
5907
+ }
5908
+ })(this._instanceCache.recordIsLoaded(identifier)) : {};
5909
+ if (resourceIsFullyDeleted(this._instanceCache, identifier)) {
5910
+ return Promise.resolve(record);
5911
+ }
5912
+ if (!options) {
5913
+ options = {};
5914
+ }
5915
+ let operation = 'updateRecord';
5916
+ if (cache.isNew(identifier)) {
5917
+ operation = 'createRecord';
5918
+ } else if (cache.isDeleted(identifier)) {
5919
+ operation = 'deleteRecord';
5920
+ }
5921
+ const request = {
5922
+ op: operation,
5923
+ data: {
5924
+ options,
5925
+ record: identifier
5926
+ },
5927
+ records: [identifier],
5928
+ cacheOptions: {
5929
+ [SkipCache]: true
5930
+ }
5931
+ };
5932
+ return this.request(request).then(document => document.content);
5933
+ };
5934
+ }
5935
+ function upgradeInstanceCaches(cache) {
5936
+ const withReferences = cache;
5937
+ if (!withReferences.reference) {
5938
+ withReferences.reference = new WeakMap();
5939
+ }
5940
+ return withReferences;
5941
+ }
5942
+ function isNonEmptyString(str) {
5943
+ return Boolean(str && typeof str === 'string');
5944
+ }
5945
+ function constructResource(type, id, lid) {
5946
+ if (typeof type === 'object' && type !== null) {
5947
+ const resource = type;
5948
+ if (isStableIdentifier(resource)) {
5949
+ return resource;
5950
+ }
5951
+ if ('id' in resource) {
5952
+ resource.id = coerceId(resource.id);
5953
+ }
5954
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5955
+ if (!test) {
5956
+ throw new Error('Expected either id or lid to be a valid string');
5957
+ }
5958
+ })('id' in resource && isNonEmptyString(resource.id) || isNonEmptyString(resource.lid)) : {};
5959
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5960
+ if (!test) {
5961
+ throw new Error('if id is present, the type must be a string');
5962
+ }
5963
+ })(!('id' in resource) || typeof resource.type === 'string') : {};
5964
+ return resource;
5965
+ } else {
5966
+ const trueId = coerceId(id);
5967
+ if (!isNonEmptyString(trueId)) {
5968
+ if (isNonEmptyString(lid)) {
5969
+ return {
5970
+ lid
5971
+ };
5972
+ }
5973
+ throw new Error('Expected either id or lid to be a valid string');
5974
+ }
5975
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5976
+ if (!test) {
5977
+ throw new Error('type must be a string');
5978
+ }
5979
+ })(typeof type === 'string') : {};
5980
+ if (isNonEmptyString(lid)) {
5981
+ return {
5982
+ type,
5983
+ id: trueId,
5984
+ lid
5985
+ };
5986
+ }
5987
+ return {
5988
+ type,
5989
+ id: trueId
5990
+ };
5991
+ }
5992
+ }
6490
5993
  function _MUTATE(target, receiver, prop, args, _SIGNAL) {
6491
5994
  const collection = receiver;
6492
5995
  switch (prop) {
@@ -8136,4 +7639,4 @@ function getRequestState(future) {
8136
7639
  }
8137
7640
  return state;
8138
7641
  }
8139
- export { Signals as A, peekInternalSignal as B, Collection as C, DISPOSE as D, withSignalStore as E, notifyInternalSignal as F, consumeInternalSignal as G, getOrCreateInternalSignal as H, IdentifierArray as I, ReactiveDocument as J, setIdentifierGenerationMethod as K, setIdentifierUpdateMethod as L, MUTATE as M, setIdentifierForgetMethod as N, setIdentifierResetMethod as O, setKeyInfoForResource as P, isExtensionProp as Q, RecordArrayManager as R, Store as S, performExtensionSet as T, performArrayExtensionGet as U, performObjectExtensionGet as V, _clearCaches as _, isDocumentIdentifier as a, coerceId as b, constructResource as c, SOURCE as d, ensureStringId as e, fastPush as f, removeRecordDataFor as g, setRecordIdentifier as h, isStableIdentifier as i, StoreMap as j, setCacheFor as k, RelatedCollection as l, log as m, normalizeModelName as n, logGroup as o, peekCache as p, getPromiseState as q, recordIdentifierFor as r, storeFor as s, createRequestSubscription as t, getRequestState as u, memoized as v, gate as w, entangleSignal as x, defineSignal as y, defineNonEnumerableSignal as z };
7642
+ export { defineNonEnumerableSignal as A, Signals as B, Collection as C, DISPOSE as D, peekInternalSignal as E, withSignalStore as F, notifyInternalSignal as G, consumeInternalSignal as H, IdentifierArray as I, getOrCreateInternalSignal as J, ReactiveDocument as K, setIdentifierGenerationMethod as L, MUTATE as M, setIdentifierUpdateMethod as N, setIdentifierForgetMethod as O, setIdentifierResetMethod as P, setKeyInfoForResource as Q, RecordArrayManager as R, Store as S, isExtensionProp as T, performExtensionSet as U, performArrayExtensionGet as V, performObjectExtensionGet as W, _clearCaches as _, isDocumentIdentifier as a, coerceId as b, constructResource as c, SOURCE as d, ensureStringId as e, fastPush as f, removeRecordDataFor as g, setRecordIdentifier as h, isStableIdentifier as i, StoreMap as j, setCacheFor as k, RelatedCollection as l, log as m, normalizeModelName as n, logGroup as o, peekCache as p, getPromiseState as q, recordIdentifierFor as r, storeFor as s, createRequestSubscription as t, getRequestState as u, memoized as v, gate as w, entangleSignal as x, defineSignal as y, defineGate as z };