@ember-data/store 5.4.0-beta.18 → 5.4.0-beta.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. package/dist/-private.js +1 -1
  2. package/dist/index.js +1 -1
  3. package/dist/{many-array-uP6jS6_J.js → many-array-BwVo-2vv.js} +395 -346
  4. package/dist/many-array-BwVo-2vv.js.map +1 -0
  5. package/package.json +21 -21
  6. package/unstable-preview-types/-private/cache-handler/handler.d.ts.map +1 -1
  7. package/unstable-preview-types/-private/cache-handler/utils.d.ts +0 -5
  8. package/unstable-preview-types/-private/cache-handler/utils.d.ts.map +1 -1
  9. package/unstable-preview-types/-private/caches/identifier-cache.d.ts +7 -3
  10. package/unstable-preview-types/-private/caches/identifier-cache.d.ts.map +1 -1
  11. package/unstable-preview-types/-private/caches/instance-cache.d.ts +4 -1
  12. package/unstable-preview-types/-private/caches/instance-cache.d.ts.map +1 -1
  13. package/unstable-preview-types/-private/document.d.ts +23 -14
  14. package/unstable-preview-types/-private/document.d.ts.map +1 -1
  15. package/unstable-preview-types/-private/managers/record-array-manager.d.ts +7 -5
  16. package/unstable-preview-types/-private/managers/record-array-manager.d.ts.map +1 -1
  17. package/unstable-preview-types/-private/record-arrays/identifier-array.d.ts +3 -1
  18. package/unstable-preview-types/-private/record-arrays/identifier-array.d.ts.map +1 -1
  19. package/unstable-preview-types/-private/store-service.d.ts +1 -3
  20. package/unstable-preview-types/-private/store-service.d.ts.map +1 -1
  21. package/unstable-preview-types/-private.d.ts +2 -2
  22. package/unstable-preview-types/-private.d.ts.map +1 -1
  23. package/unstable-preview-types/-types/q/schema-service.d.ts +8 -0
  24. package/unstable-preview-types/-types/q/schema-service.d.ts.map +1 -1
  25. package/dist/many-array-uP6jS6_J.js.map +0 -1
@@ -4,7 +4,7 @@ import { EnableHydration, SkipCache } from '@warp-drive/core-types/request';
4
4
  import { setLogging, getRuntimeConfig } from '@warp-drive/core-types/runtime';
5
5
  import { getOrSetGlobal, peekTransient, setTransient } from '@warp-drive/core-types/-private';
6
6
  import { _backburner } from '@ember/runloop';
7
- import { defineSignal, createSignal, subscribe, createArrayTags, addToTransaction, addTransactionCB } from '@ember-data/tracking/-private';
7
+ import { defineSubscription, notifySignal, defineSignal, createSignal, subscribe, createArrayTags, addToTransaction, addTransactionCB } from '@ember-data/tracking/-private';
8
8
  import { CACHE_OWNER, DEBUG_STALE_CACHE_OWNER, DEBUG_IDENTIFIER_BUCKET, DEBUG_CLIENT_ORIGINATED } from '@warp-drive/core-types/identifier';
9
9
  import { dasherize } from '@ember-data/request-utils/string';
10
10
  import { compat } from '@ember-data/tracking';
@@ -902,6 +902,309 @@ function _log(scope, prefix, subScop1, subScop2, subScop3, subScop4) {
902
902
  return [];
903
903
  }
904
904
 
905
+ /**
906
+ * @module @ember-data/store
907
+ */
908
+ function urlFromLink(link) {
909
+ if (typeof link === 'string') return link;
910
+ return link.href;
911
+ }
912
+
913
+ /**
914
+ * A Document is a class that wraps the response content from a request to the API
915
+ * returned by `Cache.put` or `Cache.peek`, converting resource-identifiers into
916
+ * record instances.
917
+ *
918
+ * It is not directly instantiated by the user, and its properties should not
919
+ * be directly modified. Whether individual properties are mutable or not is
920
+ * determined by the record instance itself.
921
+ *
922
+ * @public
923
+ * @class ReactiveDocument
924
+ */
925
+ class ReactiveDocument {
926
+ /**
927
+ * The links object for this document, if any
928
+ *
929
+ * e.g.
930
+ *
931
+ * ```
932
+ * {
933
+ * self: '/articles?page[number]=3',
934
+ * }
935
+ * ```
936
+ *
937
+ * @property links
938
+ * @type {object|undefined} - a links object
939
+ * @public
940
+ */
941
+
942
+ /**
943
+ * The primary data for this document, if any.
944
+ *
945
+ * If this document has no primary data (e.g. because it is an error document)
946
+ * this property will be `undefined`.
947
+ *
948
+ * For collections this will be an array of record instances,
949
+ * for single resource requests it will be a single record instance or null.
950
+ *
951
+ * @property data
952
+ * @public
953
+ * @type {object|Array<object>|null|undefined} - a data object
954
+ */
955
+
956
+ /**
957
+ * The errors returned by the API for this request, if any
958
+ *
959
+ * @property errors
960
+ * @public
961
+ * @type {object|undefined} - an errors object
962
+ */
963
+
964
+ /**
965
+ * The meta object for this document, if any
966
+ *
967
+ * @property meta
968
+ * @public
969
+ * @type {object|undefined} - a meta object
970
+ */
971
+
972
+ /**
973
+ * The identifier associated with this document, if any
974
+ *
975
+ * @property identifier
976
+ * @public
977
+ * @type {StableDocumentIdentifier|null}
978
+ */
979
+
980
+ constructor(store, identifier, localCache) {
981
+ this._store = store;
982
+ this._localCache = localCache;
983
+ this.identifier = identifier;
984
+
985
+ // TODO if we ever enable auto-cleanup of the cache, we will need to tear this down
986
+ // in a destroy method
987
+ if (identifier) {
988
+ store.notifications.subscribe(identifier, (_identifier, type) => {
989
+ switch (type) {
990
+ case 'updated':
991
+ // FIXME in the case of a collection we need to notify it's length
992
+ // and have it recalc
993
+ notifySignal(this, 'data');
994
+ notifySignal(this, 'links');
995
+ notifySignal(this, 'meta');
996
+ notifySignal(this, 'errors');
997
+ break;
998
+ }
999
+ });
1000
+ }
1001
+ }
1002
+ async #request(link, options) {
1003
+ const href = this.links?.[link];
1004
+ if (!href) {
1005
+ return null;
1006
+ }
1007
+ options.method = options.method || 'GET';
1008
+ Object.assign(options, {
1009
+ url: urlFromLink(href)
1010
+ });
1011
+ const response = await this._store.request(options);
1012
+ return response.content;
1013
+ }
1014
+
1015
+ /**
1016
+ * Fetches the related link for this document, returning a promise that resolves
1017
+ * with the document when the request completes. If no related link is present,
1018
+ * will fallback to the self link if present
1019
+ *
1020
+ * @method fetch
1021
+ * @public
1022
+ * @param {object} options
1023
+ * @return Promise<Document>
1024
+ */
1025
+ fetch(options = {}) {
1026
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
1027
+ if (!test) {
1028
+ throw new Error(`No self or related link`);
1029
+ }
1030
+ })(this.links?.related || this.links?.self) : {};
1031
+ options.cacheOptions = options.cacheOptions || {};
1032
+ options.cacheOptions.key = this.identifier?.lid;
1033
+ return this.#request(this.links.related ? 'related' : 'self', options);
1034
+ }
1035
+
1036
+ /**
1037
+ * Fetches the next link for this document, returning a promise that resolves
1038
+ * with the new document when the request completes, or null if there is no
1039
+ * next link.
1040
+ *
1041
+ * @method next
1042
+ * @public
1043
+ * @param {object} options
1044
+ * @return Promise<Document | null>
1045
+ */
1046
+ next(options = {}) {
1047
+ return this.#request('next', options);
1048
+ }
1049
+
1050
+ /**
1051
+ * Fetches the prev link for this document, returning a promise that resolves
1052
+ * with the new document when the request completes, or null if there is no
1053
+ * prev link.
1054
+ *
1055
+ * @method prev
1056
+ * @public
1057
+ * @param {object} options
1058
+ * @return Promise<Document | null>
1059
+ */
1060
+ prev(options = {}) {
1061
+ return this.#request('prev', options);
1062
+ }
1063
+
1064
+ /**
1065
+ * Fetches the first link for this document, returning a promise that resolves
1066
+ * with the new document when the request completes, or null if there is no
1067
+ * first link.
1068
+ *
1069
+ * @method first
1070
+ * @public
1071
+ * @param {object} options
1072
+ * @return Promise<Document | null>
1073
+ */
1074
+ first(options = {}) {
1075
+ return this.#request('first', options);
1076
+ }
1077
+
1078
+ /**
1079
+ * Fetches the last link for this document, returning a promise that resolves
1080
+ * with the new document when the request completes, or null if there is no
1081
+ * last link.
1082
+ *
1083
+ * @method last
1084
+ * @public
1085
+ * @param {object} options
1086
+ * @return Promise<Document | null>
1087
+ */
1088
+ last(options = {}) {
1089
+ return this.#request('last', options);
1090
+ }
1091
+
1092
+ /**
1093
+ * Implemented for `JSON.stringify` support.
1094
+ *
1095
+ * Returns the JSON representation of the document wrapper.
1096
+ *
1097
+ * This is a shallow serialization, it does not deeply serialize
1098
+ * the document's contents, leaving that to the individual record
1099
+ * instances to determine how to do, if at all.
1100
+ *
1101
+ * @method toJSON
1102
+ * @public
1103
+ * @return
1104
+ */
1105
+ toJSON() {
1106
+ const data = {};
1107
+ data.identifier = this.identifier;
1108
+ if (this.data !== undefined) {
1109
+ data.data = this.data;
1110
+ }
1111
+ if (this.links !== undefined) {
1112
+ data.links = this.links;
1113
+ }
1114
+ if (this.errors !== undefined) {
1115
+ data.errors = this.errors;
1116
+ }
1117
+ if (this.meta !== undefined) {
1118
+ data.meta = this.meta;
1119
+ }
1120
+ return data;
1121
+ }
1122
+ }
1123
+ defineSubscription(ReactiveDocument.prototype, 'errors', {
1124
+ get() {
1125
+ const {
1126
+ identifier
1127
+ } = this;
1128
+ if (!identifier) {
1129
+ const {
1130
+ document
1131
+ } = this._localCache;
1132
+ if ('errors' in document) {
1133
+ return document.errors;
1134
+ }
1135
+ return;
1136
+ }
1137
+ const doc = this._store.cache.peek(identifier);
1138
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
1139
+ if (!test) {
1140
+ throw new Error(`No cache data was found for the document '${identifier.lid}'`);
1141
+ }
1142
+ })(doc) : {};
1143
+ return 'errors' in doc ? doc.errors : undefined;
1144
+ }
1145
+ });
1146
+ defineSubscription(ReactiveDocument.prototype, 'data', {
1147
+ get() {
1148
+ const {
1149
+ identifier,
1150
+ _localCache
1151
+ } = this;
1152
+ const doc = identifier ? this._store.cache.peek(identifier) : _localCache.document;
1153
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
1154
+ if (!test) {
1155
+ throw new Error(`No cache data was found for the document '${identifier?.lid ?? '<uncached document>'}'`);
1156
+ }
1157
+ })(doc) : {};
1158
+ const data = 'data' in doc ? doc.data : undefined;
1159
+ if (Array.isArray(data)) {
1160
+ return this._store.recordArrayManager.getCollection({
1161
+ type: identifier ? identifier.lid : _localCache.request.url,
1162
+ identifiers: data.slice(),
1163
+ doc: identifier ? undefined : doc,
1164
+ identifier: identifier ?? null
1165
+ });
1166
+ } else if (data) {
1167
+ return this._store.peekRecord(data);
1168
+ } else {
1169
+ return data;
1170
+ }
1171
+ }
1172
+ });
1173
+ defineSubscription(ReactiveDocument.prototype, 'links', {
1174
+ get() {
1175
+ const {
1176
+ identifier
1177
+ } = this;
1178
+ if (!identifier) {
1179
+ return this._localCache.document.links;
1180
+ }
1181
+ const data = this._store.cache.peek(identifier);
1182
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
1183
+ if (!test) {
1184
+ throw new Error(`No cache data was found for the document '${identifier.lid}'`);
1185
+ }
1186
+ })(data) : {};
1187
+ return data.links;
1188
+ }
1189
+ });
1190
+ defineSubscription(ReactiveDocument.prototype, 'meta', {
1191
+ get() {
1192
+ const {
1193
+ identifier
1194
+ } = this;
1195
+ if (!identifier) {
1196
+ return this._localCache.document.meta;
1197
+ }
1198
+ const data = this._store.cache.peek(identifier);
1199
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
1200
+ if (!test) {
1201
+ throw new Error(`No cache data was found for the document '${identifier.lid}'`);
1202
+ }
1203
+ })(data) : {};
1204
+ return data.meta;
1205
+ }
1206
+ });
1207
+
905
1208
  /**
906
1209
  @module @ember-data/store
907
1210
  */
@@ -1282,12 +1585,13 @@ function storeFor(record) {
1282
1585
  return store;
1283
1586
  }
1284
1587
  class InstanceCache {
1285
- __instances = {
1286
- record: new Map(),
1287
- reference: new WeakMap()
1288
- };
1289
1588
  constructor(store) {
1290
1589
  this.store = store;
1590
+ this.__instances = {
1591
+ record: new Map(),
1592
+ reference: new WeakMap(),
1593
+ document: new Map()
1594
+ };
1291
1595
  this._storeWrapper = new CacheCapabilitiesManager(this.store);
1292
1596
  store.identifierCache.__configureMerge((identifier, matchedIdentifier, resourceData) => {
1293
1597
  let keptIdentifier = identifier;
@@ -1342,6 +1646,14 @@ class InstanceCache {
1342
1646
  peek(identifier) {
1343
1647
  return this.__instances.record.get(identifier);
1344
1648
  }
1649
+ getDocument(identifier) {
1650
+ let doc = this.__instances.document.get(identifier);
1651
+ if (!doc) {
1652
+ doc = new ReactiveDocument(this.store, identifier, null);
1653
+ this.__instances.document.set(identifier, doc);
1654
+ }
1655
+ return doc;
1656
+ }
1345
1657
  getRecord(identifier, properties) {
1346
1658
  let record = this.__instances.record.get(identifier);
1347
1659
  if (!record) {
@@ -2678,6 +2990,7 @@ class IdentifierArray {
2678
2990
  isDestroying = false;
2679
2991
  isDestroyed = false;
2680
2992
  _updatingPromise = null;
2993
+ identifier;
2681
2994
  [IS_COLLECTION] = true;
2682
2995
  [SOURCE];
2683
2996
  [NOTIFY]() {
@@ -2716,6 +3029,7 @@ class IdentifierArray {
2716
3029
  this.modelName = options.type;
2717
3030
  this.store = options.store;
2718
3031
  this._manager = options.manager;
3032
+ this.identifier = options.identifier || null;
2719
3033
  this[SOURCE] = options.identifiers;
2720
3034
  this[ARRAY_SIGNAL] = createSignal(this, 'length');
2721
3035
  const store = options.store;
@@ -3172,6 +3486,12 @@ class RecordArrayManager {
3172
3486
  this._identifiers = new Map();
3173
3487
  this._set = new Map();
3174
3488
  this._visibilitySet = new Map();
3489
+ this._subscription = this.store.notifications.subscribe('document', (identifier, type) => {
3490
+ if (type === 'updated' && this._keyedArrays.has(identifier.lid)) {
3491
+ const array = this._keyedArrays.get(identifier.lid);
3492
+ this.dirtyArray(array, 0, true);
3493
+ }
3494
+ });
3175
3495
  this._subscription = this.store.notifications.subscribe('resource', (identifier, type) => {
3176
3496
  if (type === 'added') {
3177
3497
  this._visibilitySet.set(identifier, true);
@@ -3186,11 +3506,35 @@ class RecordArrayManager {
3186
3506
  }
3187
3507
  _syncArray(array) {
3188
3508
  const pending = this._pending.get(array);
3189
- if (!pending || this.isDestroying || this.isDestroyed) {
3509
+ const isRequestArray = isCollection(array);
3510
+ if (!isRequestArray && !pending || this.isDestroying || this.isDestroyed) {
3190
3511
  return;
3191
3512
  }
3192
- sync(array, pending, this._set.get(array));
3193
- this._pending.delete(array);
3513
+
3514
+ // first flush any staged changes
3515
+ if (pending) {
3516
+ sync(array, pending, this._set.get(array));
3517
+ this._pending.delete(array);
3518
+ }
3519
+
3520
+ // then pull new state if required
3521
+ if (isRequestArray) {
3522
+ const tag = array[ARRAY_SIGNAL];
3523
+ if (tag.reason === 'cache-sync') {
3524
+ tag.reason = null;
3525
+ const doc = this.store.cache.peek(array.identifier);
3526
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
3527
+ if (!test) {
3528
+ throw new Error(`Expected to find a document for ${array.identifier.lid} but found none`);
3529
+ }
3530
+ })(doc) : {};
3531
+ const data = !('data' in doc) || !Array.isArray(doc.data) ? [] : doc.data;
3532
+ // TODO technically we should destroy here if
3533
+ // !('data' in doc) || !Array.isArray(doc.data)
3534
+ // is true.
3535
+ this.populateManagedArray(array, data, null);
3536
+ }
3537
+ }
3194
3538
  }
3195
3539
  mutate(mutation) {
3196
3540
  this.store.cache.mutate(mutation);
@@ -3229,9 +3573,13 @@ class RecordArrayManager {
3229
3573
  }
3230
3574
  return array;
3231
3575
  }
3232
- createArray(config) {
3576
+ getCollection(config) {
3577
+ if (config.identifier && this._keyedArrays.has(config.identifier.lid)) {
3578
+ return this._keyedArrays.get(config.identifier.lid);
3579
+ }
3233
3580
  const options = {
3234
3581
  type: config.type,
3582
+ identifier: config.identifier || null,
3235
3583
  links: config.doc?.links || null,
3236
3584
  meta: config.doc?.meta || null,
3237
3585
  query: config.query || null,
@@ -3244,16 +3592,22 @@ class RecordArrayManager {
3244
3592
  const array = new Collection(options);
3245
3593
  this._managed.add(array);
3246
3594
  this._set.set(array, new Set(options.identifiers || []));
3595
+ if (config.identifier) {
3596
+ this._keyedArrays.set(config.identifier.lid, array);
3597
+ }
3247
3598
  if (config.identifiers) {
3248
3599
  associate(this._identifiers, array, config.identifiers);
3249
3600
  }
3250
3601
  return array;
3251
3602
  }
3252
- dirtyArray(array, delta) {
3603
+ dirtyArray(array, delta, shouldSyncFromCache) {
3253
3604
  if (array === FAKE_ARR) {
3254
3605
  return;
3255
3606
  }
3256
3607
  const tag = array[ARRAY_SIGNAL];
3608
+ if (shouldSyncFromCache) {
3609
+ tag.reason = 'cache-sync';
3610
+ }
3257
3611
  if (!tag.shouldReset) {
3258
3612
  tag.shouldReset = true;
3259
3613
  addTransactionCB(array[NOTIFY]);
@@ -3312,13 +3666,18 @@ class RecordArrayManager {
3312
3666
  populateManagedArray(array, identifiers, payload) {
3313
3667
  this._pending.delete(array);
3314
3668
  const source = array[SOURCE];
3669
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
3670
+ if (!test) {
3671
+ throw new Error(`The new state of the collection should not be using the same array reference as the original state.`);
3672
+ }
3673
+ })(source !== identifiers) : {};
3315
3674
  const old = source.slice();
3316
3675
  source.length = 0;
3317
3676
  fastPush(source, identifiers);
3318
3677
  this._set.set(array, new Set(identifiers));
3319
3678
  notifyArray(array);
3320
- array.meta = payload.meta || null;
3321
- array.links = payload.links || null;
3679
+ array.meta = payload?.meta || null;
3680
+ array.links = payload?.links || null;
3322
3681
  array.isLoaded = true;
3323
3682
  disassociate(this._identifiers, array, old);
3324
3683
  associate(this._identifiers, array, identifiers);
@@ -3332,7 +3691,7 @@ class RecordArrayManager {
3332
3691
  changes.delete(identifier);
3333
3692
  } else {
3334
3693
  changes.set(identifier, 'add');
3335
- this.dirtyArray(array, changes.size);
3694
+ this.dirtyArray(array, changes.size, false);
3336
3695
  }
3337
3696
  });
3338
3697
  }
@@ -3346,7 +3705,7 @@ class RecordArrayManager {
3346
3705
  changes.delete(identifier);
3347
3706
  } else {
3348
3707
  changes.set(identifier, 'del');
3349
- this.dirtyArray(array, changes.size);
3708
+ this.dirtyArray(array, changes.size, false);
3350
3709
  }
3351
3710
  });
3352
3711
  }
@@ -3451,6 +3810,9 @@ function sync(array, changes, arraySet) {
3451
3810
  */
3452
3811
  }
3453
3812
  }
3813
+ function isCollection(array) {
3814
+ return array.identifier !== null;
3815
+ }
3454
3816
 
3455
3817
  /**
3456
3818
  * @module @ember-data/store
@@ -3889,6 +4251,7 @@ const app = new EmberApp(defaults, {
3889
4251
  id: 'ember-data:deprecate-store-extends-ember-object',
3890
4252
  until: '6.0',
3891
4253
  for: 'ember-data',
4254
+ url: 'https://deprecations.emberjs.com/id/ember-data-deprecate-store-extends-ember-object',
3892
4255
  since: {
3893
4256
  available: '4.13',
3894
4257
  enabled: '5.4'
@@ -4036,7 +4399,6 @@ class Store extends BaseClass {
4036
4399
  // private
4037
4400
  this._requestCache = new RequestStateService(this);
4038
4401
  this._instanceCache = new InstanceCache(this);
4039
- this._documentCache = new Map();
4040
4402
  this.isDestroying = false;
4041
4403
  this.isDestroyed = false;
4042
4404
  }
@@ -5483,18 +5845,6 @@ class Store extends BaseClass {
5483
5845
  if (macroCondition(getGlobalConfig().WarpDrive.env.DEBUG)) {
5484
5846
  assertDestroyingStore(this, '_push');
5485
5847
  }
5486
- if (macroCondition(getGlobalConfig().WarpDrive.activeLogging.LOG_PAYLOADS)) {
5487
- if (getGlobalConfig().WarpDrive.debug.LOG_PAYLOADS || globalThis.getWarpDriveRuntimeConfig().debug.LOG_PAYLOADS) {
5488
- try {
5489
- const data = JSON.parse(JSON.stringify(jsonApiDoc));
5490
- // eslint-disable-next-line no-console
5491
- console.log('EmberData | Payload - push', data);
5492
- } catch {
5493
- // eslint-disable-next-line no-console
5494
- console.log('EmberData | Payload - push', jsonApiDoc);
5495
- }
5496
- }
5497
- }
5498
5848
  if (asyncFlush) {
5499
5849
  this._enableAsyncFlush = true;
5500
5850
  }
@@ -5755,212 +6105,6 @@ function extractIdentifierFromRecord$1(recordOrPromiseRecord) {
5755
6105
  const extract = recordIdentifierFor;
5756
6106
  return extract(recordOrPromiseRecord);
5757
6107
  }
5758
-
5759
- /**
5760
- * @module @ember-data/store
5761
- */
5762
- function urlFromLink(link) {
5763
- if (typeof link === 'string') return link;
5764
- return link.href;
5765
- }
5766
-
5767
- /**
5768
- * A Document is a class that wraps the response content from a request to the API
5769
- * returned by `Cache.put` or `Cache.peek`, converting resource-identifiers into
5770
- * record instances.
5771
- *
5772
- * It is not directly instantiated by the user, and its properties should not
5773
- * be directly modified. Whether individual properties are mutable or not is
5774
- * determined by the record instance itself.
5775
- *
5776
- * @public
5777
- * @class Document
5778
- */
5779
- class Document {
5780
- /**
5781
- * The links object for this document, if any
5782
- *
5783
- * e.g.
5784
- *
5785
- * ```
5786
- * {
5787
- * self: '/articles?page[number]=3',
5788
- * }
5789
- * ```
5790
- *
5791
- * @property links
5792
- * @type {object|undefined} - a links object
5793
- * @public
5794
- */
5795
-
5796
- /**
5797
- * The primary data for this document, if any.
5798
- *
5799
- * If this document has no primary data (e.g. because it is an error document)
5800
- * this property will be `undefined`.
5801
- *
5802
- * For collections this will be an array of record instances,
5803
- * for single resource requests it will be a single record instance or null.
5804
- *
5805
- * @property data
5806
- * @public
5807
- * @type {object|Array<object>|null|undefined} - a data object
5808
- */
5809
-
5810
- /**
5811
- * The errors returned by the API for this request, if any
5812
- *
5813
- * @property errors
5814
- * @public
5815
- * @type {object|undefined} - an errors object
5816
- */
5817
-
5818
- /**
5819
- * The meta object for this document, if any
5820
- *
5821
- * @property meta
5822
- * @public
5823
- * @type {object|undefined} - a meta object
5824
- */
5825
-
5826
- /**
5827
- * The identifier associated with this document, if any
5828
- *
5829
- * @property identifier
5830
- * @public
5831
- * @type {StableDocumentIdentifier|null}
5832
- */
5833
-
5834
- #store;
5835
- constructor(store, identifier) {
5836
- this.#store = store;
5837
- this.identifier = identifier;
5838
- }
5839
- async #request(link, options) {
5840
- const href = this.links?.[link];
5841
- if (!href) {
5842
- return null;
5843
- }
5844
- options.method = options.method || 'GET';
5845
- Object.assign(options, {
5846
- url: urlFromLink(href)
5847
- });
5848
- const response = await this.#store.request(options);
5849
- return response.content;
5850
- }
5851
-
5852
- /**
5853
- * Fetches the related link for this document, returning a promise that resolves
5854
- * with the document when the request completes. If no related link is present,
5855
- * will fallback to the self link if present
5856
- *
5857
- * @method fetch
5858
- * @public
5859
- * @param {object} options
5860
- * @return Promise<Document>
5861
- */
5862
- fetch(options = {}) {
5863
- macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
5864
- if (!test) {
5865
- throw new Error(`No self or related link`);
5866
- }
5867
- })(this.links?.related || this.links?.self) : {};
5868
- options.cacheOptions = options.cacheOptions || {};
5869
- options.cacheOptions.key = this.identifier?.lid;
5870
- return this.#request(this.links.related ? 'related' : 'self', options);
5871
- }
5872
-
5873
- /**
5874
- * Fetches the next link for this document, returning a promise that resolves
5875
- * with the new document when the request completes, or null if there is no
5876
- * next link.
5877
- *
5878
- * @method next
5879
- * @public
5880
- * @param {object} options
5881
- * @return Promise<Document | null>
5882
- */
5883
- next(options = {}) {
5884
- return this.#request('next', options);
5885
- }
5886
-
5887
- /**
5888
- * Fetches the prev link for this document, returning a promise that resolves
5889
- * with the new document when the request completes, or null if there is no
5890
- * prev link.
5891
- *
5892
- * @method prev
5893
- * @public
5894
- * @param {object} options
5895
- * @return Promise<Document | null>
5896
- */
5897
- prev(options = {}) {
5898
- return this.#request('prev', options);
5899
- }
5900
-
5901
- /**
5902
- * Fetches the first link for this document, returning a promise that resolves
5903
- * with the new document when the request completes, or null if there is no
5904
- * first link.
5905
- *
5906
- * @method first
5907
- * @public
5908
- * @param {object} options
5909
- * @return Promise<Document | null>
5910
- */
5911
- first(options = {}) {
5912
- return this.#request('first', options);
5913
- }
5914
-
5915
- /**
5916
- * Fetches the last link for this document, returning a promise that resolves
5917
- * with the new document when the request completes, or null if there is no
5918
- * last link.
5919
- *
5920
- * @method last
5921
- * @public
5922
- * @param {object} options
5923
- * @return Promise<Document | null>
5924
- */
5925
- last(options = {}) {
5926
- return this.#request('last', options);
5927
- }
5928
-
5929
- /**
5930
- * Implemented for `JSON.stringify` support.
5931
- *
5932
- * Returns the JSON representation of the document wrapper.
5933
- *
5934
- * This is a shallow serialization, it does not deeply serialize
5935
- * the document's contents, leaving that to the individual record
5936
- * instances to determine how to do, if at all.
5937
- *
5938
- * @method toJSON
5939
- * @public
5940
- * @return
5941
- */
5942
- toJSON() {
5943
- const data = {};
5944
- data.identifier = this.identifier;
5945
- if (this.data !== undefined) {
5946
- data.data = this.data;
5947
- }
5948
- if (this.links !== undefined) {
5949
- data.links = this.links;
5950
- }
5951
- if (this.errors !== undefined) {
5952
- data.errors = this.errors;
5953
- }
5954
- if (this.meta !== undefined) {
5955
- data.meta = this.meta;
5956
- }
5957
- return data;
5958
- }
5959
- }
5960
- defineSignal(Document.prototype, 'data');
5961
- defineSignal(Document.prototype, 'links');
5962
- defineSignal(Document.prototype, 'errors');
5963
- defineSignal(Document.prototype, 'meta');
5964
6108
  const MUTATION_OPS = new Set(['createRecord', 'updateRecord', 'deleteRecord']);
5965
6109
  function calcShouldFetch(store, request, hasCachedValue, identifier) {
5966
6110
  const {
@@ -5977,17 +6121,6 @@ function calcShouldBackgroundFetch(store, request, willFetch, identifier) {
5977
6121
  function isMutation(request) {
5978
6122
  return Boolean(request.op && MUTATION_OPS.has(request.op));
5979
6123
  }
5980
- function copyDocumentProperties(target, source) {
5981
- if ('links' in source) {
5982
- target.links = source.links;
5983
- }
5984
- if ('meta' in source) {
5985
- target.meta = source.meta;
5986
- }
5987
- if ('errors' in source) {
5988
- target.errors = source.errors;
5989
- }
5990
- }
5991
6124
  function isCacheAffecting(document) {
5992
6125
  if (!isMutation(document.request)) {
5993
6126
  return true;
@@ -6015,9 +6148,6 @@ function cloneError(error) {
6015
6148
  Object.assign(cloned, error);
6016
6149
  return cloned;
6017
6150
  }
6018
- function isErrorDocument(document) {
6019
- return 'errors' in document;
6020
- }
6021
6151
  function getPriority(identifier, deduped, priority) {
6022
6152
  if (identifier) {
6023
6153
  const existing = deduped.get(identifier);
@@ -6147,10 +6277,10 @@ const CacheHandler = {
6147
6277
  const shouldHydrate = context.request[EnableHydration] || false;
6148
6278
  context.setResponse(peeked.response);
6149
6279
  if ('error' in peeked) {
6150
- const content = shouldHydrate ? maybeUpdateErrorUiObjects(store, {
6280
+ const content = shouldHydrate ? maybeUpdateUiObjects(store, context.request, {
6151
6281
  shouldHydrate,
6152
6282
  identifier
6153
- }, peeked.content, true) : peeked.content;
6283
+ }, peeked.content) : peeked.content;
6154
6284
  const newError = cloneError(peeked);
6155
6285
  newError.content = content;
6156
6286
  throw newError;
@@ -6158,15 +6288,15 @@ const CacheHandler = {
6158
6288
  const result = shouldHydrate ? maybeUpdateUiObjects(store, context.request, {
6159
6289
  shouldHydrate,
6160
6290
  identifier
6161
- }, peeked.content, true) : peeked.content;
6291
+ }, peeked.content) : peeked.content;
6162
6292
  return result;
6163
6293
  }
6164
6294
  };
6165
- function maybeUpdateUiObjects(store, request, options, document, isFromCache) {
6295
+ function maybeUpdateUiObjects(store, request, options, document) {
6166
6296
  const {
6167
6297
  identifier
6168
6298
  } = options;
6169
- if (!document) {
6299
+ if (!document || !options.shouldHydrate) {
6170
6300
  macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
6171
6301
  if (!test) {
6172
6302
  throw new Error(`The CacheHandler expected response content but none was found`);
@@ -6174,97 +6304,16 @@ function maybeUpdateUiObjects(store, request, options, document, isFromCache) {
6174
6304
  })(!options.shouldHydrate) : {};
6175
6305
  return document;
6176
6306
  }
6177
- if (Array.isArray(document.data)) {
6178
- const {
6179
- recordArrayManager
6180
- } = store;
6181
- if (!identifier) {
6182
- if (!options.shouldHydrate) {
6183
- return document;
6184
- }
6185
- const data = recordArrayManager.createArray({
6186
- type: request.url,
6187
- identifiers: document.data,
6188
- doc: document,
6189
- query: request
6190
- });
6191
- const doc = new Document(store, null);
6192
- doc.data = data;
6193
- doc.meta = document.meta;
6194
- doc.links = document.links;
6195
- return doc;
6196
- }
6197
- let managed = recordArrayManager._keyedArrays.get(identifier.lid);
6198
- if (!managed) {
6199
- managed = recordArrayManager.createArray({
6200
- type: identifier.lid,
6201
- identifiers: document.data,
6202
- doc: document
6203
- });
6204
- recordArrayManager._keyedArrays.set(identifier.lid, managed);
6205
- const doc = new Document(store, identifier);
6206
- doc.data = managed;
6207
- doc.meta = document.meta;
6208
- doc.links = document.links;
6209
- store._documentCache.set(identifier, doc);
6210
- return options.shouldHydrate ? doc : document;
6211
- } else {
6212
- const doc = store._documentCache.get(identifier);
6213
- if (!isFromCache) {
6214
- recordArrayManager.populateManagedArray(managed, document.data, document);
6215
- doc.data = managed;
6216
- doc.meta = document.meta;
6217
- doc.links = document.links;
6218
- }
6219
- return options.shouldHydrate ? doc : document;
6220
- }
6221
- } else {
6222
- if (!identifier && !options.shouldHydrate) {
6223
- return document;
6224
- }
6225
- const data = document.data ? store.peekRecord(document.data) : null;
6226
- let doc;
6227
- if (identifier) {
6228
- doc = store._documentCache.get(identifier);
6229
- }
6230
- if (!doc) {
6231
- doc = new Document(store, identifier);
6232
- doc.data = data;
6233
- copyDocumentProperties(doc, document);
6234
- if (identifier) {
6235
- store._documentCache.set(identifier, doc);
6236
- }
6237
- } else if (!isFromCache) {
6238
- doc.data = data;
6239
- copyDocumentProperties(doc, document);
6240
- }
6241
- return options.shouldHydrate ? doc : document;
6242
- }
6243
- }
6244
- function maybeUpdateErrorUiObjects(store, options, document, isFromCache) {
6245
- const {
6246
- identifier
6247
- } = options;
6248
-
6249
- // TODO investigate why ResourceErrorDocument is insufficient for expressing all error types
6250
- if (!isErrorDocument(document) || !identifier && !options.shouldHydrate) {
6251
- return document;
6252
- }
6253
- let doc;
6254
6307
  if (identifier) {
6255
- doc = store._documentCache.get(identifier);
6308
+ return store._instanceCache.getDocument(identifier);
6256
6309
  }
6257
- if (!doc) {
6258
- doc = new Document(store, identifier);
6259
- copyDocumentProperties(doc, document);
6260
- if (identifier) {
6261
- store._documentCache.set(identifier, doc);
6262
- }
6263
- } else if (!isFromCache) {
6264
- doc.data = undefined;
6265
- copyDocumentProperties(doc, document);
6266
- }
6267
- return options.shouldHydrate ? doc : document;
6310
+
6311
+ // if we don't have an identifier, we give the document
6312
+ // its own local cache
6313
+ return new ReactiveDocument(store, null, {
6314
+ request,
6315
+ document
6316
+ });
6268
6317
  }
6269
6318
  function updateCacheForSuccess(store, request, options, document) {
6270
6319
  let response = null;
@@ -6282,7 +6331,7 @@ function updateCacheForSuccess(store, request, options, document) {
6282
6331
  } else {
6283
6332
  response = store.cache.put(document);
6284
6333
  }
6285
- return maybeUpdateUiObjects(store, request, options, response, false);
6334
+ return maybeUpdateUiObjects(store, request, options, response);
6286
6335
  }
6287
6336
  function handleFetchSuccess(store, context, options, document) {
6288
6337
  const {
@@ -6315,7 +6364,7 @@ function updateCacheForError(store, context, options, error) {
6315
6364
  store.cache.commitWasRejected(record, errors);
6316
6365
  } else {
6317
6366
  response = store.cache.put(error);
6318
- return maybeUpdateErrorUiObjects(store, options, response, false);
6367
+ return maybeUpdateUiObjects(store, context.request, options, response);
6319
6368
  }
6320
6369
  }
6321
6370
  function handleFetchError(store, context, options, error) {
@@ -6820,7 +6869,7 @@ function assertNoDuplicates(collection, target, callback, reason) {
6820
6869
  }
6821
6870
  function mutateAddToRelatedRecords(collection, operationInfo, _SIGNAL) {
6822
6871
  mutate(collection, {
6823
- op: 'addToRelatedRecords',
6872
+ op: 'add',
6824
6873
  record: collection.identifier,
6825
6874
  field: collection.key,
6826
6875
  ...operationInfo
@@ -6828,7 +6877,7 @@ function mutateAddToRelatedRecords(collection, operationInfo, _SIGNAL) {
6828
6877
  }
6829
6878
  function mutateRemoveFromRelatedRecords(collection, operationInfo, _SIGNAL) {
6830
6879
  mutate(collection, {
6831
- op: 'removeFromRelatedRecords',
6880
+ op: 'remove',
6832
6881
  record: collection.identifier,
6833
6882
  field: collection.key,
6834
6883
  ...operationInfo
@@ -6867,4 +6916,4 @@ function mutate(collection, mutation, _SIGNAL) {
6867
6916
  collection._manager.mutate(mutation);
6868
6917
  addToTransaction(_SIGNAL);
6869
6918
  }
6870
- export { ARRAY_SIGNAL as A, CacheHandler as C, IdentifierArray as I, MUTATE as M, RecordArrayManager as R, Store as S, _clearCaches as _, setIdentifierGenerationMethod as a, setIdentifierUpdateMethod as b, setIdentifierForgetMethod as c, setIdentifierResetMethod as d, setKeyInfoForResource as e, constructResource as f, coerceId as g, ensureStringId as h, isStableIdentifier as i, Collection as j, SOURCE as k, fastPush as l, removeRecordDataFor as m, notifyArray as n, setRecordIdentifier as o, peekCache as p, StoreMap as q, recordIdentifierFor as r, storeFor as s, setCacheFor as t, normalizeModelName as u, RelatedCollection as v, log as w, logGroup as x };
6919
+ export { ARRAY_SIGNAL as A, CacheHandler as C, IdentifierArray as I, MUTATE as M, RecordArrayManager as R, Store as S, _clearCaches as _, setIdentifierGenerationMethod as a, setIdentifierUpdateMethod as b, setIdentifierForgetMethod as c, setIdentifierResetMethod as d, setKeyInfoForResource as e, isDocumentIdentifier as f, constructResource as g, coerceId as h, isStableIdentifier as i, ensureStringId as j, Collection as k, SOURCE as l, fastPush as m, notifyArray as n, removeRecordDataFor as o, peekCache as p, setRecordIdentifier as q, recordIdentifierFor as r, storeFor as s, StoreMap as t, setCacheFor as u, normalizeModelName as v, RelatedCollection as w, log as x, logGroup as y };